Refactor settings to use stack to allow dark theme. Move theme to app reducer to allow persistence.

This commit is contained in:
Patrick Fic
2025-10-23 13:58:33 -07:00
parent f170192008
commit b1ee922066
15 changed files with 60 additions and 23 deletions

View File

@@ -1,5 +0,0 @@
import Settings from "../components/settings/settings";
export default function Tab() {
return <Settings />;
}

27
app/settings/_layout.tsx Normal file
View File

@@ -0,0 +1,27 @@
import { Stack, useRouter } from "expo-router";
export default function SettingsLayout() {
const router = useRouter();
return (
<Stack>
<Stack.Screen
name="index"
options={{
title: "Settings",
headerShown: false,
// headerSearchBarOptions: {
// placement: "automatic",
// placeholder: "Search",
// autoFocus: true,
// shouldShowHintSearchIcon: true,
// onChangeText: (event) => {
// router.setParams({
// globalSearch: event?.nativeEvent?.text,
// });
// },
// },
}}
/>
</Stack>
);
}

5
app/settings/index.tsx Normal file
View File

@@ -0,0 +1,5 @@
import Settings from "../../components/settings/settings";
export default function Tab() {
return <Settings />;
}

View File

@@ -107,7 +107,7 @@ function JobListItemComponent({ openImagePicker, item }) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
pressable: { pressable: {
marginHorizontal: 12, // marginHorizontal: 12,
marginVertical: 6, marginVertical: 6,
}, },
outerShadow: { outerShadow: {

View File

@@ -1,8 +1,8 @@
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FlatList, RefreshControl, Text, View } from "react-native"; import { FlatList, RefreshControl, View } from "react-native";
import { ActivityIndicator, Button } from "react-native-paper"; import { ActivityIndicator, Button, Text } from "react-native-paper";
import { SafeAreaView } from "react-native-safe-area-context"; import { SafeAreaView } from "react-native-safe-area-context";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -48,7 +48,13 @@ export function JobListComponent({ bodyshop }) {
const jobs = data ? [...(data?.jobs || []), { id: "footer-spacer" }] : []; const jobs = data ? [...(data?.jobs || []), { id: "footer-spacer" }] : [];
return ( return (
<SafeAreaView style={{ flex: 1 }}> <SafeAreaView style={{ flex: 1, marginHorizontal: 12 }}>
<Text
variant="headlineMedium"
style={{ marginBottom: 12, fontWeight: "600" }}
>
Jobs
</Text>
<UploadProgress /> <UploadProgress />
<FlatList <FlatList
refreshControl={ refreshControl={

View File

@@ -1,5 +1,5 @@
import { setTheme } from "@/redux/user/user.actions"; import { setTheme } from "@/redux/app/app.actions";
import { selectTheme } from "@/redux/user/user.selectors"; import { selectTheme } from "@/redux/app/app.selectors";
import React from "react"; import React from "react";
import { SegmentedButtons } from "react-native-paper"; import { SegmentedButtons } from "react-native-paper";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";

View File

@@ -1,4 +1,4 @@
import { selectTheme } from "@/redux/user/user.selectors"; import { selectTheme } from "@/redux/app/app.selectors";
import { darkTheme, lightTheme } from "@/util/theme"; import { darkTheme, lightTheme } from "@/util/theme";
import { useColorScheme } from "react-native"; import { useColorScheme } from "react-native";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";

View File

@@ -27,3 +27,8 @@ export const documentUploadFailure = (error) => ({
export const toggleDeleteAfterUpload = () => ({ export const toggleDeleteAfterUpload = () => ({
type: AppActionTypes.TOGGLE_DLETE_AFTER_UPLOAD, type: AppActionTypes.TOGGLE_DLETE_AFTER_UPLOAD,
}); });
export const setTheme = (theme) => ({
type: AppActionTypes.SET_THEME,
payload: theme,
});

View File

@@ -6,6 +6,7 @@ const INITIAL_STATE = {
documentUploadInProgress: null, documentUploadInProgress: null,
documentUploadError: null, documentUploadError: null,
deleteAfterUpload: false, deleteAfterUpload: false,
theme: "system",
}; };
const appReducer = (state = INITIAL_STATE, action) => { const appReducer = (state = INITIAL_STATE, action) => {
@@ -43,6 +44,8 @@ const appReducer = (state = INITIAL_STATE, action) => {
...state, ...state,
deleteAfterUpload: !state.deleteAfterUpload, deleteAfterUpload: !state.deleteAfterUpload,
}; };
case AppActionTypes.SET_THEME:
return { ...state, theme: action.payload };
default: default:
return state; return state;
} }

View File

@@ -26,3 +26,8 @@ export const selectDeleteAfterUpload = createSelector(
[selectApp], [selectApp],
(app) => app.deleteAfterUpload (app) => app.deleteAfterUpload
); );
export const selectTheme = createSelector(
[selectApp],
(app) => app.theme
);

View File

@@ -5,5 +5,6 @@ const AppActionTypes = {
DOCUMENT_UPLOAD_SUCCESS: "DOCUMENT_UPLOAD_SUCCESS", DOCUMENT_UPLOAD_SUCCESS: "DOCUMENT_UPLOAD_SUCCESS",
DOCUMENT_UPLOAD_FAILURE: "DOCUMENT_UPLOAD_FAILURE", DOCUMENT_UPLOAD_FAILURE: "DOCUMENT_UPLOAD_FAILURE",
TOGGLE_DLETE_AFTER_UPLOAD: "TOGGLE_DLETE_AFTER_UPLOAD", TOGGLE_DLETE_AFTER_UPLOAD: "TOGGLE_DLETE_AFTER_UPLOAD",
SET_THEME: "SET_THEME",
}; };
export default AppActionTypes; export default AppActionTypes;

View File

@@ -98,7 +98,3 @@ export const validatePasswordResetFailure = (error) => ({
type: UserActionTypes.VALIDATE_PASSWORD_RESET_FAILURE, type: UserActionTypes.VALIDATE_PASSWORD_RESET_FAILURE,
payload: error, payload: error,
}); });
export const setTheme = (theme) => ({
type: UserActionTypes.SET_THEME,
payload: theme,
});

View File

@@ -49,8 +49,7 @@ const userReducer = (state = INITIAL_STATE, action) => {
case UserActionTypes.SET_SHOP_DETAILS: case UserActionTypes.SET_SHOP_DETAILS:
return { ...state, bodyshop: action.payload }; return { ...state, bodyshop: action.payload };
case UserActionTypes.SET_THEME:
return { ...state, theme: action.payload };
case UserActionTypes.SIGN_IN_FAILURE: case UserActionTypes.SIGN_IN_FAILURE:
case UserActionTypes.SIGN_OUT_FAILURE: case UserActionTypes.SIGN_OUT_FAILURE:
case UserActionTypes.EMAIL_SIGN_UP_FAILURE: case UserActionTypes.EMAIL_SIGN_UP_FAILURE:

View File

@@ -32,7 +32,3 @@ export const selectSigningIn = createSelector(
(user) => user.signingIn (user) => user.signingIn
); );
export const selectTheme = createSelector(
[selectUser],
(user) => user.theme
);

View File

@@ -26,6 +26,5 @@ const UserActionTypes = {
VALIDATE_PASSWORD_RESET_START: "VALIDATE_PASSWORD_RESET_START", VALIDATE_PASSWORD_RESET_START: "VALIDATE_PASSWORD_RESET_START",
VALIDATE_PASSWORD_RESET_SUCCESS: "VALIDATE_PASSWORD_RESET_SUCCESS", VALIDATE_PASSWORD_RESET_SUCCESS: "VALIDATE_PASSWORD_RESET_SUCCESS",
VALIDATE_PASSWORD_RESET_FAILURE: "VALIDATE_PASSWORD_RESET_FAILURE", VALIDATE_PASSWORD_RESET_FAILURE: "VALIDATE_PASSWORD_RESET_FAILURE",
SET_THEME: "SET_THEME",
}; };
export default UserActionTypes; export default UserActionTypes;