Skip to content

Commit 30f5369

Browse files
AchoArnoldCopilot
andcommitted
feat(web): port layouts (default, website, error) to Nuxt 4
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 292699f commit 30f5369

3 files changed

Lines changed: 411 additions & 0 deletions

File tree

web/app/layouts/default.vue

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<script setup lang="ts">
2+
import Pusher from "pusher-js";
3+
import { useDisplay } from "vuetify";
4+
import { setAuthHeader } from "~/composables/useApi";
5+
import { getAuth } from "firebase/auth";
6+
7+
const route = useRoute();
8+
const config = useRuntimeConfig();
9+
const { lgAndUp } = useDisplay();
10+
const authStore = useAuthStore();
11+
const phonesStore = usePhonesStore();
12+
const threadsStore = useThreadsStore();
13+
const appStore = useAppStore();
14+
15+
let poller: ReturnType<typeof setInterval> | null = null;
16+
let canPoll = false;
17+
18+
const hasDrawer = computed(() => {
19+
return ["threads", "threads-id"].includes((route.name as string) ?? "");
20+
});
21+
22+
onMounted(() => {
23+
setTimeout(() => {
24+
const pusher = new Pusher(config.public.pusherKey as string, {
25+
cluster: config.public.pusherCluster as string,
26+
});
27+
28+
if (authStore.authUser) {
29+
const channel = pusher.subscribe(authStore.authUser.id);
30+
channel.bind("phone.updated", () => {
31+
canPoll = true;
32+
});
33+
}
34+
35+
startPoller();
36+
}, 10_000);
37+
});
38+
39+
onBeforeUnmount(() => {
40+
if (poller) clearInterval(poller);
41+
});
42+
43+
function startPoller() {
44+
poller = setInterval(async () => {
45+
if (!canPoll || authStore.authUser == null) return;
46+
47+
appStore.setPolling(true);
48+
49+
if (authStore.authUser && phonesStore.owner) {
50+
const auth = getAuth();
51+
const token = await auth.currentUser?.getIdToken();
52+
if (token) setAuthHeader(token);
53+
54+
await Promise.all([
55+
phonesStore.loadPhones(true),
56+
threadsStore.loadThreads(),
57+
phonesStore.getHeartbeat(),
58+
]);
59+
}
60+
61+
canPoll = false;
62+
setTimeout(() => appStore.setPolling(false), 1000);
63+
}, 10_000);
64+
}
65+
</script>
66+
67+
<template>
68+
<v-app>
69+
<v-divider v-if="appStore.isLocal" class="py-1 bg-warning" />
70+
<v-navigation-drawer v-if="lgAndUp && hasDrawer" :width="400" permanent>
71+
<template #prepend>
72+
<v-divider v-if="appStore.isLocal" class="py-1 bg-warning" />
73+
<MessageThreadHeader />
74+
<div class="overflow-y-auto v-navigation-drawer__message-thread">
75+
<MessageThread />
76+
</div>
77+
</template>
78+
</v-navigation-drawer>
79+
<v-main :class="{ 'has-drawer': hasDrawer && lgAndUp }">
80+
<Toast />
81+
<slot v-if="authStore.authStateChanged" />
82+
<LoadingDashboard v-else />
83+
</v-main>
84+
</v-app>
85+
</template>
86+
87+
<style lang="scss">
88+
.v-application {
89+
.w-full {
90+
width: 100%;
91+
}
92+
.h-full {
93+
height: 100%;
94+
}
95+
.has-drawer {
96+
.v-snackbar {
97+
padding-left: 400px;
98+
}
99+
}
100+
.v-navigation-drawer__message-thread {
101+
height: calc(100vh - 120px);
102+
&::-webkit-scrollbar {
103+
width: 8px;
104+
}
105+
&::-webkit-scrollbar-track {
106+
background: #363636;
107+
}
108+
&::-webkit-scrollbar-thumb {
109+
background: #666666;
110+
border-radius: 8px;
111+
}
112+
}
113+
code.hljs {
114+
font-size: 16px;
115+
}
116+
}
117+
</style>

web/app/layouts/error.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
const props = defineProps<{
3+
error: {
4+
statusCode: number;
5+
message: string;
6+
};
7+
}>();
8+
</script>
9+
10+
<template>
11+
<v-app>
12+
<v-main>
13+
<v-container class="text-center mt-16">
14+
<h1 class="text-h1">{{ error.statusCode }}</h1>
15+
<p class="text-h5 mt-4">{{ error.message }}</p>
16+
<v-btn color="primary" class="mt-8" to="/">Go Home</v-btn>
17+
</v-container>
18+
</v-main>
19+
</v-app>
20+
</template>

0 commit comments

Comments
 (0)