feat(auth): email login & profile (#1745)
Co-authored-by: Liet Blue <127093491+lietblue@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { signOut } from '@proj-airi/stage-ui/libs/auth'
|
||||
import { useAuthStore } from '@proj-airi/stage-ui/stores/auth'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
@@ -14,6 +14,16 @@ const userAvatar = computed(() => user.value?.image)
|
||||
const showDropdown = ref(false)
|
||||
const dropdownRef = ref(null)
|
||||
|
||||
// Fall back to the user-icon placeholder when the avatar URL fails to load
|
||||
// (broken/expired host, network error, hot-linked image taken down). Without
|
||||
// this the header pill renders the browser's default broken-image glyph,
|
||||
// which looks worse than the explicit placeholder we already ship.
|
||||
// Reset on URL change so a fixed URL re-attempts loading.
|
||||
const avatarLoadError = ref(false)
|
||||
watch(userAvatar, () => { avatarLoadError.value = false })
|
||||
|
||||
const formattedCredits = computed(() => credits.value.toLocaleString())
|
||||
|
||||
onClickOutside(dropdownRef, () => {
|
||||
showDropdown.value = false
|
||||
})
|
||||
@@ -57,9 +67,11 @@ onClickOutside(dropdownRef, () => {
|
||||
@click="showDropdown = !showDropdown"
|
||||
>
|
||||
<img
|
||||
v-if="userAvatar"
|
||||
v-if="userAvatar && !avatarLoadError"
|
||||
:src="userAvatar"
|
||||
:alt="userName"
|
||||
class="h-7 w-7 rounded-full object-cover"
|
||||
@error="avatarLoadError = true"
|
||||
>
|
||||
<div
|
||||
v-else
|
||||
@@ -98,11 +110,20 @@ onClickOutside(dropdownRef, () => {
|
||||
</p>
|
||||
<div class="mt-1 flex items-center gap-1.5 text-xs text-primary-600 font-medium dark:text-primary-400">
|
||||
<div class="i-solar:battery-charge-bold-duotone text-sm" />
|
||||
<span>{{ credits }} Flux</span>
|
||||
<span>{{ formattedCredits }} Flux</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<RouterLink
|
||||
to="/settings/account"
|
||||
class="group w-full flex items-center gap-3 rounded-lg px-3 py-2 text-sm text-neutral-700 transition hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
|
||||
@click="showDropdown = false"
|
||||
>
|
||||
<div class="i-solar:user-id-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
|
||||
Profile
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink
|
||||
to="/settings/flux"
|
||||
class="group w-full flex items-center gap-3 rounded-lg px-3 py-2 text-sm text-neutral-700 transition hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { isStageTamagotchi } from '@proj-airi/stage-shared'
|
||||
import { LoginDrawer, PageHeader } from '@proj-airi/stage-ui/components'
|
||||
import { useBreakpoints } from '@proj-airi/stage-ui/composables'
|
||||
import { useAuthStore } from '@proj-airi/stage-ui/stores/auth'
|
||||
import { PageHeader } from '@proj-airi/stage-ui/components'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
|
||||
import { useTheme } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterView, useRoute } from 'vue-router'
|
||||
@@ -15,8 +12,6 @@ import HeaderLink from '../components/Layouts/HeaderLink.vue'
|
||||
import { themeColorFromValue, useThemeColor } from '../composables/theme-color'
|
||||
|
||||
const route = useRoute()
|
||||
const { isMobile } = useBreakpoints()
|
||||
const { needsLogin } = storeToRefs(useAuthStore())
|
||||
const { isDark: dark } = useTheme()
|
||||
const { t } = useI18n()
|
||||
const providersStore = useProvidersStore()
|
||||
@@ -111,9 +106,4 @@ onMounted(() => updateThemeColor())
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LoginDrawer
|
||||
v-if="isMobile"
|
||||
v-model:open="needsLogin"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { LoginDrawer } from '@proj-airi/stage-ui/components'
|
||||
import { useBreakpoints } from '@proj-airi/stage-ui/composables'
|
||||
import { useAuthStore } from '@proj-airi/stage-ui/stores/auth'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
const { isMobile } = useBreakpoints()
|
||||
const { needsLogin } = storeToRefs(useAuthStore())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main h-full font-cute>
|
||||
<RouterView />
|
||||
|
||||
<LoginDrawer
|
||||
v-if="isMobile"
|
||||
v-model:open="needsLogin"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user