diff --git a/src/components/floatingActionButton.tsx b/src/components/floatingActionButton.tsx index 1bd365d..fcd315f 100644 --- a/src/components/floatingActionButton.tsx +++ b/src/components/floatingActionButton.tsx @@ -1,23 +1,18 @@ import React, { useEffect, useState } from 'react'; -import { StyleSheet, Keyboard } from 'react-native'; +import { Keyboard } from 'react-native'; import { FAB } from 'react-native-paper'; import { ParamListBase, useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useDimensions } from '../contexts'; -const styles = StyleSheet.create({ - fab: { - position: 'absolute', - }, -}); - const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => { const { navigate } = useNavigation>(); - const dimensions = useDimensions(); + const { responsive } = useDimensions(); const [state, setState] = useState(false); const [keyboardOpen, setKeyboardOpen] = useState(false); + useEffect(() => { const keyboardDidShowListener = Keyboard.addListener( 'keyboardDidShow', @@ -29,8 +24,8 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => { ); return () => { - keyboardDidHideListener.remove(); keyboardDidShowListener.remove(); + keyboardDidHideListener.remove(); }; }, []); @@ -60,13 +55,10 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => { onPress={() => { if (state) navigate('Add Meme'); }} - style={[ - styles.fab, - { - paddingRight: dimensions.responsive.horizontalScale(10), - paddingBottom: dimensions.responsive.verticalScale(75), - }, - ]} + style={{ + paddingBottom: responsive.verticalScale(75), + paddingRight: responsive.horizontalScale(10), + }} /> ); }; diff --git a/src/components/index.ts b/src/components/index.ts index da66b71..b49eafa 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,4 +2,5 @@ export { default as FloatingActionButton } from './floatingActionButton'; export { default as LoadingView } from './loadingView'; export { default as RootScrollView } from './rootScrollView'; export { default as RootView } from './rootView'; +export { default as TagChip } from './tagChip'; export { default as TagPreview } from './tagPreview'; diff --git a/src/components/rootScrollView.tsx b/src/components/rootScrollView.tsx index c451e17..21f9563 100644 --- a/src/components/rootScrollView.tsx +++ b/src/components/rootScrollView.tsx @@ -1,11 +1,8 @@ import React, { ReactNode } from 'react'; -import { - StyleProp, - ScrollView, - ViewStyle, -} from 'react-native'; +import { StyleProp, ScrollView, ViewStyle } from 'react-native'; import { useTheme } from 'react-native-paper'; import styles from '../styles'; +import { useDimensions } from '../contexts'; const RootScrollView = ({ children, @@ -19,11 +16,21 @@ const RootScrollView = ({ padded?: boolean; }) => { const { colors } = useTheme(); + const { orientation } = useDimensions(); return ( { const { colors } = useTheme(); + const { orientation } = useDimensions(); return ( { + const contrastColor = getContrastColor(properties.tag.color); + + return ( + { + return ; + }} + compact + style={[ + { + backgroundColor: properties.tag.color, + }, + ]} + textStyle={{ color: contrastColor }}> + {'#' + properties.tag.name} + + ); +}; + +export default TagChip; diff --git a/src/components/tagPreview.tsx b/src/components/tagPreview.tsx index ae0722e..ec17d3c 100644 --- a/src/components/tagPreview.tsx +++ b/src/components/tagPreview.tsx @@ -1,44 +1,45 @@ import React from 'react'; -import { View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; import { Chip } from 'react-native-paper'; import styles from '../styles'; import { useDimensions } from '../contexts'; import { getContrastColor } from '../utilities'; +const tagPreviewStyles = StyleSheet.create({ + chip: { + padding: 5, + }, + text: { + fontSize: 18, + }, +}); + const TagPreview = (properties: { name: string; color: string }) => { - const dimensions = useDimensions(); + const { responsive } = useDimensions(); + const contrastColor = getContrastColor(properties.color); return ( { - return ( - - ); + return ; }} elevated style={[ + tagPreviewStyles.chip, { backgroundColor: properties.color, - padding: dimensions.static.verticalScale(5), }, ]} - textStyle={[ - { fontSize: dimensions.static.horizontalScale(15) }, - { color: getContrastColor(properties.color) }, - ]}> + textStyle={[tagPreviewStyles.text, { color: contrastColor }]}> {'#' + properties.name} diff --git a/src/contexts/dimensions.tsx b/src/contexts/dimensions.tsx index 2d617a3..b935e7f 100644 --- a/src/contexts/dimensions.tsx +++ b/src/contexts/dimensions.tsx @@ -3,6 +3,7 @@ import React, { createContext, useContext, useEffect, + useMemo, useState, } from 'react'; import { Dimensions, ScaledSize } from 'react-native'; @@ -18,6 +19,7 @@ interface ScaleFunctions { interface DimensionsContext { orientation: 'portrait' | 'landscape'; + dimensions: ScaledSize; responsive: ScaleFunctions; static: ScaleFunctions; } @@ -42,15 +44,16 @@ const DimensionsProvider = ({ children }: { children: ReactNode }) => { const orientation = dimensions.width > dimensions.height ? 'landscape' : 'portrait'; - const [initialDimensions, setInitialDimensions] = useState(dimensions); - const [initialOrientation] = useState(orientation); - - if (initialOrientation === 'landscape') { - setInitialDimensions({ - width: initialDimensions.height, - height: initialDimensions.width, - } as ScaledSize); - } + const initialDimensions = useMemo(() => { + if (orientation === 'landscape') { + return { + width: dimensions.height, + height: dimensions.width, + } as ScaledSize; + } + return dimensions; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const responsiveScale = createScaleFunctions(dimensions); const staticScale = createScaleFunctions(initialDimensions); @@ -71,6 +74,7 @@ const DimensionsProvider = ({ children }: { children: ReactNode }) => { diff --git a/src/database/index.ts b/src/database/index.ts index eabb69c..cf73766 100644 --- a/src/database/index.ts +++ b/src/database/index.ts @@ -1,2 +1,2 @@ export { MEME_TYPE, memeTypePlural, Meme } from './meme'; -export { Tag, deleteAllTags } from './tag'; +export { Tag, deleteTag, deleteAllTags } from './tag'; diff --git a/src/database/meme.ts b/src/database/meme.ts index 7538491..6a176e0 100644 --- a/src/database/meme.ts +++ b/src/database/meme.ts @@ -45,8 +45,8 @@ class Meme extends Realm.Object { description: 'string?', isFavorite: { type: 'bool', indexed: true, default: false }, tags: 'Tag[]', - dateCreated: 'date', - dateModified: 'date', + dateCreated: { type: 'date', default: new Date() }, + dateModified: { type: 'date', default: new Date() }, dateUsed: 'date?', timesUsed: { type: 'int', default: 0 }, }, diff --git a/src/database/tag.ts b/src/database/tag.ts index 568bdd3..0181271 100644 --- a/src/database/tag.ts +++ b/src/database/tag.ts @@ -6,6 +6,10 @@ class Tag extends Realm.Object { name!: string; color!: string; memes!: Realm.List; + dateCreated!: Date; + dateModified!: Date; + dateUsed?: Date; + timesUsed!: number; static schema: Realm.ObjectSchema = { name: 'Tag', @@ -15,14 +19,25 @@ class Tag extends Realm.Object { name: 'string', color: 'string', memes: 'Meme[]', + dateCreated: { type: 'date', default: new Date() }, + dateModified: { type: 'date', default: new Date() }, + dateUsed: 'date?', + timesUsed: { type: 'int', default: 0 }, }, }; } +const deleteTag = (realm: Realm, tag: Tag) => { + realm.write(() => { + realm.delete(tag); + }); +}; + const deleteAllTags = (realm: Realm) => { realm.write(() => { realm.delete(realm.objects('Tag')); }); }; -export { Tag, deleteAllTags }; + +export { Tag, deleteTag, deleteAllTags }; diff --git a/src/navigation.tsx b/src/navigation.tsx index c518718..dd0ae22 100644 --- a/src/navigation.tsx +++ b/src/navigation.tsx @@ -9,12 +9,9 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { BottomNavigation, useTheme } from 'react-native-paper'; import { Home, Tags, Settings, AddMeme, AddTag } from './screens'; import { darkNavigationTheme, lightNavigationTheme } from './theme'; -import { useDimensions } from './contexts'; import { FloatingActionButton } from './components'; const TabNavigator = () => { - const dimensions = useDimensions(); - const [showFab, setShowFab] = React.useState(true); const TabNavigatorBase = createBottomTabNavigator(); @@ -50,7 +47,7 @@ const TabNavigator = () => { return options.tabBarIcon({ focused, color, - size: dimensions.static.horizontalScale(20), + size: 22, }); } }} diff --git a/src/screens/home.tsx b/src/screens/home.tsx index c5470c0..b25b03e 100644 --- a/src/screens/home.tsx +++ b/src/screens/home.tsx @@ -23,11 +23,9 @@ import { setFilter, } from '../state'; import { MEME_TYPE, memeTypePlural } from '../database'; -import { useDimensions } from '../contexts'; const Home = () => { const theme = useTheme(); - const dimensions = useDimensions(); const sort = useSelector((state: RootState) => state.home.sort); const sortDirection = useSelector( (state: RootState) => state.home.sortDirection, @@ -65,9 +63,13 @@ const Home = () => { return ( - - - + + + setSortMenuVisible(false)} @@ -97,13 +99,13 @@ const Home = () => { dispatch(cycleView())} /> dispatch(toggleFavoritesOnly())} /> { onPress={() => setFilterMenuVisible(true)} icon={filter ? 'filter' : 'filter-outline'} iconColor={theme.colors.primary} - size={dimensions.static.verticalScale(16)} + size={18} /> }> { const noMedia = useSelector((state: RootState) => state.settings.noMedia); const dispatch = useDispatch(); - const dimensions = useDimensions(); + const { responsive } = useDimensions(); const [optimizingDatabase, setOptimizingDatabase] = useState(false); const [snackbarVisible, setSnackbarVisible] = useState(false); @@ -46,7 +48,7 @@ const SettingsScreen = () => { + + + + + Tag + Items + + {tags.map(tag => ( + deleteTag(realm, tag)}> + + + + {tag.memes.length} + + ))} + + {tags.length === 0 && ( + + No tags found + + )} ); }; diff --git a/src/screens/welcome.tsx b/src/screens/welcome.tsx index 83a0da7..6667f0e 100644 --- a/src/screens/welcome.tsx +++ b/src/screens/welcome.tsx @@ -10,7 +10,7 @@ import { useDimensions } from '../contexts'; const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => { const dispatch = useDispatch(); - const dimensions = useDimensions(); + const { responsive } = useDimensions(); const selectStorageLocation = async () => { const uri = await openDocumentTree(true).catch(noOp); @@ -25,7 +25,7 @@ const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => { variant="displayMedium" style={[ { - marginBottom: dimensions.responsive.verticalScale(30), + marginBottom: responsive.verticalScale(30), }, styles.centerText, ]}> @@ -35,7 +35,7 @@ const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => { mode="contained" onPress={selectStorageLocation} style={{ - marginBottom: dimensions.responsive.verticalScale(100), + marginBottom: responsive.verticalScale(100), }}> Select Storage Location diff --git a/src/styles.tsx b/src/styles.tsx index a0fe2d2..ce39333 100644 --- a/src/styles.tsx +++ b/src/styles.tsx @@ -2,22 +2,31 @@ import { StyleSheet } from 'react-native'; const styles = StyleSheet.create({ smallPadding: { - padding: '2.5%', + padding: '2%', + }, + smallPaddingHorizontal: { + paddingHorizontal: '2%', + }, + smallPaddingVertical: { + paddingVertical: '2%', }, padding: { padding: '5%', }, - smallPaddingHorizontal: { - paddingHorizontal: '2.5%', + paddingHorizontal: { + paddingHorizontal: '5%', + }, + paddingVertical: { + paddingVertical: '5%', }, centered: { justifyContent: 'center', alignItems: 'center', }, - centeredVertical: { + alignCenter: { alignItems: 'center', }, - centeredHorizontal: { + justifyCenter: { justifyContent: 'center', }, centerText: {