mirror of
https://github.com/balzack/databag.git
synced 2025-04-24 02:25:26 +00:00
adding settings options
This commit is contained in:
parent
cfa20fda43
commit
d4b8fbc51c
58
app/client/mobile/src/LocalStore.ts
Normal file
58
app/client/mobile/src/LocalStore.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import {SqlStore} from 'databag-client-sdk';
|
||||
import SQLite from 'react-native-sqlite-storage';
|
||||
|
||||
export class LocalStore implements SqlStore {
|
||||
private db: any = null;
|
||||
|
||||
constructor() {
|
||||
SQLite.DEBUG(false);
|
||||
SQLite.enablePromise(true);
|
||||
}
|
||||
|
||||
public async open(path: string) {
|
||||
this.db = await SQLite.openDatabase({name: path, location: 'default'});
|
||||
await this.localStoreSet("CREATE TABLE IF NOT EXISTS local_store (key text, value text, unique(key));");
|
||||
}
|
||||
|
||||
public async get(key: string, value: string, unset: string): Promise<string> {
|
||||
try {
|
||||
const rows = await this.localStoreGet(`SELECT * FROM local_store WHERE key='${key}';`);
|
||||
if (rows.length == 1 && rows[0].value != null) {
|
||||
return rows[0].value;
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
return unset;
|
||||
}
|
||||
|
||||
public async set(key: string, value: string): Promise<void> {
|
||||
await this.localStoreSet('INSERT OR REPLACE INTO local_store (key, value) values (?, ?)', [key, value]);
|
||||
}
|
||||
|
||||
public async clear(key: string): Promise<void> {
|
||||
await this.localStoreSet('INSERT OR REPLACE INTO local_store (key, value) values (?, null)', [key]);
|
||||
}
|
||||
|
||||
private async localStoreSet(
|
||||
stmt: string,
|
||||
params: (string | number | null)[],
|
||||
): Promise<void> {
|
||||
await this.db.executeSql(stmt, params);
|
||||
}
|
||||
|
||||
private async localStoreGet(
|
||||
stmt: string,
|
||||
params: (string | number | null)[],
|
||||
): Promise<any[]> {
|
||||
const res = await this.db.executeSql(stmt, params);
|
||||
const rows = [];
|
||||
if (res[0] && res[0].rows && res[0].rows.length > 0) {
|
||||
for (let i = 0; i < res[0].rows.length; i++) {
|
||||
rows.push(res[0].rows.item(i));
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export const Colors = {
|
||||
primary: '#66aa88',
|
||||
danger: '#ff8888',
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,8 @@ const Strings = [
|
||||
messaging: 'Messaging',
|
||||
timeFull: '24h',
|
||||
timeHalf: '12h',
|
||||
timeFormat: 'Time Format',
|
||||
dateFormat: 'Date Format',
|
||||
monthStart: 'mm/dd',
|
||||
monthEnd: 'dd/mm',
|
||||
error: 'Error',
|
||||
@ -238,6 +240,8 @@ const Strings = [
|
||||
messaging: 'Messagerie',
|
||||
timeFull: '24h',
|
||||
timeHalf: '12h',
|
||||
timeFormat: "Format de l'heure",
|
||||
dateFormat: 'Format de la date',
|
||||
monthStart: 'mm/jj',
|
||||
monthEnd: 'jj/mm',
|
||||
error: 'Erreur',
|
||||
@ -451,6 +455,8 @@ const Strings = [
|
||||
messaging: 'Mensajería',
|
||||
timeFull: '24h',
|
||||
timeHalf: '12h',
|
||||
timeFormat: 'Formato de Hora',
|
||||
dateFormat: 'Formato de Fecha',
|
||||
monthStart: 'mm/dd',
|
||||
monthEnd: 'dd/mm',
|
||||
error: 'Error',
|
||||
@ -661,6 +667,8 @@ const Strings = [
|
||||
display: 'Format',
|
||||
timeFull: '24h',
|
||||
timeHalf: '12h',
|
||||
timeFormat: 'Zeitformat',
|
||||
dateFormat: 'Datumsformat',
|
||||
monthStart: 'mm/dd',
|
||||
monthEnd: 'dd/mm',
|
||||
error: 'Fehler',
|
||||
@ -874,6 +882,8 @@ const Strings = [
|
||||
messaging: 'Mensagens',
|
||||
timeFull: '24h',
|
||||
timeHalf: '12h',
|
||||
timeFormat: 'Formato de hora',
|
||||
dateFormat: 'Formato de data',
|
||||
monthStart: 'mm/dd',
|
||||
monthEnd: 'dd/mm',
|
||||
error: 'Erro',
|
||||
@ -1071,6 +1081,8 @@ const Strings = [
|
||||
messaging: 'Обмен сообщениями',
|
||||
timeFull: '24 часа',
|
||||
timeHalf: '12 часов',
|
||||
timeFormat: 'Формат времени',
|
||||
dateFormat: 'Формат даты',
|
||||
monthStart: 'мм/дд',
|
||||
monthEnd: 'дд/мм',
|
||||
error: 'Ошибка',
|
||||
|
@ -1,12 +1,17 @@
|
||||
import {useState, useEffect, useRef} from 'react';
|
||||
import {DatabagSDK, Session} from 'databag-client-sdk';
|
||||
import {SessionStore} from '../SessionStore';
|
||||
import {LocalStore} from '../LocalStore';
|
||||
const DATABAG_DB = 'db_v202.db';
|
||||
const SETTINGS_DB = 'ls_v001.db';
|
||||
|
||||
export function useAppContext() {
|
||||
const local = useRef(new LocalStore());
|
||||
const sdk = useRef(new DatabagSDK(null));
|
||||
const [state, setState] = useState({
|
||||
session: null as null | Session,
|
||||
fullDayTime: false,
|
||||
monthFirstDate: true,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -15,11 +20,15 @@ export function useAppContext() {
|
||||
};
|
||||
|
||||
const setup = async () => {
|
||||
await local.current.open(SETTINGS_DB);
|
||||
const fullDayTime = await local.current.get('time_format', '12h') === '24h';
|
||||
const monthFirstDate = await local.current.get('date_format', 'month_first') === 'month_first';
|
||||
|
||||
const store = new SessionStore();
|
||||
await store.open(DATABAG_DB);
|
||||
const session: Session | null = await sdk.current.initOfflineStore(store);
|
||||
if (session) {
|
||||
updateState({session});
|
||||
updateState({session, fullDayTime, monthFirstDate});
|
||||
}
|
||||
};
|
||||
|
||||
@ -28,6 +37,14 @@ export function useAppContext() {
|
||||
}, []);
|
||||
|
||||
const actions = {
|
||||
setMonthFirstDate: async (monthFirstDate: boolean) => {
|
||||
updateState({ monthFirstDate });
|
||||
await local.current.set('date_format', monthFirstDate ? 'month_first' : 'day_first');
|
||||
},
|
||||
setFullDayTime: async (fullDayTime: boolean) => {
|
||||
updateState({ fullDayTime });
|
||||
await local.current.set('time_format', fullDayTime ? '24h' : '12h');
|
||||
},
|
||||
accountLogin: async (
|
||||
username: string,
|
||||
password: string,
|
||||
|
@ -98,18 +98,19 @@ export const styles = StyleSheet.create({
|
||||
borderColor: Colors.primary,
|
||||
},
|
||||
editDetails: {
|
||||
borderBottomLeftRadius: 8,
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
bottom: -11,
|
||||
right: 12,
|
||||
zIndex: 3,
|
||||
top: 0,
|
||||
right: 16,
|
||||
},
|
||||
editDetailsLabel: {
|
||||
fontSize: 16,
|
||||
color: Colors.primary,
|
||||
paddingLeft: 8,
|
||||
paddingRight: 8,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
},
|
||||
editBar: {
|
||||
position: 'absolute',
|
||||
@ -140,15 +141,15 @@ export const styles = StyleSheet.create({
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
position: 'relative',
|
||||
height: 32,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
paddingTop: 12,
|
||||
},
|
||||
nameSet: {
|
||||
fontSize: 24,
|
||||
width: '100%',
|
||||
paddingLeft: 32,
|
||||
paddingRight: 32,
|
||||
paddingRight: 72,
|
||||
},
|
||||
nameUnset: {
|
||||
fontSize: 24,
|
||||
@ -162,19 +163,25 @@ export const styles = StyleSheet.create({
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
width: '100%',
|
||||
paddingTop: 12,
|
||||
},
|
||||
attribute: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItem: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingLeft: 32,
|
||||
paddingRight: 32,
|
||||
paddingRight: 8,
|
||||
},
|
||||
icon: {
|
||||
flexShrink: 0,
|
||||
width: 32,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'flex-begin',
|
||||
height: '100%',
|
||||
},
|
||||
label: {
|
||||
fontSize: 16,
|
||||
},
|
||||
labelUnset: {
|
||||
fontSize: 16,
|
||||
@ -185,8 +192,43 @@ export const styles = StyleSheet.create({
|
||||
fontSize: 16,
|
||||
flexGrow: 1,
|
||||
},
|
||||
control: {
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
radioControl: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
radioButtons: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
controlIcon: {
|
||||
flexShrink: 0,
|
||||
width: 32,
|
||||
},
|
||||
controlLabel: {
|
||||
fontSize: 16,
|
||||
color: Colors.primary,
|
||||
},
|
||||
dangerLabel: {
|
||||
fontSize: 16,
|
||||
color: Colors.danger,
|
||||
},
|
||||
controlSwitch: {
|
||||
transform: [
|
||||
{ scaleX: 0.7 },
|
||||
{scaleY: 0.7 },
|
||||
]
|
||||
},
|
||||
input: {
|
||||
width: '100%',
|
||||
marginTop: 16,
|
||||
},
|
||||
option: {
|
||||
fontSize: 16,
|
||||
paddingLeft: 24,
|
||||
},
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, {useState, useContext} from 'react';
|
||||
import {Surface, Button, Text, IconButton, Divider, Icon, TextInput} from 'react-native-paper';
|
||||
import {Surface, Button, Text, IconButton, Divider, Icon, TextInput, RadioButton, Switch} from 'react-native-paper';
|
||||
import {SafeAreaView, TouchableOpacity, Modal, View, Image, ScrollView} from 'react-native';
|
||||
import {styles} from './Settings.styled';
|
||||
import {useSettings} from './useSettings.hook';
|
||||
@ -12,13 +12,16 @@ export function Settings() {
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [details, setDetails] = useState(false);
|
||||
const [savingDetails, setSavingDetails] = useState(false);
|
||||
const [savingRegistry, setSavingRegistry] = useState(false);
|
||||
const [savingNotifications, setSavingNotifications] = useState(false);
|
||||
|
||||
console.log("PREF: ", state.fullDayTime, state.monthFirstDate);
|
||||
|
||||
const selectImage = async () => {
|
||||
try {
|
||||
const full = await ImagePicker.openPicker({ mediaType: 'photo', width: 256, height: 256 });
|
||||
const crop = await ImagePicker.openCropper({ path: full.path, width: 256, height: 256, cropperCircleOverlay: true, includeBase64: true });
|
||||
const img = await ImagePicker.openPicker({ mediaType: 'photo', width: 256, height: 256, cropping: true, cropperCircleOverlay: true, includeBase64: true });
|
||||
try {
|
||||
await actions.setProfileImage(crop.data);
|
||||
await actions.setProfileImage(img.data);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
@ -46,6 +49,42 @@ export function Settings() {
|
||||
}
|
||||
}
|
||||
|
||||
const setRegistry = async (flag: boolean) => {
|
||||
if (!savingRegistry) {
|
||||
setSavingRegistry(true);
|
||||
try {
|
||||
if (flag) {
|
||||
await actions.enableRegistry();
|
||||
} else {
|
||||
await actions.disableRegistry();
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
setAlert(true);
|
||||
}
|
||||
setSavingRegistry(false);
|
||||
}
|
||||
}
|
||||
|
||||
const setNotifications = async (flag: boolean) => {
|
||||
if (!savingNotifications) {
|
||||
setSavingNotifications(true);
|
||||
try {
|
||||
if (flag) {
|
||||
await actions.enableNotifications();
|
||||
} else {
|
||||
await actions.disableNotifications();
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
setAlert(true);
|
||||
}
|
||||
setSavingNotifications(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScrollView bounces={false}>
|
||||
@ -69,9 +108,6 @@ export function Settings() {
|
||||
|
||||
<View style={styles.divider}>
|
||||
<Divider style={styles.line} bold={true} />
|
||||
<TouchableOpacity style={styles.editDetails} onPress={() => setDetails(true)}>
|
||||
<Text style={styles.editDetailsLabel}>{state.strings.edit}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.attributes}>
|
||||
@ -103,15 +139,127 @@ export function Settings() {
|
||||
<Text style={styles.labelSet}>{state.profile.description}</Text>
|
||||
)}
|
||||
</View>
|
||||
<TouchableOpacity style={styles.editDetails} onPress={() => setDetails(true)}>
|
||||
<Surface elevation={4} mode="flat">
|
||||
<Text style={styles.editDetailsLabel}>{state.strings.edit}</Text>
|
||||
</Surface>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.divider}>
|
||||
<Divider style={styles.line} bold={true} />
|
||||
</View>
|
||||
<View style={styles.attributes}>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="eye-outline" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => setRegistry(!state.config.searchable)}>
|
||||
<Text style={styles.controlLabel}>{state.strings.visibleRegistry}</Text>
|
||||
</TouchableOpacity>
|
||||
<Switch style={styles.controlSwitch} value={state.config.searchable} onValueChange={setRegistry} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="cloud-lock-outline" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => manageSeal}>
|
||||
<Text style={styles.controlLabel}>{state.strings.manageTopics}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="bell-outline" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => setRegistry(!state.config.pushEnabled)}>
|
||||
<Text style={styles.controlLabel}>{state.strings.enableNotifications}</Text>
|
||||
</TouchableOpacity>
|
||||
<Switch style={styles.controlSwitch} value={state.config.pushEnabled} onValueChange={setRegistry} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Button mode="contained" onPress={actions.logout}>
|
||||
Logout
|
||||
</Button>
|
||||
<View style={styles.divider}>
|
||||
<Divider style={styles.line} bold={true} />
|
||||
</View>
|
||||
<View style={styles.attributes}>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="ticket-confirmation-outline" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => setRegistry(!state.config.searchable)}>
|
||||
<Text style={styles.controlLabel}>{state.strings.mfaTitle}</Text>
|
||||
</TouchableOpacity>
|
||||
<Switch style={styles.controlSwitch} value={state.config.searchable} onValueChange={setRegistry} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="login" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => manageSeal}>
|
||||
<Text style={styles.controlLabel}>{state.strings.changeLogin}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="logout" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => manageSeal}>
|
||||
<Text style={styles.controlLabel}>{state.strings.logout}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.controlIcon}>
|
||||
<Icon size={24} source="account-remove" />
|
||||
</View>
|
||||
<View style={styles.control}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => manageSeal}>
|
||||
<Text style={styles.dangerLabel}>{state.strings.deleteAccount}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.divider}>
|
||||
<Divider style={styles.line} bold={true} />
|
||||
</View>
|
||||
<View style={styles.attributes}>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.icon}>
|
||||
<Icon size={24} source="clock-outline" />
|
||||
</View>
|
||||
<View style={styles.radioControl}>
|
||||
<Text style={styles.label}>{state.strings.timeFormat}:</Text>
|
||||
<View style={styles.radioButtons}>
|
||||
<RadioButton.Item label={state.strings.timeHalf} mode="android" status={state.fullDayTime ? 'unchecked' : 'checked'} onPress={() => {actions.setFullDayTime(false)}} />
|
||||
<RadioButton.Item label={state.strings.timeFull} mode="android" status={state.fullDayTime ? 'checked' : 'unchecked'} onPress={() => {actions.setFullDayTime(true)}} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.icon}>
|
||||
<Icon size={24} source="calendar-text-outline" />
|
||||
</View>
|
||||
<View style={styles.radioControl}>
|
||||
<Text style={styles.label}>{state.strings.dateFormat}:</Text>
|
||||
<View style={styles.radioButtons}>
|
||||
<RadioButton.Item label={state.strings.monthStart} mode="android" status={state.monthFirstDate ? 'checked' : 'unchecked'} onPress={() => {actions.setMonthFirstDate(true)}} />
|
||||
<RadioButton.Item label={state.strings.monthEnd} mode="android" status={state.monthFirstDate ? 'unchecked' : 'checked'} onPress={() => {actions.setMonthFirstDate(false)}} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
<Modal
|
||||
|
@ -16,8 +16,6 @@ export function useSettings() {
|
||||
profileSet: false,
|
||||
imageUrl: null,
|
||||
strings: display.state.strings,
|
||||
timeFormat: '12h',
|
||||
dateFormat: 'mm/dd',
|
||||
all: false,
|
||||
password: '',
|
||||
confirm: '',
|
||||
@ -35,6 +33,8 @@ export function useSettings() {
|
||||
sealConfirm: '',
|
||||
sealDelete: '',
|
||||
secretCopied: false,
|
||||
monthFirstDate: true,
|
||||
fullDayTime: false,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -78,6 +78,11 @@ export function useSettings() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const { fullDayTime, monthFirstDate } = app.state;
|
||||
updateState({ fullDayTime, monthFirstDate });
|
||||
}, [app.state.fullDayTime, app.state.monthFirstDate]);
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
strings,
|
||||
@ -229,6 +234,17 @@ export function useSettings() {
|
||||
setSealConfirm: (sealConfirm: string) => {
|
||||
updateState({ sealConfirm });
|
||||
},
|
||||
setFullDayTime: async (flag: boolean) => {
|
||||
try {
|
||||
await app.actions.setFullDayTime(flag);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
},
|
||||
setMonthFirstDate: async (flag: boolean) => {
|
||||
await app.actions.setMonthFirstDate(flag);
|
||||
},
|
||||
}
|
||||
|
||||
return { state, actions }
|
||||
|
Loading…
x
Reference in New Issue
Block a user