✨ All data rendering in rewind
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="py-6 px-5 min-h-screen">
|
<div class="px-5">
|
||||||
<!-- Loading State -->
|
<!-- Loading State -->
|
||||||
<div
|
<div
|
||||||
v-if="pending"
|
v-if="pending"
|
||||||
@@ -49,9 +49,7 @@
|
|||||||
<div class="space-y-0">
|
<div class="space-y-0">
|
||||||
<!-- Section 1: Pass Data -->
|
<!-- Section 1: Pass Data -->
|
||||||
<div
|
<div
|
||||||
ref="section1"
|
|
||||||
class="scroll-section min-h-screen flex items-center justify-center"
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
:class="{ 'animate-in': inView1 }"
|
|
||||||
>
|
>
|
||||||
<n-card class="w-full max-w-4xl">
|
<n-card class="w-full max-w-4xl">
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -101,7 +99,7 @@
|
|||||||
<div class="text-2xl font-bold mb-1">
|
<div class="text-2xl font-bold mb-1">
|
||||||
{{ getStreakMessage(rewindData.data.pass.maxCheckInStreak) }}
|
{{ getStreakMessage(rewindData.data.pass.maxCheckInStreak) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-lg opacity-80">
|
<div class="text-md opacity-80">
|
||||||
{{
|
{{
|
||||||
getStreakDescription(rewindData.data.pass.maxCheckInStreak)
|
getStreakDescription(rewindData.data.pass.maxCheckInStreak)
|
||||||
}}
|
}}
|
||||||
@@ -111,11 +109,130 @@
|
|||||||
</n-card>
|
</n-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section 2: Creator Career Overview -->
|
<!-- Section 2: Lotteries -->
|
||||||
|
<div
|
||||||
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<n-card
|
||||||
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<n-icon
|
||||||
|
size="28"
|
||||||
|
:component="PartyPopperIcon"
|
||||||
|
class="text-purple-600"
|
||||||
|
/>
|
||||||
|
<h2 class="text-2xl font-bold">彩票游戏</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div class="flex flex-col gap-6">
|
||||||
|
<div class="grid grid-cols-1 gap-4">
|
||||||
|
<n-statistic label="获胜次数" tabular-nums>
|
||||||
|
<n-number-animation
|
||||||
|
:to="rewindData.data.pass.lotteriesWins"
|
||||||
|
/>
|
||||||
|
<template #suffix>次</template>
|
||||||
|
</n-statistic>
|
||||||
|
<n-statistic label="失败次数" tabular-nums>
|
||||||
|
<n-number-animation
|
||||||
|
:to="rewindData.data.pass.lotteriesLosses"
|
||||||
|
/>
|
||||||
|
<template #suffix>次</template>
|
||||||
|
</n-statistic>
|
||||||
|
<n-statistic label="胜率" tabular-nums>
|
||||||
|
<n-number-animation
|
||||||
|
:to="rewindData.data.pass.lotteriesWinRate * 100"
|
||||||
|
:precision="1"
|
||||||
|
/>
|
||||||
|
<template #suffix>%</template>
|
||||||
|
</n-statistic>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:text-right pr-4 max-md:order-first">
|
||||||
|
<div class="text-5xl mb-3">
|
||||||
|
{{
|
||||||
|
rewindData.data.pass.lotteriesWinRate >= 0.5 ? "🎉" : "😅"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold mb-1">
|
||||||
|
{{ getLotteryMessage(rewindData.data.pass.lotteriesWinRate) }}
|
||||||
|
</div>
|
||||||
|
<div class="text-md opacity-80">
|
||||||
|
{{
|
||||||
|
getLotteryDescription(rewindData.data.pass.lotteriesWinRate)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 3: Social Connections -->
|
||||||
|
<div
|
||||||
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<n-card
|
||||||
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<n-icon
|
||||||
|
size="28"
|
||||||
|
:component="HeartIcon"
|
||||||
|
class="text-pink-600"
|
||||||
|
/>
|
||||||
|
<h2 class="text-2xl font-bold">社交连接</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div class="flex flex-col gap-6">
|
||||||
|
<div class="grid grid-cols-1 gap-4">
|
||||||
|
<n-statistic label="新增好友" tabular-nums>
|
||||||
|
<n-number-animation
|
||||||
|
:to="rewindData.data.pass.newFriendsCount"
|
||||||
|
/>
|
||||||
|
<template #suffix>人</template>
|
||||||
|
</n-statistic>
|
||||||
|
<n-statistic label="新增屏蔽" tabular-nums>
|
||||||
|
<n-number-animation
|
||||||
|
:to="rewindData.data.pass.newBlockedCount"
|
||||||
|
/>
|
||||||
|
<template #suffix>人</template>
|
||||||
|
</n-statistic>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:text-right pr-4 max-md:order-first">
|
||||||
|
<div class="text-5xl mb-3">🤝</div>
|
||||||
|
<div class="text-2xl font-bold mb-1">
|
||||||
|
{{
|
||||||
|
getConnectionMessage(
|
||||||
|
rewindData.data.pass.newFriendsCount,
|
||||||
|
rewindData.data.pass.newBlockedCount
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-md opacity-80">
|
||||||
|
{{
|
||||||
|
getConnectionDescription(
|
||||||
|
rewindData.data.pass.newFriendsCount,
|
||||||
|
rewindData.data.pass.newBlockedCount
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 4: Creator Career Overview -->
|
||||||
<div
|
<div
|
||||||
ref="section2"
|
|
||||||
class="scroll-section min-h-screen flex items-center justify-center"
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
:class="{ 'animate-in': inView2 }"
|
|
||||||
>
|
>
|
||||||
<n-card
|
<n-card
|
||||||
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
@@ -210,11 +327,60 @@
|
|||||||
</n-card>
|
</n-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 5: Word Cloud -->
|
||||||
|
<div
|
||||||
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<n-card
|
||||||
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<n-icon
|
||||||
|
size="28"
|
||||||
|
:component="MessageCircleIcon"
|
||||||
|
class="text-blue-600"
|
||||||
|
/>
|
||||||
|
<h2 class="text-2xl font-bold">词汇云</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="text-5xl mb-6">☁️</div>
|
||||||
|
<div class="text-2xl font-bold mb-4">你的年度词汇</div>
|
||||||
|
<div class="text-md opacity-80 mb-8">
|
||||||
|
这些是你最常使用的词汇,反映了你的表达方式和兴趣
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-wrap justify-center items-center gap-4 max-w-3xl mx-auto"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-for="word in rewindData.data.sphere.topWords"
|
||||||
|
:key="word.word"
|
||||||
|
:class="getWordCloudClass(word.count)"
|
||||||
|
class="inline-block transition-all duration-300 hover:scale-110 cursor-default"
|
||||||
|
:title="`${word.word}: ${word.count} 次`"
|
||||||
|
>
|
||||||
|
<n-tooltip>
|
||||||
|
<template #trigger>
|
||||||
|
{{ word.word }}
|
||||||
|
</template>
|
||||||
|
{{ word.count }} 次使用
|
||||||
|
</n-tooltip>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 text-sm opacity-60">
|
||||||
|
词汇使用频率越高,字体越大
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Section 3: Explore History -->
|
<!-- Section 3: Explore History -->
|
||||||
<div
|
<div
|
||||||
ref="section3"
|
|
||||||
class="scroll-section min-h-screen flex items-center justify-center"
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
:class="{ 'animate-in': inView3 }"
|
|
||||||
>
|
>
|
||||||
<n-card
|
<n-card
|
||||||
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
@@ -267,9 +433,9 @@
|
|||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right flex flex-col justify-center px-5 gap-2">
|
<div class="text-right flex flex-col justify-center px-5 gap-2">
|
||||||
<div class="text-4xl">🤔</div>
|
<div class="text-4xl">❤️</div>
|
||||||
<p class="text-lg">
|
<p class="text-lg">
|
||||||
看起来你真的喜欢他/她呢 (´▽`) <br />
|
看起来你真的喜欢他/她呢<br />
|
||||||
新的一年不妨试试探索更多优秀创作者吧!
|
新的一年不妨试试探索更多优秀创作者吧!
|
||||||
</p>
|
</p>
|
||||||
<p class="text-xs opacity-80">
|
<p class="text-xs opacity-80">
|
||||||
@@ -282,9 +448,7 @@
|
|||||||
|
|
||||||
<!-- Section 4: Chat Summary -->
|
<!-- Section 4: Chat Summary -->
|
||||||
<div
|
<div
|
||||||
ref="section4"
|
|
||||||
class="scroll-section min-h-screen flex items-center justify-center"
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
:class="{ 'animate-in': inView4 }"
|
|
||||||
>
|
>
|
||||||
<n-card
|
<n-card
|
||||||
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
@@ -312,7 +476,7 @@
|
|||||||
<n-avatar
|
<n-avatar
|
||||||
:src="
|
:src="
|
||||||
getChatRoomAvatar(
|
getChatRoomAvatar(
|
||||||
rewindData.data.sphere.mostCalledChat.chat
|
rewindData.data.sphere.mostMessagedChat.chat
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>{{
|
>{{
|
||||||
@@ -372,13 +536,111 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="font-bold mb-2 flex items-center gap-2">
|
||||||
|
<n-icon :component="PhoneCallIcon" size="16" />
|
||||||
|
通话时间最长的聊天室
|
||||||
|
</h3>
|
||||||
|
<n-card size="small">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<n-avatar
|
||||||
|
:src="
|
||||||
|
getChatRoomAvatar(
|
||||||
|
rewindData.data.sphere.mostCalledChat.chat
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>{{
|
||||||
|
rewindData.data.sphere.mostCalledChat.chat.name?.substring(
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
}}</n-avatar
|
||||||
|
>
|
||||||
|
<div class="grow flex flex-col">
|
||||||
|
<div class="text-md font-bold">
|
||||||
|
{{ rewindData.data.sphere.mostCalledChat.chat.name }}
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<n-number-animation
|
||||||
|
:to="rewindData.data.sphere.mostCalledChat.duration"
|
||||||
|
/>
|
||||||
|
分钟
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2 text-opacity-80">
|
||||||
|
与这些人一起达成这样的成就
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
rewindData.data.sphere.mostCalledAccounts.length > 0
|
||||||
|
"
|
||||||
|
class="flex justify-start gap-4 mt-2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="item in rewindData.data.sphere
|
||||||
|
.mostCalledChatTopMembers"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<n-tooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<n-avatar
|
||||||
|
object-fit="cover"
|
||||||
|
:src="getAccountAvatar(item)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
{{ item.nick }}
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="font-bold mb-2 flex items-center gap-2">
|
||||||
|
<n-icon :component="WebhookIcon" size="16" />
|
||||||
|
通话时间前三名
|
||||||
|
</h3>
|
||||||
|
<n-card
|
||||||
|
v-if="rewindData.data.sphere.mostCalledAccounts.length > 0"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<div class="flex justify-start gap-4 mt-2">
|
||||||
|
<div
|
||||||
|
v-for="item in rewindData.data.sphere
|
||||||
|
.mostCalledAccounts"
|
||||||
|
:key="item.account.id"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-center items-center text-center gap-2"
|
||||||
|
>
|
||||||
|
<n-avatar
|
||||||
|
object-fit="cover"
|
||||||
|
:src="getAccountAvatar(item.account)"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div class="text-md font-bold">
|
||||||
|
{{ item.account.nick }}
|
||||||
|
</div>
|
||||||
|
<p class="text-sm opacity-80">
|
||||||
|
{{ item.duration }} 分钟
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right flex flex-col justify-center px-5 gap-2">
|
<div class="text-right flex flex-col justify-center px-5 gap-2">
|
||||||
<div class="text-4xl">💬</div>
|
<div class="text-5xl mb-3">💬</div>
|
||||||
<p class="text-lg">
|
<div class="text-2xl font-bold">
|
||||||
一眼丁真,鉴定为 <br/>
|
{{ getChatMessage(getTotalMessages(rewindData.data.sphere)) }}
|
||||||
<b>纯纯的话唠</b>
|
</div>
|
||||||
</p>
|
<div class="text-md opacity-80">
|
||||||
|
{{
|
||||||
|
getChatDescription(getTotalMessages(rewindData.data.sphere))
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
@@ -386,9 +648,7 @@
|
|||||||
|
|
||||||
<!-- Section 6: Summary -->
|
<!-- Section 6: Summary -->
|
||||||
<div
|
<div
|
||||||
ref="section6"
|
|
||||||
class="scroll-section min-h-screen flex items-center justify-center"
|
class="scroll-section min-h-screen flex items-center justify-center"
|
||||||
:class="{ 'animate-in': inView6 }"
|
|
||||||
>
|
>
|
||||||
<n-card
|
<n-card
|
||||||
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
class="w-full max-w-4xl bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg"
|
||||||
@@ -447,12 +707,11 @@
|
|||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
<span class="font-medium opacity-80">Favorite Chat:</span>
|
<span class="font-medium opacity-80">Favorite Chat:</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm">
|
<div class="text-sm">
|
||||||
<span class="font-medium opacity-80"
|
<span class="font-medium opacity-80"
|
||||||
>Top Connection:</span
|
>Top Connections:</span
|
||||||
>
|
>
|
||||||
{{ rewindData.data.sphere.mostCalledAccounts[0]?.nick }}
|
</div>
|
||||||
</p>
|
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
<span class="font-medium opacity-80"
|
<span class="font-medium opacity-80"
|
||||||
>Loved Publisher:</span
|
>Loved Publisher:</span
|
||||||
@@ -522,13 +781,14 @@ import {
|
|||||||
PencilLineIcon,
|
PencilLineIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
ShareIcon,
|
ShareIcon,
|
||||||
PartyPopperIcon
|
PartyPopperIcon,
|
||||||
|
PhoneCallIcon,
|
||||||
|
WebhookIcon
|
||||||
} from "lucide-vue-next"
|
} from "lucide-vue-next"
|
||||||
import { ref, onMounted, onUnmounted } from "vue"
|
import { ref } from "vue"
|
||||||
import { DateTime } from "luxon"
|
|
||||||
import type {
|
import type {
|
||||||
|
SnAccount,
|
||||||
SnRewind,
|
SnRewind,
|
||||||
SnRewindMostCalledChat,
|
|
||||||
SnRewindChat,
|
SnRewindChat,
|
||||||
SnRewindChatMember
|
SnRewindChatMember
|
||||||
} from "~/types/api"
|
} from "~/types/api"
|
||||||
@@ -541,23 +801,7 @@ const pending = ref(true)
|
|||||||
const error = ref<unknown>(null)
|
const error = ref<unknown>(null)
|
||||||
const rewindData = ref<SnRewind | null>(null)
|
const rewindData = ref<SnRewind | null>(null)
|
||||||
|
|
||||||
// Scroll animation refs
|
// No animation refs needed for CSS-only animations
|
||||||
const section1 = ref<HTMLElement>()
|
|
||||||
const section2 = ref<HTMLElement>()
|
|
||||||
const section3 = ref<HTMLElement>()
|
|
||||||
const section4 = ref<HTMLElement>()
|
|
||||||
const section5 = ref<HTMLElement>()
|
|
||||||
const section6 = ref<HTMLElement>()
|
|
||||||
|
|
||||||
// Intersection Observer state
|
|
||||||
const inView1 = ref(false)
|
|
||||||
const inView2 = ref(false)
|
|
||||||
const inView3 = ref(false)
|
|
||||||
const inView4 = ref(false)
|
|
||||||
const inView5 = ref(false)
|
|
||||||
const inView6 = ref(false)
|
|
||||||
|
|
||||||
const observers: IntersectionObserver[] = []
|
|
||||||
|
|
||||||
// Fetch rewind data
|
// Fetch rewind data
|
||||||
const fetchRewindData = async () => {
|
const fetchRewindData = async () => {
|
||||||
@@ -573,57 +817,9 @@ const fetchRewindData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup scroll animations
|
onMounted(() => fetchRewindData())
|
||||||
const setupScrollAnimations = () => {
|
|
||||||
const options = {
|
|
||||||
threshold: 0.1,
|
|
||||||
rootMargin: "0px 0px -50px 0px"
|
|
||||||
}
|
|
||||||
|
|
||||||
const createObserver = (
|
|
||||||
element: HTMLElement,
|
|
||||||
inViewRef: { value: boolean }
|
|
||||||
) => {
|
|
||||||
const observer = new IntersectionObserver((entries) => {
|
|
||||||
entries.forEach((entry) => {
|
|
||||||
inViewRef.value = entry.isIntersecting
|
|
||||||
})
|
|
||||||
}, options)
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
observer.observe(element)
|
|
||||||
observers.push(observer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createObserver(section1.value!, inView1)
|
|
||||||
createObserver(section2.value!, inView2)
|
|
||||||
createObserver(section3.value!, inView3)
|
|
||||||
createObserver(section4.value!, inView4)
|
|
||||||
createObserver(section5.value!, inView5)
|
|
||||||
createObserver(section6.value!, inView6)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
fetchRewindData()
|
|
||||||
// Delay setup to ensure DOM is ready
|
|
||||||
setTimeout(setupScrollAnimations, 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
observers.forEach((observer) => observer.disconnect())
|
|
||||||
})
|
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
const formatDate = (dateString: string): string => {
|
|
||||||
return DateTime.fromISO(dateString).toFormat("MMM dd, yyyy")
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const getChatAvatar = (chat: SnRewindMostCalledChat) => {
|
|
||||||
// Return default avatar for chat rooms
|
|
||||||
return "/api/placeholder/48/48"
|
|
||||||
}
|
|
||||||
|
|
||||||
const getChatRoomAvatar = (item: SnRewindChat) => {
|
const getChatRoomAvatar = (item: SnRewindChat) => {
|
||||||
const apiBase = useSolarNetworkUrl()
|
const apiBase = useSolarNetworkUrl()
|
||||||
@@ -639,6 +835,13 @@ const getChatMemberAvatar = (member: SnRewindChatMember) => {
|
|||||||
: "/api/placeholder/64/64"
|
: "/api/placeholder/64/64"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAccountAvatar = (account: SnAccount) => {
|
||||||
|
const apiBase = useSolarNetworkUrl()
|
||||||
|
return account?.profile?.picture
|
||||||
|
? `${apiBase}/drive/files/${account.profile.picture.id}`
|
||||||
|
: "/api/placeholder/32/32"
|
||||||
|
}
|
||||||
|
|
||||||
// Download functionality
|
// Download functionality
|
||||||
const downloadSummary = () => {
|
const downloadSummary = () => {
|
||||||
// Create a simple text summary for download
|
// Create a simple text summary for download
|
||||||
@@ -711,6 +914,123 @@ const getStreakDescription = (streak: number): string => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper methods for chat messages
|
||||||
|
const getTotalMessages = (data: SnRewind["data"]["sphere"]): number => {
|
||||||
|
return (
|
||||||
|
(data.mostMessagedDirectChat?.messageCounts || 0) +
|
||||||
|
(data.mostMessagedChat?.messageCounts || 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChatMessage = (totalMessages: number): string => {
|
||||||
|
if (totalMessages >= 10000) {
|
||||||
|
return "社交达人"
|
||||||
|
} else if (totalMessages >= 5000) {
|
||||||
|
return "聊天狂人"
|
||||||
|
} else if (totalMessages >= 2000) {
|
||||||
|
return "活跃分子"
|
||||||
|
} else if (totalMessages >= 1000) {
|
||||||
|
return "话痨本痨"
|
||||||
|
} else if (totalMessages >= 500) {
|
||||||
|
return "健谈人士"
|
||||||
|
} else if (totalMessages >= 100) {
|
||||||
|
return "社交新人"
|
||||||
|
} else {
|
||||||
|
return "继续聊天"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChatDescription = (totalMessages: number): string => {
|
||||||
|
if (totalMessages >= 10000) {
|
||||||
|
return `发送了 ${totalMessages} 条消息,你就是 Solar Network 的社交传奇`
|
||||||
|
} else if (totalMessages >= 5000) {
|
||||||
|
return `发送了 ${totalMessages} 条消息,你的聊天热情无人能及`
|
||||||
|
} else if (totalMessages >= 2000) {
|
||||||
|
return `发送了 ${totalMessages} 条消息,聊天已经成为你的日常`
|
||||||
|
} else if (totalMessages >= 1000) {
|
||||||
|
return `发送了 ${totalMessages} 条消息,你真的很爱在 Solar Network 上聊天`
|
||||||
|
} else if (totalMessages >= 500) {
|
||||||
|
return `发送了 ${totalMessages} 条消息,继续保持这个交流习惯`
|
||||||
|
} else if (totalMessages >= 100) {
|
||||||
|
return `发送了 ${totalMessages} 条消息,开始享受 Solar Network 的社交功能`
|
||||||
|
} else {
|
||||||
|
return `发送了 ${totalMessages} 条消息,多多交流让社区更精彩`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper methods for lottery messages
|
||||||
|
const getLotteryMessage = (winRate: number): string => {
|
||||||
|
if (winRate >= 0.8) {
|
||||||
|
return "彩票之神"
|
||||||
|
} else if (winRate >= 0.6) {
|
||||||
|
return "幸运儿"
|
||||||
|
} else if (winRate >= 0.4) {
|
||||||
|
return "运气不错"
|
||||||
|
} else if (winRate >= 0.2) {
|
||||||
|
return "继续加油"
|
||||||
|
} else {
|
||||||
|
return "试试手气"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLotteryDescription = (winRate: number): string => {
|
||||||
|
if (winRate >= 0.8) {
|
||||||
|
return `胜率 ${(winRate * 100).toFixed(1)}%,看来你就是传说中的欧皇`
|
||||||
|
} else if (winRate >= 0.6) {
|
||||||
|
return `胜率 ${(winRate * 100).toFixed(1)}%,你的运气真的很不错`
|
||||||
|
} else if (winRate >= 0.4) {
|
||||||
|
return `胜率 ${(winRate * 100).toFixed(1)}%,运气还可以,继续保持`
|
||||||
|
} else if (winRate >= 0.2) {
|
||||||
|
return `胜率 ${(winRate * 100).toFixed(1)}%,有时候运气就是这样`
|
||||||
|
} else {
|
||||||
|
return `胜率 ${(winRate * 100).toFixed(1)}%,新的一年希望你能转运`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper methods for connection messages
|
||||||
|
const getConnectionMessage = (friends: number, blocked: number): string => {
|
||||||
|
if (friends > blocked * 2) {
|
||||||
|
return "社交之星"
|
||||||
|
} else if (friends > blocked) {
|
||||||
|
return "友好使者"
|
||||||
|
} else if (blocked > friends) {
|
||||||
|
return "谨慎选择"
|
||||||
|
} else {
|
||||||
|
return "平衡发展"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getConnectionDescription = (friends: number, blocked: number): string => {
|
||||||
|
if (friends > blocked * 2) {
|
||||||
|
return `新增了 ${friends} 位好友,只有 ${blocked} 位屏蔽,你的社交圈在不断扩大`
|
||||||
|
} else if (friends > blocked) {
|
||||||
|
return `新增了 ${friends} 位好友,${blocked} 位屏蔽,你善于结识新朋友`
|
||||||
|
} else if (blocked > friends) {
|
||||||
|
return `新增了 ${friends} 位好友,但屏蔽了 ${blocked} 位,你对社交比较谨慎`
|
||||||
|
} else {
|
||||||
|
return `新增了 ${friends} 位好友,屏蔽了 ${blocked} 位,你的社交选择很平衡`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for word cloud styling
|
||||||
|
const getWordCloudClass = (count: number): string => {
|
||||||
|
if (count >= 1000) {
|
||||||
|
return "text-4xl font-bold text-blue-600"
|
||||||
|
} else if (count >= 500) {
|
||||||
|
return "text-3xl font-semibold text-green-600"
|
||||||
|
} else if (count >= 200) {
|
||||||
|
return "text-2xl font-medium text-purple-600"
|
||||||
|
} else if (count >= 100) {
|
||||||
|
return "text-xl font-medium text-pink-600"
|
||||||
|
} else if (count >= 50) {
|
||||||
|
return "text-lg font-normal text-indigo-600"
|
||||||
|
} else if (count >= 20) {
|
||||||
|
return "text-base font-normal text-teal-600"
|
||||||
|
} else {
|
||||||
|
return "text-sm font-normal text-gray-600"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: "Solar Network Rewind 2025",
|
title: "Solar Network Rewind 2025",
|
||||||
meta: [
|
meta: [
|
||||||
@@ -723,34 +1043,43 @@ useHead({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.scroll-section {
|
@keyframes fadeInUp {
|
||||||
opacity: 0;
|
from {
|
||||||
transform: translateY(30px);
|
opacity: 0;
|
||||||
transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-section.animate-in {
|
.scroll-section {
|
||||||
opacity: 1;
|
animation: fadeInUp 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
||||||
transform: translateY(0);
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Staggered animation delays */
|
/* Staggered animation delays */
|
||||||
.scroll-section:nth-child(1).animate-in {
|
.scroll-section:nth-child(1) {
|
||||||
transition-delay: 0.1s;
|
animation-delay: 0.1s;
|
||||||
}
|
}
|
||||||
.scroll-section:nth-child(2).animate-in {
|
.scroll-section:nth-child(2) {
|
||||||
transition-delay: 0.2s;
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
.scroll-section:nth-child(3).animate-in {
|
.scroll-section:nth-child(3) {
|
||||||
transition-delay: 0.3s;
|
animation-delay: 0.3s;
|
||||||
}
|
}
|
||||||
.scroll-section:nth-child(4).animate-in {
|
.scroll-section:nth-child(4) {
|
||||||
transition-delay: 0.4s;
|
animation-delay: 0.4s;
|
||||||
}
|
}
|
||||||
.scroll-section:nth-child(5).animate-in {
|
.scroll-section:nth-child(5) {
|
||||||
transition-delay: 0.5s;
|
animation-delay: 0.5s;
|
||||||
}
|
}
|
||||||
.scroll-section:nth-child(6).animate-in {
|
.scroll-section:nth-child(6) {
|
||||||
transition-delay: 0.6s;
|
animation-delay: 0.6s;
|
||||||
|
}
|
||||||
|
.scroll-section:nth-child(7) {
|
||||||
|
animation-delay: 0.7s;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ export interface SnRewindActiveData {
|
|||||||
mostActiveWeekday: string
|
mostActiveWeekday: string
|
||||||
latestActiveTime: string
|
latestActiveTime: string
|
||||||
checkInCompleteness: number
|
checkInCompleteness: number
|
||||||
|
lotteriesWins: number
|
||||||
|
lotteriesLosses: number
|
||||||
|
lotteriesWinRate: number
|
||||||
|
newBlockedCount: number
|
||||||
|
newFriendsCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SnRewindMostCalledChat {
|
export interface SnRewindMostCalledChat {
|
||||||
@@ -94,6 +99,11 @@ export interface SnRewindMostLovedAudience {
|
|||||||
upvoteCounts: number
|
upvoteCounts: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SnAccountCallRewind {
|
||||||
|
account: SnAccount
|
||||||
|
duration: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface SnRewindSocialData {
|
export interface SnRewindSocialData {
|
||||||
totalPostCount: number
|
totalPostCount: number
|
||||||
totalUpvoteCount: number
|
totalUpvoteCount: number
|
||||||
@@ -101,10 +111,12 @@ export interface SnRewindSocialData {
|
|||||||
mostMessagedDirectChat: SnRewindChatSummary
|
mostMessagedDirectChat: SnRewindChatSummary
|
||||||
mostPopularPost: SnRewindMostPopularPost
|
mostPopularPost: SnRewindMostPopularPost
|
||||||
mostProductiveDay: SnRewindMostProductiveDay
|
mostProductiveDay: SnRewindMostProductiveDay
|
||||||
mostCalledAccounts: SnAccount[]
|
mostCalledAccounts: SnAccountCallRewind[]
|
||||||
|
mostCalledChatTopMembers: SnAccount[]
|
||||||
mostLovedPublisher: SnRewindMostLovedPublisher
|
mostLovedPublisher: SnRewindMostLovedPublisher
|
||||||
mostLovedAudience: SnRewindMostLovedAudience
|
mostLovedAudience: SnRewindMostLovedAudience
|
||||||
mostMessagedChat: SnRewindChatSummary
|
mostMessagedChat: SnRewindChatSummary
|
||||||
|
topWords: { word: string; count: number }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SnRewind {
|
export interface SnRewind {
|
||||||
|
|||||||
Reference in New Issue
Block a user