diff --git a/CHANGELOG.md b/CHANGELOG.md index c3a5e41787..04ae102227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Client - Enhance: Bull DashboardでRelationship Queueの状態も確認できるように (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/751) +- Enhance: ドライブでソートができるように +- Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正 ### Server - diff --git a/locales/index.d.ts b/locales/index.d.ts index a7849368d8..c47728313c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9275,7 +9275,7 @@ export interface Locale extends ILocale { */ "youGotQuote": ParameterizedString<"name">; /** - * {name}がRenoteしました + * {name}がリノートしました */ "youRenoted": ParameterizedString<"name">; /** @@ -9380,7 +9380,7 @@ export interface Locale extends ILocale { */ "reply": string; /** - * Renote + * リノート */ "renote": string; /** @@ -9438,7 +9438,7 @@ export interface Locale extends ILocale { */ "reply": string; /** - * Renote + * リノート */ "renote": string; }; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9f24bcbc51..86f43abdcd 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2449,7 +2449,7 @@ _notification: youGotMention: "{name}からのメンション" youGotReply: "{name}からのリプライ" youGotQuote: "{name}による引用" - youRenoted: "{name}がRenoteしました" + youRenoted: "{name}がリノートしました" youWereFollowed: "フォローされました" youReceivedFollowRequest: "フォローリクエストが来ました" yourFollowRequestAccepted: "フォローリクエストが承認されました" @@ -2477,7 +2477,7 @@ _notification: follow: "フォロー" mention: "メンション" reply: "リプライ" - renote: "Renote" + renote: "リノート" quote: "引用" reaction: "リアクション" pollEnded: "アンケートが終了" @@ -2493,7 +2493,7 @@ _notification: _actions: followBack: "フォローバック" reply: "返信" - renote: "Renote" + renote: "リノート" _deck: alwaysShowMainColumn: "常にメインカラムを表示" diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 23883a44e9..910b73c798 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -157,7 +157,12 @@ const ilFilesObserver = new IntersectionObserver( (entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(), ); +const sortModeSelect = ref('+createdAt'); + watch(folder, () => emit('cd', folder.value)); +watch(sortModeSelect, () => { + fetch(); +}); function onStreamDriveFileCreated(file: Misskey.entities.DriveFile) { addFile(file, true); @@ -558,6 +563,7 @@ async function fetch() { folderId: folder.value ? folder.value.id : null, type: props.type, limit: filesMax + 1, + sort: sortModeSelect.value, }).then(fetchedFiles => { if (fetchedFiles.length === filesMax + 1) { moreFiles.value = true; @@ -607,6 +613,7 @@ function fetchMoreFiles() { type: props.type, untilId: files.value.at(-1)?.id, limit: max + 1, + sort: sortModeSelect.value, }).then(files => { if (files.length === max + 1) { moreFiles.value = true; @@ -642,6 +649,43 @@ function getMenu() { type: 'label', }); + menu.push({ + type: 'parent', + text: i18n.ts.sort, + icon: 'ti ti-arrows-sort', + children: [{ + text: `${i18n.ts.registeredDate} (${i18n.ts.descendingOrder})`, + icon: 'ti ti-sort-descending-letters', + action: () => { sortModeSelect.value = '+createdAt'; }, + active: sortModeSelect.value === '+createdAt', + }, { + text: `${i18n.ts.registeredDate} (${i18n.ts.ascendingOrder})`, + icon: 'ti ti-sort-ascending-letters', + action: () => { sortModeSelect.value = '-createdAt'; }, + active: sortModeSelect.value === '-createdAt', + }, { + text: `${i18n.ts.size} (${i18n.ts.descendingOrder})`, + icon: 'ti ti-sort-descending-letters', + action: () => { sortModeSelect.value = '+size'; }, + active: sortModeSelect.value === '+size', + }, { + text: `${i18n.ts.size} (${i18n.ts.ascendingOrder})`, + icon: 'ti ti-sort-ascending-letters', + action: () => { sortModeSelect.value = '-size'; }, + active: sortModeSelect.value === '-size', + }, { + text: `${i18n.ts.name} (${i18n.ts.descendingOrder})`, + icon: 'ti ti-sort-descending-letters', + action: () => { sortModeSelect.value = '+name'; }, + active: sortModeSelect.value === '+name', + }, { + text: `${i18n.ts.name} (${i18n.ts.ascendingOrder})`, + icon: 'ti ti-sort-ascending-letters', + action: () => { sortModeSelect.value = '-name'; }, + active: sortModeSelect.value === '-name', + }], + }); + if (folder.value) { menu.push({ text: i18n.ts.renameFolder, diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue index a36f036303..0ea415f673 100644 --- a/packages/frontend/src/pages/settings/notifications.notification-config.vue +++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue @@ -6,13 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps_m"> <MkSelect v-model="type"> - <option value="all">{{ i18n.ts.all }}</option> - <option value="following">{{ i18n.ts.following }}</option> - <option value="follower">{{ i18n.ts.followers }}</option> - <option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option> - <option value="followingOrFollower">{{ i18n.ts.followingOrFollower }}</option> - <option value="list">{{ i18n.ts.userList }}</option> - <option value="never">{{ i18n.ts.none }}</option> + <option v-for="type in props.configurableTypes ?? notificationConfigTypes" :key="type" :value="type">{{ notificationConfigTypesI18nMap[type] }}</option> </MkSelect> <MkSelect v-if="type === 'list'" v-model="userListId"> @@ -21,31 +15,61 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSelect> <div class="_buttons"> - <MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + <MkButton inline primary :disabled="type === 'list' && userListId === null" @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> </div> </template> +<script lang="ts"> +const notificationConfigTypes = [ + 'all', + 'following', + 'follower', + 'mutualFollow', + 'followingOrFollower', + 'list', + 'never' +] as const; + +export type NotificationConfig = { + type: Exclude<typeof notificationConfigTypes[number], 'list'>; +} | { + type: 'list'; + userListId: string; +}; +</script> + <script lang="ts" setup> -import { ref } from 'vue'; import * as Misskey from 'misskey-js'; +import { ref } from 'vue'; import MkSelect from '@/components/MkSelect.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - value: any; + value: NotificationConfig; userLists: Misskey.entities.UserList[]; + configurableTypes?: NotificationConfig['type'][]; // If not specified, all types are configurable }>(); const emit = defineEmits<{ - (ev: 'update', result: any): void; + (ev: 'update', result: NotificationConfig): void; }>(); +const notificationConfigTypesI18nMap: Record<typeof notificationConfigTypes[number], string> = { + all: i18n.ts.all, + following: i18n.ts.following, + follower: i18n.ts.followers, + mutualFollow: i18n.ts.mutualFollow, + followingOrFollower: i18n.ts.followingOrFollower, + list: i18n.ts.userList, + never: i18n.ts.none, +}; + const type = ref(props.value.type); -const userListId = ref(props.value.userListId); +const userListId = ref(props.value.type === 'list' ? props.value.userListId : null); function save() { - emit('update', { type: type.value, userListId: userListId.value }); + emit('update', type.value === 'list' ? { type: type.value, userListId: userListId.value! } : { type: type.value }); } </script> diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 53b3bd4936..8ffe0d6a7a 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -22,7 +22,12 @@ SPDX-License-Identifier: AGPL-3.0-only }} </template> - <XNotificationConfig :userLists="userLists" :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" @update="(res) => updateReceiveConfig(type, res)"/> + <XNotificationConfig + :userLists="userLists" + :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" + :configurableTypes="onlyOnOrOffNotificationTypes.includes(type) ? ['all', 'never'] : undefined" + @update="(res) => updateReceiveConfig(type, res)" + /> </MkFolder> </div> </FormSection> @@ -58,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef, computed } from 'vue'; -import XNotificationConfig from './notifications.notification-config.vue'; +import XNotificationConfig, { type NotificationConfig } from './notifications.notification-config.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkFolder from '@/components/MkFolder.vue'; @@ -73,7 +78,9 @@ import { notificationTypes } from '@@/js/const.js'; const $i = signinRequired(); -const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'achievementEarned', 'test', 'exportCompleted'] as const satisfies (typeof notificationTypes[number])[]; +const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] satisfies (typeof notificationTypes[number])[] as string[]; + +const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login'] satisfies (typeof notificationTypes[number])[] as string[]; const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>(); const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer); @@ -88,7 +95,7 @@ async function readAllNotifications() { await os.apiWithDialog('notifications/mark-all-as-read'); } -async function updateReceiveConfig(type, value) { +async function updateReceiveConfig(type: typeof notificationTypes[number], value: NotificationConfig) { await os.apiWithDialog('i/update', { notificationRecieveConfig: { ...$i.notificationRecieveConfig,