dev #1
18 changed files with 366 additions and 154 deletions
11
app.vue
11
app.vue
|
@ -1,7 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
|
@ -9,10 +15,13 @@ body {
|
|||
box-sizing: border-box;
|
||||
color: rgb(190, 190, 190);
|
||||
background-color: rgb(30, 30, 30);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
*:focus-visible {
|
||||
outline: 1px solid rgb(150, 150, 150);
|
||||
}
|
||||
|
||||
a {
|
||||
color: aquamarine;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
Loading...
|
||||
<div id="loading-container">
|
||||
<Icon name="lucide:loader-circle" id="loading-circle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -8,6 +8,31 @@
|
|||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
|
||||
#loading-container {
|
||||
position: fixed;
|
||||
left: 50dvw;
|
||||
top: 50dvh;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#loading-circle {
|
||||
animation-name: spin;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="message">
|
||||
<div>
|
||||
<img v-if="props.img" class="message-author-avatar" :src="img" :alt="username">
|
||||
<img v-if="props.img" class="message-author-avatar" :src="props.img" :alt="username">
|
||||
<Icon v-else name="lucide:user" class="message-author-avatar" />
|
||||
</div>
|
||||
<div class="message-data">
|
||||
|
@ -25,7 +25,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps<{ class?: string, img?: string, username: string, text: string, timestamp: number, format: "12" | "24" }>();
|
||||
const props = defineProps<{ class?: string, img?: string | null, username: string, text: string, timestamp: number, format: "12" | "24" }>();
|
||||
|
||||
const messageDate = ref<string>();
|
||||
const showHover = ref(false);
|
||||
|
@ -74,6 +74,7 @@ if (now.getUTCHours() >= 0) {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1dvh;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.message-author {
|
||||
|
@ -84,6 +85,7 @@ if (now.getUTCHours() >= 0) {
|
|||
.message-author-avatar {
|
||||
margin-right: 1dvw;
|
||||
width: 3em;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.author-username {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<div id="message-area">
|
||||
<div id="messages" ref="messagesElement">
|
||||
<Message v-for="message of messages" :username="displayNames[message.user_uuid]" :text="message.message"
|
||||
:timestamp="uuidToTimestamp(message.uuid)" format="12" />
|
||||
<Message v-for="message of messages" :username="message.user.display_name ?? message.user.username" :text="message.message"
|
||||
:timestamp="uuidToTimestamp(message.uuid)" :img="message.user.avatar" format="12" />
|
||||
</div>
|
||||
<div id="message-box">
|
||||
<form id="message-form" @submit="sendMessage">
|
||||
<input v-model="messageInput" type="text" name="message-input" id="message-box-input">
|
||||
<button type="submit">
|
||||
<input v-model="messageInput" type="text" name="message-input" id="message-box-input" autocomplete="off">
|
||||
<button id="submit-button" type="submit">
|
||||
<Icon name="lucide:send" />
|
||||
</button>
|
||||
</form>
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import type { MessageResponse } from '~/types/interfaces';
|
||||
import fetchUser from '~/utils/fetchUser';
|
||||
import scrollToBottom from '~/utils/scrollToBottom';
|
||||
|
||||
const props = defineProps<{ channelUrl: string, amount?: number, offset?: number, reverse?: boolean }>();
|
||||
|
||||
|
@ -31,8 +31,6 @@ if (messagesRes && props.reverse) {
|
|||
|
||||
const messages = ref<MessageResponse[]>([]);
|
||||
|
||||
const displayNames = ref<Record<string, string>>({});
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const messageInput = ref<string>();
|
||||
|
@ -40,14 +38,6 @@ const messageInput = ref<string>();
|
|||
const messagesElement = ref<HTMLDivElement>();
|
||||
|
||||
if (messagesRes) messages.value = messagesRes;
|
||||
const displayNamesArr: Record<string, string> = {};
|
||||
for (const message of messages.value) {
|
||||
if (!displayNamesArr[message.user_uuid]) {
|
||||
const displayName = await getDisplayName(message.user_uuid);
|
||||
displayNamesArr[message.user_uuid] = displayName;
|
||||
}
|
||||
}
|
||||
displayNames.value = displayNamesArr;
|
||||
|
||||
const accessToken = useCookie("access_token").value;
|
||||
const apiBase = useCookie("api_base").value;
|
||||
|
@ -71,23 +61,21 @@ ws.addEventListener("open", (event) => {
|
|||
console.log("WebSocket connected!");
|
||||
});
|
||||
|
||||
ws.addEventListener("message", (event) => {
|
||||
ws.addEventListener("message", async (event) => {
|
||||
console.log("event data:", event.data);
|
||||
messages.value?.push(
|
||||
JSON.parse(event.data)
|
||||
);
|
||||
await nextTick();
|
||||
if (messagesElement.value) {
|
||||
console.log("scrolling to bottom");
|
||||
scrollToBottom(messagesElement);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await refresh();
|
||||
}
|
||||
|
||||
async function getDisplayName(memberId: string): Promise<string> {
|
||||
//const user = await fetchMember((route.params.serverId as string), memberId);
|
||||
const user = await fetchUser((route.params.serverId as string), memberId);
|
||||
return user!.display_name ?? user!.username;
|
||||
|
||||
}
|
||||
|
||||
function sendMessage(e: Event) {
|
||||
e.preventDefault();
|
||||
const text = messageInput.value;
|
||||
|
@ -100,7 +88,9 @@ function sendMessage(e: Event) {
|
|||
}
|
||||
|
||||
onMounted(async () => {
|
||||
messagesElement.value?.scrollTo({ top: messagesElement.value.scrollHeight });
|
||||
if (messagesElement.value) {
|
||||
scrollToBottom(messagesElement);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@ -108,27 +98,38 @@ onMounted(async () => {
|
|||
<style scoped>
|
||||
|
||||
#message-area {
|
||||
padding-top: 3dvh;
|
||||
}
|
||||
|
||||
#message-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 8fr 1fr;
|
||||
justify-content: space-between;
|
||||
padding-top: 3dvh;
|
||||
padding-left: 1dvw;
|
||||
padding-right: 1dvw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#message-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
border: 1px solid rgb(70, 70, 70);
|
||||
padding-bottom: 1dvh;
|
||||
padding-top: 1dvh;
|
||||
margin-bottom: 1dvh;
|
||||
margin-top: 1dvh;
|
||||
}
|
||||
|
||||
#message-input {
|
||||
width: 100%;
|
||||
#message-form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 60%;
|
||||
}
|
||||
|
||||
#message-box-input {
|
||||
width: 80%;
|
||||
background-color: rgb(50, 50, 50);
|
||||
border: none;
|
||||
}
|
||||
|
||||
#messages {
|
||||
|
@ -138,4 +139,14 @@ onMounted(async () => {
|
|||
gap: 1dvh;
|
||||
}
|
||||
|
||||
#submit-button {
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#submit-button:hover {
|
||||
background-color: rgb(40, 40, 40);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -30,11 +30,11 @@ export const useAuth = () => {
|
|||
{
|
||||
username, password: hashedPass, device_name: "Linux Laptop"
|
||||
}
|
||||
}) as { access_token: string, refresh_token: string }; fetch
|
||||
}) as { access_token: string, refresh_token: string };
|
||||
console.log("hi");
|
||||
accessToken.value = res.access_token;
|
||||
console.log("access token:", accessToken.value);
|
||||
await fetchUser();
|
||||
//await fetchUser();
|
||||
}
|
||||
|
||||
async function logout(password: string) {
|
||||
|
@ -60,19 +60,21 @@ export const useAuth = () => {
|
|||
|
||||
async function refresh() {
|
||||
console.log("refreshing");
|
||||
try {
|
||||
const res = await fetchWithApi("/auth/refresh", {
|
||||
method: "POST"
|
||||
}) as { access_token: string };
|
||||
}) as any;
|
||||
console.log("finished refreshing:", res);
|
||||
if (res && res.access_token) {
|
||||
accessToken.value = res.access_token;
|
||||
console.log("set new access token");
|
||||
} catch (error) {
|
||||
console.error("refresh error:", error);
|
||||
} else {
|
||||
console.log("refresh didn't return access token");
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUser() {
|
||||
if (!accessToken.value) return;
|
||||
console.log("fetchuser access token:", accessToken.value);
|
||||
const res = await fetchWithApi("/users/me") as UserResponse;
|
||||
user.value = res;
|
||||
return user.value;
|
||||
|
|
29
error.vue
Normal file
29
error.vue
Normal file
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<div id="error-container">
|
||||
<h2>{{ error?.statusCode }}</h2>
|
||||
<p>{{ error?.message }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { NuxtError } from '#app';
|
||||
|
||||
const props = defineProps({
|
||||
error: Object as () => NuxtError
|
||||
});
|
||||
|
||||
if (props.error?.statusCode == 401) {
|
||||
console.log("HELLO THERE");
|
||||
clearError({ redirect: `/login?redirect_to=${useRoute().fullPath}` });
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
#error-container {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<div id="root-container" style="margin-top: 5dvh;">
|
||||
<Loading v-if="!mounted" />
|
||||
<div v-else id="main-container">
|
||||
<div id="main-container">
|
||||
<div v-if="!instanceUrl">
|
||||
<div v-if="instanceError" style="color: red;">
|
||||
{{ instanceError }}
|
||||
|
@ -49,7 +48,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { FetchError } from 'ofetch';
|
||||
|
||||
const mounted = ref(false);
|
||||
const redirectTo = useRoute().query.redirect_to;
|
||||
|
||||
const apiVersion = useRuntimeConfig().public.apiVersion;
|
||||
|
@ -64,7 +62,6 @@ if (auth.accessToken.value) {
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
mounted.value = true;
|
||||
const cookie = useCookie("instance_url").value;
|
||||
instanceUrl.value = cookie;
|
||||
console.log(cookie);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div id="client-root">
|
||||
<Loading v-show="loading" />
|
||||
<div :class="{ hidden: loading, visible: !loading }" id="client-root">
|
||||
<div id="homebar">
|
||||
<div class="homebar-item">
|
||||
main bar
|
||||
|
@ -21,6 +22,8 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
|
||||
const loading = useState("loading", () => false);
|
||||
|
||||
const servers = [
|
||||
{
|
||||
name: "Test",
|
||||
|
@ -90,11 +93,21 @@ function sendMessage(e: Event) {
|
|||
<style>
|
||||
#client-root {
|
||||
/* border: 1px solid white; */
|
||||
height: 100%;
|
||||
height: 100dvh;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 4fr 18fr 4fr;
|
||||
grid-template-rows: 4dvh auto;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
.hidden {
|
||||
opacity: 0%;
|
||||
}
|
||||
|
||||
.visible {
|
||||
opacity: 100%;
|
||||
transition-duration: 500ms;
|
||||
}
|
||||
|
||||
#homebar {
|
||||
|
@ -114,7 +127,6 @@ function sendMessage(e: Event) {
|
|||
#__nuxt {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.grid-column {
|
||||
|
|
24
middleware/auth.global.ts
Normal file
24
middleware/auth.global.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||
console.log("to.fullPath:", to.fullPath);
|
||||
const loading = useState("loading");
|
||||
const accessToken = useCookie("access_token").value;
|
||||
if (["/login", "/register"].includes(to.path)) {
|
||||
if (accessToken) {
|
||||
return await navigateTo("/");
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
if (!accessToken) {
|
||||
loading.value = true;
|
||||
console.log("set loading to true");
|
||||
const { refresh } = useAuth();
|
||||
console.log("hi");
|
||||
await refresh();
|
||||
const query = new URLSearchParams();
|
||||
query.set("redirect_to", to.path);
|
||||
loading.value = false;
|
||||
console.log("set loading to false");
|
||||
return await navigateTo("/login?" + (query ?? ""));
|
||||
}
|
||||
})
|
|
@ -3,13 +3,14 @@ export default defineNuxtConfig({
|
|||
compatibilityDate: '2024-11-01',
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxt/eslint', '@nuxt/image', "@pinia/nuxt", "@nuxt/icon"],
|
||||
ssr: false,
|
||||
app: {
|
||||
/*
|
||||
Defines what prefix the client runs on
|
||||
E.g.: baseURL set to "/web" would host at https://gorb.app/web
|
||||
Default is "/" (aka root), which hosts at https://gorb.app/
|
||||
*/
|
||||
baseURL: "/web",
|
||||
baseURL: "/",
|
||||
head: {
|
||||
title: 'Gorb',
|
||||
// this is purely used to embed in that other chat app, and similar stuff
|
||||
|
|
17
pages/index.vue
Normal file
17
pages/index.vue
Normal file
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<NuxtLayout>
|
||||
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
definePageMeta({
|
||||
layout: "client"
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
</form>
|
||||
<div>
|
||||
Don't have an account? <NuxtLink href="/register">Register</NuxtLink> one!
|
||||
Don't have an account? <NuxtLink :href="registerUrl">Register</NuxtLink> one!
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
@ -34,12 +34,26 @@ const form = reactive({
|
|||
|
||||
//const authStore = useAuthStore();
|
||||
|
||||
const query = useRoute().query as Record<string, string>;
|
||||
const searchParams = new URLSearchParams(query);
|
||||
const registerUrl = `/register?${searchParams}`
|
||||
|
||||
const { login } = useAuth();
|
||||
|
||||
async function formLogin(e: Event) {
|
||||
e.preventDefault();
|
||||
console.log("Sending login data");
|
||||
try {
|
||||
await login(form.username, form.password, "Linux Laptop");
|
||||
console.log("logged in");
|
||||
if (query.redirect_to) {
|
||||
console.log("redirecting to:", query.redirect_to);
|
||||
return await navigateTo(query.redirect_to);
|
||||
}
|
||||
return await navigateTo("/");
|
||||
} catch (error) {
|
||||
console.error("Error logging in:", error);
|
||||
}
|
||||
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
</form>
|
||||
<div>
|
||||
Already have an account? <NuxtLink href="/login">Log in</NuxtLink>!
|
||||
Already have an account? <NuxtLink :href="loginUrl">Log in</NuxtLink>!
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
@ -74,7 +74,9 @@ const errorMessages = reactive({
|
|||
|
||||
//const authStore = useAuthStore();
|
||||
const auth = useAuth();
|
||||
const redirectTo = useRoute().query.redirect_to;
|
||||
const query = useRoute().query as Record<string, string>;
|
||||
const searchParams = new URLSearchParams(query);
|
||||
const loginUrl = `/login?${searchParams}`
|
||||
|
||||
onMounted(() => {
|
||||
if (auth.accessToken.value) {
|
||||
|
@ -120,7 +122,12 @@ const apiVersion = useRuntimeConfig().public.apiVersion;
|
|||
async function register(e: Event) {
|
||||
e.preventDefault();
|
||||
console.log("Sending registration data");
|
||||
try {
|
||||
await auth.register(form.username, form.email, form.password);
|
||||
return await navigateTo(query.redirect_to);
|
||||
} catch (error) {
|
||||
console.error("Error registering:", error);
|
||||
}
|
||||
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,25 +39,20 @@
|
|||
|
||||
const route = useRoute();
|
||||
|
||||
const server: GuildResponse | undefined = await fetchWithApi(`servers/${route.params.serverId}`);
|
||||
|
||||
const channels: ChannelResponse[] | undefined = await fetchWithApi(
|
||||
`servers/${route.params.serverId}/channels`
|
||||
);
|
||||
const channel: ChannelResponse | undefined = await fetchWithApi(
|
||||
route.path
|
||||
);
|
||||
const loading = useState("loading");
|
||||
|
||||
const channelUrlPath = `servers/${route.params.serverId}/channels/${route.params.channelId}`;
|
||||
|
||||
console.log("channel:", channel);
|
||||
const server = ref<GuildResponse | undefined>();
|
||||
const channels = ref<ChannelResponse[] | undefined>();
|
||||
const channel = ref<ChannelResponse | undefined>();
|
||||
|
||||
const showInvitePopup = ref(false);
|
||||
|
||||
import type { ChannelResponse, GuildResponse, MessageResponse } from "~/types/interfaces";
|
||||
|
||||
//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[];
|
||||
//console.log("servers:", servers);
|
||||
//console.log("channelid: servers:", servers);
|
||||
const members = [
|
||||
{
|
||||
id: "3287484395",
|
||||
|
@ -106,6 +101,23 @@ const members = [
|
|||
}
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
console.log("channelid: set loading to true");
|
||||
server.value = await fetchWithApi(`servers/${route.params.serverId}`);
|
||||
|
||||
channels.value = await fetchWithApi(
|
||||
`servers/${route.params.serverId}/channels`
|
||||
);
|
||||
channel.value = await fetchWithApi(
|
||||
route.path
|
||||
);
|
||||
|
||||
console.log("channelid: channel:", channel);
|
||||
loading.value = false;
|
||||
console.log("channelid: set loading to false");
|
||||
});
|
||||
|
||||
function showServerSettings() { }
|
||||
|
||||
function toggleInvitePopup(e: Event) {
|
||||
|
|
32
pages/verify-email.vue
Normal file
32
pages/verify-email.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<div id="container">
|
||||
<div v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
const errorMessage = ref();
|
||||
|
||||
const token = useRoute().query.token;
|
||||
|
||||
try {
|
||||
const res = await fetchWithApi("/auth/verify-email", { query: { token } });
|
||||
console.log("hi");
|
||||
} catch (error) {
|
||||
console.error("Error verifying email:", error);
|
||||
errorMessage.value = error;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
#container {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -32,10 +32,11 @@ export interface ChannelResponse {
|
|||
}
|
||||
|
||||
export interface MessageResponse {
|
||||
uuid: string
|
||||
channel_uuid: string
|
||||
user_uuid: string
|
||||
message: string
|
||||
uuid: string,
|
||||
channel_uuid: string,
|
||||
user_uuid: string,
|
||||
message: string,
|
||||
user: UserResponse
|
||||
}
|
||||
|
||||
export interface InviteResponse {
|
||||
|
@ -49,6 +50,6 @@ export interface UserResponse {
|
|||
username: string,
|
||||
display_name: string | null,
|
||||
avatar: string | null,
|
||||
email: string,
|
||||
email_verified: boolean
|
||||
email?: string,
|
||||
email_verified?: boolean
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ export default async <T>(path: string, options: NitroFetchOptions<string> = {})
|
|||
path = path.slice(0, path.lastIndexOf("/"));
|
||||
}
|
||||
console.log("formatted path:", path);
|
||||
try {
|
||||
const accessToken = useCookie("access_token");
|
||||
console.log("access token:", accessToken.value);
|
||||
const apiBase = useCookie("api_base").value;
|
||||
|
@ -49,23 +48,35 @@ export default async <T>(path: string, options: NitroFetchOptions<string> = {})
|
|||
|
||||
return res;
|
||||
} catch (error: any) {
|
||||
console.error("Error fetching resource");
|
||||
if (error?.response?.status === 401) {
|
||||
console.log("Error status is 401");
|
||||
if (!path.startsWith("/auth/refresh")) {
|
||||
console.log("Path is not refresh endpoint");
|
||||
try {
|
||||
console.log("Trying to refresh");
|
||||
await refresh();
|
||||
console.log("Successfully refreshed token");
|
||||
} catch (error: any) {
|
||||
console.log("Failed to refresh token");
|
||||
if (error?.response?.status === 401) {
|
||||
console.log("Refresh returned 401");
|
||||
reauthFailed = true;
|
||||
console.log("Revoking");
|
||||
await revoke();
|
||||
console.log("Redirecting to login");
|
||||
await navigateTo("/login");
|
||||
console.log("redirected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("Path is refresh endpoint, throwing error");
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("error:", error);
|
||||
console.log("throwing error");
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
utils/scrollToBottom.ts
Normal file
6
utils/scrollToBottom.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default (element: Ref<HTMLElement | undefined, HTMLElement | undefined>) => {
|
||||
if (element.value) {
|
||||
element.value.scrollTo({ top: element.value.scrollHeight });
|
||||
return;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue