Compare commits

...

3 Commits

Author SHA1 Message Date
grimhilt
7e0e27c2b6 show members in a room 2023-04-07 00:50:59 +02:00
grimhilt
7c98c1eb0c restructure members storage in rooms store 2023-04-06 20:24:11 +02:00
grimhilt
8c6a2bcfd7 show notifications front 2023-04-06 13:09:47 +02:00
14 changed files with 119 additions and 86 deletions

View File

@ -100,13 +100,16 @@ export async function getMessages(roomId) {
export async function getMembers(roomId) { export async function getMembers(roomId) {
const query = ` const query = `
SELECT SELECT
address.address_id AS id, address.address_id AS id,
address.address_name AS name, address.address_name AS name,
address.email AS email address.email AS email,
FROM app_room_member field_name.field_name as type
INNER JOIN address ON address.address_id = app_room_member.member_id FROM app_room
WHERE app_room_member.room_id = ? INNER JOIN address_field ON address_field.message_id = app_room.message_id
INNER JOIN address ON address.address_id = address_field.address_id
INNER JOIN field_name ON field_name.field_id = address_field.field_id
WHERE app_room.room_id = ?;
`; `;
const values = [roomId]; const values = [roomId];
return await execQueryAsync(query, values); return await execQueryAsync(query, values);

View File

@ -153,6 +153,7 @@ CREATE TABLE app_room_message (
); );
-- 14 -- 14
-- todo needed ?
CREATE TABLE app_room_member ( CREATE TABLE app_room_member (
room_id INT NOT NULL, room_id INT NOT NULL,
member_id INT NOT NULL, member_id INT NOT NULL,

View File

@ -76,6 +76,7 @@ export default class RegisterMessageInApp {
} }
async incrementNotSeen(roomId: number) { async incrementNotSeen(roomId: number) {
// todo it appears there is an error with notifications
if (!this.isSeen) { if (!this.isSeen) {
await incrementNotSeenRoom(roomId); await incrementNotSeenRoom(roomId);
} }

View File

@ -13,11 +13,11 @@ export default {
}, },
color: { color: {
type: String, type: String,
default: "#007bff",
}, },
type: { type: {
type: String, type: String,
default: "badge-primary", default: "badge-primary",
number: "badge-number",
}, },
}, },
}; };
@ -41,5 +41,8 @@ export default {
background-color: #007bff; background-color: #007bff;
} }
/* add more type classes for other types/colors */ .badge-number {
color: var(--primary-text);
background-color: #4d5970;
}
</style> </style>

View File

@ -7,7 +7,10 @@ export enum RoomType {
} }
export interface Message { export interface Message {
todo: true; fromA: string;
subject: string;
content: string;
date: string;
} }
export enum LoadingState { export enum LoadingState {
@ -23,7 +26,8 @@ export interface Room {
mailboxId: number; mailboxId: number;
user: string; user: string;
userId: number; userId: number;
unseen: number; members: Address[];
notSeen: number;
messages: Message[]; messages: Message[];
messageLoading: LoadingState; messageLoading: LoadingState;
threadIds: number[]; threadIds: number[];
@ -39,4 +43,5 @@ export interface Address {
id: number; id: number;
name: string | null; name: string | null;
email: string; email: string;
type: string;
} }

View File

@ -11,7 +11,7 @@ interface RoomFromBack {
mailboxId: number; mailboxId: number;
user: string; user: string;
userId: number; userId: number;
unseen: number; notSeen: number;
parent_id?: number; parent_id?: number;
// todo thread // todo thread
} }
@ -30,8 +30,9 @@ function createRoom(options: RoomFromBack): Room {
roomType: options.roomType, roomType: options.roomType,
mailboxId: options.mailboxId, mailboxId: options.mailboxId,
userId: options.userId, userId: options.userId,
members: [],
user: options.user, user: options.user,
unseen: 0, notSeen: options.notSeen,
messages: [], messages: [],
messageLoading: LoadingState.notLoaded, messageLoading: LoadingState.notLoaded,
threadIds: [], threadIds: [],
@ -41,7 +42,6 @@ function createRoom(options: RoomFromBack): Room {
export interface State { export interface State {
rooms: Room[]; rooms: Room[];
accounts: Account[]; accounts: Account[];
addresses: Address[];
activeAccount: number; activeAccount: number;
activeRoom: number; activeRoom: number;
} }
@ -53,12 +53,11 @@ const store = createStore<State>({
state: { state: {
rooms: [], //createRoom({ id: 12, userId: 1, user: "user", roomName: "room name", mailboxId: 2, roomType: 1 }) 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 }],
addresses: [],
activeAccount: 0, activeAccount: 0,
activeRoom: 0, activeRoom: 0,
}, },
mutations: { mutations: {
setactiveAccount(state, payload) { setActiveAccount(state, payload) {
state.activeAccount = payload; state.activeAccount = payload;
const account = state.accounts.find((account) => account.id == payload); const account = state.accounts.find((account) => account.id == payload);
store.dispatch("fetchRooms", { accountId: payload, account: account }); store.dispatch("fetchRooms", { accountId: payload, account: account });
@ -66,18 +65,9 @@ const store = createStore<State>({
setActiveRoom(state, payload) { setActiveRoom(state, payload) {
state.activeRoom = payload; state.activeRoom = payload;
// todo load room on load page // todo load room on load page
// let room; const room = state.rooms.find((room) => room.id == payload);
// // const room = state.rooms.find((room) => room.id == payload); if (!room) return;
// console.log(state.rooms.length); store.dispatch("fetchMessages", { roomId: payload, room: room });
// for (let i = 0; i < state.rooms.length; i++) {
// console.log(state.rooms[i].id, payload.value);
// if (state.rooms[i].id == payload.value) {
// room = state.rooms[i];
// break;
// }
// }
// console.log(room);
// store.dispatch("fetchMessages", { roomId: payload, room: room });
}, },
addAccounts(state, payload) { addAccounts(state, payload) {
payload.forEach((account: AccountFromBack) => { payload.forEach((account: AccountFromBack) => {
@ -97,7 +87,6 @@ const store = createStore<State>({
const parentRoom = state.rooms.find((room) => room.id == thread.parent_id); // todo debug parent_id to root_id const parentRoom = state.rooms.find((room) => room.id == thread.parent_id); // todo debug parent_id to root_id
if (parentRoom) { if (parentRoom) {
parentRoom.threadIds.push(thread.id); parentRoom.threadIds.push(thread.id);
console.log(parentRoom);
} else { } else {
console.log("yep..."); console.log("yep...");
} }
@ -113,8 +102,10 @@ const store = createStore<State>({
}, },
addAddress(state, payload) { addAddress(state, payload) {
// todo add if not exist // todo add if not exist
const room = state.rooms.find((room) => room.id == payload.roomId);
if (!room) return;
payload.addresses.forEach((address: Address) => { payload.addresses.forEach((address: Address) => {
state.addresses.push(address); room.members.push(address);
}); });
}, },
}, },
@ -133,8 +124,9 @@ const store = createStore<State>({
}, },
address: address:
(state) => (state) =>
(addressId: number): Address | undefined => { (roomId: number, addressId: number): Address | undefined => {
const address = state.addresses.find((address) => address.id == addressId); const room = state.rooms.find((room) => room.id == roomId);
const address = room?.members.find((address) => address.id == addressId);
return address; return address;
}, },
messages: messages:

View File

@ -1,47 +1,54 @@
<script setup lang="ts"> <script setup lang="ts">
import store from "../../store/store"; import { defineProps } from "vue";
import { defineProps, ref, watch } from "vue";
import Badge from "@/components/Badge.vue"; import Badge from "@/components/Badge.vue";
import { RoomType } from "@/store/models/model"; import { RoomType, Address } from "@/store/models/model";
import MemberList from "./MemberList.vue";
const props = defineProps({ id: Number }); const props = defineProps({ id: Number, room: Object });
const room = ref(store.getters.room(props.id));
// todo auto load
watch(
() => props.id,
(newId) => {
room.value = store.getters.room(newId);
},
);
const roomTitle = () => { const roomTitle = () => {
const type = room.value?.roomType; const type = props.room?.roomType;
if (type === RoomType.DM || type == RoomType.CHANNEL || type == RoomType.ROOM) { if (type === RoomType.DM || type == RoomType.CHANNEL || type == RoomType.ROOM) {
return room.value?.user; return props.room?.user;
} }
return room.value?.roomName; return props.room?.roomName;
}; };
const to = () => props.room?.members.filter((member: Address) => member.type == "to");
const cc = () => props.room?.members.filter((member: Address) => member.type == "cc");
</script> </script>
<template> <template>
<div class="main"> <div class="main">
<div class="infos"> <div class="context">
<Badge :value="RoomType[room?.roomType]" /> <div class="infos">
{{ roomTitle() }} <Badge :value="RoomType[room?.roomType]" />
{{ roomTitle() }}
</div>
<div class="action">action: threads message important</div>
</div>
<div class="members" v-if="room?.roomType != RoomType.DM">
<MemberList v-if="to()?.length > 0" type="to" :members="to()" />
<MemberList v-if="cc()?.length > 0" type="cc" :members="cc()" />
</div> </div>
<div class="action">action: threads message important</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.main { .main {
border-bottom: 1px solid #505050;
}
.context {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center;
border-bottom: 1px solid #505050; border-bottom: 1px solid #505050;
align-items: center;
width: 100%; width: 100%;
height: 51px; height: 35px;
}
.members {
} }
.infos { .infos {

View File

@ -0,0 +1,19 @@
<script setup lang="ts">
import { Address } from "@/store/models/model";
import { defineProps } from "vue";
const props = defineProps({ type: String, members: Object });
let emails = "";
props.members?.forEach((member: Address) => {
emails += member.email + "; ";
});
</script>
<template>
<div class="main">
{{ props.type }}:
{{ emails }}
</div>
</template>
<style scoped></style>

View File

@ -3,9 +3,16 @@ import { defineProps, onMounted, ref, watch } from "vue";
import { decodeEmojis } from "../../utils/string"; import { decodeEmojis } from "../../utils/string";
import { removeDuplicates } from "../../utils/array"; import { removeDuplicates } from "../../utils/array";
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import store from "@/store/store";
const props = defineProps({ data: Object }); const props = defineProps({
data: {
fromA: String,
subject: String,
content: String,
date: String,
},
members: Array,
});
const iframe = ref(null); const iframe = ref(null);
// todo dompurify // todo dompurify
@ -47,11 +54,10 @@ onMounted(() => {
}); });
const displayAddresses = (addressesId) => { const displayAddresses = (addressesId) => {
// todo store members in rooms ?
addressesId = removeDuplicates(addressesId); addressesId = removeDuplicates(addressesId);
let res = ""; let res = "";
addressesId.forEach((addressId) => { addressesId.forEach((addressId) => {
const address = store.getters.address(addressId); const address = props.members.find((member) => member.id == addressId);
if (address) res += address.email; if (address) res += address.email;
}); });
return res; return res;
@ -98,17 +104,18 @@ const displayAddresses = (addressesId) => {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
background-color: blue; background-color: var(--quaternary-background);
padding: 3px; padding: 3px 10px;
margin-bottom: 6px; margin-bottom: 6px;
border-radius: 4px;
} }
iframe { iframe {
overflow-y: auto; overflow-y: auto;
max-height: 300px; max-height: 300px;
width: 100%; width: 100%;
padding: 2px 10px; border: none;
max-width: 750px; /* template width being 600px to 640px up to 750px (experiment and test) */ max-width: 600px; /* template width being 600px to 640px up to 750px (experiment and test) */
background-color: rgb(234, 234, 234); background-color: rgb(234, 234, 234);
} }

View File

@ -9,30 +9,21 @@ import { RoomType } from "@/store/models/model";
const store = useStore(); const store = useStore();
const route = useRoute(); const route = useRoute();
const id = ref(parseInt(route.params?.id)); const id = ref(parseInt(route.params?.id));
// let room; let room;
onBeforeMount(async () => { onBeforeMount(async () => {
// get data store.commit("setActiveRoom", id.value);
// room = store.getters.room(id); room = store.getters.room(id.value);
// if (!room || room?.fetched === false) {
// // todo
// // await store.dispatch("fetchMessages", );
// }
store.commit("setActiveRoom", id);
}); });
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);
console.log(id); store.commit("setActiveRoom", id.value);
// room = store.getters.room(id); room = await store.getters.room(id.value);
// console.log(room);
store.commit("setActiveRoom", id);
} }
}); });
const shouldDisplayComposer = () => { const shouldDisplayComposer = () => {
if (!id.value) return false;
const room = store.getters.room(id.value);
if (!room) return false; if (!room) return false;
return room.roomType == RoomType.THREAD || room.roomType == RoomType.GROUP; return room.roomType == RoomType.THREAD || room.roomType == RoomType.GROUP;
}; };
@ -40,12 +31,16 @@ const shouldDisplayComposer = () => {
<template> <template>
<div id="main"> <div id="main">
<Header :id="id"></Header> <Header :id="id" :room="room"></Header>
<div id="RoomViewBody"> <div id="RoomViewBody">
<div class="content"> <div class="content">
<Message v-for="(message, index) in store.getters.messages(id)" :key="index" :data="message" /> <Message
v-for="(message, index) in room?.messages"
:key="index"
:data="message"
:members="room?.members"
/>
</div> </div>
{{ room?.roomType }}
<div id="composer" v-if="shouldDisplayComposer()">COMPOSER</div> <div id="composer" v-if="shouldDisplayComposer()">COMPOSER</div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="container" @click="setactiveAccount(data.id)" :class="activeAccount == data.id && 'active'"> <div class="container" @click="setActiveAccount(data.id)" :class="activeAccount == data.id && 'active'">
{{ data.email }} {{ data.email }}
</div> </div>
</template> </template>
@ -17,7 +17,7 @@ export default {
...mapState(["activeAccount"]), ...mapState(["activeAccount"]),
}, },
methods: { methods: {
...mapMutations(["setactiveAccount"]), ...mapMutations(["setActiveAccount"]),
}, },
}; };
</script> </script>

View File

@ -2,7 +2,7 @@
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { defineProps } from "vue"; import { defineProps } from "vue";
import BaseAvatar from "../../avatars/BaseAvatar.vue"; import BaseAvatar from "../../avatars/BaseAvatar.vue";
import Badge from "../../../components/Badge.vue"; import Badge from "@/components/Badge.vue";
import ThreadList from "./threads/ThreadList.vue"; import ThreadList from "./threads/ThreadList.vue";
import store from "@/store/store"; import store from "@/store/store";
@ -14,10 +14,10 @@ const props = defineProps({
userId: Number, userId: Number,
notSeen: Number, notSeen: Number,
mailboxId: Number, mailboxId: Number,
threadIds: [Number], threadIds: Array,
}, },
}); });
console.log(props.data.threadIds); // console.log(props.data.threadIds);
const router = useRouter(); const router = useRouter();
</script> </script>
@ -34,9 +34,8 @@ const router = useRouter();
<div class="sender">{{ props.data.user }}</div> <div class="sender">{{ props.data.user }}</div>
<div class="object">{{ props.data.roomName }}</div> <div class="object">{{ props.data.roomName }}</div>
</div> </div>
<Badge class="badge" v-if="props.data.unseen > 0" {{ props.data.unseen }}
><template v-slot:body>{{ props.data.unseen }}</template> <Badge class="badge" v-if="props.data.notSeen > 0" :value="props.data.notSeen" type="badge-number" />
</Badge>
</div> </div>
<ThreadList :threadIds="props.data.threadIds" /> <ThreadList :threadIds="props.data.threadIds" />
</div> </div>

View File

@ -2,12 +2,12 @@
import store from "@/store/store"; import store from "@/store/store";
import { defineProps } from "vue"; import { defineProps } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import Badge from "@/components/Badge.vue";
const props = defineProps({ const props = defineProps({
threadId: Number, threadId: Number,
}); });
const room = store.getters.room(props.threadId); const room = store.getters.room(props.threadId);
console.log(props.thread);
const router = useRouter(); const router = useRouter();
</script> </script>
@ -18,6 +18,7 @@ const router = useRouter();
class="room" class="room"
> >
{{ room.roomName }} {{ room.roomName }}
<Badge class="badge" v-if="room.notSeen > 0" :value="room.notSeen" type="badge-number" />
</div> </div>
</template> </template>

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import Thread from "./Thread.vue"; import Thread from "./Thread.vue";
import { defineProps } from "vue"; import { defineProps } from "vue";
const props = defineProps({ threadIds: [Number] }); const props = defineProps({ threadIds: Array });
</script> </script>
<template> <template>