Compare commits

..

No commits in common. "b48c834d367efe9bfc365240d048cc1b460941e9" and "e43ab6cfe180de13faf880ab4dbe8e398e7dcd9f" have entirely different histories.

7 changed files with 60 additions and 114 deletions

View File

@ -13,38 +13,29 @@ import { Request, Response } from "express";
import statusCodes from "../utils/statusCodes"; import statusCodes from "../utils/statusCodes";
import logger from "../system/Logger"; import logger from "../system/Logger";
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 validateSetFlag = ajv.compile(setFlagSchema);
class Validator { class Validator {
validateCreateAccount: any;
validateGetAccounts: any;
validateGetRooms: any;
validateGetMessages: any;
validateGetMembers: any;
validateSetFlag: any;
constructor() {
this.validateCreateAccount = ajv.compile(createAccountSchema);
this.validateGetAccounts = ajv.compile(getAccountSchema);
this.validateGetRooms = ajv.compile(getRoomSchema);
this.validateGetMessages = ajv.compile(getMessagesSchema);
this.validateGetMembers = ajv.compile(getMembersSchema);
this.validateSetFlag = ajv.compile(setFlagSchema);
}
_getSchema(name: string): any { _getSchema(name: string): any {
switch (name) { switch (name) {
case "createAccount": case "createAccount":
return this.validateCreateAccount; return validateCreateAccount;
case "getAccounts": case "getAccounts":
return this.validateGetAccounts; return validateGetAccounts;
case "getRooms": case "getRooms":
return this.validateGetRooms; return validateGetRooms;
case "getMessages": case "getMessages":
return this.validateGetMessages; return validateGetMessages;
case "getMembers": case "getMembers":
return this.validateGetMembers; return validateGetMembers;
case "addFlag": case "addFlag":
case "removeFlag": case "removeFlag":
return this.validateSetFlag; return validateSetFlag;
default: default:
logger.err(`Schema ${name} not found`); logger.err(`Schema ${name} not found`);
break; break;
@ -68,4 +59,4 @@ class Validator {
} }
const validator = new Validator(); const validator = new Validator();
export default validator; export default validator;

View File

@ -110,7 +110,7 @@ function mailChange() {
<template> <template>
<div> <div>
<button @click="modal = true">Add Account</button> <button @click="modal = true">Open Modal!</button>
<Modal v-if="modal" title="Add new account" @close-modal="modal = false"> <Modal v-if="modal" title="Add new account" @close-modal="modal = false">
<template v-slot:body> <template v-slot:body>
<div class="field"> <div class="field">

View File

@ -10,6 +10,8 @@ import { isSeenFc } from "@/utils/flagsUtils";
const props = defineProps({ const props = defineProps({
msg: Object as PropType<Message>, msg: Object as PropType<Message>,
members: Array as PropType<Address[]>, members: Array as PropType<Address[]>,
mailboxId: Number,
roomId: Number,
}); });
const displayAddresses = (addressIds: string[] | undefined): string => { const displayAddresses = (addressIds: string[] | undefined): string => {
@ -64,7 +66,7 @@ const classes = (): string => {
</div> </div>
<div class="content" :class="[classes()]"> <div class="content" :class="[classes()]">
<Content :content="props.msg?.content" /> <Content :content="props.msg?.content" />
<Options class="options" :msg="props.msg" /> <Options class="options" :mailboxId="props.mailboxId" :roomId="props.roomId" :msg="props.msg" />
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, inject, PropType } from "vue"; import { defineProps, PropType } from "vue";
import { Message } from "@/store/models/model"; import { Message } from "@/store/models/model";
import API from "@/services/imapAPI"; import API from "@/services/imapAPI";
import store from "@/store/store"; import store from "@/store/store";
@ -7,24 +7,24 @@ import { isSeenFc } from "@/utils/flagsUtils";
const props = defineProps({ const props = defineProps({
msg: Object as PropType<Message>, msg: Object as PropType<Message>,
mailboxId: Number,
roomId: Number,
}); });
const room: any = inject("room");
const setFlag = (flag: string) => { const setFlag = (flag: string) => {
// todo loading // todo loading
if (!room?.value || !props.msg) return; if (!props.mailboxId || !props.msg) return;
let apiCall = isSeenFc(props.msg?.flags) ? API.removeFlag : API.addFlag; let apiCall = isSeenFc(props.msg?.flags) ? API.removeFlag : API.addFlag;
apiCall({ apiCall({
mailboxId: room.value?.mailboxId, mailboxId: props.mailboxId,
messageId: props.msg?.id, messageId: props.msg?.id,
flag: flag, flag: flag,
}) })
.then((res) => { .then((res) => {
if (isSeenFc(props.msg?.flags)) { if (isSeenFc(props.msg?.flags)) {
store.commit("removeFlag", { roomId: room.value?.id, messageId: props.msg?.id, flag: flag }); store.commit("removeFlag", { roomId: props.roomId, messageId: props.msg?.id, flag: flag });
} else { } else {
store.commit("addFlag", { roomId: room.value?.id, messageId: props.msg?.id, flag: flag }); store.commit("addFlag", { roomId: props.roomId, messageId: props.msg?.id, flag: flag });
} }
}) })
.catch((err) => { .catch((err) => {

View File

@ -23,6 +23,7 @@ export enum LoadingState {
loaded = 2, loaded = 2,
} }
// todo store messages outside of the room
export interface Room { export interface Room {
id: number; id: number;
roomName: string; roomName: string;
@ -32,6 +33,8 @@ export interface Room {
userId: number; userId: number;
members: Address[]; members: Address[];
notSeen: number; notSeen: number;
messages: Message[];
messageLoading: LoadingState;
threadIds: number[]; threadIds: number[];
} }

View File

@ -1,5 +1,4 @@
import API from "@/services/imapAPI"; import API from "@/services/imapAPI";
import { isSeenFc } from "@/utils/flagsUtils";
import { decodeEmojis } from "@/utils/string"; import { decodeEmojis } from "@/utils/string";
import { AxiosError, AxiosResponse } from "axios"; import { AxiosError, AxiosResponse } from "axios";
import { createStore } from "vuex"; import { createStore } from "vuex";
@ -34,61 +33,30 @@ function createRoom(options: RoomFromBack): Room {
members: [], members: [],
user: options.user, user: options.user,
notSeen: options.notSeen, notSeen: options.notSeen,
messages: [],
messageLoading: LoadingState.notLoaded,
threadIds: [], threadIds: [],
}; };
} }
interface roomMessage {
roomId: number;
fetch: LoadingState;
messages: Message[];
}
export interface State { export interface State {
rooms: Room[];
accounts: Account[]; accounts: Account[];
activeAccount: number; activeAccount: number;
rooms: Room[];
activeRoom: number; activeRoom: number;
roomMessages: roomMessage[];
} }
const roomOnId = (state: State, roomId: number): Room | undefined => const roomOnId = (state: State, roomId: number) => state.rooms.find((room: Room) => room.id == roomId);
state.rooms.find((room: Room) => room.id == roomId);
const msgOnRoomId = (state: State, roomId: number) => state.roomMessages.find((roomMsg) => roomMsg.roomId == roomId);
function updateSeen(state: State, roomId: number, flag: string[], adding: boolean) { // // define injection key todo
// if is seen flag decrement unread value
if (isSeenFc(flag)) {
const room = roomOnId(state, roomId);
if (!room) return;
if (adding) {
room.notSeen--;
} else {
room.notSeen++;
}
// if is thread change seen on parent
if (room.roomType == RoomType.THREAD) {
const parentRoom = state.rooms.find((room) => room.threadIds.includes(roomId));
if (parentRoom) {
if (adding) {
parentRoom.notSeen--;
} else {
parentRoom.notSeen++;
}
}
}
}
}
// define injection key todo
// export const key: InjectionKey<Store<State>> = Symbol() // export const key: InjectionKey<Store<State>> = Symbol()
const store = createStore<State>({ const store = createStore<State>({
state: { state: {
rooms: [], //createRoom({ id: 12, userId: 1, user: "user", roomName: "room name", mailboxId: 2, roomType: 1 })
accounts: [{ id: 0, email: "All", fetched: false }], accounts: [{ id: 0, email: "All", fetched: false }],
activeAccount: 0, activeAccount: 0,
rooms: [],
activeRoom: 0, activeRoom: 0,
roomMessages: [],
}, },
mutations: { mutations: {
setActiveAccount(state, payload) { setActiveAccount(state, payload) {
@ -101,15 +69,7 @@ const store = createStore<State>({
// todo load room on load page // todo load room on load page
const room = state.rooms.find((room) => room.id == payload); const room = state.rooms.find((room) => room.id == payload);
if (!room) return; if (!room) return;
let roomMessage = msgOnRoomId(state, payload); store.dispatch("fetchMessages", { roomId: payload, room: room });
if (!roomMessage) {
state.roomMessages.push({ messages: [], fetch: LoadingState.notLoaded, roomId: payload });
roomMessage = msgOnRoomId(state, payload);
}
store.dispatch("fetchMessages", {
roomId: payload,
obj: roomMessage,
});
}, },
addAccounts(state, payload) { addAccounts(state, payload) {
payload.forEach((account: AccountFromBack) => { payload.forEach((account: AccountFromBack) => {
@ -138,16 +98,9 @@ const store = createStore<State>({
// todo add if not exist // todo add if not exist
const room = roomOnId(state, payload.roomId); const room = roomOnId(state, payload.roomId);
if (!room) return; if (!room) return;
let roomMessage = msgOnRoomId(state, payload.roomId);
if (!roomMessage) {
state.roomMessages.push({ roomId: payload.roomId, messages: [], fetch: LoadingState.notLoaded });
roomMessage = msgOnRoomId(state, payload.roomId);
}
if (!roomMessage) return;
payload.messages.forEach((message: any) => { payload.messages.forEach((message: any) => {
message.flags = message.flags?.split(",") ?? []; message.flags = message.flags?.split(",") ?? [];
roomMessage?.messages.push(message); room.messages.push(message);
}); });
}, },
addAddress(state, payload) { addAddress(state, payload) {
@ -159,17 +112,16 @@ const store = createStore<State>({
}); });
}, },
addFlag(state, payload) { addFlag(state, payload) {
const msg = msgOnRoomId(state, payload.roomId)?.messages.find((msg) => msg.id == payload.messageId); // todo if seen notif
const msg = roomOnId(state, payload.roomId)?.messages.find((msg) => msg.id == payload.messageId);
if (msg) { if (msg) {
msg.flags.push(payload.flag); msg.flags.push(payload.flag);
updateSeen(state, payload.roomId, payload.flag, true);
} }
}, },
removeFlag(state, payload) { removeFlag(state, payload) {
const msg = msgOnRoomId(state, payload.roomId)?.messages.find((msg) => msg.id == payload.messageId); const msg = roomOnId(state, payload.roomId)?.messages.find((msg) => msg.id == payload.messageId);
if (msg) { if (msg) {
msg.flags?.splice(msg.flags?.indexOf(payload.flag), 1); msg.flags?.splice(msg.flags?.indexOf(payload.flag), 1);
updateSeen(state, payload.roomId, payload.flag, false);
} }
}, },
}, },
@ -195,11 +147,12 @@ const store = createStore<State>({
messages: messages:
(state) => (state) =>
(roomId: number): Message[] => { (roomId: number): Message[] => {
const roomMessage = msgOnRoomId(state, roomId); const room = roomOnId(state, roomId);
if (roomMessage) return roomMessage.messages; if (!room) return [];
state.roomMessages.push({ messages: [], roomId: roomId, fetch: LoadingState.notLoaded }); if (room.messageLoading === LoadingState.notLoaded) {
store.dispatch("fetchMessages", { roomId: roomId, obj: msgOnRoomId(state, roomId) }); store.dispatch("fetchMessages", { roomId: room.id, room: room });
return msgOnRoomId(state, roomId)?.messages ?? []; }
return room.messages;
}, },
}, },
actions: { actions: {
@ -225,16 +178,16 @@ const store = createStore<State>({
} }
}, },
fetchMessages: async (context, data) => { fetchMessages: async (context, data) => {
if (data.obj.fetch === LoadingState.notLoaded) { if (!data.room || data.room.messageLoading === LoadingState.notLoaded) {
data.obj.fetch = LoadingState.loading; data.room.messageLoading = LoadingState.loading;
store.dispatch("fetchRoomMembers", { roomId: data.roomId }); store.dispatch("fetchRoomMembers", { roomId: data.roomId });
API.getMessages(data.roomId) API.getMessages(data.roomId)
.then((res: AxiosResponse) => { .then((res: AxiosResponse) => {
data.obj.fetch = LoadingState.loaded; data.room.messageLoading = LoadingState.loaded;
context.commit("addMessages", { messages: res.data, roomId: data.roomId }); context.commit("addMessages", { messages: res.data, roomId: data.roomId });
}) })
.catch((err: AxiosError) => { .catch((err: AxiosError) => {
data.obj.fetch = LoadingState.notLoaded; data.room.messageLoading = LoadingState.notLoaded;
console.log(err); console.log(err);
}); });
} }

View File

@ -1,10 +1,10 @@
<script setup> <script setup>
import { useStore } from "vuex"; import { useStore } from "vuex";
import { useRoute, onBeforeRouteUpdate } from "vue-router"; import { useRoute, onBeforeRouteUpdate } from "vue-router";
import { onBeforeMount, provide, ref } from "vue"; import { onBeforeMount, ref } from "vue";
import { RoomType } from "@/store/models/model";
import Header from "./Header.vue"; import Header from "./Header.vue";
import Message from "../../components/structure/message/Message.vue"; import Message from "../../components/structure/message/Message.vue";
import { Room, RoomType } from "@/store/models/model";
import MessageViewModal from "@/components/modals/MessageViewModal.vue"; import MessageViewModal from "@/components/modals/MessageViewModal.vue";
const store = useStore(); const store = useStore();
@ -12,34 +12,29 @@ const route = useRoute();
const messageIdView = ref(-1); const messageIdView = ref(-1);
const message = ref(undefined); const message = ref(undefined);
const id = ref(parseInt(route.params.id)); const id = ref(parseInt(route.params.id));
let room = ref(); let room;
onBeforeMount(async () => { onBeforeMount(async () => {
store.commit("setActiveRoom", id.value); store.commit("setActiveRoom", id.value);
room.value = store.getters.room(id.value); room = store.getters.room(id.value);
console.log(room.value);
}); });
onBeforeRouteUpdate(async (to, from) => { onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) { if (to.params.id !== from.params.id) {
id.value = parseInt(to.params.id); id.value = parseInt(to.params.id);
store.commit("setActiveRoom", id.value); store.commit("setActiveRoom", id.value);
room.value = await store.getters.room(id.value); room = await store.getters.room(id.value);
console.log(room.value);
} }
}); });
const shouldDisplayComposer = () => { const shouldDisplayComposer = () => {
if (!room?.value) return false; if (!room) return false;
return room.value.roomType == RoomType.THREAD || room.value.roomType == RoomType.GROUP; return room.roomType == RoomType.THREAD || room.roomType == RoomType.GROUP;
}; };
function openMessageView(id) { function openMessageView(id) {
messageIdView.value = id; messageIdView.value = id;
message.value = room.value?.messages.find((message) => message.id == id); message.value = room?.messages.find((message) => message.id == id);
} }
provide("room", room);
</script> </script>
<template> <template>
@ -48,10 +43,12 @@ provide("room", room);
<div id="RoomViewBody"> <div id="RoomViewBody">
<div class="content"> <div class="content">
<Message <Message
v-for="(message, index) in store.getters.messages(room?.id)" v-for="(message, index) in room?.messages"
:key="index" :key="index"
:msg="message" :msg="message"
:members="room?.members" :members="room?.members"
:mailboxId="room.mailboxId"
:roomId="room.id"
@open-message-view="(id) => openMessageView(id)" @open-message-view="(id) => openMessageView(id)"
/> />
</div> </div>