diff --git a/CHANGELOG.md b/CHANGELOG.md index d89d81e6ac..2fc561d9aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ - ### Client -- +- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように +- Enhance: リアクション・いいねの総数を表示するように +- Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように +- Fix: 一部のページ内リンクが正しく動作しない問題を修正 ### Server - diff --git a/locales/en-US.yml b/locales/en-US.yml index 6b20134b2d..49688d24ea 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2338,6 +2338,7 @@ _notification: sendTestNotification: "Send test notification" notificationWillBeDisplayedLikeThis: "Notifications look like this" reactedBySomeUsers: "{n} users reacted" + likedBySomeUsers: "{n} users liked" renotedBySomeUsers: "Renote from {n} users" followedBySomeUsers: "Followed by {n} users" flushNotification: "Clear notifications" diff --git a/locales/index.d.ts b/locales/index.d.ts index 49d2ad1949..ac4cae7845 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9089,6 +9089,10 @@ export interface Locale extends ILocale { * {n}人がリアクションしました */ "reactedBySomeUsers": ParameterizedString<"n">; + /** + * {n}人がいいねしました + */ + "likedBySomeUsers": ParameterizedString<"n">; /** * {n}人がリノートしました */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4371539feb..978f4b6403 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2403,6 +2403,7 @@ _notification: sendTestNotification: "テスト通知を送信する" notificationWillBeDisplayedLikeThis: "通知はこのように表示されます" reactedBySomeUsers: "{n}人がリアクションしました" + likedBySomeUsers: "{n}人がいいねしました" renotedBySomeUsers: "{n}人がリノートしました" followedBySomeUsers: "{n}人にフォローされました" flushNotification: "通知の履歴をリセットする" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index bf504378f8..75578750ce 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -2315,6 +2315,7 @@ _notification: sendTestNotification: "테스트 알림 보내기" notificationWillBeDisplayedLikeThis: "알림이 이렇게 표시됩니다" reactedBySomeUsers: "{n}명이 반응했습니다" + likedBySomeUsers: "{n}명이 좋아요했습니다" renotedBySomeUsers: "{n}명이 리노트했습니다" followedBySomeUsers: "{n}명에게 팔로우됨" _types: diff --git a/packages/backend/src/@types/fastify.d.ts b/packages/backend/src/@types/fastify.d.ts index b15176ec23..f63a90f948 100644 --- a/packages/backend/src/@types/fastify.d.ts +++ b/packages/backend/src/@types/fastify.d.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import FastifyReply from "fastify"; +import FastifyReply from 'fastify'; declare module 'fastify' { interface FastifyReply { diff --git a/packages/backend/src/core/UserAuthService.ts b/packages/backend/src/core/UserAuthService.ts index 39eb771d6c..14f298c51f 100644 --- a/packages/backend/src/core/UserAuthService.ts +++ b/packages/backend/src/core/UserAuthService.ts @@ -8,7 +8,7 @@ import * as OTPAuth from 'otpauth'; import { DI } from '@/di-symbols.js'; import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; -import { IdentifiableError } from "@/misc/identifiable-error.js"; +import { IdentifiableError } from '@/misc/identifiable-error.js'; @Injectable() export class UserAuthService { diff --git a/packages/backend/src/core/entities/AnnouncementEntityService.ts b/packages/backend/src/core/entities/AnnouncementEntityService.ts index 6a70aeece9..b4a67460d3 100644 --- a/packages/backend/src/core/entities/AnnouncementEntityService.ts +++ b/packages/backend/src/core/entities/AnnouncementEntityService.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from "@/models/_.js"; +import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 583d1282bb..6f8f5797a1 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -250,7 +250,7 @@ export class DriveFileEntityService { folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, { detail: true, }) : null, - userId: opts.withUser ? file.userId : null, + userId: file.userId, user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId, me) : null, }); } diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 500d7741a7..f111b65402 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -338,6 +338,7 @@ export class NoteEntityService implements OnModuleInit { visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, renoteCount: note.renoteCount, repliesCount: note.repliesCount, + reactionCount: Object.values(note.reactions).reduce((a, b) => a + b, 0), reactions: this.reactionService.convertLegacyReactions(note.reactions), reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host), reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined, diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index bb4ccc7ee4..2641161c8b 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -223,6 +223,10 @@ export const packedNoteSchema = { }], }, }, + reactionCount: { + type: 'number', + optional: false, nullable: false, + }, renoteCount: { type: 'number', optional: false, nullable: false, diff --git a/packages/backend/src/queue/const.ts b/packages/backend/src/queue/const.ts index 1d0caecf92..06edef1a20 100644 --- a/packages/backend/src/queue/const.ts +++ b/packages/backend/src/queue/const.ts @@ -4,7 +4,7 @@ */ import type * as Bull from 'bullmq'; -import type { RedisOptions } from "ioredis"; +import type { RedisOptions } from 'ioredis'; import type { RedisOptionsSource } from '@/config.js'; export const QUEUE = { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 5583ab2053..3a6a70a1d0 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -17,8 +17,8 @@ import { QueryService } from '@/core/QueryService.js'; import { MetaService } from '@/core/MetaService.js'; import { MiLocalUser } from '@/models/User.js'; import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js'; +import { FanoutTimelineName } from '@/core/FanoutTimelineService.js'; import { ApiError } from '../../error.js'; -import { FanoutTimelineName } from "@/core/FanoutTimelineService.js"; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 917f5efa89..1013941724 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -12,8 +12,8 @@ import { DI } from '@/di-symbols.js'; import { CacheService } from '@/core/CacheService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { RoleService } from '@/core/RoleService.js'; +import { MiNoteReaction } from '@/models/_.js'; import { ApiError } from '../../error.js'; -import { MiNoteReaction } from "@/models/_.js"; export const meta = { tags: ['users', 'reactions'], diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index f9dda1c31b..60f1e05e8a 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -72,8 +72,8 @@ //#endregion //#region Script - function importAppScript() { - import(`/vite/${CLIENT_ENTRY}`) + async function importAppScript() { + await import(`/vite/${CLIENT_ENTRY}`) .catch(async e => { console.error(e); renderError('APP_IMPORT', e); diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index 4716454ef1..81da0fac31 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -10,7 +10,7 @@ import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; import { GlobalModule } from '@/GlobalModule.js'; import { AnnouncementService } from '@/core/AnnouncementService.js'; -import { AnnouncementEntityService } from "@/core/entities/AnnouncementEntityService.js"; +import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js'; import type { AnnouncementReadsRepository, AnnouncementsRepository, diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index f2ed8e6647..cf44e10671 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -82,9 +82,7 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.data const menuShowing = ref(false); function showMenu(ev: MouseEvent) { - let menu: MenuItem[] = []; - - menu = [ + const menu: MenuItem[] = [ // TODO: 再生キューに追加 { text: i18n.ts.hide, @@ -95,15 +93,37 @@ function showMenu(ev: MouseEvent) { }, ]; - if (iAmModerator) { + if ($i?.id === props.audio.userId || iAmModerator) { menu.push({ type: 'divider', - }, { + }); + } + + if (iAmModerator) { + menu.push({ text: props.audio.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, icon: props.audio.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation', danger: true, action: () => toggleSensitive(props.audio), }); + + if ($i?.id !== props.audio.userId) { + menu.push({ + type: 'link' as const, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-info-circle', + to: `/admin/file/${props.audio.id}`, + }); + } + } + + if ($i?.id === props.audio.userId) { + menu.push({ + type: 'link' as const, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-info-circle', + to: `/my/drive/file/${props.audio.id}`, + }); } menuShowing.value = true; diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index 06217ae264..72ee04b5ac 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -53,6 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only