Add home screen view options
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -6,6 +6,7 @@ buildscript {
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 33
|
||||
targetSdkVersion = 33
|
||||
kotlinVersion = "1.8.22"
|
||||
|
||||
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||
ndkVersion = "23.1.7779620"
|
||||
|
3541
package-lock.json
generated
3541
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,11 @@
|
||||
"@react-navigation/native": "^6.1.7",
|
||||
"@react-navigation/native-stack": "^6.9.13",
|
||||
"@realm/react": "^0.5.1",
|
||||
"@shopify/flash-list": "^1.4.3",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.1",
|
||||
"react-native-document-picker": "^9.0.1",
|
||||
"react-native-fast-image": "^8.6.3",
|
||||
"react-native-file-access": "^3.0.4",
|
||||
"react-native-gesture-handler": "^2.12.0",
|
||||
"react-native-paper": "^5.9.1",
|
||||
|
@@ -8,7 +8,7 @@ import { Meme, Tag } from './database';
|
||||
import NavigationContainer from './navigation';
|
||||
import { SettingsProvider } from './contexts';
|
||||
|
||||
function App() {
|
||||
const App = () => {
|
||||
const colorScheme = useColorScheme();
|
||||
const isDarkMode = colorScheme === 'dark';
|
||||
const theme = isDarkMode ? darkTheme : lightTheme;
|
||||
@@ -28,6 +28,6 @@ function App() {
|
||||
</PaperProvider>
|
||||
</RealmProvider>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
@@ -29,7 +29,7 @@ const SettingsContext = createContext<SettingsContextType | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
function SettingsProvider({ children }: { children: ReactNode }) {
|
||||
const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [storageUri, setStorageUri] = useState('');
|
||||
const [noMedia, setNoMedia] = useState(false);
|
||||
|
||||
@@ -128,22 +128,25 @@ function SettingsProvider({ children }: { children: ReactNode }) {
|
||||
)}
|
||||
</SettingsContext.Provider>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function useStorageUri(): [string, (newStorageUri: string) => Promise<void>] {
|
||||
const useStorageUri = (): [
|
||||
string,
|
||||
(newStorageUri: string) => Promise<void>,
|
||||
] => {
|
||||
const context = useContext(SettingsContext);
|
||||
if (!context) {
|
||||
throw new Error('useStorageUri must be used within a SettingsProvider');
|
||||
}
|
||||
return [context.storageUri, context.setStorageUri];
|
||||
}
|
||||
};
|
||||
|
||||
function useNoMedia(): [boolean, (newNoMedia: boolean) => Promise<void>] {
|
||||
const useNoMedia = (): [boolean, (newNoMedia: boolean) => Promise<void>] => {
|
||||
const context = useContext(SettingsContext);
|
||||
if (!context) {
|
||||
throw new Error('useNoMedia must be used within a SettingsProvider');
|
||||
}
|
||||
return [context.noMedia, context.setNoMedia];
|
||||
}
|
||||
};
|
||||
|
||||
export { SettingsProvider, useStorageUri, useNoMedia };
|
||||
|
@@ -14,6 +14,7 @@ class Meme extends Realm.Object<Meme> {
|
||||
id!: Realm.BSON.UUID;
|
||||
type!: MEME_TYPE;
|
||||
uri!: string;
|
||||
size!: number;
|
||||
title!: string;
|
||||
description?: string;
|
||||
isFavorite!: boolean;
|
||||
@@ -30,6 +31,7 @@ class Meme extends Realm.Object<Meme> {
|
||||
id: 'uuid',
|
||||
type: { type: 'string', indexed: true },
|
||||
uri: 'string',
|
||||
size: 'int',
|
||||
title: 'string',
|
||||
description: 'string?',
|
||||
isFavorite: { type: 'bool', indexed: true, default: false },
|
||||
|
@@ -12,7 +12,7 @@ import { horizontalScale } from './styles';
|
||||
import { FloatingActionButton } from './components';
|
||||
import { darkNavigationTheme, lightNavigationTheme } from './theme';
|
||||
|
||||
function TabNavigator() {
|
||||
const TabNavigator = () => {
|
||||
const TabNavigatorBase = createBottomTabNavigator();
|
||||
const [fabVisible, setFabVisible] = React.useState(true);
|
||||
|
||||
@@ -102,9 +102,9 @@ function TabNavigator() {
|
||||
</Portal.Host>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function NavigationContainer() {
|
||||
const NavigationContainer = () => {
|
||||
const StackNavigatorBase = createNativeStackNavigator();
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -121,6 +121,6 @@ function NavigationContainer() {
|
||||
</StackNavigatorBase.Navigator>
|
||||
</NavigationContainerBase>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default NavigationContainer;
|
||||
|
@@ -1,12 +1,88 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Text } from 'react-native-paper';
|
||||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import {
|
||||
Button,
|
||||
Menu,
|
||||
IconButton,
|
||||
Divider,
|
||||
useTheme,
|
||||
} from 'react-native-paper';
|
||||
import { PaddedView } from '../components';
|
||||
import styles, { verticalScale } from '../styles';
|
||||
import { SORT, SORT_DIRECTION, VIEW } from '../types';
|
||||
import { getNextView, getSortIcon, getViewIcon } from '../utilities';
|
||||
|
||||
const Home = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const [sortMenuVisible, setSortMenuVisible] = useState(false);
|
||||
const [sort, setSort] = useState(SORT.TITLE);
|
||||
const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASCENDING);
|
||||
const [viewType, setViewType] = useState(VIEW.MASONRY);
|
||||
const [favoritesOnly, setFavoritesOnly] = useState(false);
|
||||
|
||||
const handleSortModeChange = (newSort: SORT) => {
|
||||
if (newSort === sort) {
|
||||
setSortDirection(
|
||||
sortDirection === SORT_DIRECTION.ASCENDING
|
||||
? SORT_DIRECTION.DESCENDING
|
||||
: SORT_DIRECTION.ASCENDING,
|
||||
);
|
||||
} else {
|
||||
setSort(newSort);
|
||||
|
||||
if (newSort === SORT.TITLE) {
|
||||
setSortDirection(SORT_DIRECTION.ASCENDING);
|
||||
} else {
|
||||
setSortDirection(SORT_DIRECTION.DESCENDING);
|
||||
}
|
||||
}
|
||||
|
||||
setSortMenuVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<PaddedView centered>
|
||||
<Text>Home</Text>
|
||||
<PaddedView>
|
||||
<View style={[styles.flexRowSpaceBetween, styles.centeredVertical]}>
|
||||
<Menu
|
||||
visible={sortMenuVisible}
|
||||
onDismiss={() => setSortMenuVisible(false)}
|
||||
anchor={
|
||||
<Button
|
||||
onPress={() => setSortMenuVisible(true)}
|
||||
icon={getSortIcon(sort, sortDirection)}
|
||||
contentStyle={styles.flexRowReverse}>
|
||||
Sort By: {sort}
|
||||
</Button>
|
||||
}>
|
||||
{Object.keys(SORT).map(key => {
|
||||
return (
|
||||
<Menu.Item
|
||||
key={key}
|
||||
onPress={() =>
|
||||
handleSortModeChange(SORT[key as keyof typeof SORT])
|
||||
}
|
||||
title={SORT[key as keyof typeof SORT]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
<View style={styles.flexRow}>
|
||||
<IconButton
|
||||
icon={favoritesOnly ? 'heart' : 'heart-outline'}
|
||||
iconColor={theme.colors.primary}
|
||||
size={verticalScale(16)}
|
||||
onPress={() => setFavoritesOnly(!favoritesOnly)}
|
||||
/>
|
||||
<IconButton
|
||||
icon={getViewIcon(viewType)}
|
||||
iconColor={theme.colors.primary}
|
||||
size={verticalScale(16)}
|
||||
onPress={() => setViewType(getNextView(viewType))}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Divider />
|
||||
</PaddedView>
|
||||
);
|
||||
};
|
||||
|
@@ -31,13 +31,22 @@ const styles = StyleSheet.create({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
centeredVertical: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
centerText: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
flexRow: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
flexRowSpaceBetween: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
flexRowReverse: {
|
||||
flexDirection: 'row-reverse',
|
||||
},
|
||||
});
|
||||
|
||||
export { horizontalScale, verticalScale, moderateScale, styles as default };
|
||||
|
2
src/types/index.ts
Normal file
2
src/types/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { SORT, SORT_DIRECTION } from './sort';
|
||||
export { VIEW } from './view';
|
15
src/types/sort.ts
Normal file
15
src/types/sort.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
enum SORT {
|
||||
TITLE = 'Title',
|
||||
DATE_CREATED = 'Date Created',
|
||||
DATE_MODIFIED = 'Date Modified',
|
||||
DATE_USED = 'Last Used',
|
||||
TIMES_USED = 'Times Used',
|
||||
SIZE = 'Size',
|
||||
}
|
||||
|
||||
enum SORT_DIRECTION {
|
||||
ASCENDING = 'Ascending',
|
||||
DESCENDING = 'Descending',
|
||||
}
|
||||
|
||||
export { SORT, SORT_DIRECTION };
|
7
src/types/view.ts
Normal file
7
src/types/view.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
enum VIEW {
|
||||
MASONRY = 'Masonry',
|
||||
GRID = 'Grid',
|
||||
LIST = 'List',
|
||||
}
|
||||
|
||||
export { VIEW };
|
@@ -1 +1,3 @@
|
||||
export { isPermissionForPath, clearPermissions } from './permissions';
|
||||
export { getSortIcon } from './sort';
|
||||
export { getViewIcon, getNextView } from './view';
|
||||
|
38
src/utilities/sort.ts
Normal file
38
src/utilities/sort.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { SORT, SORT_DIRECTION } from '../types';
|
||||
|
||||
const getSortIcon = (sort: SORT, sortDirection: SORT_DIRECTION) => {
|
||||
let sortIcon = '';
|
||||
|
||||
switch (sort) {
|
||||
case SORT.TITLE: {
|
||||
sortIcon = 'sort-alphabetical';
|
||||
break;
|
||||
}
|
||||
case SORT.DATE_CREATED:
|
||||
case SORT.DATE_MODIFIED:
|
||||
case SORT.DATE_USED: {
|
||||
sortIcon = 'sort-calendar';
|
||||
break;
|
||||
}
|
||||
case SORT.TIMES_USED:
|
||||
case SORT.SIZE: {
|
||||
sortIcon = 'sort-numeric';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (sortDirection) {
|
||||
case SORT_DIRECTION.ASCENDING: {
|
||||
sortIcon += '-ascending';
|
||||
break;
|
||||
}
|
||||
case SORT_DIRECTION.DESCENDING: {
|
||||
sortIcon += '-descending';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sortIcon;
|
||||
};
|
||||
|
||||
export { getSortIcon };
|
31
src/utilities/view.ts
Normal file
31
src/utilities/view.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { VIEW } from '../types';
|
||||
|
||||
const getViewIcon = (view: VIEW) => {
|
||||
switch (view) {
|
||||
case VIEW.MASONRY: {
|
||||
return 'view-dashboard';
|
||||
}
|
||||
case VIEW.GRID: {
|
||||
return 'view-grid';
|
||||
}
|
||||
case VIEW.LIST: {
|
||||
return 'view-list';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getNextView = (view: VIEW) => {
|
||||
switch (view) {
|
||||
case VIEW.MASONRY: {
|
||||
return VIEW.GRID;
|
||||
}
|
||||
case VIEW.GRID: {
|
||||
return VIEW.LIST;
|
||||
}
|
||||
case VIEW.LIST: {
|
||||
return VIEW.MASONRY;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { getViewIcon, getNextView };
|
Reference in New Issue
Block a user