diff --git a/app/(tabs)/modes/text.tsx b/app/(tabs)/modes/text.tsx index 3de23c3..784a783 100644 --- a/app/(tabs)/modes/text.tsx +++ b/app/(tabs)/modes/text.tsx @@ -1,44 +1,79 @@ import ThemedBackground from "@/src/components/themed/ThemedBackground"; -import {useState} from "react"; +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 { useAuth } from "@/src/context/AuthProvider"; +import ColorSelector from "@/src/components/themed/ColorSelector"; +import { View, StyleSheet } from "react-native"; +import ThemedSegmentedButtons from "@/src/components/themed/ThemedSegmentedButtons"; +import { MatrixState } from '@/src/model/User'; -import CustomColorPicker from "@/src/components/themed/CustomColorPicker"; -import {View} from "react-native"; +type TextProps = MatrixState['text']; export default function TextScreen() { - const {authenticatedUser} = useAuth(); - const [textProps, setTextProps] = useState( - authenticatedUser?.lastState?.text! || { text: '', color: [255, 255, 255] } + const { authenticatedUser } = useAuth(); + + const [textProps, setTextProps] = useState( + authenticatedUser?.lastState?.text || { + text: '', + color: [255, 255, 255], + align: 'center', + speed: 50, + size: 16, + } ); + + const updateTextProp = (prop: Partial) => { + setTextProps(prev => ({ ...prev, ...prop })); + }; + return ( - - { - setTextProps(prev => ({ - ...prev, - text: value - })); - }} - autoCapitalize="none" - /> + + + updateTextProp({ text })} + /> - setTextProps(prev => ({ ...prev!, color: rgb }))} - defaultColor={textProps.color} - /> - + updateTextProp({ color })} + defaultColor={textProps.color} + /> - - console.log(textProps)} title={"Cooler Knopf"} /> + updateTextProp({ align })} + options={{ + left: 'Links', + center: 'Mitte', + right: 'Rechts', + }} + /> + + + + console.log(textProps)} + title={"An die Matrix senden"} + /> + ); } - +const styles = StyleSheet.create({ + contentWrapper: { + flex: 1, + justifyContent: 'space-between', + paddingVertical: 20, + }, + inputGroup: { + gap: 15, + }, + actionGroup: { + } +}); \ No newline at end of file diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index e2fc01b..0e4c12f 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -1,7 +1,7 @@ import ThemedHeader from "@/src/components/themed/ThemedHeader"; import React from "react"; import ThemedBackground from "@/src/components/themed/ThemedBackground"; -import ChangePasswordModal from "@/src/components/ChangePasswordModal"; +import ChangePasswordFeature from "@/src/components/ChangePasswordFeature"; import ThemeToggleButton from "@/src/components/ThemeToggleButton"; import SpotifyAuthButton from "@/src/components/SpotifyAuthButton"; import {RestService, Token} from "@/src/services/RestService"; @@ -36,7 +36,7 @@ export default function SettingsScreen() { Einen wunderschönen guten Tag, {authenticatedUser?.name} - + { console.log(isAuthenticated); @@ -30,7 +32,7 @@ export default function LoginScreen() { const onLoginPressed = async () => { console.log("Login wird ausgeführt...") - await login(username, password); + await login(username, password, stayLoggedIn); }; if (isAuthenticated) { @@ -68,6 +70,12 @@ export default function LoginScreen() { errorText={error?.message} autoComplete="password" /> + + diff --git a/src/components/ChangePasswordFeature.tsx b/src/components/ChangePasswordFeature.tsx new file mode 100644 index 0000000..35b3166 --- /dev/null +++ b/src/components/ChangePasswordFeature.tsx @@ -0,0 +1,33 @@ +import React, { useState } from "react"; +import CustomModal from "@/src/components/themed/CustomModal"; +import ThemedButton from "@/src/components/themed/ThemedButton"; +import ChangePasswordForm from "./ChangePasswordForm"; // Unser neues Formular + +export default function ChangePasswordFeature() { + const [isModalVisible, setIsModalVisible] = useState(false); + + const closeModal = () => { + setIsModalVisible(false); + }; + + return ( + <> + setIsModalVisible(true)} + title="Passwort ändern" + /> + + + + + + ); +} \ No newline at end of file diff --git a/src/components/ChangePasswordForm.tsx b/src/components/ChangePasswordForm.tsx new file mode 100644 index 0000000..812c9e2 --- /dev/null +++ b/src/components/ChangePasswordForm.tsx @@ -0,0 +1,112 @@ +import React, {useRef, useState} from "react"; +import { useTheme } from "@/src/context/ThemeProvider"; +import {StyleSheet, TextInput, View} from "react-native"; +import { ApiResponse, RestService } from "@/src/services/RestService"; +import { useAuth } from "@/src/context/AuthProvider"; +import PasswordInput from "@/src/components/PasswordInput"; +import ThemedButton from "@/src/components/themed/ThemedButton"; +import { Text } from "react-native-paper"; + + +interface ChangePasswordFormProps { + onSuccess: () => void; + onCancel: () => void; +} + +export default function ChangePasswordForm({ onSuccess, onCancel }: ChangePasswordFormProps) { + const { theme } = useTheme(); + const { token: jwtToken } = useAuth(); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [apiResponse, setApiResponse] = useState | null>(null); + + const confirmPasswordRef = useRef(null); + + const handleConfirm = async () => { + if (!password || !confirmPassword) { + setApiResponse({ ok: false, data: { message: "Bitte füllen Sie alle Felder aus!" } }); + return; + } + if (password !== confirmPassword) { + setApiResponse({ ok: false, data: { message: "Passwörter stimmen nicht überein!" } }); + return; + } + + const response = await new RestService(jwtToken).changeSelfPassword(password, confirmPassword); + setApiResponse(response); + + if (response.ok) { + setTimeout(() => { + onSuccess(); + }, 1500); + } + }; + + return ( + + Passwort ändern + + {apiResponse && apiResponse.data?.message && ( + + + {apiResponse.data.message} + + + )} + + {!apiResponse?.ok && ( + <> + confirmPasswordRef.current?.focus()} + submitBehavior="submit" + /> + + + )} + + + {apiResponse?.ok ? ( + + ) : ( + <> + + + + )} + + + ); +} + +const styles = StyleSheet.create({ + modalContent: { + padding: 20, + borderRadius: 12, + alignSelf: 'center', + width: '100%', + maxWidth: 400, + }, + apiResponseBox: { + marginBottom: 8, + marginTop: 8, + padding: 12, + borderRadius: 8, + }, + buttonGroup: { + flexDirection: "row", + justifyContent: "flex-end", + gap: 10, + marginTop: 16, + }, +}); \ No newline at end of file diff --git a/src/components/ChangePasswordModal.tsx b/src/components/ChangePasswordModal.tsx deleted file mode 100644 index 2a371cd..0000000 --- a/src/components/ChangePasswordModal.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React, {useRef, useState} from "react"; -import { Paragraph} from "react-native-paper"; -import {useTheme} from "@/src/context/ThemeProvider"; -import {StyleSheet, View} from "react-native"; -import {ApiResponse, RestService} from "@/src/services/RestService"; -import {useAuth} from "@/src/context/AuthProvider"; -import CustomModal, {CustomModalHandles} from "@/src/components/themed/CustomModal"; -import PasswordInput from "@/src/components/PasswordInput"; -import ThemedButton from "@/src/components/themed/ThemedButton"; - - -export default function ChangePasswordModal() { - const {theme} = useTheme(); - const {token: jwtToken} = useAuth(); - const [password, setPassword] = useState(""); - const [confirmPassword, setConfirmPassword] = useState(""); - const [apiResponse, setApiResponse] = useState | null>(null); - const modalRef = useRef(null); - - - const handleConfirm = () => { - if (!password || !confirmPassword) { - setApiResponse({ok: false, data: {message: "Bitte füllen Sie alle Felder aus!"}}); - return; - } - if (password !== confirmPassword) { - setApiResponse({ok: false, data: { message: "Passwörter stimmen nicht überein!"}}); - return; - } - new RestService(jwtToken).changeSelfPassword(password, confirmPassword).then( - (response) => { - console.log(response); - setApiResponse(response); - if (response.ok) { - // add something here - setPassword(""); - setConfirmPassword(""); - } - } - ) - - - }; - const resetModal = () => { - setApiResponse(null); - setPassword(""); - setConfirmPassword(""); - } - - return ( - <> - - - Passwort ändern - {apiResponse && apiResponse.data?.message && ( - - {apiResponse.data.message} - - )} - - - - - - - { - modalRef.current?.close(); - resetModal(); - }} title={"Schließen"} /> - - - - - ); -} - -const styles = StyleSheet.create({ - modalContent: { - padding: 16, - borderRadius: 8, - }, - apiResponseBox: { - marginBottom: 8, - marginTop: 8, - padding: 8, - }, - buttonGroup: { - flexDirection: "row", - justifyContent: "space-between", - marginTop: 16, - }, -}); diff --git a/src/components/themed/ColorSelector.tsx b/src/components/themed/ColorSelector.tsx new file mode 100644 index 0000000..1319eea --- /dev/null +++ b/src/components/themed/ColorSelector.tsx @@ -0,0 +1,92 @@ +import React, { useState } from 'react'; +import { View, StyleSheet, Dimensions } from 'react-native'; +import ColorPicker, { Panel1, Swatches, Preview, HueSlider, ColorFormatsObject } from 'reanimated-color-picker'; +import ThemedButton from "@/src/components/themed/ThemedButton"; +import { useTheme } from "@/src/context/ThemeProvider"; +import ThemedColorPickerButton from "@/src/components/themed/ThemedColorPickerButton"; +import CustomModal from './CustomModal'; // Importiere den NEUEN CustomModal + + +interface ColorSelectorProps { + defaultColor?: [number, number, number]; + onSelect: (rgb: [number, number, number]) => void; +} + +export default function ColorSelector({ defaultColor = [255, 255, 255], onSelect }: ColorSelectorProps) { + const { theme } = useTheme(); + + const [isModalVisible, setIsModalVisible] = useState(false); + + const [pickerHex, setPickerHex] = useState(() => rgbToHex(defaultColor)); + + const openModal = () => { + setPickerHex(rgbToHex(defaultColor)); + setIsModalVisible(true); + }; + + const closeModal = () => { + setIsModalVisible(false); + }; + + const handleConfirm = () => { + const rgb = hexToRgbArray(pickerHex); + onSelect(rgb); + closeModal(); + }; + + const { width } = Dimensions.get('window'); + const modalWidth = Math.min(350, width * 0.9); + + return ( + + + + + + setPickerHex(color.hex)} + > + + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + modalContent: { + padding: 20, + borderRadius: 12, + alignSelf: 'center', + alignItems: 'center', + }, +}); + +const rgbToHex = ([r, g, b]: [number, number, number]) => + `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; + +const hexToRgbArray = (hex: string): [number, number, number] => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] + : [255, 255, 255]; +}; \ No newline at end of file diff --git a/src/components/themed/CustomColorPicker.tsx b/src/components/themed/CustomColorPicker.tsx deleted file mode 100644 index 86b3822..0000000 --- a/src/components/themed/CustomColorPicker.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, {useEffect, useRef, useState} from 'react'; -import {StyleSheet, View, Dimensions} from 'react-native'; -import ColorPicker, {Panel1, Swatches, Preview, HueSlider, ColorFormatsObject} from 'reanimated-color-picker'; -import CustomModal, {CustomModalHandles} from "@/src/components/themed/CustomModal"; -import ThemedButton from "@/src/components/themed/ThemedButton"; -import {useTheme} from "@/src/context/ThemeProvider"; - -interface CustomColorPickerProps { - defaultColor?: [number, number, number]; - onSelect: (rgb: [number, number, number]) => void; -} - -export default function CustomColorPicker({defaultColor = [255, 255, 255], onSelect}: CustomColorPickerProps) { - const [currentColor, setCurrentColor] = useState(defaultColor); - const modalRef = useRef(null); - const {theme} = useTheme(); - - const rgbToHex = ([r, g, b]: [number, number, number]) => - `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; - - const hexToRgbArray = (hex: string): [number, number, number] => { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] - : [255, 255, 255]; - }; - - const [pickerHex, setPickerHex] = useState(rgbToHex(defaultColor)); - - useEffect(() => { - setCurrentColor(defaultColor); - setPickerHex(rgbToHex(defaultColor)); - }, [defaultColor]); - - const {width, height} = Dimensions.get('window'); - const pickerWidth = Math.min(400, width * 0.9); - const pickerHeight = Math.min(400, height * 0.6); - - const handleOk = () => { - const rgb = hexToRgbArray(pickerHex); - setCurrentColor(rgb); - onSelect(rgb); - console.log(rgb) - console.log(modalRef.current) - modalRef.current?.close(); - }; - - const resetModal = () => { - setCurrentColor(defaultColor); - } - - return ( - - - - - setPickerHex(color.hex)} - > - - - - - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - }, - overlay: { - flex: 1, - backgroundColor: 'rgba(0,0,0,0.5)', - }, - modalWrapper: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - position: 'absolute', - width: '100%', - height: '100%', - }, - modalContent: { - padding: 20, - backgroundColor: 'white', - borderRadius: 12, - alignItems: 'center', - }, - buttonContainer: { - marginTop: 15, - width: '50%', - }, -}); diff --git a/src/components/themed/CustomModal.tsx b/src/components/themed/CustomModal.tsx index 7045800..d2f87f3 100644 --- a/src/components/themed/CustomModal.tsx +++ b/src/components/themed/CustomModal.tsx @@ -1,7 +1,6 @@ -import React, {forwardRef, useImperativeHandle, useState} from "react"; +import React from "react"; import {StyleSheet, View} from "react-native"; import Modal from "react-native-modal"; -import ThemedButton from "@/src/components/themed/ThemedButton"; export interface CustomModalHandles { open: () => void; @@ -9,43 +8,29 @@ export interface CustomModalHandles { } export interface Props { - resetCallback: () => void; + isVisible: boolean; + onClose: () => void; children: React.ReactNode; - buttonTitle: string; - buttonMode?: 'text' | 'outlined' | 'contained'; + onModalDidHide?: () => void; } -const CustomModal = forwardRef( - ({resetCallback, children, buttonTitle, buttonMode = "outlined"}, ref) => { - const [isVisible, setIsVisible] = useState(false); - - const resetModal = () => { - setIsVisible(false); - resetCallback(); - }; - - useImperativeHandle(ref, () => ({ - open: () => setIsVisible(true), - close: () => setIsVisible(false), - })); - - return ( - <> - setIsVisible(true)} title={buttonTitle} /> - - - {children} - - - - ); - } -); +const CustomModal = ({ isVisible, onClose, children, onModalDidHide }: Props) => { + return ( + + + {children} + + + ); +}; export default CustomModal; diff --git a/src/components/themed/ThemedBackground.tsx b/src/components/themed/ThemedBackground.tsx index 331f2e9..7d3d721 100644 --- a/src/components/themed/ThemedBackground.tsx +++ b/src/components/themed/ThemedBackground.tsx @@ -1,8 +1,6 @@ import React from "react"; -import {ImageBackground, KeyboardAvoidingView, StyleSheet,} from "react-native"; +import {ImageBackground, KeyboardAvoidingView, Platform, StyleSheet,} from "react-native"; import {useTheme} from "../../context/ThemeProvider"; -import { Dimensions } from "react-native"; - type Props = { children: React.ReactNode; @@ -16,15 +14,16 @@ export default function ThemedBackground({children}: Props) { resizeMode="repeat" style={[styles.background, {backgroundColor: theme.colors.background}]} > - + {children} ); } -const screenWidth = Dimensions.get("window").width; - const styles = StyleSheet.create({ background: { flex: 1, @@ -33,9 +32,8 @@ const styles = StyleSheet.create({ container: { flex: 1, padding: 20, - width: Math.min(screenWidth, 340), + width: "90%", + maxWidth: 600, alignSelf: "center", - alignItems: "center", - justifyContent: "center", }, -}); +}); \ No newline at end of file diff --git a/src/components/themed/ThemedButton.tsx b/src/components/themed/ThemedButton.tsx index 50b3d41..4660be6 100644 --- a/src/components/themed/ThemedButton.tsx +++ b/src/components/themed/ThemedButton.tsx @@ -1,7 +1,8 @@ import React from "react"; -import {StyleSheet, Text} from "react-native"; -import {Button as PaperButton} from "react-native-paper"; +import {StyleSheet} from "react-native"; +import { Button as PaperButton } from "react-native-paper"; import {useTheme} from "@/src/context/ThemeProvider"; +import {IconSource} from "react-native-paper/src/components/Icon"; type Props = { mode: "text" | "outlined" | "contained" | "elevated" | "contained-tonal" @@ -10,9 +11,10 @@ type Props = { onPress: () => void; disabled?: boolean; title: string; + icon?: IconSource; } -export default function ThemedButton({mode, style, title, ...props}: Props) { +export default function ThemedButton({mode, style, title, icon, ...props}: Props) { const {theme} = useTheme(); return ( - {title} + {title} ); } diff --git a/src/components/themed/ThemedCheckbox.tsx b/src/components/themed/ThemedCheckbox.tsx new file mode 100644 index 0000000..775e45d --- /dev/null +++ b/src/components/themed/ThemedCheckbox.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Text, StyleSheet, Pressable } from 'react-native'; +import Checkbox from 'expo-checkbox'; +import { useTheme } from '@/src/context/ThemeProvider'; + +type ThemedCheckboxProps = { + label: string; + value: boolean; + onValueChange: (newValue: boolean) => void; + style?: object; +}; + +const ThemedCheckbox = ({ label, value, onValueChange, style }: ThemedCheckboxProps) => { + const { theme } = useTheme(); + + const componentStyles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + marginVertical: 12, + }, + checkbox: { + marginRight: 8, + }, + label: { + fontSize: 14, + color: theme.colors.onSurface, + }, + }); + + return ( + onValueChange(!value)} style={[componentStyles.container, style]}> + + {label} + + ); +}; + +export default ThemedCheckbox; \ No newline at end of file diff --git a/src/components/themed/ThemedColorPickerButton.tsx b/src/components/themed/ThemedColorPickerButton.tsx new file mode 100644 index 0000000..d223354 --- /dev/null +++ b/src/components/themed/ThemedColorPickerButton.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { Pressable, View, Text, StyleSheet, StyleProp, ViewStyle } from 'react-native'; +import { useTheme } from '@/src/context/ThemeProvider'; + +type Props = { + color: [number, number, number]; + onPress: () => void; + title?: string; + style?: StyleProp; +}; + +const ThemedColorPickerButton = ({ color, onPress, title = "Farbe wählen", style }: Props) => { + const { theme } = useTheme(); + const rgbColor = `rgb(${color.join(',')})`; + + return ( + [ + styles.container, + { + backgroundColor: theme.colors.surface, + borderColor: theme.colors.outline + }, + pressed && styles.pressed, + style + ]} + onPress={onPress} + > + + {title} + + ); +}; + +export default ThemedColorPickerButton; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 12, + paddingHorizontal: 16, + borderRadius: 28, + borderWidth: 1, + gap: 12, + }, + swatch: { + width: 24, + height: 24, + borderRadius: 4, + borderWidth: 1, + borderColor: 'rgba(255, 255, 255, 0.5)', + }, + pressed: { + opacity: 0.7, + }, +}); \ No newline at end of file diff --git a/src/components/themed/ThemedSegmentedButtons.tsx b/src/components/themed/ThemedSegmentedButtons.tsx new file mode 100644 index 0000000..5928113 --- /dev/null +++ b/src/components/themed/ThemedSegmentedButtons.tsx @@ -0,0 +1,60 @@ +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 = { + value: T; + label: string; + icon?: string; + disabled?: boolean; +}; + +type ThemedSegmentedButtonsProps = { + value: T; + onValueChange: (value: T) => void; + style?: StyleProp; +} & ({ + buttons: SegmentedButton[]; + options?: never; +} | { + buttons?: never; + options: Record; +}); + +const ThemedSegmentedButtons = ({ + value, + onValueChange, + buttons, + options, + style, + }: ThemedSegmentedButtonsProps) => { + + const finalButtons = buttons || (options ? (Object.keys(options) as T[]).map(key => ({ + value: key, + label: options[key], + })) : []); + + const handleValueChange = (val: string) => { + onValueChange(val as T); + }; + + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + width: '100%', + marginVertical: 12, + }, +}); + +export default ThemedSegmentedButtons; \ No newline at end of file diff --git a/src/components/themed/ThemedTextInput.tsx b/src/components/themed/ThemedTextInput.tsx index 676cdc5..360331e 100644 --- a/src/components/themed/ThemedTextInput.tsx +++ b/src/components/themed/ThemedTextInput.tsx @@ -1,9 +1,9 @@ import React, { forwardRef } from "react"; import { StyleSheet, Text, View } from "react-native"; -import { TextInput as Input, TextInputProps } from "react-native-paper"; // Importiere den korrekten Typ +import { TextInput as Input, TextInputProps } from "react-native-paper"; import { useTheme } from "@/src/context/ThemeProvider"; -export type ThemedTextInputProps = TextInputProps & { // Erweitere die Typen um die Eigenschaften von TextInput +export type ThemedTextInputProps = TextInputProps & { errorText?: string; description?: string; error?: boolean; @@ -28,8 +28,8 @@ const ThemedTextInput = forwardRef( {description && !error ? ( {description} diff --git a/src/context/AuthProvider.tsx b/src/context/AuthProvider.tsx index da7a8e2..3989e4f 100644 --- a/src/context/AuthProvider.tsx +++ b/src/context/AuthProvider.tsx @@ -8,7 +8,7 @@ import {Platform} from "react-native"; type AuthContextType = { isAuthenticated: boolean | null; token: string | null; - login: (username: string, password: string) => Promise; + login: (username: string, password: string, stayLoggedIn?: boolean) => Promise; logout: () => Promise; authenticatedUser: User | null; error: { field: string, message: string } | null; @@ -53,7 +53,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children} setToken(null); setIsAuthenticated(false); setAuthenticatedUser(null); - setError({field: "general", message:"Token is invalid."}); + setError({field: "general", message: "Token is invalid."}); return null; } const user = response.data; @@ -61,7 +61,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children} return user; } - const login = async (username: string, password: string) => { + const login = async (username: string, password: string, stayLoggedIn?: boolean) => { if (isAuthenticated) { console.log("Already authenticated"); return; @@ -69,7 +69,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children} setLoading(true); setError(null); - const response = await new RestService(null).login(username, password); + const response = await new RestService(null).login(username, password, stayLoggedIn); if (!response.ok) { console.error("Login failed:", response.data); const message = response.data.message!; diff --git a/src/services/RestService.ts b/src/services/RestService.ts index cad0b6d..c71a8ff 100644 --- a/src/services/RestService.ts +++ b/src/services/RestService.ts @@ -125,7 +125,7 @@ class RestService { return this.request>('DELETE', '/user/me/spotify'); } - async login(username: string, password: string): Promise>( "POST", '/auth/login', - {username, password}, + {username, password, stayLoggedIn}, {'Content-Type': 'application/json'} ); }