Convert all pages to Typescript
Convert all files in `client/src/pages` to Typescript
Gitlab: #30
Change-Id: I9b5ec5b042487d732bb7d46b584f797049eb068c
diff --git a/client/src/components/AccountPreferences.tsx b/client/src/components/AccountPreferences.tsx
new file mode 100644
index 0000000..598d07f
--- /dev/null
+++ b/client/src/components/AccountPreferences.tsx
@@ -0,0 +1,241 @@
+import { AddCircle, DeleteRounded, GroupRounded, PhoneCallbackRounded } from '@mui/icons-material';
+import {
+ Card,
+ CardContent,
+ Grid,
+ IconButton,
+ List,
+ ListItem,
+ ListItemAvatar,
+ ListItemIcon,
+ ListItemSecondaryAction,
+ ListItemText,
+ ListSubheader,
+ Paper,
+ Switch,
+ TextField,
+ Toolbar,
+ Typography,
+} from '@mui/material';
+import { motion } from 'framer-motion';
+import { useState } from 'react';
+
+import Account from '../../../model/Account';
+import AccountDetails from '../../../model/AccountDetails';
+import authManager from '../AuthManager';
+import ConversationAvatar from './ConversationAvatar';
+import ConversationsOverviewCard from './ConversationsOverviewCard';
+import JamiIdCard from './JamiIdCard';
+
+const transition = { duration: 0.3, ease: [0.43, 0.13, 0.23, 0.96] };
+
+const thumbnailVariants = {
+ initial: { scale: 0.9, opacity: 0 },
+ enter: { scale: 1, opacity: 1, transition },
+ exit: {
+ scale: 0.5,
+ opacity: 0,
+ transition: { ...transition, duration: 1.5 },
+ },
+};
+
+type AccountPreferencesProps = {
+ account: Account;
+ onAccountChanged?: (account: Account) => void;
+};
+
+export default function AccountPreferences({ account }: AccountPreferencesProps) {
+ const devices: string[][] = [];
+ const accountDevices = account.getDevices();
+ for (const i in accountDevices) devices.push([i, accountDevices[i]]);
+
+ console.log(devices);
+
+ const isJamiAccount = account.getType() === Account.TYPE_JAMI;
+ const alias = isJamiAccount ? 'Jami account' : 'SIP account';
+ const moderators = account.getDefaultModerators();
+ const [defaultModeratorUri, setDefaultModeratorUri] = useState('');
+
+ const [details, setDetails] = useState(account.getDetails());
+
+ const addModerator = () => {
+ if (defaultModeratorUri) {
+ authManager.fetch(`/api/accounts/${account.getId()}/defaultModerators/${defaultModeratorUri}`, { method: 'PUT' });
+ setDefaultModeratorUri('');
+ }
+ };
+
+ const removeModerator = (uri: string) =>
+ authManager.fetch(`/api/accounts/${account.getId()}/defaultModerators/${uri}`, { method: 'DELETE' });
+
+ const handleToggle = (key: keyof AccountDetails, value: boolean) => {
+ console.log(`handleToggle ${key} ${value}`);
+ const newDetails: Partial<AccountDetails> = {};
+ newDetails[key] = value ? 'true' : 'false';
+ console.log(newDetails);
+ authManager.fetch(`/api/accounts/${account.getId()}`, {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(newDetails),
+ });
+ setDetails({ ...account.updateDetails(newDetails) });
+ };
+
+ return (
+ <motion.div
+ initial="initial"
+ animate="enter"
+ exit="exit"
+ variants={{
+ enter: { transition: { staggerChildren: 0.05 } },
+ exit: { transition: { staggerChildren: 0.02 } },
+ }}
+ >
+ <motion.div variants={thumbnailVariants}>
+ <Typography variant="h2" component="h2" gutterBottom>
+ {alias}
+ </Typography>
+ </motion.div>
+ <Grid container spacing={3} style={{ marginBottom: 16 }}>
+ {isJamiAccount && (
+ <Grid item xs={12}>
+ <motion.div variants={thumbnailVariants}>
+ <JamiIdCard account={account} />
+ </motion.div>
+ </Grid>
+ )}
+
+ <Grid item xs={12} sm={6}>
+ <motion.div variants={thumbnailVariants}>
+ <ConversationsOverviewCard accountId={account.getId()} />
+ </motion.div>
+ </Grid>
+
+ <Grid item xs={12} sm={6}>
+ <motion.div variants={thumbnailVariants}>
+ <Card>
+ <CardContent>
+ <Typography color="textSecondary" gutterBottom>
+ Current calls
+ </Typography>
+ <Typography gutterBottom variant="h5" component="h2">
+ 0
+ </Typography>
+ </CardContent>
+ </Card>
+ </motion.div>
+ </Grid>
+
+ <Grid item xs={12} sm={6}>
+ <motion.div variants={thumbnailVariants}>
+ <Card>
+ <CardContent>
+ <Typography color="textSecondary" gutterBottom>
+ Appareils associés
+ </Typography>
+ <Typography gutterBottom variant="h5" component="h2">
+ {devices.map((device, i) => (
+ <ListItem key={i}>
+ <GroupRounded />
+ <ListItemText id="switch-list-label-rendezvous" primary={device[1]} secondary={device[0]} />
+ </ListItem>
+ ))}
+ {/* <ListItemTextsion> */}
+ </Typography>
+ </CardContent>
+ </Card>
+ </motion.div>
+ </Grid>
+ </Grid>
+
+ <List
+ subheader={
+ <motion.div variants={thumbnailVariants}>
+ <ListSubheader>Settings</ListSubheader>
+ </motion.div>
+ }
+ >
+ <motion.div variants={thumbnailVariants}>
+ <ListItem>
+ <ListItemIcon>
+ <GroupRounded />
+ </ListItemIcon>
+ <ListItemText id="switch-list-label-rendezvous" primary="Rendez-Vous point" />
+ <ListItemSecondaryAction>
+ <Switch
+ edge="end"
+ onChange={(e) => handleToggle('Account.rendezVous', e.target.checked)}
+ checked={details['Account.rendezVous'] === 'true'}
+ inputProps={{
+ 'aria-labelledby': 'switch-list-label-rendezvous',
+ }}
+ />
+ </ListItemSecondaryAction>
+ </ListItem>
+ </motion.div>
+ <motion.div variants={thumbnailVariants}>
+ <ListItem>
+ <ListItemIcon>
+ <PhoneCallbackRounded />
+ </ListItemIcon>
+ <ListItemText id="switch-list-label-publicin" primary="Allow connection from unkown peers" />
+ <ListItemSecondaryAction>
+ <Switch
+ edge="end"
+ onChange={(e) => handleToggle('DHT.PublicInCalls', e.target.checked)}
+ checked={details['DHT.PublicInCalls'] === 'true'}
+ inputProps={{ 'aria-labelledby': 'switch-list-label-publicin' }}
+ />
+ </ListItemSecondaryAction>
+ </ListItem>
+ </motion.div>
+ <motion.div variants={thumbnailVariants}>
+ <Paper>
+ <Toolbar>
+ <Typography variant="h6">Default moderators</Typography>
+ </Toolbar>
+ <List>
+ <ListItem key="add">
+ <TextField
+ variant="outlined"
+ value={defaultModeratorUri}
+ onChange={(e) => setDefaultModeratorUri(e.target.value)}
+ label="Add new default moderator"
+ placeholder="Enter new moderator name or URI"
+ fullWidth
+ />
+ <ListItemSecondaryAction>
+ <IconButton onClick={addModerator} size="large">
+ <AddCircle />
+ </IconButton>
+ </ListItemSecondaryAction>
+ </ListItem>
+ {!moderators || moderators.length === 0 ? (
+ <ListItem key="placeholder">
+ <ListItemText primary="No default moderator" />
+ </ListItem>
+ ) : (
+ moderators.map((moderator) => (
+ <ListItem key={moderator.getUri()}>
+ <ListItemAvatar>
+ <ConversationAvatar displayName={moderator.getDisplayName()} />
+ </ListItemAvatar>
+ <ListItemText primary={moderator.getDisplayName()} />
+ <ListItemSecondaryAction>
+ <IconButton onClick={(e) => removeModerator(moderator.getUri())} size="large">
+ <DeleteRounded />
+ </IconButton>
+ </ListItemSecondaryAction>
+ </ListItem>
+ ))
+ )}
+ </List>
+ </Paper>
+ </motion.div>
+ </List>
+ </motion.div>
+ );
+}