chore: format

This commit is contained in:
Kainoa Kanter 2023-04-07 17:18:13 -07:00
parent cda17e71b0
commit 6d072bbdc7
8 changed files with 510 additions and 399 deletions

View File

@ -10,7 +10,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, provide, onUnmounted } from "vue"; import { computed, provide, onUnmounted } from "vue";
import XNotes from "@/components/MkNotes.vue"; import XNotes from "@/components/MkNotes.vue";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import { stream } from "@/stream"; import { stream } from "@/stream";
import * as sound from "@/scripts/sound"; import * as sound from "@/scripts/sound";
import { $i } from "@/account"; import { $i } from "@/account";
@ -97,14 +97,14 @@ if (props.src === "antenna") {
endpoint = "notes/global-timeline"; endpoint = "notes/global-timeline";
connection = stream.useChannel("globalTimeline"); connection = stream.useChannel("globalTimeline");
connection.on("note", prepend); connection.on("note", prepend);
} else if (props.src === 'featured') { } else if (props.src === "featured") {
endpoint = 'notes/featured'; endpoint = "notes/featured";
query = { query = {
origin: 'combined', origin: "combined",
offsetMode: true, offsetMode: true,
days: 5, days: 5,
limit: 10, limit: 10,
} };
} else if (props.src === "mentions") { } else if (props.src === "mentions") {
endpoint = "notes/mentions"; endpoint = "notes/mentions";
connection = stream.useChannel("main"); connection = stream.useChannel("main");

View File

@ -154,16 +154,22 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, defineComponent, ref, toRef } from 'vue'; import {
import MkButton from '@/components/MkButton.vue'; computed,
import MkInput from '@/components/form/input.vue'; defineAsyncComponent,
import MkPagination from '@/components/MkPagination.vue'; defineComponent,
import MkSwitch from '@/components/form/switch.vue'; ref,
import FormSplit from '@/components/form/split.vue'; toRef,
import { selectFile, selectFiles } from '@/scripts/select-file'; } from "vue";
import * as os from '@/os'; import MkButton from "@/components/MkButton.vue";
import { i18n } from '@/i18n'; import MkInput from "@/components/form/input.vue";
import { definePageMetadata } from '@/scripts/page-metadata'; 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>>(); const emojisPaginationComponent = ref<InstanceType<typeof MkPagination>>();

View File

@ -1,26 +1,36 @@
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header
<div class="lznhrdub"> ><MkPageHeader
<MkSpacer :content-max="1200"> v-model:tab="tab"
<swiper :actions="headerActions"
:modules="[Virtual]" :tabs="headerTabs"
:space-between="20" /></template>
:virtual="true" <div class="lznhrdub">
:allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" <MkSpacer :content-max="1200">
@swiper="setSwiperRef" <swiper
@slide-change="onSlideChange" :modules="[Virtual]"
:space-between="20"
:virtual="true"
:allow-touch-move="
!(
deviceKind === 'desktop' &&
!defaultStore.state.swipeOnDesktop
)
"
@swiper="setSwiperRef"
@slide-change="onSlideChange"
> >
<swiper-slide> <swiper-slide>
<XUsers/> <XUsers />
</swiper-slide> </swiper-slide>
<swiper-slide> <swiper-slide>
<XFeatured/> <XFeatured />
</swiper-slide> </swiper-slide>
</swiper> </swiper>
</MkSpacer> </MkSpacer>
</div> </div>
</MkStickyContainer> </MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -41,7 +51,7 @@ const props = defineProps<{
tag?: string; tag?: string;
}>(); }>();
const tabs = ['users', 'featured']; const tabs = ["users", "featured"];
let tab = $ref(tabs[0]); let tab = $ref(tabs[0]);
watch($$(tab), () => syncSlide(tabs.indexOf(tab))); watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
@ -56,15 +66,18 @@ watch(
const headerActions = $computed(() => []); const headerActions = $computed(() => []);
const headerTabs = $computed(() => [{ const headerTabs = $computed(() => [
key: 'users', {
icon: 'ph-users ph-bold ph-lg', key: "users",
title: i18n.ts.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, key: "featured",
}]); icon: "ph-lightning ph-bold ph-lg",
title: i18n.ts.featured,
},
]);
definePageMetadata( definePageMetadata(
computed(() => ({ computed(() => ({

View File

@ -1,42 +1,19 @@
<template> <template>
<div class="_formRoot"> <div class="_formRoot">
<FormSelect v-model="lang" class="_formBlock"> <FormSelect v-model="lang" class="_formBlock">
<template #label>{{ i18n.ts.uiLanguage }}</template> <template #label>{{ i18n.ts.uiLanguage }}</template>
<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> <option v-for="x in langs" :key="x[0]" :value="x[0]">
<template #caption> {{ x[1] }}
<I18n :src="i18n.ts.i18nInfo" tag="span"> </option>
<template #link> <template #caption>
<MkLink url="https://hosted.weblate.org/engage/calckey/">Weblate</MkLink> <I18n :src="i18n.ts.i18nInfo" tag="span">
</template> <template #link>
</I18n> <MkLink url="https://hosted.weblate.org/engage/calckey/"
</template> >Weblate</MkLink
</FormSelect> >
</template>
<FormRadios v-model="overridedDeviceKind" class="_formBlock"> </I18n>
<template #label>{{ i18n.ts.overridedDeviceKind }}</template> </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> </FormSelect>
<FormRadios v-model="overridedDeviceKind" class="_formBlock"> <FormRadios v-model="overridedDeviceKind" class="_formBlock">
@ -55,17 +32,6 @@
</option> </option>
</FormRadios> </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">{{ <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{
i18n.ts.showFixedPostForm i18n.ts.showFixedPostForm
}}</FormSwitch> }}</FormSwitch>
@ -108,6 +74,71 @@
{{ i18n.ts._serverDisconnectedBehavior.nothing }} {{ i18n.ts._serverDisconnectedBehavior.nothing }}
</option> </option>
</FormSelect> </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>
<FormSection> <FormSection>
@ -260,32 +291,76 @@ async function reloadAsk() {
unisonReload(); unisonReload();
} }
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); const overridedDeviceKind = computed(
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); defaultStore.makeGetterSetter("overridedDeviceKind")
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); );
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); const serverDisconnectedBehavior = computed(
const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); defaultStore.makeGetterSetter("serverDisconnectedBehavior")
const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline')); );
const showAds = computed(defaultStore.makeGetterSetter('showAds')); const reduceAnimation = computed(
const disableAnimatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v)); defaultStore.makeGetterSetter(
const useOsNativeEmojis = computed(defaultStore.makeGetterSetter('useOsNativeEmojis')); "animation",
const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); (v) => !v,
const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); (v) => !v
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); )
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); );
const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const useBlurEffectForModal = computed(
const disablePagesScript = computed(defaultStore.makeGetterSetter('disablePagesScript')); defaultStore.makeGetterSetter("useBlurEffectForModal")
const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); );
const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); const useBlurEffect = computed(defaultStore.makeGetterSetter("useBlurEffect"));
const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); const showGapBetweenNotesInTimeline = computed(
const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); defaultStore.makeGetterSetter("showGapBetweenNotesInTimeline")
const enterSendsMessage = computed(defaultStore.makeGetterSetter('enterSendsMessage')); );
const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); const showAds = computed(defaultStore.makeGetterSetter("showAds"));
const seperateRenoteQuote = computed(defaultStore.makeGetterSetter('seperateRenoteQuote')); const disableAnimatedMfm = computed(
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); defaultStore.makeGetterSetter(
const showUpdates = computed(defaultStore.makeGetterSetter('showUpdates')); "animatedMfm",
const swipeOnDesktop = computed(defaultStore.makeGetterSetter('swipeOnDesktop')); (v) => !v,
const showAdminUpdates = computed(defaultStore.makeGetterSetter('showAdminUpdates')); (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, () => { watch(lang, () => {
localStorage.setItem("lang", lang.value as string); localStorage.setItem("lang", lang.value as string);
@ -308,23 +383,26 @@ watch(useSystemFont, () => {
} }
}); });
watch([ watch(
lang, [
fontSize, lang,
useSystemFont, fontSize,
enableInfiniteScroll, useSystemFont,
squareAvatars, enableInfiniteScroll,
showGapBetweenNotesInTimeline, squareAvatars,
instanceTicker, showGapBetweenNotesInTimeline,
overridedDeviceKind, instanceTicker,
showAds, overridedDeviceKind,
showUpdates, showAds,
swipeOnDesktop, showUpdates,
seperateRenoteQuote, swipeOnDesktop,
showAdminUpdates, seperateRenoteQuote,
], async () => { showAdminUpdates,
await reloadAsk(); ],
}); async () => {
await reloadAsk();
}
);
const headerActions = $computed(() => []); const headerActions = $computed(() => []);

View File

@ -73,48 +73,48 @@ const { t, ts } = i18n;
useCssModule(); useCssModule();
const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ const defaultStoreSaveKeys: (keyof (typeof defaultStore)["state"])[] = [
'menu', "menu",
'visibility', "visibility",
'localOnly', "localOnly",
'statusbars', "statusbars",
'widgets', "widgets",
'tl', "tl",
'forYouTl', "forYouTl",
'discoverTl', "discoverTl",
'overridedDeviceKind', "overridedDeviceKind",
'serverDisconnectedBehavior', "serverDisconnectedBehavior",
'nsfw', "nsfw",
'showAds', "showAds",
'animation', "animation",
'animatedMfm', "animatedMfm",
'loadRawImages', "loadRawImages",
'imageNewTab', "imageNewTab",
'disableShowingAnimatedImages', "disableShowingAnimatedImages",
'disablePagesScript', "disablePagesScript",
'enterSendsMessage', "enterSendsMessage",
'useOsNativeEmojis', "useOsNativeEmojis",
'disableDrawer', "disableDrawer",
'useBlurEffectForModal', "useBlurEffectForModal",
'useBlurEffect', "useBlurEffect",
'showFixedPostForm', "showFixedPostForm",
'enableInfiniteScroll', "enableInfiniteScroll",
'useReactionPickerForContextMenu', "useReactionPickerForContextMenu",
'showGapBetweenNotesInTimeline', "showGapBetweenNotesInTimeline",
'instanceTicker', "instanceTicker",
'reactionPickerSize', "reactionPickerSize",
'reactionPickerWidth', "reactionPickerWidth",
'reactionPickerHeight', "reactionPickerHeight",
'reactionPickerUseDrawerForMobile', "reactionPickerUseDrawerForMobile",
'defaultSideView', "defaultSideView",
'menuDisplay', "menuDisplay",
'reportError', "reportError",
'squareAvatars', "squareAvatars",
'numberOfPageCache', "numberOfPageCache",
'showUpdates', "showUpdates",
'swipeOnDesktop', "swipeOnDesktop",
'showAdminUpdates', "showAdminUpdates",
'enableCustomKaTeXMacro', "enableCustomKaTeXMacro",
]; ];
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
"lightTheme", "lightTheme",

View File

@ -1,9 +1,13 @@
<template> <template>
<MkSpacer :content-max="800"> <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 value="hot">{{ i18n.ts._timelines.hot }}</option>
<option v-if="isRecommendedTimelineAvailable" value="recommended">{{ i18n.ts._timelines.recommended }}</option> <option v-if="isRecommendedTimelineAvailable" value="recommended">
<option v-if="isGlobalTimelineAvailable" value="global">{{ i18n.ts._timelines.global }}</option> {{ i18n.ts._timelines.recommended }}
</option>
<option v-if="isGlobalTimelineAvailable" value="global">
{{ i18n.ts._timelines.global }}
</option>
</MkTab> </MkTab>
<XTimeline <XTimeline
v-if="tab === 'hot'" v-if="tab === 'hot'"
@ -30,25 +34,23 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import XTimeline from '@/components/MkTimeline.vue'; import XTimeline from "@/components/MkTimeline.vue";
import MkTab from '@/components/MkTab.vue'; import MkTab from "@/components/MkTab.vue";
import { defaultStore } from '@/store'; import { defaultStore } from "@/store";
import { i18n } from '@/i18n'; import { i18n } from "@/i18n";
import { instance } from '@/instance'; import { instance } from "@/instance";
import { $i } from '@/account'; import { $i } from "@/account";
const tab = $computed({ const tab = $computed({
get: () => defaultStore.reactiveState.discoverTl.value.src, get: () => defaultStore.reactiveState.discoverTl.value.src,
set: (x) => saveSrc(x), set: (x) => saveSrc(x),
}); });
function saveSrc( function saveSrc(newSrc: "hot" | "recommended" | "global"): void {
newSrc: 'hot' | 'recommended' | 'global', defaultStore.set("discoverTl", {
): void {
defaultStore.set('discoverTl', {
...defaultStore.state.discoverTl, ...defaultStore.state.discoverTl,
src: newSrc src: newSrc,
}) });
} }
const isRecommendedTimelineAvailable = !instance.disableRecommendedTimeline; const isRecommendedTimelineAvailable = !instance.disableRecommendedTimeline;

View File

@ -1,9 +1,13 @@
<template> <template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<MkTab v-model="tab" style="margin-bottom: var(--margin);"> <MkTab v-model="tab" style="margin-bottom: var(--margin)">
<option v-if="isLocalTimelineAvailable" value="social">{{ i18n.ts._timelines.social }}</option> <option v-if="isLocalTimelineAvailable" value="social">
{{ i18n.ts._timelines.social }}
</option>
<option value="home">{{ i18n.ts._timelines.home }}</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> </MkTab>
<XTimeline <XTimeline
v-if="tab === 'social'" v-if="tab === 'social'"
@ -30,25 +34,23 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import XTimeline from '@/components/MkTimeline.vue'; import XTimeline from "@/components/MkTimeline.vue";
import MkTab from '@/components/MkTab.vue'; import MkTab from "@/components/MkTab.vue";
import { defaultStore } from '@/store'; import { defaultStore } from "@/store";
import { i18n } from '@/i18n'; import { i18n } from "@/i18n";
import { instance } from '@/instance'; import { instance } from "@/instance";
import { $i } from '@/account'; import { $i } from "@/account";
const tab = $computed({ const tab = $computed({
get: () => defaultStore.reactiveState.forYouTl.value.src, get: () => defaultStore.reactiveState.forYouTl.value.src,
set: (x) => saveSrc(x), set: (x) => saveSrc(x),
}); });
function saveSrc( function saveSrc(newSrc: "home" | "local" | "social"): void {
newSrc: 'home' | 'local' | 'social', defaultStore.set("forYouTl", {
): void {
defaultStore.set('forYouTl', {
...defaultStore.state.forYouTl, ...defaultStore.state.forYouTl,
src: newSrc src: newSrc,
}) });
} }
const isLocalTimelineAvailable = const isLocalTimelineAvailable =

View File

@ -36,218 +36,228 @@
:modules="[Virtual]" :modules="[Virtual]"
:space-between="20" :space-between="20"
:virtual="true" :virtual="true"
:allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" :allow-touch-move="
!(
deviceKind === 'desktop' &&
!defaultStore.state.swipeOnDesktop
)
"
@swiper="setSwiperRef" @swiper="setSwiperRef"
@slide-change="onSlideChange" @slide-change="onSlideChange"
> >
<swiper-slide> <swiper-slide>
<XForYou/> <XForYou />
</swiper-slide> </swiper-slide>
<swiper-slide> <swiper-slide>
<XDiscover/> <XDiscover />
</swiper-slide> </swiper-slide>
</swiper> </swiper>
</div> </div>
</div> </div>
</MkSpacer> </MkSpacer>
</MkStickyContainer> </MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, onMounted } from 'vue'; import { computed, ref, onMounted } from "vue";
import { Virtual } from 'swiper'; import { Virtual } from "swiper";
import { Swiper, SwiperSlide } from 'swiper/vue'; import { Swiper, SwiperSlide } from "swiper/vue";
import XTutorial from '@/components/MkTutorialDialog.vue'; import XTutorial from "@/components/MkTutorialDialog.vue";
import XPostForm from '@/components/MkPostForm.vue'; import XPostForm from "@/components/MkPostForm.vue";
import XForYou from './timeline.foryou.vue'; import XForYou from "./timeline.foryou.vue";
import XDiscover from './timeline.discover.vue'; import XDiscover from "./timeline.discover.vue";
import { scroll } from '@/scripts/scroll'; import { scroll } from "@/scripts/scroll";
import * as os from '@/os'; import * as os from "@/os";
import { defaultStore } from '@/store'; import { defaultStore } from "@/store";
import { i18n } from '@/i18n'; import { i18n } from "@/i18n";
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from "@/scripts/page-metadata";
import { deviceKind } from '@/scripts/device-kind'; import { deviceKind } from "@/scripts/device-kind";
import 'swiper/scss'; import "swiper/scss";
import 'swiper/scss/virtual'; import "swiper/scss/virtual";
if (defaultStore.reactiveState.tutorial.value !== -1) { if (defaultStore.reactiveState.tutorial.value !== -1) {
os.popup(XTutorial, {}, {}, 'closed'); os.popup(XTutorial, {}, {}, "closed");
} }
let timelines = ['forYou', 'discover']; let timelines = ["forYou", "discover"];
const MOBILE_THRESHOLD = 500; const MOBILE_THRESHOLD = 500;
// UI deviceKind === 'desktop' // UI deviceKind === 'desktop'
const isMobile = ref( const isMobile = ref(
deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD, deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD
); );
window.addEventListener('resize', () => { window.addEventListener("resize", () => {
isMobile.value = isMobile.value =
(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD;
}); });
const rootEl = $ref<HTMLElement>(); const rootEl = $ref<HTMLElement>();
let queue = $ref(0); let queue = $ref(0);
const src = $computed({ const src = $computed({
get: () => defaultStore.reactiveState.tl.value.src, get: () => defaultStore.reactiveState.tl.value.src,
set: (x) => { set: (x) => {
saveSrc(x); saveSrc(x);
syncSlide(timelines.indexOf(x)); syncSlide(timelines.indexOf(x));
}, },
}); });
function top(): void { function top(): void {
scroll(rootEl!, { top: 0 }); scroll(rootEl!, { top: 0 });
} }
async function chooseList(ev: MouseEvent): Promise<void> { async function chooseList(ev: MouseEvent): Promise<void> {
const lists = await os.api('users/lists/list'); const lists = await os.api("users/lists/list");
const items = [{ const items = [
type: 'link' as const, {
type: "link" as const,
text: i18n.ts.manageLists, text: i18n.ts.manageLists,
icon: 'ph-faders-horizontal ph-bold ph-lg', icon: "ph-faders-horizontal ph-bold ph-lg",
to: '/my/lists', to: "/my/lists",
}].concat(lists.map((list) => ({ },
type: 'link' as const, ].concat(
lists.map((list) => ({
type: "link" as const,
text: list.name, text: list.name,
icon: '', icon: "",
to: `/timeline/list/${list.id}`, to: `/timeline/list/${list.id}`,
}))); }))
os.popupMenu(items, ev.currentTarget ?? ev.target); );
} os.popupMenu(items, ev.currentTarget ?? ev.target);
}
async function chooseAntenna(ev: MouseEvent): Promise<void> { async function chooseAntenna(ev: MouseEvent): Promise<void> {
const antennas = await os.api('antennas/list'); const antennas = await os.api("antennas/list");
const items = [{ const items = [
type: 'link' as const, {
type: "link" as const,
indicate: false, indicate: false,
text: i18n.ts.manageAntennas, text: i18n.ts.manageAntennas,
icon: 'ph-faders-horizontal ph-bold ph-lg', icon: "ph-faders-horizontal ph-bold ph-lg",
to: '/my/antennas', to: "/my/antennas",
}].concat(antennas.map((antenna) => ({ },
type: 'link' as const, ].concat(
antennas.map((antenna) => ({
type: "link" as const,
text: antenna.name, text: antenna.name,
icon: '', icon: "",
indicate: antenna.hasUnreadNote, indicate: antenna.hasUnreadNote,
to: `/timeline/antenna/${antenna.id}`, to: `/timeline/antenna/${antenna.id}`,
}))); }))
os.popupMenu(items, ev.currentTarget ?? ev.target); );
} os.popupMenu(items, ev.currentTarget ?? ev.target);
}
function saveSrc( function saveSrc(newSrc: "forYou" | "discover"): void {
newSrc: 'forYou' | 'discover', defaultStore.set("tl", {
): void { ...defaultStore.state.tl,
defaultStore.set('tl', { src: newSrc,
...defaultStore.state.tl, });
src: newSrc, }
});
}
function saveDiscoverSrc( function saveDiscoverSrc(newSrc: "hot" | "recommended" | "global"): void {
newSrc: 'hot' | 'recommended' | 'global', defaultStore.set("discoverTl", {
): void { ...defaultStore.state.discoverTl,
defaultStore.set('discoverTl', { src: newSrc,
...defaultStore.state.discoverTl, });
src: newSrc }
})
}
const headerActions = $computed(() => [ const headerActions = $computed(() => [
{ {
icon: 'ph-list-bullets ph-bold ph-lg', icon: "ph-list-bullets ph-bold ph-lg",
title: i18n.ts.lists, title: i18n.ts.lists,
text: i18n.ts.lists, text: i18n.ts.lists,
iconOnly: true, iconOnly: true,
handler: chooseList, handler: chooseList,
}, },
{ {
icon: 'ph-flying-saucer ph-bold ph-lg', icon: "ph-flying-saucer ph-bold ph-lg",
title: i18n.ts.antennas, title: i18n.ts.antennas,
text: i18n.ts.antennas, text: i18n.ts.antennas,
iconOnly: true, iconOnly: true,
handler: chooseAntenna, handler: chooseAntenna,
} /* **TODO: fix timetravel** { } /* **TODO: fix timetravel** {
icon: 'ph-calendar-blank ph-bold ph-lg', icon: 'ph-calendar-blank ph-bold ph-lg',
title: i18n.ts.jumpToSpecifiedDate, title: i18n.ts.jumpToSpecifiedDate,
iconOnly: true, iconOnly: true,
handler: timetravel, handler: timetravel,
}*/, }*/,
]); ]);
const headerTabs = $computed(() => [ const headerTabs = $computed(() => [
{ {
key: 'forYou', key: "forYou",
title: i18n.ts._timelines.forYou, title: i18n.ts._timelines.forYou,
icon: 'ph-house ph-bold ph-lg', icon: "ph-house ph-bold ph-lg",
}, },
{ {
key: 'discover', key: "discover",
title: i18n.ts._timelines.discover, title: i18n.ts._timelines.discover,
icon: 'ph-planet ph-bold ph-lg', icon: "ph-planet ph-bold ph-lg",
} },
]); ]);
definePageMetadata( definePageMetadata(
computed(() => ({ computed(() => ({
title: i18n.ts.timeline, title: i18n.ts.timeline,
icon: icon:
src === 'discover' src === "discover"
? 'ph-planet ph-bold ph-lg' ? "ph-planet ph-bold ph-lg"
: 'ph-house 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)
); );
});
</script>
let swiperRef: any = null; <style lang="scss" scoped>
.cmuxhskf {
--swiper-theme-color: var(--accent);
function setSwiperRef(swiper) { > .new {
swiperRef = swiper; position: sticky;
syncSlide(timelines.indexOf(src)); top: calc(var(--stickyTop, 0px) + 16px);
} z-index: 1000;
width: 100%;
pointer-events: none;
function onSlideChange() { > button {
saveSrc(timelines[swiperRef.activeIndex]); display: block;
} margin: var(--margin) auto 0 auto;
padding: 8px 16px;
function syncSlide(index) { border-radius: 32px;
swiperRef.slideTo(index); pointer-events: all;
}
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;
} }
} }
</style>
> .post-form {
border-radius: var(--radius);
}
> .tl {
background: var(--bg);
border-radius: var(--radius);
overflow: clip;
}
}
</style>