import React, { useCallback, useEffect, useRef, useState } from 'react'; import { ScrollView, View } from 'react-native'; 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, StagingMeme } from '../../types'; import { pickSingle } from 'react-native-document-picker'; import { AndroidScoped, FileSystem } from 'react-native-file-access'; import { extension } from 'react-native-mime-types'; import { useSelector } from 'react-redux'; import { Meme } from '../../database'; import { allowedMimeTypes, deleteMeme, favoriteMeme, getMemeTypeFromMimeType, guessMimeType, noOp, validateMemeTitle, } from '../../utilities'; import { MemeEditor } from '../../components'; import editorStyles from './editorStyles'; import { RootState } from '../../state'; const EditMeme = ({ route, }: NativeStackScreenProps) => { const { goBack } = useNavigation(); 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.schema.name, BSON.UUID.createFromHexString(route.params.id), )!; const [isSaving, setIsSaving] = useState(false); const [uri, setUri] = useState(); const [mimeType, setMimeType] = useState(); const [loading, setLoading] = useState(true); const [error, setError] = useState(); const [staging, setStaging] = useState(); const originalStaging = useRef(); const resetState = useCallback( async (newUri: string) => { setLoading(true); // eslint-disable-next-line unicorn/no-useless-undefined setError(undefined); setUri(newUri); const guessedMimeType = await guessMimeType(newUri); if (!guessedMimeType) { setError( new Error('Could not determine MIME type or file is not supported.'), ); return; } setMimeType(guessedMimeType); const stagingMeme = { title: validateMemeTitle(meme.title), isFavorite: meme.isFavorite, tags: new Map(meme.tags.map(tag => [tag.id.toHexString(), tag])), }; setStaging(stagingMeme); originalStaging.current = stagingMeme; setLoading(false); }, [meme.isFavorite, meme.tags, meme.title], ); useEffect( () => void resetState(AndroidScoped.appendPath(storageUri, meme.filename)), [meme.filename, resetState, storageUri], ); const handleSave = useCallback(() => { if (!mimeType || !staging) return; setIsSaving(true); realm.write(() => { meme.tags.forEach(tag => { if (!staging.tags.has(tag.id.toHexString())) { tag.memes.slice(tag.memes.indexOf(meme), 1); tag.memesLength -= 1; tag.dateModified = new Date(); } }); staging.tags.forEach(tag => { if (!meme.tags.some(memeTag => memeTag.id.equals(tag.id))) { tag.memes.push(meme); tag.memesLength = tag.memes.length; tag.dateModified = new Date(); } }); meme.title = staging.title.parsed; // @ts-expect-error - Realm is a fuck meme.tags = [...staging.tags.values()]; meme.tagsLength = staging.tags.size; meme.dateModified = new Date(); }); goBack(); }, [goBack, meme, mimeType, realm, staging]); const handleDelete = useCallback(async () => { setIsSaving(true); await deleteMeme(realm, storageUri, meme); goBack(); }, [goBack, meme, realm, storageUri]); const handleFixUri = useCallback(async () => { const file = await pickSingle({ type: allowedMimeTypes }).catch(noOp); if (!file) return; const guessedMimeType = await guessMimeType(file.uri, file.type); if (!guessedMimeType) { setError( new Error('Could not determine MIME type or file is not supported.'), ); return; } const memeType = getMemeTypeFromMimeType(guessedMimeType); if (!memeType) return; const fileExtension = extension(guessedMimeType); if (!fileExtension) return; const filename = `${meme.id.toHexString()}-${ Date.now() / 1000 }.${fileExtension}`; const newUri = AndroidScoped.appendPath(storageUri, filename); await FileSystem.cp(file.uri, newUri); const { size } = await FileSystem.stat(newUri); realm.write(() => { meme.filename = filename; meme.memeType = memeType; meme.mimeType = guessedMimeType; meme.size = size; }); void resetState(newUri); }, [meme, realm, resetState, storageUri]); return ( <> goBack()} /> favoriteMeme(realm, meme)} /> {error?.message} ); }; export default EditMeme;