mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
fix backHandler in Editor
This commit is contained in:
@@ -98,7 +98,8 @@ export const NotebookPlaceHolder = ({colors, animation}) => {
|
|||||||
height: 8,
|
height: 8,
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
marginBottom: 15,
|
marginBottom: 15,
|
||||||
}}></View>
|
}}
|
||||||
|
/>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
fontFamily: WEIGHT.bold,
|
fontFamily: WEIGHT.bold,
|
||||||
@@ -153,7 +154,8 @@ export const NotebookPlaceHolder = ({colors, animation}) => {
|
|||||||
height: 8,
|
height: 8,
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
marginBottom: 15,
|
marginBottom: 15,
|
||||||
}}></View>
|
}}
|
||||||
|
/>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
fontFamily: WEIGHT.bold,
|
fontFamily: WEIGHT.bold,
|
||||||
@@ -489,104 +491,7 @@ export const TrashPlaceHolder = ({colors, animation}) => {
|
|||||||
style={{
|
style={{
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}>
|
}}>
|
||||||
<Animatable.View
|
<View
|
||||||
animation={deleteItems(-50, -100)}
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={3000}
|
|
||||||
iterationDelay={7000}
|
|
||||||
easing="ease-in"
|
|
||||||
useNativeDriver={true}
|
|
||||||
style={{
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
backgroundColor: colors.errorBg,
|
|
||||||
borderRadius: 5,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: -50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translateY: -100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Animatable.View
|
|
||||||
animation={deleteItems(-80, -70)}
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={3000}
|
|
||||||
iterationDelay={7000}
|
|
||||||
easing="ease-in"
|
|
||||||
useNativeDriver={true}
|
|
||||||
style={{
|
|
||||||
width: 25,
|
|
||||||
height: 25,
|
|
||||||
backgroundColor: colors.errorBg,
|
|
||||||
borderRadius: 5,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: -80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translateY: -70,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Animatable.View
|
|
||||||
animation={deleteItems(-120, -50)}
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={3000}
|
|
||||||
iterationDelay={7000}
|
|
||||||
easing="ease-in"
|
|
||||||
useNativeDriver={true}
|
|
||||||
style={{
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
backgroundColor: colors.errorBg,
|
|
||||||
borderRadius: 5,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: -120,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translateY: -50,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Animatable.View
|
|
||||||
animation={deleteItems(-120, -140)}
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={3000}
|
|
||||||
iterationDelay={7000}
|
|
||||||
easing="ease-in"
|
|
||||||
useNativeDriver={true}
|
|
||||||
style={{
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
backgroundColor: colors.errorBg,
|
|
||||||
borderRadius: 5,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: -120,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translateY: -140,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Animatable.View
|
|
||||||
animation={rotate}
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={3000}
|
|
||||||
iterationDelay={3500}
|
|
||||||
direction="alternate"
|
|
||||||
easing="ease-in"
|
|
||||||
useNativeDriver={true}
|
|
||||||
style={{
|
style={{
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 15,
|
height: 15,
|
||||||
@@ -607,7 +512,7 @@ export const TrashPlaceHolder = ({colors, animation}) => {
|
|||||||
marginTop: -9,
|
marginTop: -9,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Animatable.View>
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -623,14 +528,7 @@ export const TrashPlaceHolder = ({colors, animation}) => {
|
|||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
}}>
|
}}>
|
||||||
<Animatable.View
|
<View
|
||||||
animation={opacity}
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={3000}
|
|
||||||
iterationDelay={3500}
|
|
||||||
direction="alternate"
|
|
||||||
easing="ease-in"
|
|
||||||
useNativeDriver={true}
|
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
width: '90%',
|
width: '90%',
|
||||||
@@ -666,7 +564,7 @@ export const TrashPlaceHolder = ({colors, animation}) => {
|
|||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Animatable.View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { createRef, useEffect, useState } from 'react';
|
import React, {createRef, useEffect, useState} from 'react';
|
||||||
import {
|
import {
|
||||||
BackHandler,
|
BackHandler,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import WebView from 'react-native-webview';
|
import WebView from 'react-native-webview';
|
||||||
import { normalize, SIZE, WEIGHT } from '../../common/common';
|
import {normalize, SIZE, WEIGHT} from '../../common/common';
|
||||||
import {
|
import {
|
||||||
ActionSheetEvent,
|
ActionSheetEvent,
|
||||||
simpleDialogEvent,
|
simpleDialogEvent,
|
||||||
@@ -21,8 +21,8 @@ import {
|
|||||||
TEMPLATE_EXIT_FULLSCREEN,
|
TEMPLATE_EXIT_FULLSCREEN,
|
||||||
TEMPLATE_INFO,
|
TEMPLATE_INFO,
|
||||||
} from '../../components/DialogManager/templates';
|
} from '../../components/DialogManager/templates';
|
||||||
import { useTracked } from '../../provider';
|
import {useTracked} from '../../provider';
|
||||||
import { ACTIONS } from '../../provider/actions';
|
import {ACTIONS} from '../../provider/actions';
|
||||||
import {
|
import {
|
||||||
eSendEvent,
|
eSendEvent,
|
||||||
eSubscribeEvent,
|
eSubscribeEvent,
|
||||||
@@ -35,10 +35,10 @@ import {
|
|||||||
eOpenFullscreenEditor,
|
eOpenFullscreenEditor,
|
||||||
refreshNotesPage,
|
refreshNotesPage,
|
||||||
} from '../../services/events';
|
} from '../../services/events';
|
||||||
import { exitEditorAnimation } from '../../utils/animations';
|
import {exitEditorAnimation} from '../../utils/animations';
|
||||||
import { sideMenuRef } from '../../utils/refs';
|
import {sideMenuRef} from '../../utils/refs';
|
||||||
import { db, DDS, editing, timeConverter, ToastEvent } from '../../utils/utils';
|
import {db, DDS, editing, timeConverter, ToastEvent} from '../../utils/utils';
|
||||||
import { useSafeArea } from 'react-native-safe-area-context';
|
import {useSafeArea} from 'react-native-safe-area-context';
|
||||||
|
|
||||||
const EditorWebView = createRef();
|
const EditorWebView = createRef();
|
||||||
let note = {};
|
let note = {};
|
||||||
@@ -49,10 +49,12 @@ let timer = null;
|
|||||||
let saveCounter = 0;
|
let saveCounter = 0;
|
||||||
let tapCount = 0;
|
let tapCount = 0;
|
||||||
let canSave = false;
|
let canSave = false;
|
||||||
const Editor = ({ noMenu }) => {
|
let handleBack;
|
||||||
|
|
||||||
|
const Editor = ({noMenu}) => {
|
||||||
// Global State
|
// Global State
|
||||||
const [state, dispatch] = useTracked();
|
const [state, dispatch] = useTracked();
|
||||||
const { colors } = state;
|
const {colors} = state;
|
||||||
const [fullscreen, setFullscreen] = useState(false);
|
const [fullscreen, setFullscreen] = useState(false);
|
||||||
const [dateEdited, setDateEdited] = useState(0);
|
const [dateEdited, setDateEdited] = useState(0);
|
||||||
const insets = useSafeArea();
|
const insets = useSafeArea();
|
||||||
@@ -62,7 +64,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
EditorWebView.current?.postMessage(JSON.stringify(message));
|
EditorWebView.current?.postMessage(JSON.stringify(message));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let c = { ...colors };
|
let c = {...colors};
|
||||||
c.factor = normalize(1);
|
c.factor = normalize(1);
|
||||||
post({
|
post({
|
||||||
type: 'theme',
|
type: 'theme',
|
||||||
@@ -80,9 +82,10 @@ const Editor = ({ noMenu }) => {
|
|||||||
|
|
||||||
const loadNote = async item => {
|
const loadNote = async item => {
|
||||||
//EditorWebView.current?.requestFocus();
|
//EditorWebView.current?.requestFocus();
|
||||||
|
editing.currentlyEditing = true;
|
||||||
noMenu ? null : sideMenuRef.current?.setGestureEnabled(false);
|
noMenu ? null : sideMenuRef.current?.setGestureEnabled(false);
|
||||||
if (note && note.id) {
|
if (note && note.id) {
|
||||||
dispatch({ type: ACTIONS.NOTES });
|
dispatch({type: ACTIONS.NOTES});
|
||||||
if (item && item.type === 'new') {
|
if (item && item.type === 'new') {
|
||||||
await clearEditor();
|
await clearEditor();
|
||||||
post({
|
post({
|
||||||
@@ -99,7 +102,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
updateEditor();
|
updateEditor();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: ACTIONS.NOTES });
|
dispatch({type: ACTIONS.NOTES});
|
||||||
if (item && item.type === 'new') {
|
if (item && item.type === 'new') {
|
||||||
await clearEditor();
|
await clearEditor();
|
||||||
post({
|
post({
|
||||||
@@ -142,22 +145,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
const onCallClear = () => {
|
const onCallClear = () => {
|
||||||
canSave = false;
|
canSave = false;
|
||||||
exitEditorAnimation();
|
exitEditorAnimation();
|
||||||
setDateEdited(0);
|
clearEditor();
|
||||||
title = null;
|
|
||||||
content = null;
|
|
||||||
note = null;
|
|
||||||
id = null;
|
|
||||||
tapCount = 0;
|
|
||||||
saveCounter = 0;
|
|
||||||
post({
|
|
||||||
type: 'clearEditor',
|
|
||||||
});
|
|
||||||
post({
|
|
||||||
type: 'clearTitle',
|
|
||||||
});
|
|
||||||
post({
|
|
||||||
type: 'blur',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -251,13 +239,19 @@ const Editor = ({ noMenu }) => {
|
|||||||
const saveNote = async (lockNote = true) => {
|
const saveNote = async (lockNote = true) => {
|
||||||
if (!canSave) return;
|
if (!canSave) return;
|
||||||
if (!title && !content) return;
|
if (!title && !content) return;
|
||||||
if (content && content.text.trim().length === 0 && title && title.trim().length == 0) return;
|
if (
|
||||||
|
content &&
|
||||||
|
content.text.trim().length === 0 &&
|
||||||
|
title &&
|
||||||
|
title.trim().length == 0
|
||||||
|
)
|
||||||
|
return;
|
||||||
if (!content && title && title.trim().length === 0) return;
|
if (!content && title && title.trim().length === 0) return;
|
||||||
if (!title && content && content.text.trim.length === 0) return;
|
if (!title && content && content.text.trim.length === 0) return;
|
||||||
if (!content) {
|
if (!content) {
|
||||||
content = {
|
content = {
|
||||||
text: '',
|
text: '',
|
||||||
delta: { ops: [] },
|
delta: {ops: []},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +343,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
type: 'focusTitle',
|
type: 'focusTitle',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let c = { ...colors };
|
let c = {...colors};
|
||||||
c.factor = normalize(1);
|
c.factor = normalize(1);
|
||||||
post({
|
post({
|
||||||
type: 'theme',
|
type: 'theme',
|
||||||
@@ -361,7 +355,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
title = note.title;
|
title = note.title;
|
||||||
id = note.id;
|
id = note.id;
|
||||||
setDateEdited(note.dateEdited);
|
setDateEdited(note.dateEdited);
|
||||||
content = { ...note.content };
|
content = {...note.content};
|
||||||
saveCounter = 0;
|
saveCounter = 0;
|
||||||
|
|
||||||
if (title !== null || title === '') {
|
if (title !== null || title === '') {
|
||||||
@@ -396,7 +390,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
value: delta,
|
value: delta,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
post({ type: 'text', value: note.content.text });
|
post({type: 'text', value: note.content.text});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -425,44 +419,39 @@ const Editor = ({ noMenu }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const _onHardwareBackPress = async () => {
|
const _onHardwareBackPress = async () => {
|
||||||
if (tapCount > 0) {
|
if (editing.currentlyEditing) {
|
||||||
exitEditorAnimation();
|
if (tapCount > 0) {
|
||||||
if (note && note.id) {
|
exitEditorAnimation();
|
||||||
ToastEvent.show('Note Saved!', 'success');
|
if (note && note.id) {
|
||||||
|
ToastEvent.show('Note Saved!', 'success');
|
||||||
|
}
|
||||||
|
await clearEditor();
|
||||||
|
if (handleBack) {
|
||||||
|
handleBack.remove();
|
||||||
|
handleBack = null;
|
||||||
|
}
|
||||||
|
if (noMenu) return true;
|
||||||
|
DDS.isTab ? sideMenuRef.current?.openMenu(true) : null;
|
||||||
|
sideMenuRef.current?.setGestureEnabled(true);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
tapCount = 1;
|
||||||
|
setTimeout(() => {
|
||||||
|
tapCount = 0;
|
||||||
|
}, 3000);
|
||||||
|
ToastEvent.show('Press back again to exit editor', 'success');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
await clearEditor();
|
|
||||||
if (noMenu) return true;
|
|
||||||
DDS.isTab ? sideMenuRef.current?.openMenu(true) : null;
|
|
||||||
sideMenuRef.current?.setGestureEnabled(true);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
tapCount = 1;
|
|
||||||
setTimeout(() => {
|
|
||||||
tapCount = 0;
|
|
||||||
}, 3000);
|
|
||||||
ToastEvent.show('Press back again to exit editor', 'success');
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let handleBack;
|
|
||||||
if (!noMenu && DDS.isTab) {
|
if (!noMenu && DDS.isTab) {
|
||||||
handleBack = BackHandler.addEventListener('hardwareBackPress', () => {
|
handleBack = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||||
simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN());
|
simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN());
|
||||||
editing.isFullscreen = false;
|
editing.isFullscreen = false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else if (!DDS.isTab) {
|
|
||||||
handleBack = BackHandler.addEventListener(
|
|
||||||
'hardwareBackPress',
|
|
||||||
_onHardwareBackPress,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (handleBack) {
|
|
||||||
handleBack.remove();
|
|
||||||
handleBack = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -470,14 +459,24 @@ const Editor = ({ noMenu }) => {
|
|||||||
handleBack.remove();
|
handleBack.remove();
|
||||||
handleBack = null;
|
handleBack = null;
|
||||||
}
|
}
|
||||||
title = null;
|
|
||||||
content = null;
|
|
||||||
id = null;
|
|
||||||
timer = null;
|
|
||||||
note = {};
|
|
||||||
};
|
};
|
||||||
}, [noMenu]);
|
}, [noMenu]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!DDS.isTab) {
|
||||||
|
handleBack = BackHandler.addEventListener(
|
||||||
|
'hardwareBackPress',
|
||||||
|
_onHardwareBackPress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (handleBack) {
|
||||||
|
handleBack.remove();
|
||||||
|
handleBack = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView
|
||||||
style={{
|
style={{
|
||||||
@@ -497,11 +496,13 @@ const Editor = ({ noMenu }) => {
|
|||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
marginTop: Platform.OS === 'ios' ? 0 : StatusBar.currentHeight,
|
marginTop: Platform.OS === 'ios' ? 0 : StatusBar.currentHeight,
|
||||||
}}></View>
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{noMenu ? null : (
|
{noMenu ? null : (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
|
editing.currentlyEditing = true;
|
||||||
if (DDS.isTab) {
|
if (DDS.isTab) {
|
||||||
simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN());
|
simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN());
|
||||||
} else {
|
} else {
|
||||||
@@ -512,6 +513,10 @@ const Editor = ({ noMenu }) => {
|
|||||||
await clearEditor();
|
await clearEditor();
|
||||||
DDS.isTab ? sideMenuRef.current?.openMenu(true) : null;
|
DDS.isTab ? sideMenuRef.current?.openMenu(true) : null;
|
||||||
sideMenuRef.current?.setGestureEnabled(true);
|
sideMenuRef.current?.setGestureEnabled(true);
|
||||||
|
if (handleBack) {
|
||||||
|
handleBack.remove();
|
||||||
|
handleBack = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
@@ -597,8 +602,7 @@ const Editor = ({ noMenu }) => {
|
|||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
marginTop:
|
marginTop: Platform.OS === 'ios' ? 45 : insets.top + 45,
|
||||||
Platform.OS === 'ios' ? 45 : insets.top + 45,
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
@@ -660,11 +664,11 @@ const Editor = ({ noMenu }) => {
|
|||||||
originWhitelist={['*']}
|
originWhitelist={['*']}
|
||||||
source={
|
source={
|
||||||
Platform.OS === 'ios'
|
Platform.OS === 'ios'
|
||||||
? { uri: sourceUri }
|
? {uri: sourceUri}
|
||||||
: {
|
: {
|
||||||
uri: 'file:///android_asset/texteditor.html',
|
uri: 'file:///android_asset/texteditor.html',
|
||||||
baseUrl: 'file:///android_asset/',
|
baseUrl: 'file:///android_asset/',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
style={{
|
style={{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|||||||
@@ -167,14 +167,14 @@ export const Notes = ({navigation}) => {
|
|||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
bottomButtonText="Create a new note"
|
bottomButtonText="Create a new note"
|
||||||
canGoBack={false}
|
canGoBack={params.type === 'color' ? false : true}
|
||||||
|
menu={params.type === 'color' ? true : false}
|
||||||
heading={
|
heading={
|
||||||
params.type == 'tag'
|
params.type == 'tag'
|
||||||
? '#' + params.title
|
? '#' + params.title
|
||||||
: params.title.slice(0, 1).toUpperCase() + params.title.slice(1)
|
: params.title.slice(0, 1).toUpperCase() + params.title.slice(1)
|
||||||
}
|
}
|
||||||
headerColor={params.type == 'color' ? params.title : null}
|
headerColor={params.type == 'color' ? params.title : null}
|
||||||
canGoBack={true}
|
|
||||||
data={notes}
|
data={notes}
|
||||||
type="notes"
|
type="notes"
|
||||||
placeholder={`Search in ${
|
placeholder={`Search in ${
|
||||||
|
|||||||
Reference in New Issue
Block a user