Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/sxzz/midden. Archive posts and media across platforms, with tenant accounts and Telegram access
Something went wrong. Try again.
5.3 kB · 210 lines
Vue
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211<script setup vapor lang="ts">import { computed, nextTick, shallowRef, watch } from 'vue'import { api, type Collection, type Usage } from '../api'import { useCollection } from '../composables/useCollection'import { navigate, useRoute } from '../composables/useRoute'import { backButton, host } from '../host'import CollectionDetail from './CollectionDetail.vue'import CollectionView from './CollectionView.vue'const showSensitive = shallowRef(false)const route = useRoute()const collection = useCollection()const usage = shallowRef<Usage>()const { items, next, loading, error } = collectionconst id = computed( () => /^\/collection\/([a-f0-9-]+)$/.exec(route.value)?.[1] || '',)const query = computed(() => route.value.startsWith('/?') ? route.value.slice(2) : '',)// Telegram draws its own back button; only stand in for it elsewhere.const standalone = !host()?.initDataconst savedAt = computed( () => items.value.find((a) => a.id === id.value)?.saved_at,)let loadedQuery: string | undefinedlet scroll = 0let listRoute = '/'function apiQuery(raw: string) { const q = new URLSearchParams(raw) const from = q.get('from_date') const to = q.get('to_date') q.delete('from_date') q.delete('to_date') if (from) { const d = new Date(`${from}T00:00:00`) if (Number.isFinite(d.getTime())) q.set('saved_from', d.toISOString()) } if (to) { const d = new Date(`${to}T00:00:00`) if (Number.isFinite(d.getTime())) { d.setDate(d.getDate() + 1) q.set('saved_before', d.toISOString()) } } return q.toString()}watch( route, async () => { backButton(!!id.value) if (!id.value) { listRoute = route.value if (loadedQuery !== query.value) { loadedQuery = query.value scroll = 0 await collection.load(apiQuery(query.value)) } await nextTick() window.scrollTo(0, scroll) } else window.scrollTo(0, 0) }, { immediate: true },)async function loadUsage() { try { usage.value = await api<Usage>('/usage') } catch { usage.value = undefined }}loadUsage()function open(id: string) { scroll = window.scrollY navigate(`/collection/${id}`)}function updated(collection: Collection) { items.value = items.value.map((a) => a.id === id.value ? { ...collection, saved_at: a.saved_at } : a, ) loadUsage()}function deleted(id: string) { collection.remove(id) navigate(listRoute) loadUsage()}function search(q: string) { loadedQuery = undefined navigate(`/${q ? `?${q}` : ''}`) if (route.value === `/${q ? `?${q}` : ''}`) collection.load(apiQuery(q))}</script>
<template> <main class="page"> <header class="bar"> <button v-if="id && standalone" type="button" class="back" @click="navigate(listRoute)" > <span class="chevron" aria-hidden="true">‹</span>返回 </button> <h1>{{ id ? '收藏详情' : '我的收藏' }}</h1> <button type="button" class="sensitive-toggle" aria-label="显示敏感内容" :aria-pressed="showSensitive" :title="showSensitive ? '隐藏敏感内容' : '显示敏感内容'" @click="showSensitive = !showSensitive" > <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" > <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z" /> <circle cx="12" cy="12" r="3" /> <path v-if="!showSensitive" d="m3 3 18 18" /> </svg> </button> </header> <CollectionView v-if="!id" :items="items" :show-sensitive="showSensitive" :query="query" :loading="loading" :error="error" :next="next" :usage="usage" @search="search" @open="open" @more="collection.more()" @retry="collection.load(apiQuery(query))" /> <CollectionDetail v-else :id="id" :key="id" :saved-at="savedAt" :show-sensitive="showSensitive" @deleted="deleted" @updated="updated" /> </main></template>
<style scoped>.page { max-width: 560px; margin: 0 auto; min-height: 100dvh; padding-top: var(--tg-content-safe-area-inset-top, 0px); padding-bottom: max(32px, env(safe-area-inset-bottom));}.bar { display: flex; align-items: center; gap: 4px; min-height: 48px; padding: 10px calc(var(--gutter) + 4px) 2px;}.sensitive-toggle { margin-left: auto; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; color: var(--subtle);}.sensitive-toggle[aria-pressed='true'] { color: var(--link);}.sensitive-toggle:active { background: var(--fill);}.sensitive-toggle:focus-visible { outline: 2px solid var(--link); outline-offset: 2px;}.back { display: flex; align-items: center; min-height: 44px; margin-left: -6px; padding-right: 8px; font-size: 16px; color: var(--link);}.chevron { font-size: 26px; line-height: 1; padding-right: 2px;}h1 { margin: 0; font-size: 17px; font-weight: 600;}</style>