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
|
minSdkVersion = 21
|
||||||
compileSdkVersion = 33
|
compileSdkVersion = 33
|
||||||
targetSdkVersion = 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.
|
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||||
ndkVersion = "23.1.7779620"
|
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": "^6.1.7",
|
||||||
"@react-navigation/native-stack": "^6.9.13",
|
"@react-navigation/native-stack": "^6.9.13",
|
||||||
"@realm/react": "^0.5.1",
|
"@realm/react": "^0.5.1",
|
||||||
|
"@shopify/flash-list": "^1.4.3",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.72.1",
|
"react-native": "0.72.1",
|
||||||
"react-native-document-picker": "^9.0.1",
|
"react-native-document-picker": "^9.0.1",
|
||||||
|
"react-native-fast-image": "^8.6.3",
|
||||||
"react-native-file-access": "^3.0.4",
|
"react-native-file-access": "^3.0.4",
|
||||||
"react-native-gesture-handler": "^2.12.0",
|
"react-native-gesture-handler": "^2.12.0",
|
||||||
"react-native-paper": "^5.9.1",
|
"react-native-paper": "^5.9.1",
|
||||||
|
@@ -8,7 +8,7 @@ import { Meme, Tag } from './database';
|
|||||||
import NavigationContainer from './navigation';
|
import NavigationContainer from './navigation';
|
||||||
import { SettingsProvider } from './contexts';
|
import { SettingsProvider } from './contexts';
|
||||||
|
|
||||||
function App() {
|
const App = () => {
|
||||||
const colorScheme = useColorScheme();
|
const colorScheme = useColorScheme();
|
||||||
const isDarkMode = colorScheme === 'dark';
|
const isDarkMode = colorScheme === 'dark';
|
||||||
const theme = isDarkMode ? darkTheme : lightTheme;
|
const theme = isDarkMode ? darkTheme : lightTheme;
|
||||||
@@ -28,6 +28,6 @@ function App() {
|
|||||||
</PaperProvider>
|
</PaperProvider>
|
||||||
</RealmProvider>
|
</RealmProvider>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@@ -29,7 +29,7 @@ const SettingsContext = createContext<SettingsContextType | undefined>(
|
|||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
function SettingsProvider({ children }: { children: ReactNode }) {
|
const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [storageUri, setStorageUri] = useState('');
|
const [storageUri, setStorageUri] = useState('');
|
||||||
const [noMedia, setNoMedia] = useState(false);
|
const [noMedia, setNoMedia] = useState(false);
|
||||||
|
|
||||||
@@ -128,22 +128,25 @@ function SettingsProvider({ children }: { children: ReactNode }) {
|
|||||||
)}
|
)}
|
||||||
</SettingsContext.Provider>
|
</SettingsContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
function useStorageUri(): [string, (newStorageUri: string) => Promise<void>] {
|
const useStorageUri = (): [
|
||||||
|
string,
|
||||||
|
(newStorageUri: string) => Promise<void>,
|
||||||
|
] => {
|
||||||
const context = useContext(SettingsContext);
|
const context = useContext(SettingsContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useStorageUri must be used within a SettingsProvider');
|
throw new Error('useStorageUri must be used within a SettingsProvider');
|
||||||
}
|
}
|
||||||
return [context.storageUri, context.setStorageUri];
|
return [context.storageUri, context.setStorageUri];
|
||||||
}
|
};
|
||||||
|
|
||||||
function useNoMedia(): [boolean, (newNoMedia: boolean) => Promise<void>] {
|
const useNoMedia = (): [boolean, (newNoMedia: boolean) => Promise<void>] => {
|
||||||
const context = useContext(SettingsContext);
|
const context = useContext(SettingsContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useNoMedia must be used within a SettingsProvider');
|
throw new Error('useNoMedia must be used within a SettingsProvider');
|
||||||
}
|
}
|
||||||
return [context.noMedia, context.setNoMedia];
|
return [context.noMedia, context.setNoMedia];
|
||||||
}
|
};
|
||||||
|
|
||||||
export { SettingsProvider, useStorageUri, useNoMedia };
|
export { SettingsProvider, useStorageUri, useNoMedia };
|
||||||
|
@@ -14,6 +14,7 @@ class Meme extends Realm.Object<Meme> {
|
|||||||
id!: Realm.BSON.UUID;
|
id!: Realm.BSON.UUID;
|
||||||
type!: MEME_TYPE;
|
type!: MEME_TYPE;
|
||||||
uri!: string;
|
uri!: string;
|
||||||
|
size!: number;
|
||||||
title!: string;
|
title!: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
isFavorite!: boolean;
|
isFavorite!: boolean;
|
||||||
@@ -30,6 +31,7 @@ class Meme extends Realm.Object<Meme> {
|
|||||||
id: 'uuid',
|
id: 'uuid',
|
||||||
type: { type: 'string', indexed: true },
|
type: { type: 'string', indexed: true },
|
||||||
uri: 'string',
|
uri: 'string',
|
||||||
|
size: 'int',
|
||||||
title: 'string',
|
title: 'string',
|
||||||
description: 'string?',
|
description: 'string?',
|
||||||
isFavorite: { type: 'bool', indexed: true, default: false },
|
isFavorite: { type: 'bool', indexed: true, default: false },
|
||||||
|
@@ -12,7 +12,7 @@ import { horizontalScale } from './styles';
|
|||||||
import { FloatingActionButton } from './components';
|
import { FloatingActionButton } from './components';
|
||||||
import { darkNavigationTheme, lightNavigationTheme } from './theme';
|
import { darkNavigationTheme, lightNavigationTheme } from './theme';
|
||||||
|
|
||||||
function TabNavigator() {
|
const TabNavigator = () => {
|
||||||
const TabNavigatorBase = createBottomTabNavigator();
|
const TabNavigatorBase = createBottomTabNavigator();
|
||||||
const [fabVisible, setFabVisible] = React.useState(true);
|
const [fabVisible, setFabVisible] = React.useState(true);
|
||||||
|
|
||||||
@@ -102,9 +102,9 @@ function TabNavigator() {
|
|||||||
</Portal.Host>
|
</Portal.Host>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
function NavigationContainer() {
|
const NavigationContainer = () => {
|
||||||
const StackNavigatorBase = createNativeStackNavigator();
|
const StackNavigatorBase = createNativeStackNavigator();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@@ -121,6 +121,6 @@ function NavigationContainer() {
|
|||||||
</StackNavigatorBase.Navigator>
|
</StackNavigatorBase.Navigator>
|
||||||
</NavigationContainerBase>
|
</NavigationContainerBase>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default NavigationContainer;
|
export default NavigationContainer;
|
||||||
|
@@ -1,12 +1,88 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
import { Text } from 'react-native-paper';
|
import {
|
||||||
|
Button,
|
||||||
|
Menu,
|
||||||
|
IconButton,
|
||||||
|
Divider,
|
||||||
|
useTheme,
|
||||||
|
} from 'react-native-paper';
|
||||||
import { PaddedView } from '../components';
|
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 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 (
|
return (
|
||||||
<PaddedView centered>
|
<PaddedView>
|
||||||
<Text>Home</Text>
|
<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>
|
</PaddedView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -31,13 +31,22 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
centeredVertical: {
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
centerText: {
|
centerText: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
|
flexRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
flexRowSpaceBetween: {
|
flexRowSpaceBetween: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
|
flexRowReverse: {
|
||||||
|
flexDirection: 'row-reverse',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export { horizontalScale, verticalScale, moderateScale, styles as default };
|
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 { 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