disable status badge strip in admin-user and instance-info

This commit is contained in:
Hazelnoot 2025-05-29 22:20:21 -04:00
parent f303cb1171
commit 979c7628b1
9 changed files with 211 additions and 9 deletions

28
locales/index.d.ts vendored
View file

@ -13145,10 +13145,38 @@ export interface Locale extends ILocale {
* Last posted: {at} * Last posted: {at}
*/ */
"lastPosted": ParameterizedString<"at">; "lastPosted": ParameterizedString<"at">;
/**
* NSFW
*/
"nsfw": string;
/** /**
* Raw * Raw
*/ */
"raw": string; "raw": string;
/**
* CW
*/
"cw": string;
/**
* Media Silenced
*/
"mediaSilenced": string;
/**
* Bubble
*/
"bubble": string;
/**
* Verified
*/
"verified": string;
/**
* Not Verified
*/
"notVerified": string;
/**
* Hibernated
*/
"hibernated": string;
} }
declare const locales: { declare const locales: {
[lang: string]: Locale; [lang: string]: Locale;

View file

@ -67,6 +67,15 @@ export class UtilityService {
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
} }
@bindThis
public isBubbledHost(host: string | null): boolean {
if (host == null) return false;
// TODO remove null conditional after merging lab/persisted-instance-blocks
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
return this.meta.bubbleInstances?.includes(host);
}
@bindThis @bindThis
public concatNoteContentsForKeyWordCheck(content: { public concatNoteContentsForKeyWordCheck(content: {
cw?: string | null; cw?: string | null;

View file

@ -62,6 +62,7 @@ export class InstanceEntityService {
rejectReports: instance.rejectReports, rejectReports: instance.rejectReports,
rejectQuotes: instance.rejectQuotes, rejectQuotes: instance.rejectQuotes,
moderationNote: iAmModerator ? instance.moderationNote : null, moderationNote: iAmModerator ? instance.moderationNote : null,
isBubbled: this.utilityService.isBubbledHost(instance.host),
}; };
} }

View file

@ -135,5 +135,9 @@ export const packedFederationInstanceSchema = {
type: 'string', type: 'string',
optional: true, nullable: true, optional: true, nullable: true,
}, },
isBubbled: {
type: 'boolean',
optional: false, nullable: false,
},
}, },
} as const; } as const;

View file

@ -122,6 +122,10 @@ export const meta = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
isAdministrator: {
type: 'boolean',
optional: false, nullable: false,
},
isSystem: { isSystem: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
@ -257,6 +261,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
const isModerator = await this.roleService.isModerator(user); const isModerator = await this.roleService.isModerator(user);
const isAdministrator = await this.roleService.isAdministrator(user);
const isSilenced = user.isSilenced || !(await this.roleService.getUserPolicies(user.id)).canPublicNote; const isSilenced = user.isSilenced || !(await this.roleService.getUserPolicies(user.id)).canPublicNote;
const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
@ -289,6 +294,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
mutedInstances: profile.mutedInstances, mutedInstances: profile.mutedInstances,
notificationRecieveConfig: profile.notificationRecieveConfig, notificationRecieveConfig: profile.notificationRecieveConfig,
isModerator: isModerator, isModerator: isModerator,
isAdministrator: isAdministrator,
isSystem: isSystemAccount(user), isSystem: isSystemAccount(user),
isSilenced: isSilenced, isSilenced: isSilenced,
isSuspended: user.isSuspended, isSuspended: user.isSuspended,

View file

@ -20,16 +20,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<span class="_monospace">{{ user.id }}</span> <span class="_monospace">{{ user.id }}</span>
<button v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copyToClipboard(user.id)"><i class="ti ti-copy"></i></button> <button v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copyToClipboard(user.id)"><i class="ti ti-copy"></i></button>
</span> </span>
<span class="state">
<span v-if="!approved" class="silenced">{{ i18n.ts.notApproved }}</span>
<span v-if="approved && !user.host" class="moderator">{{ i18n.ts.approved }}</span>
<span v-if="suspended" class="suspended">{{ i18n.ts.suspended }}</span>
<span v-if="silenced" class="silenced">{{ i18n.ts.silenced }}</span>
<span v-if="moderator" class="moderator">{{ i18n.ts.moderator }}</span>
</span>
</div> </div>
</div> </div>
<SkBadgeStrip v-if="badges.length > 0" :badges="badges"></SkBadgeStrip>
<MkInfo v-if="isSystem">{{ i18n.ts.isSystemAccount }}</MkInfo> <MkInfo v-if="isSystem">{{ i18n.ts.isSystemAccount }}</MkInfo>
<MkFolder v-if="!isSystem"> <MkFolder v-if="!isSystem">
@ -248,6 +243,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, defineAsyncComponent, watch, ref } from 'vue'; import { computed, defineAsyncComponent, watch, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js'; import { url } from '@@/js/config.js';
import type { Badge } from '@/components/SkBadgeStrip.vue';
import MkChart from '@/components/MkChart.vue'; import MkChart from '@/components/MkChart.vue';
import MkObjectView from '@/components/MkObjectView.vue'; import MkObjectView from '@/components/MkObjectView.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
@ -272,6 +268,7 @@ import MkPagination from '@/components/MkPagination.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkNumber from '@/components/MkNumber.vue'; import MkNumber from '@/components/MkNumber.vue';
import { copyToClipboard } from '@/utility/copy-to-clipboard'; import { copyToClipboard } from '@/utility/copy-to-clipboard';
import SkBadgeStrip from '@/components/SkBadgeStrip.vue';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
userId: string; userId: string;
@ -304,6 +301,98 @@ const filesPagination = {
})), })),
}; };
const badges = computed(() => {
const arr: Badge[] = [];
if (info.value && user.value) {
if (info.value.isSuspended) {
arr.push({
key: 'suspended',
label: i18n.ts.suspended,
style: 'error',
});
}
if (info.value.isSilenced) {
arr.push({
key: 'silenced',
label: i18n.ts.silenced,
style: 'warning',
});
}
if (info.value.alwaysMarkNsfw) {
arr.push({
key: 'nsfw',
label: i18n.ts.nsfw,
style: 'warning',
});
}
if (user.value.mandatoryCW) {
arr.push({
key: 'cw',
label: i18n.ts.cw,
style: 'warning',
});
}
if (info.value.isHibernated) {
arr.push({
key: 'hibernated',
label: i18n.ts.hibernated,
style: 'neutral',
});
}
if (info.value.isAdministrator) {
arr.push({
key: 'admin',
label: i18n.ts.administrator,
style: 'success',
});
} else if (info.value.isModerator) {
arr.push({
key: 'mod',
label: i18n.ts.moderator,
style: 'success',
});
}
if (user.value.host == null) {
if (info.value.email) {
if (info.value.emailVerified) {
arr.push({
key: 'verified',
label: i18n.ts.verified,
style: 'success',
});
} else {
arr.push({
key: 'not_verified',
label: i18n.ts.notVerified,
style: 'success',
});
}
}
if (info.value.approved) {
arr.push({
key: 'approved',
label: i18n.ts.approved,
style: 'success',
});
} else {
arr.push({
key: 'not_approved',
label: i18n.ts.notApproved,
style: 'warning',
});
}
}
}
return arr;
});
const announcementsStatus = ref<'active' | 'archived'>('active'); const announcementsStatus = ref<'active' | 'archived'>('active');
const announcementsPagination = { const announcementsPagination = {

View file

@ -24,6 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</span> </span>
</div> </div>
</div> </div>
<SkBadgeStrip v-if="badges.length > 0" :badges="badges"></SkBadgeStrip>
<MkFolder> <MkFolder>
<template #icon><i class="ph-list-bullets ph-bold ph-lg"></i></template> <template #icon><i class="ph-list-bullets ph-bold ph-lg"></i></template>
<template #label>{{ i18n.ts.details }}</template> <template #label>{{ i18n.ts.details }}</template>
@ -200,10 +203,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch } from 'vue'; import { ref, computed, watch, useCssModule } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { ChartSrc } from '@/components/MkChart.vue'; import type { ChartSrc } from '@/components/MkChart.vue';
import type { Paging } from '@/components/MkPagination.vue'; import type { Paging } from '@/components/MkPagination.vue';
import type { Badge } from '@/components/SkBadgeStrip.vue';
import MkChart from '@/components/MkChart.vue'; import MkChart from '@/components/MkChart.vue';
import MkObjectView from '@/components/MkObjectView.vue'; import MkObjectView from '@/components/MkObjectView.vue';
import FormLink from '@/components/form/link.vue'; import FormLink from '@/components/form/link.vue';
@ -230,6 +234,9 @@ import { copyToClipboard } from '@/utility/copy-to-clipboard';
import { acct } from '@/filters/user'; import { acct } from '@/filters/user';
import MkFolder from '@/components/MkFolder.vue'; import MkFolder from '@/components/MkFolder.vue';
import MkNumber from '@/components/MkNumber.vue'; import MkNumber from '@/components/MkNumber.vue';
import SkBadgeStrip from '@/components/SkBadgeStrip.vue';
const $style = useCssModule();
const props = defineProps<{ const props = defineProps<{
host: string; host: string;
@ -266,6 +273,55 @@ const isBaseBlocked = computed(() => meta.value && baseDomains.value.some(d => m
const isBaseSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.silencedHosts.includes(d))); const isBaseSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.silencedHosts.includes(d)));
const isBaseMediaSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.mediaSilencedHosts.includes(d))); const isBaseMediaSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.mediaSilencedHosts.includes(d)));
const badges = computed(() => {
const arr: Badge[] = [];
if (instance.value) {
if (instance.value.isBlocked) {
arr.push({
key: 'blocked',
label: i18n.ts.blocked,
style: 'error',
});
}
if (instance.value.isSuspended) {
arr.push({
key: 'suspended',
label: i18n.ts.suspended,
style: 'error',
});
}
if (instance.value.isSilenced) {
arr.push({
key: 'silenced',
label: i18n.ts.silenced,
style: 'warning',
});
}
if (instance.value.isMediaSilenced) {
arr.push({
key: 'media_silenced',
label: i18n.ts.mediaSilenced,
style: 'warning',
});
}
if (instance.value.isNSFW) {
arr.push({
key: 'nsfw',
label: i18n.ts.nsfw,
style: 'warning',
});
}
if (instance.value.isBubbled) {
arr.push({
key: 'bubbled',
label: i18n.ts.bubble,
style: 'success',
});
}
}
return arr;
});
const usersPagination = { const usersPagination = {
endpoint: iAmModerator ? 'admin/show-users' : 'users', endpoint: iAmModerator ? 'admin/show-users' : 'users',
limit: 10, limit: 10,

View file

@ -5294,6 +5294,7 @@ export type components = {
rejectReports: boolean; rejectReports: boolean;
rejectQuotes: boolean; rejectQuotes: boolean;
moderationNote?: string | null; moderationNote?: string | null;
isBubbled: boolean;
}; };
GalleryPost: { GalleryPost: {
/** /**
@ -11209,6 +11210,7 @@ export type operations = {
}]>; }]>;
}; };
isModerator: boolean; isModerator: boolean;
isAdministrator: boolean;
isSystem: boolean; isSystem: boolean;
isSilenced: boolean; isSilenced: boolean;
isSuspended: boolean; isSuspended: boolean;

View file

@ -594,4 +594,11 @@ followingPub: "Following (Pub)"
followersSub: "Followers (Sub)" followersSub: "Followers (Sub)"
wellKnownResources: "Well-known resources" wellKnownResources: "Well-known resources"
lastPosted: "Last posted: {at}" lastPosted: "Last posted: {at}"
raw: 'Raw' nsfw: "NSFW"
raw: "Raw"
cw: "CW"
mediaSilenced: "Media Silenced"
bubble: "Bubble"
verified: "Verified"
notVerified: "Not Verified"
hibernated: "Hibernated"