Skip to content

Commit

Permalink
Merge pull request #8 from 10play/compose-followup
Browse files Browse the repository at this point in the history
Compose followup
  • Loading branch information
GuySerfaty committed Feb 6, 2024
2 parents 529b530 + 0e096db commit 5891c89
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 61 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ jobs:
with:
path: |
**/ios/Pods
key: ${{ runner.os }}-cocoapods-v2-${{ hashFiles('example/ios/Podfile.lock') }}
key: ${{ runner.os }}-cocoapods-v3-${{ hashFiles('example/ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-cocoapods-v2-
${{ runner.os }}-cocoapods-v3-
- name: Install cocoapods
if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
Expand Down
2 changes: 1 addition & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Basic } from './Examples/Basic';
import { CustomKeyboardExample } from './Examples/CustomKeyboardExample';
import { EditorStickToKeyboardExample } from './Examples/EditorStickToKeyboardExample';
import { Advanced } from './Examples/Advanced/AdvancedRichText';
import { Compose } from './Examples/Compose';
import { Compose } from './Examples/Compose/Compose';

const examples = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import React, { useRef } from 'react';
import React from 'react';
import {
SafeAreaView,
View,
KeyboardAvoidingView,
Platform,
StyleSheet,
TouchableOpacity,
Text,
TextInput,
Alert,
} from 'react-native';
import {
ColorBridge,
CoreBridge,
HighlightBridge,
ImageBridge,
LinkBridge,
RichText,
TaskListBridge,
TenTapStartKit,
Toolbar,
UnderlineBridge,
useNativeEditor,
type EditorInstance,
} from 'tentap';
import { ColorKeyboard } from '../../../src/RichText/Keyboard/ColorKeyboard';
import { CustomKeyboard } from '../../../src/RichText/Keyboard';
import { Icon } from './Icon';
import { Icon } from '../Icon';
import { ComposeRichText } from './ComposeRichText';

export const Compose = ({
navigation,
Expand All @@ -47,6 +42,7 @@ export const Compose = ({

const onSendClick = async () => {
const mailContent = await editor.getContent();
Alert.alert('Mail Content', mailContent);
console.log('Send Clicked! Mail content: ', mailContent);
};

Expand Down Expand Up @@ -83,64 +79,19 @@ export const Compose = ({
/>
</View>
</View>
<ComposeEditor editor={editor} />
<ComposeRichText editor={editor} onSendClick={onSendClick} />
</SafeAreaView>
);
};

interface ComposeEditorProps {
editor: EditorInstance;
}
const ComposeEditor = ({ editor }: ComposeEditorProps) => {
const TapRef = useRef(null);
const [activeKeyboard, setActiveKeyboard] = React.useState<string>();

return (
<>
<View style={[exampleStyles.compose]} ref={TapRef}>
<View style={exampleStyles.fullScreen}>
<RichText editor={editor} avoidIosKeyboard />
</View>
</View>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={exampleStyles.keyboardAvoidingView}
>
<Toolbar
activeKeyboard={activeKeyboard}
setActiveKeyboard={setActiveKeyboard}
editor={editor}
hidden={false}
/>
<CustomKeyboard
rootRef={TapRef}
activeKeyboardID={activeKeyboard}
setActiveKeyboardID={setActiveKeyboard}
keyboards={[ColorKeyboard]}
editor={editor}
/>
</KeyboardAvoidingView>
</>
);
};

const exampleStyles = StyleSheet.create({
header: {
paddingHorizontal: 14,
backgroundColor: 'white',
},
compose: {
flex: 1,
paddingHorizontal: 14,
},
fullScreen: {
flex: 1,
backgroundColor: 'white',
},
keyboardAvoidingView: {
position: 'absolute',
width: '100%',
bottom: 0,
header: {
paddingHorizontal: 14,
backgroundColor: 'white',
},
topBar: {
flexDirection: 'row',
Expand Down
193 changes: 193 additions & 0 deletions example/src/Examples/Compose/ComposeRichText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
View,
KeyboardAvoidingView,
Platform,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import { RichText, useNativeEditorState } from '../../../../src/RichText';
import { CustomKeyboard } from '../../../../src/RichText/Keyboard';
import { ColorKeyboard } from '../../../../src/RichText/Keyboard/ColorKeyboard';
import type { EditorInstance } from '../../../../src/types';
import { icons } from '../../assets';
import { Icon } from '../Icon';
import { useKeyboard } from '../../../../src/utils';

interface ComposeRichTextProps {
editor: EditorInstance;
onSendClick: () => void;
}
export const ComposeRichText = ({
editor,
onSendClick,
}: ComposeRichTextProps) => {
const rootRef = useRef(null);
const [activeKeyboard, setActiveKeyboard] = React.useState<string>();

return (
<>
<View style={composeStyles.compose}>
<RichText editor={editor} avoidIosKeyboard />
</View>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={composeStyles.keyboardAvoidingView}
>
<ComposeToolbar
editor={editor}
activeKeyboard={activeKeyboard}
setActiveKeyboard={setActiveKeyboard}
onSendClick={onSendClick}
/>
<CustomKeyboard
rootRef={rootRef}
activeKeyboardID={activeKeyboard}
setActiveKeyboardID={setActiveKeyboard}
keyboards={[ColorKeyboard]}
editor={editor}
/>
</KeyboardAvoidingView>
</>
);
};

interface ComposeToolbarProps {
editor: EditorInstance;
activeKeyboard: string | undefined;
setActiveKeyboard: (keyboard: string) => void;
onSendClick: () => void;
}
interface CustomToolbarAction {
isActive: boolean;
isDisabled: boolean;
onPress: () => void;
icon: keyof typeof icons;
}
enum ToolbarType {
Main,
Link,
Formatting,
}
const ComposeToolbar = ({ editor, onSendClick }: ComposeToolbarProps) => {
const editorState = useNativeEditorState(editor);
const { isKeyboardUp } = useKeyboard();
const [toolbarType, setToolbar] = useState<ToolbarType>(ToolbarType.Main);

const hideToolbar = !isKeyboardUp || !editorState.isFocused;

useEffect(() => {
if (hideToolbar) {
setToolbar(ToolbarType.Main);
}
}, [hideToolbar]);

const formattingOptions: CustomToolbarAction[] = useMemo(
() => [
{
isActive: true,
isDisabled: false,
onPress: () => {
setToolbar(ToolbarType.Main);
},
icon: 'formatting',
},
{
isActive: editorState.isBoldActive,
isDisabled: !editorState.canToggleBold,
onPress: editor.toggleBold,
icon: 'bold',
},
{
isActive: editorState.headingLevel === 1,
isDisabled: !editorState.canToggleHeading,
onPress: () => editor.toggleHeading(1),
icon: 'h1',
},
],
[editor, editorState]
);

if (toolbarType === ToolbarType.Main) {
return (
<View
style={[hideToolbar && composeStyles.hidden, composeStyles.mainToolbar]}
>
<TouchableOpacity onPress={() => setToolbar(ToolbarType.Formatting)}>
<Icon name={'formatting'} style={composeStyles.button} />
</TouchableOpacity>
<TouchableOpacity onPress={onSendClick}>
<Icon name={'send'} style={composeStyles.button} />
</TouchableOpacity>
</View>
);
}

return (
<View
style={[
hideToolbar && composeStyles.hidden,
composeStyles.formattingToolbar,
]}
>
{formattingOptions.map(({ isDisabled, isActive, icon, onPress }) => (
<TouchableOpacity
onPress={onPress}
disabled={isDisabled}
style={isActive ? composeStyles.activeButton : undefined}
key={icon}
>
<Icon
name={icon}
fill={isDisabled ? 'gray' : 'black'}
style={composeStyles.button}
/>
</TouchableOpacity>
))}
</View>
);
};

const composeStyles = StyleSheet.create({
compose: {
flex: 1,
paddingHorizontal: 14,
backgroundColor: 'white',
},
keyboardAvoidingView: {
position: 'absolute',
width: '100%',
bottom: 0,
},
mainToolbar: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 8,
paddingVertical: 4,
backgroundColor: 'white',
borderTopWidth: 1,
borderTopColor: 'lightgray',
},
formattingToolbar: {
flexDirection: 'row',
justifyContent: 'flex-start',
backgroundColor: 'white',
borderTopWidth: 1,
paddingVertical: 4,
paddingHorizontal: 8,
gap: 4,
borderTopColor: 'lightgray',
},
button: {
width: 24,
margin: 4,
padding: 5,
},
activeButton: {
backgroundColor: 'lightblue',
borderRadius: 10,
},
hidden: {
display: 'none',
},
});
3 changes: 3 additions & 0 deletions example/src/assets/bold.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions example/src/assets/formatting.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions example/src/assets/h1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions example/src/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import close from './close.svg';
import send from './send.svg';
import formatting from './formatting.svg';
import bold from './bold.svg';
import h1 from './h1.svg';

export const icons = {
close,
send,
formatting,
bold,
h1,
};

0 comments on commit 5891c89

Please sign in to comment.