switch front to typescript

This commit is contained in:
grimhilt 2023-04-02 16:44:54 +02:00
parent 8258581435
commit 4b5bd9da67
36 changed files with 10000 additions and 578 deletions

View File

@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

7335
front/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,27 +5,45 @@
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@vueuse/components": "^9.13.0",
"@vueuse/core": "^9.13.0",
"axios": "^1.3.4",
"core-js": "^3.8.3",
"dompurify": "^3.0.1",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-router": "^4.1.6", "vue-router": "^4.0.3",
"vuex": "^4.0.2" "vuex": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.16", "@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16", "@babel/eslint-parser": "^7.12.16",
"@babel/preset-typescript": "^7.21.4", "@babel/preset-typescript": "^7.21.4",
"@types/jest": "^27.0.1",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-typescript": "~5.0.0",
"@vue/cli-plugin-unit-jest": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"@vue/eslint-config-typescript": "^9.1.0",
"@vue/test-utils": "^2.0.0-0",
"@vue/vue3-jest": "^27.0.0-alpha.1",
"@vueuse/components": "^9.13.0",
"@vueuse/core": "^9.13.0",
"axios": "^1.3.4",
"babel-jest": "^27.0.6",
"core-js": "^3.8.3",
"dompurify": "^3.0.1",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3" "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.0.3",
"jest": "^27.0.5",
"prettier": "^2.4.1",
"ts-jest": "^27.0.4",
"typescript": "~4.5.5"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,
@ -34,14 +52,27 @@
}, },
"extends": [ "extends": [
"plugin:vue/vue3-essential", "plugin:vue/vue3-essential",
"eslint:recommended" "eslint:recommended",
"@vue/typescript/recommended",
"plugin:prettier/recommended"
], ],
"parserOptions": { "parserOptions": {
"parser": "@babel/eslint-parser" "ecmaVersion": 2020
}, },
"rules": { "rules": {
"vue/multi-word-component-names": "off" "vue/multi-word-component-names": "off"
} },
"overrides": [
{
"files": [
"**/__tests__/*.{j,t}s?(x)",
"**/tests/unit/**/*.spec.{j,t}s?(x)"
],
"env": {
"jest": true
}
}
]
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@ -3,28 +3,27 @@ import { RouterView } from "vue-router";
</script> </script>
<template> <template>
<div id="app"> <div id="app">
<Sidebar /> <Sidebar />
<RouterView/> <RouterView />
</div> </div>
</template> </template>
<script> <script>
import Sidebar from './views/sidebar/Sidebar' import Sidebar from "./views/sidebar/Sidebar";
export default { export default {
name: 'App', name: "App",
components: { components: {
Sidebar, Sidebar,
}, },
} };
</script> </script>
<style> <style>
#app { #app {
display: flex; display: flex;
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
</style> </style>

View File

@ -1,8 +1,6 @@
<template> <template>
<div class="wrapper"> <div class="wrapper">
<slot class="badge" name="body"> <slot class="badge" name="body"> 0 </slot>
0
</slot>
</div> </div>
</template> </template>
@ -24,5 +22,4 @@
font-size: 1.1rem; font-size: 1.1rem;
line-height: 1.4rem; line-height: 1.4rem;
} }
</style>
</style>

View File

@ -1,11 +0,0 @@
import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
import store from './store/store'
const app = createApp(App);
app.use(router);
app.use(store);
app.mount('#app');

10
front/src/main.ts Normal file
View File

@ -0,0 +1,10 @@
import { createApp } from "vue";
import router from "./router";
import App from "./App.vue";
import store from "./store/store";
const app = createApp(App);
app.use(router);
app.use(store);
app.mount("#app");

View File

@ -1,31 +0,0 @@
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home";
import RoomView from "../views/room/RoomView";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
{
path: "/:id",
component: RoomView
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;

30
front/src/router/index.ts Normal file
View File

@ -0,0 +1,30 @@
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
import RoomView from "../views/room/RoomView.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
{
path: "/:id",
component: RoomView,
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;

View File

@ -1,7 +0,0 @@
import axios from 'axios'
export default(url='/api') => {
return axios.create({
baseURL: url,
});
}

View File

@ -0,0 +1,7 @@
import axios from "axios";
export default (url = "/api") => {
return axios.create({
baseURL: url,
});
};

View File

@ -1,11 +1,11 @@
import API from './API' import API from "./API";
export default { export default {
registerAccount(data) { registerAccount(data) {
return API().post('/mail/account', data); return API().post("/mail/account", data);
}, },
getAccounts() { getAccounts() {
return API().get('/mail/accounts'); return API().get("/mail/accounts");
}, },
getRooms(mailboxId) { getRooms(mailboxId) {
return API().get(`/mail/${mailboxId}/rooms`); return API().get(`/mail/${mailboxId}/rooms`);
@ -16,4 +16,4 @@ export default {
getMembers(roomId) { getMembers(roomId) {
return API().get(`/mail/${roomId}/members`); return API().get(`/mail/${roomId}/members`);
}, },
} };

6
front/src/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
/* eslint-disable */
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}

View File

@ -1,7 +0,0 @@
export default class Account {
constructor(_id, _mail) {
this.id = _id;
this.email = _mail;
this.fetched = false;
}
}

View File

@ -1,32 +0,0 @@
export const roomType = {
ROOM: 0,
CHANNEL: 1,
GROUP: 2,
DM: 3,
THREAD: 4,
};
export default class Room {
constructor(room, thread) {
this.id = room.id;
this.user = room.user;
this.userId = room.userId;
this.roomName = room.roomName;
this.mailboxId = room.mailboxId;
this.unseen = room.unseen;
// this.type = room.type;
this.messages = [];
this.messagesFetched = false;
if (!thread) {
this.threads = [
// new Room({id:12, user: this.user, roomName: "thread 1", mailbboxId:this.mailboxId}, true),
// new Room({id:12, user: this.user, roomName: "thread 1", mailbboxId:this.mailboxId}, true),
// new Room({id:12, user: this.user, roomName: "thread 1", mailbboxId:this.mailboxId}, true),
];
} else {
this.threads = [];
}
}
}

View File

@ -1,5 +0,0 @@
export default class Thread {
constructor (roomId, name) {
}
}

View File

@ -0,0 +1,31 @@
export enum RoomType {
ROOM = 0,
CHANNEL = 1,
GROUP = 2,
DM = 3,
THREAD = 4,
};
export interface Room {
id: number;
roomName: string;
roomType: RoomType;
mailboxId: number;
user: string;
userId: number;
unseen: number;
messages: object[];
messagesFetched: boolean;
threads: object[];
}
export interface Account {
id: number;
email: string;
fetched: boolean;
}
export interface Address {
todo: boolean;
}

View File

@ -1,18 +1,40 @@
import API from "@/services/imapAPI"; import API from "@/services/imapAPI";
import { createStore } from "vuex"; import { createStore, Store } from "vuex";
import Room from "./models/Room"; import { Room, Account, Address } from "./models/model";
import Account from "./models/Account";
const store = createStore({ function createRoom(options): Room {
state() { return {
return { id: options.id,
rooms: [new Room({ id: 12, user: "user", roomName: "room name", mailbboxId: 2, type: 1 })], roomName: options.roomName,
messages: [], roomType: options.roomType,
accounts: [new Account(0, "ALL")], mailboxId: options.mailboxId,
addresses: [], userId: options.userId,
activeAccount: 0, user: options.user,
activeRoom: 0, unseen: options.unseen,
}; messages: [],
messagesFetched: false,
threads: [],
};
}
export interface State {
rooms: Room[];
accounts: Account[];
addresses: Address[];
activeAccount: number;
activeRoom: number;
}
// // define injection key todo
// export const key: InjectionKey<Store<State>> = Symbol()
const store = createStore<State>({
state: {
rooms: [createRoom({ id: 12, userId: 1, user: "user", roomName: "room name", mailboxId: 2, roomType: 1 })],
accounts: [{ id: 0, email: "All", fetched: false }],
addresses: [],
activeAccount: 0,
activeRoom: 0,
}, },
mutations: { mutations: {
setactiveAccount(state, payload) { setactiveAccount(state, payload) {
@ -27,13 +49,13 @@ const store = createStore({
}, },
addAccounts(state, payload) { addAccounts(state, payload) {
payload.forEach((account) => { payload.forEach((account) => {
state.accounts.push(new Account(account.id, account.email)); state.accounts.push({ id: account.id, email: account.email, fetched: false });
}); });
}, },
addRooms(state, payload) { addRooms(state, payload) {
// todo add if not exist // todo add if not exist
payload.rooms.forEach((room) => { payload.rooms.forEach((room) => {
state.rooms.push(new Room(room)); state.rooms.push(createRoom(room));
}); });
}, },
addMessages(state, payload) { addMessages(state, payload) {

View File

@ -1,3 +1,3 @@
<template> <template>
<h1>About</h1> <h1>About</h1>
</template> </template>

View File

@ -1,3 +1,3 @@
<template> <template>
<h1>home</h1> <h1>home</h1>
</template> </template>

View File

@ -1,15 +1,14 @@
<template> <template>
<img :src="require('../../assets/'+ url)" > <img :src="require('../../assets/' + url)" />
</template> </template>
<script> <script>
export default { export default {
name: 'BaseAvatar', name: "BaseAvatar",
props: { props: {
url: String url: String,
} },
} };
</script> </script>
<style scoped> <style scoped>
@ -17,4 +16,4 @@ img {
width: 32px; width: 32px;
height: 32px; height: 32px;
} }
</style> </style>

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, computed, watchEffect } from 'vue' import { ref, computed, watchEffect } from "vue";
import Modal from './Modal' import Modal from "./Modal";
import API from '../../services/imapAPI'; import API from "../../services/imapAPI";
const modal = ref(false); const modal = ref(false);
@ -15,187 +15,181 @@ const port = ref(993);
const error = ref(""); const error = ref("");
const knownHosts = { const knownHosts = {
'outlook.com': { "outlook.com": {
'host': 'outlook.office365.com', host: "outlook.office365.com",
}, },
'hotmail.com': { "hotmail.com": {
'host': 'outlook.office365.com', host: "outlook.office365.com",
}, },
'live.com': { "live.com": {
'host': 'outlook.office365.com', host: "outlook.office365.com",
}, },
'zoho.com': { "zoho.com": {
'host': 'imap.zoho.eu', host: "imap.zoho.eu",
}, },
'yahoo.com': { "yahoo.com": {
'host': 'imap.mail.yahoo.com', host: "imap.mail.yahoo.com",
}, },
'icloud.com': { "icloud.com": {
'host': 'imap.mail.me.com', host: "imap.mail.me.com",
}, },
} };
const refHost = computed({ const refHost = computed({
set: (val) => { set: (val) => {
host.value = val host.value = val;
} },
}); });
const err = computed({ const err = computed({
set: (val) => { error.value = val } set: (val) => {
error.value = val;
},
}); });
function validateEmail(email) { function validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const re =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase()); return re.test(String(email).toLowerCase());
} }
function checkError() { function checkError() {
if (!validateEmail(email.value)) { if (!validateEmail(email.value)) {
err.value = "The email is not valid."; err.value = "The email is not valid.";
} else if (pwd.value == xoauth.value == xoauth2.value == "") { } else if (((pwd.value == xoauth.value) == xoauth2.value) == "") {
err.value = "You need at least one authentification method."; err.value = "You need at least one authentification method.";
} else if ([pwd.value, xoauth.value, xoauth2.value].filter((val) => val != "").length > 1) { } else if ([pwd.value, xoauth.value, xoauth2.value].filter((val) => val != "").length > 1) {
err.value = "You need only one authentification method."; err.value = "You need only one authentification method.";
} else if (host.value == "" || port.value == "") { } else if (host.value == "" || port.value == "") {
err.value = "You need to complete the port and the host."; err.value = "You need to complete the port and the host.";
} else { } else {
err.value = ""; err.value = "";
} }
} }
function addAccountRequest() { function addAccountRequest() {
checkError(); checkError();
const data = { const data = {
email: email.value, email: email.value,
pwd: pwd.value, pwd: pwd.value,
xoauth: xoauth.value, xoauth: xoauth.value,
xoauth2: xoauth2.value, xoauth2: xoauth2.value,
host: host.value, host: host.value,
port: port.value, port: port.value,
tls: true tls: true,
}; };
API.registerAccount(data).then((res) => { API.registerAccount(data)
console.log(res.status); .then((res) => {
}).catch((err) => { console.log(res.status);
console.log(err.request.status) })
}); .catch((err) => {
console.log(err.request.status);
});
} }
watchEffect(() => { watchEffect(() => {
if (error.value != "") { if (error.value != "") {
checkError(); checkError();
} }
}); });
function mailChange() { function mailChange() {
if (email.value.includes('@')) { if (email.value.includes("@")) {
const domain = email.value.split('@')[1]; const domain = email.value.split("@")[1];
if (!knownHosts[domain]) { if (!knownHosts[domain]) {
refHost.value = ("imap."+domain); refHost.value = "imap." + domain;
} else { } else {
refHost.value = (knownHosts[domain].host); refHost.value = knownHosts[domain].host;
}
// todo check if manual
} }
// todo check if manual
}
} }
</script> </script>
<template> <template>
<div> <div>
<button <button @click="modal = true">Open Modal!</button>
@click="modal = true" <Modal v-if="modal" title="Add new account" @close-modal="modal = false">
> <template v-slot:body>
Open Modal! <div class="field">
</button> <label>Email: </label>
<Modal <input @change="mailChange" v-model="email" type="email" required />
v-if="modal" </div>
title="Add new account"
@close-modal="modal = false"
>
<template v-slot:body>
<div class="field">
<label>Email: </label>
<input @change="mailChange" v-model="email" type="email" required>
</div>
<fieldset> <fieldset>
<legend>Authentification method</legend> <legend>Authentification method</legend>
<div class="field"> <div class="field">
<label>Plain password:</label> <label>Plain password:</label>
<input v-model="pwd" type="password"> <input v-model="pwd" type="password" />
</div> </div>
<div class="field"> <div class="field">
<label>Xoauth:</label> <label>Xoauth:</label>
<input v-model="xoauth" type="text"> <input v-model="xoauth" type="text" />
</div> </div>
<div class="field"> <div class="field">
<label>Xoauth2:</label> <label>Xoauth2:</label>
<input v-model="xoauth2" type="text"> <input v-model="xoauth2" type="text" />
</div> </div>
</fieldset> </fieldset>
<div class="config">
<div class="config"> <input v-model="host" id="host" type="text" placeholder="imap host" />
<input v-model="host" id="host" type="text" placeholder="imap host"> <input v-model="port" id="port" type="number" placeholder="port" />
<input v-model="port" id="port" type="number" placeholder="port"> </div>
</div> <!-- tls -->
<!-- tls --> <div>
<div> <button :disabled="error != ''" @click="addAccountRequest">Add</button>
<button :disabled="error != ''" @click="addAccountRequest">Add</button> {{ error }}
{{ error }} </div>
</div> </template>
</template> </Modal>
</Modal> </div>
</div>
</template> </template>
<style> <style>
/* Chrome, Safari, Edge, Opera */
/* Chrome, Safari, Edge, Opera */ input::-webkit-outer-spin-button,
input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
input::-webkit-inner-spin-button {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
} }
/* Firefox */ /* Firefox */
input[type=number] { input[type="number"] {
appearance: textfield; appearance: textfield;
} }
.field { .field {
margin-bottom: 5px; margin-bottom: 5px;
} }
.field > input { .field > input {
margin-top: 2px; margin-top: 2px;
width: 95%; width: 95%;
} }
fieldset { fieldset {
margin-top: 5px; margin-top: 5px;
border-radius: 5px; border-radius: 5px;
display: grid; display: grid;
} }
.config { .config {
margin: 10px 0; margin: 10px 0;
} }
#host { #host {
margin-right: 8px; margin-right: 8px;
width: calc(95% - 100px); width: calc(95% - 100px);
} }
#port { #port {
width: 70px; width: 70px;
} }
button { button {
padding: 5px; padding: 5px;
padding: 7px 18px; padding: 7px 18px;
background-color: #09a35b; background-color: #09a35b;
@ -205,13 +199,13 @@ function mailChange() {
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
transition: opacity 0.5s; transition: opacity 0.5s;
} }
button:hover { button:hover {
opacity: 0.6; opacity: 0.6;
} }
input { input {
-webkit-box-flex: 1; -webkit-box-flex: 1;
background-color: #303a46; background-color: #303a46;
border: none; border: none;
@ -224,9 +218,9 @@ function mailChange() {
font-weight: 400; font-weight: 400;
min-width: 0; min-width: 0;
padding: 8px 9px; padding: 8px 9px;
} }
input:focus { input:focus {
outline: none; outline: none;
} }
</style> </style>

View File

@ -1,38 +1,33 @@
<script setup> <script setup>
import { vOnClickOutside } from '@vueuse/components' import { vOnClickOutside } from "@vueuse/components";
import { defineEmits, defineProps } from 'vue' import { defineEmits, defineProps } from "vue";
const emit = defineEmits(['close-modal']); const emit = defineEmits(["close-modal"]);
const props = defineProps({ title: String }); const props = defineProps({ title: String });
// todo close on escape // todo close on escape
function close() { function close() {
emit('close-modal'); emit("close-modal");
} }
</script> </script>
<template> <template>
<div class="modal-wrapper"> <div class="modal-wrapper">
<div class="modal" v-on-click-outside="close"> <div class="modal" v-on-click-outside="close">
<header class="modal-header"> <header class="modal-header">
<h2>{{ props.title }}</h2> <h2>{{ props.title }}</h2>
<div class="close-button" @click="close"></div> <div class="close-button" @click="close"></div>
</header>
</header> <div class="modal-body">
<slot name="body"> This is the default body! </slot>
<div class="modal-body"> </div>
<slot name="body"> </div>
This is the default body!
</slot>
</div>
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.modal-wrapper { .modal-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
position: fixed; position: fixed;
@ -44,28 +39,28 @@ function close() {
z-index: 4000; z-index: 4000;
background-color: rgba(0, 0, 0, 0.8); background-color: rgba(0, 0, 0, 0.8);
} }
.modal { .modal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-radius: 5px; border-radius: 5px;
color: white; color: white;
background-color: #1D1D23; background-color: #1d1d23;
padding: 20px padding: 20px;
} }
.modal-header { .modal-header {
margin-bottom: 10px; margin-bottom: 10px;
} }
h2 { h2 {
display: inline-block; display: inline-block;
font-size: 2.4rem; font-size: 2.4rem;
margin: 0; margin: 0;
} }
.close-button { .close-button {
background-color: #9fa9ba; background-color: #9fa9ba;
cursor: pointer; cursor: pointer;
height: 18px; height: 18px;
@ -75,6 +70,5 @@ function close() {
width: 18px; width: 18px;
float: right; float: right;
margin-top: 5px; margin-top: 5px;
} }
</style>
</style>

View File

@ -1,29 +1,22 @@
<script setup> <script setup>
import {defineProps} from "vue"; import { defineProps } from "vue";
const props = defineProps({ room: Object }); const props = defineProps({ room: Object });
</script> </script>
<template> <template>
<div class="main"> <div class="main">
<div> <div>
{{ props }} {{ props }}
type: name / sender type: name / sender
</div> </div>
<div> <div>action: threads message important</div>
action:
threads
message important
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.main { .main {
border-bottom: 1px solid #505050; border-bottom: 1px solid #505050;
width: 100%; width: 100%;
height: 51px; height: 51px;
} }
</style> </style>

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { defineProps, onMounted, ref } from "vue"; import { defineProps, onMounted, ref } from "vue";
import { decodeEmojis } from "../../utils/string"; import { decodeEmojis } from "../../utils/string";
import DOMPurify from 'dompurify'; import DOMPurify from "dompurify";
const props = defineProps({ data: Object }); const props = defineProps({ data: Object });
const date = new Date(props.data.date); const date = new Date(props.data.date);
@ -78,7 +78,7 @@ iframe {
max-height: 300px; max-height: 300px;
width: 100%; width: 100%;
max-width: 750px; /* template width being 600px to 640px up to 750px (experiment and test) */ max-width: 750px; /* template width being 600px to 640px up to 750px (experiment and test) */
background-color: rgb(234, 234, 234);; background-color: rgb(234, 234, 234);
} }
.left, .left,

View File

@ -11,7 +11,7 @@ let { id } = route.params;
onBeforeMount(async () => { onBeforeMount(async () => {
// get data // get data
let room = store.state.rooms.find((room) => room.id === id); let room = store.state.rooms.find((room) => room.id === id);
console.log(room) console.log(room);
if (!room || room?.fetched === false) { if (!room || room?.fetched === false) {
// todo // todo
// await store.dispatch("fetchMessages", ); // await store.dispatch("fetchMessages", );
@ -29,7 +29,7 @@ onBeforeRouteUpdate(async (to, from) => {
<template> <template>
<div id="main"> <div id="main">
<Header :room="room" ></Header> <Header :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 store.getters.messages(id)" :key="index" :data="message" />
@ -68,6 +68,4 @@ onBeforeRouteUpdate(async (to, from) => {
overflow: auto; overflow: auto;
margin-bottom: 100px; margin-bottom: 100px;
} }
</style> </style>

View File

@ -1,34 +1,33 @@
<template> <template>
<div> <div>
<Accounts /> <Accounts />
<Rooms id="rooms"/> <Rooms id="rooms" />
</div> </div>
</template> </template>
<script> <script>
import Accounts from './accounts/Accounts' import Accounts from "./accounts/Accounts";
import Rooms from './rooms/Rooms.vue' import Rooms from "./rooms/Rooms.vue";
export default { export default {
name: 'Sidebar', name: "Sidebar",
components: { components: {
Accounts, Accounts,
Rooms, Rooms,
} },
} };
</script> </script>
<style scoped> <style scoped>
div { div {
display: flex; display: flex;
height: 100%; height: 100%;
} }
#rooms { #rooms {
max-width: 300px; max-width: 300px;
min-width: 250px; min-width: 250px;
} }
/* todo setup max size */ /* todo setup max size */
</style>
</style>

View File

@ -5,45 +5,42 @@
</template> </template>
<script> <script>
import { mapMutations, mapState } from 'vuex' import { mapMutations, mapState } from "vuex";
export default { export default {
name: 'Account', name: "Account",
components: { components: {},
}, props: {
props: { data: { mail: String, id: Number },
data: {mail: String, id: Number} },
}, computed: {
computed: { ...mapState(["activeAccount"]),
...mapState(['activeAccount']) },
}, methods: {
methods: { ...mapMutations(["setactiveAccount"]),
...mapMutations(['setactiveAccount']) },
} };
}
</script> </script>
<style scoped> <style scoped>
.container { .container {
width: 65px; width: 65px;
text-align: center; text-align: center;
white-space: normal; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
padding: 5px; padding: 5px;
margin: 3px 0; margin: 3px 0;
cursor: pointer; cursor: pointer;
border-radius: 8px; border-radius: 8px;
border: 2px solid transparent; border: 2px solid transparent;
} }
.container:hover { .container:hover {
background-color: aqua !important; background-color: aqua !important;
} }
.active { .active {
border: 2px solid white; border: 2px solid white;
opacity: 0.9; opacity: 0.9;
} }
</style>
</style>

View File

@ -1,37 +1,35 @@
<template> <template>
<div id="main"> <div id="main">
<div id="userMenu"> <div id="userMenu">
<!-- deconnect --> <!-- deconnect -->
</div> </div>
<span class="divider"></span> <span class="divider"></span>
<Account v-for="(account, index) in accounts" :key="index" :data="account"/> <Account v-for="(account, index) in accounts" :key="index" :data="account" />
<span class="divider"></span> <span class="divider"></span>
<AddAccountModal /> <AddAccountModal />
</div> </div>
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState } from "vuex";
import Account from './Account' import Account from "./Account";
import AddAccountModal from '../../modals/AddAccountModal' import AddAccountModal from "../../modals/AddAccountModal";
import store from '@/store/store' import store from "@/store/store";
export default { export default {
name: 'Accounts', name: "Accounts",
components: { components: {
Account, Account,
AddAccountModal AddAccountModal,
}, },
computed: { computed: {
...mapState(['accounts']) ...mapState(["accounts"]),
}, },
created() { created() {
store.dispatch('fetchAccounts'); store.dispatch("fetchAccounts");
} },
} };
</script> </script>
<style scoped> <style scoped>
@ -40,19 +38,18 @@ export default {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 5px; padding: 5px;
background-color: #2A2A33; background-color: #2a2a33;
color: white; color: white;
} }
#userMenu { #userMenu {
width: 32px; width: 32px;
height: 32px; height: 32px;
background-color: yellow !important; background-color: yellow !important;
} }
.divider { .divider {
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
margin: 5px 0; margin: 5px 0;
width: 90%; width: 90%;
} }
</style> </style>

View File

@ -1,18 +1,12 @@
<template> <template>
<div id="main"> <div id="main">jij</div>
jij
</div>
</template> </template>
<script> <script>
export default { export default {
name: 'All', name: "All",
components: { components: {},
} };
}
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<div id="main"> <div id="main">
<div id="userMenu"> <div id="userMenu">
<!-- deconnect --> <!-- deconnect -->
</div> </div>
<span class="divider"></span> <span class="divider"></span>
<!-- <h5>Accounts: </h5> --> <!-- <h5>Accounts: </h5> -->
@ -11,13 +11,10 @@
</template> </template>
<script> <script>
export default { export default {
name: 'Account', name: "Account",
components: { components: {},
} };
}
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,33 +1,30 @@
<template> <template>
<div class="test"> <div class="test">
<Room v-for="(room, index) in rooms()" :key="index" :data="room" /> <Room v-for="(room, index) in rooms()" :key="index" :data="room" />
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from "vuex";
import Room from './Room' import Room from "./Room";
export default { export default {
name: 'Rooms', name: "Rooms",
props: { props: {},
components: {
}, Room,
components: { },
Room computed: {
}, ...mapGetters(["rooms"]),
computed: { },
...mapGetters(['rooms']) };
}
}
</script> </script>
<style scoped> <style scoped>
.test { .test {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: #24242B; background-color: #24242b;
overflow: auto; overflow: auto;
} }
</style>
</style>

View File

@ -1,16 +1,15 @@
<script setup> <script setup>
import { defineProps } from 'vue' import { defineProps } from "vue";
const props = defineProps({ const props = defineProps({
thread: Object thread: Object,
}) });
console.log(props.thread) console.log(props.thread);
</script> </script>
<template> <template>
<div class="room"> <div class="room">
{{props.thread.roomName}} {{ props.thread.roomName }}
</div> </div>
</template> </template>
@ -25,8 +24,9 @@ console.log(props.thread)
color: #a9b2bc; color: #a9b2bc;
} }
.room:hover, .selected { .room:hover,
background-color: #41474f;; .selected {
background-color: #41474f;
border-radius: 8px; border-radius: 8px;
} }
@ -35,4 +35,4 @@ console.log(props.thread)
margin: 0 10px; margin: 0 10px;
content: ""; content: "";
} }
</style> </style>

43
front/tsconfig.json Normal file
View File

@ -0,0 +1,43 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
// "strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

File diff suppressed because it is too large Load Diff