diff --git a/locales/en-US.yml b/locales/en-US.yml index 69d6424199..e33999d6d4 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -922,6 +922,7 @@ recentNHours: "Last {n} hours" recentNDays: "Last {n} days" noEmailServerWarning: "Email server not configured." thereIsUnresolvedAbuseReportWarning: "There are unsolved reports." +pendingUserApprovals: "There are users awaiting approval." recommended: "Recommended" check: "Check" driveCapOverrideLabel: "Change the drive capacity for this user" diff --git a/locales/index.d.ts b/locales/index.d.ts index be6ad77821..93f5e65af0 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -925,6 +925,7 @@ export interface Locale { "recentNDays": string; "noEmailServerWarning": string; "thereIsUnresolvedAbuseReportWarning": string; + "pendingUserApprovals": string; "recommended": string; "check": string; "driveCapOverrideLabel": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e8a9d980be..0d43a5d5a6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -922,6 +922,7 @@ recentNHours: "直近{n}時間" recentNDays: "直近{n}日" noEmailServerWarning: "メールサーバーの設定がされていません。" thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。" +pendingUserApprovals: "承認待ちのユーザーがいる。" recommended: "推奨" check: "チェック" driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更" diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index b02b0af234..02fbd50ee4 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -370,6 +370,7 @@ export class UserEntityService implements OnModuleInit { isCat: user.isCat, isSilenced: user.isSilenced || this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), speakAsCat: user.speakAsCat ?? falsy, + approved: user.approved, instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? { name: instance.name, softwareName: instance.softwareName, @@ -489,7 +490,6 @@ export class UserEntityService implements OnModuleInit { ...(opts.includeSecrets ? { email: profile!.email, emailVerified: profile!.emailVerified, - approved: user.approved, signupReason: user.signupReason, securityKeysList: profile!.twoFactorEnabled ? this.userSecurityKeysRepository.find({ diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index 465a109887..70c2a37b53 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-adaptive-bg :class="[$style.root, { yellow: user.isSilenced, red: user.isSuspended, gray: false }]"> +<div v-adaptive-bg :class="[$style.root, { yellow: user.isSilenced, blue: !user.approved, red: user.isSuspended, gray: false }]"> <MkAvatar class="avatar" :user="user" indicator/> <div class="body"> <span class="name"><MkUserName class="name" :user="user"/></span> @@ -97,6 +97,12 @@ onMounted(() => { background-size: 16px 16px; } + &:global(.blue) { + --c: rgba(0 153 255 / 15%); + background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); + background-size: 16px 16px; + } + &:global(.red) { --c: rgb(255 0 0 / 15%); background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 768f9fee25..ce608c89ce 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> <MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> <MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> + <MkInfo v-if="pendingUserApprovals" warn class="info">{{ i18n.ts.pendingUserApprovals }} <MkA to="/admin/users" class="_link">{{ i18n.ts.check }}</MkA></MkInfo> <MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu> </div> @@ -60,6 +61,7 @@ let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instan let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile; let noEmailServer = !instance.enableEmail; let thereIsUnresolvedAbuseReport = $ref(false); +let pendingUserApprovals = $ref(false); let currentPage = $computed(() => router.currentRef.value.child); os.api('admin/abuse-user-reports', { @@ -69,6 +71,13 @@ os.api('admin/abuse-user-reports', { if (reports.length > 0) thereIsUnresolvedAbuseReport = true; }); +os.api('admin/show-users', { + state: 'approved', + limit: 1, +}).then(approvals => { + if (approvals.length > 0) pendingUserApprovals = true; +}); + const NARROW_THRESHOLD = 600; const ro = new ResizeObserver((entries, observer) => { if (entries.length === 0) return; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 4ab57c6409..baf59e7283 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -16,6 +16,7 @@ export type UserLite = { onlineStatus: 'online' | 'active' | 'offline' | 'unknown'; avatarUrl: string; avatarBlurhash: string; + approved: boolean; emojis: { name: string; url: string;