This commit is contained in:
syuilo
2025-03-22 19:49:59 +09:00
parent 7e826ce5d6
commit 34760459b2
7 changed files with 67 additions and 63 deletions

16
locales/index.d.ts vendored
View File

@@ -1978,14 +1978,6 @@ export interface Locale extends ILocale {
* クリップボードのテキストが長いです。テキストファイルとして添付しますか? * クリップボードのテキストが長いです。テキストファイルとして添付しますか?
*/ */
"attachAsFileQuestion": string; "attachAsFileQuestion": string;
/**
* まだチャットはありません
*/
"noMessagesYet": string;
/**
* 新しいメッセージがあります
*/
"newMessageExists": string;
/** /**
* メッセージに添付できるファイルはひとつです * メッセージに添付できるファイルはひとつです
*/ */
@@ -5359,6 +5351,14 @@ export interface Locale extends ILocale {
*/ */
"chat": string; "chat": string;
"_chat": { "_chat": {
/**
* まだメッセージはありません
*/
"noMessagesYet": string;
/**
* 新しいメッセージ
*/
"newMessage": string;
/** /**
* 個人チャット * 個人チャット
*/ */

View File

@@ -490,8 +490,6 @@ noteOf: "{user}のノート"
quoteAttached: "引用付き" quoteAttached: "引用付き"
quoteQuestion: "引用として添付しますか?" quoteQuestion: "引用として添付しますか?"
attachAsFileQuestion: "クリップボードのテキストが長いです。テキストファイルとして添付しますか?" attachAsFileQuestion: "クリップボードのテキストが長いです。テキストファイルとして添付しますか?"
noMessagesYet: "まだチャットはありません"
newMessageExists: "新しいメッセージがあります"
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです" onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
signinRequired: "続行する前に、登録またはログインが必要です" signinRequired: "続行する前に、登録またはログインが必要です"
signinOrContinueOnRemote: "続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります" signinOrContinueOnRemote: "続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります"
@@ -1337,6 +1335,8 @@ information: "情報"
chat: "チャット" chat: "チャット"
_chat: _chat:
noMessagesYet: "まだメッセージはありません"
newMessage: "新しいメッセージ"
individualChat: "個人チャット" individualChat: "個人チャット"
individualChat_description: "特定ユーザーとの一対一のチャットができます。" individualChat_description: "特定ユーザーとの一対一のチャットができます。"
roomChat: "ルームチャット" roomChat: "ルームチャット"

View File

@@ -90,27 +90,27 @@ export class ChatService {
if (!otherApprovedMe) { if (!otherApprovedMe) {
if (toUser.chatScope === 'none') { if (toUser.chatScope === 'none') {
throw new Error('recipient is cannot chat'); throw new Error('recipient is cannot chat (none)');
} else if (toUser.chatScope === 'followers') { } else if (toUser.chatScope === 'followers') {
const isFollower = await this.userFollowingService.isFollowing(fromUser.id, toUser.id); const isFollower = await this.userFollowingService.isFollowing(fromUser.id, toUser.id);
if (!isFollower) { if (!isFollower) {
throw new Error('recipient is cannot chat'); throw new Error('recipient is cannot chat (followers)');
} }
} else if (toUser.chatScope === 'following') { } else if (toUser.chatScope === 'following') {
const isFollowing = await this.userFollowingService.isFollowing(toUser.id, fromUser.id); const isFollowing = await this.userFollowingService.isFollowing(toUser.id, fromUser.id);
if (!isFollowing) { if (!isFollowing) {
throw new Error('recipient is cannot chat'); throw new Error('recipient is cannot chat (following)');
} }
} else if (toUser.chatScope === 'mutual') { } else if (toUser.chatScope === 'mutual') {
const isMutual = await this.userFollowingService.isMutual(fromUser.id, toUser.id); const isMutual = await this.userFollowingService.isMutual(fromUser.id, toUser.id);
if (!isMutual) { if (!isMutual) {
throw new Error('recipient is cannot chat'); throw new Error('recipient is cannot chat (mutual)');
} }
} }
} }
if ((await this.roleService.getUserPolicies(toUser.id)).canChat) { if (!(await this.roleService.getUserPolicies(toUser.id)).canChat) {
throw new Error('recipient is cannot chat'); throw new Error('recipient is cannot chat (policy)');
} }
const blocked = await this.userBlockingService.checkBlocked(toUser.id, fromUser.id); const blocked = await this.userBlockingService.checkBlocked(toUser.id, fromUser.id);

View File

@@ -292,7 +292,7 @@ const $swSubscriptionsRepository: Provider = {
const $systemAccountsRepository: Provider = { const $systemAccountsRepository: Provider = {
provide: DI.systemAccountsRepository, provide: DI.systemAccountsRepository,
useFactory: (db: DataSource) => db.getRepository(MiSystemAccount), useFactory: (db: DataSource) => db.getRepository(MiSystemAccount).extend(miRepository as MiRepository<MiSystemAccount>),
inject: [DI.db], inject: [DI.db],
}; };
@@ -310,7 +310,7 @@ const $abuseUserReportsRepository: Provider = {
const $abuseReportNotificationRecipientRepository: Provider = { const $abuseReportNotificationRecipientRepository: Provider = {
provide: DI.abuseReportNotificationRecipientRepository, provide: DI.abuseReportNotificationRecipientRepository,
useFactory: (db: DataSource) => db.getRepository(MiAbuseReportNotificationRecipient), useFactory: (db: DataSource) => db.getRepository(MiAbuseReportNotificationRecipient).extend(miRepository as MiRepository<MiAbuseReportNotificationRecipient>),
inject: [DI.db], inject: [DI.db],
}; };
@@ -442,7 +442,7 @@ const $webhooksRepository: Provider = {
const $systemWebhooksRepository: Provider = { const $systemWebhooksRepository: Provider = {
provide: DI.systemWebhooksRepository, provide: DI.systemWebhooksRepository,
useFactory: (db: DataSource) => db.getRepository(MiSystemWebhook), useFactory: (db: DataSource) => db.getRepository(MiSystemWebhook).extend(miRepository as MiRepository<MiSystemWebhook>),
inject: [DI.db], inject: [DI.db],
}; };
@@ -514,7 +514,7 @@ const $chatRoomMembershipsRepository: Provider = {
const $chatApprovalsRepository: Provider = { const $chatApprovalsRepository: Provider = {
provide: DI.chatApprovalsRepository, provide: DI.chatApprovalsRepository,
useFactory: (db: DataSource) => db.getRepository(MiChatApproval), useFactory: (db: DataSource) => db.getRepository(MiChatApproval).extend(miRepository as MiRepository<MiChatApproval>),
inject: [DI.db], inject: [DI.db],
}; };

View File

@@ -125,9 +125,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw err; throw err;
}); });
return await this.chatService.createMessage({ return await this.chatService.createMessage(me, toUser, {
fromUser: me,
toUser,
text: ps.text, text: ps.text,
file: file, file: file,
}); });

View File

@@ -9,8 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="700"> <MkSpacer :contentMax="700">
<MkPagination v-if="pagination" ref="pagingComponent" :key="userId || roomId" :pagination="pagination" :disableAutoLoad="true" :scrollReversed="true"> <MkPagination v-if="pagination" ref="pagingComponent" :key="userId || roomId" :pagination="pagination" :disableAutoLoad="true" :scrollReversed="true">
<template #empty> <template #empty>
<div class="_fullinfo"> <div class="_gaps" style="text-align: center;">
<div>{{ i18n.ts.noMessagesYet }}</div> <div>{{ i18n.ts.noMessagesYet }}</div>
<template v-if="user">
<div v-if="user.chatScope === 'followers'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowers }}</div>
<div v-else-if="user.chatScope === 'following'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowing }}</div>
<div v-else-if="user.chatScope === 'mutual'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromMutualFollowing }}</div>
<div v-else>{{ i18n.ts._chat.thisUserNotAllowedChatAnyone }}</div>
</template>
</div> </div>
</template> </template>
<template #default="{ items: messages }"> <template #default="{ items: messages }">

View File

@@ -151,6 +151,16 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
const menuItems: MenuItem[] = []; const menuItems: MenuItem[] = [];
if (iAmModerator) {
menuItems.push({
icon: 'ti ti-user-exclamation',
text: i18n.ts.moderation,
action: () => {
router.push(`/admin/user/${user.id}`);
},
}, { type: 'divider' });
}
menuItems.push({ menuItems.push({
icon: 'ti ti-at', icon: 'ti ti-at',
text: i18n.ts.copyUsername, text: i18n.ts.copyUsername,
@@ -159,25 +169,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
}, },
}); });
if (notesSearchAvailable && (user.host == null || canSearchNonLocalNotes)) { menuItems.push({
menuItems.push({ icon: 'ti ti-share',
icon: 'ti ti-search', text: i18n.ts.copyProfileUrl,
text: i18n.ts.searchThisUsersNotes, action: () => {
action: () => { const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`;
router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`); copyToClipboard(`${url}/${canonical}`);
}, },
}); });
}
if (iAmModerator) {
menuItems.push({
icon: 'ti ti-user-exclamation',
text: i18n.ts.moderation,
action: () => {
router.push(`/admin/user/${user.id}`);
},
});
}
menuItems.push({ menuItems.push({
icon: 'ti ti-rss', icon: 'ti ti-rss',
@@ -210,29 +209,18 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
}); });
} }
menuItems.push({ if (notesSearchAvailable && (user.host == null || canSearchNonLocalNotes)) {
icon: 'ti ti-share', menuItems.push({
text: i18n.ts.copyProfileUrl, icon: 'ti ti-search',
action: () => { text: i18n.ts.searchThisUsersNotes,
const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; action: () => {
copyToClipboard(`${url}/${canonical}`); router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`);
}, },
}); });
}
if ($i) { if ($i) {
menuItems.push({ type: 'divider' }, { menuItems.push({ type: 'divider' }, {
icon: 'ti ti-mail',
text: i18n.ts.sendMessage,
action: () => {
const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`;
os.post({ specified: user, initialText: `${canonical} ` });
},
}, {
type: 'link',
icon: 'ti ti-messages',
text: i18n.ts._chat.chatWithThisUser,
to: `/chat/user/${user.id}`,
}, { type: 'divider' }, {
icon: 'ti ti-pencil', icon: 'ti ti-pencil',
text: i18n.ts.editMemo, text: i18n.ts.editMemo,
action: editMemo, action: editMemo,
@@ -368,6 +356,18 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
//} //}
menuItems.push({ type: 'divider' }, { menuItems.push({ type: 'divider' }, {
icon: 'ti ti-mail',
text: i18n.ts.sendMessage,
action: () => {
const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`;
os.post({ specified: user, initialText: `${canonical} ` });
},
}, {
type: 'link',
icon: 'ti ti-messages',
text: i18n.ts._chat.chatWithThisUser,
to: `/chat/user/${user.id}`,
}, { type: 'divider' }, {
icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off', icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off',
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute, text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
action: toggleMute, action: toggleMute,