mirror of
https://github.com/balzack/databag.git
synced 2025-03-13 09:00:06 +00:00
rendering admin config
This commit is contained in:
parent
10e5e75dfc
commit
680f0affb5
@ -39,4 +39,42 @@ export const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
busy: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 32,
|
||||
height: '100%',
|
||||
},
|
||||
option: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
},
|
||||
label: {
|
||||
fontSize: 16,
|
||||
},
|
||||
inputSurface: {
|
||||
flexGrow: 1,
|
||||
marginRight: 8,
|
||||
marginLeft: 16,
|
||||
display: 'flex',
|
||||
borderRadius: 8,
|
||||
},
|
||||
input: {
|
||||
flexGrow: 1,
|
||||
backgroundColor: 'transparent',
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
display: 'flex',
|
||||
height: 40,
|
||||
maxHeight: 40,
|
||||
borderRadius: 8,
|
||||
},
|
||||
inputUnderline: {
|
||||
display: 'none',
|
||||
},
|
||||
});
|
||||
|
@ -1,23 +1,81 @@
|
||||
import React from 'react';
|
||||
import {SafeAreaView, Image, View, Pressable} from 'react-native';
|
||||
import {Divider, Text} from 'react-native-paper';
|
||||
import {ActivityIndicator, Surface, Divider, TextInput, Text} from 'react-native-paper';
|
||||
import {styles} from './Setup.styled';
|
||||
import {useSetup} from './useSetup.hook';
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
|
||||
export function Setup() {
|
||||
const { state, actions } = useSetup();
|
||||
|
||||
const errorParams = {
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
action: actions.clearError,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.setup}>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.busy}>
|
||||
{ state.loading && (
|
||||
<ActivityIndicator size={18} />
|
||||
)}
|
||||
</View>
|
||||
<Text style={styles.title}>{ state.strings.setup }</Text>
|
||||
<View style={styles.busy}>
|
||||
{ state.updating && (
|
||||
<ActivityIndicator size={18} />
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
<Divider style={styles.line} bold={true} />
|
||||
<KeyboardAwareScrollView enableOnAndroid={true} style={styles.form} contentContainerStyle={styles.content}>
|
||||
<Text>CONTENT</Text>
|
||||
<View style={styles.option}>
|
||||
<Text style={styles.label}>{ state.strings.federatedHost }</Text>
|
||||
<Surface mode="flat" elevation={5} style={styles.inputSurface}>
|
||||
<TextInput
|
||||
dense={true}
|
||||
style={styles.input}
|
||||
outlineColor="transparent"
|
||||
activeOutlineColor="transparent"
|
||||
autoCapitalize={false}
|
||||
underlineStyle={styles.inputUnderline}
|
||||
mode="outlined"
|
||||
disabled={state.loading}
|
||||
placeholder={state.strings.hostHint}
|
||||
value={state.setup?.domain}
|
||||
onChangeText={value => actions.setDomain(value)}
|
||||
/>
|
||||
</Surface>
|
||||
</View>
|
||||
<View style={styles.option}>
|
||||
<Text style={styles.label}>{ state.strings.storageLimit }</Text>
|
||||
<Surface mode="flat" elevation={5} style={styles.inputSurface}>
|
||||
<TextInput
|
||||
type="number"
|
||||
dense={true}
|
||||
style={styles.input}
|
||||
keyboardType="numeric"
|
||||
outlineColor="transparent"
|
||||
activeOutlineColor="transparent"
|
||||
autoCapitalize={false}
|
||||
underlineStyle={styles.inputUnderline}
|
||||
mode="outlined"
|
||||
disabled={state.loading}
|
||||
placeholder={state.strings.storageHint}
|
||||
value={state.accountStorage}
|
||||
onChangeText={value => actions.setAccountStorage(value)}
|
||||
/>
|
||||
</Surface>
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
<Divider style={styles.line} bold={true} />
|
||||
<Confirm show={state.error} params={errorParams} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -2,14 +2,26 @@ import {useEffect, useState, useContext, useRef} from 'react';
|
||||
import {AppContext} from '../context/AppContext';
|
||||
import {DisplayContext} from '../context/DisplayContext';
|
||||
import {ContextType} from '../context/ContextType';
|
||||
import type { Member } from 'databag-client-sdk';
|
||||
import type { Setup } from 'databag-client-sdk';
|
||||
|
||||
const DEBOUNCE_MS = 2000;
|
||||
const DELAY_MS = 1000;
|
||||
|
||||
export function useSetup() {
|
||||
const updated = useRef(false);
|
||||
const loading = useRef(false);
|
||||
const debounce = useRef(null);
|
||||
const setup = useRef(null as null | Setup);
|
||||
const app = useContext(AppContext);
|
||||
const display = useContext(DisplayContext);
|
||||
const [state, setState] = useState({
|
||||
layout: '',
|
||||
strings: {},
|
||||
loading: true,
|
||||
updating: false,
|
||||
error: false,
|
||||
accountStorage: '',
|
||||
setup: null as null | Setup,
|
||||
});
|
||||
|
||||
const updateState = (value: any) => {
|
||||
@ -17,14 +29,41 @@ export function useSetup() {
|
||||
};
|
||||
|
||||
const sync = async () => {
|
||||
try {
|
||||
const service = app.state.service;
|
||||
//
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
while (loading.current) {
|
||||
try {
|
||||
const service = app.state.service;
|
||||
setup.current = await service.getSetup();
|
||||
loading.current = false;
|
||||
const storage = Math.floor(setup.current.accountStorage / 1073741824);
|
||||
updateState({ setup: setup.current, accountStorage: storage.toString(), loading: false });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
await new Promise((r) => setTimeout(r, DELAY_MS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
updated.current = true;
|
||||
updateState({ updating: true });
|
||||
clearTimeout(debounce.current);
|
||||
debounce.current = setTimeout(async () => {
|
||||
updated.current = false;
|
||||
try {
|
||||
const service = app.state.service;
|
||||
await service.setSetup(setup.current);
|
||||
if (updated.current) {
|
||||
save()
|
||||
} else {
|
||||
updateState({ updating: false });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
updateState({ error: true });
|
||||
}
|
||||
}, DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { layout, strings} = display.state;
|
||||
updateState({ layout, strings});
|
||||
@ -32,11 +71,33 @@ export function useSetup() {
|
||||
|
||||
useEffect(() => {
|
||||
if (app.state.service) {
|
||||
loading.current = true;
|
||||
sync();
|
||||
return () => {
|
||||
loading.current = false;
|
||||
}
|
||||
}
|
||||
}, [app.state.service]);
|
||||
}, []);
|
||||
|
||||
const actions = {
|
||||
clearError: () => {
|
||||
updateState({ error: false });
|
||||
},
|
||||
setDomain: (domain: string) => {
|
||||
if (setup.current) {
|
||||
setup.current.domain = domain;
|
||||
updateState({ setup: setup.current });
|
||||
save();
|
||||
}
|
||||
},
|
||||
setAccountStorage: (accountStorage: number) => {
|
||||
if (setup.current) {
|
||||
const storage = parseInt(accountStorage) * 1073741824;
|
||||
setup.current.accountStorage = storage;
|
||||
updateState({ setup: setup.current, accountStorage });
|
||||
save();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return {state, actions};
|
||||
|
@ -225,7 +225,7 @@ export type ProfileEntity = {
|
||||
};
|
||||
|
||||
export type AccountEntity = {
|
||||
acconutId: number;
|
||||
accountId: number;
|
||||
guid: string;
|
||||
handle: string;
|
||||
name: string;
|
||||
@ -251,6 +251,26 @@ export const defaultProfileEntity = {
|
||||
node: '',
|
||||
};
|
||||
|
||||
export type SetupEntity = {
|
||||
domain: string;
|
||||
accountStorage: number;
|
||||
enableImage: boolean;
|
||||
enableAudio: boolean;
|
||||
enableVideo: boolean;
|
||||
enableBinary: boolean;
|
||||
keyType: string;
|
||||
pushSupported: boolean;
|
||||
allowUnsealed: boolean;
|
||||
transformSupported: boolean;
|
||||
enableIce: boolean;
|
||||
iceService: string;
|
||||
iceUrl: string;
|
||||
iceUsername: string;
|
||||
icePassword: string;
|
||||
enableOpenAccess: boolean;
|
||||
openAccessLimit: number;
|
||||
};
|
||||
|
||||
export type Calling = {
|
||||
id: string;
|
||||
cardId: string;
|
||||
|
4
app/sdk/src/net/getMemberImageUrl.ts
Normal file
4
app/sdk/src/net/getMemberImageUrl.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export function getMemberImageUrl(server: string, secure: boolean, token: string, accountId: number, revision: number) {
|
||||
return `http${secure ? 's' : ''}://${server}/admin/accounts/${accountId}/image?token=${token}&revision=${revision}`;
|
||||
}
|
||||
|
9
app/sdk/src/net/getMembers.ts
Normal file
9
app/sdk/src/net/getMembers.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
import { AccountEntity } from '../entities';
|
||||
|
||||
export async function getMembers(server: string, secure: boolean, token: string): Promise<AccountEntity[]> {
|
||||
const endpoint = `http${secure ? 's' : ''}://${server}/admin/accounts?token=${token}`;
|
||||
const accounts = await fetchWithTimeout(endpoint, { method: 'GET' });
|
||||
checkResponse(accounts.status);
|
||||
return await accounts.json();
|
||||
}
|
10
app/sdk/src/net/getNodeConfig.ts
Normal file
10
app/sdk/src/net/getNodeConfig.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
import { SetupEntity } from '../entities';
|
||||
|
||||
export async function getNodeConfig(server: string, secure: boolean, token: string): Promise<ConfigEntity> {
|
||||
const endpoint = `http${secure ? 's' : ''}://${server}/admin/config?token=${token}`;
|
||||
const config = await fetchWithTimeout(endpoint, { method: 'GET' });
|
||||
checkResponse(config.status);
|
||||
return await config.json();
|
||||
}
|
||||
|
9
app/sdk/src/net/setNodeConfig.ts
Normal file
9
app/sdk/src/net/setNodeConfig.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
import { SetupEntity } from '../entities';
|
||||
|
||||
export async function setNodeConfig(server: string, secure: boolean, token: string, config: SetupEntity) {
|
||||
const endpoint = `http${secure ? 's' : ''}://${server}/admin/config?token=${token}`;
|
||||
const { status }= await fetchWithTimeout(endpoint, { method: 'PUT', body: JSON.stringify(config) });
|
||||
checkResponse(status);
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import { getAdminMFAuth } from './net/getAdminMFAuth';
|
||||
import { setAdminMFAuth } from './net/setAdminMFAuth';
|
||||
import { addAdminMFAuth } from './net/addAdminMFAuth';
|
||||
import { removeAdminMFAuth } from './net/removeAdminMFAuth';
|
||||
import { getNodeConfig } from './net/getNodeConfig';
|
||||
import { setNodeConfig } from './net/setNodeConfig';
|
||||
|
||||
export class ServiceModule implements Service {
|
||||
private log: Logging;
|
||||
@ -45,28 +47,29 @@ export class ServiceModule implements Service {
|
||||
}
|
||||
|
||||
public async getSetup(): Promise<Setup> {
|
||||
return {
|
||||
domain: '',
|
||||
accountStorage: '',
|
||||
enableImage: true,
|
||||
enableAudio: true,
|
||||
enableVideo: true,
|
||||
enableBinary: true,
|
||||
keyType: '',
|
||||
pushSupported: true,
|
||||
allowUnsealed: true,
|
||||
transformSupported: true,
|
||||
enableIce: true,
|
||||
iceService: '',
|
||||
iceUrl: '',
|
||||
iceUsername: '',
|
||||
icePassword: '',
|
||||
enableOpenAccess: true,
|
||||
openAccessLimit: 0,
|
||||
};
|
||||
}
|
||||
const { node, secure, token } = this;
|
||||
const entity = await getNodeConfig(node, secure, token);
|
||||
const { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
|
||||
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService,
|
||||
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit } = entity;
|
||||
const service = iceService ? iceService : 'default';
|
||||
const setup = { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
|
||||
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService: service,
|
||||
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit };
|
||||
return setup;
|
||||
}
|
||||
|
||||
public async setSetup(config: Setup): Promise<void> {}
|
||||
public async setSetup(setup: Setup): Promise<void> {
|
||||
const { node, secure, token } = this;
|
||||
const { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
|
||||
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService,
|
||||
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit } = setup;
|
||||
const service = iceService === 'default' ? null : iceService;
|
||||
const entity = { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
|
||||
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService: service,
|
||||
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit };
|
||||
await setNodeConfig(node, secure, token, entity);
|
||||
}
|
||||
|
||||
public async enableMFAuth(): Promise<{ image: string, text: string}> {
|
||||
const { node, secure, token } = this;
|
||||
|
@ -222,19 +222,29 @@ export type Member = {
|
||||
storageUsed: number,
|
||||
};
|
||||
|
||||
export enum KeyType {
|
||||
RSA_4096 = 'RSA4096',
|
||||
RSA_2048 = 'RSA2048',
|
||||
}
|
||||
|
||||
export enum ICEService {
|
||||
Cloudflare = 'cloudflage',
|
||||
Default = 'default',
|
||||
}
|
||||
|
||||
export type Setup = {
|
||||
domain: string;
|
||||
accountStorage: string;
|
||||
accountStorage: number;
|
||||
enableImage: boolean;
|
||||
enableAudio: boolean;
|
||||
enableVideo: boolean;
|
||||
enableBinary: boolean;
|
||||
keyType: string;
|
||||
keyType: KeyType;
|
||||
pushSupported: boolean;
|
||||
allowUnsealed: boolean;
|
||||
transformSupported: boolean;
|
||||
enableIce: boolean;
|
||||
iceService: string;
|
||||
iceService: ICEService;
|
||||
iceUrl: string;
|
||||
iceUsername: string;
|
||||
icePassword: string;
|
||||
|
Loading…
Reference in New Issue
Block a user