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/AuthManager.ts b/client/src/AuthManager.ts
deleted file mode 100644
index 91ac981..0000000
--- a/client/src/AuthManager.ts
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2022 Savoir-faire Linux Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
- */
-
-/* eslint-disable no-undef */
-// TODO: This hides eslint errors for this file. This should be removed once this file is cleaned up.
-
-import { PromiseExecutor } from 'jami-web-common';
-
-interface AuthManagerState {
- initialized: boolean;
- authenticated: boolean;
- setupComplete: boolean;
- error: boolean;
-}
-
-interface AuthManagerTask extends PromiseExecutor<Response> {
- url: string;
- init?: RequestInit;
-}
-
-interface InitData {
- loggedin?: true;
- username?: string;
- type?: string;
- setupComplete?: boolean;
-}
-
-type OnAuthChanged = (auth: AuthManagerState) => void;
-
-class AuthManager {
- private authenticating: boolean;
- private readonly _state: AuthManagerState;
- private tasks: AuthManagerTask[];
- private onAuthChanged: OnAuthChanged | undefined;
-
- constructor() {
- console.log('AuthManager()');
- this.authenticating = false;
-
- this._state = {
- initialized: false,
- authenticated: true,
- setupComplete: true,
- error: false,
- };
-
- this.tasks = [];
- this.onAuthChanged = undefined;
- }
-
- isAuthenticated() {
- return this._state.authenticated;
- }
-
- getState() {
- return this._state;
- }
-
- setInitData(data: InitData) {
- this.authenticating = false;
- this._state.initialized = true;
- if (data.username) {
- Object.assign(this._state, {
- authenticated: true,
- setupComplete: true,
- error: false,
- user: { username: data.username, type: data.type },
- });
- } else {
- Object.assign(this._state, {
- authenticated: false,
- setupComplete: data.setupComplete ?? true,
- error: false,
- });
- }
- console.log('Init ended');
- /*if (this.onAuthChanged)
- this.onAuthChanged(this._state)*/
- }
-
- init(cb: OnAuthChanged) {
- this.onAuthChanged = cb;
- if (this._state.initialized || this.authenticating) return;
- /*if (initData) {
- console.log("Using static initData")
- this.setInitData(initData)
- return
- }*/
- this.authenticating = true;
- fetch('/auth')
- .then(async (response) => {
- this.authenticating = false;
- this._state.initialized = true;
- if (response.status === 200) {
- this.setInitData(await response.json());
- } else if (response.status === 401) {
- this.setInitData(await response.json());
- } else {
- this._state.error = true;
- if (this.onAuthChanged) this.onAuthChanged(this._state);
- }
- })
- .catch((e) => {
- this.authenticating = false;
- console.log(e);
- });
- }
-
- deinit() {
- console.log('Deinit');
- this.onAuthChanged = undefined;
- }
-
- async setup(password: string) {
- if (this.authenticating || this._state.setupComplete) return;
- console.log('Starting setup');
- this.authenticating = true;
- const response = await fetch(`/setup`, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ password }),
- });
- console.log(response);
- if (response.ok) {
- console.log('Success, going home');
- //navigate('/')
- }
-
- this.authenticating = false;
- this._state.setupComplete = true;
- if (this.onAuthChanged) this.onAuthChanged(this._state);
- return response.ok;
- }
-
- authenticate(username: string, password: string) {
- if (this.authenticating) return;
- console.log('Starting authentication');
- this.authenticating = true;
- fetch(`/auth/local?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`, {
- method: 'POST',
- })
- .then((response) => {
- console.log(response);
- this.authenticating = false;
- this._state.authenticated = response.ok && response.status === 200;
- if (this.onAuthChanged) this.onAuthChanged(this._state);
- while (true) {
- const task = this.tasks.shift();
- if (!task) {
- break;
- }
- if (this._state.authenticated) {
- fetch(task.url, task.init)
- .then((res) => task.resolve(res))
- .catch((e) => console.log('Error executing pending task: ' + e));
- } else {
- task.reject(new Error('Authentication failed'));
- }
- }
- })
- .catch((e) => {
- this.authenticating = false;
- console.log(e);
- });
- }
-
- disconnect() {
- console.log('Disconnect');
- this._state.authenticated = false;
- if (this.onAuthChanged) this.onAuthChanged(this._state);
- }
-
- fetch(url: string, init?: RequestInit): Promise<Response> {
- console.log(`fetch ${url}`);
- if (!this._state.authenticated) {
- if (!init || !init.method || init.method === 'GET') {
- return new Promise<Response>((resolve, reject) => this.tasks.push({ url, init, resolve, reject }));
- } else {
- return new Promise<Response>((resolve, reject) => reject('Not authenticated'));
- }
- }
- return fetch(url, init).then((response) => {
- if (response.status === 401) {
- this.disconnect();
- return this.fetch(url, init);
- }
- return response;
- });
- }
-}
-
-export default new AuthManager();
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;
})
diff --git a/client/src/index.tsx b/client/src/index.tsx
index ef5e5d4..c8885ad 100644
--- a/client/src/index.tsx
+++ b/client/src/index.tsx
@@ -32,12 +32,8 @@
import AuthProvider from './contexts/AuthProvider';
import { SocketProvider } from './contexts/Socket';
import WebSocketProvider from './contexts/WebSocketProvider';
-import AccountSelection from './pages/AccountSelection';
import AccountSettings from './pages/AccountSettings';
-import CallInterface from './pages/CallInterface';
-import DeprecatedAccountSettings from './pages/DeprecatedAccountSettings';
import JamiMessenger from './pages/JamiMessenger';
-import Messenger from './pages/Messenger';
import ServerSetup from './pages/ServerSetup';
import Welcome from './pages/Welcome';
import { store } from './redux/store';
@@ -73,14 +69,6 @@
<Route path="contacts" element={<ContactList />} />
</Route>
<Route path="setup" element={<ServerSetup />} />
- {/* TODO: Remove this block after migration to new server*/}
- <Route path="deprecated-account" element={<AccountSelection />} />
- <Route path="deprecated-account/:accountId" element={<Messenger />}>
- <Route path="addContact/:contactId" element={<Messenger />} />
- <Route path="conversation/:conversationId" element={<Messenger />} />
- <Route path="call/:conversationId" element={<CallInterface />} />
- </Route>
- <Route path="deprecated-account/:accountId/settings" element={<DeprecatedAccountSettings />} />
</Route>
)
);
diff --git a/client/src/pages/AccountSelection.tsx b/client/src/pages/AccountSelection.tsx
deleted file mode 100644
index f8daf0a..0000000
--- a/client/src/pages/AccountSelection.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2022 Savoir-faire Linux Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
- */
-import { AddRounded } from '@mui/icons-material';
-import { Avatar, Card, CardHeader, Container, List } from '@mui/material';
-import { motion } from 'framer-motion';
-import { Account } from 'jami-web-common';
-import { Fragment, useEffect, useState } from 'react';
-import { useNavigate } from 'react-router';
-
-import authManager from '../AuthManager';
-import ConversationAvatar from '../components/ConversationAvatar';
-import Header from '../components/Header';
-import ListItemLink from '../components/ListItemLink';
-import LoadingPage from '../components/Loading';
-
-const variants = {
- enter: { opacity: 1, y: 0 },
- exit: { opacity: 0, y: '-50px' },
-};
-
-const AccountSelection = () => {
- const navigate = useNavigate();
- const [loaded, setLoaded] = useState(false);
- const [, setError] = useState(false);
- const [accounts, setAccounts] = useState<Account[]>([]);
-
- authManager.authenticate('admin', 'admin');
-
- useEffect(() => {
- const controller = new AbortController();
- authManager
- .fetch(`/api/accounts`, { signal: controller.signal })
- .then((res) => res.json())
- .then(
- (result: Account[]) => {
- console.log(result);
- if (result.length === 0) {
- navigate('/newAccount');
- } else {
- setLoaded(true);
- setAccounts(result.map((account) => Account.from(account)));
- }
- },
- (error) => {
- console.log(`get error ${error}`);
- setLoaded(true);
- setError(true);
- }
- )
- .catch((e) => console.log(e));
- // return () => controller.abort() // crash on React18
- }, [navigate]);
-
- if (!loaded) return <LoadingPage />;
- return (
- <Fragment>
- <Header />
- <Container maxWidth="sm" style={{ paddingBottom: 32 }}>
- <motion.div drag="x" initial="exit" animate="enter" exit="exit" variants={variants}>
- <Card style={{ marginTop: 32, marginBottom: 32 }}>
- <CardHeader title="Choose an account" />
- <List>
- {accounts.map((account) => (
- <ListItemLink
- key={account.getId()}
- icon={<ConversationAvatar displayName={account.getDisplayNameNoFallback()} />}
- to={`/deprecated-account/${account.getId()}/settings`}
- primary={account.getDisplayName()}
- secondary={account.getDisplayUri()}
- />
- ))}
- <ListItemLink
- icon={
- <Avatar>
- <AddRounded />
- </Avatar>
- }
- to="/newAccount"
- primary="Create new account"
- />
- </List>
- </Card>
- </motion.div>
- </Container>
- </Fragment>
- );
-};
-
-export default AccountSelection;
diff --git a/client/src/pages/AddContactPage.tsx b/client/src/pages/AddContactPage.tsx
index b0dfad0..dd93376 100644
--- a/client/src/pages/AddContactPage.tsx
+++ b/client/src/pages/AddContactPage.tsx
@@ -17,43 +17,40 @@
*/
import GroupAddRounded from '@mui/icons-material/GroupAddRounded';
import { Box, Card, CardContent, Container, Fab, Typography } from '@mui/material';
-import { useNavigate, useParams } from 'react-router-dom';
+import { useNavigate } 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';
type AddContactPageProps = {
- accountId: string;
contactId: string;
};
-export default function AddContactPage(props: AddContactPageProps) {
+export default function AddContactPage({ contactId }: AddContactPageProps) {
+ const { token } = useAuthContext();
const navigate = useNavigate();
- const params = useParams();
- const accountId = props.accountId || params.accountId;
- const contactId = props.contactId || params.contactId;
const dispatch = useAppDispatch();
const handleClick = async () => {
- const response = await authManager
- .fetch(`/api/accounts/${accountId}/conversations`, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ members: [contactId] }),
- })
- .then((res) => {
- dispatch(setRefreshFromSlice());
- return res.json();
- });
+ const response = await fetch(new URL(`/conversations`, apiUrl), {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ Authorization: `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ members: [contactId] }),
+ }).then((res) => {
+ dispatch(setRefreshFromSlice());
+ return res.json();
+ });
console.log(response);
if (response.conversationId) {
- navigate(`/deprecated-account/${accountId}/conversation/${response.conversationId}`);
+ navigate(`/account/conversation/${response.conversationId}`);
}
};
diff --git a/client/src/pages/ChatInterface.tsx b/client/src/pages/ChatInterface.tsx
index 4ad1830..b09f2fd 100644
--- a/client/src/pages/ChatInterface.tsx
+++ b/client/src/pages/ChatInterface.tsx
@@ -16,7 +16,7 @@
* <https://www.gnu.org/licenses/>.
*/
import { Box, Divider, Stack } from '@mui/material';
-import { Account, ConversationMember, Message } from 'jami-web-common';
+import { ConversationMember, Message } from 'jami-web-common';
import { useCallback, useContext, useEffect, useState } from 'react';
import { useDropzone } from 'react-dropzone';
@@ -29,18 +29,17 @@
import { FileHandler } from '../utils/files';
type ChatInterfaceProps = {
- account: Account;
conversationId: string;
members: ConversationMember[];
};
-const ChatInterface = ({ account, conversationId, members }: ChatInterfaceProps) => {
+const ChatInterface = ({ conversationId, members }: ChatInterfaceProps) => {
const socket = useContext(SocketContext);
const [messages, setMessages] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(false);
- const messagesQuery = useMessagesQuery(account.getId(), conversationId);
- const sendMessageMutation = useSendMessageMutation(account.getId(), conversationId);
+ const messagesQuery = useMessagesQuery(conversationId);
+ const sendMessageMutation = useSendMessageMutation(conversationId);
const [fileHandlers, setFileHandlers] = useState<FileHandler[]>([]);
@@ -119,14 +118,14 @@
/>
)}
<input {...getInputProps()} />
- <MessageList account={account} members={members} messages={messages} />
+ <MessageList members={members} messages={messages} />
<Divider
sx={{
margin: '30px 16px 0px 16px',
borderTop: '1px solid #E5E5E5',
}}
/>
- <SendMessageForm account={account} members={members} onSend={sendMessage} openFilePicker={openFilePicker} />
+ <SendMessageForm members={members} onSend={sendMessage} openFilePicker={openFilePicker} />
{fileHandlers.length > 0 && <FilePreviewsList fileHandlers={fileHandlers} removeFile={removeFile} />}
</Stack>
);
diff --git a/client/src/pages/DeprecatedAccountSettings.tsx b/client/src/pages/DeprecatedAccountSettings.tsx
deleted file mode 100644
index f69334d..0000000
--- a/client/src/pages/DeprecatedAccountSettings.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2022 Savoir-faire Linux Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
- */
-import { CircularProgress, Container } from '@mui/material';
-import { Account } from 'jami-web-common';
-import { useEffect, useState } from 'react';
-import { useParams } from 'react-router';
-
-import authManager from '../AuthManager';
-import AccountPreferences from '../components/AccountPreferences';
-import Header from '../components/Header';
-import { setAccountId } from '../redux/appSlice';
-import { useAppDispatch } from '../redux/hooks';
-
-type AccountSettingsProps = {
- accountId?: string;
- account?: Account;
-};
-
-const DeprecatedAccountSettings = (props: AccountSettingsProps) => {
- console.log('ACCOUNT SETTINGS', props.account);
- const params = useParams();
- const accountId = props.accountId || params.accountId;
-
- if (accountId == null) {
- throw new Error('Missing accountId');
- }
-
- const dispatch = useAppDispatch();
-
- const [localAccount, setLocalAccount] = useState<Account | null>(null);
-
- useEffect(() => {
- dispatch(setAccountId(accountId));
-
- const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${accountId}`, { signal: controller.signal })
- .then((res) => res.json())
- .then((result) => {
- console.log(result);
- const account = Account.from(result);
- account.setDevices(result.devices);
- setLocalAccount(account);
- })
- .catch((e) => console.log(e));
- // return () => controller.abort() // crash on React18
- }, [accountId, dispatch]);
-
- return (
- <Container maxWidth="sm">
- <Header />
- {localAccount != null ? <AccountPreferences account={localAccount} /> : <CircularProgress />}
- </Container>
- );
-};
-
-export default DeprecatedAccountSettings;
diff --git a/client/src/pages/JamiMessenger.tsx b/client/src/pages/JamiMessenger.tsx
index 2cb485b..b0b0812 100644
--- a/client/src/pages/JamiMessenger.tsx
+++ b/client/src/pages/JamiMessenger.tsx
@@ -21,6 +21,7 @@
import CallInterface from './CallInterface';
import Messenger from './Messenger';
+export type MessengerRouteParams = RouteParams<{ conversationId?: string; contactId?: string }, Record<string, never>>;
export type CallRouteParams = RouteParams<{ conversationId: string }, { video?: 'true' }>;
export default function JamiMessenger() {
diff --git a/client/src/pages/Messenger.tsx b/client/src/pages/Messenger.tsx
index c38294a..fdbb0f4 100644
--- a/client/src/pages/Messenger.tsx
+++ b/client/src/pages/Messenger.tsx
@@ -18,58 +18,60 @@
import { Stack } from '@mui/material';
import { Contact, Conversation } from 'jami-web-common';
import { useEffect, useState } from 'react';
-import { useParams } from 'react-router';
-import authManager from '../AuthManager';
//import Sound from 'react-sound';
import ConversationList from '../components/ConversationList';
import ConversationView from '../components/ConversationView';
import Header from '../components/Header';
import LoadingPage from '../components/Loading';
import NewContactForm from '../components/NewContactForm';
+import { useAuthContext } from '../contexts/AuthProvider';
import { useAppSelector } from '../redux/hooks';
+import { apiUrl } from '../utils/constants';
+import { useUrlParams } from '../utils/hooks';
import AddContactPage from './AddContactPage';
+import { MessengerRouteParams } from './JamiMessenger';
-type MessengerProps = {
- accountId?: string;
- conversationId?: string;
- contactId?: string;
-};
-
-const Messenger = (props: MessengerProps) => {
+const Messenger = () => {
const { refresh } = useAppSelector((state) => state.userInfo);
+ const { token, account } = useAuthContext();
const [conversations, setConversations] = useState<Conversation[] | undefined>(undefined);
const [searchQuery, setSearchQuery] = useState('');
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;
+ const {
+ urlParams: { conversationId, contactId },
+ } = useUrlParams<MessengerRouteParams>();
- if (accountId == null) {
- throw new Error('Missing accountId');
- }
+ const accountId = account.getId();
useEffect(() => {
console.log('REFRESH CONVERSATIONS FROM MESSENGER');
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: Conversation[]) => {
console.log(result);
setConversations(Object.values(result).map((c) => Conversation.from(accountId, c)));
});
// return () => controller.abort()
- }, [accountId, refresh]);
+ }, [token, accountId, refresh]);
useEffect(() => {
if (!searchQuery) return;
const controller = new AbortController();
- authManager
- .fetch(`/api/accounts/${accountId}/ns/name/${searchQuery}`, { signal: controller.signal })
+ fetch(new URL(`/ns/username/${searchQuery}`, apiUrl), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ signal: controller.signal,
+ })
.then((response) => {
if (response.status === 200) {
return response.json();
@@ -80,14 +82,14 @@
.then((response) => {
console.log(response);
const contact = new Contact(response.address);
- contact.setRegisteredName(response.name);
+ contact.setRegisteredName(response.username);
setSearchResults(contact ? Conversation.fromSingleContact(accountId, contact) : undefined);
})
.catch(() => {
setSearchResults(undefined);
});
// return () => controller.abort() // crash on React18
- }, [accountId, searchQuery]);
+ }, [accountId, searchQuery, token]);
console.log('Messenger render');
return (
@@ -95,7 +97,7 @@
<Stack flexGrow={0} flexShrink={0} overflow="auto">
<Header />
<NewContactForm onChange={setSearchQuery} />
- {contactId && <AddContactPage accountId={accountId} contactId={contactId} />}
+ {contactId && <AddContactPage contactId={contactId} />}
{conversations ? (
<ConversationList search={searchResult} conversations={conversations} accountId={accountId} />
) : (
@@ -104,9 +106,7 @@
</div>
)}
</Stack>
- <Stack flexGrow={1}>
- {conversationId && <ConversationView accountId={accountId} conversationId={conversationId} />}
- </Stack>
+ <Stack flexGrow={1}>{conversationId && <ConversationView conversationId={conversationId} />}</Stack>
</Stack>
);
};
diff --git a/client/src/pages/ServerConfiguration.tsx b/client/src/pages/ServerConfiguration.tsx
deleted file mode 100644
index 56d06a8..0000000
--- a/client/src/pages/ServerConfiguration.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2022 Savoir-faire Linux Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
- */
-import CircularProgress from '@mui/material/CircularProgress';
-import Container from '@mui/material/Container';
-import { Account } from 'jami-web-common';
-import { useEffect, useState } from 'react';
-import { useParams } from 'react-router-dom';
-
-import authManager from '../AuthManager';
-import AccountPreferences from '../components/AccountPreferences';
-import Header from '../components/Header';
-
-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();
- authManager
- .fetch(`/api/serverConfig`, { signal: controller.signal })
- .then((res) => res.json())
- .then((result) => {
- console.log(result);
- setAccount(Account.from(result));
- })
- .catch((e) => console.log(e));
- // return () => controller.abort() // crash on React18
- }, [accountId]);
-
- return (
- <Container maxWidth="sm" className="app">
- <Header />
- {account != null ? <AccountPreferences account={account} /> : <CircularProgress />}
- </Container>
- );
-};
-
-export default ServerOverview;
diff --git a/client/src/pages/ServerSetup.tsx b/client/src/pages/ServerSetup.tsx
index e045751..e6ff0aa 100644
--- a/client/src/pages/ServerSetup.tsx
+++ b/client/src/pages/ServerSetup.tsx
@@ -19,8 +19,6 @@
import { Box, Card, CardContent, Container, Fab, Input, Typography } from '@mui/material';
import { FormEvent, useState } from 'react';
-import authManager from '../AuthManager';
-
export default function ServerSetup() {
const [password, setPassword] = useState('');
const [passwordRepeat, setPasswordRepeat] = useState('');
@@ -32,7 +30,8 @@
e.preventDefault();
setLoading(true);
if (!isValid()) return;
- authManager.setup(password);
+ // TODO: Migrate to new server
+ // authManager.setup(password);
};
return (
diff --git a/client/src/services/Account.ts b/client/src/services/Account.ts
deleted file mode 100644
index a955883..0000000
--- a/client/src/services/Account.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2022 Savoir-faire Linux Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
- */
-import { useQuery } from '@tanstack/react-query';
-import axios from 'axios';
-
-export const useAccountQuery = (accountId: string) => {
- return useQuery(['accounts', accountId], () => fetchAccount(accountId), {
- enabled: !!accountId,
- });
-};
-
-const fetchAccount = (accountId: string) => axios.get(`/api/accounts/${accountId}`).then((result) => result.data);
diff --git a/client/src/services/Conversation.ts b/client/src/services/Conversation.ts
index f89d5e9..d742730 100644
--- a/client/src/services/Conversation.ts
+++ b/client/src/services/Conversation.ts
@@ -18,30 +18,57 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
-export const useConversationQuery = (accountId: string, conversationId: string) => {
- return useQuery(['conversation', accountId, conversationId], () => fetchConversation(accountId, conversationId), {
- enabled: !!(accountId && conversationId),
+import { useAuthContext } from '../contexts/AuthProvider';
+import { apiUrl } from '../utils/constants';
+
+export const useConversationQuery = (conversationId: string) => {
+ const { token } = useAuthContext();
+ return useQuery(['conversation', conversationId], () => fetchConversation(conversationId, token), {
+ enabled: !!conversationId,
});
};
-export const useMessagesQuery = (accountId: string, conversationId: string) => {
- return useQuery(['messages', accountId, conversationId], () => fetchMessages(accountId, conversationId), {
- enabled: !!(accountId && conversationId),
+export const useMessagesQuery = (conversationId: string) => {
+ const { token } = useAuthContext();
+ return useQuery(['messages', conversationId], () => fetchMessages(conversationId, token), {
+ enabled: !!conversationId,
});
};
-export const useSendMessageMutation = (accountId: string, conversationId: string) => {
+export const useSendMessageMutation = (conversationId: string) => {
+ const { token } = useAuthContext();
const queryClient = useQueryClient();
return useMutation(
- (message: string) => axios.post(`/api/accounts/${accountId}/conversations/${conversationId}`, { message }),
+ (message: string) =>
+ axios.post(
+ new URL(`/conversations/${conversationId}/messages`, apiUrl).toString(),
+ { message },
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ ),
{
- onSuccess: () => queryClient.invalidateQueries(['messages', accountId, conversationId]),
+ onSuccess: () => queryClient.invalidateQueries(['messages', conversationId]),
}
);
};
-const fetchConversation = (accountId: string, conversationId: string) =>
- axios.get(`/api/accounts/${accountId}/conversations/${conversationId}`).then((result) => result.data);
+const fetchConversation = (conversationId: string, token: string) =>
+ axios
+ .get(new URL(`/conversations/${conversationId}`, apiUrl).toString(), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
+ .then((result) => result.data);
-const fetchMessages = (accountId: string, conversationId: string) =>
- axios.get(`/api/accounts/${accountId}/conversations/${conversationId}/messages`).then((result) => result.data);
+const fetchMessages = (conversationId: string, token: string) =>
+ axios
+ .get(new URL(`/conversations/${conversationId}/messages`, apiUrl).toString(), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
+ .then((result) => result.data);
diff --git a/client/vite.config.ts b/client/vite.config.ts
index f62be3c..55090d8 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -23,12 +23,6 @@
server: {
host: '0.0.0.0',
port: 3000,
- proxy: {
- '^/(api)|^/(auth)|^/(setup)': {
- target: 'http://localhost:3001',
- secure: false,
- },
- },
},
preview: {
port: 8080,