Migrate client features to use new server
Remove all authManager references where possible.
Change fetch calls url to new server.
GitLab: #79
GitLab: #100
GitLab: #110
Change-Id: I1dce64108ceba67531372df764f8f7563cc50a3b
diff --git a/client/src/components/AccountPreferences.tsx b/client/src/components/AccountPreferences.tsx
index 8ca098e..f0c5d31 100644
--- a/client/src/components/AccountPreferences.tsx
+++ b/client/src/components/AccountPreferences.tsx
@@ -38,8 +38,8 @@
import { Account, AccountDetails } from 'jami-web-common';
import { useState } from 'react';
-import authManager from '../AuthManager';
import { useAuthContext } from '../contexts/AuthProvider';
+import { apiUrl } from '../utils/constants';
import ConversationAvatar from './ConversationAvatar';
import ConversationsOverviewCard from './ConversationsOverviewCard';
import JamiIdCard from './JamiIdCard';
@@ -64,7 +64,8 @@
export default function AccountPreferences({ account: _account }: AccountPreferencesProps) {
const authContext = useAuthContext(true);
const account = _account ?? authContext?.account;
- if (!account) {
+ const token = authContext?.token;
+ if (!account || !token) {
throw new Error('Account not defined');
}
@@ -83,23 +84,34 @@
const addModerator = () => {
if (defaultModeratorUri) {
- authManager.fetch(`/api/accounts/${account.getId()}/defaultModerators/${defaultModeratorUri}`, { method: 'PUT' });
+ fetch(new URL(`/default-moderators/${defaultModeratorUri}`, apiUrl), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ method: 'PUT',
+ });
setDefaultModeratorUri('');
}
};
const removeModerator = (uri: string) =>
- authManager.fetch(`/api/accounts/${account.getId()}/defaultModerators/${uri}`, { method: 'DELETE' });
+ fetch(new URL(`/default-moderators/${uri}`, apiUrl), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ 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',
+ fetch(new URL('/account', apiUrl), {
+ method: 'PATCH',
headers: {
Accept: 'application/json',
+ Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(newDetails),
@@ -133,7 +145,7 @@
<Grid item xs={12} sm={6}>
<motion.div variants={thumbnailVariants}>
- <ConversationsOverviewCard accountId={account.getId()} />
+ <ConversationsOverviewCard />
</motion.div>
</Grid>
diff --git a/client/src/components/ContactList.jsx b/client/src/components/ContactList.jsx
index c35c4f0..3bc040b 100644
--- a/client/src/components/ContactList.jsx
+++ b/client/src/components/ContactList.jsx
@@ -21,8 +21,9 @@
import { useEffect, useState } from 'react';
import Modal from 'react-modal';
-import authManager from '../AuthManager';
+import { useAuthContext } from '../contexts/AuthProvider';
import { useAppDispatch, useAppSelector } from '../redux/hooks';
+import { apiUrl } from '../utils/constants';
import ConversationAvatar from './ConversationAvatar';
const customStyles = {
@@ -37,6 +38,7 @@
};
export default function ContactList() {
+ const { token } = useAuthContext();
const { accountId } = useAppSelector((state) => state.userInfo);
const dispatch = useAppDispatch();
@@ -59,10 +61,12 @@
const getContactDetails = () => {
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${accountId}/contacts/details/${currentContact.id}`, {
- signal: controller.signal,
- })
+ fetch(new URL(`/contacts/${currentContact.id}`, apiUrl), {
+ signal: controller.signal,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
.then((res) => res.json())
.then((result) => {
console.log('CONTACT LIST - DETAILS: ', result);
@@ -70,33 +74,41 @@
.catch((e) => console.log('ERROR GET CONTACT DETAILS: ', e));
};
- const removeOrBlock = (typeOfRemove) => {
+ const removeOrBlock = (block = false) => {
console.log('REMOVE');
setBlockOrRemove(false);
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${accountId}/contacts/${typeOfRemove}/${currentContact.id}`, {
- signal: controller.signal,
- method: 'DELETE',
- })
+ let url = `/contacts/${currentContact.id}`;
+ if (block) {
+ url += '/block';
+ }
+ fetch(new URL(url, apiUrl), {
+ signal: controller.signal,
+ method: block ? 'POST' : 'DELETE',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
.then((res) => res.json())
- .catch((e) => console.log(`ERROR ${typeOfRemove}ing CONTACT : `, e));
+ .catch((e) => console.log(`ERROR ${block ? 'blocking' : 'removing'} CONTACT : `, e));
closeModalDelete();
};
useEffect(() => {
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${accountId}/contacts`, {
- signal: controller.signal,
- })
+ fetch(new URL(`/contacts`, apiUrl), {
+ signal: controller.signal,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
.then((res) => res.json())
.then((result) => {
console.log('CONTACTS: ', result);
setContacts(result);
});
return () => controller.abort();
- }, [accountId, blockOrRemove]);
+ }, [token]);
return (
<div className="rooms-list">
@@ -165,9 +177,9 @@
Voulez vous vraiment {blockOrRemove ? 'bloquer' : 'supprimer'} ce contact?
<br />
{blockOrRemove ? (
- <button onClick={() => removeOrBlock('block')}>Bloquer</button>
+ <button onClick={() => removeOrBlock(true)}>Bloquer</button>
) : (
- <button onClick={() => removeOrBlock('remove')}>Supprimer</button>
+ <button onClick={() => removeOrBlock()}>Supprimer</button>
)}
<button onClick={closeModalDelete}>Annuler</button>
</Modal>
diff --git a/client/src/components/ConversationListItem.tsx b/client/src/components/ConversationListItem.tsx
index 9a928e7..c094d71 100644
--- a/client/src/components/ConversationListItem.tsx
+++ b/client/src/components/ConversationListItem.tsx
@@ -33,12 +33,20 @@
import Modal from 'react-modal';
import { useNavigate, useParams } from 'react-router-dom';
-import authManager from '../AuthManager';
+import { useAuthContext } from '../contexts/AuthProvider';
import { setRefreshFromSlice } from '../redux/appSlice';
import { useAppDispatch } from '../redux/hooks';
+import { apiUrl } from '../utils/constants';
import ConversationAvatar from './ConversationAvatar';
-import { CancelIcon, RemoveContactIcon, VideoCallIcon } from './SvgIcon';
-import { AudioCallIcon, BlockContactIcon, ContactDetailsIcon, MessageIcon } from './SvgIcon';
+import {
+ AudioCallIcon,
+ BlockContactIcon,
+ CancelIcon,
+ ContactDetailsIcon,
+ MessageIcon,
+ RemoveContactIcon,
+ VideoCallIcon,
+} from './SvgIcon';
const cancelStyles: Modal.Styles = {
content: {
@@ -81,6 +89,7 @@
};
export default function ConversationListItem({ conversation }: ConversationListItemProps) {
+ const { token } = useAuthContext();
const { conversationId, contactId } = useParams();
const dispatch = useAppDispatch();
@@ -95,8 +104,6 @@
const [userId] = useState(conversation?.getFirstMember()?.contact.getUri());
const [isSwarm] = useState(true);
- const navigateUrlPrefix = `/deprecated-account/${conversation.getAccountId()}`;
-
const openMenu = (e: MouseEvent<HTMLDivElement>) => {
e.preventDefault();
console.log(e);
@@ -110,10 +117,12 @@
const getContactDetails = () => {
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${conversation.getAccountId()}/contacts/details/${userId}`, {
- signal: controller.signal,
- })
+ fetch(new URL(`/contacts/${userId}`, apiUrl), {
+ signal: controller.signal,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
.then((res) => res.json())
.then((result) => {
console.log('CONTACT LIST - DETAILS: ', result);
@@ -121,38 +130,43 @@
.catch((e) => console.log('ERROR GET CONTACT DETAILS: ', e));
};
- const removeOrBlock = (typeOfRemove: 'block' | 'remove') => {
- console.log(typeOfRemove);
+ const removeOrBlock = (block = false) => {
setBlockOrRemove(false);
- console.log('EEEH', typeOfRemove, conversation.getAccountId(), userId);
+ console.log('EEEH', conversation.getAccountId(), userId);
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${conversation.getAccountId()}/contacts/${typeOfRemove}/${userId}`, {
- signal: controller.signal,
- method: 'DELETE',
- })
+ let url = `/contacts/${userId}`;
+ if (block) {
+ url += '/block';
+ }
+ fetch(new URL(url, apiUrl), {
+ signal: controller.signal,
+ method: block ? 'POST' : 'DELETE',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
.then((res) => res.json())
.then(() => {
console.log('propre');
dispatch(setRefreshFromSlice());
})
.catch((e) => {
- console.log(`ERROR ${typeOfRemove}ing CONTACT : `, e);
+ console.log(`ERROR ${block ? 'blocking' : 'removing'} CONTACT : `, e);
dispatch(setRefreshFromSlice());
});
closeModalDelete();
};
- const uri = conversation.getId() ? `conversation/${conversation.getId()}` : `addContact/${userId}`;
+ const uri = conversation.getId() ? `/account/conversation/${conversation.getId()}` : `/account/addContact/${userId}`;
return (
<div onContextMenu={openMenu}>
<div>
<Menu open={!!menuAnchorEl} onClose={closeModal} anchorEl={menuAnchorEl}>
<MenuItem
onClick={() => {
- navigate(`${navigateUrlPrefix}/${uri}`);
+ navigate(uri);
closeModal();
}}
>
@@ -165,7 +179,7 @@
</MenuItem>
<MenuItem
onClick={() => {
- navigate(`${navigateUrlPrefix}/call/${conversation.getId()}`);
+ navigate(`/account/call/${conversation.getId()}`);
}}
>
<ListItemIcon>
@@ -178,7 +192,7 @@
<MenuItem
onClick={() => {
- navigate(`${navigateUrlPrefix}/call/${conversation.getId()}?video=true`);
+ navigate(`call/${conversation.getId()}?video=true`);
}}
>
<ListItemIcon>
@@ -192,7 +206,7 @@
{isSelected && (
<MenuItem
onClick={() => {
- navigate(`${navigateUrlPrefix}/`);
+ navigate(`/account`);
closeModal();
}}
>
@@ -366,8 +380,8 @@
<Stack direction={'row'} top={'25px'} alignSelf="center" spacing={1}>
<Box
onClick={() => {
- if (blockOrRemove) removeOrBlock('block');
- else removeOrBlock('remove');
+ if (blockOrRemove) removeOrBlock(true);
+ else removeOrBlock(false);
}}
style={{
width: '100px',
@@ -398,12 +412,7 @@
</Modal>
</div>
- <ListItem
- button
- alignItems="flex-start"
- selected={isSelected}
- onClick={() => navigate(`${navigateUrlPrefix}/${uri}`)}
- >
+ <ListItem button alignItems="flex-start" selected={isSelected} onClick={() => navigate(uri)}>
<ListItemAvatar>
<ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
</ListItemAvatar>
diff --git a/client/src/components/ConversationView.tsx b/client/src/components/ConversationView.tsx
index 4ecffe8..03f0717 100644
--- a/client/src/components/ConversationView.tsx
+++ b/client/src/components/ConversationView.tsx
@@ -21,33 +21,27 @@
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router';
+import { useAuthContext } from '../contexts/AuthProvider';
import { SocketContext } from '../contexts/Socket';
import ChatInterface from '../pages/ChatInterface';
-import { useAccountQuery } from '../services/Account';
import { useConversationQuery } from '../services/Conversation';
import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
import { AddParticipantButton, ShowOptionsMenuButton, StartAudioCallButton, StartVideoCallButton } from './Button';
import LoadingPage from './Loading';
type ConversationViewProps = {
- accountId: string;
conversationId: string;
};
-const ConversationView = ({ accountId, conversationId }: ConversationViewProps) => {
+const ConversationView = ({ conversationId }: ConversationViewProps) => {
+ const { account } = useAuthContext();
const socket = useContext(SocketContext);
- const [account, setAccount] = useState<Account | undefined>();
const [conversation, setConversation] = useState<Conversation | undefined>();
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(false);
- const accountQuery = useAccountQuery(accountId);
- const conversationQuery = useConversationQuery(accountId, conversationId);
+ const accountId = account.getId();
- useEffect(() => {
- if (accountQuery.isSuccess) {
- setAccount(Account.from(accountQuery.data));
- }
- }, [accountQuery.isSuccess, accountQuery.data]);
+ const conversationQuery = useConversationQuery(conversationId);
useEffect(() => {
if (conversationQuery.isSuccess) {
@@ -57,12 +51,12 @@
}, [accountId, conversationQuery.isSuccess, conversationQuery.data]);
useEffect(() => {
- setIsLoading(accountQuery.isLoading || conversationQuery.isLoading);
- }, [accountQuery.isLoading, conversationQuery.isLoading]);
+ setIsLoading(conversationQuery.isLoading);
+ }, [conversationQuery.isLoading]);
useEffect(() => {
- setError(accountQuery.isError || conversationQuery.isError);
- }, [accountQuery.isError, conversationQuery.isError]);
+ setError(conversationQuery.isError);
+ }, [conversationQuery.isError]);
useEffect(() => {
if (!conversation) return;
@@ -94,7 +88,7 @@
borderTop: '1px solid #E5E5E5',
}}
/>
- <ChatInterface account={account} conversationId={conversationId} members={conversation.getMembers()} />
+ <ChatInterface conversationId={conversationId} members={conversation.getMembers()} />
</Stack>
);
};
diff --git a/client/src/components/ConversationsOverviewCard.jsx b/client/src/components/ConversationsOverviewCard.tsx
similarity index 60%
rename from client/src/components/ConversationsOverviewCard.jsx
rename to client/src/components/ConversationsOverviewCard.tsx
index d233df0..4ed0ed4 100644
--- a/client/src/components/ConversationsOverviewCard.jsx
+++ b/client/src/components/ConversationsOverviewCard.tsx
@@ -16,43 +16,46 @@
* <https://www.gnu.org/licenses/>.
*/
import { Card, CardActionArea, CardContent, CircularProgress, Typography } from '@mui/material';
-import { Conversation } from 'jami-web-common';
+import { Conversation } from 'jami-web-common/dist/Conversation';
import { useEffect, useState } from 'react';
-import { useNavigate, useParams } from 'react-router';
+import { useNavigate } from 'react-router';
-import authManager from '../AuthManager';
+import { useAuthContext } from '../contexts/AuthProvider';
+import { apiUrl } from '../utils/constants';
-export default function ConversationsOverviewCard(props) {
+export default function ConversationsOverviewCard() {
+ const { token, account } = useAuthContext();
const navigate = useNavigate();
- let accountId = useParams().accountId;
- if (props.accountId) {
- accountId = props.accountId;
- }
- const [loaded, setLoaded] = useState(false);
- const [conversations, setConversations] = useState([]);
+
+ const [conversationCount, setConversationCount] = useState<number | undefined>();
+
+ const accountId = account.getId();
useEffect(() => {
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${accountId}/conversations`, { signal: controller.signal })
+ fetch(new URL('/conversations', apiUrl), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ signal: controller.signal,
+ })
.then((res) => res.json())
- .then((result) => {
+ .then((result: Conversation[]) => {
console.log(result);
- setLoaded(true);
- setConversations(Object.values(result).map((c) => Conversation.from(accountId, c)));
+ setConversationCount(result.length);
});
- // return () => controller.abort() // crash on React18
- }, [accountId]);
+ return () => controller.abort(); // crash on React18
+ }, [token, accountId]);
return (
- <Card onClick={() => navigate(`/deprecated-account/${accountId}`)}>
+ <Card onClick={() => navigate(`/account`)}>
<CardActionArea>
<CardContent>
<Typography color="textSecondary" gutterBottom>
Conversations
</Typography>
<Typography gutterBottom variant="h5" component="h2">
- {loaded ? conversations.length : <CircularProgress size={24} />}
+ {conversationCount != null ? conversationCount : <CircularProgress size={24} />}
</Typography>
</CardContent>
</CardActionArea>
diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx
index 445dbae..6276fa4 100644
--- a/client/src/components/Header.tsx
+++ b/client/src/components/Header.tsx
@@ -17,29 +17,19 @@
*/
import { Box, Button, Menu, MenuItem } from '@mui/material';
import { MouseEvent, useState } from 'react';
-import { useNavigate, useParams } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthProvider';
-import { setAccessToken } from '../utils/auth';
export default function Header() {
- const authContext = useAuthContext(true);
+ const { logout } = useAuthContext();
const navigate = useNavigate();
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget);
const handleClose = () => setAnchorEl(null);
- const params = useParams();
const goToContacts = () => navigate(`/contacts`);
- const goToAccountSettings = () => navigate(`/deprecated-account/${params.accountId}/settings`);
-
- const deprecatedLogout = () => {
- setAccessToken('');
- navigate('/deprecated-account', { replace: true });
- };
- // TODO: Remove deprecated_logout once migration to new server is complete
- const logout = authContext?.logout ?? deprecatedLogout;
return (
<Box>
@@ -48,7 +38,7 @@
</Button>
<Menu id="simple-menu" anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={goToContacts}>Contacts</MenuItem>
- {params.accountId && <MenuItem onClick={goToAccountSettings}>Account settings</MenuItem>}
+ <MenuItem onClick={() => navigate('/settings')}>Account settings</MenuItem>
<MenuItem onClick={logout}>Log out</MenuItem>
</Menu>
</Box>
diff --git a/client/src/components/MessageList.tsx b/client/src/components/MessageList.tsx
index 7b9bf2d..0ae04d2 100644
--- a/client/src/components/MessageList.tsx
+++ b/client/src/components/MessageList.tsx
@@ -17,21 +17,22 @@
*/
import { Typography } from '@mui/material';
import { Box, Stack } from '@mui/system';
-import { Account, ConversationMember, Message } from 'jami-web-common';
+import { ConversationMember, Message } from 'jami-web-common';
import { MutableRefObject, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Waypoint } from 'react-waypoint';
+import { useAuthContext } from '../contexts/AuthProvider';
import { MessageRow } from './Message';
import { ArrowDownIcon } from './SvgIcon';
interface MessageListProps {
- account: Account;
members: ConversationMember[];
messages: Message[];
}
-export default function MessageList({ account, members, messages }: MessageListProps) {
+export default function MessageList({ members, messages }: MessageListProps) {
+ const { account } = useAuthContext();
const [showScrollButton, setShowScrollButton] = useState(false);
const listBottomRef = useRef<HTMLElement>();
diff --git a/client/src/components/SendMessageForm.tsx b/client/src/components/SendMessageForm.tsx
index 797d810..dfb5f3d 100644
--- a/client/src/components/SendMessageForm.tsx
+++ b/client/src/components/SendMessageForm.tsx
@@ -17,10 +17,11 @@
*/
import { InputBase } from '@mui/material';
import { Stack } from '@mui/system';
-import { Account, ConversationMember } from 'jami-web-common';
+import { ConversationMember } from 'jami-web-common';
import { ChangeEvent, FormEvent, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
+import { useAuthContext } from '../contexts/AuthProvider';
import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
import {
RecordVideoMessageButton,
@@ -31,15 +32,14 @@
} from './Button';
type SendMessageFormProps = {
- account: Account;
members: ConversationMember[];
onSend: (message: string) => void;
openFilePicker: () => void;
};
-export default function SendMessageForm({ account, members, onSend, openFilePicker }: SendMessageFormProps) {
+export default function SendMessageForm({ members, onSend, openFilePicker }: SendMessageFormProps) {
const [currentMessage, setCurrentMessage] = useState('');
- const placeholder = usePlaceholder(account, members);
+ const placeholder = usePlaceholder(members);
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
@@ -90,7 +90,8 @@
);
}
-const usePlaceholder = (account: Account, members: ConversationMember[]) => {
+const usePlaceholder = (members: ConversationMember[]) => {
+ const { account } = useAuthContext();
const { t } = useTranslation();
return useMemo(() => {
diff --git a/client/src/components/UsernameChooser.jsx b/client/src/components/UsernameChooser.jsx
index 486d947..3c1f584 100644
--- a/client/src/components/UsernameChooser.jsx
+++ b/client/src/components/UsernameChooser.jsx
@@ -20,7 +20,7 @@
import { useEffect, useState } from 'react';
import usePromise from 'react-fetch-hook/usePromise';
-import authManager from '../AuthManager';
+import { apiUrl } from '../utils/constants.js';
const isInputValid = (input) => input && input.length > 2;
@@ -30,7 +30,7 @@
const { isLoading, data, error } = usePromise(
() =>
isInputValid(query)
- ? authManager.fetch(`/api/ns/name/${query}`).then((res) => {
+ ? fetch(new URL(`/ns/username/${query}`, apiUrl)).then((res) => {
if (res.status === 200) return res.json();
else throw res.status;
})