Add root snackbar
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Keyboard, StyleSheet } from 'react-native';
|
||||
import { FAB, Snackbar } from 'react-native-paper';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { FAB } from 'react-native-paper';
|
||||
import { ParamListBase, useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
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 { documentPickerResponseToAddMemeFile, ROUTE } from '../types';
|
||||
import {
|
||||
@@ -13,6 +16,8 @@ import {
|
||||
guessMimeType,
|
||||
noOp,
|
||||
} from '../utilities';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setSnackbarMessage } from '../state';
|
||||
|
||||
const floatingActionButtonStyles = StyleSheet.create({
|
||||
fab: {
|
||||
@@ -23,36 +28,16 @@ const floatingActionButtonStyles = StyleSheet.create({
|
||||
paddingBottom: 40,
|
||||
paddingRight: 12,
|
||||
},
|
||||
snackbar: {
|
||||
marginBottom: 90,
|
||||
},
|
||||
});
|
||||
|
||||
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
const { navigate } =
|
||||
useNavigation<NativeStackNavigationProp<ParamListBase>>();
|
||||
const orientation = useDeviceOrientation();
|
||||
const keyboardOpen = useKeyboard().keyboardShown;
|
||||
const dispatch = useDispatch();
|
||||
|
||||
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 response = await pick({
|
||||
@@ -71,12 +56,12 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
const handlePaste = useCallback(async () => {
|
||||
const uri = await Clipboard.getURI();
|
||||
if (!uri) {
|
||||
setSnackbarMessage('Clipboard does not contain a URI.');
|
||||
dispatch(setSnackbarMessage('Clipboard does not contain a URI.'));
|
||||
return;
|
||||
}
|
||||
const mimeType = guessMimeType(uri);
|
||||
if (!mimeType) {
|
||||
setSnackbarMessage('Unsupported MIME type.');
|
||||
dispatch(setSnackbarMessage('Unsupported MIME type.'));
|
||||
return;
|
||||
}
|
||||
navigate(ROUTE.ADD_MEME, {
|
||||
@@ -88,7 +73,7 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
},
|
||||
],
|
||||
});
|
||||
}, [navigate]);
|
||||
}, [dispatch, navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -120,18 +105,6 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
: 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -7,8 +7,8 @@ import {
|
||||
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { useTheme } from 'react-native-paper';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Snackbar, useTheme } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
Memes,
|
||||
Tags,
|
||||
@@ -32,11 +32,28 @@ import {
|
||||
} from './types';
|
||||
import { RootState } from './state';
|
||||
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 navVisible = useSelector(
|
||||
(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 TabNavigatorBase = createBottomTabNavigator();
|
||||
@@ -87,6 +104,22 @@ const TabNavigator = () => {
|
||||
/>
|
||||
</TabNavigatorBase.Navigator>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -5,7 +5,6 @@ import {
|
||||
List,
|
||||
Portal,
|
||||
SegmentedButtons,
|
||||
Snackbar,
|
||||
Switch,
|
||||
Text,
|
||||
useTheme,
|
||||
@@ -17,6 +16,7 @@ import {
|
||||
setGridColumns,
|
||||
setMasonryColumns,
|
||||
setNoMedia,
|
||||
setSnackbarMessage,
|
||||
} from '../state';
|
||||
import StorageLocationChangeDialog from '../components/storageLocationChangeDialog';
|
||||
import { useRealm } from '@realm/react';
|
||||
@@ -27,9 +27,6 @@ const settingsStyles = StyleSheet.create({
|
||||
scrollView: {
|
||||
paddingHorizontal: '4%',
|
||||
},
|
||||
snackbar: {
|
||||
marginBottom: 90,
|
||||
},
|
||||
marginBottom: {
|
||||
marginBottom: 15,
|
||||
},
|
||||
@@ -60,8 +57,6 @@ const Settings = () => {
|
||||
const dispatch = useDispatch();
|
||||
const realm = useRealm();
|
||||
|
||||
const [snackbarMessage, setSnackbarMessage] = useState<string>();
|
||||
|
||||
const [
|
||||
storageLocationChangeDialogVisible,
|
||||
setStorageLocationChangeDialogVisible,
|
||||
@@ -82,7 +77,7 @@ const Settings = () => {
|
||||
});
|
||||
});
|
||||
|
||||
setSnackbarMessage('Meme metadata refreshed.');
|
||||
dispatch(setSnackbarMessage('Meme metadata refreshed.'));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -156,25 +151,13 @@ const Settings = () => {
|
||||
</List.Section>
|
||||
</ScrollView>
|
||||
<Portal>
|
||||
<Portal>
|
||||
<StorageLocationChangeDialog
|
||||
visible={storageLocationChangeDialogVisible}
|
||||
setVisible={setStorageLocationChangeDialogVisible}
|
||||
setSnackbarMessage={setSnackbarMessage}
|
||||
/>
|
||||
</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>
|
||||
<StorageLocationChangeDialog
|
||||
visible={storageLocationChangeDialogVisible}
|
||||
setVisible={setStorageLocationChangeDialogVisible}
|
||||
setSnackbarMessage={message => {
|
||||
dispatch(setSnackbarMessage(message));
|
||||
}}
|
||||
/>
|
||||
</Portal>
|
||||
</>
|
||||
);
|
||||
|
@@ -79,4 +79,5 @@ export {
|
||||
type NavigationState,
|
||||
setNavVisible,
|
||||
toggleNavVisible,
|
||||
setSnackbarMessage,
|
||||
} from './navigation';
|
||||
|
@@ -2,10 +2,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface NavigationState {
|
||||
navVisible: boolean;
|
||||
snackbarMessage: string | undefined;
|
||||
}
|
||||
|
||||
const initialState: NavigationState = {
|
||||
navVisible: true,
|
||||
snackbarMessage: undefined,
|
||||
};
|
||||
|
||||
const navigationSlice = createSlice({
|
||||
@@ -18,14 +20,19 @@ const navigationSlice = createSlice({
|
||||
toggleNavVisible: state => {
|
||||
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 {
|
||||
type NavigationState,
|
||||
setNavVisible,
|
||||
toggleNavVisible,
|
||||
setSnackbarMessage,
|
||||
};
|
||||
export default navigationSlice.reducer;
|
||||
|
Reference in New Issue
Block a user