Compare commits

...

3 Commits

Author SHA1 Message Date
grimhilt
e44584df0a simplify and composer design 2023-04-15 00:39:24 +02:00
grimhilt
956bc35158 fix duplication of threads on account change 2023-04-14 23:04:09 +02:00
grimhilt
79e17ad24f fix mail view in modal 2023-04-14 21:00:23 +02:00
4 changed files with 98 additions and 148 deletions

View File

@ -27,7 +27,7 @@ export async function getAccounts() {
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
} }
export async function getRooms(mailboxId) { export async function getRooms(mailboxId: number) {
const query = ` const query = `
SELECT SELECT
room.room_id AS id, room.room_id AS id,

View File

@ -3,7 +3,7 @@ import imapAPI from "@/services/imapAPI";
import store from "@/store/store"; import store from "@/store/store";
import StarterKit from "@tiptap/starter-kit"; import StarterKit from "@tiptap/starter-kit";
import { BubbleMenu, useEditor, EditorContent, FloatingMenu } from "@tiptap/vue-3"; import { BubbleMenu, useEditor, EditorContent, FloatingMenu } from "@tiptap/vue-3";
import { inject, onBeforeUnmount } from "vue"; import { inject } from "vue";
const editor = useEditor({ const editor = useEditor({
extensions: [StarterKit], extensions: [StarterKit],
@ -35,120 +35,75 @@ const send = () => {
</script> </script>
<template> <template>
<div v-if="editor"> <div class="main">
<bubble-menu class="bubble-menu" :tippy-options="{ duration: 100 }" :editor="editor"> <div v-if="editor">
<button <bubble-menu class="bubble-menu" :tippy-options="{ duration: 100 }" :editor="editor">
@click="editor.chain().focus().toggleBold().run()" <button
:class="{ 'is-active': editor.isActive('bold') }" @click="editor.chain().focus().toggleBold().run()"
> :class="{ 'is-active': editor.isActive('bold') }"
Bold >
</button> Bold
<button </button>
@click="editor.chain().focus().toggleItalic().run()" <button
:class="{ 'is-active': editor.isActive('italic') }" @click="editor.chain().focus().toggleItalic().run()"
> :class="{ 'is-active': editor.isActive('italic') }"
Italic >
</button> Italic
<button </button>
@click="editor.chain().focus().toggleStrike().run()" <button
:class="{ 'is-active': editor.isActive('strike') }" @click="editor.chain().focus().toggleStrike().run()"
> :class="{ 'is-active': editor.isActive('strike') }"
Strike >
</button> Strike
</bubble-menu> </button>
</bubble-menu>
<floating-menu class="floating-menu" :tippy-options="{ duration: 100 }" :editor="editor"> <floating-menu class="floating-menu" :tippy-options="{ duration: 100 }" :editor="editor">
<button <button
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()" @click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
> >
H1 H1
</button> </button>
<button <button
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()" @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
> >
H2 H2
</button> </button>
<button <button
@click="editor.chain().focus().toggleBulletList().run()" @click="editor.chain().focus().toggleBulletList().run()"
:class="{ 'is-active': editor.isActive('bulletList') }" :class="{ 'is-active': editor.isActive('bulletList') }"
> >
Bullet List Bullet List
</button> </button>
</floating-menu> </floating-menu>
</div>
<editor-content class="editor" :editor="editor" aria-expanded="false" />
<button @click="send()">SEND</button>
</div> </div>
<editor-content class="editor" :editor="editor" />
<div class="send" @click="send()">SEND</div>
</template> </template>
<style lang="scss"> <style>
.send:hover { .ProseMirror:focus {
background-color: var(--selected); outline: none;
} }
/* Basic editor styles */ </style>
.editor {
background-color: var(--secondary-background); <style lang="scss" scoped>
} .main {
display: flex;
.ProseMirror { flex-direction: row;
> * + * { padding: 10px;
margin-top: 0.75em; }
} .editor {
background-color: var(--secondary-background);
ul, flex: 1;
ol { margin-right: 10px;
padding: 0 1rem; border-radius: 10px;
} padding: 0 10px;
line-height: 0.6rem;
blockquote {
padding-left: 1rem;
border-left: 2px solid rgba(#0d0d0d, 0.1);
}
}
.bubble-menu {
display: flex;
background-color: #0d0d0d;
padding: 0.2rem;
border-radius: 0.5rem;
button {
border: none;
background: none;
color: #fff;
font-size: 0.85rem;
font-weight: 500;
padding: 0 0.2rem;
opacity: 0.6;
&:hover,
&.is-active {
opacity: 1;
}
}
}
.floating-menu {
display: flex;
background-color: #0d0d0d10;
padding: 0.2rem;
border-radius: 0.5rem;
button {
border: none;
background: none;
font-size: 0.85rem;
font-weight: 500;
padding: 0 0.2rem;
opacity: 0.6;
&:hover,
&.is-active {
opacity: 1;
}
}
} }
</style> </style>

View File

@ -21,8 +21,6 @@ interface AccountFromBack {
email: string; email: string;
} }
const buffer: RoomFromBack[] = [];
function createRoom(options: RoomFromBack): Room { function createRoom(options: RoomFromBack): Room {
return { return {
id: options.id, id: options.id,
@ -117,6 +115,7 @@ const store = createStore<State>({
}, },
addRooms(state, payload) { addRooms(state, payload) {
// todo add if not exist // todo add if not exist
const buffer: RoomFromBack[] = [];
payload.rooms.forEach((room: RoomFromBack) => { payload.rooms.forEach((room: RoomFromBack) => {
if (room.roomType == RoomType.THREAD) { if (room.roomType == RoomType.THREAD) {
buffer.push(room); buffer.push(room);
@ -174,7 +173,7 @@ const store = createStore<State>({
}, },
getters: { getters: {
rooms: (state) => (): Room[] => { rooms: (state) => (): Room[] => {
if (state.activeAccount === 0) return state.rooms; if (state.activeAccount === 0) return state.rooms.filter((room) => room.roomType != RoomType.THREAD);
return state.rooms.filter( return state.rooms.filter(
(room) => room.mailboxId == state.activeAccount && room.roomType != RoomType.THREAD, (room) => room.mailboxId == state.activeAccount && room.roomType != RoomType.THREAD,
); );
@ -200,6 +199,13 @@ const store = createStore<State>({
store.dispatch("fetchMessages", { roomId: roomId, obj: msgOnRoomId(state, roomId) }); store.dispatch("fetchMessages", { roomId: roomId, obj: msgOnRoomId(state, roomId) });
return msgOnRoomId(state, roomId)?.messages ?? []; return msgOnRoomId(state, roomId)?.messages ?? [];
}, },
message:
(state) =>
(roomId: number, messageId: number): Message | undefined => {
const roomMessage = msgOnRoomId(state, roomId);
if (!roomMessage) return;
return roomMessage.messages.find((msg) => msg.id === messageId);
},
accountOfRoom: accountOfRoom:
(state) => (state) =>
(roomId: number): string | undefined => { (roomId: number): string | undefined => {

View File

@ -39,59 +39,48 @@ const shouldDisplayComposer = () => {
); );
}; };
function openMessageView(id) { function openMessageView(messageId) {
messageIdView.value = id; messageIdView.value = messageId;
message.value = room.value?.messages.find((message) => message.id == id); if (messageId === -1) return;
message.value = store.getters.message(room.value.id, messageId);
} }
provide("room", room); provide("room", room);
</script> </script>
<template> <template>
<div id="main"> <div class="container">
<Header :id="id" :room="room"></Header> <Header :id="id" :room="room"></Header>
<div id="RoomViewBody"> <div class="messages">
<div class="content"> <Message
<Message v-for="(message, index) in store.getters.messages(room?.id)"
v-for="(message, index) in store.getters.messages(room?.id)" :key="index"
:key="index" :msg="message"
:msg="message" :members="room?.members"
:members="room?.members" @open-message-view="(id) => openMessageView(id)"
@open-message-view="(id) => openMessageView(id)" />
/>
</div>
<Composer class="composer" v-if="shouldDisplayComposer()" />
</div> </div>
<Composer class="composer" v-if="shouldDisplayComposer()" />
<MessageViewModal :message="message" :messageId="messageIdView" @close="() => openMessageView(-1)" /> <MessageViewModal :message="message" :messageId="messageIdView" @close="() => openMessageView(-1)" />
</div> </div>
</template> </template>
<style scoped> <style scoped>
#main { .container {
display: flex;
flex-direction: column;
background-color: var(--primary-background); background-color: var(--primary-background);
color: var(--primary-text); color: var(--primary-text);
height: 100vh;
width: 100%; width: 100%;
} }
#RoomViewBody { .messages {
display: flex; flex-grow: 1;
flex-direction: column;
height: 100%;
} }
.composer { .composer {
position: absolute;
bottom: 0;
width: 100%;
padding-top: 10px;
height: 35px;
}
.content {
display: flex; display: flex;
flex-direction: column-reverse; align-items: center;
overflow: auto;
margin-bottom: 60px;
} }
</style> </style>