Create new interfaces for objects transmitted using the REST API

Changes:
- Create new IContact, IAccount, and IConversation interfaces in common/
    - These interfaces represent the serialized versions of the models which are transferred
    - The client models are classes which implement these interfaces
- Create new LookupResult interface for nameserver lookup results
- Create new IConversationMember interface for conversation members
    - The client interface ConversationMember extends this interface to have a Contact field rather than IContact
- Create new ConversationInfos interface for conversation infos
- Create new ContactDetails interface for contact details (used by contacts routes)
- Move request and response body interfaces into common/
- Merge AccountConfig into AccountDetails interface
- Create interfaces for server-only objects:
    - ConversationMemberInfos
    - ConversationRequestMetadata
- Ensure interfaces in jami-signal-interfaces.ts do not contain fields with JamiSwig types
- Rename models/ filenames to camelCase as they are not components
- Rewrite client models to have proper TypeScript accessors and remove unused getters
- Rewrite how client models are initialized from the serialized interface using .fromInterface static methods
- Make client models implement the interfaces in common/ for consistency
- Remove unneeded _next parameter for Express.js route handlers
- Use Partial<T> for all Express.js request body types on server
- Type all Axios response body types with interfaces

GitLab: #92
Change-Id: I4b2c75ac632ec5d9bf12a874a5ba04467c76fa6d
diff --git a/client/src/components/AccountPreferences.tsx b/client/src/components/AccountPreferences.tsx
index 6788535..da926d0 100644
--- a/client/src/components/AccountPreferences.tsx
+++ b/client/src/components/AccountPreferences.tsx
@@ -39,7 +39,6 @@
 import { useState } from 'react';
 
 import { useAuthContext } from '../contexts/AuthProvider';
-import { Account } from '../models/Account';
 import ConversationAvatar from './ConversationAvatar';
 import ConversationsOverviewCard from './ConversationsOverviewCard';
 import JamiIdCard from './JamiIdCard';
@@ -60,17 +59,17 @@
   const { account, axiosInstance } = useAuthContext();
 
   const devices: string[][] = [];
-  const accountDevices = account.getDevices();
+  const accountDevices = account.devices;
   for (const i in accountDevices) devices.push([i, accountDevices[i]]);
 
   console.log(devices);
 
-  const isJamiAccount = account.getType() === Account.TYPE_JAMI;
+  const isJamiAccount = account.getType() === 'RING';
   const alias = isJamiAccount ? 'Jami account' : 'SIP account';
-  const moderators = account.getDefaultModerators();
+  const moderators = account.defaultModerators;
   const [defaultModeratorUri, setDefaultModeratorUri] = useState('');
 
-  const [details, setDetails] = useState(account.getDetails());
+  const [details, setDetails] = useState(account.details);
 
   const addModerator = async () => {
     if (defaultModeratorUri) {
@@ -87,7 +86,8 @@
     newDetails[key] = value ? 'true' : 'false';
     console.log(newDetails);
     await axiosInstance.patch('/account', newDetails);
-    setDetails({ ...account.updateDetails(newDetails) });
+    account.updateDetails(newDetails);
+    setDetails(account.details);
   };
 
   return (
@@ -219,19 +219,19 @@
                   </IconButton>
                 </ListItemSecondaryAction>
               </ListItem>
-              {!moderators || moderators.length === 0 ? (
+              {moderators.length === 0 ? (
                 <ListItem key="placeholder">
                   <ListItemText primary="No default moderator" />
                 </ListItem>
               ) : (
                 moderators.map((moderator) => (
-                  <ListItem key={moderator.getUri()}>
+                  <ListItem key={moderator.uri}>
                     <ListItemAvatar>
                       <ConversationAvatar displayName={moderator.getDisplayName()} />
                     </ListItemAvatar>
                     <ListItemText primary={moderator.getDisplayName()} />
                     <ListItemSecondaryAction>
-                      <IconButton onClick={() => removeModerator(moderator.getUri())} size="large">
+                      <IconButton onClick={() => removeModerator(moderator.uri)} size="large">
                         <DeleteRounded />
                       </IconButton>
                     </ListItemSecondaryAction>
diff --git a/client/src/components/ConversationList.tsx b/client/src/components/ConversationList.tsx
index 6b03224..08553b7 100644
--- a/client/src/components/ConversationList.tsx
+++ b/client/src/components/ConversationList.tsx
@@ -22,7 +22,7 @@
 import { useContext, useEffect } from 'react';
 
 import { MessengerContext } from '../contexts/MessengerProvider';
-import { Conversation } from '../models/Conversation';
+import { Conversation } from '../models/conversation';
 import { useAppSelector } from '../redux/hooks';
 import ConversationListItem from './ConversationListItem';
 
@@ -48,7 +48,7 @@
           </div>
         )}
         {conversations.map((conversation) => (
-          <ConversationListItem key={conversation.getId()} conversation={conversation} />
+          <ConversationListItem key={conversation.id} conversation={conversation} />
         ))}
         {conversations.length === 0 && (
           <div className="list-placeholder">
diff --git a/client/src/components/ConversationListItem.tsx b/client/src/components/ConversationListItem.tsx
index 41cfbe4..d94733d 100644
--- a/client/src/components/ConversationListItem.tsx
+++ b/client/src/components/ConversationListItem.tsx
@@ -16,6 +16,7 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { Box, ListItem, ListItemAvatar, ListItemText } from '@mui/material';
+import { ContactDetails } from 'jami-web-common';
 import { QRCodeCanvas } from 'qrcode.react';
 import { useCallback, useContext, useMemo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -25,7 +26,7 @@
 import { useConversationContext } from '../contexts/ConversationProvider';
 import { MessengerContext } from '../contexts/MessengerProvider';
 import { useStartCall } from '../hooks/useStartCall';
-import { Conversation } from '../models/Conversation';
+import { Conversation } from '../models/conversation';
 import { setRefreshFromSlice } from '../redux/appSlice';
 import { useAppDispatch } from '../redux/hooks';
 import ContextMenu, { ContextMenuHandler, useContextMenuHandler } from './ContextMenu';
@@ -56,10 +57,10 @@
   const isSelected = conversation.getDisplayUri() === pathId;
 
   const navigate = useNavigate();
-  const userId = conversation?.getFirstMember()?.contact.getUri();
+  const userId = conversation?.getFirstMember()?.contact.uri;
 
   const onClick = useCallback(() => {
-    const newConversationId = conversation.getId();
+    const newConversationId = conversation.id;
     if (newConversationId) {
       navigate(`/conversation/${newConversationId}`);
     } else {
@@ -116,7 +117,7 @@
   const getContactDetails = useCallback(async () => {
     const controller = new AbortController();
     try {
-      const { data } = await axiosInstance.get(`/contacts/${userId}`, {
+      const { data } = await axiosInstance.get<ContactDetails>(`/contacts/${userId}`, {
         signal: controller.signal,
       });
       console.log('CONTACT LIST - DETAILS: ', data);
@@ -125,7 +126,7 @@
     }
   }, [axiosInstance, userId]);
 
-  const conversationId = conversation.getId();
+  const conversationId = conversation.id;
 
   const menuOptions: PopoverListItemData[] = useMemo(
     () => [
diff --git a/client/src/components/ConversationView.tsx b/client/src/components/ConversationView.tsx
index 075ca6e..c9a19a2 100644
--- a/client/src/components/ConversationView.tsx
+++ b/client/src/components/ConversationView.tsx
@@ -22,7 +22,7 @@
 import { useAuthContext } from '../contexts/AuthProvider';
 import { useConversationContext } from '../contexts/ConversationProvider';
 import { useStartCall } from '../hooks/useStartCall';
-import { ConversationMember } from '../models/Conversation';
+import { ConversationMember } from '../models/conversation';
 import ChatInterface from '../pages/ChatInterface';
 import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
 import { AddParticipantButton, ShowOptionsMenuButton, StartAudioCallButton, StartVideoCallButton } from './Button';
@@ -46,7 +46,7 @@
   const { conversation, conversationId } = useConversationContext();
   const { t } = useTranslation();
 
-  const members = conversation.getMembers();
+  const members = conversation.members;
   const adminTitle = conversation.infos.title as string;
 
   const title = useMemo(() => {
diff --git a/client/src/components/ConversationsOverviewCard.tsx b/client/src/components/ConversationsOverviewCard.tsx
index cdc1c79..67b4b68 100644
--- a/client/src/components/ConversationsOverviewCard.tsx
+++ b/client/src/components/ConversationsOverviewCard.tsx
@@ -16,11 +16,11 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { Card, CardActionArea, CardContent, CircularProgress, Typography } from '@mui/material';
+import { IConversation } from 'jami-web-common';
 import { useEffect, useState } from 'react';
 import { useNavigate } from 'react-router';
 
 import { useAuthContext } from '../contexts/AuthProvider';
-import { Conversation } from '../models/Conversation';
 
 export default function ConversationsOverviewCard() {
   const { axiosInstance, account } = useAuthContext();
@@ -28,12 +28,12 @@
 
   const [conversationCount, setConversationCount] = useState<number | undefined>();
 
-  const accountId = account.getId();
+  const accountId = account.id;
 
   useEffect(() => {
     const controller = new AbortController();
     axiosInstance
-      .get<Conversation[]>('/conversations', {
+      .get<IConversation[]>('/conversations', {
         signal: controller.signal,
       })
       .then(({ data }) => {
diff --git a/client/src/components/JamiIdCard.tsx b/client/src/components/JamiIdCard.tsx
index 73c038c..bfa8045 100644
--- a/client/src/components/JamiIdCard.tsx
+++ b/client/src/components/JamiIdCard.tsx
@@ -17,7 +17,7 @@
  */
 import { Box, Card, CardContent, Typography } from '@mui/material';
 
-import { Account } from '../models/Account';
+import { Account } from '../models/account';
 
 type JamiIdCardProps = {
   account: Account;
diff --git a/client/src/components/Message.tsx b/client/src/components/Message.tsx
index 669f041..8807295 100644
--- a/client/src/components/Message.tsx
+++ b/client/src/components/Message.tsx
@@ -23,8 +23,8 @@
 import { useTranslation } from 'react-i18next';
 
 import dayjs from '../dayjsInitializer';
-import { Account } from '../models/Account';
-import { Contact } from '../models/Contact';
+import { Account } from '../models/account';
+import { Contact } from '../models/contact';
 import { EmojiButton, MoreButton, ReplyMessageButton } from './Button';
 import ConversationAvatar from './ConversationAvatar';
 import PopoverList, { PopoverListItemData } from './PopoverList';
diff --git a/client/src/components/MessageList.tsx b/client/src/components/MessageList.tsx
index 538acc7..356d3e6 100644
--- a/client/src/components/MessageList.tsx
+++ b/client/src/components/MessageList.tsx
@@ -23,7 +23,7 @@
 import { Waypoint } from 'react-waypoint';
 
 import { useAuthContext } from '../contexts/AuthProvider';
-import { ConversationMember } from '../models/Conversation';
+import { ConversationMember } from '../models/conversation';
 import { MessageRow } from './Message';
 import { ArrowDownIcon } from './SvgIcon';
 
@@ -56,7 +56,7 @@
               if (isAccountMessage) {
                 author = account;
               } else {
-                const member = members.find((member) => message.author === member.contact.getUri());
+                const member = members.find((member) => message.author === member.contact.uri);
                 author = member?.contact;
               }
               if (!author) {
diff --git a/client/src/components/SendMessageForm.tsx b/client/src/components/SendMessageForm.tsx
index d1267aa..7bb7a7f 100644
--- a/client/src/components/SendMessageForm.tsx
+++ b/client/src/components/SendMessageForm.tsx
@@ -21,7 +21,7 @@
 import { useTranslation } from 'react-i18next';
 
 import { useAuthContext } from '../contexts/AuthProvider';
-import { ConversationMember } from '../models/Conversation';
+import { ConversationMember } from '../models/conversation';
 import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
 import {
   RecordVideoMessageButton,
diff --git a/client/src/components/UsernameChooser.jsx b/client/src/components/UsernameChooser.jsx
index 19b8380..b098d1d 100644
--- a/client/src/components/UsernameChooser.jsx
+++ b/client/src/components/UsernameChooser.jsx
@@ -20,7 +20,7 @@
 import axios from 'axios';
 import { useEffect, useState } from 'react';
 
-import { apiUrl } from '../utils/constants.js';
+import { apiUrl } from '../utils/constants';
 
 const isInputValid = (input) => input && input.length > 2;