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 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>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -79,4 +79,5 @@ export {
|
|||||||
type NavigationState,
|
type NavigationState,
|
||||||
setNavVisible,
|
setNavVisible,
|
||||||
toggleNavVisible,
|
toggleNavVisible,
|
||||||
|
setSnackbarMessage,
|
||||||
} from './navigation';
|
} from './navigation';
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user