Add root snackbar

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-29 23:11:00 +03:00
parent 5770a9b234
commit e794832f38
5 changed files with 67 additions and 70 deletions

View File

@@ -1,10 +1,13 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Keyboard, StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import { FAB, Snackbar } from 'react-native-paper'; 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 { pick } from 'react-native-document-picker'; import { pick } from 'react-native-document-picker';
import { useDeviceOrientation } from '@react-native-community/hooks'; import {
useDeviceOrientation,
useKeyboard,
} from '@react-native-community/hooks';
import Clipboard from '@react-native-clipboard/clipboard'; import Clipboard from '@react-native-clipboard/clipboard';
import { documentPickerResponseToAddMemeFile, ROUTE } from '../types'; import { documentPickerResponseToAddMemeFile, ROUTE } from '../types';
import { import {
@@ -13,6 +16,8 @@ import {
guessMimeType, guessMimeType,
noOp, noOp,
} from '../utilities'; } from '../utilities';
import { useDispatch } from 'react-redux';
import { setSnackbarMessage } from '../state';
const floatingActionButtonStyles = StyleSheet.create({ const floatingActionButtonStyles = StyleSheet.create({
fab: { fab: {
@@ -23,36 +28,16 @@ const floatingActionButtonStyles = StyleSheet.create({
paddingBottom: 40, paddingBottom: 40,
paddingRight: 12, paddingRight: 12,
}, },
snackbar: {
marginBottom: 90,
},
}); });
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => { const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
const { navigate } = const { navigate } =
useNavigation<NativeStackNavigationProp<ParamListBase>>(); useNavigation<NativeStackNavigationProp<ParamListBase>>();
const orientation = useDeviceOrientation(); const orientation = useDeviceOrientation();
const keyboardOpen = useKeyboard().keyboardShown;
const dispatch = useDispatch();
const [state, setState] = useState(false); const [state, setState] = useState(false);
const [keyboardOpen, setKeyboardOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState<string>();
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => setKeyboardOpen(true),
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => setKeyboardOpen(false),
);
return () => {
keyboardDidShowListener.remove();
keyboardDidHideListener.remove();
};
}, []);
const handleAddMeme = useCallback(async () => { const handleAddMeme = useCallback(async () => {
const response = await pick({ const response = await pick({
@@ -71,12 +56,12 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
const handlePaste = useCallback(async () => { const handlePaste = useCallback(async () => {
const uri = await Clipboard.getURI(); const uri = await Clipboard.getURI();
if (!uri) { if (!uri) {
setSnackbarMessage('Clipboard does not contain a URI.'); dispatch(setSnackbarMessage('Clipboard does not contain a URI.'));
return; return;
} }
const mimeType = guessMimeType(uri); const mimeType = guessMimeType(uri);
if (!mimeType) { if (!mimeType) {
setSnackbarMessage('Unsupported MIME type.'); dispatch(setSnackbarMessage('Unsupported MIME type.'));
return; return;
} }
navigate(ROUTE.ADD_MEME, { navigate(ROUTE.ADD_MEME, {
@@ -88,7 +73,7 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
}, },
], ],
}); });
}, [navigate]); }, [dispatch, navigate]);
return ( return (
<> <>
@@ -120,18 +105,6 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
: floatingActionButtonStyles.fabLandscape : floatingActionButtonStyles.fabLandscape
} }
/> />
<Snackbar
visible={!!snackbarMessage}
// eslint-disable-next-line unicorn/no-useless-undefined
onDismiss={() => setSnackbarMessage(undefined)}
style={floatingActionButtonStyles.snackbar}
action={{
label: 'Dismiss',
// eslint-disable-next-line unicorn/no-useless-undefined
onPress: () => setSnackbarMessage(undefined),
}}>
{snackbarMessage}
</Snackbar>
</> </>
); );
}; };

View File

@@ -7,8 +7,8 @@ import {
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 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 { Snackbar, useTheme } from 'react-native-paper';
import { useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { import {
Memes, Memes,
Tags, Tags,
@@ -32,11 +32,28 @@ import {
} from './types'; } from './types';
import { RootState } from './state'; import { RootState } from './state';
import ShareMenu from 'react-native-share-menu'; import ShareMenu from 'react-native-share-menu';
import { setSnackbarMessage } from './state/navigation';
import { StyleSheet } from 'react-native';
import { useKeyboard } from '@react-native-community/hooks';
const tabNavigatorStyles = StyleSheet.create({
snackbar: {
marginBottom: 90,
},
snackbarKeyboard: {
marginBottom: 10,
},
});
const TabNavigator = () => { const TabNavigator = () => {
const navVisible = useSelector( const navVisible = useSelector(
(state: RootState) => state.navigation.navVisible, (state: RootState) => state.navigation.navVisible,
); );
const snackbarMessage = useSelector(
(state: RootState) => state.navigation.snackbarMessage,
);
const dispatch = useDispatch();
const keyboardOpen = useKeyboard().keyboardShown;
const [route, setRoute] = React.useState(ROUTE.MEMES); const [route, setRoute] = React.useState(ROUTE.MEMES);
const TabNavigatorBase = createBottomTabNavigator(); const TabNavigatorBase = createBottomTabNavigator();
@@ -87,6 +104,22 @@ const TabNavigator = () => {
/> />
</TabNavigatorBase.Navigator> </TabNavigatorBase.Navigator>
<FloatingActionButton visible={navVisible && route !== ROUTE.SETTINGS} /> <FloatingActionButton visible={navVisible && route !== ROUTE.SETTINGS} />
<Snackbar
visible={!!snackbarMessage}
// eslint-disable-next-line unicorn/no-useless-undefined
onDismiss={() => dispatch(setSnackbarMessage(undefined))}
style={
keyboardOpen
? tabNavigatorStyles.snackbarKeyboard
: tabNavigatorStyles.snackbar
}
action={{
label: 'Dismiss',
// eslint-disable-next-line unicorn/no-useless-undefined
onPress: () => dispatch(setSnackbarMessage(undefined)),
}}>
{snackbarMessage}
</Snackbar>
</> </>
); );
}; };

View File

@@ -5,7 +5,6 @@ import {
List, List,
Portal, Portal,
SegmentedButtons, SegmentedButtons,
Snackbar,
Switch, Switch,
Text, Text,
useTheme, useTheme,
@@ -17,6 +16,7 @@ import {
setGridColumns, setGridColumns,
setMasonryColumns, setMasonryColumns,
setNoMedia, setNoMedia,
setSnackbarMessage,
} from '../state'; } from '../state';
import StorageLocationChangeDialog from '../components/storageLocationChangeDialog'; import StorageLocationChangeDialog from '../components/storageLocationChangeDialog';
import { useRealm } from '@realm/react'; import { useRealm } from '@realm/react';
@@ -27,9 +27,6 @@ const settingsStyles = StyleSheet.create({
scrollView: { scrollView: {
paddingHorizontal: '4%', paddingHorizontal: '4%',
}, },
snackbar: {
marginBottom: 90,
},
marginBottom: { marginBottom: {
marginBottom: 15, marginBottom: 15,
}, },
@@ -60,8 +57,6 @@ const Settings = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const realm = useRealm(); const realm = useRealm();
const [snackbarMessage, setSnackbarMessage] = useState<string>();
const [ const [
storageLocationChangeDialogVisible, storageLocationChangeDialogVisible,
setStorageLocationChangeDialogVisible, setStorageLocationChangeDialogVisible,
@@ -82,7 +77,7 @@ const Settings = () => {
}); });
}); });
setSnackbarMessage('Meme metadata refreshed.'); dispatch(setSnackbarMessage('Meme metadata refreshed.'));
}; };
return ( return (
@@ -156,25 +151,13 @@ const Settings = () => {
</List.Section> </List.Section>
</ScrollView> </ScrollView>
<Portal> <Portal>
<Portal> <StorageLocationChangeDialog
<StorageLocationChangeDialog visible={storageLocationChangeDialogVisible}
visible={storageLocationChangeDialogVisible} setVisible={setStorageLocationChangeDialogVisible}
setVisible={setStorageLocationChangeDialogVisible} setSnackbarMessage={message => {
setSnackbarMessage={setSnackbarMessage} dispatch(setSnackbarMessage(message));
/> }}
</Portal> />
<Snackbar
visible={!!snackbarMessage}
// eslint-disable-next-line unicorn/no-useless-undefined
onDismiss={() => setSnackbarMessage(undefined)}
style={settingsStyles.snackbar}
action={{
label: 'Dismiss',
// eslint-disable-next-line unicorn/no-useless-undefined
onPress: () => setSnackbarMessage(undefined),
}}>
{snackbarMessage}
</Snackbar>
</Portal> </Portal>
</> </>
); );

View File

@@ -79,4 +79,5 @@ export {
type NavigationState, type NavigationState,
setNavVisible, setNavVisible,
toggleNavVisible, toggleNavVisible,
setSnackbarMessage,
} from './navigation'; } from './navigation';

View File

@@ -2,10 +2,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface NavigationState { interface NavigationState {
navVisible: boolean; navVisible: boolean;
snackbarMessage: string | undefined;
} }
const initialState: NavigationState = { const initialState: NavigationState = {
navVisible: true, navVisible: true,
snackbarMessage: undefined,
}; };
const navigationSlice = createSlice({ const navigationSlice = createSlice({
@@ -18,14 +20,19 @@ const navigationSlice = createSlice({
toggleNavVisible: state => { toggleNavVisible: state => {
state.navVisible = !state.navVisible; state.navVisible = !state.navVisible;
}, },
setSnackbarMessage: (state, action: PayloadAction<string | undefined>) => {
state.snackbarMessage = action.payload;
},
}, },
}); });
const { setNavVisible, toggleNavVisible } = navigationSlice.actions; const { setNavVisible, toggleNavVisible, setSnackbarMessage } =
navigationSlice.actions;
export { export {
type NavigationState, type NavigationState,
setNavVisible, setNavVisible,
toggleNavVisible, toggleNavVisible,
setSnackbarMessage,
}; };
export default navigationSlice.reducer; export default navigationSlice.reducer;