diff --git a/locales/index.d.ts b/locales/index.d.ts index f4a1c20b34..44bef43ceb 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -108,6 +108,22 @@ export interface Locale extends ILocale { * グローバルタイムラインを表示する */ "showGlobalTimeline": string; + /** + * ホームタイムラインを表示する + */ + "showHomeTimeline": string; + /** + * ローカルタイムラインを表示する + */ + "showLocalTimeline": string; + /** + * ソーシャルタイムラインを表示する + */ + "showSocialTimeline": string; + /** + * 上のバーのTLの名前を表示する + */ + "topBarNameShown": string; /** * {user}がリノート */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fe86f66dc7..a1a66cecce 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -23,6 +23,10 @@ myLists: "自分の作成したリスト" noThankYou: "やめておく" enterUsername: "ユーザー名を入力" showGlobalTimeline: "グローバルタイムラインを表示する" +showHomeTimeline: "ホームタイムラインを表示する" +showLocalTimeline: "ローカルタイムラインを表示する" +showSocialTimeline: "ソーシャルタイムラインを表示する" +topBarNameShown: "上のバーのTLの名前を表示する" renotedBy: "{user}がリノート" noNotes: "ノートはありません" noNotifications: "通知はありません" diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index 4f01cc2217..fe6d9637e8 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -5,7 +5,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, NotesRepository , DriveFilesRepository, MiDriveFile} from '@/models/_.js'; +import type { UsersRepository, NotesRepository, DriveFilesRepository, MiDriveFile } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; @@ -14,7 +14,6 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { ApiError } from '../../error.js'; - export const meta = { tags: ['notes'], @@ -35,11 +34,11 @@ export const meta = { code: 'NO_SUCH_NOTE', id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474', }, - noSuchFile: { - message: 'Some files are not found.', - code: 'NO_SUCH_FILE', - id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', - }, + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, }, } as const; @@ -49,8 +48,8 @@ export const paramDef = { noteId: { type: 'string', format: 'misskey:id' }, visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, visibleUserIds: { type: 'array', uniqueItems: true, items: { - type: 'string', format: 'misskey:id', - } }, + type: 'string', format: 'misskey:id', + } }, cw: { type: 'string', nullable: true, maxLength: 100 }, localOnly: { type: 'boolean', default: false }, reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, @@ -132,34 +131,43 @@ export default class extends Endpoint { // eslint- throw err; }); - let files: MiDriveFile[] = []; - const fileIds = ps.fileIds ?? null; + let files: MiDriveFile[] = []; + const fileIds = ps.fileIds ?? null; - if (fileIds != null) { - files = await this.driveFilesRepository.createQueryBuilder('file') - .where('file.userId = :userId AND file.id IN (:...fileIds)', { - userId: me.id, - fileIds, - }) - .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') - .setParameters({ fileIds }) - .getMany(); + if (fileIds != null) { + files = await this.driveFilesRepository.createQueryBuilder('file') + .where('file.userId = :userId AND file.id IN (:...fileIds)', { + userId: me.id, + fileIds, + }) + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') + .setParameters({ fileIds }) + .getMany(); - if (files.length !== fileIds.length) { - throw new ApiError(meta.errors.noSuchFile); - } - } + if (files.length !== fileIds.length) { + throw new ApiError(meta.errors.noSuchFile); + } + } if (note.userId !== me.id) { throw new ApiError(meta.errors.noSuchNote); } - await this.notesRepository.update({ id: note.id }, { updatedAt: new Date(), cw: ps.cw, text: ps.text, fileIds: files.length > 0 ? files.map(f => f.id) : undefined, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, }); this.globalEventService.publishNoteStream(note.id, 'updated', { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index bc8a55c76f..c217a45a41 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -38,16 +38,19 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.showFixedPostFormInChannel }} -
+
{{ pinnedLists.name }} - {{ i18n.ts.remove }} + {{ i18n.ts.remove }}
{{ i18n.ts.add }} - {{i18n.ts.all}}{{ i18n.ts.remove }} - + {{ i18n.ts.all }}{{ i18n.ts.remove }} {{ i18n.ts.showMediaTimeline }} {{ i18n.ts.showGlobalTimeline }} + {{ i18n.ts.showHomeTimeline }} + {{ i18n.ts.showSocialTimeline }} + {{ i18n.ts.showLocalTimeline }} + {{ i18n.ts.topBarNameShown }}
@@ -350,7 +353,7 @@ import MkInfo from '@/components/MkInfo.vue'; import { langs } from '@/config.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; -import {signinRequired} from '@/account.js'; +import { signinRequired } from '@/account.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -422,6 +425,10 @@ const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWit const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showGlobalTimeline = computed(defaultStore.makeGetterSetter('showGlobalTimeline')); +const showLocalTimeline = computed(defaultStore.makeGetterSetter('showLocalTimeline')); +const showHomeTimeline = computed(defaultStore.makeGetterSetter('showHomeTimeline')); +const showSocialTimeline = computed(defaultStore.makeGetterSetter('showSocialTimeline')); +const topBarNameShown = computed(defaultStore.makeGetterSetter('topBarNameShown')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); @@ -449,8 +456,8 @@ const remoteLocalTimelineEnable3 = computed(defaultStore.makeGetterSetter('remot const remoteLocalTimelineEnable4 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable4')); const remoteLocalTimelineEnable5 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable5')); const $i = signinRequired(); -const pinnedMax = $i.policies?.listPinnedLimit; -const maxLocalTimeline = $i.policies?.localTimelineAnyLimit; +const pinnedMax = $i.policies.listPinnedLimit; +const maxLocalTimeline = $i.policies.localTimelineAnyLimit; watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); @@ -518,6 +525,10 @@ watch([ showVisibilityColor, enableonlyAndWithSave, showGlobalTimeline, + showSocialTimeline, + showLocalTimeline, + showHomeTimeline, + topBarNameShown, disableStreamingTimeline, enableSeasonalScreenEffect, ], async () => { @@ -611,26 +622,24 @@ async function setPinnedList() { } } -async function removePinnedList(id,name) { +async function removePinnedList(id, name) { + if (!id) return; + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.tsx.removeAreYouSure({ x: name ?? id }), + }); + if (canceled) return; - if (!id) return; - const {canceled} = await os.confirm({ - type: 'warning', - text: i18n.tsx.removeAreYouSure({x: name ?? id }), - }); - if (canceled) return; + if (id === 'all') { + if (canceled) return; - if (id === 'all') { + defaultStore.set('pinnedUserLists', []); + return; + } - if (canceled) return; - - defaultStore.set('pinnedUserLists', []); - return; - } - - const pinnedLists = defaultStore.state.pinnedUserLists; - const newPinnedLists = pinnedLists.filter(pinnedList => pinnedList.id !== id); - defaultStore.set('pinnedUserLists', newPinnedLists); + const pinnedLists = defaultStore.state.pinnedUserLists; + const newPinnedLists = pinnedLists.filter(pinnedList => pinnedList.id !== id); + defaultStore.set('pinnedUserLists', newPinnedLists); } let smashCount = 0; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 32b66680a5..6a936f3ebd 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -55,7 +55,7 @@ import { miLocalStorage } from '@/local-storage.js'; provide('shouldOmitHeaderTitle', true); -const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); +const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable && defaultStore.state.showLocalTimeline) || ($i != null && $i.policies.ltlAvailable && defaultStore.state.showLocalTimeline); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable && defaultStore.state.showGlobalTimeline) || ($i != null && $i.policies.gtlAvailable && defaultStore.state.showGlobalTimeline); const keymap = { 't': focus, @@ -110,6 +110,8 @@ const remoteLocalTimelineEnable2 = ref(defaultStore.state.remoteLocalTimelineEna const remoteLocalTimelineEnable3 = ref(defaultStore.state.remoteLocalTimelineEnable3); const remoteLocalTimelineEnable4 = ref(defaultStore.state.remoteLocalTimelineEnable4); const remoteLocalTimelineEnable5 = ref(defaultStore.state.remoteLocalTimelineEnable5); +const showHomeTimeline = ref(defaultStore.state.showHomeTimeline); +const showSocialTimeline = ref(defaultStore.state.showSocialTimeline); watch(src, () => { queue.value = 0; }); @@ -282,57 +284,57 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList key: 'list:' + l.id, title: l.name, icon: 'ti ti-star', - iconOnly: false, -}))), { + iconOnly: defaultStore.state.topBarNameShown ?? false, +}))), ...(showHomeTimeline.value ? [{ key: 'home', title: i18n.ts._timelines.home, icon: 'ti ti-home', - iconOnly: false, -}, ...(isLocalTimelineAvailable ? [{ + iconOnly: defaultStore.state.topBarNameShown ?? false, +}] : []), ...(isLocalTimelineAvailable ? [{ key: 'local', title: i18n.ts._timelines.local, icon: 'ti ti-planet', - iconOnly: false, -}, ...(isShowMediaTimeline.value ? [{ + iconOnly: defaultStore.state.topBarNameShown ?? false, +}] : []), ...(isShowMediaTimeline.value ? [{ key: 'media', title: i18n.ts._timelines.media, icon: 'ti ti-photo', - iconOnly: false, -}] : []), { + iconOnly: defaultStore.state.topBarNameShown ?? false, +}] : []), ...(showSocialTimeline.value ? [{ key: 'social', title: i18n.ts._timelines.social, icon: 'ti ti-universe', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable1.value ? [{ key: 'custom-timeline-1', title: defaultStore.state.remoteLocalTimelineName1, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable2.value ? [{ key: 'custom-timeline-2', title: defaultStore.state.remoteLocalTimelineName2, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable3.value ? [{ key: 'custom-timeline-3', title: defaultStore.state.remoteLocalTimelineName3, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable4.value ? [{ key: 'custom-timeline-4', title: defaultStore.state.remoteLocalTimelineName4, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable5.value ? [{ key: 'custom-timeline-5', title: defaultStore.state.remoteLocalTimelineName5, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(isGlobalTimelineAvailable ? [{ key: 'global', title: i18n.ts._timelines.global, icon: 'ti ti-whirl', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), { icon: 'ti ti-list', title: i18n.ts.lists, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index cb5f1b3416..009dfaa2aa 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -364,6 +364,22 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + topBarNameShown: { + where: 'device', + default: false, + }, + showHomeTimeline: { + where: 'device', + default: true, + }, + showLocalTimeline: { + where: 'device', + default: true, + }, + showSocialTimeline: { + where: 'device', + default: true, + }, showGapBetweenNotesInTimeline: { where: 'device', default: false, @@ -676,10 +692,10 @@ export const defaultStore = markRaw(new Storage('base', { sfxVolume: 1, }, }, - hemisphere: { + hemisphere: { where: 'device', default: hemisphere as 'N' | 'S', - }, + }, enableHorizontalSwipe: { where: 'device', default: true,