🔨 Add snakbar
This commit is contained in:
parent
157f4c615c
commit
ec9a9b59e1
File diff suppressed because one or more lines are too long
3
.gitignore
vendored
3
.gitignore
vendored
@ -20,6 +20,7 @@ coverage
|
|||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
!.vscode/settings.json
|
||||||
.idea
|
.idea
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
@ -28,3 +29,5 @@ coverage
|
|||||||
*.sw?
|
*.sw?
|
||||||
.yarn/cache
|
.yarn/cache
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
.eslintcache
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"johnsoncodehk.volar",
|
"vue.volar",
|
||||||
"johnsoncodehk.vscode-typescript-vue-plugin",
|
|
||||||
"arcanis.vscode-zipfs",
|
"arcanis.vscode-zipfs",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"esbenp.prettier-vscode"
|
"esbenp.prettier-vscode"
|
||||||
|
17
.vscode/settings.json
vendored
Normal file
17
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"search.exclude": {
|
||||||
|
"**/.yarn": true,
|
||||||
|
"**/.pnp.*": true,
|
||||||
|
"**/node_modules": true,
|
||||||
|
},
|
||||||
|
"typescript.inlayHints.enumMemberValues.enabled": true,
|
||||||
|
"typescript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||||
|
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
|
||||||
|
"typescript.inlayHints.parameterNames.enabled": "literals",
|
||||||
|
"typescript.inlayHints.parameterTypes.enabled": true,
|
||||||
|
"javascript.inlayHints.enumMemberValues.enabled": true,
|
||||||
|
"javascript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||||
|
"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
|
||||||
|
"javascript.inlayHints.parameterNames.enabled": "literals",
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "5.9.55",
|
"@mdi/font": "5.9.55",
|
||||||
"@types/js-cookie": "^3.0.2",
|
"@types/js-cookie": "^3.0.2",
|
||||||
|
"@types/uuid": "^8.3.4",
|
||||||
"@vue/cli": "^5.0.4",
|
"@vue/cli": "^5.0.4",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"jose": "^4.8.1",
|
"jose": "^4.8.1",
|
||||||
@ -24,6 +25,7 @@
|
|||||||
"pinia": "^2.0.13",
|
"pinia": "^2.0.13",
|
||||||
"pinia-plugin-persistedstate": "^1.6.0",
|
"pinia-plugin-persistedstate": "^1.6.0",
|
||||||
"roboto-fontface": "*",
|
"roboto-fontface": "*",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
"vue": "^3.2.33",
|
"vue": "^3.2.33",
|
||||||
"vue-router": "^4.0.14",
|
"vue-router": "^4.0.14",
|
||||||
"vuetify": "^3.0.0-beta.2",
|
"vuetify": "^3.0.0-beta.2",
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
<header-component />
|
<header-component />
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container>
|
<v-container>
|
||||||
<router-view />
|
<router-view v-slot="{ Component }">
|
||||||
|
<v-fade-transition>
|
||||||
|
<component :is="Component" />
|
||||||
|
</v-fade-transition>
|
||||||
|
</router-view>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
<v-app-bar-title class="text-yellow font-weight-black text-h4">
|
<v-app-bar-title class="text-yellow font-weight-black text-h4">
|
||||||
Claptrap Bot
|
Claptrap Bot
|
||||||
</v-app-bar-title>
|
</v-app-bar-title>
|
||||||
<v-btn icon v-if="isLoggedIn">
|
|
||||||
<v-icon>mdi-exit-to-app</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-navigation-drawer expand-on-hover rail position="right" v-if="isLoggedIn">
|
<v-navigation-drawer expand-on-hover rail position="right" v-if="isLoggedIn">
|
||||||
<v-list class="overflow-hidden">
|
<v-list class="overflow-hidden">
|
||||||
@ -16,39 +13,57 @@
|
|||||||
>
|
>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-list-item-avatar end>
|
<v-list-item-avatar end>
|
||||||
<v-btn icon="mdi-exit-to-app"></v-btn>
|
<v-btn
|
||||||
|
icon="mdi-exit-to-app"
|
||||||
|
class="text-high-emphasis text-white"
|
||||||
|
v-on:click="logout(false, false)"
|
||||||
|
></v-btn>
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-list nav>
|
<v-list nav>
|
||||||
<v-list-item
|
<v-list-item>
|
||||||
prepend-icon="mdi-folder"
|
<v-list-item-avatar :start="true">
|
||||||
title="My Files"
|
<v-progress-circular
|
||||||
value="myfiles"
|
indeterminate
|
||||||
></v-list-item>
|
color="primary"
|
||||||
<v-list-item
|
></v-progress-circular>
|
||||||
prepend-icon="mdi-account-multiple"
|
</v-list-item-avatar>
|
||||||
title="Shared with me"
|
<v-list-item-subtitle> Loading... </v-list-item-subtitle>
|
||||||
value="shared"
|
</v-list-item>
|
||||||
></v-list-item>
|
|
||||||
<v-list-item
|
|
||||||
prepend-icon="mdi-star"
|
|
||||||
title="Starred"
|
|
||||||
value="starred"
|
|
||||||
></v-list-item>
|
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
|
<v-snackbar
|
||||||
<v-main style="height: 250px"></v-main>
|
v-for="(data, index) of snacks"
|
||||||
|
:key="data.uuid"
|
||||||
|
v-model="data.snack"
|
||||||
|
top="true"
|
||||||
|
left="true"
|
||||||
|
:style="{ 'margin-top': calcMargin(index) }"
|
||||||
|
:color="data.color"
|
||||||
|
>
|
||||||
|
<div>{{ data.text }}</div>
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn
|
||||||
|
variant="text"
|
||||||
|
icon="mdi-close"
|
||||||
|
@click="data.snack = false"
|
||||||
|
></v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
|
import { logout } from "@/services/authService";
|
||||||
|
import { useEventQueuStore } from "@/stores/eventQueu";
|
||||||
|
import { ref, watch } from "vue";
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const eventQueuStore = useEventQueuStore();
|
||||||
|
|
||||||
const { userName, avatar, discriminator, isLoggedIn } = storeToRefs(userStore);
|
const { userName, avatar, discriminator, isLoggedIn } = storeToRefs(userStore);
|
||||||
|
|
||||||
@ -56,6 +71,51 @@ function getAvatar() {
|
|||||||
const avatarBaseUrl = import.meta.env.VITE_DISCORD_USER_AVATAR_URL;
|
const avatarBaseUrl = import.meta.env.VITE_DISCORD_USER_AVATAR_URL;
|
||||||
return avatarBaseUrl + userStore.discordId + "/" + avatar.value + ".png";
|
return avatarBaseUrl + userStore.discordId + "/" + avatar.value + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calcMargin(i: number) {
|
||||||
|
return i * 60 + 70 + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
let snacks = ref<
|
||||||
|
{ text: string; color: string; snack: boolean; uuid: string }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
eventQueuStore.$subscribe((mutation, state) => {
|
||||||
|
if (state.events.length != 0) {
|
||||||
|
let event = eventQueuStore.shift();
|
||||||
|
if (event?.uuid) {
|
||||||
|
snacks.value.push({
|
||||||
|
snack: true,
|
||||||
|
text: event.text,
|
||||||
|
color: event.type,
|
||||||
|
uuid: event.uuid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
snacks,
|
||||||
|
(newValue, oldValue) => {
|
||||||
|
let filtered = snacks.value.filter((value) => value.snack);
|
||||||
|
if (JSON.stringify(filtered) != JSON.stringify(snacks.value)) {
|
||||||
|
snacks.value = filtered;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eventQueuStore.size != 0) {
|
||||||
|
let event = eventQueuStore.shift();
|
||||||
|
if (event?.uuid) {
|
||||||
|
snacks.value.push({
|
||||||
|
snack: true,
|
||||||
|
text: event.text,
|
||||||
|
color: event.type,
|
||||||
|
uuid: event.uuid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { useEventQueuStore } from "@/stores/eventQueu";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
async function login(code: string): Promise<boolean> {
|
async function login(code: string): Promise<boolean> {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@ -21,16 +23,45 @@ async function login(code: string): Promise<boolean> {
|
|||||||
|
|
||||||
userStore.loginFail = false;
|
userStore.loginFail = false;
|
||||||
console.log("Loggin success !");
|
console.log("Loggin success !");
|
||||||
|
const eventQueuStore = useEventQueuStore();
|
||||||
|
eventQueuStore.push({
|
||||||
|
uuid: undefined,
|
||||||
|
type: "success",
|
||||||
|
text: "You are now logged in",
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (reason) {
|
} catch (reason) {
|
||||||
console.log("Loggin fail !");
|
console.log("Loggin fail !");
|
||||||
console.log(reason);
|
console.log(reason);
|
||||||
userStore.token = "";
|
logout(false, true);
|
||||||
userStore.userName = "";
|
|
||||||
userStore.discordId = "";
|
const eventQueuStore = useEventQueuStore();
|
||||||
userStore.loginFail = true;
|
eventQueuStore.push({
|
||||||
|
uuid: undefined,
|
||||||
|
type: "error",
|
||||||
|
text: "Login fail, Please try aguain.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { login };
|
function logout(expired: boolean, loginFail: boolean): void {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
userStore.token = "";
|
||||||
|
userStore.userName = "";
|
||||||
|
userStore.discordId = "";
|
||||||
|
userStore.loginFail = loginFail;
|
||||||
|
userStore.asExpired = expired;
|
||||||
|
|
||||||
|
const eventQueuStore = useEventQueuStore();
|
||||||
|
eventQueuStore.push({
|
||||||
|
uuid: undefined,
|
||||||
|
type: "success",
|
||||||
|
text: "Disconnected",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { login, logout };
|
||||||
|
31
src/stores/eventQueu.ts
Normal file
31
src/stores/eventQueu.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
|
interface Event {
|
||||||
|
uuid: string | undefined;
|
||||||
|
type: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useEventQueuStore = defineStore({
|
||||||
|
id: "eventQueu",
|
||||||
|
state: () => ({
|
||||||
|
events: [] as Array<Event>,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
size: (state) => {
|
||||||
|
return state.events.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
shift(): Event | undefined {
|
||||||
|
return this.events.shift();
|
||||||
|
},
|
||||||
|
push(event: Event) {
|
||||||
|
if (!event.uuid) {
|
||||||
|
event.uuid = uuidv4();
|
||||||
|
}
|
||||||
|
this.events.push(event);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -1,6 +1,5 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import * as jose from "jose";
|
import * as jose from "jose";
|
||||||
import axios from "axios";
|
|
||||||
import { cookiesStorage } from "./coockiesStorage";
|
import { cookiesStorage } from "./coockiesStorage";
|
||||||
|
|
||||||
export const useUserStore = defineStore("user", {
|
export const useUserStore = defineStore("user", {
|
||||||
@ -11,6 +10,7 @@ export const useUserStore = defineStore("user", {
|
|||||||
avatar: "",
|
avatar: "",
|
||||||
token: "",
|
token: "",
|
||||||
loginFail: false,
|
loginFail: false,
|
||||||
|
asExpired: false,
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
isLoggedIn(): boolean {
|
isLoggedIn(): boolean {
|
||||||
@ -22,12 +22,13 @@ export const useUserStore = defineStore("user", {
|
|||||||
isExpired(): boolean {
|
isExpired(): boolean {
|
||||||
if (this.getTokenPayload?.exp) {
|
if (this.getTokenPayload?.exp) {
|
||||||
const exp = new Date(this.getTokenPayload.exp * 1000);
|
const exp = new Date(this.getTokenPayload.exp * 1000);
|
||||||
return exp < new Date();
|
const expired = exp < new Date();
|
||||||
|
this.asExpired = expired;
|
||||||
|
return expired;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
getTokenPayload: (state) => {
|
getTokenPayload: (state) => {
|
||||||
const token = state.token;
|
|
||||||
return jose.decodeJwt(state.token);
|
return jose.decodeJwt(state.token);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-row v-if="loginFail" titl>
|
<v-row v-if="!userStore.isLoggedIn">
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-alert title="Login Fail" closable type="error" density="compact">
|
<v-btn to="/oauth2/redirect" prepend-icon="mdi-discord" color="primary">
|
||||||
Login fail, please try again.
|
Login
|
||||||
</v-alert>
|
</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import { storeToRefs } from "pinia";
|
import { onBeforeMount } from "vue";
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { loginFail } = storeToRefs(userStore);
|
onBeforeMount(() => {
|
||||||
|
userStore.isLoggedIn;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -2026,6 +2026,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/uuid@npm:^8.3.4":
|
||||||
|
version: 8.3.4
|
||||||
|
resolution: "@types/uuid@npm:8.3.4"
|
||||||
|
checksum: 6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/webfontloader@npm:^1.0.0":
|
"@types/webfontloader@npm:^1.0.0":
|
||||||
version: 1.6.34
|
version: 1.6.34
|
||||||
resolution: "@types/webfontloader@npm:1.6.34"
|
resolution: "@types/webfontloader@npm:1.6.34"
|
||||||
@ -3609,6 +3616,7 @@ __metadata:
|
|||||||
"@types/js-cookie": ^3.0.2
|
"@types/js-cookie": ^3.0.2
|
||||||
"@types/jsdom": ^16.2.14
|
"@types/jsdom": ^16.2.14
|
||||||
"@types/node": ^16.11.27
|
"@types/node": ^16.11.27
|
||||||
|
"@types/uuid": ^8.3.4
|
||||||
"@types/webfontloader": ^1.0.0
|
"@types/webfontloader": ^1.0.0
|
||||||
"@vitejs/plugin-vue": ^2.3.1
|
"@vitejs/plugin-vue": ^2.3.1
|
||||||
"@vue/cli": ^5.0.4
|
"@vue/cli": ^5.0.4
|
||||||
@ -3633,6 +3641,7 @@ __metadata:
|
|||||||
sass: ^1.38.0
|
sass: ^1.38.0
|
||||||
sass-loader: ^10.0.0
|
sass-loader: ^10.0.0
|
||||||
typescript: ~4.6.3
|
typescript: ~4.6.3
|
||||||
|
uuid: ^8.3.2
|
||||||
vite: ^2.9.5
|
vite: ^2.9.5
|
||||||
vitest: ^0.9.3
|
vitest: ^0.9.3
|
||||||
vue: ^3.2.33
|
vue: ^3.2.33
|
||||||
|
Loading…
Reference in New Issue
Block a user