Add pasting from clipboard

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-29 22:43:58 +03:00
parent 391e232bf7
commit 5770a9b234
8 changed files with 388 additions and 64 deletions

View File

@@ -1,12 +1,18 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { Keyboard, StyleSheet } from 'react-native';
import { FAB } from 'react-native-paper';
import { FAB, Snackbar } from 'react-native-paper';
import { ParamListBase, useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { pick } from 'react-native-document-picker';
import { useDeviceOrientation } from '@react-native-community/hooks';
import Clipboard from '@react-native-clipboard/clipboard';
import { documentPickerResponseToAddMemeFile, ROUTE } from '../types';
import { allowedMimeTypes, noOp } from '../utilities';
import {
allowedMimeTypes,
getFilenameFromUri,
guessMimeType,
noOp,
} from '../utilities';
const floatingActionButtonStyles = StyleSheet.create({
fab: {
@@ -17,6 +23,9 @@ const floatingActionButtonStyles = StyleSheet.create({
paddingBottom: 40,
paddingRight: 12,
},
snackbar: {
marginBottom: 90,
},
});
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
@@ -27,6 +36,8 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
const [state, setState] = useState(false);
const [keyboardOpen, setKeyboardOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState<string>();
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
@@ -43,35 +54,85 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
};
}, []);
return (
<FAB.Group
open={state}
visible={visible && !keyboardOpen}
icon={state ? 'image' : 'plus'}
actions={[
const handleAddMeme = useCallback(async () => {
const response = await pick({
type: allowedMimeTypes,
allowMultiSelection: true,
}).catch(noOp);
if (!response) return;
const files = documentPickerResponseToAddMemeFile(response);
navigate(ROUTE.ADD_MEME, { files });
}, [navigate]);
const handleAddTag = useCallback(() => {
navigate(ROUTE.ADD_TAG);
}, [navigate]);
const handlePaste = useCallback(async () => {
const uri = await Clipboard.getURI();
if (!uri) {
setSnackbarMessage('Clipboard does not contain a URI.');
return;
}
const mimeType = guessMimeType(uri);
if (!mimeType) {
setSnackbarMessage('Unsupported MIME type.');
return;
}
navigate(ROUTE.ADD_MEME, {
files: [
{
icon: 'tag',
label: 'Tag',
onPress: () => navigate(ROUTE.ADD_TAG),
uri: uri,
filename: getFilenameFromUri(uri),
type: mimeType,
},
]}
onStateChange={({ open }) => setState(open)}
onPress={async () => {
if (!state) return;
const response = await pick({
type: allowedMimeTypes,
allowMultiSelection: true,
}).catch(noOp);
if (!response) return;
const files = documentPickerResponseToAddMemeFile(response);
navigate(ROUTE.ADD_MEME, { files });
}}
style={
orientation === 'portrait'
? floatingActionButtonStyles.fab
: floatingActionButtonStyles.fabLandscape
}
/>
],
});
}, [navigate]);
return (
<>
<FAB.Group
open={state}
visible={visible && !keyboardOpen}
icon={state ? 'close' : 'plus'}
actions={[
{
icon: 'content-paste',
label: 'Paste',
onPress: handlePaste,
},
{
icon: 'tag',
label: 'Tag',
onPress: handleAddTag,
},
{
icon: 'image',
label: 'Meme',
onPress: handleAddMeme,
},
]}
onStateChange={({ open }) => setState(open)}
style={
orientation === 'portrait'
? floatingActionButtonStyles.fab
: floatingActionButtonStyles.fabLandscape
}
/>
<Snackbar
visible={!!snackbarMessage}
// eslint-disable-next-line unicorn/no-useless-undefined
onDismiss={() => setSnackbarMessage(undefined)}
style={floatingActionButtonStyles.snackbar}
action={{
label: 'Dismiss',
// eslint-disable-next-line unicorn/no-useless-undefined
onPress: () => setSnackbarMessage(undefined),
}}>
{snackbarMessage}
</Snackbar>
</>
);
};