Refactor dimension handling

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-13 19:03:53 +03:00
parent 703155232d
commit 4128b0df20
17 changed files with 406 additions and 250 deletions

View File

@@ -3,17 +3,18 @@ import { AppState, StatusBar, useColorScheme } from 'react-native';
import { PaperProvider } from 'react-native-paper';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { RealmProvider } from '@realm/react';
import { Provider } from 'react-redux';
import { Provider as ReduxProvider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import type {} from 'redux-thunk/extend-redux';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { lightTheme, darkTheme } from './theme';
import { Meme, Tag } from './database';
import NavigationContainer from './navigation';
import { store, persistor, validateSettings } from './state';
import { LoadingView } from './components';
import { Welcome } from './screens';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import styles from './styles';
import { DimensionsProvider } from './contexts';
const App = () => {
const [showWelcome, setShowWelcome] = useState(false);
@@ -43,28 +44,30 @@ const App = () => {
return (
<PaperProvider theme={theme}>
<Provider store={store}>
<PersistGate
loading={<LoadingView />}
persistor={persistor}
onBeforeLift={onBeforeLift}>
<RealmProvider schema={[Meme, Tag]}>
<GestureHandlerRootView style={styles.flex}>
<SafeAreaProvider>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={theme.colors.background}
/>
{showWelcome ? (
<Welcome onWelcomeComplete={() => setShowWelcome(false)} />
) : (
<NavigationContainer />
)}
</SafeAreaProvider>
</GestureHandlerRootView>
</RealmProvider>
</PersistGate>
</Provider>
<DimensionsProvider>
<ReduxProvider store={store}>
<PersistGate
loading={<LoadingView />}
persistor={persistor}
onBeforeLift={onBeforeLift}>
<RealmProvider schema={[Meme, Tag]}>
<GestureHandlerRootView style={styles.flex}>
<SafeAreaProvider>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={theme.colors.background}
/>
{showWelcome ? (
<Welcome onWelcomeComplete={() => setShowWelcome(false)} />
) : (
<NavigationContainer />
)}
</SafeAreaProvider>
</GestureHandlerRootView>
</RealmProvider>
</PersistGate>
</ReduxProvider>
</DimensionsProvider>
</PaperProvider>
);
};

View File

@@ -1,24 +1,22 @@
import * as React from 'react';
import React, { useEffect, useState } from 'react';
import { StyleSheet, Keyboard } from 'react-native';
import { FAB, Portal } from 'react-native-paper';
import { horizontalScale, verticalScale } from '../styles';
import { FAB } from 'react-native-paper';
import { ParamListBase, useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useEffect, useState } from 'react';
import { useDimensions } from '../contexts';
const styles = StyleSheet.create({
fab: {
position: 'absolute',
right: horizontalScale(10),
bottom: verticalScale(75),
},
});
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
const [state, setState] = useState(false);
const { navigate } =
useNavigation<NativeStackNavigationProp<ParamListBase>>();
const dimensions = useDimensions();
const [state, setState] = useState(false);
const [keyboardOpen, setKeyboardOpen] = useState(false);
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
@@ -37,35 +35,39 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
}, []);
return (
<Portal>
<FAB.Group
open={state}
visible={visible && !keyboardOpen}
icon={state ? 'image' : 'plus'}
actions={[
{
icon: 'tag',
label: 'Tag',
onPress: () => navigate('Add Tag'),
},
{
icon: 'note-text',
label: 'Text',
onPress: () => navigate('Add Meme'),
},
{
icon: 'image-album',
label: 'Album',
onPress: () => navigate('Add Meme'),
},
]}
onStateChange={({ open }) => setState(open)}
onPress={() => {
if (state) navigate('Add Meme');
}}
style={styles.fab}
/>
</Portal>
<FAB.Group
open={state}
visible={visible && !keyboardOpen}
icon={state ? 'image' : 'plus'}
actions={[
{
icon: 'tag',
label: 'Tag',
onPress: () => navigate('Add Tag'),
},
{
icon: 'note-text',
label: 'Text',
onPress: () => navigate('Add Meme'),
},
{
icon: 'image-album',
label: 'Album',
onPress: () => navigate('Add Meme'),
},
]}
onStateChange={({ open }) => setState(open)}
onPress={() => {
if (state) navigate('Add Meme');
}}
style={[
styles.fab,
{
paddingRight: dimensions.responsive.horizontalScale(10),
paddingBottom: dimensions.responsive.verticalScale(75),
},
]}
/>
);
};

View File

@@ -1,3 +1,5 @@
export { default as FloatingActionButton } from './floatingActionButton';
export { default as LoadingView } from './loadingView';
export { default as PaddedView } from './paddedView';
export { default as RootScrollView } from './rootScrollView';
export { default as RootView } from './rootView';
export { default as TagPreview } from './tagPreview';

View File

@@ -1,15 +1,15 @@
import React from 'react';
import { ActivityIndicator } from 'react-native';
import { useTheme } from 'react-native-paper';
import PaddedView from './paddedView';
import { RootView } from '.';
const LoadingView = () => {
const { colors } = useTheme();
return (
<PaddedView centered>
<RootView centered>
<ActivityIndicator size="large" color={colors.primary} />
</PaddedView>
</RootView>
);
};

View File

@@ -0,0 +1,37 @@
import React, { ReactNode } from 'react';
import {
StyleProp,
ScrollView,
ViewStyle,
} from 'react-native';
import { useTheme } from 'react-native-paper';
import styles from '../styles';
const RootScrollView = ({
children,
style,
centered,
padded,
}: {
children: ReactNode;
style?: StyleProp<ViewStyle>;
centered?: boolean;
padded?: boolean;
}) => {
const { colors } = useTheme();
return (
<ScrollView
contentContainerStyle={[
padded && styles.padding,
centered && [styles.centered, styles.flex],
{ backgroundColor: colors.background },
style,
]}
nestedScrollEnabled>
{children}
</ScrollView>
);
};
export default RootScrollView;

View File

@@ -3,22 +3,24 @@ import { StyleProp, View, ViewStyle } from 'react-native';
import { useTheme } from 'react-native-paper';
import styles from '../styles';
const PaddedView = ({
const RootView = ({
children,
style,
centered,
padded,
}: {
children: ReactNode;
style?: StyleProp<ViewStyle>;
centered?: boolean;
padded?: boolean;
}) => {
const { colors } = useTheme();
return (
<View
style={[
styles.padding,
centered && styles.centered,
padded && styles.padding,
centered && [styles.centered, styles.flex],
{ backgroundColor: colors.background },
style,
]}>
@@ -27,4 +29,4 @@ const PaddedView = ({
);
};
export default PaddedView;
export default RootView;

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { View } from 'react-native';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { Chip } from 'react-native-paper';
import styles from '../styles';
import { useDimensions } from '../contexts';
import { getContrastColor } from '../utilities';
const TagPreview = (properties: { name: string; color: string }) => {
const dimensions = useDimensions();
return (
<View
style={[
styles.centeredHorizontal,
styles.flexRow,
{
margin: dimensions.responsive.verticalScale(50),
},
]}>
<Chip
icon={() => {
return (
<FontAwesome5
name="tag"
size={dimensions.static.horizontalScale(12)}
color={getContrastColor(properties.color)}
/>
);
}}
elevated
style={[
{
backgroundColor: properties.color,
padding: dimensions.static.verticalScale(5),
},
]}
textStyle={[
{ fontSize: dimensions.static.horizontalScale(15) },
{ color: getContrastColor(properties.color) },
]}>
{'#' + properties.name}
</Chip>
</View>
);
};
export default TagPreview;

View File

@@ -0,0 +1,90 @@
import React, {
ReactNode,
createContext,
useContext,
useEffect,
useState,
} from 'react';
import { Dimensions, ScaledSize } from 'react-native';
const guidelineBaseWidth = 350;
const guidelineBaseHeight = 680;
interface ScaleFunctions {
horizontalScale: (size: number) => number;
verticalScale: (size: number) => number;
moderateScale: (size: number, factor?: number) => number;
}
interface DimensionsContext {
orientation: 'portrait' | 'landscape';
responsive: ScaleFunctions;
static: ScaleFunctions;
}
const createScaleFunctions = (dimensionsIn: ScaledSize) => {
const horizontalScale = (size: number) =>
(dimensionsIn.width / guidelineBaseWidth) * size;
const verticalScale = (size: number) =>
(dimensionsIn.height / guidelineBaseHeight) * size;
const moderateScale = (size: number, factor = 0.5) =>
size + (horizontalScale(size) - size) * factor;
return { horizontalScale, verticalScale, moderateScale };
};
const DimensionsContext = createContext<DimensionsContext | undefined>(
undefined,
);
const DimensionsProvider = ({ children }: { children: ReactNode }) => {
const [dimensions, setDimensions] = useState(Dimensions.get('window'));
const orientation =
dimensions.width > dimensions.height ? 'landscape' : 'portrait';
const [initialDimensions, setInitialDimensions] = useState(dimensions);
const [initialOrientation] = useState(orientation);
if (initialOrientation === 'landscape') {
setInitialDimensions({
width: initialDimensions.height,
height: initialDimensions.width,
} as ScaledSize);
}
const responsiveScale = createScaleFunctions(dimensions);
const staticScale = createScaleFunctions(initialDimensions);
useEffect(() => {
const onChange = ({ window }: { window: ScaledSize }) => {
setDimensions(window);
};
const subscription = Dimensions.addEventListener('change', onChange);
return () => {
subscription.remove();
};
}, []);
return (
<DimensionsContext.Provider
value={{
orientation,
responsive: responsiveScale,
static: staticScale,
}}>
{children}
</DimensionsContext.Provider>
);
};
const useDimensions = (): DimensionsContext => {
const context = useContext(DimensionsContext);
if (!context) {
throw new Error('useDimensions must be used within a DimensionsProvider');
}
return context;
};
export { DimensionsProvider, useDimensions };

1
src/contexts/index.ts Normal file
View File

@@ -0,0 +1 @@
export { DimensionsProvider, useDimensions } from './dimensions';

View File

@@ -6,91 +6,87 @@ import {
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { BottomNavigation, Portal, useTheme } from 'react-native-paper';
import { BottomNavigation, useTheme } from 'react-native-paper';
import { Home, Tags, Settings, AddMeme, AddTag } from './screens';
import { horizontalScale } from './styles';
import { FloatingActionButton } from './components';
import { darkNavigationTheme, lightNavigationTheme } from './theme';
import { useDimensions } from './contexts';
import { FloatingActionButton } from './components';
const TabNavigator = () => {
const dimensions = useDimensions();
const TabNavigatorBase = createBottomTabNavigator();
const [fabVisible, setFabVisible] = React.useState(true);
return (
<>
<Portal.Host>
<TabNavigatorBase.Navigator
screenOptions={{
headerShown: false,
}}
tabBar={({ navigation, state, descriptors, insets }) => (
<BottomNavigation.Bar
navigationState={state}
safeAreaInsets={insets}
onTabPress={({ route, preventDefault }) => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
<TabNavigatorBase.Navigator
screenOptions={{
headerShown: false,
}}
tabBar={({ navigation, state, descriptors, insets }) => (
<BottomNavigation.Bar
navigationState={state}
safeAreaInsets={insets}
onTabPress={({ route, preventDefault }) => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (event.defaultPrevented) {
preventDefault();
} else {
navigation.dispatch({
...CommonActions.navigate(route.name, route.params),
target: state.key,
});
if (event.defaultPrevented) {
preventDefault();
} else {
navigation.dispatch({
...CommonActions.navigate(route.name, route.params),
target: state.key,
});
}
route.name === 'Settings'
? setFabVisible(false)
: setFabVisible(true);
}}
renderIcon={({ route, focused, color }) => {
const { options } = descriptors[route.key];
if (options.tabBarIcon) {
return options.tabBarIcon({
focused,
color,
size: horizontalScale(20),
});
}
}}
getLabelText={({ route }) => {
const { options } = descriptors[route.key];
return options.title ?? route.name;
}}
/>
)}>
<TabNavigatorBase.Screen
name="Home"
component={Home}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="home" color={color} size={size} />
),
}
}}
renderIcon={({ route, focused, color }) => {
const { options } = descriptors[route.key];
if (options.tabBarIcon) {
return options.tabBarIcon({
focused,
color,
size: dimensions.static.horizontalScale(20),
});
}
}}
getLabelText={({ route }) => {
const { options } = descriptors[route.key];
return options.title ?? route.name;
}}
/>
<TabNavigatorBase.Screen
name="Tags"
component={Tags}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="tags" color={color} size={size} />
),
}}
/>
<TabNavigatorBase.Screen
name="Settings"
component={Settings}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="cog" color={color} size={size} />
),
}}
/>
</TabNavigatorBase.Navigator>
<FloatingActionButton visible={fabVisible} />
</Portal.Host>
)}>
<TabNavigatorBase.Screen
name="Home"
component={Home}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="home" color={color} size={size} />
),
}}
/>
<TabNavigatorBase.Screen
name="Tags"
component={Tags}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="tags" color={color} size={size} />
),
}}
/>
<TabNavigatorBase.Screen
name="Settings"
component={Settings}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="cog" color={color} size={size} />
),
}}
/>
</TabNavigatorBase.Navigator>
<FloatingActionButton />
</>
);
};

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Appbar, Text } from 'react-native-paper';
import { PaddedView } from '../components';
import { RootScrollView } from '../components';
import { useNavigation } from '@react-navigation/native';
const AddMeme = () => {
@@ -12,9 +12,9 @@ const AddMeme = () => {
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Add Meme" />
</Appbar.Header>
<PaddedView centered>
<RootScrollView centered padded>
<Text>Add Meme</Text>
</PaddedView>
</RootScrollView>
</>
);
};

View File

@@ -1,37 +1,12 @@
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import {
Chip,
TextInput,
Appbar,
HelperText,
Button,
} from 'react-native-paper';
import { View } from 'react-native';
import { TextInput, Appbar, HelperText, Button } from 'react-native-paper';
import { useNavigation } from '@react-navigation/native';
import { BSON } from 'realm';
import { useRealm } from '@realm/react';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { PaddedView } from '../components';
import styles, { horizontalScale, verticalScale } from '../styles';
import {
generateRandomColor,
getContrastColor,
isValidColor,
} from '../utilities';
const tagStyles = StyleSheet.create({
preview: {
justifyContent: 'center',
flexDirection: 'row',
marginVertical: verticalScale(75),
},
chip: {
padding: horizontalScale(5),
},
chipText: {
fontSize: horizontalScale(15),
},
});
import { RootScrollView, TagPreview } from '../components';
import styles from '../styles';
import { generateRandomColor, isValidColor } from '../utilities';
const AddTag = () => {
const navigation = useNavigation();
@@ -87,34 +62,19 @@ const AddTag = () => {
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Add Tag" />
</Appbar.Header>
<PaddedView style={[styles.flex, styles.flexColumnSpaceBetween]}>
<View>
<View style={[tagStyles.preview]}>
<Chip
icon={() => {
return (
<FontAwesome5
name="tag"
size={horizontalScale(12)}
color={getContrastColor(validatedTagColor)}
/>
);
}}
elevated
style={[tagStyles.chip, { backgroundColor: validatedTagColor }]}
textStyle={[
tagStyles.chipText,
{ color: getContrastColor(validatedTagColor) },
]}>
{'#' + tagName}
</Chip>
</View>
<RootScrollView
padded
style={[styles.flexGrow, styles.flexColumnSpaceBetween]}>
<View style={[styles.flex, styles.justifyStart]}>
<TagPreview name={tagName} color={validatedTagColor} />
<TextInput
mode="outlined"
label="Tag Name"
value={tagName}
onChangeText={handleTagNameChange}
error={!!tagNameError}
autoCapitalize="none"
selectTextOnFocus
/>
<HelperText type="error" visible={!!tagNameError}>
{tagNameError}
@@ -125,6 +85,7 @@ const AddTag = () => {
value={tagColor}
onChangeText={handleTagColorChange}
error={!!tagColorError}
autoCorrect={false}
right={
<TextInput.Icon
icon="palette"
@@ -136,14 +97,16 @@ const AddTag = () => {
{tagColorError}
</HelperText>
</View>
<Button
mode="contained"
icon="floppy"
onPress={handleSave}
disabled={!!tagNameError || !!tagColorError}>
Save
</Button>
</PaddedView>
<View style={[styles.flex, styles.justifyEnd]}>
<Button
mode="contained"
icon="floppy"
onPress={handleSave}
disabled={!!tagNameError || !!tagColorError}>
Save
</Button>
</View>
</RootScrollView>
</>
);
};

View File

@@ -9,8 +9,8 @@ import {
Searchbar,
} from 'react-native-paper';
import { useDispatch, useSelector } from 'react-redux';
import { PaddedView } from '../components';
import styles, { verticalScale } from '../styles';
import { RootScrollView } from '../components';
import styles from '../styles';
import { SORT, SORT_DIRECTION } from '../types';
import { getSortIcon, getViewIcon } from '../utilities';
import {
@@ -23,10 +23,11 @@ import {
setFilter,
} from '../state';
import { MEME_TYPE, memeTypePlural } from '../database';
import { useDimensions } from '../contexts';
const Home = () => {
const theme = useTheme();
const dimensions = useDimensions();
const sort = useSelector((state: RootState) => state.home.sort);
const sortDirection = useSelector(
(state: RootState) => state.home.sortDirection,
@@ -63,7 +64,7 @@ const Home = () => {
const [search, setSearch] = useState('');
return (
<PaddedView>
<RootScrollView padded>
<Searchbar placeholder="Search" value={search} onChangeText={setSearch} />
<View style={[styles.flexRowSpaceBetween, styles.centeredVertical]}>
<View style={[styles.flexRow, styles.centeredVertical]}>
@@ -96,13 +97,13 @@ const Home = () => {
<IconButton
icon={getViewIcon(view)}
iconColor={theme.colors.primary}
size={verticalScale(16)}
size={dimensions.static.verticalScale(16)}
onPress={() => dispatch(cycleView())}
/>
<IconButton
icon={favoritesOnly ? 'heart' : 'heart-outline'}
iconColor={theme.colors.primary}
size={verticalScale(16)}
size={dimensions.static.verticalScale(16)}
onPress={() => dispatch(toggleFavoritesOnly())}
/>
<Menu
@@ -113,7 +114,7 @@ const Home = () => {
onPress={() => setFilterMenuVisible(true)}
icon={filter ? 'filter' : 'filter-outline'}
iconColor={theme.colors.primary}
size={verticalScale(16)}
size={dimensions.static.verticalScale(16)}
/>
}>
<Menu.Item
@@ -138,7 +139,7 @@ const Home = () => {
</View>
</View>
<Divider />
</PaddedView>
</RootScrollView>
);
};

View File

@@ -4,18 +4,19 @@ import { Button, List, Snackbar, Switch, Text } from 'react-native-paper';
import { useRealm } from '@realm/react';
import { openDocumentTree } from 'react-native-scoped-storage';
import { useDispatch, useSelector } from 'react-redux';
import { PaddedView } from '../components';
import { RootScrollView } from '../components';
import styles from '../styles';
import { Meme } from '../database';
import { RootState, updateNoMedia, updateStorageUri } from '../state';
import type {} from 'redux-thunk/extend-redux';
import { useDimensions } from '../contexts';
const SettingsScreen = () => {
const [optimizingDatabase, setOptimizingDatabase] = useState(false);
const noMedia = useSelector((state: RootState) => state.settings.noMedia);
const dispatch = useDispatch();
const dimensions = useDimensions();
const [optimizingDatabase, setOptimizingDatabase] = useState(false);
const [snackbarVisible, setSnackbarVisible] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState('');
@@ -45,13 +46,15 @@ const SettingsScreen = () => {
return (
<>
<PaddedView>
<RootScrollView padded>
<View>
<List.Section>
<List.Subheader>Database</List.Subheader>
<Button
mode="elevated"
style={styles.marginBottom}
style={{
marginBottom: dimensions.responsive.verticalScale(15),
}}
loading={optimizingDatabase}
onPress={optimizeDatabase}>
Optimize Database Now
@@ -61,7 +64,9 @@ const SettingsScreen = () => {
<List.Subheader>Media Storage</List.Subheader>
<Button
mode="elevated"
style={styles.marginBottom}
style={{
marginBottom: dimensions.responsive.verticalScale(15),
}}
onPress={async () => {
const { uri } = await openDocumentTree(true);
void dispatch(updateStorageUri(uri));
@@ -72,7 +77,9 @@ const SettingsScreen = () => {
style={[
styles.flexRowSpaceBetween,
styles.smallPaddingHorizontal,
styles.marginBottom,
{
marginBottom: dimensions.responsive.verticalScale(15),
},
]}>
<Text>Hide media from gallery</Text>
<Switch
@@ -84,7 +91,7 @@ const SettingsScreen = () => {
</View>
</List.Section>
</View>
</PaddedView>
</RootScrollView>
<Snackbar
visible={snackbarVisible}
onDismiss={() => setSnackbarVisible(false)}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Button, Text } from 'react-native-paper';
import { PaddedView } from '../components';
import { RootScrollView } from '../components';
import { useQuery, useRealm } from '@realm/react';
import { Tag, deleteAllTags } from '../database';
@@ -10,14 +10,14 @@ const Tags = () => {
const tags = useQuery<Tag>('Tag');
return (
<PaddedView centered>
<RootScrollView centered padded>
{tags.map(tag => (
<Text key={tag.id.toHexString()} style={{ color: tag.color }}>
{tag.name}
</Text>
))}
<Button onPress={() => deleteAllTags(realm)}>Delete All Tags</Button>
</PaddedView>
</RootScrollView>
);
};

View File

@@ -2,13 +2,15 @@ import React from 'react';
import { Button, Text } from 'react-native-paper';
import { useDispatch } from 'react-redux';
import { openDocumentTree } from 'react-native-scoped-storage';
import { PaddedView } from '../components';
import { RootView } from '../components';
import styles from '../styles';
import { noOp } from '../utilities';
import { updateStorageUri } from '../state';
import { useDimensions } from '../contexts';
const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => {
const dispatch = useDispatch();
const dimensions = useDimensions();
const selectStorageLocation = async () => {
const uri = await openDocumentTree(true).catch(noOp);
@@ -18,19 +20,26 @@ const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => {
};
return (
<PaddedView centered>
<RootView centered padded>
<Text
variant="displayMedium"
style={[styles.bigMarginBottom, styles.centerText]}>
style={[
{
marginBottom: dimensions.responsive.verticalScale(30),
},
styles.centerText,
]}>
Welcome to Terminally Online!
</Text>
<Button
mode="contained"
onPress={selectStorageLocation}
style={styles.extremeMarginBottom}>
style={{
marginBottom: dimensions.responsive.verticalScale(100),
}}>
Select Storage Location
</Button>
</PaddedView>
</RootView>
);
};

View File

@@ -1,24 +1,8 @@
import { StyleSheet, Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
const guidelineBaseWidth = 350;
const guidelineBaseHeight = 680;
const horizontalScale = (size: number) => (width / guidelineBaseWidth) * size;
const verticalScale = (size: number) => (height / guidelineBaseHeight) * size;
const moderateScale = (size: number, factor = 0.5) =>
size + (horizontalScale(size) - size) * factor;
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
marginBottom: {
marginBottom: verticalScale(15),
},
bigMarginBottom: {
marginBottom: verticalScale(30),
},
extremeMarginBottom: {
marginBottom: verticalScale(100),
smallPadding: {
padding: '2.5%',
},
padding: {
padding: '5%',
@@ -27,19 +11,24 @@ const styles = StyleSheet.create({
paddingHorizontal: '2.5%',
},
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
centeredVertical: {
alignItems: 'center',
},
centeredHorizontal: {
justifyContent: 'center',
},
centerText: {
textAlign: 'center',
},
flex: {
flex: 1,
},
flexGrow: {
flexGrow: 1,
},
flexRow: {
flexDirection: 'row',
},
@@ -57,6 +46,12 @@ const styles = StyleSheet.create({
flexRowReverse: {
flexDirection: 'row-reverse',
},
justifyStart: {
justifyContent: 'flex-start',
},
justifyEnd: {
justifyContent: 'flex-end',
},
});
export { horizontalScale, verticalScale, moderateScale, styles as default };
export default styles;