♻️ Moved the components to use naive ui + daisyui

This commit is contained in:
2025-11-27 00:02:21 +08:00
parent 0ed0dbcab0
commit 8af7037b24
18 changed files with 324 additions and 308 deletions

View File

@@ -1,9 +1,9 @@
<template>
<div class="d-flex justify-center">
<div class="flex justify-center">
<div v-if="provider === 'cloudflare'">
<turnstile v-if="!!apiKey" :sitekey="apiKey" @callback="handleSuccess" />
<div v-else class="mx-auto">
<v-progress-circular indeterminate />
<n-spin />
</div>
</div>
<div v-else-if="provider === 'hcaptcha'">
@@ -13,7 +13,7 @@
@verify="(tk: string) => handleSuccess(tk)"
/>
<div v-else class="mx-auto">
<v-progress-circular indeterminate />
<n-spin />
</div>
</div>
<div
@@ -21,8 +21,8 @@
class="h-captcha"
:data-sitekey="apiKey"
/>
<div v-else class="d-flex flex-column align-center justify-center gap-1">
<v-icon size="32"> mdi-alert-circle-outline </v-icon>
<div v-else class="flex flex-col items-center justify-center gap-1">
<span class="mdi mdi-alert-circle-outline text-3xl"></span>
<span>Captcha provider not configured correctly.</span>
</div>
</div>

View File

@@ -1,30 +1,17 @@
<template>
<v-container class="footer">
<div class="d-flex justify-space-between align-center">
<v-select
:items="['English (United States)']"
model-value="English (United States)"
variant="plain"
density="compact"
hide-details
<div class="container absolute bottom-5 left-1/2 -translate-x-1/2">
<div class="flex justify-between items-center">
<n-select
:options="[{ label: 'English (United States)', value: 'en-us' }]"
model-value="en-us"
size="small"
class="flex-grow-0"
/>
<div class="d-flex">
<v-btn variant="text" size="small" class="text-capitalize">Help</v-btn>
<v-btn variant="text" size="small" class="text-capitalize"
>Privacy</v-btn
>
<v-btn variant="text" size="small" class="text-capitalize">Terms</v-btn>
<div class="flex">
<n-button text size="small">Help</n-button>
<n-button text size="small">Privacy</n-button>
<n-button text size="small">Terms</n-button>
</div>
</div>
</v-container>
</div>
</template>
<style scoped>
.footer {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
</style>

View File

@@ -1,19 +1,20 @@
<template>
<div class="flex flex-col gap-2">
<pub-select v-model:value="publisher" />
<v-textarea
v-model="content"
<n-input
v-model:value="content"
type="textarea"
placeholder="What's happended?!"
@keydown.meta.enter.exact="submit"
@keydown.ctrl.enter.exact="submit"
/>
<div class="flex justify-between">
<v-btn type="primary" :loading="submitting" @click="submit">
<n-button type="primary" :loading="submitting" @click="submit">
Post
<template #append>
<v-icon>mdi-send</v-icon>
<template #icon>
<span class="mdi mdi-send"></span>
</template>
</v-btn>
</n-button>
</div>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<v-card :flat="props.flat">
<v-card-text :style="props.slim ? 'padding: 0' : null">
<div :class="['card', { 'shadow-none': props.flat }]">
<div :class="['card-body', { 'p-0': props.slim }]">
<div :class="['flex flex-col', compact ? 'gap-1' : 'gap-3']">
<post-header :item="props.item" :compact="compact" />
@@ -21,50 +21,42 @@
</article>
<template v-if="showReferenced">
<div v-if="props.item.repliedPost || props.item.repliedGone">
<v-card
title="Replying to"
prepend-icon="mdi-reply"
density="compact"
<div v-if="props.item.repliedPost || props.item.repliedGone" class="border rounded-md">
<div class="p-2 flex items-center gap-2">
<span class="mdi mdi-reply"></span>
<span class="font-bold">Replying to</span>
</div>
<div v-if="props.item.repliedGone" class="px-4 pb-3 text-sm opacity-60">
Post unavailable
</div>
<post-item
v-else-if="props.item.repliedPost"
class="px-4 pb-3"
:item="props.item.repliedPost"
slim
compact
flat
border
>
<div v-if="props.item.repliedGone" class="px-4 pb-3 text-sm opacity-60">
Post unavailable
</div>
<post-item
v-else-if="props.item.repliedPost"
class="px-4 pb-3"
:item="props.item.repliedPost"
slim
compact
flat
@react="handleReaction"
/>
</v-card>
@react="handleReaction"
/>
</div>
<div v-if="props.item.forwardedPost || props.item.forwardedGone">
<v-card
title="Forwarded"
prepend-icon="mdi-forward"
density="compact"
<div v-if="props.item.forwardedPost || props.item.forwardedGone" class="border rounded-md">
<div class="p-2 flex items-center gap-2">
<span class="mdi mdi-forward"></span>
<span class="font-bold">Forwarded</span>
</div>
<div v-if="props.item.forwardedGone" class="px-4 pb-3 text-sm opacity-60">
Post unavailable
</div>
<post-item
v-else-if="props.item.forwardedPost"
class="px-4 pb-3"
:item="props.item.forwardedPost"
slim
compact
flat
border
>
<div v-if="props.item.forwardedGone" class="px-4 pb-3 text-sm opacity-60">
Post unavailable
</div>
<post-item
v-else-if="props.item.forwardedPost"
class="px-4 pb-3"
:item="props.item.forwardedPost"
slim
compact
flat
@react="handleReaction"
/>
</v-card>
@react="handleReaction"
/>
</div>
</template>
@@ -74,22 +66,19 @@
:max-height="640"
/>
<v-lazy
v-if="props.item.repliesCount && !compact"
:options="{ threshold: 0.5 }"
transition="fade-transition"
>
<div ref="repliesTarget">
<replies-compact-list
v-if="props.item.repliesCount && !compact && repliesVisible"
:params="{ postId: props.item.id }"
:hide-quick-reply="true"
@react="handleReplyReaction"
/>
</v-lazy>
</div>
<div
v-if="props.item.isTruncated"
class="flex gap-2 text-xs opacity-80"
class="flex gap-2 text-xs opacity-80 items-center"
>
<v-icon icon="mdi-dots-horizontal" size="small" />
<span class="mdi mdi-dots-horizontal"></span>
<p>Post truncated, tap to see details...</p>
</div>
@@ -104,14 +93,15 @@
/>
</div>
</div>
</v-card-text>
</v-card>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue"
import { useMarkdownProcessor } from "~/composables/useMarkdownProcessor"
import type { SnPost } from "~/types/api"
import { useIntersectionObserver } from '@vueuse/core'
const props = withDefaults(
defineProps<{
@@ -156,4 +146,19 @@ watch(
},
{ immediate: true, deep: true }
)
const repliesTarget = ref(null)
const repliesVisible = ref(false)
useIntersectionObserver(
repliesTarget,
([{ isIntersecting }]) => {
if (isIntersecting) {
repliesVisible.value = true
}
},
{
threshold: 0.5
}
)
</script>

View File

@@ -1,38 +1,19 @@
<template>
<v-select
:items="pubStore.publishers"
item-title="nick"
item-value="name"
:model-value="props.value"
@update:model-value="(v) => emits('update:value', v)"
>
<template #item="{ props: itemProps, item }">
<v-list-item v-bind="itemProps">
<template #prepend>
<v-avatar
:image="item.raw.picture ? `${apiBase}/drive/files/${item.raw.picture.id}` : undefined"
size="small"
/>
</template>
<v-list-item-subtitle>@{{ item.raw?.name }}</v-list-item-subtitle>
</v-list-item>
</template>
<template #selection="{ item }">
<div class="d-flex align-center">
<v-avatar
:image="item.raw.picture ? `${apiBase}/drive/files/${item.raw.picture.id}` : undefined"
size="24"
class="me-2"
/>
{{ item.raw?.nick }}
</div>
</template>
</v-select>
<n-select
:options="pubStore.publishers"
label-field="nick"
value-field="name"
:value="props.value"
@update:value="(v) => emits('update:value', v)"
:render-label="renderLabel"
:render-tag="renderTag"
/>
</template>
<script setup lang="ts">
import { usePubStore } from '~/stores/pub'
import { watch } from 'vue'
import { watch, h } from 'vue'
import type { SelectRenderLabel, SelectRenderTag } from 'naive-ui'
const pubStore = usePubStore()
const apiBase = useSolarNetworkUrl()
@@ -40,6 +21,38 @@ const apiBase = useSolarNetworkUrl()
const props = defineProps<{ value: string | undefined }>()
const emits = defineEmits(['update:value'])
const renderLabel: SelectRenderLabel = (option) => {
return h('div', { class: 'flex items-center' }, [
h(NAvatar, {
src: option.picture ? `${apiBase.value}/drive/files/${option.picture.id}` : undefined,
size: 'small',
class: 'mr-2'
}),
h('div', null, [
h('div', null, option.nick as string),
h('div', { class: 'text-xs text-gray-500' }, `@${option.name as string}`)
])
])
}
const renderTag: SelectRenderTag = ({ option }) => {
return h(
'div',
{
class: 'flex items-center'
},
[
h(NAvatar, {
src: option.picture ? `${apiBase.value}/drive/files/${option.picture.id}` : undefined,
size: 'small',
class: 'mr-2'
}),
option.nick as string
]
)
}
watch(
pubStore,
(value) => {

View File

@@ -6,15 +6,15 @@
<span>FloatingIsland</span>
</div>
<div class="flex flex-wrap gap-1.5">
<a class="link" target="_blank" href="https://solsynth.dev/terms">
<a class="hover:underline" target="_blank" href="https://solsynth.dev/terms">
Terms of Services
</a>
<span class="font-bold">·</span>
<a class="link" target="_blank" href="https://status.solsynth.dev">
<a class="hover:underline" target="_blank" href="https://status.solsynth.dev">
Service Status
</a>
<span class="font-bold">·</span>
<nuxt-link class="link" target="_blank" to="/swagger"> API </nuxt-link>
<nuxt-link class="hover:underline" target="_blank" to="/swagger"> API </nuxt-link>
</div>
<p class="mt-2 opacity-80">
The FloatingIsland do not provides all the features the Solar Network has,
@@ -23,9 +23,3 @@
</p>
</div>
</template>
<style scoped>
.link:hover {
text-decoration: underline;
}
</style>