Add broken URI handling

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-26 16:10:45 +03:00
parent 083e798fdf
commit acba17462f
17 changed files with 370 additions and 91 deletions

View File

@@ -1,16 +1,29 @@
import React, { useCallback, useState } from 'react';
import { ScrollView, View } from 'react-native';
import { Appbar, Button, useTheme } from 'react-native-paper';
import { Appbar, Banner, Button, useTheme } from 'react-native-paper';
import { useNavigation } from '@react-navigation/native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useObject, useRealm } from '@realm/react';
import { useDeviceOrientation } from '@react-native-community/hooks';
import { BSON } from 'realm';
import { RootStackParamList, ROUTE } from '../../types';
import { pickSingle } from 'react-native-document-picker';
import { AndroidScoped, FileSystem } from 'react-native-file-access';
import { Tag, Meme } from '../../database';
import { deleteMeme, favoriteMeme, validateMemeTitle } from '../../utilities';
import {
StringValidationResult,
allowedMimeTypes,
deleteMeme,
favoriteMeme,
getMemeType,
noOp,
validateMemeTitle,
} from '../../utilities';
import { MemeEditor } from '../../components';
import editorStyles from './editorStyles';
import { extension } from 'react-native-mime-types';
import { useSelector } from 'react-redux';
import { RootState } from '../../state';
const EditMeme = ({
route,
@@ -19,6 +32,10 @@ const EditMeme = ({
const { colors } = useTheme();
const orientation = useDeviceOrientation();
const realm = useRealm();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const storageUri = useSelector(
(state: RootState) => state.settings.storageUri,
)!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const meme = useObject<Meme>(
@@ -26,11 +43,24 @@ const EditMeme = ({
BSON.UUID.createFromHexString(route.params.id),
)!;
const [hasChanges, setHasChanges] = useState(false);
const [memeUriError, setMemeUriError] = useState<Error>();
const [memeTitle, setMemeTitle] = useState(validateMemeTitle(meme.title));
const [memeTags, setMemeTags] = useState(
new Map<string, Tag>(meme.tags.map(tag => [tag.id.toHexString(), tag])),
);
const handleMemeTitleChange = useCallback((title: StringValidationResult) => {
setMemeTitle(title);
setHasChanges(true);
}, []);
const handleMemeTagsChange = useCallback((tags: Map<string, Tag>) => {
setMemeTags(tags);
setHasChanges(true);
}, []);
const [isSaving, setIsSaving] = useState(false);
const handleSave = useCallback(() => {
@@ -59,6 +89,34 @@ const EditMeme = ({
});
}, [meme, memeTags, memeTitle.parsed, realm]);
const handleFixUri = useCallback(async () => {
const file = await pickSingle({ type: allowedMimeTypes }).catch(noOp);
if (!file) return;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const mimeType = file.type!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const memeType = getMemeType(mimeType)!;
const fileExtension = extension(mimeType) as string;
if (!fileExtension) return;
const uri = AndroidScoped.appendPath(
storageUri,
`${meme.id.toHexString()}-${Date.now() / 1000}.${fileExtension}`,
);
await FileSystem.cp(file.uri, uri);
const { size } = await FileSystem.stat(uri);
realm.write(() => {
meme.uri = uri;
meme.type = memeType;
meme.mimeType = mimeType;
meme.size = size;
});
}, [meme, realm, storageUri]);
return (
<>
<Appbar.Header>
@@ -78,6 +136,26 @@ const EditMeme = ({
}}
/>
</Appbar.Header>
<Banner
visible={!!memeUriError}
actions={[
{
label: 'Fix URI',
onPress: handleFixUri,
},
{
label: 'Delete Meme',
onPress: async () => {
setIsSaving(true);
await deleteMeme(realm, meme);
setIsSaving(false);
goBack();
},
},
]}>
The URI for this meme appears to be broken. This may have been caused by
the file being moved or deleted.
</Banner>
<ScrollView
contentContainerStyle={[
editorStyles.scrollView,
@@ -89,10 +167,12 @@ const EditMeme = ({
<View style={editorStyles.editorView}>
<MemeEditor
memeUri={meme.uri}
memeUriError={memeUriError}
setMemeUriError={setMemeUriError}
memeTitle={memeTitle}
setMemeTitle={setMemeTitle}
setMemeTitle={handleMemeTitleChange}
memeTags={memeTags}
setMemeTags={setMemeTags}
setMemeTags={handleMemeTagsChange}
/>
</View>
<View style={editorStyles.saveButtonView}>
@@ -105,8 +185,11 @@ const EditMeme = ({
setIsSaving(false);
goBack();
}}
disabled={!memeTitle.valid || isSaving}
loading={isSaving}>
disabled={
!memeTitle.valid || !hasChanges || isSaving || !!memeUriError
}
loading={isSaving}
style={editorStyles.soloSaveButton}>
Save
</Button>
</View>