From 4e79ab12dcc6d238521479d9b1b85f224aff9bc7 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Wed, 12 Apr 2023 19:01:40 +0200 Subject: [PATCH] add button to set seen flag on front --- back/abl/Messages-abl.ts | 15 ++++++ back/abl/messages.ts | 16 +++--- back/routes/mail.ts | 21 ++++++++ back/validator/schemas/setFlag-schema.json | 20 ++++++++ back/validator/validator.ts | 3 ++ .../components/structure/message/Content.vue | 5 +- .../components/structure/message/Message.vue | 18 ++++--- .../components/structure/message/Options.vue | 50 ++++++++++++++++--- front/src/services/imapAPI.ts | 6 +++ front/src/store/models/model.ts | 3 +- front/src/store/store.ts | 28 ++++++++--- front/src/utils/flagsUtils.ts | 3 ++ front/src/views/room/RoomView.vue | 2 + 13 files changed, 159 insertions(+), 31 deletions(-) create mode 100644 back/abl/Messages-abl.ts create mode 100644 back/validator/schemas/setFlag-schema.json create mode 100644 front/src/utils/flagsUtils.ts diff --git a/back/abl/Messages-abl.ts b/back/abl/Messages-abl.ts new file mode 100644 index 0000000..14a123f --- /dev/null +++ b/back/abl/Messages-abl.ts @@ -0,0 +1,15 @@ +import statusCode from "../utils/statusCodes"; +import { Response } from "express"; + +export default class Message { + + static async addFlag(body, res: Response) { + console.log("hit") + res.status(statusCode.OK).send(); + } + + static async removeFlag(body, res: Response) { + console.log("hit") + res.status(statusCode.OK).send(); + } +} diff --git a/back/abl/messages.ts b/back/abl/messages.ts index 7c24490..9a92d87 100644 --- a/back/abl/messages.ts +++ b/back/abl/messages.ts @@ -5,10 +5,12 @@ import { Response } from "express"; export async function messages(body, res: Response) { const { roomId } = body; - getMessages(roomId).then((messages) => { - res.status(statusCode.OK).json(messages); - }).catch((err) => { - logger.err(err) - res.status(statusCode.INTERNAL_SERVER_ERROR); - }); -} \ No newline at end of file + getMessages(roomId) + .then((messages) => { + res.status(statusCode.OK).json(messages); + }) + .catch((err) => { + logger.err(err); + res.status(statusCode.INTERNAL_SERVER_ERROR); + }); +} diff --git a/back/routes/mail.ts b/back/routes/mail.ts index 6e5c1e8..bab08d7 100644 --- a/back/routes/mail.ts +++ b/back/routes/mail.ts @@ -3,6 +3,7 @@ import express from "express"; const router = express.Router(); import { rooms } from "../abl/rooms"; +import Message from "../abl/Messages-abl"; import { messages } from "../abl/messages"; import { members } from "../abl/members"; import { @@ -11,6 +12,8 @@ import { validateGetMembers, validateGetMessages, validateGetRooms, + validateAddFlag, + validateRemoveFlag, } from "../validator/validator"; import Account from "../abl/Account-abl"; @@ -75,4 +78,22 @@ router.post("/account", async (req, res) => { } }); +router.post("/addFlag", async (req, res) => { + const valid = validateAddFlag(req.body); + if (!valid) { + res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateAddFlag.errors }); + } else { + await Message.addFlag(req.body, res); + } +}); + +router.post("/removeFlag", async (req, res) => { + const valid = validateRemoveFlag(req.body); + if (!valid) { + res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateRemoveFlag.errors }); + } else { + await Message.removeFlag(req.body, res); + } +}); + export default router; diff --git a/back/validator/schemas/setFlag-schema.json b/back/validator/schemas/setFlag-schema.json new file mode 100644 index 0000000..fbfd61f --- /dev/null +++ b/back/validator/schemas/setFlag-schema.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "properties": { + "mailboxId": { + "type": "number" + }, + "messageId": { + "type": "number" + }, + "flag": { + "type": "string" + } + }, + "required": [ + "mailboxId", + "messageId", + "flag" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/back/validator/validator.ts b/back/validator/validator.ts index 08883ec..9841fb1 100644 --- a/back/validator/validator.ts +++ b/back/validator/validator.ts @@ -8,9 +8,12 @@ import getAccountSchema from "./schemas/getAccounts-schema.json"; import getRoomSchema from "./schemas/getRooms-schema.json"; import getMessagesSchema from "./schemas/getMessages-schema.json"; import getMembersSchema from "./schemas/getMembers-schema.json"; +import setFlagSchema from "./schemas/setFlag-schema.json"; export const validateCreateAccount = ajv.compile(createAccountSchema); export const validateGetAccounts = ajv.compile(getAccountSchema); export const validateGetRooms = ajv.compile(getRoomSchema); export const validateGetMessages = ajv.compile(getMessagesSchema); export const validateGetMembers = ajv.compile(getMembersSchema); +export const validateAddFlag = ajv.compile(setFlagSchema); +export const validateRemoveFlag = ajv.compile(setFlagSchema); \ No newline at end of file diff --git a/front/src/components/structure/message/Content.vue b/front/src/components/structure/message/Content.vue index db3af9f..99b38fc 100644 --- a/front/src/components/structure/message/Content.vue +++ b/front/src/components/structure/message/Content.vue @@ -9,7 +9,6 @@ const props = defineProps({ const iframe = ref(); -// todo dompurify // background vs color const htmlDefault = (html: string) => { return ` @@ -33,7 +32,9 @@ function setIframeContent(content: string | undefined) { if (!content) return; const doc = iframe.value.contentDocument || iframe.value.contentWindow?.document; if (!doc) return; - const html = DOMPurify.sanitize(content); + // todo dompurify for image + const html = DOMPurify.sanitize(content, { FORBID_TAGS: ["style"] }); + doc.open(); doc.write(htmlDefault(html)); doc.close(); diff --git a/front/src/components/structure/message/Message.vue b/front/src/components/structure/message/Message.vue index fc1e798..bbd1ac9 100644 --- a/front/src/components/structure/message/Message.vue +++ b/front/src/components/structure/message/Message.vue @@ -1,13 +1,17 @@ @@ -25,4 +51,16 @@ const props = defineProps({ div { text-align: center; } + +.button { + border: solid 1px; + border-radius: 6px; + display: initial; + padding: 1px 5px; + cursor: pointer; +} + +.button:hover { + background-color: var(--selected); +} diff --git a/front/src/services/imapAPI.ts b/front/src/services/imapAPI.ts index 949b046..9123ea4 100644 --- a/front/src/services/imapAPI.ts +++ b/front/src/services/imapAPI.ts @@ -16,4 +16,10 @@ export default { getMembers(roomId: number) { return API().get(`/mail/${roomId}/members`); }, + addFlag(data: { mailboxId: number; messageId: number; flag: string }) { + return API().post(`/mail/addFlag`, data); + }, + removeFlag(data: { mailboxId: number; messageId: number; flag: string }) { + return API().post(`/mail/removeFlag`, data); + }, }; diff --git a/front/src/store/models/model.ts b/front/src/store/models/model.ts index 518b091..ef9470f 100644 --- a/front/src/store/models/model.ts +++ b/front/src/store/models/model.ts @@ -14,7 +14,7 @@ export interface Message { subject: string; content: string; date: string; - flags: string; + flags: string[]; } export enum LoadingState { @@ -23,6 +23,7 @@ export enum LoadingState { loaded = 2, } +// todo store messages outside of the room export interface Room { id: number; roomName: string; diff --git a/front/src/store/store.ts b/front/src/store/store.ts index 7da257d..79e195f 100644 --- a/front/src/store/store.ts +++ b/front/src/store/store.ts @@ -46,6 +46,8 @@ export interface State { activeRoom: number; } +const roomOnId = (state: State, roomId: number) => state.rooms.find((room: Room) => room.id == roomId); + // // define injection key todo // export const key: InjectionKey> = Symbol() @@ -94,20 +96,33 @@ const store = createStore({ }, addMessages(state, payload) { // todo add if not exist - const room = state.rooms.find((room) => room.id == payload.roomId); + const room = roomOnId(state, payload.roomId); if (!room) return; - payload.messages.forEach((message: Message) => { + payload.messages.forEach((message: any) => { + message.flags = message.flags?.split(",") ?? []; room.messages.push(message); }); }, addAddress(state, payload) { // todo add if not exist - const room = state.rooms.find((room) => room.id == payload.roomId); + const room = roomOnId(state, payload.roomId); if (!room) return; payload.addresses.forEach((address: Address) => { room.members.push(address); }); }, + addFlag(state, payload) { + const msg = roomOnId(state, payload.roomId)?.messages.find((msg) => msg.id == payload.messageId); + if (msg) { + msg.flags.push(payload.flag); + } + }, + removeFlag(state, payload) { + const msg = roomOnId(state, payload.roomId)?.messages.find((msg) => msg.id == payload.messageId); + if (msg) { + msg.flags?.splice(msg.flags?.indexOf(payload.flag), 1); + } + }, }, getters: { rooms: (state) => (): Room[] => { @@ -119,20 +134,19 @@ const store = createStore({ room: (state) => (roomId: number): Room | undefined => { - const room = state.rooms.find((room) => room.id == roomId); - return room; + return roomOnId(state, roomId); }, address: (state) => (roomId: number, addressId: number): Address | undefined => { - const room = state.rooms.find((room) => room.id == roomId); + const room = roomOnId(state, roomId); const address = room?.members.find((address) => address.id == addressId); return address; }, messages: (state) => (roomId: number): Message[] => { - const room = state.rooms.find((room) => room.id == roomId); + const room = roomOnId(state, roomId); if (!room) return []; if (room.messageLoading === LoadingState.notLoaded) { store.dispatch("fetchMessages", { roomId: room.id, room: room }); diff --git a/front/src/utils/flagsUtils.ts b/front/src/utils/flagsUtils.ts new file mode 100644 index 0000000..9ef5ac3 --- /dev/null +++ b/front/src/utils/flagsUtils.ts @@ -0,0 +1,3 @@ +export function isSeenFc(flags: string[] | undefined): boolean { + return flags?.includes("\\Seen") ?? false; +} diff --git a/front/src/views/room/RoomView.vue b/front/src/views/room/RoomView.vue index 3740044..563b44d 100644 --- a/front/src/views/room/RoomView.vue +++ b/front/src/views/room/RoomView.vue @@ -47,6 +47,8 @@ function openMessageView(id) { :key="index" :msg="message" :members="room?.members" + :mailboxId="room.mailboxId" + :roomId="room.id" @open-message-view="(id) => openMessageView(id)" />