small refactoring

This commit is contained in:
2025-09-24 02:42:54 +02:00
parent e977f20d56
commit ec3093cbf3
4 changed files with 125 additions and 79 deletions
+106 -56
View File
@@ -1,11 +1,12 @@
import {Link, Tabs} from 'expo-router';
import React from "react";
import React, {createContext, useContext, useState, useEffect } from "react";
import {Feather} from "@expo/vector-icons";
import {useTheme} from "@/src/context/ThemeProvider";
import AuthenticatedWrapper from "@/src/components/AuthenticatedWrapper";
import {Dimensions} from "react-native";
import { useWindowDimensions } from "react-native";
import {MatrixState} from "@/src/model/User";
import {useAuth} from "@/src/context/AuthProvider";
const screenWidth = Dimensions.get("window").width;
const tabs = [
@@ -16,66 +17,115 @@ const tabs = [
{name: 'modes/clock', title: 'Uhr', icon: 'clock'},
] as const;
type MatrixContextType = {
matrixState: MatrixState;
updateMatrixState: (newState: Partial<MatrixState>) => void;
};
const MatrixContext = createContext<MatrixContextType | undefined>(undefined);
export const useMatrix = () => {
const context = useContext(MatrixContext);
if (!context) throw new Error("useMatrix must be used within a MatrixProvider");
return context;
};
const getInitialState = (lastState?: MatrixState | null): MatrixState => {
const defaults: MatrixState = {
global: { mode: 'idle', brightness: 100 },
text: { text: 'Hello World', align: 'center', speed: 50, size: 16, color: [255, 255, 255] },
image: { image: 'dino.gif' },
clock: { color: [0, 255, 0] },
music: { fullscreen: false }
};
return {
...defaults,
...(lastState || {}),
global: { ...defaults.global, ...(lastState?.global || {}) },
text: { ...defaults.text, ...(lastState?.text || {}) },
image: { ...defaults.image, ...(lastState?.image || {}) },
clock: { ...defaults.clock, ...(lastState?.clock || {}) },
music: { ...defaults.music, ...(lastState?.music || {}) },
};
};
export default function TabLayout() {
const theme = useTheme();
const shouldHideText = (screenWidth < 400);
const {theme} = useTheme();
const {authenticatedUser} = useAuth();
const { width } = useWindowDimensions();
const shouldHideText = (width < 400);
const [matrixState, setMatrixState] = useState<MatrixState>(() => getInitialState(authenticatedUser?.lastState));
const updateMatrixState = (newState: Partial<MatrixState>) => {
setMatrixState(prevState => ({...prevState, ...newState}));
};
useEffect(() => {
if (authenticatedUser?.lastState) {
console.log("Authenticated user or lastState changed, updating matrix context...");
setMatrixState(getInitialState(authenticatedUser.lastState));
}
}, [authenticatedUser]);
return (
<AuthenticatedWrapper>
<Tabs
screenOptions={({route}) => ({
headerStyle: {
backgroundColor: theme.theme.colors.primaryContainer,
},
headerTitleStyle: {
color: theme.theme.colors.onPrimaryContainer,
},
headerTitleContainerStyle: {
paddingHorizontal: 16,
},
headerRight: () => (
<Link href="/settings" style={{marginRight: 16}}>
<Feather
name="settings"
size={24}
color={theme.theme.colors.onPrimaryContainer}
/>
</Link>
),
tabBarActiveTintColor: theme.theme.colors.primary,
tabBarInactiveTintColor: theme.theme.colors.onSurfaceVariant,
tabBarStyle: {
backgroundColor: theme.theme.colors.surface,
borderTopColor: theme.theme.colors.outline,
elevation: 4,
},
tabBarItemStyle: {
borderLeftWidth: route.name === tabs[0].name ? 0 : 1,
borderLeftColor: theme.theme.colors.outlineVariant, // Trennlinienfarbe
},
})}
>
{tabs.map(({name, title, icon}) => (
<Tabs.Screen
key={name}
name={name}
options={{
title: shouldHideText ? '' : title,
tabBarIcon: ({color}) => (
<MatrixContext.Provider value={{matrixState, updateMatrixState}}>
<Tabs
screenOptions={({route}) => ({
headerStyle: {
backgroundColor: theme.colors.primaryContainer,
},
headerTitleStyle: {
color: theme.colors.onPrimaryContainer,
},
headerTitleContainerStyle: {
paddingHorizontal: 16,
},
headerRight: () => (
<Link href="/settings" style={{marginRight: 16}}>
<Feather
name="settings"
size={24}
color={theme.colors.onPrimaryContainer}
/>
</Link>
),
tabBarActiveTintColor: theme.colors.primary,
tabBarInactiveTintColor: theme.colors.onSurfaceVariant,
tabBarStyle: {
backgroundColor: theme.colors.surface,
borderTopColor: theme.colors.outline,
elevation: 4,
},
tabBarItemStyle: {
borderLeftWidth: route.name === tabs[0].name ? 0 : 1,
borderLeftColor: theme.colors.outlineVariant,
},
})}
>
{tabs.map(({name, title, icon}) => (
<Tabs.Screen
key={name}
name={name}
options={{
title: shouldHideText ? '' : title,
tabBarIcon: ({color}) => (
<Feather size={28} name={icon} color={color}/>
),
),
}}
/>
))}
<Tabs.Screen
name="settings"
options={{
title: 'Einstellungen',
href: null, // prevents showing in the tab bar
}}
/>
))}
<Tabs.Screen
name="settings"
options={{
title: 'Einstellungen',
href: null, // prevents showing in the tab bar
}}
/>
</Tabs>
</Tabs>
</MatrixContext.Provider>
</AuthenticatedWrapper>
);
}
+18 -21
View File
@@ -1,49 +1,45 @@
import ThemedBackground from "@/src/components/themed/ThemedBackground";
import { useState } from "react";
import ThemedTextInput from "@/src/components/themed/ThemedTextInput";
import ThemedButton from "@/src/components/themed/ThemedButton";
import { useAuth } from "@/src/context/AuthProvider";
import ColorSelector from "@/src/components/themed/ColorSelector";
import { View, StyleSheet } from "react-native";
import {View, StyleSheet} from "react-native";
import ThemedSegmentedButtons from "@/src/components/themed/ThemedSegmentedButtons";
import { MatrixState } from '@/src/model/User';
import {useMatrix} from "@/app/(tabs)/_layout";
type TextProps = MatrixState['text'];
export default function TextScreen() {
const { authenticatedUser } = useAuth();
const [textProps, setTextProps] = useState<TextProps>(
authenticatedUser?.lastState?.text || {
text: '',
color: [255, 255, 255],
align: 'center',
speed: 50,
size: 16,
}
);
const { matrixState, updateMatrixState } = useMatrix();
const updateTextProp = (prop: Partial<TextProps>) => {
setTextProps(prev => ({ ...prev, ...prop }));
updateMatrixState({
text: { ...matrixState.text, ...prop },
global: { ...matrixState.global, mode: 'text' }
});
};
const handleSendToMatrix = () => {
console.log("Sende an Matrix:", matrixState);
};
return (
<ThemedBackground>
<View style={styles.contentWrapper}>
<View style={[styles.contentWrapper]}>
<View style={styles.inputGroup}>
<ThemedTextInput
label="Text"
value={textProps.text}
value={matrixState.text.text}
onChangeText={(text) => updateTextProp({ text })}
/>
<ColorSelector
onSelect={(color) => updateTextProp({ color })}
defaultColor={textProps.color}
defaultColor={matrixState.text.color}
/>
<ThemedSegmentedButtons
value={textProps.align}
value={matrixState.text.align}
onValueChange={(align) => updateTextProp({ align })}
options={{
left: 'Links',
@@ -56,7 +52,7 @@ export default function TextScreen() {
<View style={styles.actionGroup}>
<ThemedButton
mode="contained"
onPress={() => console.log(textProps)}
onPress={handleSendToMatrix}
title={"An die Matrix senden"}
/>
</View>
@@ -69,11 +65,12 @@ const styles = StyleSheet.create({
contentWrapper: {
flex: 1,
justifyContent: 'space-between',
paddingVertical: 20,
padding: 20,
},
inputGroup: {
gap: 15,
},
actionGroup: {
paddingTop: 20,
}
});
+1 -1
View File
@@ -48,7 +48,7 @@ export default function ChangePasswordForm({ onSuccess, onCancel }: ChangePasswo
{apiResponse && apiResponse.data?.message && (
<View style={[styles.apiResponseBox, { backgroundColor: apiResponse.ok ? theme.colors.success : theme.colors.error }]}>
<Text variant="bodyMedium"style={{ color: apiResponse.ok ? theme.colors.onSuccess : theme.colors.onError }}>
<Text variant="bodyMedium" style={{ color: apiResponse.ok ? theme.colors.onSuccess : theme.colors.onError }}>
{apiResponse.data.message}
</Text>
</View>
@@ -1,7 +1,6 @@
import React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import { SegmentedButtons } from 'react-native-paper';
import { useTheme } from '@/src/context/ThemeProvider';
export type SegmentedButton<T extends string> = {
value: T;