mirror of
https://github.com/balzack/databag.git
synced 2025-03-13 00:50:03 +00:00
rendering accounts for admin screen
This commit is contained in:
parent
72f2ab4b88
commit
49142bdad8
@ -7,11 +7,53 @@ export const styles = StyleSheet.create({
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: 0,
|
||||
},
|
||||
largeTitle: {
|
||||
fontSize: 20,
|
||||
flexGrow: 1,
|
||||
paddingLeft: 16,
|
||||
},
|
||||
smallTitle: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
flexGrow: 1,
|
||||
},
|
||||
header: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: 48,
|
||||
},
|
||||
line: {
|
||||
width: '100%',
|
||||
},
|
||||
members: {
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
},
|
||||
empty: {
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
label: {
|
||||
fontSize: 18,
|
||||
color: Colors.placeholder,
|
||||
},
|
||||
card: {
|
||||
width: '100%',
|
||||
height: 48,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
paddingLeft: 16,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
@ -1,14 +1,69 @@
|
||||
import React from 'react';
|
||||
import {SafeAreaView, Image, View, Pressable} from 'react-native';
|
||||
import {Text} from 'react-native-paper';
|
||||
import React, {useState} from 'react';
|
||||
import {FlatList, SafeAreaView, Image, View, Pressable} from 'react-native';
|
||||
import {Text, IconButton, Divider, Surface, useTheme} from 'react-native-paper';
|
||||
import {useAccounts} from './useAccounts.hook';
|
||||
import {styles} from './Accounts.styled';
|
||||
import { Card } from '../card/Card';
|
||||
import { Colors } from '../constants/Colors';
|
||||
|
||||
export function Accounts({ setup }: { setup: ()=>void }) {
|
||||
const { state, actions } = useAccounts();
|
||||
const theme = useTheme();
|
||||
const [loading, setLoading] = useState('');
|
||||
|
||||
export function Accounts() {
|
||||
return (
|
||||
<View style={styles.accounts}>
|
||||
<View style={styles.header}>
|
||||
<Text>ACCOUNTS</Text>
|
||||
</View>
|
||||
{ state.layout === 'large' && (
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.largeTitle}>{ state.strings.accounts }</Text>
|
||||
<IconButton style={styles.icon} loading={loading} iconColor={Colors.primary} mode="contained" icon="refresh" onPress={()=>{}} />
|
||||
<IconButton style={styles.icon} loading={loading} iconColor={Colors.primary} mode="contained" icon="account-plus-outline" onPress={()=>{}} />
|
||||
<IconButton style={styles.icon} loading={loading} iconColor={Colors.primary} mode="contained" icon="cog-outline" onPress={setup} />
|
||||
</View>
|
||||
)}
|
||||
{ state.layout === 'small' && (
|
||||
<View style={styles.header}>
|
||||
<IconButton style={styles.icon} loading={loading} iconColor={Colors.primary} mode="contained" icon="refresh" onPress={()=>{}} />
|
||||
<Text style={styles.smallTitle}>{ state.strings.accounts }</Text>
|
||||
<IconButton style={styles.icon} loading={loading} iconColor={Colors.primary} mode="contained" icon="account-plus-outline" onPress={()=>{}} />
|
||||
</View>
|
||||
)}
|
||||
<Divider style={styles.line} bold={true} />
|
||||
{state.members.length !== 0 && (
|
||||
<FlatList
|
||||
style={styles.members}
|
||||
data={state.members}
|
||||
initialNumToRender={32}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={({item}) => {
|
||||
const options = [
|
||||
<IconButton key="disable" style={styles.icon} loading={loading} iconColor={Colors.primary} mode="contained" icon="lock-open-variant-outline" onPress={()=>{}} />,
|
||||
<IconButton key="reset" style={styles.icon} loading={loading} iconColor={Colors.pending} mode="contained" icon="account-cancel-outline" onPress={()=>{}} />,
|
||||
<IconButton key="remove" style={styles.icon} loading={loading} iconColor={Colors.offsync} mode="contained" icon="trash-can-outline" onPress={()=>{}} />
|
||||
];
|
||||
return (
|
||||
<Card
|
||||
containerStyle={{
|
||||
...styles.card,
|
||||
borderColor: theme.colors.outlineVariant,
|
||||
}}
|
||||
imageUrl={item.imageUrl}
|
||||
name={item.storageUsed > 1048576 ? `${item.handle} [${Math.floor(item.storageUsed / 1048576)}MB]` : item.handle}
|
||||
handle={item.guid}
|
||||
node={item.node}
|
||||
placeholder={state.strings.name}
|
||||
select={()=>{}}
|
||||
actions={options}
|
||||
/>
|
||||
);
|
||||
}} />
|
||||
)}
|
||||
{state.members.length === 0 && (
|
||||
<View style={styles.empty}>
|
||||
<Text style={styles.label}>{ state.strings.noAccounts }</Text>
|
||||
</View>
|
||||
)}
|
||||
<Divider style={styles.line} bold={true} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
45
app/client/mobile/src/accounts/useAccounts.hook.ts
Normal file
45
app/client/mobile/src/accounts/useAccounts.hook.ts
Normal file
@ -0,0 +1,45 @@
|
||||
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';
|
||||
|
||||
export function useAccounts() {
|
||||
const app = useContext(AppContext);
|
||||
const display = useContext(DisplayContext);
|
||||
const [state, setState] = useState({
|
||||
layout: '',
|
||||
strings: {},
|
||||
members: [] as Member[],
|
||||
});
|
||||
|
||||
const updateState = (value: any) => {
|
||||
setState(s => ({...s, ...value}));
|
||||
};
|
||||
|
||||
const sync = async () => {
|
||||
try {
|
||||
const service = app.state.service;
|
||||
const members = await service.getMembers();
|
||||
updateState({ members });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { layout, strings} = display.state;
|
||||
updateState({ layout, strings});
|
||||
}, [display.state]);
|
||||
|
||||
useEffect(() => {
|
||||
if (app.state.service) {
|
||||
sync();
|
||||
}
|
||||
}, [app.state.service]);
|
||||
|
||||
const actions = {
|
||||
};
|
||||
|
||||
return {state, actions};
|
||||
}
|
@ -17,6 +17,9 @@ export const en = {
|
||||
noAccess: 'No Access',
|
||||
connecting: 'Connecting',
|
||||
|
||||
accounts: 'Accounts',
|
||||
noAccounts: 'No Accounts',
|
||||
|
||||
membership: 'Membership',
|
||||
channelGuest: 'Topic Guest',
|
||||
channelHost: 'Topic Host',
|
||||
@ -300,6 +303,9 @@ export const fr = {
|
||||
noAccess: 'Pas d\'Accès',
|
||||
connecting: 'Démarrage de la Connexion',
|
||||
|
||||
accounts: 'Comptes',
|
||||
noAccounts: 'Aucun Compte',
|
||||
|
||||
membership: 'Adhésion',
|
||||
channelHost: 'Hôte du Sujet',
|
||||
channelGuest: 'Invité du Sujet',
|
||||
@ -578,6 +584,9 @@ export const sp = {
|
||||
reportTopicPrompt: '¿Estás seguro de que deseas reportar este tema para revisión del administrador?',
|
||||
connecting: 'Conexión Inicial',
|
||||
|
||||
accounts: 'Cuentas',
|
||||
noAccounts: 'No hay cuentas',
|
||||
|
||||
noAccess: 'Sin Acceso',
|
||||
membership: 'Afiliación',
|
||||
channelHost: 'Anfitrión del Tema',
|
||||
@ -856,6 +865,9 @@ export const pt = {
|
||||
reportTopicPrompt: 'Tem certeza de que deseja denunciar este tópico para revisão do administrador?',
|
||||
connecting: 'Iniciando Conexão',
|
||||
|
||||
accounts: 'Contas',
|
||||
noAccounts: 'Sem Contas',
|
||||
|
||||
noAccess: 'Sem Acesso',
|
||||
membership: 'Associação',
|
||||
channelHost: 'Anfitrião do Tópico',
|
||||
@ -1135,6 +1147,9 @@ export const de = {
|
||||
reportTopicPrompt: 'Sind Sie sicher, dass Sie dieses Thema zur Überprüfung durch den Administrator melden möchten?',
|
||||
connecting: 'Startverbindung',
|
||||
|
||||
accounts: 'Konten',
|
||||
noAccounts: 'Keine Konten',
|
||||
|
||||
noAccess: 'Kein Zugriff',
|
||||
channelHost: 'Themenhost',
|
||||
channelGuest: 'Thema Gast',
|
||||
@ -1413,6 +1428,9 @@ export const ru = {
|
||||
reportTopicPrompt: 'Вы уверены, что хотите отправить эту тему на рассмотрение администратору?',
|
||||
connecting: 'начало соединения',
|
||||
|
||||
accounts: 'Учетные записи',
|
||||
noAccounts: 'Нет учетных записей',
|
||||
|
||||
noAccess: 'Нет доступа',
|
||||
membership: 'Членство',
|
||||
channelHost: 'Ведущий темы',
|
||||
|
@ -88,6 +88,10 @@ export const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
minHeight: 0,
|
||||
height: '1%',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
tabs: {
|
||||
flexShrink: 0,
|
||||
|
@ -19,10 +19,10 @@ export function Service() {
|
||||
const showSetup = {display: tab === 'setup' ? 'flex' : 'none'};
|
||||
|
||||
return (
|
||||
<View style={styles.service}>
|
||||
<SafeAreaView style={styles.service}>
|
||||
{state.layout !== 'large' && (
|
||||
<Surface elevation={3}>
|
||||
<SafeAreaView style={styles.full}>
|
||||
<View>
|
||||
<View style={styles.full}>
|
||||
<View style={styles.screen}>
|
||||
<View
|
||||
style={{
|
||||
@ -85,8 +85,8 @@ export function Service() {
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</Surface>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{state.layout === 'large' && (
|
||||
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
@ -95,7 +95,7 @@ export function Service() {
|
||||
</View>
|
||||
</NavigationContainer>
|
||||
)}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
@ -130,13 +130,7 @@ function SetupScreen() {
|
||||
function AccountScreen({setup}) {
|
||||
return (
|
||||
<View style={styles.frame}>
|
||||
<Accounts />
|
||||
<IconButton
|
||||
mode="contained"
|
||||
icon={'cog'}
|
||||
size={28}
|
||||
onPress={setup.openDrawer}
|
||||
/>
|
||||
<Accounts setup={setup.openDrawer} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -169,6 +169,9 @@ export interface Service {
|
||||
removeMember(accountId: number): Promise<void>;
|
||||
getSetup(): Promise<Setup>;
|
||||
setSetup(setup: Setup): Promise<void>;
|
||||
enableMFAuth(): Promise<{ image: string, text: string }>;
|
||||
confirmMFAuth(code: string): Promise<void>;
|
||||
disableMFAuth(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface Contributor {
|
||||
|
@ -225,6 +225,7 @@ export type ProfileEntity = {
|
||||
};
|
||||
|
||||
export type AccountEntity = {
|
||||
acconutId: number;
|
||||
guid: string;
|
||||
handle: string;
|
||||
name: string;
|
||||
@ -235,6 +236,7 @@ export type AccountEntity = {
|
||||
seal?: string;
|
||||
version: string;
|
||||
node: string;
|
||||
storageUsed: string;
|
||||
};
|
||||
|
||||
export const defaultProfileEntity = {
|
||||
|
@ -1,6 +1,9 @@
|
||||
import type { Service } from './api';
|
||||
import type { Member, Setup } from './types';
|
||||
import { type AccountEntity, avatar } from './entities';
|
||||
import type { Logging } from './logging';
|
||||
import { getMembers } from './net/getMembers';
|
||||
import { getMemberImageUrl } from './net/getMemberImageUrl';
|
||||
import { getAdminMFAuth } from './net/getAdminMFAuth';
|
||||
import { setAdminMFAuth } from './net/setAdminMFAuth';
|
||||
import { addAdminMFAuth } from './net/addAdminMFAuth';
|
||||
@ -32,7 +35,13 @@ export class ServiceModule implements Service {
|
||||
public async removeMember(accountId: number): Promise<void> {}
|
||||
|
||||
public async getMembers(): Promise<Member[]> {
|
||||
return [];
|
||||
const { node, secure, token } = this;
|
||||
const accounts = await getMembers(node, secure, token);
|
||||
return accounts.map(account => {
|
||||
const { accountId, guid, handle, name, imageSet, revision, disabled, storageUsed } = account;
|
||||
const imageUrl = imageSet ? getMemberImageUrl(node, secure, token, accountId, revision) : avatar;
|
||||
return { accountId, guid, handle, name, imageUrl, storageUsed };
|
||||
});
|
||||
}
|
||||
|
||||
public async getSetup(): Promise<Setup> {
|
||||
@ -59,18 +68,18 @@ export class ServiceModule implements Service {
|
||||
|
||||
public async setSetup(config: Setup): Promise<void> {}
|
||||
|
||||
public async enableMFA(): Promise<{ secretImage: string, secretText: string}> {
|
||||
public async enableMFAuth(): Promise<{ image: string, text: string}> {
|
||||
const { node, secure, token } = this;
|
||||
const { secretImage, secretText } = await addAdminMFAuth(node, secure, token);
|
||||
return { secretImage, secretText };
|
||||
}
|
||||
|
||||
public async disableMFA(): Promise<void> {
|
||||
public async disableMFAuth(): Promise<void> {
|
||||
const { node, secure, token } = this;
|
||||
await removeAdminMFAuth(node, secure, token);
|
||||
}
|
||||
|
||||
public async confirmMFA(code: string): Promise<void> {
|
||||
public async confirmMFAuth(code: string): Promise<void> {
|
||||
const { node, secure, token } = this;
|
||||
await setAdminMFAuth(node, secure, token, code);
|
||||
}
|
||||
|
@ -217,8 +217,9 @@ export type Member = {
|
||||
name: string;
|
||||
description: string;
|
||||
location: string;
|
||||
imageSet: boolean;
|
||||
imageUrl: string;
|
||||
disabled: boolean;
|
||||
storageUsed: number,
|
||||
};
|
||||
|
||||
export type Setup = {
|
||||
|
Loading…
Reference in New Issue
Block a user