Convert all pages to Typescript
Convert all files in `client/src/pages` to Typescript
Gitlab: #30
Change-Id: I9b5ec5b042487d732bb7d46b584f797049eb068c
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 595fd70..b0fd67a 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -9,14 +9,14 @@
import authManager from './AuthManager';
import WelcomeAnimation from './components/welcome';
-import NotFoundPage from './pages/404.jsx';
-import AccountCreationDialog from './pages/accountCreation.jsx';
-import AccountSelection from './pages/accountSelection.jsx';
-import AccountSettings from './pages/accountSettings.jsx';
-import JamiAccountDialog from './pages/jamiAccountCreation.jsx';
-import JamiMessenger from './pages/JamiMessenger.jsx';
-import SignInPage from './pages/loginDialog.jsx';
-import ServerSetup from './pages/serverSetup.jsx';
+import NotFoundPage from './pages/404';
+import AccountCreationDialog from './pages/AccountCreation';
+import AccountSelection from './pages/AccountSelection';
+import AccountSettings from './pages/AccountSettings';
+import JamiAccountDialog from './pages/JamiAccountCreation';
+import JamiMessenger from './pages/JamiMessenger';
+import SignInPage from './pages/LoginDialog';
+import ServerSetup from './pages/ServerSetup';
import defaultTheme from './themes/default';
import { ThemeDemonstrator } from './themes/ThemeDemonstrator';
diff --git a/client/src/components/AccountPreferences.js b/client/src/components/AccountPreferences.tsx
similarity index 89%
rename from client/src/components/AccountPreferences.js
rename to client/src/components/AccountPreferences.tsx
index 23b23f6..598d07f 100644
--- a/client/src/components/AccountPreferences.js
+++ b/client/src/components/AccountPreferences.tsx
@@ -21,6 +21,7 @@
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';
@@ -34,14 +35,19 @@
exit: {
scale: 0.5,
opacity: 0,
- transition: { duration: 1.5, ...transition },
+ transition: { ...transition, duration: 1.5 },
},
};
-export default function AccountPreferences(props) {
- const account = props.account;
- let devices = [];
- for (var i in account.devices) devices.push([i, account.devices[i]]);
+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);
@@ -59,12 +65,12 @@
}
};
- const removeModerator = (uri) =>
+ const removeModerator = (uri: string) =>
authManager.fetch(`/api/accounts/${account.getId()}/defaultModerators/${uri}`, { method: 'DELETE' });
- const handleToggle = (key, value) => {
+ const handleToggle = (key: keyof AccountDetails, value: boolean) => {
console.log(`handleToggle ${key} ${value}`);
- const newDetails = {};
+ const newDetails: Partial<AccountDetails> = {};
newDetails[key] = value ? 'true' : 'false';
console.log(newDetails);
authManager.fetch(`/api/accounts/${account.getId()}`, {
@@ -213,13 +219,13 @@
</ListItem>
) : (
moderators.map((moderator) => (
- <ListItem key={moderator.uri}>
+ <ListItem key={moderator.getUri()}>
<ListItemAvatar>
- <ConversationAvatar name={moderator.getDisplayName()} />
+ <ConversationAvatar displayName={moderator.getDisplayName()} />
</ListItemAvatar>
<ListItemText primary={moderator.getDisplayName()} />
<ListItemSecondaryAction>
- <IconButton onClick={(e) => removeModerator(moderator.uri)} size="large">
+ <IconButton onClick={(e) => removeModerator(moderator.getUri())} size="large">
<DeleteRounded />
</IconButton>
</ListItemSecondaryAction>
diff --git a/client/src/pages/404.jsx b/client/src/pages/404.tsx
similarity index 100%
rename from client/src/pages/404.jsx
rename to client/src/pages/404.tsx
diff --git a/client/src/pages/accountCreation.jsx b/client/src/pages/AccountCreation.tsx
similarity index 96%
rename from client/src/pages/accountCreation.jsx
rename to client/src/pages/AccountCreation.tsx
index b8c323b..30a22ee 100644
--- a/client/src/pages/accountCreation.jsx
+++ b/client/src/pages/AccountCreation.tsx
@@ -3,7 +3,7 @@
import ListItemLink from '../components/ListItemLink';
-export default function AccountCreationDialog(props) {
+export default function AccountCreationDialog() {
return (
<Container>
<Card>
diff --git a/client/src/pages/accountSelection.jsx b/client/src/pages/AccountSelection.tsx
similarity index 95%
rename from client/src/pages/accountSelection.jsx
rename to client/src/pages/AccountSelection.tsx
index e8bbf1e..9f6fcc8 100644
--- a/client/src/pages/accountSelection.jsx
+++ b/client/src/pages/AccountSelection.tsx
@@ -16,11 +16,11 @@
exit: { opacity: 0, y: '-50px' },
};
-const AccountSelection = (props) => {
+const AccountSelection = () => {
const navigate = useNavigate();
const [loaded, setLoaded] = useState(false);
const [error, setError] = useState(false);
- const [accounts, setAccounts] = useState([]);
+ const [accounts, setAccounts] = useState<Account[]>([]);
useEffect(() => {
const controller = new AbortController();
@@ -28,7 +28,7 @@
.fetch(`/api/accounts`, { signal: controller.signal })
.then((res) => res.json())
.then(
- (result) => {
+ (result: Account[]) => {
console.log(result);
if (result.length === 0) {
navigate('/newAccount');
diff --git a/client/src/pages/accountSettings.jsx b/client/src/pages/AccountSettings.tsx
similarity index 67%
rename from client/src/pages/accountSettings.jsx
rename to client/src/pages/AccountSettings.tsx
index 10dab29..0e715b5 100644
--- a/client/src/pages/accountSettings.jsx
+++ b/client/src/pages/AccountSettings.tsx
@@ -9,15 +9,23 @@
import AccountPreferences from '../components/AccountPreferences';
import Header from '../components/Header';
-const AccountSettings = (props) => {
+type AccountSettingsProps = {
+ accountId?: string;
+ account?: Account;
+};
+
+const AccountSettings = (props: AccountSettingsProps) => {
console.log('ACCOUNT SETTINGS', props.account);
- let accountId = useParams().accountId;
- if (props.accountId) {
- accountId = props.accountId;
+ const params = useParams();
+ const accountId = props.accountId || params.accountId;
+
+ if (accountId == null) {
+ throw new Error('Missing accountId');
}
+
const dispatch = useAppDispatch();
- const [state, setState] = useState({ loaded: false });
+ const [account, setAccount] = useState<Account | null>(null);
useEffect(() => {
dispatch(setAccountId(accountId));
@@ -27,10 +35,10 @@
.fetch(`/api/accounts/${accountId}`, { signal: controller.signal })
.then((res) => res.json())
.then((result) => {
- let account = Account.from(result);
+ const account = Account.from(result);
account.setDevices(result.devices);
dispatch(setAccountObject(account));
- setState({ loaded: true, account: account });
+ setAccount(account);
})
.catch((e) => console.log(e));
// return () => controller.abort() // crash on React18
@@ -39,16 +47,8 @@
return (
<Container maxWidth="sm">
<Header />
- {state.loaded ? (
- <AccountPreferences
- account={state.account}
- onAccontChanged={(account) =>
- setState((state) => {
- state.account = account;
- return state;
- })
- }
- />
+ {account != null ? (
+ <AccountPreferences account={account} onAccountChanged={(account: Account) => setAccount(account)} />
) : (
<CircularProgress />
)}
diff --git a/client/src/pages/addContactPage.jsx b/client/src/pages/AddContactPage.tsx
similarity index 79%
rename from client/src/pages/addContactPage.jsx
rename to client/src/pages/AddContactPage.tsx
index c1b242f..2dfae73 100644
--- a/client/src/pages/addContactPage.jsx
+++ b/client/src/pages/AddContactPage.tsx
@@ -1,18 +1,25 @@
import GroupAddRounded from '@mui/icons-material/GroupAddRounded';
import { Box, Card, CardContent, Container, Fab, Typography } from '@mui/material';
-import { useNavigate } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import { setRefreshFromSlice } from '../../redux/appSlice';
import { useAppDispatch } from '../../redux/hooks';
import authManager from '../AuthManager';
-export default function AddContactPage(props) {
+type AddContactPageProps = {
+ accountId: string;
+ contactId: string;
+};
+
+export default function AddContactPage(props: AddContactPageProps) {
const navigate = useNavigate();
- const accountId = props.accountId || props.match.params.accountId;
- const contactId = props.contactId || props.match.params.contactId;
+
+ const params = useParams();
+ const accountId = props.accountId || params.accountId;
+ const contactId = props.contactId || params.contactId;
const dispatch = useAppDispatch();
- const handleClick = async (e) => {
+ const handleClick = async () => {
const response = await authManager
.fetch(`/api/accounts/${accountId}/conversations`, {
method: 'POST',
diff --git a/client/src/pages/jamiAccountCreation.jsx b/client/src/pages/JamiAccountCreation.tsx
similarity index 93%
rename from client/src/pages/jamiAccountCreation.jsx
rename to client/src/pages/JamiAccountCreation.tsx
index 09f8612..8360e06 100644
--- a/client/src/pages/jamiAccountCreation.jsx
+++ b/client/src/pages/JamiAccountCreation.tsx
@@ -1,18 +1,18 @@
import { AddRounded } from '@mui/icons-material';
import { Box, Card, CardActions, CardContent, Container, Fab, Typography } from '@mui/material';
-import { useState } from 'react';
+import { FormEvent, useState } from 'react';
import { useNavigate } from 'react-router';
import authManager from '../AuthManager';
import UsernameChooser from '../components/UsernameChooser';
-export default function JamiAccountDialog(props) {
+export default function JamiAccountDialog() {
const [name, setName] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const navigate = useNavigate();
- const onSubmit = async (event) => {
+ const onSubmit = async (event: FormEvent) => {
event.preventDefault();
setLoading(true);
const result = await authManager
diff --git a/client/src/pages/JamiMessenger.jsx b/client/src/pages/JamiMessenger.tsx
similarity index 62%
rename from client/src/pages/JamiMessenger.jsx
rename to client/src/pages/JamiMessenger.tsx
index 017a9b9..e2709b9 100644
--- a/client/src/pages/JamiMessenger.jsx
+++ b/client/src/pages/JamiMessenger.tsx
@@ -1,13 +1,13 @@
import { Route, Routes } from 'react-router-dom';
-import Messenger from './messenger.jsx';
+import Messenger from './Messenger';
-export default function JamiMessenger(props) {
+export default function JamiMessenger() {
return (
<Routes>
<Route path="addContact/:contactId" element={<Messenger />} />
<Route path="conversation/:conversationId" element={<Messenger />} />
- <Route index path="*" element={<Messenger />} />
+ <Route path="*" element={<Messenger />} />
</Routes>
);
}
diff --git a/client/src/pages/loginDialog.jsx b/client/src/pages/LoginDialog.tsx
similarity index 85%
rename from client/src/pages/loginDialog.jsx
rename to client/src/pages/LoginDialog.tsx
index 34070d7..e98cc79 100644
--- a/client/src/pages/loginDialog.jsx
+++ b/client/src/pages/LoginDialog.tsx
@@ -8,7 +8,7 @@
import Link from '@mui/material/Link';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
-import { Component } from 'react';
+import { ChangeEvent, Component, MouseEvent } from 'react';
import authManager from '../AuthManager';
@@ -26,8 +26,22 @@
);
}
-class SignInPage extends Component {
- constructor(props) {
+type SignInPageProps = {
+ open: boolean;
+};
+type SignInPageState = {
+ username?: string;
+ password?: string;
+ submitted?: boolean;
+ loading?: boolean;
+ redirect?: boolean;
+ error?: boolean;
+ open?: boolean;
+ errorMessage?: string;
+};
+
+class SignInPage extends Component<SignInPageProps, SignInPageState> {
+ constructor(props: SignInPageProps) {
console.log('SignInPage ' + props.open);
super(props);
this.state = {
@@ -38,11 +52,11 @@
this.localLogin = this.localLogin.bind(this);
}
- handleusername(text) {
+ handleusername(text: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
this.setState({ username: text.target.value });
}
- handlePassword(text) {
+ handlePassword(text: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
this.setState({ password: text.target.value });
}
@@ -82,11 +96,12 @@
})*/
}
- handleSubmit(event) {
+ handleSubmit(event: MouseEvent<HTMLButtonElement>) {
event.preventDefault();
- let obj = {};
- obj.username = this.state.username;
- obj.password = this.state.password;
+ const obj = {
+ username: this.state.username,
+ password: this.state.password,
+ };
this.setState({
submitted: true,
@@ -94,7 +109,7 @@
});
fetch('/api/login?username=' + obj.username + '&password=' + obj.password, {
- header: {
+ headers: {
'Content-Type': 'application/json',
},
method: 'POST',
@@ -102,11 +117,11 @@
//body: JSON.stringify({ obj })
})
.then((res) => {
- if (res.status === '200') {
+ if (res.status === 200) {
this.setState({
redirect: true,
});
- } else if (res.status === '401') {
+ } else if (res.status === 401) {
this.setState({
loading: false,
error: true,
diff --git a/client/src/pages/messenger.jsx b/client/src/pages/Messenger.tsx
similarity index 84%
rename from client/src/pages/messenger.jsx
rename to client/src/pages/Messenger.tsx
index 3108fe6..c9449cd 100644
--- a/client/src/pages/messenger.jsx
+++ b/client/src/pages/Messenger.tsx
@@ -12,27 +12,37 @@
import Header from '../components/Header';
import LoadingPage from '../components/loading';
import NewContactForm from '../components/NewContactForm';
-import AddContactPage from './addContactPage.jsx';
+import AddContactPage from './AddContactPage';
-const Messenger = (props) => {
+type MessengerProps = {
+ accountId?: string;
+ conversationId?: string;
+ contactId?: string;
+};
+
+const Messenger = (props: MessengerProps) => {
const { refresh } = useAppSelector((state) => state.app);
- const [conversations, setConversations] = useState(undefined);
+ const [conversations, setConversations] = useState<Conversation[] | undefined>(undefined);
const [searchQuery, setSearchQuery] = useState('');
- const [searchResult, setSearchResults] = useState(undefined);
+ const [searchResult, setSearchResults] = useState<Conversation | undefined>(undefined);
const params = useParams();
const accountId = props.accountId || params.accountId;
const conversationId = props.conversationId || params.conversationId;
const contactId = props.contactId || params.contactId;
+ if (accountId == null) {
+ throw new Error('Missing accountId');
+ }
+
useEffect(() => {
console.log('REFRESH CONVERSATIONS FROM MESSENGER');
const controller = new AbortController();
authManager
.fetch(`/api/accounts/${accountId}/conversations`, { signal: controller.signal })
.then((res) => res.json())
- .then((result) => {
+ .then((result: Conversation[]) => {
console.log(result);
setConversations(Object.values(result).map((c) => Conversation.from(accountId, c)));
});
@@ -48,7 +58,7 @@
if (response.status === 200) {
return response.json();
} else {
- throw new Error(response.status);
+ throw new Error(response.status.toString());
}
})
.then((response) => {
diff --git a/client/src/pages/serverConfiguration.jsx b/client/src/pages/ServerConfiguration.tsx
similarity index 68%
rename from client/src/pages/serverConfiguration.jsx
rename to client/src/pages/ServerConfiguration.tsx
index 829977e..e9e5328 100644
--- a/client/src/pages/serverConfiguration.jsx
+++ b/client/src/pages/ServerConfiguration.tsx
@@ -1,16 +1,21 @@
import CircularProgress from '@mui/material/CircularProgress';
import Container from '@mui/material/Container';
import { useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
import Account from '../../../model/Account';
import authManager from '../AuthManager';
import AccountPreferences from '../components/AccountPreferences';
import Header from '../components/Header';
-const ServerOverview = (props) => {
- const [loaded, setLoaded] = useState(false);
- const [account, setAccount] = useState();
- const accountId = props.accountId || props.match.params.accountId;
+type ServerOverviewProps = {
+ accountId?: string;
+};
+
+const ServerOverview = (props: ServerOverviewProps) => {
+ const [account, setAccount] = useState<Account | null>(null);
+ const params = useParams();
+ const accountId = props.accountId || params.accountId;
useEffect(() => {
const controller = new AbortController();
@@ -19,7 +24,6 @@
.then((res) => res.json())
.then((result) => {
console.log(result);
- setLoaded(true);
setAccount(Account.from(result));
})
.catch((e) => console.log(e));
@@ -29,7 +33,7 @@
return (
<Container maxWidth="sm" className="app">
<Header />
- {loaded ? <AccountPreferences account={account} /> : <CircularProgress />}
+ {account != null ? <AccountPreferences account={account} /> : <CircularProgress />}
</Container>
);
};
diff --git a/client/src/pages/serverSetup.jsx b/client/src/pages/ServerSetup.tsx
similarity index 89%
rename from client/src/pages/serverSetup.jsx
rename to client/src/pages/ServerSetup.tsx
index b7f79a7..dd414d3 100644
--- a/client/src/pages/serverSetup.jsx
+++ b/client/src/pages/ServerSetup.tsx
@@ -1,17 +1,17 @@
import GroupAddRounded from '@mui/icons-material/GroupAddRounded';
import { Box, Card, CardContent, Container, Fab, Input, Typography } from '@mui/material';
-import { useState } from 'react';
+import { FormEvent, useState } from 'react';
import authManager from '../AuthManager';
-export default function ServerSetup(props) {
+export default function ServerSetup() {
const [password, setPassword] = useState('');
const [passwordRepeat, setPasswordRepeat] = useState('');
const [loading, setLoading] = useState(false);
const isValid = () => password && password === passwordRepeat;
- const handleSubmit = (e) => {
+ const handleSubmit = (e: FormEvent) => {
e.preventDefault();
setLoading(true);
if (!isValid()) return;
@@ -20,7 +20,7 @@
return (
<Container className="message-list">
- <Card disabled={loading}>
+ <Card>
<CardContent component="form" onSubmit={handleSubmit}>
<Typography gutterBottom variant="h5" component="h2">
Jami Web Node setup
@@ -43,6 +43,7 @@
type="password"
placeholder="New password"
autoComplete="new-password"
+ disabled={loading}
/>
</div>
<div>
@@ -54,11 +55,12 @@
type="password"
placeholder="Repeat password"
autoComplete="new-password"
+ disabled={loading}
/>
</div>
</Box>
<Box style={{ textAlign: 'center', marginTop: 24 }}>
- <Fab variant="extended" color="primary" type="submit" disabled={!isValid()}>
+ <Fab variant="extended" color="primary" type="submit" disabled={!isValid() || loading}>
<GroupAddRounded />
Create admin account
</Fab>