diff --git a/locales/index.d.ts b/locales/index.d.ts index b84f39b04d..69c63cc714 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2447,7 +2447,7 @@ export interface Locale extends ILocale { */ "disablePagesScript": string; /** - * リモートユーザー情報の更新 + * Refresh remote data */ "updateRemoteUser": string; /** @@ -13069,6 +13069,58 @@ export interface Locale extends ILocale { * Users popular on {name} */ "popularUsersLocal": ParameterizedString<"name">; + /** + * Silenced + */ + "silenced": string; + /** + * Total followers + */ + "totalFollowers": string; + /** + * Total following + */ + "totalFollowing": string; + /** + * Local followers + */ + "localFollowers": string; + /** + * Local following + */ + "localFollowing": string; + /** + * Remote followers + */ + "remoteFollowers": string; + /** + * Remote following + */ + "remoteFollowing": string; + /** + * Activity Pub + */ + "activityPub": string; + /** + * IP + */ + "ip": string; + /** + * The date is when IP address was first used. + */ + "ipTip": string; + /** + * Period + */ + "rolePeriod": string; + /** + * Assigned + */ + "roleAssigned": string; + /** + * automatic + */ + "roleAutomatic": string; /** * Translation timeout */ diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 6dbfbf9d9a..1579719246 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -12,6 +12,7 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; import { IdService } from '@/core/IdService.js'; import { notificationRecieveConfig } from '@/models/json-schema/user.js'; import { isSystemAccount } from '@/misc/is-system-account.js'; +import { CacheService } from '@/core/CacheService.js'; export const meta = { tags: ['admin'], @@ -186,6 +187,36 @@ export const meta = { }, }, }, + followStats: { + type: 'object', + optional: false, nullable: false, + properties: { + totalFollowing: { + type: 'number', + optional: false, nullable: false, + }, + totalFollowers: { + type: 'number', + optional: false, nullable: false, + }, + localFollowing: { + type: 'number', + optional: false, nullable: false, + }, + localFollowers: { + type: 'number', + optional: false, nullable: false, + }, + remoteFollowing: { + type: 'number', + optional: false, nullable: false, + }, + remoteFollowers: { + type: 'number', + optional: false, nullable: false, + }, + }, + }, }, }, } as const; @@ -213,6 +244,7 @@ export default class extends Endpoint { // eslint- private roleService: RoleService, private roleEntityService: RoleEntityService, private idService: IdService, + private readonly cacheService: CacheService, ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ @@ -237,6 +269,8 @@ export default class extends Endpoint { // eslint- const roleAssigns = await this.roleService.getUserAssigns(user.id); const roles = await this.roleService.getUserRoles(user.id); + const followStats = await this.cacheService.getFollowStats(user.id); + return { email: profile.email, emailVerified: profile.emailVerified, @@ -269,6 +303,11 @@ export default class extends Endpoint { // eslint- expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null, roleId: a.roleId, })), + followStats: { + ...followStats, + totalFollowers: Math.max(user.followersCount, followStats.localFollowers + followStats.remoteFollowers), + totalFollowing: Math.max(user.followingCount, followStats.localFollowing + followStats.remoteFollowing), + }, }; }); } diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index d37f7f39f8..53453be2c1 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only + + + {{ user.id }} + + {{ i18n.ts.notApproved }} {{ i18n.ts.approved }} - Suspended - Silenced - Moderator + {{ i18n.ts.suspended }} + {{ i18n.ts.silenced }} + {{ i18n.ts.moderator }} {{ i18n.ts.isSystemAccount }} - {{ i18n.ts.instanceInfo }} - -
- - - - - - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + - + + + +
+
+ {{ policy }} ... {{ info.policies[policy] }} +
+
+
+ + + + + {{ i18n.ts.ipTip }} +
+ {{ record.createdAt }} + {{ record.ip }} +
+
+ + + - -
+ + + + + - - + +
@@ -73,12 +132,10 @@ SPDX-License-Identifier: AGPL-3.0-only
- - {{ i18n.ts.updateRemoteUser }}
- +
{{ i18n.ts.silence }} {{ i18n.ts.suspend }} @@ -90,58 +147,40 @@ SPDX-License-Identifier: AGPL-3.0-only -
- {{ i18n.ts.resetPassword }} +
+ {{ i18n.ts.updateRemoteUser }} + {{ i18n.ts.resetPassword }} + {{ i18n.ts.unsetUserAvatar }} + {{ i18n.ts.unsetUserBanner }} + {{ i18n.ts.deleteAllFiles }} + {{ i18n.ts.deleteAccount }}
- - - - -
-
- {{ policy }} ... {{ info.policies[policy] }} -
-
-
- - - - - {{ i18n.ts.requireAdminForView }} - - The date is the IP address was first acknowledged. - - - -
- {{ i18n.ts.unsetUserAvatar }} - {{ i18n.ts.unsetUserBanner }} - {{ i18n.ts.deleteAllFiles }} -
- {{ i18n.ts.deleteAccount }}
- {{ i18n.ts.assign }} + {{ i18n.ts.assign }}
- - + +
-
Assigned:
-
Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}
-
Period: {{ i18n.ts.indefinitely }}
+ +
@@ -231,6 +270,8 @@ import { iAmAdmin, $i, iAmModerator } from '@/i.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkInput from '@/components/MkInput.vue'; +import MkNumber from '@/components/MkNumber.vue'; +import { copyToClipboard } from '@/utility/copy-to-clipboard'; const props = withDefaults(defineProps<{ userId: string; @@ -740,4 +781,12 @@ definePage(() => ({ border-radius: var(--MI-radius-sm); cursor: pointer; } + +.buttonStrip { + margin: calc(var(--MI-margin) / 2 * -1); + + >* { + margin: calc(var(--MI-margin) / 2); + } +} diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 778b00c66d..dee803a9e6 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -11222,6 +11222,14 @@ export type operations = { expiresAt: string | null; roleId: string; })[]; + followStats: { + totalFollowing: number; + totalFollowers: number; + localFollowing: number; + localFollowers: number; + remoteFollowing: number; + remoteFollowers: number; + }; }; }; }; diff --git a/sharkey-locales/en-US.yml b/sharkey-locales/en-US.yml index 154e25f8eb..f1ad66fc8c 100644 --- a/sharkey-locales/en-US.yml +++ b/sharkey-locales/en-US.yml @@ -572,5 +572,20 @@ bubbleTimelineMustBeEnabled: "Note: the bubble timeline is hidden by default, an popularUsersGlobal: "Users popular on the global network" popularUsersLocal: "Users popular on {name}" +silenced: "Silenced" +totalFollowers: "Total followers" +totalFollowing: "Total following" +localFollowers: "Local followers" +localFollowing: "Local following" +remoteFollowers: "Remote followers" +remoteFollowing: "Remote following" +updateRemoteUser: "Refresh remote data" +activityPub: "Activity Pub" +ip: "IP" +ipTip: "The date is when IP address was first used." +rolePeriod: "Period" +roleAssigned: "Assigned" +roleAutomatic: "automatic" + translationTimeoutLabel: "Translation timeout" translationTimeoutCaption: "Timeout in milliseconds for translation API requests."