load message in front

This commit is contained in:
grimhilt 2023-03-25 13:06:59 +01:00
parent 926dc60920
commit 4d4ef54bcb
12 changed files with 246 additions and 131 deletions

View File

@ -4,7 +4,7 @@ const { getMessages } = require("../db/api.js");
async function messages(body, res) { async function messages(body, res) {
const { roomId } = body; const { roomId } = body;
getMessages(roomId).then((messages) => { getMessages(roomId).then((messages) => {
res.status(statusCode.OK).json(messages.data); res.status(statusCode.OK).json(messages);
}).catch((err) => { }).catch((err) => {
console.log(err) console.log(err)
res.status(statusCode.INTERNAL_SERVER_ERROR); res.status(statusCode.INTERNAL_SERVER_ERROR);

View File

@ -12,9 +12,11 @@ async function registerMailbox(userId, pwd, xoauth, xoauth2, host, port, tls) {
async function getMailboxes() { async function getMailboxes() {
const query = ` const query = `
SELECT app_account.account_id AS id, address.email SELECT
FROM app_account INNER JOIN address app_account.account_id AS id,
WHERE address.address_id = app_account.user_id address.email
FROM app_account INNER JOIN address
WHERE address.address_id = app_account.user_id
`; `;
const values = []; const values = [];
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
@ -46,31 +48,68 @@ async function getRooms(mailboxId) {
async function getMessages(roomId) { async function getMessages(roomId) {
// todo attachements name // todo attachements name
// todo html, text, textAsHtml
// todo datetime
const query = ` const query = `
SELECT SELECT
address_field.address_id, msg.message_id AS id,
bodypart.text, GROUP_CONCAT(fromT.address_id) AS fromA,
header_field.value GROUP_CONCAT(toT.address_id) AS toA,
FROM bodypart GROUP_CONCAT(ccT.address_id) AS ccA,
INNER JOIN header_field subjectT.value AS subject,
INNER JOIN address_field content.text AS content,
INNER JOIN field_name message.idate AS date
FROM app_room_message msg
LEFT JOIN (
SELECT address_field.address_id, address_field.message_id
FROM address_field
INNER JOIN field_name
WHERE WHERE
( field_name.field_id = address_field.field_id AND
header_field.field_id = field_name.field_id OR field_name.field_name = 'from'
address_field.field_id = field_name.field_id ) fromT ON msg.message_id = fromT.message_id
) AND LEFT JOIN (
( SELECT address_field.address_id, address_field.message_id
field_name.field_name = 'html' OR FROM address_field
field_name.field_name = 'text' OR INNER JOIN field_name
field_name.field_name = 'textAsHtml' OR WHERE
field_name.field_name = 'to' OR field_name.field_id = address_field.field_id AND
field_name.field_name = 'cc' OR field_name.field_name = 'to'
field_name.field_name = 'subject' ) toT ON msg.message_id = toT.message_id
) LEFT JOIN (
`; SELECT address_field.address_id, address_field.message_id
// todo roomID FROM address_field
const values = []; INNER JOIN field_name
WHERE
field_name.field_id = address_field.field_id AND
field_name.field_name = 'cc'
) ccT ON msg.message_id = ccT.message_id
LEFT JOIN (
SELECT header_field.message_id, header_field.value
FROM header_field
INNER JOIN field_name
WHERE
field_name.field_id = header_field.field_id AND
field_name.field_name = 'subject'
) subjectT ON msg.message_id = subjectT.message_id
LEFT JOIN (
SELECT bodypart.text, header_field.message_id FROM bodypart
INNER JOIN header_field
INNER JOIN field_name
WHERE
field_name.field_id = header_field.field_id AND
field_name.field_name = 'html' AND
bodypart.bodypart_id = header_field.bodypart_id
) content ON msg.message_id = content.message_id
INNER JOIN message ON message.message_id = msg.message_id
WHERE msg.room_id = ?
GROUP BY msg.message_id;
`;
const values = [roomId];
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
} }

View File

@ -14,10 +14,10 @@ async function registerMessage(timestamp, rfc822size, messageId) {
function registerMailbox_message(mailboxId, uid, messageId, modseq, seen, deleted) { function registerMailbox_message(mailboxId, uid, messageId, modseq, seen, deleted) {
const query = ` const query = `
INSERT IGNORE INTO mailbox_message INSERT IGNORE INTO mailbox_message
(mailbox_id, uid, message_id, modseq, seen, deleted) VALUES (1, 19, 10, '12450', 0, 0) (mailbox_id, uid, message_id, modseq, seen, deleted) VALUES (?, ?, ?, ?, ?, ?)
`; `;
// todo
const values = [mailboxId, uid, messageId, modseq, seen, deleted]; const values = [mailboxId, uid, messageId, modseq, seen, deleted];
console.log(values)
execQuery(query, values); execQuery(query, values);
} }

View File

@ -9,7 +9,7 @@ async function createRoom(roomName, ownerId, messageId) {
} }
async function registerMessageInRoom(messageId, roomId, isSeen) { async function registerMessageInRoom(messageId, roomId, isSeen) {
const query = `INSERT INTO app_space_message (message_id, room_id) VALUES (?, ?)`; const query = `INSERT IGNORE INTO app_room_message (message_id, room_id) VALUES (?, ?)`;
const values = [messageId, roomId]; const values = [messageId, roomId];
await execQueryAsync(query, values); await execQueryAsync(query, values);
@ -68,8 +68,8 @@ async function isRoomGroup(roomId) {
}); });
} }
async function findSpacesFromMessage(messageId) { async function findRoomsFromMessage(messageId) {
const query = `SELECT room_id, thread_id FROM app_space_message WHERE message_id = '${messageId}'`; const query = `SELECT room_id FROM app_room_message WHERE message_id = '${messageId}'`;
return await execQueryAsync(query); return await execQueryAsync(query);
} }
@ -85,5 +85,5 @@ module.exports = {
createThread, createThread,
registerMessageInThread, registerMessageInThread,
isRoomGroup, isRoomGroup,
findSpacesFromMessage, findRoomsFromMessage,
}; };

View File

@ -131,25 +131,24 @@ CREATE TABLE app_room (
-- 12 -- 12
CREATE TABLE app_thread ( CREATE TABLE app_thread (
thread_id INT AUTO_INCREMENT,
room_id INT NOT NULL, room_id INT NOT NULL,
thread_name VARCHAR(255) NOT NULL, parent_room_id INT,
notSeen INT NOT NULL DEFAULT 0, root_room_id INT,
lastUpdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
isDm BOOLEAN NOT NULL DEFAULT false, isDm BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (thread_id), PRIMARY KEY (room_id),
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE UNIQUE KEY (room_id, parent_room_id, root_room_id),
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE,
FOREIGN KEY (parent_room_id) REFERENCES app_room(room_id) ON DELETE SET NULL,
FOREIGN KEY (root_room_id) REFERENCES app_room(room_id) ON DELETE SET NULL
); );
-- 13 -- 13
CREATE TABLE app_space_message ( CREATE TABLE app_room_message (
message_id INT NOT NULL, message_id INT NOT NULL,
room_id INT, room_id INT,
thread_id INT, UNIQUE KEY (message_id, room_id),
UNIQUE KEY (message_id, room_id, thread_id),
FOREIGN KEY (message_id) REFERENCES message(message_id) ON DELETE CASCADE, FOREIGN KEY (message_id) REFERENCES message(message_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE SET NULL, FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE SET NULL
FOREIGN KEY (thread_id) REFERENCES app_thread(thread_id) ON DELETE SET NULL
); );
-- 14 -- 14
@ -159,11 +158,3 @@ CREATE TABLE app_room_member (
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE, FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE,
FOREIGN KEY (member_id) REFERENCES address(address_id) FOREIGN KEY (member_id) REFERENCES address(address_id)
); );
-- 14
CREATE TABLE app_thread_member (
thread_id INT NOT NULL,
member_id INT NOT NULL,
FOREIGN KEY (thread_id) REFERENCES app_thread(thread_id) ON DELETE CASCADE,
FOREIGN KEY (member_id) REFERENCES address(address_id)
);

View File

@ -28,6 +28,7 @@ if (shouldReset) {
execQuery("SET FOREIGN_KEY_CHECKS=0"); execQuery("SET FOREIGN_KEY_CHECKS=0");
results.map((table) => { results.map((table) => {
execQuery("DELETE FROM " + table.table_name); execQuery("DELETE FROM " + table.table_name);
// execQuery("DROP TABLE " + table.table_name);
}); });
}); });
return; return;

View File

@ -19,6 +19,7 @@ const validate_mailbox = ajv.compile(schema_mailbox);
*/ */
router.get("/mailboxes", (req, res) => { router.get("/mailboxes", (req, res) => {
getMailboxes().then((data) => { getMailboxes().then((data) => {
data[0].id = 1; // todo debug
res.status(statusCodes.OK).json(data) res.status(statusCodes.OK).json(data)
}); });
}); });
@ -49,9 +50,10 @@ router.get("/:roomId/messages", async (req, res) => {
* Register a new mailbox inside the app * Register a new mailbox inside the app
*/ */
router.post("/mailbox", async (req, res) => { router.post("/mailbox", async (req, res) => {
console.log(req.body)
const valid = validate_mailbox(req.body); const valid = validate_mailbox(req.body);
if (!valid) { if (!valid) {
res.status(statusCodes.NOT_ACCEPTABLE).send(validate_mailbox.errors) res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validate_mailbox.errors });
} else { } else {
await addMailbox(req.body, res); await addMailbox(req.body, res);
} }

View File

@ -0,0 +1,13 @@
export default class Room {
constructor(id, user, userId, roomName, mailboxId) {
this.id = id;
this.user = user;
this.userId = userId;
this.roomName = roomName;
this.mailboxId = mailboxId;
this.messages = [];
this.messagesFetched = false;
this.threads = [];
}
}

View File

@ -1,42 +1,19 @@
import API from "@/services/imapAPI"; import API from "@/services/imapAPI";
import { createStore } from "vuex"; import { createStore } from "vuex";
import Room from "./models/Room";
const store = createStore({ const store = createStore({
state() { state() {
return { return {
rooms: [ rooms: [
{ new Room(1, "clemence", 0, "Est ex adipisicing non ipsum voluptate duis enim adipisicing labore.", 1),
id: 0, new Room(2, "laurance", 0, "Est ex adipisicing non ipsum voluptate duis enim adipisicing labore.", 2),
user: "clemnce", new Room(3, "mathilde", 0, "Est ex adipisicing non ipsum voluptate duis enim adipisicing labore.", 1),
userId: 0,
roomName: "Lorem magna minim cillum labore ex eiusmod proident excepteur sint irure ipsum.",
mailboxId: 1,
},
{
id: 1,
user: "juliette",
roomName: "Lorem magna minim cillum labore ex eiusmod proident excepteur sint irure ipsum.",
mailboxId: 1,
},
{
id: 2,
user: "jean",
roomName: "Lorem magna minim cillum labore ex eiusmod proident excepteur sint irure ipsum.",
mailboxId: 2,
},
{
id: 3,
user: "luc",
roomName: "Lorem magna minim cillum labore ex eiusmod proident excepteur sint irure ipsum.",
mailboxId: 2,
},
],
messages: [
], ],
messages: [],
mailboxes: [], mailboxes: [],
activeMailbox: 0, activeMailbox: 0,
activeRoom: 0 activeRoom: 0,
}; };
}, },
mutations: { mutations: {
@ -48,16 +25,18 @@ const store = createStore({
const mailbox = state.mailboxes.find((mailbox) => mailbox.id == payload); const mailbox = state.mailboxes.find((mailbox) => mailbox.id == payload);
// todo fetched mailbox all // todo fetched mailbox all
if (mailbox?.fetched == false) { if (mailbox?.fetched == false) {
API.getRooms(payload).then((res) => { API.getRooms(payload)
// todo add if not exist .then((res) => {
mailbox.fetched = true; // todo add if not exist
res.data.forEach((room) => { console.log(res.data)
room.fetched = false; mailbox.fetched = true;
state.rooms.push(room); res.data.forEach((room) => {
state.rooms.push(new Room(room.id, room.user, room.userId, room.roomName, room.mailboxId));
});
})
.catch((err) => {
console.log(err);
}); });
}).catch((err) => {
console.log(err)
});
} }
}, },
setActiveRoom(state, payload) { setActiveRoom(state, payload) {
@ -66,24 +45,35 @@ const store = createStore({
// fetch messages for this room if not already fetched // fetch messages for this room if not already fetched
const room = state.rooms.find((room) => room.id == payload); const room = state.rooms.find((room) => room.id == payload);
if (!room || room?.fetched == false) { if (!room || room?.fetched == false) {
console.log("add messages") console.log("add messages");
API.getMessages(payload).then((res) => { API.getMessages(payload)
// todo add if not exist .then((res) => {
room.fetched = true; // todo add if not exist
res.data.forEach((msg) => { room.fetched = true;
state.messages.push(msg); res.data.forEach((msg) => {
state.messages.push(msg);
});
})
.catch((err) => {
console.log(err);
}); });
}).catch((err) => {
console.log(err)
});
} }
}, },
addMailboxes(state, payload) { addMailboxes(state, payload) {
console.log(payload)
payload.forEach((mailbox) => { payload.forEach((mailbox) => {
mailbox.fetched = false; mailbox.fetched = false;
state.mailboxes.push(mailbox); state.mailboxes.push(mailbox);
}); });
}, },
addMessages(state, payload) {
const room = state.rooms.find((room) => room.id == payload.roomId);
payload.messages.forEach((message) => {
room.messages.push(message);
});
room.messagesFetched = true;
console.log(room.messages)
},
}, },
getters: { getters: {
rooms: (state) => () => { rooms: (state) => () => {
@ -91,16 +81,16 @@ const store = createStore({
return state.rooms.filter((room) => room.mailboxId == state.activeMailbox); return state.rooms.filter((room) => room.mailboxId == state.activeMailbox);
}, },
messages: (state) => (roomId) => { messages: (state) => (roomId) => {
const room = state.rooms.find((room) => room.id === roomId); const room = state.rooms.find((room) => room.id == roomId);
if (room?.fetched === false) { if (!room.messagesFetched) {
console.log("ok") console.log("fetched Messages")
store.dispatch("fetchMessages", { roomId: room.id });
} }
return [1, 2]; return room.messages;
} },
}, },
actions: { actions: {
async fetchMailboxes(context) { fetchMailboxes: async (context) => {
console.log("add mailboxes");
API.getMailboxes() API.getMailboxes()
.then((res) => { .then((res) => {
context.commit("addMailboxes", res.data); context.commit("addMailboxes", res.data);
@ -109,8 +99,15 @@ const store = createStore({
console.log(err); console.log(err);
}); });
}, },
async fetchMessages(context) { fetchMessages: async (context, data) => {
console.log(context) API.getMessages(data.roomId)
.then((res) => {
console.log(res.data)
context.commit("addMessages", { messages: res.data, roomId: data.roomId });
})
.catch((err) => {
console.log(err);
});
}, },
}, },
}); });

View File

@ -19,7 +19,7 @@ export default {
<style scoped> <style scoped>
#main { #main {
background-color: blue; border-bottom: 1px solid #505050;
width: 100%; width: 100%;
height: 51px; height: 51px;
} }

View File

@ -1,30 +1,72 @@
<script setup> <script setup>
const data = { import { defineProps } from "vue";
html: "<div dir=\"ltr\">new content<br><div><br><div class=\"gmail_quote\"><div dir=\"ltr\" class=\"gmail_attr\">---------- Forwarded message ---------<br>De : <b class=\"gmail_sendername\" dir=\"auto\">Ulysse Carlier</b> <span dir=\"auto\">&lt;<a href=\"mailto:carlierulysse@gmail.com\">carlierulysse@gmail.com</a>&gt;</span><br>Date: ven. 10 mars 2023 à 14:52<br>Subject: message to transfer<br>To: Hugueprime M &lt;<a href=\"mailto:hugueprime@gmail.com\">hugueprime@gmail.com</a>&gt;<br></div><br><br><div dir=\"ltr\">content to transfer<br></div>\n</div></div></div>\n",
text: "new content\n\n---------- Forwarded message ---------\nDe : Ulysse Carlier <carlierulysse@gmail.com>\nDate: ven. 10 mars 2023 à 14:52\nSubject: message to transfer\nTo: Hugueprime M <hugueprime@gmail.com>\n\n\ncontent to transfer\n", const props = defineProps({ data: Object });
textAsHtml: "<p>new content</p><p>---------- Forwarded message ---------<br/>De : Ulysse Carlier &lt;<a href=\"mailto:carlierulysse@gmail.com\">carlierulysse@gmail.com</a>&gt;<br/>Date: ven. 10 mars 2023 &agrave; 14:52<br/>Subject: message to transfer<br/>To: Hugueprime M &lt;<a href=\"mailto:hugueprime@gmail.com\">hugueprime@gmail.com</a>&gt;</p><p>content to transfer</p>", console.log(props.data.date);
subject: "Fwd: message to transfer", const date = new Date(props.data.date);
references: "<CAAGJdR0i=4w1i2Nb9=zx5bCPqkR+2eUCs=_qbXTSzugZu4EXFQ@mail.gmail.com>", console.log(date);
date: "2023-03-10T13:52:21.000Z",
}
</script> </script>
<!-- to if to is more than me <!-- to if to is more than me
cc --> cc -->
<!-- object (channel only) <!-- object (channel only)
content content
attachments --> attachments -->
<template> <template>
<div class="message"> <div class="message">
<!-- <div v-html="data.html"></div> --> <div id="context">
<!-- <div v-html="data.text"></div> --> <div class="left" id="profile">Carrefour@emailing .carrefor.fr "carrefour"</div>
<div></div> <div class="middle">{{ props.data.subject }}</div>
<div v-html="data.textAsHtml"></div> <div class="right" id="date">
{{
date.toLocaleString("en-GB", {
weekday: "short",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
timezone: "UTC+1",
})
}}
</div>
</div>
<div id="content">
<div v-html="props.data.content"></div>
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.message { .message {
width: 100%; width: auto;
border: white 1px solid; border: white 1px solid;
padding: 10px;
margin: 5px;
}
#context {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
#content {
overflow: auto;
max-height: 300px;
width: 750px; /* template width being 600px to 640px up to 750px (experiment and test) */
}
.left,
.right {
display: flex;
align-items: center;
}
.middle {
margin: 0 10px;
flex: 1;
align-self: center;
display: contents;
} }
</style> </style>

View File

@ -14,7 +14,8 @@ onBeforeMount(async () => {
console.log(store.state.rooms.find((room) => room.id === id)?.fetched); console.log(store.state.rooms.find((room) => room.id === id)?.fetched);
let room = store.state.rooms.find((room) => room.id === id); let room = store.state.rooms.find((room) => room.id === id);
if (!room || room?.fetched === false) { if (!room || room?.fetched === false) {
await store.dispatch("fetchMessages"); // todo
// await store.dispatch("fetchMessages", );
} }
store.commit("setActiveRoom", id); store.commit("setActiveRoom", id);
}); });
@ -28,20 +29,49 @@ onBeforeRouteUpdate(async (to, from) => {
</script> </script>
<template> <template>
<div> <div id="main">
<Header></Header> <Header></Header>
<div> <div id="RoomViewBody">
<Message v-for="(message, index) in store.getters.messages(id)" :key="index" :data="message" /> <div class="content">
<b>{{ id }}</b> <Message v-for="(message, index) in store.getters.messages(id)" :key="index" :data="message" />
{{ messages.length }} <b>{{ id }}</b>
{{ messages.length }}
</div>
<div id="composer">COMPOSER</div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
div { #main {
background-color: #1d1d23; background-color: #1d1d23;
color: white; color: white;
width: 100%; width: 100%;
} }
#RoomViewBody {
display: flex;
flex-direction: column;
height: 100%;
}
#composer {
position: absolute;
bottom: 0;
width: 100%;
padding-top: 10px;
/* todo composer */
height: 35px;
background-color: red;
}
.content {
display: flex;
flex-direction: column;
overflow: auto;
margin-bottom: 100px;
}
</style> </style>