Refactor editor architecture
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
documentPickerResponseToAddMemeFile,
|
||||
ROUTE,
|
||||
RootStackParamList,
|
||||
StagingMeme,
|
||||
} from '../../types';
|
||||
import { Meme, Tag } from '../../database';
|
||||
import { RootState } from '../../state';
|
||||
@@ -43,92 +44,83 @@ const AddMeme = ({
|
||||
const file = useRef(files.current[index]);
|
||||
const isLastFile = index === files.current.length - 1;
|
||||
|
||||
const [memeLoading, setMemeLoading] = useState(true);
|
||||
const [memeError, setMemeError] = useState<Error>();
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isSavingAndAddingMore, setIsSavingAndAddingMore] = useState(false);
|
||||
|
||||
const [memeUri, setMemeUri] = useState(file.current.uri);
|
||||
const [memeFilename, setMemeFilename] = useState(file.current.filename);
|
||||
const [memeMimeType, setMemeMimeType] = useState<string>();
|
||||
const [memeTitle, setMemeTitle] = useState(validateMemeTitle('New Meme'));
|
||||
const [memeIsFavorite, setMemeIsFavorite] = useState(false);
|
||||
const [memeTags, setMemeTags] = useState(new Map<string, Tag>());
|
||||
const [uri, setUri] = useState<string>();
|
||||
const [mimeType, setMimeType] = useState<string>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error>();
|
||||
const [staging, setStaging] = useState<StagingMeme>();
|
||||
|
||||
const resetState = useCallback(async (newIndex = 0) => {
|
||||
setMemeLoading(true);
|
||||
const resetState = useCallback(async (newIndex: number) => {
|
||||
setLoading(true);
|
||||
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||
setMemeError(undefined);
|
||||
setError(undefined);
|
||||
|
||||
setIndex(newIndex);
|
||||
file.current = files.current[newIndex];
|
||||
|
||||
setMemeUri(file.current.uri);
|
||||
setMemeFilename(file.current.filename);
|
||||
setMemeTitle(validateMemeTitle('New Meme'));
|
||||
setMemeIsFavorite(false);
|
||||
setMemeTags(new Map<string, Tag>());
|
||||
setUri(file.current.uri);
|
||||
|
||||
const mimeType = await guessMimeType(file.current.uri);
|
||||
if (!mimeType) {
|
||||
setMemeError(
|
||||
const guessedMimeType = await guessMimeType(file.current.uri);
|
||||
if (!guessedMimeType) {
|
||||
setError(
|
||||
new Error('Could not determine MIME type or file is not supported.'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setMemeMimeType(mimeType);
|
||||
setMimeType(guessedMimeType);
|
||||
|
||||
setMemeLoading(false);
|
||||
setStaging({
|
||||
title: validateMemeTitle('New Meme'),
|
||||
isFavorite: false,
|
||||
tags: new Map<string, Tag>(),
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => void resetState(), [resetState]);
|
||||
useEffect(() => void resetState(0), [resetState]);
|
||||
|
||||
const saveMeme = useCallback(async () => {
|
||||
if (!memeMimeType) return;
|
||||
if (!mimeType || !staging) return;
|
||||
const uuid = new BSON.UUID();
|
||||
|
||||
const memeType = getMemeTypeFromMimeType(memeMimeType);
|
||||
const memeType = getMemeTypeFromMimeType(mimeType);
|
||||
if (!memeType) return;
|
||||
|
||||
const fileExtension = extension(memeMimeType);
|
||||
const fileExtension = extension(mimeType);
|
||||
if (!fileExtension) return;
|
||||
|
||||
const filename = `${uuid.toHexString()}-${Math.round(
|
||||
Date.now() / 1000,
|
||||
)}.${fileExtension}`;
|
||||
const uri = AndroidScoped.appendPath(storageUri, filename);
|
||||
const finalUri = AndroidScoped.appendPath(storageUri, filename);
|
||||
|
||||
await FileSystem.cp(file.current.uri, uri);
|
||||
const { size } = await FileSystem.stat(uri);
|
||||
await FileSystem.cp(file.current.uri, finalUri);
|
||||
const { size } = await FileSystem.stat(finalUri);
|
||||
|
||||
realm.write(() => {
|
||||
const meme: Meme | undefined = realm.create<Meme>(Meme.schema.name, {
|
||||
id: uuid,
|
||||
memeType,
|
||||
filename,
|
||||
mimeType: memeMimeType,
|
||||
mimeType: mimeType,
|
||||
size,
|
||||
title: memeTitle.parsed,
|
||||
isFavorite: memeIsFavorite,
|
||||
tags: [...memeTags.values()],
|
||||
tagsLength: memeTags.size,
|
||||
title: staging.title.parsed,
|
||||
isFavorite: staging.isFavorite,
|
||||
tags: [...staging.tags.values()],
|
||||
tagsLength: staging.tags.size,
|
||||
});
|
||||
|
||||
memeTags.forEach(tag => {
|
||||
staging.tags.forEach(tag => {
|
||||
tag.dateModified = new Date();
|
||||
tag.memes.push(meme);
|
||||
tag.memesLength = tag.memes.length;
|
||||
});
|
||||
});
|
||||
}, [
|
||||
memeIsFavorite,
|
||||
memeMimeType,
|
||||
memeTags,
|
||||
memeTitle.parsed,
|
||||
realm,
|
||||
storageUri,
|
||||
]);
|
||||
}, [mimeType, realm, staging, storageUri]);
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
setIsSaving(true);
|
||||
@@ -162,20 +154,23 @@ const AddMeme = ({
|
||||
<Appbar.BackAction onPress={() => goBack()} />
|
||||
<Appbar.Content title={'Add Meme'} />
|
||||
<Appbar.Action
|
||||
icon={memeIsFavorite ? 'heart' : 'heart-outline'}
|
||||
onPress={() => setMemeIsFavorite(!memeIsFavorite)}
|
||||
icon={staging?.isFavorite ? 'heart' : 'heart-outline'}
|
||||
disabled={!staging}
|
||||
onPress={() =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
setStaging({ ...staging!, isFavorite: !staging!.isFavorite })
|
||||
}
|
||||
/>
|
||||
</Appbar.Header>
|
||||
<Banner
|
||||
visible={!!memeError}
|
||||
visible={!!error}
|
||||
actions={[
|
||||
{
|
||||
label: 'Cancel',
|
||||
onPress: goBack,
|
||||
},
|
||||
]}>
|
||||
The selected URI appears to be broken. This may have been caused by the
|
||||
file being corrupted or unsupported.
|
||||
{error?.message}
|
||||
</Banner>
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
@@ -187,14 +182,14 @@ const AddMeme = ({
|
||||
]}>
|
||||
<View style={editorStyles.editorView}>
|
||||
<MemeEditor
|
||||
memeUri={memeUri}
|
||||
memeFilename={memeFilename}
|
||||
memeError={memeError}
|
||||
setMemeError={setMemeError}
|
||||
memeTitle={memeTitle}
|
||||
setMemeTitle={setMemeTitle}
|
||||
memeTags={memeTags}
|
||||
setMemeTags={setMemeTags}
|
||||
uri={uri}
|
||||
mimeType={mimeType}
|
||||
loading={loading}
|
||||
setLoading={setLoading}
|
||||
error={error}
|
||||
setError={setError}
|
||||
staging={staging}
|
||||
setStaging={setStaging}
|
||||
/>
|
||||
</View>
|
||||
<View style={editorStyles.saveButtonView}>
|
||||
@@ -203,11 +198,11 @@ const AddMeme = ({
|
||||
icon="plus"
|
||||
onPress={handleSaveAndAddMore}
|
||||
disabled={
|
||||
memeLoading ||
|
||||
!!memeError ||
|
||||
loading ||
|
||||
!!error ||
|
||||
isSaving ||
|
||||
isSavingAndAddingMore ||
|
||||
!memeTitle.valid ||
|
||||
!staging?.title.valid ||
|
||||
!isLastFile
|
||||
}
|
||||
loading={isSavingAndAddingMore}
|
||||
@@ -219,11 +214,11 @@ const AddMeme = ({
|
||||
icon="floppy"
|
||||
onPress={isLastFile ? handleSave : handleSaveAndNext}
|
||||
disabled={
|
||||
memeLoading ||
|
||||
!!memeError ||
|
||||
loading ||
|
||||
!!error ||
|
||||
isSaving ||
|
||||
isSavingAndAddingMore ||
|
||||
!memeTitle.valid
|
||||
!staging?.title.valid
|
||||
}
|
||||
loading={isSaving}
|
||||
style={editorStyles.saveButton}>
|
||||
|
@@ -12,6 +12,7 @@ import {
|
||||
import { Tag } from '../../database';
|
||||
import { TagEditor } from '../../components';
|
||||
import editorStyles from './editorStyles';
|
||||
import { StagingTag } from '../../types';
|
||||
|
||||
const AddTag = () => {
|
||||
const { goBack } = useNavigation();
|
||||
@@ -19,10 +20,10 @@ const AddTag = () => {
|
||||
const orientation = useDeviceOrientation();
|
||||
const realm = useRealm();
|
||||
|
||||
const [tagName, setTagName] = useState(validateTagName('newTag'));
|
||||
const [tagColor, setTagColor] = useState(
|
||||
validateColor(generateRandomColor()),
|
||||
);
|
||||
const [staging, setStaging] = useState<StagingTag>({
|
||||
name: validateTagName('newTag'),
|
||||
color: validateColor(generateRandomColor()),
|
||||
});
|
||||
|
||||
// Although saving tags is instantaneous, we still want to show a loading
|
||||
// indicator to prevent the user from spamming the save button.
|
||||
@@ -31,11 +32,11 @@ const AddTag = () => {
|
||||
const saveTag = useCallback(() => {
|
||||
realm.write(() => {
|
||||
realm.create(Tag.schema.name, {
|
||||
name: tagName.parsed,
|
||||
color: tagColor.parsed,
|
||||
name: staging.name.parsed,
|
||||
color: staging.color.parsed,
|
||||
});
|
||||
});
|
||||
}, [realm, tagColor.parsed, tagName.parsed]);
|
||||
}, [realm, staging.color.parsed, staging.name.parsed]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
saveTag();
|
||||
@@ -46,8 +47,10 @@ const AddTag = () => {
|
||||
setIsSavingAndAddingMore(true);
|
||||
saveTag();
|
||||
setTimeout(() => setIsSavingAndAddingMore(false), 250);
|
||||
setTagName(validateTagName('newTag'));
|
||||
setTagColor(validateColor(generateRandomColor()));
|
||||
setStaging({
|
||||
name: validateTagName('newTag'),
|
||||
color: validateColor(generateRandomColor()),
|
||||
});
|
||||
}, [saveTag]);
|
||||
|
||||
return (
|
||||
@@ -65,19 +68,14 @@ const AddTag = () => {
|
||||
{ backgroundColor: colors.background },
|
||||
]}>
|
||||
<View style={editorStyles.editorView}>
|
||||
<TagEditor
|
||||
tagName={tagName}
|
||||
setTagName={setTagName}
|
||||
tagColor={tagColor}
|
||||
setTagColor={setTagColor}
|
||||
/>
|
||||
<TagEditor staging={staging} setStaging={setStaging} />
|
||||
</View>
|
||||
<View style={editorStyles.saveButtonView}>
|
||||
<Button
|
||||
mode="contained-tonal"
|
||||
icon="plus"
|
||||
onPress={handleSaveAndAddMore}
|
||||
disabled={!tagName.valid || isSavingAndAddingMore}
|
||||
disabled={!staging.name.valid || isSavingAndAddingMore}
|
||||
loading={isSavingAndAddingMore}
|
||||
style={editorStyles.saveAndAddButton}>
|
||||
Save & Add
|
||||
@@ -86,7 +84,7 @@ const AddTag = () => {
|
||||
mode="contained"
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
disabled={!tagName.valid || isSavingAndAddingMore}
|
||||
disabled={!staging.name.valid || isSavingAndAddingMore}
|
||||
style={editorStyles.saveButton}>
|
||||
Save
|
||||
</Button>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
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';
|
||||
@@ -6,14 +6,13 @@ 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 { 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 { Tag, Meme } from '../../database';
|
||||
import { Meme } from '../../database';
|
||||
import {
|
||||
StringValidationResult,
|
||||
allowedMimeTypes,
|
||||
deleteMeme,
|
||||
favoriteMeme,
|
||||
@@ -43,42 +42,67 @@ const EditMeme = ({
|
||||
Meme.schema.name,
|
||||
BSON.UUID.createFromHexString(route.params.id),
|
||||
)!;
|
||||
const uri = AndroidScoped.appendPath(storageUri, meme.filename);
|
||||
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
|
||||
const [memeError, setMemeError] = 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 [uri, setUri] = useState<string>();
|
||||
const [mimeType, setMimeType] = useState<string>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error>();
|
||||
const [staging, setStaging] = useState<StagingMeme>();
|
||||
const originalStaging = useRef<StagingMeme>();
|
||||
|
||||
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 (!memeTags.has(tag.id.toHexString())) {
|
||||
if (!staging.tags.has(tag.id.toHexString())) {
|
||||
tag.memes.slice(tag.memes.indexOf(meme), 1);
|
||||
tag.memesLength -= 1;
|
||||
tag.dateModified = new Date();
|
||||
}
|
||||
});
|
||||
|
||||
memeTags.forEach(tag => {
|
||||
staging.tags.forEach(tag => {
|
||||
if (!meme.tags.some(memeTag => memeTag.id.equals(tag.id))) {
|
||||
tag.memes.push(meme);
|
||||
tag.memesLength = tag.memes.length;
|
||||
@@ -86,15 +110,15 @@ const EditMeme = ({
|
||||
}
|
||||
});
|
||||
|
||||
meme.title = memeTitle.parsed;
|
||||
meme.title = staging.title.parsed;
|
||||
// @ts-expect-error - Realm is a fuck
|
||||
meme.tags = [...memeTags.values()];
|
||||
meme.tagsLength = memeTags.size;
|
||||
meme.tags = [...staging.tags.values()];
|
||||
meme.tagsLength = staging.tags.size;
|
||||
meme.dateModified = new Date();
|
||||
});
|
||||
|
||||
goBack();
|
||||
}, [goBack, meme, memeTags, memeTitle.parsed, realm]);
|
||||
}, [goBack, meme, mimeType, realm, staging]);
|
||||
|
||||
const handleDelete = useCallback(async () => {
|
||||
setIsSaving(true);
|
||||
@@ -106,18 +130,18 @@ const EditMeme = ({
|
||||
const file = await pickSingle({ type: allowedMimeTypes }).catch(noOp);
|
||||
if (!file) return;
|
||||
|
||||
const mimeType = await guessMimeType(file.uri, file.type);
|
||||
if (!mimeType) {
|
||||
setMemeError(
|
||||
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(mimeType);
|
||||
const memeType = getMemeTypeFromMimeType(guessedMimeType);
|
||||
if (!memeType) return;
|
||||
|
||||
const fileExtension = extension(mimeType) as string;
|
||||
const fileExtension = extension(guessedMimeType);
|
||||
if (!fileExtension) return;
|
||||
|
||||
const filename = `${meme.id.toHexString()}-${
|
||||
@@ -131,10 +155,12 @@ const EditMeme = ({
|
||||
realm.write(() => {
|
||||
meme.filename = filename;
|
||||
meme.memeType = memeType;
|
||||
meme.mimeType = mimeType;
|
||||
meme.mimeType = guessedMimeType;
|
||||
meme.size = size;
|
||||
});
|
||||
}, [meme, realm, storageUri]);
|
||||
|
||||
void resetState(newUri);
|
||||
}, [meme, realm, resetState, storageUri]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -148,7 +174,7 @@ const EditMeme = ({
|
||||
<Appbar.Action icon="delete" onPress={handleDelete} />
|
||||
</Appbar.Header>
|
||||
<Banner
|
||||
visible={!!memeError}
|
||||
visible={!!error}
|
||||
actions={[
|
||||
{
|
||||
label: 'Fix URI',
|
||||
@@ -159,8 +185,7 @@ const EditMeme = ({
|
||||
onPress: handleDelete,
|
||||
},
|
||||
]}>
|
||||
The URI for this meme appears to be broken. This may have been caused by
|
||||
the file being moved or deleted.
|
||||
{error?.message}
|
||||
</Banner>
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
@@ -172,14 +197,14 @@ const EditMeme = ({
|
||||
]}>
|
||||
<View style={editorStyles.editorView}>
|
||||
<MemeEditor
|
||||
memeUri={uri}
|
||||
memeFilename={meme.filename}
|
||||
memeError={memeError}
|
||||
setMemeError={setMemeError}
|
||||
memeTitle={memeTitle}
|
||||
setMemeTitle={handleMemeTitleChange}
|
||||
memeTags={memeTags}
|
||||
setMemeTags={handleMemeTagsChange}
|
||||
uri={uri}
|
||||
mimeType={mimeType}
|
||||
loading={loading}
|
||||
setLoading={setLoading}
|
||||
error={error}
|
||||
setError={setError}
|
||||
staging={staging}
|
||||
setStaging={setStaging}
|
||||
/>
|
||||
</View>
|
||||
<View style={editorStyles.saveButtonView}>
|
||||
@@ -188,7 +213,11 @@ const EditMeme = ({
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
disabled={
|
||||
!memeTitle.valid || !hasChanges || isSaving || !!memeError
|
||||
loading ||
|
||||
!!error ||
|
||||
isSaving ||
|
||||
!staging?.title.valid ||
|
||||
originalStaging.current === staging
|
||||
}
|
||||
loading={isSaving}
|
||||
style={editorStyles.soloSaveButton}>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import { Appbar, Button, useTheme } from 'react-native-paper';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
@@ -7,14 +7,9 @@ import { BSON } from 'realm';
|
||||
import { useObject, useRealm } from '@realm/react';
|
||||
import { useDeviceOrientation } from '@react-native-community/hooks';
|
||||
import { TagEditor } from '../../components';
|
||||
import { ROUTE, RootStackParamList } from '../../types';
|
||||
import { ROUTE, RootStackParamList, StagingTag } from '../../types';
|
||||
import { Tag } from '../../database';
|
||||
import {
|
||||
StringValidationResult,
|
||||
deleteTag,
|
||||
validateColor,
|
||||
validateTagName,
|
||||
} from '../../utilities';
|
||||
import { deleteTag, validateColor, validateTagName } from '../../utilities';
|
||||
import editorStyles from './editorStyles';
|
||||
|
||||
const EditTag = ({
|
||||
@@ -31,29 +26,21 @@ const EditTag = ({
|
||||
BSON.UUID.createFromHexString(route.params.id),
|
||||
)!;
|
||||
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [tagName, setTagName] = useState(validateTagName(tag.name));
|
||||
const [tagColor, setTagColor] = useState(validateColor(tag.color));
|
||||
|
||||
const handleTagNameChange = useCallback((name: StringValidationResult) => {
|
||||
setTagName(name);
|
||||
setHasChanges(true);
|
||||
}, []);
|
||||
|
||||
const handleTagColorChange = useCallback((color: StringValidationResult) => {
|
||||
setTagColor(color);
|
||||
setHasChanges(true);
|
||||
}, []);
|
||||
const [staging, setStaging] = useState<StagingTag>({
|
||||
name: validateTagName(tag.name),
|
||||
color: validateColor(tag.color),
|
||||
});
|
||||
const originalStaging = useRef<StagingTag>(staging);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
realm.write(() => {
|
||||
tag.name = tagName.parsed;
|
||||
tag.color = tagColor.parsed;
|
||||
tag.name = staging.name.parsed;
|
||||
tag.color = staging.color.parsed;
|
||||
tag.dateModified = new Date();
|
||||
});
|
||||
|
||||
goBack();
|
||||
}, [goBack, realm, tag, tagColor.parsed, tagName.parsed]);
|
||||
}, [goBack, realm, staging.color.parsed, staging.name.parsed, tag]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -78,19 +65,18 @@ const EditTag = ({
|
||||
]}
|
||||
nestedScrollEnabled>
|
||||
<View style={editorStyles.editorView}>
|
||||
<TagEditor
|
||||
tagName={tagName}
|
||||
setTagName={handleTagNameChange}
|
||||
tagColor={tagColor}
|
||||
setTagColor={handleTagColorChange}
|
||||
/>
|
||||
<TagEditor staging={staging} setStaging={setStaging} />
|
||||
</View>
|
||||
<View style={editorStyles.saveButtonView}>
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
disabled={!tagName.valid || !tagColor.valid || !hasChanges}
|
||||
disabled={
|
||||
!staging.name.valid ||
|
||||
!staging.color.valid ||
|
||||
originalStaging.current === staging
|
||||
}
|
||||
style={editorStyles.soloSaveButton}>
|
||||
Save
|
||||
</Button>
|
||||
|
Reference in New Issue
Block a user