♻️ Continued to migrate components
This commit is contained in:
@@ -27,3 +27,11 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
filter: blur(1rem);
|
filter: blur(1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.n-image-preview-toolbar .n-base-icon {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-image-preview-toolbar {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
14
app/components.d.ts
vendored
14
app/components.d.ts
vendored
@@ -14,15 +14,21 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
NAlert: typeof import('naive-ui')['NAlert']
|
NAlert: typeof import('naive-ui')['NAlert']
|
||||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||||
|
NBtn: typeof import('naive-ui')['NBtn']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
NCardSection: typeof import('naive-ui')['NCardSection']
|
NCardSection: typeof import('naive-ui')['NCardSection']
|
||||||
|
NCarousel: typeof import('naive-ui')['NCarousel']
|
||||||
|
NCarouselItem: typeof import('naive-ui')['NCarouselItem']
|
||||||
NChip: typeof import('naive-ui')['NChip']
|
NChip: typeof import('naive-ui')['NChip']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
NDialog: typeof import('naive-ui')['NDialog']
|
NDialog: typeof import('naive-ui')['NDialog']
|
||||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||||
NIcon: typeof import('naive-ui')['NIcon']
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
|
NImage: typeof import('naive-ui')['NImage']
|
||||||
|
NImg: typeof import('naive-ui')['NImg']
|
||||||
|
NInfiniteScroll: typeof import('naive-ui')['NInfiniteScroll']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
@@ -33,6 +39,7 @@ declare module 'vue' {
|
|||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
NSpin: typeof import('naive-ui')['NSpin']
|
NSpin: typeof import('naive-ui')['NSpin']
|
||||||
NTag: typeof import('naive-ui')['NTag']
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
|
NTextarea: typeof import('naive-ui')['NTextarea']
|
||||||
NThemeEditor: typeof import('naive-ui')['NThemeEditor']
|
NThemeEditor: typeof import('naive-ui')['NThemeEditor']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
@@ -43,15 +50,21 @@ declare module 'vue' {
|
|||||||
declare global {
|
declare global {
|
||||||
const NAlert: typeof import('naive-ui')['NAlert']
|
const NAlert: typeof import('naive-ui')['NAlert']
|
||||||
const NAvatar: typeof import('naive-ui')['NAvatar']
|
const NAvatar: typeof import('naive-ui')['NAvatar']
|
||||||
|
const NBtn: typeof import('naive-ui')['NBtn']
|
||||||
const NButton: typeof import('naive-ui')['NButton']
|
const NButton: typeof import('naive-ui')['NButton']
|
||||||
const NCard: typeof import('naive-ui')['NCard']
|
const NCard: typeof import('naive-ui')['NCard']
|
||||||
const NCardSection: typeof import('naive-ui')['NCardSection']
|
const NCardSection: typeof import('naive-ui')['NCardSection']
|
||||||
|
const NCarousel: typeof import('naive-ui')['NCarousel']
|
||||||
|
const NCarouselItem: typeof import('naive-ui')['NCarouselItem']
|
||||||
const NChip: typeof import('naive-ui')['NChip']
|
const NChip: typeof import('naive-ui')['NChip']
|
||||||
const NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
const NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
const NDialog: typeof import('naive-ui')['NDialog']
|
const NDialog: typeof import('naive-ui')['NDialog']
|
||||||
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
const NDropdown: typeof import('naive-ui')['NDropdown']
|
const NDropdown: typeof import('naive-ui')['NDropdown']
|
||||||
const NIcon: typeof import('naive-ui')['NIcon']
|
const NIcon: typeof import('naive-ui')['NIcon']
|
||||||
|
const NImage: typeof import('naive-ui')['NImage']
|
||||||
|
const NImg: typeof import('naive-ui')['NImg']
|
||||||
|
const NInfiniteScroll: typeof import('naive-ui')['NInfiniteScroll']
|
||||||
const NInput: typeof import('naive-ui')['NInput']
|
const NInput: typeof import('naive-ui')['NInput']
|
||||||
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
const NMenu: typeof import('naive-ui')['NMenu']
|
const NMenu: typeof import('naive-ui')['NMenu']
|
||||||
@@ -62,6 +75,7 @@ declare global {
|
|||||||
const NSpace: typeof import('naive-ui')['NSpace']
|
const NSpace: typeof import('naive-ui')['NSpace']
|
||||||
const NSpin: typeof import('naive-ui')['NSpin']
|
const NSpin: typeof import('naive-ui')['NSpin']
|
||||||
const NTag: typeof import('naive-ui')['NTag']
|
const NTag: typeof import('naive-ui')['NTag']
|
||||||
|
const NTextarea: typeof import('naive-ui')['NTextarea']
|
||||||
const NThemeEditor: typeof import('naive-ui')['NThemeEditor']
|
const NThemeEditor: typeof import('naive-ui')['NThemeEditor']
|
||||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
const RouterView: typeof import('vue-router')['RouterView']
|
const RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
|||||||
@@ -15,13 +15,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Main image -->
|
<!-- Main image -->
|
||||||
<img
|
<n-image
|
||||||
:src="remoteSource"
|
:src="remoteSource"
|
||||||
class="w-full h-auto rounded-md transition-opacity duration-500 object-cover cursor-pointer"
|
class="w-full h-auto rounded-md transition-opacity duration-500 object-cover cursor-pointer"
|
||||||
:class="{ 'opacity-0': !imageLoaded && blurhash }"
|
:class="{ 'opacity-0': !imageLoaded && blurhash }"
|
||||||
@load="imageLoaded = true"
|
@load="imageLoaded = true"
|
||||||
@error="imageLoaded = true"
|
@error="imageLoaded = true"
|
||||||
@click="openExternally"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -98,7 +97,9 @@ const remoteSource = computed(
|
|||||||
|
|
||||||
const blurhashContainerStyle = computed(() => {
|
const blurhashContainerStyle = computed(() => {
|
||||||
return {
|
return {
|
||||||
"padding-bottom": `${aspectRatio.value == null ? 0 : aspectRatio.value * 100}%`
|
"padding-bottom": `${
|
||||||
|
aspectRatio.value == null ? 0 : aspectRatio.value * 100
|
||||||
|
}%`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -14,28 +14,16 @@
|
|||||||
class="carousel-container rounded-lg overflow-hidden"
|
class="carousel-container rounded-lg overflow-hidden"
|
||||||
:style="carouselStyle"
|
:style="carouselStyle"
|
||||||
>
|
>
|
||||||
<v-card width="100%" height="100%" class="transition-all duration-300" border>
|
<n-carousel height="100%" show-arrow>
|
||||||
<v-carousel
|
<n-carousel-item
|
||||||
|
v-for="attachment in attachments"
|
||||||
|
:key="attachment.id"
|
||||||
height="100%"
|
height="100%"
|
||||||
hide-delimiter-background
|
cover
|
||||||
show-arrows="hover"
|
|
||||||
hide-delimiters
|
|
||||||
progress="primary"
|
|
||||||
>
|
>
|
||||||
<v-carousel-item
|
<attachment-item original class="h-full" :item="attachment" />
|
||||||
v-for="attachment in attachments"
|
</n-carousel-item>
|
||||||
:key="attachment.id"
|
</n-carousel>
|
||||||
height="100%"
|
|
||||||
cover
|
|
||||||
>
|
|
||||||
<attachment-item
|
|
||||||
original
|
|
||||||
class="h-full"
|
|
||||||
:item="attachment"
|
|
||||||
/>
|
|
||||||
</v-carousel-item>
|
|
||||||
</v-carousel>
|
|
||||||
</v-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mixed content: vertical scrollable -->
|
<!-- Mixed content: vertical scrollable -->
|
||||||
@@ -60,7 +48,6 @@ const props = defineProps<{
|
|||||||
maxHeight?: number
|
maxHeight?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|
||||||
const isAllImages = computed(
|
const isAllImages = computed(
|
||||||
() =>
|
() =>
|
||||||
props.attachments.length > 0 &&
|
props.attachments.length > 0 &&
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-card>
|
<n-card :size="compact ? 'small' : 'medium'">
|
||||||
<div :class="['flex flex-col', compact ? 'gap-1' : 'gap-3']">
|
<div :class="['flex flex-col', compact ? 'gap-1' : 'gap-3']">
|
||||||
<post-header :item="props.item" :compact="compact" />
|
<post-header :item="props.item" :compact="compact" />
|
||||||
|
|
||||||
@@ -22,10 +22,10 @@
|
|||||||
<template v-if="showReferenced">
|
<template v-if="showReferenced">
|
||||||
<div
|
<div
|
||||||
v-if="props.item.repliedPost || props.item.repliedGone"
|
v-if="props.item.repliedPost || props.item.repliedGone"
|
||||||
class="border rounded-md"
|
class="border rounded-xl"
|
||||||
>
|
>
|
||||||
<div class="p-2 flex items-center gap-2">
|
<div class="p-2 flex items-center gap-2">
|
||||||
<span class="mdi mdi-reply"></span>
|
<n-icon :component="ReplyIcon" class="ms-2" />
|
||||||
<span class="font-bold">Replying to</span>
|
<span class="font-bold">Replying to</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -47,10 +47,10 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="props.item.forwardedPost || props.item.forwardedGone"
|
v-if="props.item.forwardedPost || props.item.forwardedGone"
|
||||||
class="border rounded-md"
|
class="border rounded-xl"
|
||||||
>
|
>
|
||||||
<div class="p-2 flex items-center gap-2">
|
<div class="p-2 flex items-center gap-2">
|
||||||
<span class="mdi mdi-forward"></span>
|
<n-icon :component="ForwardIcon" class="ms-2" />
|
||||||
<span class="font-bold">Forwarded</span>
|
<span class="font-bold">Forwarded</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -112,6 +112,7 @@ import { ref, watch } from "vue"
|
|||||||
import { useMarkdownProcessor } from "~/composables/useMarkdownProcessor"
|
import { useMarkdownProcessor } from "~/composables/useMarkdownProcessor"
|
||||||
import type { SnPost } from "~/types/api"
|
import type { SnPost } from "~/types/api"
|
||||||
import { useIntersectionObserver } from "@vueuse/core"
|
import { useIntersectionObserver } from "@vueuse/core"
|
||||||
|
import { ForwardIcon, ReplyIcon } from "lucide-vue-next"
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|||||||
@@ -1,22 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card
|
<n-card title="Post your reply" size="small" embedded>
|
||||||
title="Post your reply"
|
<n-input
|
||||||
prepend-icon="mdi-post-lamp"
|
type="textarea"
|
||||||
flat
|
placeholder="Talk about this post for a bit."
|
||||||
border
|
size="large"
|
||||||
density="comfortable"
|
:rows="5"
|
||||||
>
|
auto-grow
|
||||||
<v-card-text>
|
></n-input>
|
||||||
<v-textarea
|
<div class="flex justify-end mt-4">
|
||||||
placeholder="Talk about this post for a bit."
|
<n-button append-icon="mdi-send" size="small">Send</n-button>
|
||||||
disabled
|
</div>
|
||||||
max-rows="5"
|
</n-card>
|
||||||
auto-grow
|
|
||||||
:hide-details="true"
|
|
||||||
></v-textarea>
|
|
||||||
<div class="flex justify-end mt-4">
|
|
||||||
<v-btn append-icon="mdi-send">Send</v-btn>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,17 +14,16 @@
|
|||||||
</n-alert>
|
</n-alert>
|
||||||
|
|
||||||
<!-- Replies List -->
|
<!-- Replies List -->
|
||||||
<v-infinite-scroll
|
<n-infinite-scroll
|
||||||
class="flex flex-col gap-4 mt-0"
|
class="flex flex-col gap-4 mt-0"
|
||||||
height="auto"
|
:distance="10"
|
||||||
side="end"
|
|
||||||
manual
|
|
||||||
@load="loadMore"
|
@load="loadMore"
|
||||||
>
|
>
|
||||||
<template v-for="item in replies" :key="item.id">
|
<template v-for="item in replies" :key="item.id">
|
||||||
<post-item
|
<post-item
|
||||||
:show-referenced="false"
|
:show-referenced="false"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
class="mb-4"
|
||||||
@click="router.push('/posts/' + item.id)"
|
@click="router.push('/posts/' + item.id)"
|
||||||
@react="
|
@react="
|
||||||
(symbol, attitude, delta) =>
|
(symbol, attitude, delta) =>
|
||||||
@@ -33,23 +32,17 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Loading State -->
|
|
||||||
<template #loading>
|
|
||||||
<div class="flex justify-center py-4">
|
|
||||||
<n-spin size="large" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State -->
|
||||||
<template #empty>
|
<div
|
||||||
<div v-if="!replies" class="text-center py-8 text-muted-foreground">
|
v-if="!replies || replies.length === 0"
|
||||||
<n-icon size="48" class="mb-2 opacity-50">
|
class="text-center py-8 text-muted-foreground"
|
||||||
<i class="mdi mdi-comment-outline"></i>
|
>
|
||||||
</n-icon>
|
<n-icon size="48" class="mb-2 opacity-50">
|
||||||
<p>No replies yet</p>
|
<i class="mdi mdi-comment-outline"></i>
|
||||||
</div>
|
</n-icon>
|
||||||
</template>
|
<p>No replies yet</p>
|
||||||
</v-infinite-scroll>
|
</div>
|
||||||
|
</n-infinite-scroll>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -74,7 +67,5 @@ const { replies, hasError, error, loadMore, refresh } = useRepliesList(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.replies-list .v-infinite-scroll .v-infinite-scroll__side:first-child {
|
/* Removed Vuetify-specific styles */
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -24,14 +24,13 @@
|
|||||||
<n-avatar
|
<n-avatar
|
||||||
round
|
round
|
||||||
class="mr-4 cursor-pointer"
|
class="mr-4 cursor-pointer"
|
||||||
:size="32"
|
|
||||||
:src="
|
:src="
|
||||||
user?.profile.picture
|
user?.profile.picture
|
||||||
? `${apiBase}/drive/files/${user?.profile.picture?.id}`
|
? `${apiBase}/drive/files/${user?.profile.picture?.id}`
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span v-if="!user" class="mdi mdi-account-circle text-3xl"></span>
|
<n-icon :component="UserCircleIcon" />
|
||||||
</n-avatar>
|
</n-avatar>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +48,7 @@ import IconLight from "~/assets/images/cloudy-lamb.png"
|
|||||||
import type { MenuOption } from "naive-ui"
|
import type { MenuOption } from "naive-ui"
|
||||||
import { computed, h } from "vue"
|
import { computed, h } from "vue"
|
||||||
import { useRouter, useRoute } from "vue-router"
|
import { useRouter, useRoute } from "vue-router"
|
||||||
import { CompassIcon } from "lucide-vue-next"
|
import { CompassIcon, UserCircleIcon } from "lucide-vue-next"
|
||||||
|
|
||||||
const apiBase = useSolarNetworkUrl()
|
const apiBase = useSolarNetworkUrl()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
@@ -1,20 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container mx-auto">
|
<div class="container mx-auto px-5">
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div v-for="activity in activites" :key="activity.id" class="mb-4">
|
<n-infinite-scroll
|
||||||
<post-item
|
style="overflow: auto"
|
||||||
v-if="activity.type.startsWith('posts')"
|
:distance="0"
|
||||||
:item="activity.data"
|
@load="fetchActivites"
|
||||||
@click="router.push('/posts/' + activity.id)"
|
>
|
||||||
/>
|
<div v-for="activity in activites" :key="activity.id" class="mb-4">
|
||||||
</div>
|
<post-item
|
||||||
|
v-if="activity.type.startsWith('posts')"
|
||||||
|
:item="activity.data"
|
||||||
|
@click="router.push('/posts/' + activity.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="loading" class="flex justify-center py-4">
|
||||||
|
<n-spin size="large" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="!loading && activites.length === 0"
|
||||||
|
class="text-center py-8 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<n-icon size="48" class="mb-2 opacity-50">
|
||||||
|
<i class="mdi mdi-post-outline"></i>
|
||||||
|
</n-icon>
|
||||||
|
<p>No posts yet</p>
|
||||||
|
</div>
|
||||||
|
</n-infinite-scroll>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar flex flex-col gap-3">
|
<div class="sidebar flex flex-col gap-3">
|
||||||
<div
|
<div v-if="!userStore.isAuthenticated">
|
||||||
v-if="!userStore.isAuthenticated"
|
|
||||||
class="card w-full bg-base-100 shadow-xl"
|
|
||||||
>
|
|
||||||
<n-card>
|
<n-card>
|
||||||
<h2 class="card-title">About</h2>
|
<h2 class="card-title">About</h2>
|
||||||
<p>Welcome to the <b>Solar Network</b></p>
|
<p>Welcome to the <b>Solar Network</b></p>
|
||||||
@@ -43,7 +60,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from "vue"
|
import { computed, onMounted, ref } from "vue"
|
||||||
import { useInfiniteScroll } from "@vueuse/core"
|
|
||||||
import { useUserStore } from "~/stores/user"
|
import { useUserStore } from "~/stores/user"
|
||||||
import { useSolarNetwork } from "~/composables/useSolarNetwork"
|
import { useSolarNetwork } from "~/composables/useSolarNetwork"
|
||||||
import type { SnVersion, SnActivity } from "~/types/api"
|
import type { SnVersion, SnActivity } from "~/types/api"
|
||||||
@@ -105,11 +121,6 @@ async function fetchActivites() {
|
|||||||
}
|
}
|
||||||
onMounted(() => fetchActivites())
|
onMounted(() => fetchActivites())
|
||||||
|
|
||||||
useInfiniteScroll(window, fetchActivites, {
|
|
||||||
canLoadMore: () => !loading.value && activitesHasMore.value,
|
|
||||||
distance: 10
|
|
||||||
})
|
|
||||||
|
|
||||||
async function refreshActivities() {
|
async function refreshActivities() {
|
||||||
activites.value = []
|
activites.value = []
|
||||||
fetchActivites()
|
fetchActivites()
|
||||||
|
|||||||
@@ -42,18 +42,18 @@
|
|||||||
<!-- Post Metadata -->
|
<!-- Post Metadata -->
|
||||||
<div class="flex items-center gap-4 text-sm text-medium-emphasis">
|
<div class="flex items-center gap-4 text-sm text-medium-emphasis">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<n-icon size="16" name="mdi-calendar" />
|
<n-icon :component="CalendarIcon" />
|
||||||
<span>{{ formatDate(post.createdAt) }}</span>
|
<span>{{ formatDate(post.createdAt) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="post.updatedAt && post.updatedAt !== post.createdAt"
|
v-if="post.updatedAt && post.updatedAt !== post.createdAt"
|
||||||
class="flex items-center gap-1"
|
class="flex items-center gap-1"
|
||||||
>
|
>
|
||||||
<n-icon size="16" name="mdi-pencil" />
|
<n-icon :component="PencilIcon" />
|
||||||
<span>Updated {{ formatDate(post.updatedAt) }}</span>
|
<span>Updated {{ formatDate(post.updatedAt) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<n-icon size="16" name="mdi-eye" />
|
<n-icon :component="EyeIcon" />
|
||||||
<span>
|
<span>
|
||||||
{{ post.viewsTotal }} / {{ post.viewsUnique }}
|
{{ post.viewsTotal }} / {{ post.viewsUnique }}
|
||||||
views
|
views
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
|
|
||||||
<n-card class="pa-6">
|
<n-card class="px-6 py-0">
|
||||||
<article
|
<article
|
||||||
v-if="htmlContent"
|
v-if="htmlContent"
|
||||||
class="prose dark:prose-invert prose-slate max-w-none mb-8"
|
class="prose dark:prose-invert prose-slate max-w-none mb-8"
|
||||||
@@ -127,6 +127,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { CalendarIcon, EyeIcon, PencilIcon } from "lucide-vue-next"
|
||||||
import { computed, ref } from "vue" // Added ref
|
import { computed, ref } from "vue" // Added ref
|
||||||
import { useMarkdownProcessor } from "~/composables/useMarkdownProcessor"
|
import { useMarkdownProcessor } from "~/composables/useMarkdownProcessor"
|
||||||
import type { SnPost } from "~/types/api"
|
import type { SnPost } from "~/types/api"
|
||||||
|
|||||||
Reference in New Issue
Block a user