made login screen

This commit is contained in:
2024-09-18 12:36:02 +02:00
parent a1e83a19b0
commit e60c611cd2
4 changed files with 223 additions and 2 deletions

View File

@@ -7,6 +7,7 @@ import { ThemeButton, ThemeText } from './common/ThemeTypes';
import { StyleSheet, Text, View } from 'react-native'; import { StyleSheet, Text, View } from 'react-native';
import { useColors } from './common/colors'; import { useColors } from './common/colors';
import {decode as atob, encode as btoa} from 'base-64' import {decode as atob, encode as btoa} from 'base-64'
import Login from './screens/Login';
let errorObject; let errorObject;
export const InterfaceContext = createContext<any>(null); export const InterfaceContext = createContext<any>(null);
@@ -167,7 +168,7 @@ const Interface = ({ children }) => {
<ThemeText>{errorObject?.message}</ThemeText> <ThemeText>{errorObject?.message}</ThemeText>
<ThemeButton style={styles.button} onPress={refresh}><Text>Neu Versuchen</Text></ThemeButton> <ThemeButton style={styles.button} onPress={refresh}><Text>Neu Versuchen</Text></ThemeButton>
</View> </View>
<Settings /> <Login />
</View> </View>
) )
if (!loading) return (children) if (!loading) return (children)

View File

@@ -19,7 +19,7 @@ const defaultSettings: SettingsType = {
password: '', password: '',
systemTheme: true, systemTheme: true,
darkTheme: false, darkTheme: false,
requireAuth: false, requireAuth: true,
}; };
const SettingsProvider = ({ children }) => { const SettingsProvider = ({ children }) => {

81
common/Credits.tsx Normal file
View File

@@ -0,0 +1,81 @@
import React, { useState, useEffect, useContext } from 'react';
import { View, Text, ScrollView, TextInput, Button, StyleSheet, Alert, Linking, TouchableOpacity, Platform, ActivityIndicator, Image } from 'react-native';
import { SettingsContext } from '../SettingsProvider';
import { useColors } from '../common/colors';
import ErrorBoundary from 'react-native-error-boundary';
import { ThemeSwitch, ThemeText, ThemeTextInput } from '../common/ThemeTypes'
import * as Application from 'expo-application';
import { ToastType, useToast } from '../common/ToastProvider';
const Credits = () => {
const colors = useColors();
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
label: {
fontWeight: 'bold',
marginTop: 8,
marginBottom: 6,
},
title: {
fontWeight: 'bold',
marginBottom: 3,
fontSize: 32,
},
section: {
marginBottom: 40,
},
endSection: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
switchContainer: {
marginTop: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
activityindicator: {
position: 'absolute',
width: '100%',
height: '100%',
},
logo: {
width: 100,
height: 100,
resizeMode: 'contain'
}
});
return (
<>
<View style={[styles.section, { marginTop: 20 }]}>
<ThemeText>EDV-Schule Plattling</ThemeText>
<TouchableOpacity onPress={() => Linking.openURL('https://www.edvschule-plattling.de/impressum')}>
<Text style={{ color: colors.primary }}>Impressum</Text>
</TouchableOpacity>
</View>
<View style={[styles.section, styles.endSection]}>
<View>
{ /* Wer des löscht werd weggeklagt */}
<ThemeText>Heinrich Thaler</ThemeText>
<TouchableOpacity onPress={() => Linking.openURL('mailto:Heinrich.Thaler@edvschule-plattling.de')}>
<Text style={{ color: colors.primary }}>Heinrich.Thaler@edvschule-plattling.de</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => Linking.openURL('https://www.it-thaler.de')}>
<Text style={{ color: colors.primary }}>www.it-thaler.de</Text>
</TouchableOpacity>
{Platform.OS != 'web' && <ThemeText>Version: {Application.nativeApplicationVersion} ({Application.nativeBuildVersion})</ThemeText>}
</View>
<Image style={styles.logo} source={require('../assets/logo-thaler.png')} />
</View>
</>
);
};
export default Credits;

139
screens/Login.tsx Normal file
View File

@@ -0,0 +1,139 @@
import React, { useState, useEffect, useContext } from 'react';
import { View, Text, ScrollView, TextInput, Button, StyleSheet, Alert, Linking, TouchableOpacity, Platform, ActivityIndicator, Image } from 'react-native';
import { SettingsContext } from '../SettingsProvider';
import { useColors } from '../common/colors';
import ErrorBoundary from 'react-native-error-boundary';
import { ThemeSwitch, ThemeText, ThemeTextInput } from '../common/ThemeTypes'
import { ToastType, useToast } from '../common/ToastProvider';
import Credits from '../common/Credits';
const Login = () => {
const { showToast, hideToast } = useToast();
const colors = useColors();
const { settings, updateSettings } = useContext(SettingsContext);
const [url, setUrl] = useState('');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isSystemTheme, setIsSystemTheme] = useState(settings.systemTheme);
const [isDarkMode, setIsDarkMode] = useState(settings.darkTheme);
const [requireAuth, setRequireAuth] = useState(settings.requireAuth);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
if (settings) {
setUrl(settings.url || '');
setUsername(settings.username || '');
setPassword(settings.password || '');
setIsSystemTheme(settings.systemTheme);
setIsDarkMode(settings.darkTheme);
setRequireAuth(settings.requireAuth);
}
}, [settings]);
const handleSave = async () => {
if (isSaving) return;
try {
setIsSaving(true);
await updateSettings({
url,
username,
password,
systemTheme: isSystemTheme,
darkTheme: isDarkMode,
requireAuth,
});
hideToast()
} catch (error) {
console.error('Error updating settings:', error);
Alert.alert('Error', 'Failed to save settings.');
} finally {
setIsSaving(false);
}
};
const handleChange = (setter) => (value) => {
setter(value);
showToast(ToastType.Warn, 'Ungespeicherte Änderungen');
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
label: {
fontWeight: 'bold',
marginTop: 8,
marginBottom: 6,
},
title: {
fontWeight: 'bold',
marginBottom: 3,
fontSize: 32,
},
section: {
marginBottom: 40,
},
switchContainer: {
marginTop: 15,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
activityindicator: {
position: 'absolute',
width: '100%',
height: '100%',
},
});
return (
<ErrorBoundary>
<ScrollView style={[styles.container, { backgroundColor: colors.backPrimary }]}>
<View style={styles.section}>
<ThemeText style={styles.label}>Benutzername</ThemeText>
<ThemeTextInput
value={username}
onValueChange={handleChange(setUsername)}
placeholder="Benutzername"
disabled={!requireAuth}
/>
<ThemeText style={styles.label}>Passwort</ThemeText>
<ThemeTextInput
value={password}
onValueChange={handleChange(setPassword)}
secureTextEntry
placeholder="Passwort"
disabled={!requireAuth}
/>
</View>
<View style={styles.section}>
<Button title={isSaving ? "" : "Einloggen"} onPress={handleSave} disabled={isSaving} />
{isSaving && <ActivityIndicator size="small" color={colors.primary} style={styles.activityindicator} />}
</View>
<View style={styles.section}>
<ThemeText style={styles.label}>URL</ThemeText>
<ThemeTextInput
value={url}
onValueChange={handleChange(setUrl)}
placeholder="URL"
/>
<View style={styles.switchContainer}>
<ThemeText>Authentifizierung</ThemeText>
<ThemeSwitch
value={requireAuth}
onValueChange={handleChange(setRequireAuth)}
/>
</View>
</View>
<Credits/>
</ScrollView>
</ErrorBoundary>
);
};
export default Login;