Add tag editing

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-19 13:18:33 +03:00
parent 6e1f7bd81f
commit 85732e247a
11 changed files with 93 additions and 42 deletions

View File

@@ -107,7 +107,8 @@
"unicorn/expiring-todo-comments": "off", "unicorn/expiring-todo-comments": "off",
"unicorn/no-array-for-each": "off", "unicorn/no-array-for-each": "off",
"unicorn/filename-case": ["error", { "case": "camelCase" }], "unicorn/filename-case": ["error", { "case": "camelCase" }],
"unicorn/numeric-separators-style": "off" "unicorn/numeric-separators-style": "off",
"unicorn/prevent-abbreviations": "off"
}, },
"ignorePatterns": ["*.config.js"] "ignorePatterns": ["*.config.js"]
} }

View File

@@ -4,6 +4,7 @@ import { FAB } from 'react-native-paper';
import { ParamListBase, useNavigation } from '@react-navigation/native'; import { ParamListBase, useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useDimensions } from '../contexts'; import { useDimensions } from '../contexts';
import { ROUTE } from '../types';
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => { const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
const { navigate } = const { navigate } =
@@ -38,22 +39,22 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
{ {
icon: 'tag', icon: 'tag',
label: 'Tag', label: 'Tag',
onPress: () => navigate('Add Tag'), onPress: () => navigate(ROUTE.EDIT_TAG),
}, },
{ {
icon: 'note-text', icon: 'note-text',
label: 'Text', label: 'Text',
onPress: () => navigate('Add Meme'), onPress: () => navigate(ROUTE.EDIT_MEME),
}, },
{ {
icon: 'image-album', icon: 'image-album',
label: 'Album', label: 'Album',
onPress: () => navigate('Add Meme'), onPress: () => navigate(ROUTE.EDIT_MEME),
}, },
]} ]}
onStateChange={({ open }) => setState(open)} onStateChange={({ open }) => setState(open)}
onPress={() => { onPress={() => {
if (state) navigate('Add Meme'); if (state) navigate(ROUTE.EDIT_MEME);
}} }}
style={{ style={{
paddingBottom: responsive.verticalScale(75), paddingBottom: responsive.verticalScale(75),

View File

@@ -1,4 +1,5 @@
import { Realm } from '@realm/react'; import { Realm } from '@realm/react';
import { BSON } from 'realm';
import { Tag } from './tag'; import { Tag } from './tag';
enum MEME_TYPE { enum MEME_TYPE {
@@ -20,7 +21,7 @@ const memeTypePlural = {
}; };
class Meme extends Realm.Object<Meme> { class Meme extends Realm.Object<Meme> {
id!: Realm.BSON.UUID; id!: BSON.UUID;
type!: MEME_TYPE; type!: MEME_TYPE;
uri!: Realm.List<string>; uri!: Realm.List<string>;
size!: number; size!: number;
@@ -38,7 +39,7 @@ class Meme extends Realm.Object<Meme> {
name: 'Meme', name: 'Meme',
primaryKey: 'id', primaryKey: 'id',
properties: { properties: {
id: 'uuid', id: { type: 'uuid', default: () => new BSON.UUID() },
type: { type: 'string', indexed: true }, type: { type: 'string', indexed: true },
uri: 'string[]', uri: 'string[]',
size: 'int', size: 'int',
@@ -47,8 +48,8 @@ class Meme extends Realm.Object<Meme> {
isFavorite: { type: 'bool', indexed: true, default: false }, isFavorite: { type: 'bool', indexed: true, default: false },
tags: { type: 'list', objectType: 'Tag', default: [] }, tags: { type: 'list', objectType: 'Tag', default: [] },
tagsLength: { type: 'int', default: 0 }, tagsLength: { type: 'int', default: 0 },
dateCreated: { type: 'date', default: new Date() }, dateCreated: { type: 'date', default: () => new Date() },
dateModified: { type: 'date', default: new Date() }, dateModified: { type: 'date', default: () => new Date() },
dateUsed: 'date?', dateUsed: 'date?',
timesUsed: { type: 'int', default: 0 }, timesUsed: { type: 'int', default: 0 },
}, },

View File

@@ -1,8 +1,9 @@
import { Realm } from '@realm/react'; import { Realm } from '@realm/react';
import { BSON } from 'realm';
import { Meme } from './meme'; import { Meme } from './meme';
class Tag extends Realm.Object<Tag> { class Tag extends Realm.Object<Tag> {
id!: Realm.BSON.UUID; id!: BSON.UUID;
name!: string; name!: string;
color!: string; color!: string;
memes!: Realm.List<Meme>; memes!: Realm.List<Meme>;
@@ -15,13 +16,13 @@ class Tag extends Realm.Object<Tag> {
name: 'Tag', name: 'Tag',
primaryKey: 'id', primaryKey: 'id',
properties: { properties: {
id: 'uuid', id: { type: 'uuid', default: () => new BSON.UUID() },
name: { type: 'string', indexed: true }, name: { type: 'string', indexed: true },
color: 'string', color: 'string',
memes: { type: 'list', objectType: 'Meme', default: [] }, memes: { type: 'list', objectType: 'Meme', default: [] },
memesLength: { type: 'int', default: 0 }, memesLength: { type: 'int', default: 0 },
dateCreated: { type: 'date', default: new Date() }, dateCreated: { type: 'date', default: () => new Date() },
dateModified: { type: 'date', default: new Date() }, dateModified: { type: 'date', default: () => new Date() },
timesUsed: { type: 'int', default: 0 }, timesUsed: { type: 'int', default: 0 },
}, },
}; };

View File

@@ -5,13 +5,13 @@ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useTheme } from 'react-native-paper'; import { useTheme } from 'react-native-paper';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Home, Tags, Settings, AddMeme, AddTag } from './screens'; import { Home, Tags, Settings, EditMeme, EditTag } from './screens';
import { darkNavigationTheme, lightNavigationTheme } from './theme'; import { darkNavigationTheme, lightNavigationTheme } from './theme';
import { import {
FloatingActionButton, FloatingActionButton,
HideableBottomNavigationBar, HideableBottomNavigationBar,
} from './components'; } from './components';
import { ROUTE } from './types'; import { ROUTE, RootStackParamList } from './types';
import { RootState } from './state'; import { RootState } from './state';
const TabNavigator = () => { const TabNavigator = () => {
@@ -73,7 +73,7 @@ const TabNavigator = () => {
const NavigationContainer = () => { const NavigationContainer = () => {
const theme = useTheme(); const theme = useTheme();
const StackNavigatorBase = createNativeStackNavigator(); const StackNavigatorBase = createNativeStackNavigator<RootStackParamList>();
return ( return (
<NavigationContainerBase <NavigationContainerBase
@@ -85,8 +85,11 @@ const NavigationContainer = () => {
animation: 'slide_from_bottom', animation: 'slide_from_bottom',
}}> }}>
<StackNavigatorBase.Screen name={ROUTE.MAIN} component={TabNavigator} /> <StackNavigatorBase.Screen name={ROUTE.MAIN} component={TabNavigator} />
<StackNavigatorBase.Screen name={ROUTE.ADD_MEME} component={AddMeme} /> <StackNavigatorBase.Screen
<StackNavigatorBase.Screen name={ROUTE.ADD_TAG} component={AddTag} /> name={ROUTE.EDIT_MEME}
component={EditMeme}
/>
<StackNavigatorBase.Screen name={ROUTE.EDIT_TAG} component={EditTag} />
</StackNavigatorBase.Navigator> </StackNavigatorBase.Navigator>
</NavigationContainerBase> </NavigationContainerBase>
); );

View File

@@ -5,7 +5,7 @@ import { useDimensions } from '../contexts';
import { ScrollView } from 'react-native'; import { ScrollView } from 'react-native';
import styles from '../styles'; import styles from '../styles';
const AddMeme = () => { const EditMeme = () => {
const navigation = useNavigation(); const navigation = useNavigation();
const { colors } = useTheme(); const { colors } = useTheme();
const { orientation } = useDimensions(); const { orientation } = useDimensions();
@@ -32,4 +32,4 @@ const AddMeme = () => {
); );
}; };
export default AddMeme; export default EditMeme;

View File

@@ -8,21 +8,31 @@ import {
useTheme, useTheme,
} from 'react-native-paper'; } from 'react-native-paper';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { BSON } from 'realm'; import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { BSON, UpdateMode } from 'realm';
import { useRealm } from '@realm/react'; import { useRealm } from '@realm/react';
import { TagPreview } from '../components'; import { TagPreview } from '../components';
import styles from '../styles'; import styles from '../styles';
import { generateRandomColor, isValidColor } from '../utilities'; import { generateRandomColor, isValidColor } from '../utilities';
import { useDimensions } from '../contexts'; import { useDimensions } from '../contexts';
import { ROUTE, RootStackParamList } from '../types';
import { Tag } from '../database';
const AddTag = () => { const EditTag = ({
route,
}: NativeStackScreenProps<RootStackParamList, ROUTE.EDIT_TAG>) => {
const navigation = useNavigation(); const navigation = useNavigation();
const { colors } = useTheme(); const { colors } = useTheme();
const { orientation } = useDimensions(); const { orientation } = useDimensions();
const realm = useRealm(); const realm = useRealm();
const [tagName, setTagName] = useState('newTag'); const tagId = route.params?.id;
const [tagColor, setTagColor] = useState(generateRandomColor()); const tag = tagId
? realm.objectForPrimaryKey(Tag, BSON.UUID.createFromHexString(tagId))
: undefined;
const [tagName, setTagName] = useState(tag?.name ?? 'newTag');
const [tagColor, setTagColor] = useState(tag?.color ?? generateRandomColor());
const [validatedTagColor, setValidatedTagColor] = useState(tagColor); const [validatedTagColor, setValidatedTagColor] = useState(tagColor);
const [tagNameError, setTagNameError] = useState<string | undefined>(); const [tagNameError, setTagNameError] = useState<string | undefined>();
@@ -55,11 +65,22 @@ const AddTag = () => {
const handleSave = () => { const handleSave = () => {
realm.write(() => { realm.write(() => {
realm.create('Tag', { realm.create(
id: new BSON.UUID(), Tag,
name: tagName, {
color: tagColor, id: tag?.id,
}); name: tagName,
color: tagColor,
},
UpdateMode.Modified,
);
});
navigation.goBack();
};
const handleDelete = () => {
realm.write(() => {
realm.delete(tag);
}); });
navigation.goBack(); navigation.goBack();
}; };
@@ -68,7 +89,8 @@ const AddTag = () => {
<> <>
<Appbar.Header> <Appbar.Header>
<Appbar.BackAction onPress={() => navigation.goBack()} /> <Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Add Tag" /> <Appbar.Content title={tag ? 'Edit Tag' : 'Add Tag'} />
{tag && <Appbar.Action icon="delete" onPress={handleDelete} />}
</Appbar.Header> </Appbar.Header>
<ScrollView <ScrollView
contentContainerStyle={[ contentContainerStyle={[
@@ -126,4 +148,4 @@ const AddTag = () => {
); );
}; };
export default AddTag; export default EditTag;

View File

@@ -1,5 +1,5 @@
export { default as AddMeme } from './addMeme'; export { default as EditMeme } from './editMeme';
export { default as AddTag } from './addTag'; export { default as EditTag } from './editTag';
export { default as Home } from './home'; export { default as Home } from './home';
export { default as Settings } from './settings'; export { default as Settings } from './settings';
export { default as Tags } from './tags'; export { default as Tags } from './tags';

View File

@@ -16,11 +16,11 @@ import {
TouchableRipple, TouchableRipple,
useTheme, useTheme,
} from 'react-native-paper'; } from 'react-native-paper';
import { useQuery, useRealm } from '@realm/react'; import { useQuery } from '@realm/react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { FlashList } from '@shopify/flash-list'; import { FlashList } from '@shopify/flash-list';
import { TagChip } from '../components'; import { TagChip } from '../components';
import { Tag, deleteTag } from '../database'; import { Tag } from '../database';
import styles from '../styles'; import styles from '../styles';
import { import {
RootState, RootState,
@@ -29,9 +29,16 @@ import {
setTagsSortDirection, setTagsSortDirection,
toggleTagsSortDirection, toggleTagsSortDirection,
} from '../state'; } from '../state';
import { SORT_DIRECTION, TAG_SORT, tagSortQuery } from '../types'; import {
ROUTE,
RootStackParamList,
SORT_DIRECTION,
TAG_SORT,
tagSortQuery,
} from '../types';
import { getSortIcon } from '../utilities'; import { getSortIcon } from '../utilities';
import { useDimensions } from '../contexts'; import { useDimensions } from '../contexts';
import { NavigationProp, useNavigation } from '@react-navigation/native';
const tagsStyles = StyleSheet.create({ const tagsStyles = StyleSheet.create({
headerView: { headerView: {
@@ -68,7 +75,7 @@ const tagsStyles = StyleSheet.create({
const Tags = () => { const Tags = () => {
const { colors } = useTheme(); const { colors } = useTheme();
const { orientation } = useDimensions(); const { orientation } = useDimensions();
const realm = useRealm(); const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const sort = useSelector((state: RootState) => state.tags.sort); const sort = useSelector((state: RootState) => state.tags.sort);
const sortDirection = useSelector( const sortDirection = useSelector(
(state: RootState) => state.tags.sortDirection, (state: RootState) => state.tags.sortDirection,
@@ -208,7 +215,10 @@ const Tags = () => {
estimatedItemSize={52} estimatedItemSize={52}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
renderItem={({ item: tag }) => ( renderItem={({ item: tag }) => (
<TouchableRipple onPress={() => deleteTag(realm, tag)}> <TouchableRipple
onPress={() =>
navigation.navigate(ROUTE.EDIT_TAG, { id: tag.id.toHexString() })
}>
<View style={tagsStyles.tagRow}> <View style={tagsStyles.tagRow}>
<View style={tagsStyles.tagView}> <View style={tagsStyles.tagView}>
<TagChip tag={tag} /> <TagChip tag={tag} />

View File

@@ -1,4 +1,4 @@
export { ROUTE } from './route'; export { ROUTE, type RootStackParamList } from './route';
export { export {
MEME_SORT, MEME_SORT,
homeSortQuery, homeSortQuery,

View File

@@ -3,7 +3,19 @@ enum ROUTE {
HOME = 'Home', HOME = 'Home',
TAGS = 'Tags', TAGS = 'Tags',
SETTINGS = 'Settings', SETTINGS = 'Settings',
ADD_MEME = 'Add Meme', EDIT_MEME = 'Edit Meme',
ADD_TAG = 'Add Tag', EDIT_TAG = 'Edit Tag',
} }
export { ROUTE };
interface EditTagRouteParams {
id: string;
}
interface RootStackParamList {
[key: string]: undefined | EditTagRouteParams;
[ROUTE.MAIN]: undefined;
[ROUTE.EDIT_MEME]: undefined;
[ROUTE.EDIT_TAG]: EditTagRouteParams | undefined;
}
export { ROUTE, type RootStackParamList };