diff --git a/app.vue b/app.vue index c6b132ea..f378be87 100644 --- a/app.vue +++ b/app.vue @@ -1,21 +1,8 @@ <script setup> -import { APP_NAME } from './constants' - -const isDev = process.dev -const isPreview = window.location.hostname.includes('deploy-preview') - -useHead({ - titleTemplate: title => `${title ? `${title} | ` : ''}${APP_NAME}${isDev ? ' (dev)' : isPreview ? ' (preview)' : ''}`, - link: [ - { rel: 'icon', type: 'image/svg+png', href: isDev || isPreview ? '/favicon-dev.png' : '/favicon.png' }, - ], -}) +usePageHeader() // We want to trigger rerendering the page when account changes const key = computed(() => useMasto().instances.config.url || 'default') - -// eslint-disable-next-line no-unused-expressions -isDark.value </script> <template> @@ -27,19 +14,3 @@ isDark.value id="teleport-end" /> </template> - -<style> -html, body , #__nuxt{ - height: 100vh; - margin: 0; - padding: 0; -} - -html.dark { - color-scheme: dark; -} - -html { - --at-apply: bg-base text-base; -} -</style> diff --git a/composables/page-header.ts b/composables/page-header.ts new file mode 100644 index 00000000..dce199d7 --- /dev/null +++ b/composables/page-header.ts @@ -0,0 +1,16 @@ +import { APP_NAME } from '~/constants' + +const isDev = process.dev +const isPreview = window.location.hostname.includes('deploy-preview') + +export function usePageHeader() { + useHead({ + titleTemplate: title => `${title ? `${title} | ` : ''}${APP_NAME}${isDev ? ' (dev)' : isPreview ? ' (preview)' : ''}`, + link: [ + { rel: 'icon', type: 'image/svg+png', href: isDev || isPreview ? '/favicon-dev.png' : '/favicon.png' }, + ], + }) + + // eslint-disable-next-line no-unused-expressions + isDark.value +} diff --git a/error.vue b/error.vue new file mode 100644 index 00000000..bc5bd593 --- /dev/null +++ b/error.vue @@ -0,0 +1,57 @@ +<script setup lang="ts"> +import type { NuxtError } from '#app' + +// prevent reactive update when clearing error +const { error } = defineProps<{ + error: Partial<NuxtError> +}>() + +usePageHeader() + +// add more custom status codes messages here +const errorCodes: Record<number, string> = { + 404: 'Page not found', +} + +const defaultMessage = 'Something went wrong' + +const message = error.message ?? errorCodes[error.statusCode!] ?? defaultMessage + +const state = ref<'error' | 'reloading'>('error') +const reload = async () => { + state.value = 'reloading' + try { + if (!useMasto()) + await loginTo(currentUser.value) + clearError({ redirect: currentUser.value ? '/home' : '/public' }) + } + catch { + state.value = 'error' + } +} +</script> + +<template> + <NuxtLoadingIndicator color="repeating-linear-gradient(to right,var(--c-primary) 0%,var(--c-primary-active) 100%)" /> + <NuxtLayout> + <MainContent> + <template #title> + <span text-lg font-bold>Error</span> + </template> + <slot> + <form p5 grid gap-y-4 @submit="reload"> + <div text-lg> + Something went wrong + </div> + <div text-secondary> + {{ message }} + </div> + <button flex items-center gap-2 justify-center btn-solid text-center :disabled="state === 'reloading'"> + <span v-if="state === 'reloading'" i-ri:loader-2-fill animate-spin inline-block /> + {{ state === 'reloading' ? 'Reloading' : 'Reload' }} + </button> + </form> + </slot> + </MainContent> + </NuxtLayout> +</template> diff --git a/nuxt.config.ts b/nuxt.config.ts index af7efcf4..070c162a 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -55,6 +55,12 @@ export default defineNuxtConfig({ translateApi: '', }, }, + nitro: { + prerender: { + crawlLinks: false, + routes: ['/200.html'], + }, + }, app: { keepalive: true, }, diff --git a/plugins/masto.ts b/plugins/masto.ts index 875a2f3b..35dabe40 100644 --- a/plugins/masto.ts +++ b/plugins/masto.ts @@ -2,6 +2,7 @@ import type { MastoClient } from 'masto' import { currentUser } from '../composables/users' export default defineNuxtPlugin(async () => { + let masto!: MastoClient try { const { query } = useRoute() const user = typeof query.server === 'string' && typeof query.token === 'string' @@ -9,23 +10,22 @@ export default defineNuxtPlugin(async () => { : currentUser.value // TODO: improve upstream to make this synchronous (delayed auth) - const masto = await loginTo(user) as MastoClient - - return { - provide: { - masto: shallowReactive({ - replace(api: MastoClient) { this.api = api }, - api: masto, - }), - }, - } + masto = await loginTo(user) } catch { - // TODO: handle error // Show error page when Mastodon server is down - throw createError({ + showError({ fatal: true, statusMessage: 'Could not log into account.', }) } + + return { + provide: { + masto: shallowReactive({ + replace(api: MastoClient) { this.api = api }, + api: masto, + }), + }, + } }) diff --git a/styles/global.css b/styles/global.css index 81590eb9..ef78c278 100644 --- a/styles/global.css +++ b/styles/global.css @@ -103,3 +103,17 @@ html { background-position: 0 50% } } + +html, body , #__nuxt{ + height: 100vh; + margin: 0; + padding: 0; +} + +html.dark { + color-scheme: dark; +} + +html { + --at-apply: bg-base text-base; +}