implement list of contacts and CRUD contacts API route

Change-Id: I749d25c8b1ef6205e5d888bb517c43233bc9215b
diff --git a/JamiDaemon.js b/JamiDaemon.js
index f25f743..7f92f38 100755
--- a/JamiDaemon.js
+++ b/JamiDaemon.js
@@ -251,6 +251,11 @@
                 JamiDaemon.mapToJs(this.dring.getAccountDetails(accountId)),
                 JamiDaemon.mapToJs(this.dring.getVolatileAccountDetails(accountId))
             )
+
+            account.contacts = JamiDaemon.vectMapToJs(
+              this.dring.getContacts(accountId)
+            );
+
             JamiDaemon.vectToJs(this.dring.getConversations(accountId)).forEach(conversationId => {
                 const members = JamiDaemon.vectMapToJs(this.dring.getConversationMembers(accountId, conversationId))
                 members.forEach(member => {
@@ -399,6 +404,20 @@
         return details
     }
 
+    removeContact(accountId, contactId){
+        //bool ban false
+        this.dring.removeContact(accountId, contactId, false);
+    }
+
+    blockContact(accountId, contactId){
+        //bool ban true
+        this.dring.removeContact(accountId, contactId, true);
+    }
+
+    getContactDetails(accountId, contactId){
+        return JamiDaemon.mapToJs(this.dring.getContactDetails(accountId, contactId))
+    }
+
     getDefaultModerators(accountId) {
         const account = this.getAccount(accountId)
         if (!account) {
diff --git a/client/redux/appSlice.ts b/client/redux/appSlice.ts
index e430aad..8ad47cd 100644
--- a/client/redux/appSlice.ts
+++ b/client/redux/appSlice.ts
@@ -1,14 +1,17 @@
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import Account from "../../model/Account";
 import type { RootState } from "./store";
 
 // Define a type for the slice state
 interface appState {
   accountId: string;
+  accountObject: Account;
 }
 
 // Define the initial state using that type
 const initialState: appState = {
   accountId: "",
+  accountObject: null,
 };
 
 export const appSlice = createSlice({
@@ -19,10 +22,13 @@
     setAccountId: (state, action: PayloadAction<string>) => {
       state.accountId = action.payload;
     },
+    setAccountObject: (state, action: PayloadAction<Account>) => {
+      state.accountObject = action.payload ;
+    },
   },
 });
 
-export const { setAccountId } = appSlice.actions;
+export const { setAccountId, setAccountObject } = appSlice.actions;
 
 // Other code such as selectors can use the imported `RootState` type
 // export const selectCount = (state: RootState) => state.app.value;
diff --git a/client/src/components/ContactList.js b/client/src/components/ContactList.js
index 73950a2..b15238f 100644
--- a/client/src/components/ContactList.js
+++ b/client/src/components/ContactList.js
@@ -1,35 +1,163 @@
 import List from "@mui/material/List";
 import authManager from "../AuthManager";
+import ConversationAvatar from "./ConversationAvatar";
+import Modal from 'react-modal';
 import React, { useState, useEffect } from "react";
+import { Person } from "@mui/icons-material";
 import { useAppDispatch, useAppSelector } from "../../redux/hooks";
+import { ListItem, ListItemAvatar, ListItemText } from "@mui/material";
 
+const customStyles = {
+  content: {
+    top: "50%",
+    left: "50%",
+    right: "auto",
+    bottom: "auto",
+    marginRight: "-50%",
+    transform: "translate(-50%, -50%)",
+  },
+};
 
 export default function ContactList() {
-  const { accountId } = useAppSelector((state) => state.app);
+  const { accountId, accountObject } = useAppSelector((state) => state.app);
   const dispatch = useAppDispatch();
 
   const [contacts, setContacts] = useState([]);
+  const [currentContact, setCurrentContact] = useState({});
+
+  const [modalIsOpen, setIsOpen] = useState(false);
+  const [modalDetailsIsOpen, setModalDetailsIsOpen] = useState(false);
+  const [modalDeleteIsOpen, setModalDeleteIsOpen] = useState(false);
+
+  const openModal = () => setIsOpen(true);
+  const openModalDetails = () => setModalDetailsIsOpen(true);
+  const openModalDelete = () => setModalDeleteIsOpen(true);
+
+  const closeModal = () => setIsOpen(false);
+
+  const closeModalDetails = () => setModalDetailsIsOpen(false);
+  const closeModalDelete = () => setModalDeleteIsOpen(false);
+
+  const getContactDetails = () => {
+    const controller = new AbortController();
+    authManager
+      .fetch(`/api/accounts/${accountId}/contacts/${currentContact.id}`, {
+        signal: controller.signal,
+      })
+      .then((res) => res.json())
+      .then((result) => {
+        // setContacts(result);
+        console.log("CONTACT LIST - DETAILS: ", result);
+      })
+      .catch((e) => console.log("ERROR 2", e));
+  };
+
+  const removeContact = () => {};
+
+  const blockContact = () => {};
+
 
   useEffect(() => {
     const controller = new AbortController();
     authManager
-      .fetch(`/api/accounts/${accountId}/contacts/`, {
-        method: "GET",
-        header: { "Content-Type": "application/json" },
-        // signal: controller.signal,
+      .fetch(`/api/accounts/${accountId}/conversations`, {
+        signal: controller.signal,
       })
-      .then((res) => {
-        res.json();
-      })
+      .then((res) => res.json())
       .then((result) => {
-        console.log(result);
+        setContacts(result);
       });
     return () => controller.abort();
   }, []);
 
   return (
     <div className="rooms-list">
-      <List></List>
+      <Modal
+        isOpen={modalIsOpen}
+        //   onAfterOpen={afterOpenModal}
+        onRequestClose={closeModal}
+        style={customStyles}
+        contentLabel="Example Modal"
+      >
+        {/* <h2 ref={(_subtitle) => (subtitle = _subtitle)}>Hello</h2> */}
+        <button onClick={closeModal}>close</button>
+
+        {/* <div>
+            <Person /> Démarrer appel vidéo
+          </div>
+          <br />
+
+          <div>
+            <Person /> Démarrer appel audio
+          </div> */}
+        <br />
+
+        <div
+          onClick={() => {
+            console.log("open dialog Supprimer: ");
+            closeModal();
+            openModalDelete();
+          }}
+        >
+          <Person /> Supprimer contact
+        </div>
+        <br />
+
+        <div
+          onClick={() => {
+            console.log("open dialog BLOCK: ");
+            closeModal();
+            openModalDelete();
+          }}
+        >
+          <Person /> Bloquer le contact
+        </div>
+        <br />
+
+        <div
+          onClick={() => {
+            console.log("open details contact for: ");
+            closeModal();
+            openModalDetails();
+            getContactDetails();
+          }}
+        >
+          <Person /> Détails du contact
+        </div>
+      </Modal>
+      <Modal
+        isOpen={modalDeleteIsOpen}
+        //   onAfterOpen={afterOpenModalDetails}
+        onRequestClose={closeModalDelete}
+        style={customStyles}
+        contentLabel="Merci de confirmer"
+      >
+        Voulez vous vraiment supprimer ce contact?
+        <button onClick={closeModalDelete}>Bloquer</button>
+        <button onClick={closeModalDelete}>Annuler</button>
+      </Modal>
+
+      <List>
+        {contacts.map((contact) => (
+          <ListItem
+            button
+            alignItems="flex-start"
+            // selected={isSelected}
+            onClick={() => {
+              setCurrentContact(contact);
+              openModal();
+            }}
+          >
+            <ListItemAvatar>
+              <ConversationAvatar
+              // displayName={conversation.getDisplayNameNoFallback()}
+              // displayName={`${contact.id}`}
+              />
+            </ListItemAvatar>
+            <ListItemText primary={contact.id} secondary={contact.id} />
+          </ListItem>
+        ))}
+      </List>
     </div>
   );
 }
diff --git a/client/src/components/ConversationListItem.js b/client/src/components/ConversationListItem.js
index 26736e9..b45f562 100644
--- a/client/src/components/ConversationListItem.js
+++ b/client/src/components/ConversationListItem.js
@@ -75,7 +75,6 @@
       })
       .then((result) => {
         console.log("YYY 1", result);
-        // setState({ loaded: true, account: Account.from(result) });
       })
       .catch((e) => console.log("YYY 2", e));
     //       return () => controller.abort()
diff --git a/client/src/components/ListItemLink.js b/client/src/components/ListItemLink.js
index 915a24b..a0ebc3b 100644
--- a/client/src/components/ListItemLink.js
+++ b/client/src/components/ListItemLink.js
@@ -6,10 +6,12 @@
 import { Link as RouterLink } from 'react-router-dom';
 
 function ListItemLink(props) {
-  const { icon, primary, secondary, to } = props
+  const { icon, primary, secondary, to, account } = props
 
   const renderLink = useMemo(
-    () => forwardRef((itemProps, ref) => <RouterLink to={to} ref={ref} {...itemProps} />),
+    () => forwardRef((itemProps, ref) => {
+    console.log("LIST ITEM LINK: ", account, itemProps)
+    return <RouterLink to={to} ref={ref} {...itemProps} state={account}/>}),
     [to])
 
   return (
@@ -25,6 +27,7 @@
   primary: PropTypes.string.isRequired,
   secondary: PropTypes.string,
   to: PropTypes.string.isRequired,
+  account: PropTypes.object
 }
 
 export default ListItemLink
\ No newline at end of file
diff --git a/client/src/pages/accountSettings.jsx b/client/src/pages/accountSettings.jsx
index d555820..fa29a7d 100644
--- a/client/src/pages/accountSettings.jsx
+++ b/client/src/pages/accountSettings.jsx
@@ -6,9 +6,10 @@
 import authManager from '../AuthManager'
 import Account from '../../../model/Account'
 import { useAppSelector, useAppDispatch } from '../../redux/hooks';
-import { setAccountId } from '../../redux/appSlice';
+import { setAccountId, setAccountObject } from '../../redux/appSlice';
 
 const AccountSettings = (props) => {
+  console.log("ACCOUNT SETTINGS", props.account)
   const accountId = props.accountId || useParams().accountId
   const dispatch = useAppDispatch();
 
@@ -23,6 +24,7 @@
       .then(result => {
         let account = Account.from(result)
         account.setDevices(result.devices)
+        dispatch(setAccountObject(account))
         setState({ loaded: true, account: account})
 
       }).catch(e => console.log(e))
diff --git a/routes/jami.js b/routes/jami.js
index 7ed056e..cbe3286 100644
--- a/routes/jami.js
+++ b/routes/jami.js
@@ -6,8 +6,8 @@
     }
 
     getRouter() {
-        const router = Router({mergeParams: true})
-        //router.use(express.json());
+      const router = Router({ mergeParams: true });
+      //router.use(express.json());
 
         // Accounts
         router.get('/accounts', async (req, res) => {
@@ -53,7 +53,7 @@
         router.use('/accounts/:accountId', checkAccount, accountRouter)
 
         accountRouter.get('/', async (req, res) => {
-            console.log(`BBBBBBB Get account ${req.params.accountId}`)
+            console.log(`Get account ${req.params.accountId}`)
             const account = this.jami.getAccount(req.params.accountId)
             if (account) {
                 account.defaultModerators = this.jami.getDefaultModerators(account.getId())
@@ -68,7 +68,6 @@
           console.log(`Set account details ${req.params.accountId}`);
           const account = this.jami.getAccount(req.params.accountId);
           if (account) {
-            console.log(req.body);
             const newDetails = account.updateDetails(req.body);
             this.jami.setAccountDetails(account.getId(), newDetails);
             res.status(200).end();
@@ -77,17 +76,15 @@
 
         // Contacts
         accountRouter.get("/contacts", (req, res) => {
-          console.log(`AAAAAAAA Get account ${req.params.accountId}`);
+          console.log(`Get account ${req.params.accountId}`);
           const account = this.jami.getAccount(req.params.accountId);
           if (account) {
             let rep = account.getContacts();
-            console.log(rep);
             res.json(rep);
           } else res.status(404).end();
         });
 
         accountRouter.get("/contacts/:contactId", (req, res) => {
-          console.log("CCCCCCC JAMI.JS: ", req.params);
           console.log(`Get account details fot ${req.params.accountId}`);
           const account = this.jami.getAccount(req.params.accountId);
           const uri = req.params.uri;
@@ -97,140 +94,179 @@
           } else res.status(404).end();
         });
 
-        // Default modertors
-        accountRouter.put("/defaultModerators/:contactId", async (req, res) => {
+
+
+
+
+      // Default modertors
+      accountRouter.put("/defaultModerators/:contactId", async (req, res) => {
+        console.log(
+          `Adding default moderator ${req.params.contactId} to account ${req.params.accountId}`
+        );
+        this.jami.addDefaultModerator(
+          req.params.accountId,
+          req.params.contactId
+        );
+        res.status(200).end();
+      });
+
+      accountRouter.delete(
+        "/defaultModerators/:contactId",
+        async (req, res) => {
           console.log(
-            `Adding default moderator ${req.params.contactId} to account ${req.params.accountId}`
+            `Removing default moderator to account ${req.params.accountId}`
           );
-          this.jami.addDefaultModerator(
+          this.jami.removeDefaultModerator(
             req.params.accountId,
             req.params.contactId
           );
           res.status(200).end();
-        });
+        }
+      );
 
-        accountRouter.delete('/defaultModerators/:contactId', async (req, res) => {
-            console.log(`Removing default moderator to account ${req.params.accountId}`)
-            this.jami.removeDefaultModerator(req.params.accountId, req.params.contactId)
-            res.status(200).end()
-        })
-
-        // Conversations
-        accountRouter.get('/conversations', async (req, res, next) => {
-            console.log(`Get conversations for account ${req.params.accountId}`)
-            const account = this.jami.getAccount(req.params.accountId)
-            if (!account)
-                return res.sendStatus(404)
-            const conversations = account.getConversations()
-            res.json(await Promise.all(Object.keys(conversations).map(async conversationId => await conversations[conversationId].getObject({
-                memberFilter: member => member.contact.getUri() !== account.getUri()
-            }))))
-            //res.json(account.getConversations())
-        })
-
-        accountRouter.post('/conversations', (req, res) => {
-            console.log(`Create conversations for account, contact ${req.params.accountId}`)
-            console.log(req.body)
-            const account = this.jami.getAccount(req.params.accountId)
-            if (!account)
-                return res.sendStatus(404)
-            if (req.body.members.length === 1) {
-                const details = this.jami.addContact(req.params.accountId, req.body.members[0])
-                res.json(details)
-            } else
-                res.status(400).end()
-        })
-
-        accountRouter.post('/conversations/:conversationId', async (req, res) => {
-            console.log(`Sending message to ${req.params.conversationId} for account ${req.params.accountId}`)
-            this.jami.sendMessage(req.params.accountId, req.params.conversationId, req.body.message)
-            res.status(200).end()
-        })
-
-        accountRouter.get('/conversations/:conversationId', async (req, res) => {
-            console.log(`Get conversation ${req.params.conversationId} for account ${req.params.accountId}`)
-            const account = this.jami.getAccount(req.params.accountId)
-            if (!account)
-                return res.sendStatus(404)
-            const conversation = account.getConversation(req.params.conversationId)
-            if (!conversation)
-                res.status(404).end()
-            else {
-                res.json(await conversation.getObject({
-                    memberFilter: member => member.contact.getUri() !== account.getUri()
-                }))
-            }
-        })
-
-        accountRouter.get('/conversations/:conversationId/messages', async (req, res) => {
-            console.log(`Get messages for conversation ${req.params.conversationId} for account ${req.params.accountId}`)
-            try {
-                const messages = await this.jami.loadMessages(req.params.accountId, req.params.conversationId)
-                res.json(messages).end()
-            } catch (e) {
-                res.status(400).json({ error: e.message })
-            }
-        })
-
-        // Calls
-
-        accountRouter.get('/calls', async (req, res) => {
-            console.log(`Get calls for account ${req.params.accountId}`)
-            try {
-                const calls = await this.jami.getCalls(req.params.accountId)
-                res.json(calls).end()
-            } catch (e) {
-                res.status(400).json({ error: e.message })
-            }
-        })
-
-        accountRouter.get('/calls/:callId', async (req, res) => {
-            console.log(`Get call ${callId} for account ${req.params.accountId}`)
-            try {
-                const messages = await this.jami.getCall(req.params.accountId, req.params.callId)
-                res.json(messages).end()
-            } catch (e) {
-                res.status(400).json({ error: e.message })
-            }
-        })
-
-        // Nameserver
-        const nsRouter = Router({mergeParams: true})
-        accountRouter.use('/ns', nsRouter) // use account nameserver
-        router.use('/ns', nsRouter) // use default nameserver
-
-        nsRouter.get(['/name/:nameQuery'], (req, res, next) => {
-            console.log(`Name lookup ${req.params.nameQuery}`)
-            this.jami.lookupName(req.params.accountId || '', req.params.nameQuery)
-                .then(result => {
-                    if (result.state == 0)
-                        res.json(result)
-                    else if (result.state == 1)
-                        res.status(400).json({})
-                    else
-                        res.status(404).json({})
-                }).catch(e => {
-                    res.status(404).json({})
+      // Conversations
+      accountRouter.get("/conversations", async (req, res, next) => {
+        console.log(`Get conversations for account ${req.params.accountId}`);
+        const account = this.jami.getAccount(req.params.accountId);
+        if (!account) return res.sendStatus(404);
+        const conversations = account.getConversations();
+        res.json(
+          await Promise.all(
+            Object.keys(conversations).map(
+              async (conversationId) =>
+                await conversations[conversationId].getObject({
+                  memberFilter: (member) =>
+                    member.contact.getUri() !== account.getUri(),
                 })
-        })
+            )
+          )
+        );
+        //res.json(account.getConversations())
+      });
 
-        nsRouter.get(['/addr/:addrQuery'], (req, res, next) => {
-            console.log(`Address lookup ${req.params.addrQuery}`)
-            this.jami.lookupAddress(req.params.accountId || '', req.params.addrQuery)
-                .then(result => {
-                    if (result.state == 0)
-                        res.json(result)
-                    else if (result.state == 1)
-                        res.status(400).json({})
-                    else
-                        res.status(404).json({})
-                }).catch(e => {
-                    res.status(404).json({})
-                })
-        })
+      accountRouter.post("/conversations", (req, res) => {
+        console.log(
+          `Create conversations for account, contact ${req.params.accountId}`
+        );
+        // console.log(req.body)
+        const account = this.jami.getAccount(req.params.accountId);
+        if (!account) return res.sendStatus(404);
+        if (req.body.members.length === 1) {
+          const details = this.jami.addContact(
+            req.params.accountId,
+            req.body.members[0]
+          );
+          res.json(details);
+        } else res.status(400).end();
+      });
 
+      accountRouter.post("/conversations/:conversationId", async (req, res) => {
+        console.log(
+          `Sending message to ${req.params.conversationId} for account ${req.params.accountId}`
+        );
+        this.jami.sendMessage(
+          req.params.accountId,
+          req.params.conversationId,
+          req.body.message
+        );
+        res.status(200).end();
+      });
 
-        return router
+      accountRouter.get("/conversations/:conversationId", async (req, res) => {
+        console.log(
+          `Get conversation ${req.params.conversationId} for account ${req.params.accountId}`
+        );
+        const account = this.jami.getAccount(req.params.accountId);
+        if (!account) return res.sendStatus(404);
+        const conversation = account.getConversation(req.params.conversationId);
+        if (!conversation) res.status(404).end();
+        else {
+          res.json(
+            await conversation.getObject({
+              memberFilter: (member) =>
+                member.contact.getUri() !== account.getUri(),
+            })
+          );
+        }
+      });
+
+      accountRouter.get(
+        "/conversations/:conversationId/messages",
+        async (req, res) => {
+          console.log(
+            `Get messages for conversation ${req.params.conversationId} for account ${req.params.accountId}`
+          );
+          try {
+            const messages = await this.jami.loadMessages(
+              req.params.accountId,
+              req.params.conversationId
+            );
+            res.json(messages).end();
+          } catch (e) {
+            res.status(400).json({ error: e.message });
+          }
+        }
+      );
+
+      // Calls
+
+      accountRouter.get("/calls", async (req, res) => {
+        console.log(`Get calls for account ${req.params.accountId}`);
+        try {
+          const calls = await this.jami.getCalls(req.params.accountId);
+          res.json(calls).end();
+        } catch (e) {
+          res.status(400).json({ error: e.message });
+        }
+      });
+
+      accountRouter.get("/calls/:callId", async (req, res) => {
+        console.log(`Get call ${callId} for account ${req.params.accountId}`);
+        try {
+          const messages = await this.jami.getCall(
+            req.params.accountId,
+            req.params.callId
+          );
+          res.json(messages).end();
+        } catch (e) {
+          res.status(400).json({ error: e.message });
+        }
+      });
+
+      // Nameserver
+      const nsRouter = Router({ mergeParams: true });
+      accountRouter.use("/ns", nsRouter); // use account nameserver
+      router.use("/ns", nsRouter); // use default nameserver
+
+      nsRouter.get(["/name/:nameQuery"], (req, res, next) => {
+        console.log(`Name lookup ${req.params.nameQuery}`);
+        this.jami
+          .lookupName(req.params.accountId || "", req.params.nameQuery)
+          .then((result) => {
+            if (result.state == 0) res.json(result);
+            else if (result.state == 1) res.status(400).json({});
+            else res.status(404).json({});
+          })
+          .catch((e) => {
+            res.status(404).json({});
+          });
+      });
+
+      nsRouter.get(["/addr/:addrQuery"], (req, res, next) => {
+        console.log(`Address lookup ${req.params.addrQuery}`);
+        this.jami
+          .lookupAddress(req.params.accountId || "", req.params.addrQuery)
+          .then((result) => {
+            if (result.state == 0) res.json(result);
+            else if (result.state == 1) res.status(400).json({});
+            else res.status(404).json({});
+          })
+          .catch((e) => {
+            res.status(404).json({});
+          });
+      });
+
+      return router;
     }
 }