diff --git a/packages/client/src/components/MkTimeline.vue b/packages/client/src/components/MkTimeline.vue index 2d43dbe220..a921a05037 100644 --- a/packages/client/src/components/MkTimeline.vue +++ b/packages/client/src/components/MkTimeline.vue @@ -10,7 +10,7 @@ <script lang="ts" setup> import { computed, provide, onUnmounted } from "vue"; import XNotes from "@/components/MkNotes.vue"; -import { defaultStore } from "@/store"; +import { defaultStore } from "@/store"; import { stream } from "@/stream"; import * as sound from "@/scripts/sound"; import { $i } from "@/account"; @@ -97,14 +97,14 @@ if (props.src === "antenna") { endpoint = "notes/global-timeline"; connection = stream.useChannel("globalTimeline"); connection.on("note", prepend); -} else if (props.src === 'featured') { - endpoint = 'notes/featured'; +} else if (props.src === "featured") { + endpoint = "notes/featured"; query = { - origin: 'combined', + origin: "combined", offsetMode: true, days: 5, limit: 10, - } + }; } else if (props.src === "mentions") { endpoint = "notes/mentions"; connection = stream.useChannel("main"); diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue index 911eb9db9c..edba57d0cb 100644 --- a/packages/client/src/pages/admin/emojis.vue +++ b/packages/client/src/pages/admin/emojis.vue @@ -154,16 +154,22 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, defineComponent, ref, toRef } from 'vue'; -import MkButton from '@/components/MkButton.vue'; -import MkInput from '@/components/form/input.vue'; -import MkPagination from '@/components/MkPagination.vue'; -import MkSwitch from '@/components/form/switch.vue'; -import FormSplit from '@/components/form/split.vue'; -import { selectFile, selectFiles } from '@/scripts/select-file'; -import * as os from '@/os'; -import { i18n } from '@/i18n'; -import { definePageMetadata } from '@/scripts/page-metadata'; +import { + computed, + defineAsyncComponent, + defineComponent, + ref, + toRef, +} from "vue"; +import MkButton from "@/components/MkButton.vue"; +import MkInput from "@/components/form/input.vue"; +import MkPagination from "@/components/MkPagination.vue"; +import MkSwitch from "@/components/form/switch.vue"; +import FormSplit from "@/components/form/split.vue"; +import { selectFile, selectFiles } from "@/scripts/select-file"; +import * as os from "@/os"; +import { i18n } from "@/i18n"; +import { definePageMetadata } from "@/scripts/page-metadata"; const emojisPaginationComponent = ref<InstanceType<typeof MkPagination>>(); diff --git a/packages/client/src/pages/explore.vue b/packages/client/src/pages/explore.vue index c9ed1234df..f35a938663 100644 --- a/packages/client/src/pages/explore.vue +++ b/packages/client/src/pages/explore.vue @@ -1,26 +1,36 @@ <template> -<MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> - <div class="lznhrdub"> - <MkSpacer :content-max="1200"> - <swiper - :modules="[Virtual]" - :space-between="20" - :virtual="true" - :allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" - @swiper="setSwiperRef" - @slide-change="onSlideChange" + <MkStickyContainer> + <template #header + ><MkPageHeader + v-model:tab="tab" + :actions="headerActions" + :tabs="headerTabs" + /></template> + <div class="lznhrdub"> + <MkSpacer :content-max="1200"> + <swiper + :modules="[Virtual]" + :space-between="20" + :virtual="true" + :allow-touch-move=" + !( + deviceKind === 'desktop' && + !defaultStore.state.swipeOnDesktop + ) + " + @swiper="setSwiperRef" + @slide-change="onSlideChange" > - <swiper-slide> - <XUsers/> - </swiper-slide> - <swiper-slide> - <XFeatured/> - </swiper-slide> - </swiper> - </MkSpacer> - </div> -</MkStickyContainer> + <swiper-slide> + <XUsers /> + </swiper-slide> + <swiper-slide> + <XFeatured /> + </swiper-slide> + </swiper> + </MkSpacer> + </div> + </MkStickyContainer> </template> <script lang="ts" setup> @@ -41,7 +51,7 @@ const props = defineProps<{ tag?: string; }>(); -const tabs = ['users', 'featured']; +const tabs = ["users", "featured"]; let tab = $ref(tabs[0]); watch($$(tab), () => syncSlide(tabs.indexOf(tab))); @@ -56,15 +66,18 @@ watch( const headerActions = $computed(() => []); -const headerTabs = $computed(() => [{ - key: 'users', - icon: 'ph-users ph-bold ph-lg', - title: i18n.ts.users, -}, { - key: 'featured', - icon: 'ph-lightning ph-bold ph-lg', - title: i18n.ts.featured, -}]); +const headerTabs = $computed(() => [ + { + key: "users", + icon: "ph-users ph-bold ph-lg", + title: i18n.ts.users, + }, + { + key: "featured", + icon: "ph-lightning ph-bold ph-lg", + title: i18n.ts.featured, + }, +]); definePageMetadata( computed(() => ({ diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index 879afe54ee..7bd98a4dac 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -1,42 +1,19 @@ <template> -<div class="_formRoot"> - <FormSelect v-model="lang" class="_formBlock"> - <template #label>{{ i18n.ts.uiLanguage }}</template> - <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> - <template #caption> - <I18n :src="i18n.ts.i18nInfo" tag="span"> - <template #link> - <MkLink url="https://hosted.weblate.org/engage/calckey/">Weblate</MkLink> - </template> - </I18n> - </template> - </FormSelect> - - <FormRadios v-model="overridedDeviceKind" class="_formBlock"> - <template #label>{{ i18n.ts.overridedDeviceKind }}</template> - <option :value="null">{{ i18n.ts.auto }}</option> - <option value="smartphone"><i class="ph-device-mobile ph-bold ph-lg"/> {{ i18n.ts.smartphone }}</option> - <option value="tablet"><i class="ph-device-tablet ph-bold ph-lg"/> {{ i18n.ts.tablet }}</option> - <option value="desktop"><i class="ph-desktop ph-bold ph-lg"/> {{ i18n.ts.desktop }}</option> - </FormRadios> - - <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch> - - <FormSection> - <template #label>{{ i18n.ts.behavior }}</template> - <FormSwitch v-model="imageNewTab" class="_formBlock">{{ i18n.ts.openImageInNewTab }}</FormSwitch> - <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ i18n.ts.enableInfiniteScroll }}</FormSwitch> - <FormSwitch v-model="useReactionPickerForContextMenu" class="_formBlock">{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch> - <FormSwitch v-model="swipeOnDesktop" class="_formBlock">{{ i18n.ts.swipeOnDesktop }}</FormSwitch> - <FormSwitch v-model="enterSendsMessage" class="_formBlock">{{ i18n.ts.enterSendsMessage }}</FormSwitch> - <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ i18n.ts.disablePagesScript }}</FormSwitch> - - <FormSelect v-model="serverDisconnectedBehavior" class="_formBlock"> - <template #label>{{ i18n.ts.whenServerDisconnected }}</template> - <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option> - <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option> - <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option> - <option value="nothing">{{ i18n.ts._serverDisconnectedBehavior.nothing }}</option> + <div class="_formRoot"> + <FormSelect v-model="lang" class="_formBlock"> + <template #label>{{ i18n.ts.uiLanguage }}</template> + <option v-for="x in langs" :key="x[0]" :value="x[0]"> + {{ x[1] }} + </option> + <template #caption> + <I18n :src="i18n.ts.i18nInfo" tag="span"> + <template #link> + <MkLink url="https://hosted.weblate.org/engage/calckey/" + >Weblate</MkLink + > + </template> + </I18n> + </template> </FormSelect> <FormRadios v-model="overridedDeviceKind" class="_formBlock"> @@ -55,17 +32,6 @@ </option> </FormRadios> - <FormRadios v-model="showLocalPostsInTimeline" class="_formBlock"> - <template #label>{{ i18n.ts.showLocalPosts }}</template> - <option value="home"> - <i class="ph-house ph-bold ph-lg" /> {{ i18n.ts.homeTimeline }} - </option> - <option value="social"> - <i class="ph-handshake ph-bold ph-lg" /> - {{ i18n.ts.socialTimeline }} - </option> - </FormRadios> - <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch> @@ -108,6 +74,71 @@ {{ i18n.ts._serverDisconnectedBehavior.nothing }} </option> </FormSelect> + + <FormRadios v-model="overridedDeviceKind" class="_formBlock"> + <template #label>{{ i18n.ts.overridedDeviceKind }}</template> + <option :value="null">{{ i18n.ts.auto }}</option> + <option value="smartphone"> + <i class="ph-device-mobile ph-bold ph-lg" /> + {{ i18n.ts.smartphone }} + </option> + <option value="tablet"> + <i class="ph-device-tablet ph-bold ph-lg" /> + {{ i18n.ts.tablet }} + </option> + <option value="desktop"> + <i class="ph-desktop ph-bold ph-lg" /> {{ i18n.ts.desktop }} + </option> + </FormRadios> + + <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ + i18n.ts.showFixedPostForm + }}</FormSwitch> + + <FormSection> + <template #label>{{ i18n.ts.behavior }}</template> + <FormSwitch v-model="imageNewTab" class="_formBlock">{{ + i18n.ts.openImageInNewTab + }}</FormSwitch> + <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ + i18n.ts.enableInfiniteScroll + }}</FormSwitch> + <FormSwitch + v-model="useReactionPickerForContextMenu" + class="_formBlock" + >{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch + > + <FormSwitch v-model="swipeOnDesktop" class="_formBlock">{{ + i18n.ts.swipeOnDesktop + }}</FormSwitch> + <FormSwitch v-model="enterSendsMessage" class="_formBlock">{{ + i18n.ts.enterSendsMessage + }}</FormSwitch> + <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ + i18n.ts.disablePagesScript + }}</FormSwitch> + + <FormSelect + v-model="serverDisconnectedBehavior" + class="_formBlock" + > + <template #label>{{ + i18n.ts.whenServerDisconnected + }}</template> + <option value="reload"> + {{ i18n.ts._serverDisconnectedBehavior.reload }} + </option> + <option value="dialog"> + {{ i18n.ts._serverDisconnectedBehavior.dialog }} + </option> + <option value="quiet"> + {{ i18n.ts._serverDisconnectedBehavior.quiet }} + </option> + <option value="nothing"> + {{ i18n.ts._serverDisconnectedBehavior.nothing }} + </option> + </FormSelect> + </FormSection> </FormSection> <FormSection> @@ -260,32 +291,76 @@ async function reloadAsk() { unisonReload(); } -const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); -const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); -const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); -const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); -const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); -const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline')); -const showAds = computed(defaultStore.makeGetterSetter('showAds')); -const disableAnimatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v)); -const useOsNativeEmojis = computed(defaultStore.makeGetterSetter('useOsNativeEmojis')); -const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); -const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); -const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); -const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); -const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); -const disablePagesScript = computed(defaultStore.makeGetterSetter('disablePagesScript')); -const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); -const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); -const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); -const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); -const enterSendsMessage = computed(defaultStore.makeGetterSetter('enterSendsMessage')); -const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); -const seperateRenoteQuote = computed(defaultStore.makeGetterSetter('seperateRenoteQuote')); -const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); -const showUpdates = computed(defaultStore.makeGetterSetter('showUpdates')); -const swipeOnDesktop = computed(defaultStore.makeGetterSetter('swipeOnDesktop')); -const showAdminUpdates = computed(defaultStore.makeGetterSetter('showAdminUpdates')); +const overridedDeviceKind = computed( + defaultStore.makeGetterSetter("overridedDeviceKind") +); +const serverDisconnectedBehavior = computed( + defaultStore.makeGetterSetter("serverDisconnectedBehavior") +); +const reduceAnimation = computed( + defaultStore.makeGetterSetter( + "animation", + (v) => !v, + (v) => !v + ) +); +const useBlurEffectForModal = computed( + defaultStore.makeGetterSetter("useBlurEffectForModal") +); +const useBlurEffect = computed(defaultStore.makeGetterSetter("useBlurEffect")); +const showGapBetweenNotesInTimeline = computed( + defaultStore.makeGetterSetter("showGapBetweenNotesInTimeline") +); +const showAds = computed(defaultStore.makeGetterSetter("showAds")); +const disableAnimatedMfm = computed( + defaultStore.makeGetterSetter( + "animatedMfm", + (v) => !v, + (v) => !v + ) +); +const useOsNativeEmojis = computed( + defaultStore.makeGetterSetter("useOsNativeEmojis") +); +const disableDrawer = computed(defaultStore.makeGetterSetter("disableDrawer")); +const disableShowingAnimatedImages = computed( + defaultStore.makeGetterSetter("disableShowingAnimatedImages") +); +const loadRawImages = computed(defaultStore.makeGetterSetter("loadRawImages")); +const imageNewTab = computed(defaultStore.makeGetterSetter("imageNewTab")); +const nsfw = computed(defaultStore.makeGetterSetter("nsfw")); +const disablePagesScript = computed( + defaultStore.makeGetterSetter("disablePagesScript") +); +const showFixedPostForm = computed( + defaultStore.makeGetterSetter("showFixedPostForm") +); +const numberOfPageCache = computed( + defaultStore.makeGetterSetter("numberOfPageCache") +); +const instanceTicker = computed( + defaultStore.makeGetterSetter("instanceTicker") +); +const enableInfiniteScroll = computed( + defaultStore.makeGetterSetter("enableInfiniteScroll") +); +const enterSendsMessage = computed( + defaultStore.makeGetterSetter("enterSendsMessage") +); +const useReactionPickerForContextMenu = computed( + defaultStore.makeGetterSetter("useReactionPickerForContextMenu") +); +const seperateRenoteQuote = computed( + defaultStore.makeGetterSetter("seperateRenoteQuote") +); +const squareAvatars = computed(defaultStore.makeGetterSetter("squareAvatars")); +const showUpdates = computed(defaultStore.makeGetterSetter("showUpdates")); +const swipeOnDesktop = computed( + defaultStore.makeGetterSetter("swipeOnDesktop") +); +const showAdminUpdates = computed( + defaultStore.makeGetterSetter("showAdminUpdates") +); watch(lang, () => { localStorage.setItem("lang", lang.value as string); @@ -308,23 +383,26 @@ watch(useSystemFont, () => { } }); -watch([ - lang, - fontSize, - useSystemFont, - enableInfiniteScroll, - squareAvatars, - showGapBetweenNotesInTimeline, - instanceTicker, - overridedDeviceKind, - showAds, - showUpdates, - swipeOnDesktop, - seperateRenoteQuote, - showAdminUpdates, -], async () => { - await reloadAsk(); -}); +watch( + [ + lang, + fontSize, + useSystemFont, + enableInfiniteScroll, + squareAvatars, + showGapBetweenNotesInTimeline, + instanceTicker, + overridedDeviceKind, + showAds, + showUpdates, + swipeOnDesktop, + seperateRenoteQuote, + showAdminUpdates, + ], + async () => { + await reloadAsk(); + } +); const headerActions = $computed(() => []); diff --git a/packages/client/src/pages/settings/preferences-backups.vue b/packages/client/src/pages/settings/preferences-backups.vue index 953fe2b915..9d894ae389 100644 --- a/packages/client/src/pages/settings/preferences-backups.vue +++ b/packages/client/src/pages/settings/preferences-backups.vue @@ -73,48 +73,48 @@ const { t, ts } = i18n; useCssModule(); -const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ - 'menu', - 'visibility', - 'localOnly', - 'statusbars', - 'widgets', - 'tl', - 'forYouTl', - 'discoverTl', - 'overridedDeviceKind', - 'serverDisconnectedBehavior', - 'nsfw', - 'showAds', - 'animation', - 'animatedMfm', - 'loadRawImages', - 'imageNewTab', - 'disableShowingAnimatedImages', - 'disablePagesScript', - 'enterSendsMessage', - 'useOsNativeEmojis', - 'disableDrawer', - 'useBlurEffectForModal', - 'useBlurEffect', - 'showFixedPostForm', - 'enableInfiniteScroll', - 'useReactionPickerForContextMenu', - 'showGapBetweenNotesInTimeline', - 'instanceTicker', - 'reactionPickerSize', - 'reactionPickerWidth', - 'reactionPickerHeight', - 'reactionPickerUseDrawerForMobile', - 'defaultSideView', - 'menuDisplay', - 'reportError', - 'squareAvatars', - 'numberOfPageCache', - 'showUpdates', - 'swipeOnDesktop', - 'showAdminUpdates', - 'enableCustomKaTeXMacro', +const defaultStoreSaveKeys: (keyof (typeof defaultStore)["state"])[] = [ + "menu", + "visibility", + "localOnly", + "statusbars", + "widgets", + "tl", + "forYouTl", + "discoverTl", + "overridedDeviceKind", + "serverDisconnectedBehavior", + "nsfw", + "showAds", + "animation", + "animatedMfm", + "loadRawImages", + "imageNewTab", + "disableShowingAnimatedImages", + "disablePagesScript", + "enterSendsMessage", + "useOsNativeEmojis", + "disableDrawer", + "useBlurEffectForModal", + "useBlurEffect", + "showFixedPostForm", + "enableInfiniteScroll", + "useReactionPickerForContextMenu", + "showGapBetweenNotesInTimeline", + "instanceTicker", + "reactionPickerSize", + "reactionPickerWidth", + "reactionPickerHeight", + "reactionPickerUseDrawerForMobile", + "defaultSideView", + "menuDisplay", + "reportError", + "squareAvatars", + "numberOfPageCache", + "showUpdates", + "swipeOnDesktop", + "showAdminUpdates", + "enableCustomKaTeXMacro", ]; const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ "lightTheme", diff --git a/packages/client/src/pages/timeline.discover.vue b/packages/client/src/pages/timeline.discover.vue index 7c9ca9883e..81f40c1df9 100644 --- a/packages/client/src/pages/timeline.discover.vue +++ b/packages/client/src/pages/timeline.discover.vue @@ -1,9 +1,13 @@ <template> <MkSpacer :content-max="800"> - <MkTab v-model="tab" style="margin-bottom: var(--margin);"> + <MkTab v-model="tab" style="margin-bottom: var(--margin)"> <option value="hot">{{ i18n.ts._timelines.hot }}</option> - <option v-if="isRecommendedTimelineAvailable" value="recommended">{{ i18n.ts._timelines.recommended }}</option> - <option v-if="isGlobalTimelineAvailable" value="global">{{ i18n.ts._timelines.global }}</option> + <option v-if="isRecommendedTimelineAvailable" value="recommended"> + {{ i18n.ts._timelines.recommended }} + </option> + <option v-if="isGlobalTimelineAvailable" value="global"> + {{ i18n.ts._timelines.global }} + </option> </MkTab> <XTimeline v-if="tab === 'hot'" @@ -30,25 +34,23 @@ </template> <script lang="ts" setup> -import XTimeline from '@/components/MkTimeline.vue'; -import MkTab from '@/components/MkTab.vue'; -import { defaultStore } from '@/store'; -import { i18n } from '@/i18n'; -import { instance } from '@/instance'; -import { $i } from '@/account'; +import XTimeline from "@/components/MkTimeline.vue"; +import MkTab from "@/components/MkTab.vue"; +import { defaultStore } from "@/store"; +import { i18n } from "@/i18n"; +import { instance } from "@/instance"; +import { $i } from "@/account"; const tab = $computed({ get: () => defaultStore.reactiveState.discoverTl.value.src, set: (x) => saveSrc(x), }); -function saveSrc( - newSrc: 'hot' | 'recommended' | 'global', -): void { - defaultStore.set('discoverTl', { +function saveSrc(newSrc: "hot" | "recommended" | "global"): void { + defaultStore.set("discoverTl", { ...defaultStore.state.discoverTl, - src: newSrc - }) + src: newSrc, + }); } const isRecommendedTimelineAvailable = !instance.disableRecommendedTimeline; diff --git a/packages/client/src/pages/timeline.foryou.vue b/packages/client/src/pages/timeline.foryou.vue index 14812103ff..3ba52f9789 100644 --- a/packages/client/src/pages/timeline.foryou.vue +++ b/packages/client/src/pages/timeline.foryou.vue @@ -1,9 +1,13 @@ <template> <MkSpacer :content-max="800"> - <MkTab v-model="tab" style="margin-bottom: var(--margin);"> - <option v-if="isLocalTimelineAvailable" value="social">{{ i18n.ts._timelines.social }}</option> + <MkTab v-model="tab" style="margin-bottom: var(--margin)"> + <option v-if="isLocalTimelineAvailable" value="social"> + {{ i18n.ts._timelines.social }} + </option> <option value="home">{{ i18n.ts._timelines.home }}</option> - <option value="local" v-if="isLocalTimelineAvailable">{{ i18n.ts._timelines.local }}</option> + <option value="local" v-if="isLocalTimelineAvailable"> + {{ i18n.ts._timelines.local }} + </option> </MkTab> <XTimeline v-if="tab === 'social'" @@ -30,25 +34,23 @@ </template> <script lang="ts" setup> -import XTimeline from '@/components/MkTimeline.vue'; -import MkTab from '@/components/MkTab.vue'; -import { defaultStore } from '@/store'; -import { i18n } from '@/i18n'; -import { instance } from '@/instance'; -import { $i } from '@/account'; +import XTimeline from "@/components/MkTimeline.vue"; +import MkTab from "@/components/MkTab.vue"; +import { defaultStore } from "@/store"; +import { i18n } from "@/i18n"; +import { instance } from "@/instance"; +import { $i } from "@/account"; const tab = $computed({ get: () => defaultStore.reactiveState.forYouTl.value.src, set: (x) => saveSrc(x), }); -function saveSrc( - newSrc: 'home' | 'local' | 'social', -): void { - defaultStore.set('forYouTl', { +function saveSrc(newSrc: "home" | "local" | "social"): void { + defaultStore.set("forYouTl", { ...defaultStore.state.forYouTl, - src: newSrc - }) + src: newSrc, + }); } const isLocalTimelineAvailable = diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue index 4a29a7ea50..acc203006c 100644 --- a/packages/client/src/pages/timeline.vue +++ b/packages/client/src/pages/timeline.vue @@ -15,7 +15,7 @@ class="post-form _block" fixed /> - + <div v-if="queue > 0" class="new"> <button class="_buttonPrimary" @click="top()"> {{ i18n.ts.newNoteRecived }} @@ -36,218 +36,228 @@ :modules="[Virtual]" :space-between="20" :virtual="true" - :allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" + :allow-touch-move=" + !( + deviceKind === 'desktop' && + !defaultStore.state.swipeOnDesktop + ) + " @swiper="setSwiperRef" @slide-change="onSlideChange" - > + > <swiper-slide> - <XForYou/> + <XForYou /> </swiper-slide> <swiper-slide> - <XDiscover/> + <XDiscover /> </swiper-slide> </swiper> </div> </div> </MkSpacer> </MkStickyContainer> - </template> - - <script lang="ts" setup> - import { computed, ref, onMounted } from 'vue'; - import { Virtual } from 'swiper'; - import { Swiper, SwiperSlide } from 'swiper/vue'; - import XTutorial from '@/components/MkTutorialDialog.vue'; - import XPostForm from '@/components/MkPostForm.vue'; - import XForYou from './timeline.foryou.vue'; - import XDiscover from './timeline.discover.vue'; - import { scroll } from '@/scripts/scroll'; - import * as os from '@/os'; - import { defaultStore } from '@/store'; - import { i18n } from '@/i18n'; - import { definePageMetadata } from '@/scripts/page-metadata'; - import { deviceKind } from '@/scripts/device-kind'; - import 'swiper/scss'; - import 'swiper/scss/virtual'; - - if (defaultStore.reactiveState.tutorial.value !== -1) { - os.popup(XTutorial, {}, {}, 'closed'); - } - - let timelines = ['forYou', 'discover']; - - const MOBILE_THRESHOLD = 500; - - // デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない - const isMobile = ref( - deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD, - ); - window.addEventListener('resize', () => { - isMobile.value = - (deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); - }); - - const rootEl = $ref<HTMLElement>(); - - let queue = $ref(0); - const src = $computed({ - get: () => defaultStore.reactiveState.tl.value.src, - set: (x) => { - saveSrc(x); - syncSlide(timelines.indexOf(x)); - }, - }); - - function top(): void { - scroll(rootEl!, { top: 0 }); - } - - async function chooseList(ev: MouseEvent): Promise<void> { - const lists = await os.api('users/lists/list'); - const items = [{ - type: 'link' as const, +</template> + +<script lang="ts" setup> +import { computed, ref, onMounted } from "vue"; +import { Virtual } from "swiper"; +import { Swiper, SwiperSlide } from "swiper/vue"; +import XTutorial from "@/components/MkTutorialDialog.vue"; +import XPostForm from "@/components/MkPostForm.vue"; +import XForYou from "./timeline.foryou.vue"; +import XDiscover from "./timeline.discover.vue"; +import { scroll } from "@/scripts/scroll"; +import * as os from "@/os"; +import { defaultStore } from "@/store"; +import { i18n } from "@/i18n"; +import { definePageMetadata } from "@/scripts/page-metadata"; +import { deviceKind } from "@/scripts/device-kind"; +import "swiper/scss"; +import "swiper/scss/virtual"; + +if (defaultStore.reactiveState.tutorial.value !== -1) { + os.popup(XTutorial, {}, {}, "closed"); +} + +let timelines = ["forYou", "discover"]; + +const MOBILE_THRESHOLD = 500; + +// デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない +const isMobile = ref( + deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD +); +window.addEventListener("resize", () => { + isMobile.value = + deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD; +}); + +const rootEl = $ref<HTMLElement>(); + +let queue = $ref(0); +const src = $computed({ + get: () => defaultStore.reactiveState.tl.value.src, + set: (x) => { + saveSrc(x); + syncSlide(timelines.indexOf(x)); + }, +}); + +function top(): void { + scroll(rootEl!, { top: 0 }); +} + +async function chooseList(ev: MouseEvent): Promise<void> { + const lists = await os.api("users/lists/list"); + const items = [ + { + type: "link" as const, text: i18n.ts.manageLists, - icon: 'ph-faders-horizontal ph-bold ph-lg', - to: '/my/lists', - }].concat(lists.map((list) => ({ - type: 'link' as const, + icon: "ph-faders-horizontal ph-bold ph-lg", + to: "/my/lists", + }, + ].concat( + lists.map((list) => ({ + type: "link" as const, text: list.name, - icon: '', + icon: "", to: `/timeline/list/${list.id}`, - }))); - os.popupMenu(items, ev.currentTarget ?? ev.target); - } - - async function chooseAntenna(ev: MouseEvent): Promise<void> { - const antennas = await os.api('antennas/list'); - const items = [{ - type: 'link' as const, + })) + ); + os.popupMenu(items, ev.currentTarget ?? ev.target); +} + +async function chooseAntenna(ev: MouseEvent): Promise<void> { + const antennas = await os.api("antennas/list"); + const items = [ + { + type: "link" as const, indicate: false, text: i18n.ts.manageAntennas, - icon: 'ph-faders-horizontal ph-bold ph-lg', - to: '/my/antennas', - }].concat(antennas.map((antenna) => ({ - type: 'link' as const, + icon: "ph-faders-horizontal ph-bold ph-lg", + to: "/my/antennas", + }, + ].concat( + antennas.map((antenna) => ({ + type: "link" as const, text: antenna.name, - icon: '', + icon: "", indicate: antenna.hasUnreadNote, to: `/timeline/antenna/${antenna.id}`, - }))); - os.popupMenu(items, ev.currentTarget ?? ev.target); - } - - function saveSrc( - newSrc: 'forYou' | 'discover', - ): void { - defaultStore.set('tl', { - ...defaultStore.state.tl, - src: newSrc, - }); - } - - function saveDiscoverSrc( - newSrc: 'hot' | 'recommended' | 'global', - ): void { - defaultStore.set('discoverTl', { - ...defaultStore.state.discoverTl, - src: newSrc - }) - } - - const headerActions = $computed(() => [ - { - icon: 'ph-list-bullets ph-bold ph-lg', - title: i18n.ts.lists, - text: i18n.ts.lists, - iconOnly: true, - handler: chooseList, - }, - { - icon: 'ph-flying-saucer ph-bold ph-lg', - title: i18n.ts.antennas, - text: i18n.ts.antennas, - iconOnly: true, - handler: chooseAntenna, - } /* **TODO: fix timetravel** { + })) + ); + os.popupMenu(items, ev.currentTarget ?? ev.target); +} + +function saveSrc(newSrc: "forYou" | "discover"): void { + defaultStore.set("tl", { + ...defaultStore.state.tl, + src: newSrc, + }); +} + +function saveDiscoverSrc(newSrc: "hot" | "recommended" | "global"): void { + defaultStore.set("discoverTl", { + ...defaultStore.state.discoverTl, + src: newSrc, + }); +} + +const headerActions = $computed(() => [ + { + icon: "ph-list-bullets ph-bold ph-lg", + title: i18n.ts.lists, + text: i18n.ts.lists, + iconOnly: true, + handler: chooseList, + }, + { + icon: "ph-flying-saucer ph-bold ph-lg", + title: i18n.ts.antennas, + text: i18n.ts.antennas, + iconOnly: true, + handler: chooseAntenna, + } /* **TODO: fix timetravel** { icon: 'ph-calendar-blank ph-bold ph-lg', title: i18n.ts.jumpToSpecifiedDate, iconOnly: true, handler: timetravel, }*/, - ]); - - const headerTabs = $computed(() => [ - { - key: 'forYou', - title: i18n.ts._timelines.forYou, - icon: 'ph-house ph-bold ph-lg', - }, - { - key: 'discover', - title: i18n.ts._timelines.discover, - icon: 'ph-planet ph-bold ph-lg', - } - ]); - - definePageMetadata( - computed(() => ({ - title: i18n.ts.timeline, - icon: - src === 'discover' - ? 'ph-planet ph-bold ph-lg' - : 'ph-house ph-bold ph-lg', - })), +]); + +const headerTabs = $computed(() => [ + { + key: "forYou", + title: i18n.ts._timelines.forYou, + icon: "ph-house ph-bold ph-lg", + }, + { + key: "discover", + title: i18n.ts._timelines.discover, + icon: "ph-planet ph-bold ph-lg", + }, +]); + +definePageMetadata( + computed(() => ({ + title: i18n.ts.timeline, + icon: + src === "discover" + ? "ph-planet ph-bold ph-lg" + : "ph-house ph-bold ph-lg", + })) +); + +let swiperRef: any = null; + +function setSwiperRef(swiper) { + swiperRef = swiper; + syncSlide(timelines.indexOf(src)); +} + +function onSlideChange() { + saveSrc(timelines[swiperRef.activeIndex]); +} + +function syncSlide(index) { + swiperRef.slideTo(index); +} + +onMounted(() => { + syncSlide( + timelines.indexOf(defaultStore.state.tl?.src || swiperRef.activeIndex) ); - - let swiperRef: any = null; - - function setSwiperRef(swiper) { - swiperRef = swiper; - syncSlide(timelines.indexOf(src)); - } - - function onSlideChange() { - saveSrc(timelines[swiperRef.activeIndex]); - } - - function syncSlide(index) { - swiperRef.slideTo(index); - } - - onMounted(() => { - syncSlide(timelines.indexOf(defaultStore.state.tl?.src || swiperRef.activeIndex)); - }); - - </script> - - <style lang="scss" scoped> - .cmuxhskf { - --swiper-theme-color: var(--accent); - - > .new { - position: sticky; - top: calc(var(--stickyTop, 0px) + 16px); - z-index: 1000; - width: 100%; - pointer-events: none; - - > button { - display: block; - margin: var(--margin) auto 0 auto; - padding: 8px 16px; - border-radius: 32px; - pointer-events: all; - } - } - - > .post-form { - border-radius: var(--radius); - } - - > .tl { - background: var(--bg); - border-radius: var(--radius); - overflow: clip; +}); +</script> + +<style lang="scss" scoped> +.cmuxhskf { + --swiper-theme-color: var(--accent); + + > .new { + position: sticky; + top: calc(var(--stickyTop, 0px) + 16px); + z-index: 1000; + width: 100%; + pointer-events: none; + + > button { + display: block; + margin: var(--margin) auto 0 auto; + padding: 8px 16px; + border-radius: 32px; + pointer-events: all; } } - </style> + + > .post-form { + border-radius: var(--radius); + } + + > .tl { + background: var(--bg); + border-radius: var(--radius); + overflow: clip; + } +} +</style>