Add home screen state persistence
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -10,10 +10,10 @@ import type {} from 'redux-thunk/extend-redux';
|
|||||||
import { lightTheme, darkTheme } from './theme';
|
import { lightTheme, darkTheme } from './theme';
|
||||||
import { Meme, Tag } from './database';
|
import { Meme, Tag } from './database';
|
||||||
import NavigationContainer from './navigation';
|
import NavigationContainer from './navigation';
|
||||||
import { store, persistor, updateStorageUri, validateSettings } from './redux';
|
import { store, persistor, updateStorageUri, validateSettings } from './state';
|
||||||
import { LoadingView } from './components';
|
import { LoadingView } from './components';
|
||||||
import { Welcome } from './screens';
|
import { Welcome } from './screens';
|
||||||
import { noOp } from './constants';
|
import { noOp } from './utilities';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [showWelcome, setShowWelcome] = useState(false);
|
const [showWelcome, setShowWelcome] = useState(false);
|
||||||
@@ -43,13 +43,13 @@ const App = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<PaperProvider theme={theme}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<PersistGate
|
<PersistGate
|
||||||
loading={<LoadingView />}
|
loading={<LoadingView />}
|
||||||
persistor={persistor}
|
persistor={persistor}
|
||||||
onBeforeLift={onBeforeLift}>
|
onBeforeLift={onBeforeLift}>
|
||||||
<RealmProvider schema={[Meme, Tag]}>
|
<RealmProvider schema={[Meme, Tag]}>
|
||||||
<PaperProvider theme={theme}>
|
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<StatusBar
|
<StatusBar
|
||||||
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
||||||
@@ -68,10 +68,10 @@ const App = () => {
|
|||||||
<NavigationContainer />
|
<NavigationContainer />
|
||||||
)}
|
)}
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
</PaperProvider>
|
|
||||||
</RealmProvider>
|
</RealmProvider>
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
</PaperProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -7,37 +7,46 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { PaddedView } from '../components';
|
import { PaddedView } from '../components';
|
||||||
import styles, { verticalScale } from '../styles';
|
import styles, { verticalScale } from '../styles';
|
||||||
import { SORT, SORT_DIRECTION, VIEW } from '../types';
|
import { SORT, SORT_DIRECTION } from '../types';
|
||||||
import { getNextView, getSortIcon, getViewIcon } from '../utilities';
|
import { getSortIcon, getViewIcon } from '../utilities';
|
||||||
|
import {
|
||||||
|
RootState,
|
||||||
|
cycleView,
|
||||||
|
toggleSortDirection,
|
||||||
|
setSortDirection,
|
||||||
|
toggleFavoritesOnly,
|
||||||
|
setSort,
|
||||||
|
} from '../state';
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const sort = useSelector((state: RootState) => state.home.sort);
|
||||||
|
const sortDirection = useSelector(
|
||||||
|
(state: RootState) => state.home.sortDirection,
|
||||||
|
);
|
||||||
|
const view = useSelector((state: RootState) => state.home.view);
|
||||||
|
const favoritesOnly = useSelector(
|
||||||
|
(state: RootState) => state.home.favoritesOnly,
|
||||||
|
);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const [sortMenuVisible, setSortMenuVisible] = useState(false);
|
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) => {
|
const handleSortModeChange = (newSort: SORT) => {
|
||||||
if (newSort === sort) {
|
if (newSort === sort) {
|
||||||
setSortDirection(
|
dispatch(toggleSortDirection());
|
||||||
sortDirection === SORT_DIRECTION.ASCENDING
|
|
||||||
? SORT_DIRECTION.DESCENDING
|
|
||||||
: SORT_DIRECTION.ASCENDING,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
setSort(newSort);
|
dispatch(setSort(newSort));
|
||||||
|
|
||||||
if (newSort === SORT.TITLE) {
|
if (newSort === SORT.TITLE) {
|
||||||
setSortDirection(SORT_DIRECTION.ASCENDING);
|
dispatch(setSortDirection(SORT_DIRECTION.ASCENDING));
|
||||||
} else {
|
} else {
|
||||||
setSortDirection(SORT_DIRECTION.DESCENDING);
|
dispatch(setSortDirection(SORT_DIRECTION.DESCENDING));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSortMenuVisible(false);
|
setSortMenuVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,13 +81,13 @@ const Home = () => {
|
|||||||
icon={favoritesOnly ? 'heart' : 'heart-outline'}
|
icon={favoritesOnly ? 'heart' : 'heart-outline'}
|
||||||
iconColor={theme.colors.primary}
|
iconColor={theme.colors.primary}
|
||||||
size={verticalScale(16)}
|
size={verticalScale(16)}
|
||||||
onPress={() => setFavoritesOnly(!favoritesOnly)}
|
onPress={() => dispatch(toggleFavoritesOnly())}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={getViewIcon(viewType)}
|
icon={getViewIcon(view)}
|
||||||
iconColor={theme.colors.primary}
|
iconColor={theme.colors.primary}
|
||||||
size={verticalScale(16)}
|
size={verticalScale(16)}
|
||||||
onPress={() => setViewType(getNextView(viewType))}
|
onPress={() => dispatch(cycleView())}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@@ -7,7 +7,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||||||
import { PaddedView } from '../components';
|
import { PaddedView } from '../components';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { Meme } from '../database';
|
import { Meme } from '../database';
|
||||||
import { RootState, updateNoMedia, updateStorageUri } from '../redux';
|
import { RootState, updateNoMedia, updateStorageUri } from '../state';
|
||||||
import type {} from 'redux-thunk/extend-redux';
|
import type {} from 'redux-thunk/extend-redux';
|
||||||
|
|
||||||
const SettingsScreen = () => {
|
const SettingsScreen = () => {
|
||||||
|
79
src/state/home.ts
Normal file
79
src/state/home.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { SORT, SORT_DIRECTION, VIEW } from '../types';
|
||||||
|
|
||||||
|
interface HomeState {
|
||||||
|
sort: SORT;
|
||||||
|
sortDirection: SORT_DIRECTION;
|
||||||
|
view: VIEW;
|
||||||
|
favoritesOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: HomeState = {
|
||||||
|
sort: SORT.TITLE,
|
||||||
|
sortDirection: SORT_DIRECTION.ASCENDING,
|
||||||
|
view: VIEW.MASONRY,
|
||||||
|
favoritesOnly: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const homeSlice = createSlice({
|
||||||
|
name: 'home',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setSort: (state, action: PayloadAction<SORT>) => {
|
||||||
|
state.sort = action.payload;
|
||||||
|
},
|
||||||
|
setSortDirection: (state, action: PayloadAction<SORT_DIRECTION>) => {
|
||||||
|
state.sortDirection = action.payload;
|
||||||
|
},
|
||||||
|
toggleSortDirection: state => {
|
||||||
|
state.sortDirection ^= 1;
|
||||||
|
},
|
||||||
|
setView: (state, action: PayloadAction<VIEW>) => {
|
||||||
|
state.view = action.payload;
|
||||||
|
},
|
||||||
|
cycleView: state => {
|
||||||
|
switch (state.view) {
|
||||||
|
case VIEW.MASONRY: {
|
||||||
|
state.view = VIEW.LIST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VIEW.LIST: {
|
||||||
|
state.view = VIEW.GRID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VIEW.GRID: {
|
||||||
|
state.view = VIEW.MASONRY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setFavoritesOnly: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.favoritesOnly = action.payload;
|
||||||
|
},
|
||||||
|
toggleFavoritesOnly: state => {
|
||||||
|
state.favoritesOnly = !state.favoritesOnly;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
setSort,
|
||||||
|
setSortDirection,
|
||||||
|
toggleSortDirection,
|
||||||
|
setView,
|
||||||
|
cycleView,
|
||||||
|
setFavoritesOnly,
|
||||||
|
toggleFavoritesOnly,
|
||||||
|
} = homeSlice.actions;
|
||||||
|
|
||||||
|
export {
|
||||||
|
type HomeState,
|
||||||
|
setSort,
|
||||||
|
setSortDirection,
|
||||||
|
toggleSortDirection,
|
||||||
|
setView,
|
||||||
|
cycleView,
|
||||||
|
setFavoritesOnly,
|
||||||
|
toggleFavoritesOnly,
|
||||||
|
};
|
||||||
|
export default homeSlice.reducer;
|
@@ -11,13 +11,16 @@ import {
|
|||||||
} from 'redux-persist';
|
} from 'redux-persist';
|
||||||
import { createRealmPersistStorage } from '@bankify/redux-persist-realm';
|
import { createRealmPersistStorage } from '@bankify/redux-persist-realm';
|
||||||
import settingsReducer from './settings';
|
import settingsReducer from './settings';
|
||||||
|
import homeReducer from './home';
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
settings: settingsReducer,
|
settings: settingsReducer,
|
||||||
|
home: homeReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
interface RootState {
|
interface RootState {
|
||||||
settings: ReturnType<typeof settingsReducer>;
|
settings: ReturnType<typeof settingsReducer>;
|
||||||
|
home: ReturnType<typeof homeReducer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const persistConfig = {
|
const persistConfig = {
|
||||||
@@ -46,3 +49,13 @@ export {
|
|||||||
updateNoMedia,
|
updateNoMedia,
|
||||||
validateSettings,
|
validateSettings,
|
||||||
} from './settings';
|
} from './settings';
|
||||||
|
export {
|
||||||
|
type HomeState,
|
||||||
|
setSort,
|
||||||
|
setSortDirection,
|
||||||
|
toggleSortDirection,
|
||||||
|
setView,
|
||||||
|
cycleView,
|
||||||
|
setFavoritesOnly,
|
||||||
|
toggleFavoritesOnly,
|
||||||
|
} from './home';
|
@@ -8,8 +8,8 @@ enum SORT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum SORT_DIRECTION {
|
enum SORT_DIRECTION {
|
||||||
ASCENDING = 'Ascending',
|
ASCENDING = 0,
|
||||||
DESCENDING = 'Descending',
|
DESCENDING = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export { SORT, SORT_DIRECTION };
|
export { SORT, SORT_DIRECTION };
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { SORT, SORT_DIRECTION } from '../types';
|
import { SORT, SORT_DIRECTION, VIEW } from '../types';
|
||||||
|
|
||||||
const getSortIcon = (sort: SORT, sortDirection: SORT_DIRECTION) => {
|
const getSortIcon = (sort: SORT, sortDirection: SORT_DIRECTION) => {
|
||||||
let sortIcon = '';
|
let sortIcon = '';
|
||||||
@@ -35,4 +35,18 @@ const getSortIcon = (sort: SORT, sortDirection: SORT_DIRECTION) => {
|
|||||||
return sortIcon;
|
return sortIcon;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getSortIcon };
|
const getViewIcon = (view: VIEW) => {
|
||||||
|
switch (view) {
|
||||||
|
case VIEW.MASONRY: {
|
||||||
|
return 'view-dashboard';
|
||||||
|
}
|
||||||
|
case VIEW.GRID: {
|
||||||
|
return 'view-grid';
|
||||||
|
}
|
||||||
|
case VIEW.LIST: {
|
||||||
|
return 'view-list';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getSortIcon, getViewIcon };
|
@@ -1,3 +1,3 @@
|
|||||||
|
export { packageName, appName, escapedAppName, noOp } from './constants';
|
||||||
export { isPermissionForPath, clearPermissions } from './permissions';
|
export { isPermissionForPath, clearPermissions } from './permissions';
|
||||||
export { getSortIcon } from './sort';
|
export { getSortIcon, getViewIcon } from './icon';
|
||||||
export { getViewIcon, getNextView } from './view';
|
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
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