summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-10-03 20:26:11 +0900
committerGitHub <noreply@github.com>2023-10-03 20:26:11 +0900
commit6277a5545c746fac15ee6b4fe58de2e354ed7fda (patch)
tree700b0d162795f03d644a15ba2375628d66f95d92 /packages/frontend/src
parentUpdate about-misskey.vue (diff)
downloadmisskey-6277a5545c746fac15ee6b4fe58de2e354ed7fda.tar.gz
misskey-6277a5545c746fac15ee6b4fe58de2e354ed7fda.tar.bz2
misskey-6277a5545c746fac15ee6b4fe58de2e354ed7fda.zip
feat: improve tl performance (#11946)
* wip * wip * wip * wip * wip * wip * Update NoteCreateService.ts * wip * wip * wip * wip * Update NoteCreateService.ts * wip * Update NoteCreateService.ts * wip * Update user-notes.ts * wip * wip * wip * Update NoteCreateService.ts * wip * Update timeline.ts * Update timeline.ts * Update timeline.ts * Update timeline.ts * Update timeline.ts * wip * Update timelines.ts * Update timelines.ts * Update timelines.ts * wip * wip * wip * Update timelines.ts * Update misskey-js.api.md * Update timelines.ts * Update timelines.ts * wip * wip * wip * Update timelines.ts * wip * Update timelines.ts * wip * test * Update activitypub.ts * refactor: UserListJoining -> UserListMembership * Update NoteCreateService.ts * wip
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkNote.vue4
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue4
-rw-r--r--packages/frontend/src/components/MkNoteSub.vue8
-rw-r--r--packages/frontend/src/components/MkTimeline.vue12
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue94
-rw-r--r--packages/frontend/src/pages/settings/word-mute.vue52
-rw-r--r--packages/frontend/src/pages/timeline.vue9
-rw-r--r--packages/frontend/src/pages/user/home.vue6
-rw-r--r--packages/frontend/src/pages/user/index.files.vue (renamed from packages/frontend/src/pages/user/index.photos.vue)32
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts13
-rw-r--r--packages/frontend/src/store.ts6
-rw-r--r--packages/frontend/src/ui/deck/deck-store.ts1
-rw-r--r--packages/frontend/src/ui/deck/tl-column.vue14
13 files changed, 106 insertions, 149 deletions
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index b397f3eee9..4860f42cdc 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -165,7 +165,7 @@ import { deepClone } from '@/scripts/clone.js';
import { useTooltip } from '@/scripts/use-tooltip.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { getNoteSummary } from '@/scripts/get-note-summary.js';
-import { MenuItem } from '@/types/menu';
+import { MenuItem } from '@/types/menu.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { shouldCollapsed } from '@/scripts/collapsed.js';
@@ -211,7 +211,7 @@ const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : n
const isLong = shouldCollapsed(appearNote);
const collapsed = ref(appearNote.cw == null && isLong);
const isDeleted = ref(false);
-const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
+const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
const translation = ref<any>(null);
const translating = ref(false);
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index ab8886e8ba..a9da5a3a62 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -214,7 +214,7 @@ import { useNoteCapture } from '@/scripts/use-note-capture.js';
import { deepClone } from '@/scripts/clone.js';
import { useTooltip } from '@/scripts/use-tooltip.js';
import { claimAchievement } from '@/scripts/achievements.js';
-import { MenuItem } from '@/types/menu';
+import { MenuItem } from '@/types/menu.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
@@ -258,7 +258,7 @@ let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note
const isMyRenote = $i && ($i.id === note.userId);
const showContent = ref(false);
const isDeleted = ref(false);
-const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
+const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
const translation = ref(null);
const translating = ref(false);
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 2a3cd9bf02..bc52101f42 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -49,9 +49,9 @@ import { notePage } from '@/filters/note.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
-import { userPage } from "@/filters/user";
-import { checkWordMute } from "@/scripts/check-word-mute";
-import { defaultStore } from "@/store";
+import { userPage } from '@/filters/user.js';
+import { checkWordMute } from '@/scripts/check-word-mute.js';
+import { defaultStore } from '@/store.js';
const props = withDefaults(defineProps<{
note: Misskey.entities.Note;
@@ -63,7 +63,7 @@ const props = withDefaults(defineProps<{
depth: 1,
});
-const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords));
+const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
let showContent = $ref(false);
let replies: Misskey.entities.Note[] = $ref([]);
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 1dcafd6be1..c4a34667ef 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -23,11 +23,9 @@ const props = withDefaults(defineProps<{
role?: string;
sound?: boolean;
withRenotes?: boolean;
- withReplies?: boolean;
onlyFiles?: boolean;
}>(), {
withRenotes: true,
- withReplies: false,
onlyFiles: false,
});
@@ -70,12 +68,10 @@ if (props.src === 'antenna') {
endpoint = 'notes/timeline';
query = {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('homeTimeline', {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
@@ -85,12 +81,10 @@ if (props.src === 'antenna') {
endpoint = 'notes/local-timeline';
query = {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('localTimeline', {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
@@ -98,12 +92,10 @@ if (props.src === 'antenna') {
endpoint = 'notes/hybrid-timeline';
query = {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('hybridTimeline', {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
@@ -111,12 +103,10 @@ if (props.src === 'antenna') {
endpoint = 'notes/global-timeline';
query = {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('globalTimeline', {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
@@ -140,13 +130,11 @@ if (props.src === 'antenna') {
endpoint = 'notes/user-list-timeline';
query = {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
listId: props.list,
};
connection = stream.useChannel('userList', {
withRenotes: props.withRenotes,
- withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
listId: props.list,
});
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index df13998f6b..b600f99fbc 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -29,16 +29,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<MkButton rounded primary style="margin: 0 auto;" @click="addUser()">{{ i18n.ts.addUser }}</MkButton>
- <div v-for="user in users" :key="user.id" :class="$style.userItem">
- <MkA :class="$style.userItemBody" :to="`${userPage(user)}`">
- <MkUserCardMini :user="user"/>
- </MkA>
- <button class="_button" :class="$style.remove" @click="removeUser(user, $event)"><i class="ti ti-x"></i></button>
- </div>
- <MkButton v-if="!fetching && queueUserIds.length !== 0" v-appear="enableInfiniteScroll ? fetchMoreUsers : null" :class="$style.more" :style="{ cursor: 'pointer' }" primary rounded @click="fetchMoreUsers">
- {{ i18n.ts.loadMore }}
- </MkButton>
- <MkLoading v-if="fetching" class="loading"/>
+
+ <MkPagination ref="paginationEl" :pagination="membershipsPagination">
+ <template #default="{ items }">
+ <div class="_gaps_s">
+ <div v-for="item in items" :key="item.id">
+ <div :class="$style.userItem">
+ <MkA :class="$style.userItemBody" :to="`${userPage(item.user)}`">
+ <MkUserCardMini :user="item.user"/>
+ </MkA>
+ <button class="_button" :class="$style.menu" @click="showMembershipMenu(item, $event)"><i class="ti ti-dots"></i></button>
+ <button class="_button" :class="$style.remove" @click="removeUser(item, $event)"><i class="ti ti-x"></i></button>
+ </div>
+ </div>
+ </div>
+ </template>
+ </MkPagination>
</div>
</MkFolder>
</div>
@@ -59,9 +65,11 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkInput from '@/components/MkInput.vue';
-import { userListsCache } from '@/cache';
+import { userListsCache } from '@/cache.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
+
const {
enableInfiniteScroll,
} = defaultStore.reactiveState;
@@ -70,40 +78,25 @@ const props = defineProps<{
listId: string;
}>();
-const FETCH_USERS_LIMIT = 20;
-
+const paginationEl = ref<InstanceType<typeof MkPagination>>();
let list = $ref<Misskey.entities.UserList | null>(null);
-let users = $ref<Misskey.entities.UserLite[]>([]);
-let queueUserIds = $ref<string[]>([]);
-let fetching = $ref(true);
const isPublic = ref(false);
const name = ref('');
+const membershipsPagination = {
+ endpoint: 'users/lists/get-memberships' as const,
+ limit: 30,
+ params: computed(() => ({
+ listId: props.listId,
+ })),
+};
function fetchList() {
- fetching = true;
os.api('users/lists/show', {
listId: props.listId,
}).then(_list => {
list = _list;
name.value = list.name;
isPublic.value = list.isPublic;
- queueUserIds = list.userIds;
-
- return fetchMoreUsers();
- });
-}
-
-function fetchMoreUsers() {
- if (!list) return;
- if (fetching && users.length !== 0) return; // fetchingがtrueならやめるが、usersが空なら続行
- fetching = true;
- os.api('users/show', {
- userIds: queueUserIds.slice(0, FETCH_USERS_LIMIT),
- }).then(_users => {
- users = users.concat(_users);
- queueUserIds = queueUserIds.slice(FETCH_USERS_LIMIT);
- }).finally(() => {
- fetching = false;
});
}
@@ -114,12 +107,12 @@ function addUser() {
listId: list.id,
userId: user.id,
}).then(() => {
- users.push(user);
+ paginationEl.value.reload();
});
});
}
-async function removeUser(user, ev) {
+async function removeUser(item, ev) {
os.popupMenu([{
text: i18n.ts.remove,
icon: 'ti ti-x',
@@ -128,9 +121,28 @@ async function removeUser(user, ev) {
if (!list) return;
os.api('users/lists/pull', {
listId: list.id,
- userId: user.id,
+ userId: item.userId,
+ }).then(() => {
+ paginationEl.value.removeItem(item.id);
+ });
+ },
+ }], ev.currentTarget ?? ev.target);
+}
+
+async function showMembershipMenu(item, ev) {
+ os.popupMenu([{
+ text: item.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
+ icon: item.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
+ action: async () => {
+ os.api('users/lists/update-membership', {
+ listId: list.id,
+ userId: item.userId,
+ withReplies: !item.withReplies,
}).then(() => {
- users = users.filter(x => x.id !== user.id);
+ paginationEl.value.updateItem(item.id, (old) => ({
+ ...old,
+ withReplies: !item.withReplies,
+ }));
});
},
}], ev.currentTarget ?? ev.target);
@@ -202,6 +214,12 @@ definePageMetadata(computed(() => list ? {
align-self: center;
}
+.menu {
+ width: 32px;
+ height: 32px;
+ align-self: center;
+}
+
.more {
margin-left: auto;
margin-right: auto;
diff --git a/packages/frontend/src/pages/settings/word-mute.vue b/packages/frontend/src/pages/settings/word-mute.vue
index 1fefbdc92b..4e698698fe 100644
--- a/packages/frontend/src/pages/settings/word-mute.vue
+++ b/packages/frontend/src/pages/settings/word-mute.vue
@@ -5,29 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
- <MkTab v-model="tab">
- <option value="soft">{{ i18n.ts._wordMute.soft }}</option>
- <option value="hard">{{ i18n.ts._wordMute.hard }}</option>
- </MkTab>
<div>
- <div v-show="tab === 'soft'" class="_gaps_m">
- <MkInfo>{{ i18n.ts._wordMute.softDescription }}</MkInfo>
- <MkTextarea v-model="softMutedWords">
- <span>{{ i18n.ts._wordMute.muteWords }}</span>
- <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
- </MkTextarea>
- </div>
- <div v-show="tab === 'hard'" class="_gaps_m">
- <MkInfo>{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo>
- <MkTextarea v-model="hardMutedWords">
- <span>{{ i18n.ts._wordMute.muteWords }}</span>
- <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
- </MkTextarea>
- <MkKeyValue v-if="hardWordMutedNotesCount != null">
- <template #key>{{ i18n.ts._wordMute.mutedNotes }}</template>
- <template #value>{{ number(hardWordMutedNotesCount) }}</template>
- </MkKeyValue>
- </div>
+ <MkTextarea v-model="mutedWords">
+ <span>{{ i18n.ts._wordMute.muteWords }}</span>
+ <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
+ </MkTextarea>
</div>
<MkButton primary inline :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</div>
@@ -56,25 +38,15 @@ const render = (mutedWords) => mutedWords.map(x => {
}).join('\n');
const tab = ref('soft');
-const softMutedWords = ref(render(defaultStore.state.mutedWords));
-const hardMutedWords = ref(render($i!.mutedWords));
-const hardWordMutedNotesCount = ref(null);
+const mutedWords = ref(render($i!.mutedWords));
const changed = ref(false);
-os.api('i/get-word-muted-notes-count', {}).then(response => {
- hardWordMutedNotesCount.value = response?.count;
-});
-
-watch(softMutedWords, () => {
- changed.value = true;
-});
-
-watch(hardMutedWords, () => {
+watch(mutedWords, () => {
changed.value = true;
});
async function save() {
- const parseMutes = (mutes, tab) => {
+ const parseMutes = (mutes) => {
// split into lines, remove empty lines and unnecessary whitespace
let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== '');
@@ -92,7 +64,7 @@ async function save() {
os.alert({
type: 'error',
title: i18n.ts.regexpError,
- text: i18n.t('regexpErrorDescription', { tab, line: i + 1 }) + '\n' + err.toString(),
+ text: i18n.t('regexpErrorDescription', { tab: 'word mute', line: i + 1 }) + '\n' + err.toString(),
});
// re-throw error so these invalid settings are not saved
throw err;
@@ -105,18 +77,16 @@ async function save() {
return lines;
};
- let softMutes, hardMutes;
+ let parsed;
try {
- softMutes = parseMutes(softMutedWords.value, i18n.ts._wordMute.soft);
- hardMutes = parseMutes(hardMutedWords.value, i18n.ts._wordMute.hard);
+ parsed = parseMutes(mutedWords.value);
} catch (err) {
// already displayed error message in parseMutes
return;
}
- defaultStore.set('mutedWords', softMutes);
await os.api('i/update', {
- mutedWords: hardMutes,
+ mutedWords: parsed,
});
changed.value = false;
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 5bad689aee..b8deb77952 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -15,11 +15,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.tl">
<MkTimeline
ref="tlComponent"
- :key="src + withRenotes + withReplies + onlyFiles"
+ :key="src + withRenotes + onlyFiles"
:src="src.split(':')[0]"
:list="src.split(':')[1]"
:withRenotes="withRenotes"
- :withReplies="withReplies"
:onlyFiles="onlyFiles"
:sound="true"
@queue="queueUpdated"
@@ -62,7 +61,6 @@ let queue = $ref(0);
let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global');
const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) });
const withRenotes = $ref(true);
-const withReplies = $ref(false);
const onlyFiles = $ref(false);
watch($$(src), () => queue = 0);
@@ -146,11 +144,6 @@ const headerActions = $computed(() => [{
ref: $$(withRenotes),
}, {
type: 'switch',
- text: i18n.ts.withReplies,
- icon: 'ti ti-arrow-back-up',
- ref: $$(withReplies),
- }, {
- type: 'switch',
text: i18n.ts.fileAttachedOnly,
icon: 'ti ti-photo',
ref: $$(onlyFiles),
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 385c81a97f..71eec0aa26 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -128,14 +128,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo>
<template v-if="narrow">
- <XPhotos :key="user.id" :user="user"/>
+ <XFiles :key="user.id" :user="user"/>
<XActivity :key="user.id" :user="user"/>
</template>
<MkNotes v-if="!disableNotes" :class="$style.tl" :noGap="true" :pagination="pagination"/>
</div>
</div>
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;">
- <XPhotos :key="user.id" :user="user"/>
+ <XFiles :key="user.id" :user="user"/>
<XActivity :key="user.id" :user="user"/>
</div>
</div>
@@ -182,7 +182,7 @@ function calcAge(birthdate: string): number {
return yearDiff;
}
-const XPhotos = defineAsyncComponent(() => import('./index.photos.vue'));
+const XFiles = defineAsyncComponent(() => import('./index.files.vue'));
const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/pages/user/index.photos.vue b/packages/frontend/src/pages/user/index.files.vue
index b6cae9f131..205da5071d 100644
--- a/packages/frontend/src/pages/user/index.photos.vue
+++ b/packages/frontend/src/pages/user/index.files.vue
@@ -6,20 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkContainer :max-height="300" :foldable="true">
<template #icon><i class="ti ti-photo"></i></template>
- <template #header>{{ i18n.ts.images }}</template>
+ <template #header>{{ i18n.ts.files }}</template>
<div :class="$style.root">
<MkLoading v-if="fetching"/>
- <div v-if="!fetching && images.length > 0" :class="$style.stream">
+ <div v-if="!fetching && files.length > 0" :class="$style.stream">
<MkA
- v-for="image in images"
- :key="image.note.id + image.file.id"
+ v-for="file in files"
+ :key="file.note.id + file.file.id"
:class="$style.img"
- :to="notePage(image.note)"
+ :to="notePage(file.note)"
>
- <ImgWithBlurhash :hash="image.file.blurhash" :src="thumbnail(image.file)" :title="image.file.name"/>
+ <!-- TODO: 画像以外のファイルに対応 -->
+ <ImgWithBlurhash :hash="file.file.blurhash" :src="thumbnail(file.file)" :title="file.file.name"/>
</MkA>
</div>
- <p v-if="!fetching && images.length == 0" :class="$style.empty">{{ i18n.ts.nothing }}</p>
+ <p v-if="!fetching && files.length == 0" :class="$style.empty">{{ i18n.ts.nothing }}</p>
</div>
</MkContainer>
</template>
@@ -40,7 +41,7 @@ const props = defineProps<{
}>();
let fetching = $ref(true);
-let images = $ref<{
+let files = $ref<{
note: Misskey.entities.Note;
file: Misskey.entities.DriveFile;
}[]>([]);
@@ -52,24 +53,15 @@ function thumbnail(image: Misskey.entities.DriveFile): string {
}
onMounted(() => {
- const image = [
- 'image/jpeg',
- 'image/webp',
- 'image/avif',
- 'image/png',
- 'image/gif',
- 'image/apng',
- 'image/vnd.mozilla.apng',
- ];
os.api('users/notes', {
userId: props.user.id,
- fileType: image,
+ withFiles: true,
excludeNsfw: defaultStore.state.nsfw !== 'ignore',
- limit: 10,
+ limit: 15,
}).then(notes => {
for (const note of notes) {
for (const file of note.files) {
- images.push({
+ files.push({
note,
file,
});
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 128cbafb15..be514be5b1 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -80,6 +80,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
});
}
+ async function toggleWithReplies() {
+ os.apiWithDialog('following/update', {
+ userId: user.id,
+ withReplies: !user.withReplies,
+ }).then(() => {
+ user.withReplies = !user.withReplies;
+ });
+ }
+
async function toggleNotify() {
os.apiWithDialog('following/update', {
userId: user.id,
@@ -282,6 +291,10 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
// フォローしたとしても user.isFollowing はリアルタイム更新されないので不便なため
//if (user.isFollowing) {
menu = menu.concat([{
+ icon: user.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
+ text: user.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
+ action: toggleWithReplies,
+ }, {
icon: user.notify === 'none' ? 'ti ti-bell' : 'ti ti-bell-off',
text: user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes,
action: toggleNotify,
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index e715088d03..58730c7cef 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -5,7 +5,7 @@
import { markRaw, ref } from 'vue';
import * as Misskey from 'misskey-js';
-import { miLocalStorage } from './local-storage';
+import { miLocalStorage } from './local-storage.js';
import { Storage } from '@/pizzax.js';
interface PostFormAction {
@@ -101,10 +101,6 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'account',
default: 'nonSensitiveOnly' as 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null,
},
- mutedWords: {
- where: 'account',
- default: [],
- },
mutedAds: {
where: 'account',
default: [] as string[],
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 49fdf4d314..b2a44ac96b 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -31,7 +31,6 @@ export type Column = {
excludeTypes?: typeof notificationTypes[number][];
tl?: 'home' | 'local' | 'social' | 'global';
withRenotes?: boolean;
- withReplies?: boolean;
onlyFiles?: boolean;
};
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index aad73d73a1..847752247e 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -23,10 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTimeline
v-else-if="column.tl"
ref="timeline"
- :key="column.tl + withRenotes + withReplies + onlyFiles"
+ :key="column.tl + withRenotes + onlyFiles"
:src="column.tl"
:withRenotes="withRenotes"
- :withReplies="withReplies"
:onlyFiles="onlyFiles"
/>
</XColumn>
@@ -52,7 +51,6 @@ let disabled = $ref(false);
const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
const withRenotes = $ref(props.column.withRenotes ?? true);
-const withReplies = $ref(props.column.withReplies ?? false);
const onlyFiles = $ref(props.column.onlyFiles ?? false);
watch($$(withRenotes), v => {
@@ -61,12 +59,6 @@ watch($$(withRenotes), v => {
});
});
-watch($$(withReplies), v => {
- updateColumn(props.column.id, {
- withReplies: v,
- });
-});
-
watch($$(onlyFiles), v => {
updateColumn(props.column.id, {
onlyFiles: v,
@@ -117,10 +109,6 @@ const menu = [{
ref: $$(withRenotes),
}, {
type: 'switch',
- text: i18n.ts.withReplies,
- ref: $$(withReplies),
-}, {
- type: 'switch',
text: i18n.ts.fileAttachedOnly,
ref: $$(onlyFiles),
}];