timelineBackTopBehavior
This commit is contained in:
parent
9f79e494f5
commit
4785b9bfdd
7 changed files with 38 additions and 10 deletions
5
locales/index.d.ts
vendored
5
locales/index.d.ts
vendored
|
@ -1097,6 +1097,7 @@ export interface Locale {
|
||||||
"doYouAgree": string;
|
"doYouAgree": string;
|
||||||
"beSureToReadThisAsItIsImportant": string;
|
"beSureToReadThisAsItIsImportant": string;
|
||||||
"iHaveReadXCarefullyAndAgree": string;
|
"iHaveReadXCarefullyAndAgree": string;
|
||||||
|
"timelineBackTopBehavior": string;
|
||||||
"_initialAccountSetting": {
|
"_initialAccountSetting": {
|
||||||
"accountCreated": string;
|
"accountCreated": string;
|
||||||
"letsStartAccountSetup": string;
|
"letsStartAccountSetup": string;
|
||||||
|
@ -1637,6 +1638,10 @@ export interface Locale {
|
||||||
"dialog": string;
|
"dialog": string;
|
||||||
"quiet": string;
|
"quiet": string;
|
||||||
};
|
};
|
||||||
|
"_timelineBackTopBehavior": {
|
||||||
|
"newest": string;
|
||||||
|
"next": string;
|
||||||
|
};
|
||||||
"_channel": {
|
"_channel": {
|
||||||
"create": string;
|
"create": string;
|
||||||
"edit": string;
|
"edit": string;
|
||||||
|
|
|
@ -1094,6 +1094,7 @@ expired: "期限切れ"
|
||||||
doYouAgree: "同意しますか?"
|
doYouAgree: "同意しますか?"
|
||||||
beSureToReadThisAsItIsImportant: "重要ですので必ずお読みください。"
|
beSureToReadThisAsItIsImportant: "重要ですので必ずお読みください。"
|
||||||
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよく読み、同意します。"
|
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよく読み、同意します。"
|
||||||
|
timelineBackTopBehavior: "タイムラインのスクロールが先頭に戻った時の挙動"
|
||||||
|
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "アカウントの作成が完了しました!"
|
accountCreated: "アカウントの作成が完了しました!"
|
||||||
|
@ -1555,6 +1556,10 @@ _serverDisconnectedBehavior:
|
||||||
dialog: "ダイアログで警告"
|
dialog: "ダイアログで警告"
|
||||||
quiet: "控えめに警告"
|
quiet: "控えめに警告"
|
||||||
|
|
||||||
|
_timelineBackTopBehavior:
|
||||||
|
newest: "最新の投稿を表示"
|
||||||
|
next: "次の投稿を遡る"
|
||||||
|
|
||||||
_channel:
|
_channel:
|
||||||
create: "チャンネルを作成"
|
create: "チャンネルを作成"
|
||||||
edit: "チャンネルを編集"
|
edit: "チャンネルを編集"
|
||||||
|
|
|
@ -47,7 +47,7 @@ import MkButton from '@/components/MkButton.vue';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
import { MisskeyEntity } from '@/types/date-separated-list';
|
import { MisskeyEntity } from '@/types/date-separated-list';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { UAParser } from 'ua-parser-js';
|
import { isWebKit } from '@/scripts/useragent';
|
||||||
|
|
||||||
const SECOND_FETCH_LIMIT = 30;
|
const SECOND_FETCH_LIMIT = 30;
|
||||||
const TOLERANCE = 6;
|
const TOLERANCE = 6;
|
||||||
|
@ -94,8 +94,7 @@ function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): M
|
||||||
return new Map([...map, ...arrayToEntries(entities)]);
|
return new Map([...map, ...arrayToEntries(entities)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ua = new UAParser(navigator.userAgent);
|
const timelineBackTopBehavior = computed(() => isWebKit() ? 'newest' : defaultStore.reactiveState.timelineBackTopBehavior.value);
|
||||||
const isWebKit = ua.getEngine().name === 'WebKit';
|
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { infoImageUrl } from '@/instance';
|
import { infoImageUrl } from '@/instance';
|
||||||
|
@ -200,7 +199,7 @@ watch([$$(rootEl), $$(scrollObserver)], () => {
|
||||||
* weakBackedがtrue→falseになったらexecuteQueue
|
* weakBackedがtrue→falseになったらexecuteQueue
|
||||||
*/
|
*/
|
||||||
watch($$(weakBacked), () => {
|
watch($$(weakBacked), () => {
|
||||||
if (!isWebKit && !weakBacked) {
|
if (timelineBackTopBehavior.value === 'next' && !weakBacked) {
|
||||||
executeQueue();
|
executeQueue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -499,7 +498,7 @@ const prepend = (item: MisskeyEntity): void => {
|
||||||
// かなりスクロールの先頭にいる場合
|
// かなりスクロールの先頭にいる場合
|
||||||
if (items.value.has(item.id)) return; // 既にタイムラインにある場合は何もしない
|
if (items.value.has(item.id)) return; // 既にタイムラインにある場合は何もしない
|
||||||
unshiftItems([item]);
|
unshiftItems([item]);
|
||||||
} else if (!isWebKit && !weakBacked) {
|
} else if (timelineBackTopBehavior.value === 'next' && !weakBacked) {
|
||||||
// ちょっと先頭にいる場合はスクロールを調整する
|
// ちょっと先頭にいる場合はスクロールを調整する
|
||||||
prependQueue(item);
|
prependQueue(item);
|
||||||
executeQueue();
|
executeQueue();
|
||||||
|
@ -538,7 +537,7 @@ function concatItems(oldItems: MisskeyEntity[]) {
|
||||||
async function executeQueue() {
|
async function executeQueue() {
|
||||||
if (queue.value.size === 0) return;
|
if (queue.value.size === 0) return;
|
||||||
if (isPausingUpdateByExecutingQueue.value) return;
|
if (isPausingUpdateByExecutingQueue.value) return;
|
||||||
if (isWebKit) {
|
if (timelineBackTopBehavior === 'newest') {
|
||||||
// Safariは最新のアイテムにするだけ
|
// Safariは最新のアイテムにするだけ
|
||||||
const newItems = Array.from(queue.value.values()).slice(-1 * props.pagination.limit);
|
const newItems = Array.from(queue.value.values()).slice(-1 * props.pagination.limit);
|
||||||
unshiftItems(newItems);
|
unshiftItems(newItems);
|
||||||
|
|
|
@ -21,10 +21,18 @@
|
||||||
</MkRadios>
|
</MkRadios>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
|
<div class="_gaps_s">
|
||||||
<MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
|
<MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
|
||||||
<MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch>
|
<MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
|
||||||
|
<MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkSelect v-model="timelineBackTopBehavior" :disabled="isWebKit()">
|
||||||
|
<template #label>{{ i18n.ts.timelineBackTopBehavior }}</template>
|
||||||
|
<option value="newest">{{ i18n.ts._timelineBackTopBehavior.newest }}</option>
|
||||||
|
<option value="next">{{ i18n.ts._timelineBackTopBehavior.next }}</option>
|
||||||
|
</MkSelect>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
|
@ -180,6 +188,7 @@ import { unisonReload } from '@/scripts/unison-reload';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { miLocalStorage } from '@/local-storage';
|
import { miLocalStorage } from '@/local-storage';
|
||||||
|
import { isWebKit } from '@/scripts/useragent';
|
||||||
|
|
||||||
const lang = ref(miLocalStorage.getItem('lang'));
|
const lang = ref(miLocalStorage.getItem('lang'));
|
||||||
const fontSize = ref(miLocalStorage.getItem('fontSize'));
|
const fontSize = ref(miLocalStorage.getItem('fontSize'));
|
||||||
|
@ -226,6 +235,7 @@ const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('
|
||||||
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
|
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
|
||||||
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
|
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
|
||||||
const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies'));
|
const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies'));
|
||||||
|
const timelineBackTopBehavior = computed(defaultStore.makeGetterSetter('timelineBackTopBehavior'));
|
||||||
|
|
||||||
watch(lang, () => {
|
watch(lang, () => {
|
||||||
miLocalStorage.setItem('lang', lang.value as string);
|
miLocalStorage.setItem('lang', lang.value as string);
|
||||||
|
|
|
@ -87,6 +87,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
||||||
'numberOfPageCache',
|
'numberOfPageCache',
|
||||||
'aiChanMode',
|
'aiChanMode',
|
||||||
'mediaListWithOneImageAppearance',
|
'mediaListWithOneImageAppearance',
|
||||||
|
'timelineBackTopBehavior',
|
||||||
];
|
];
|
||||||
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
||||||
'lightTheme',
|
'lightTheme',
|
||||||
|
|
3
packages/frontend/src/scripts/useragent.ts
Normal file
3
packages/frontend/src/scripts/useragent.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { UAParser } from 'ua-parser-js';
|
||||||
|
const ua = new UAParser(navigator.userAgent);
|
||||||
|
export const isWebKit = () => ua.getEngine().name === 'WebKit';
|
|
@ -1,5 +1,6 @@
|
||||||
import { markRaw, ref } from 'vue';
|
import { markRaw, ref } from 'vue';
|
||||||
import { Storage } from './pizzax';
|
import { Storage } from './pizzax';
|
||||||
|
import { isWebKit } from './scripts/useragent';
|
||||||
|
|
||||||
interface PostFormAction {
|
interface PostFormAction {
|
||||||
title: string,
|
title: string,
|
||||||
|
@ -342,6 +343,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: {} as Record<string, Record<string, string[]>>,
|
default: {} as Record<string, Record<string, string[]>>,
|
||||||
},
|
},
|
||||||
|
timelineBackTopBehavior: {
|
||||||
|
where: 'device',
|
||||||
|
default: (isWebKit() ? 'newest' : 'next') as 'newest' | 'next',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
Loading…
Reference in a new issue