mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-23 01:44:10 +02:00
refactor(frontend): os.select, MkSelectのitem指定をオブジェクトによる定義に統一し、型を狭める (#16475)
* refactor(frontend): MkSelectのitem指定をオブジェクトによる定義に統一 * fix * spdx * fix * fix os.select * fix lint * add comment * fix * fix: os.select対応漏れを修正 * fix * fix * fix: MkSelectのmodelに対する型チェックを厳格化 * fix * fix * fix * Update packages/frontend/src/components/MkEmbedCodeGenDialog.vue Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * fix * fix types * fix * fix * Update packages/frontend/src/pages/admin/roles.editor.vue Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * fix: MkSelectに直接配列を指定している場合に正常に型が解決されるように --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
@@ -6,26 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<div class="_gaps">
|
||||
<div :class="$style.header">
|
||||
<MkSelect v-model="type" :class="$style.typeSelect">
|
||||
<option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option>
|
||||
<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
|
||||
<option value="isSuspended">{{ i18n.ts._role._condition.isSuspended }}</option>
|
||||
<option value="isLocked">{{ i18n.ts._role._condition.isLocked }}</option>
|
||||
<option value="isBot">{{ i18n.ts._role._condition.isBot }}</option>
|
||||
<option value="isCat">{{ i18n.ts._role._condition.isCat }}</option>
|
||||
<option value="isExplorable">{{ i18n.ts._role._condition.isExplorable }}</option>
|
||||
<option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option>
|
||||
<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
|
||||
<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
|
||||
<option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option>
|
||||
<option value="followersMoreThanOrEq">{{ i18n.ts._role._condition.followersMoreThanOrEq }}</option>
|
||||
<option value="followingLessThanOrEq">{{ i18n.ts._role._condition.followingLessThanOrEq }}</option>
|
||||
<option value="followingMoreThanOrEq">{{ i18n.ts._role._condition.followingMoreThanOrEq }}</option>
|
||||
<option value="notesLessThanOrEq">{{ i18n.ts._role._condition.notesLessThanOrEq }}</option>
|
||||
<option value="notesMoreThanOrEq">{{ i18n.ts._role._condition.notesMoreThanOrEq }}</option>
|
||||
<option value="and">{{ i18n.ts._role._condition.and }}</option>
|
||||
<option value="or">{{ i18n.ts._role._condition.or }}</option>
|
||||
<option value="not">{{ i18n.ts._role._condition.not }}</option>
|
||||
<MkSelect v-model="type" :items="typeDef" :class="$style.typeSelect">
|
||||
</MkSelect>
|
||||
<button v-if="draggable" class="drag-handle _button" :class="$style.dragHandle">
|
||||
<i class="ti ti-menu-2"></i>
|
||||
@@ -58,8 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type)" v-model="v.value" type="number">
|
||||
</MkInput>
|
||||
|
||||
<MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId">
|
||||
<option v-for="role in roles.filter(r => r.target === 'manual')" :key="role.id" :value="role.id">{{ role.name }}</option>
|
||||
<MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId" :items="assignedToDef">
|
||||
</MkSelect>
|
||||
</div>
|
||||
</template>
|
||||
@@ -69,6 +49,7 @@ import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import type { GetMkSelectValueTypesFromDef, MkSelectItem } from '@/components/MkSelect.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { deepClone } from '@/utility/clone.js';
|
||||
@@ -99,7 +80,29 @@ watch(v, () => {
|
||||
emit('update:modelValue', v.value);
|
||||
}, { deep: true });
|
||||
|
||||
const type = computed({
|
||||
const typeDef = [
|
||||
{ label: i18n.ts._role._condition.isLocal, value: 'isLocal' },
|
||||
{ label: i18n.ts._role._condition.isRemote, value: 'isRemote' },
|
||||
{ label: i18n.ts._role._condition.isSuspended, value: 'isSuspended' },
|
||||
{ label: i18n.ts._role._condition.isLocked, value: 'isLocked' },
|
||||
{ label: i18n.ts._role._condition.isBot, value: 'isBot' },
|
||||
{ label: i18n.ts._role._condition.isCat, value: 'isCat' },
|
||||
{ label: i18n.ts._role._condition.isExplorable, value: 'isExplorable' },
|
||||
{ label: i18n.ts._role._condition.roleAssignedTo, value: 'roleAssignedTo' },
|
||||
{ label: i18n.ts._role._condition.createdLessThan, value: 'createdLessThan' },
|
||||
{ label: i18n.ts._role._condition.createdMoreThan, value: 'createdMoreThan' },
|
||||
{ label: i18n.ts._role._condition.followersLessThanOrEq, value: 'followersLessThanOrEq' },
|
||||
{ label: i18n.ts._role._condition.followersMoreThanOrEq, value: 'followersMoreThanOrEq' },
|
||||
{ label: i18n.ts._role._condition.followingLessThanOrEq, value: 'followingLessThanOrEq' },
|
||||
{ label: i18n.ts._role._condition.followingMoreThanOrEq, value: 'followingMoreThanOrEq' },
|
||||
{ label: i18n.ts._role._condition.notesLessThanOrEq, value: 'notesLessThanOrEq' },
|
||||
{ label: i18n.ts._role._condition.notesMoreThanOrEq, value: 'notesMoreThanOrEq' },
|
||||
{ label: i18n.ts._role._condition.and, value: 'and' },
|
||||
{ label: i18n.ts._role._condition.or, value: 'or' },
|
||||
{ label: i18n.ts._role._condition.not, value: 'not' },
|
||||
] as const satisfies MkSelectItem[];
|
||||
|
||||
const type = computed<GetMkSelectValueTypesFromDef<typeof typeDef>>({
|
||||
get: () => v.value.type,
|
||||
set: (t) => {
|
||||
if (t === 'and') v.value.values = [];
|
||||
@@ -118,6 +121,8 @@ const type = computed({
|
||||
},
|
||||
});
|
||||
|
||||
const assignedToDef = computed(() => roles.filter(r => r.target === 'manual').map(r => ({ label: r.name, value: r.id })) satisfies MkSelectItem[]);
|
||||
|
||||
function addValue() {
|
||||
v.value.values.push({ id: genId(), type: 'isRemote' });
|
||||
}
|
||||
|
||||
@@ -22,27 +22,19 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInput v-model="title">
|
||||
<template #label>{{ i18n.ts.title }}</template>
|
||||
</MkInput>
|
||||
<MkSelect v-model="method">
|
||||
<MkSelect v-model="method" :items="methodDef">
|
||||
<template #label>{{ i18n.ts._abuseReport._notificationRecipient.recipientType }}</template>
|
||||
<option value="email">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.mail }}</option>
|
||||
<option value="webhook">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.webhook }}</option>
|
||||
<template #caption>
|
||||
{{ methodCaption }}
|
||||
</template>
|
||||
</MkSelect>
|
||||
<div>
|
||||
<MkSelect v-if="method === 'email'" v-model="userId">
|
||||
<MkSelect v-if="method === 'email'" v-model="userId" :items="userIdDef">
|
||||
<template #label>{{ i18n.ts._abuseReport._notificationRecipient.notifiedUser }}</template>
|
||||
<option v-for="user in moderators" :key="user.id" :value="user.id">
|
||||
{{ user.name ? `${user.name}(${user.username})` : user.username }}
|
||||
</option>
|
||||
</MkSelect>
|
||||
<div v-else-if="method === 'webhook'" :class="$style.systemWebhook">
|
||||
<MkSelect v-model="systemWebhookId" style="flex: 1">
|
||||
<MkSelect v-model="systemWebhookId" :items="systemWebhookIdDef" style="flex: 1">
|
||||
<template #label>{{ i18n.ts._abuseReport._notificationRecipient.notifiedWebhook }}</template>
|
||||
<option v-for="webhook in systemWebhooks" :key="webhook.id ?? undefined" :value="webhook.id">
|
||||
{{ webhook.name }}
|
||||
</option>
|
||||
</MkSelect>
|
||||
<MkButton rounded :class="$style.systemWebhookEditButton" @click="onEditSystemWebhookClicked">
|
||||
<span v-if="systemWebhookId === null" class="ti ti-plus" style="line-height: normal"/>
|
||||
@@ -79,14 +71,13 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import { showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkDivider from '@/components/MkDivider.vue';
|
||||
import * as os from '@/os.js';
|
||||
|
||||
type NotificationRecipientMethod = 'email' | 'webhook';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'submitted'): void;
|
||||
(ev: 'canceled'): void;
|
||||
@@ -105,9 +96,28 @@ const dialogEl = useTemplateRef('dialogEl');
|
||||
const loading = ref<number>(0);
|
||||
|
||||
const title = ref<string>('');
|
||||
const method = ref<NotificationRecipientMethod>('email');
|
||||
const userId = ref<string | null>(null);
|
||||
const systemWebhookId = ref<string | null>(null);
|
||||
const {
|
||||
model: method,
|
||||
def: methodDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts._abuseReport._notificationRecipient._recipientType.mail, value: 'email' },
|
||||
{ label: i18n.ts._abuseReport._notificationRecipient._recipientType.webhook, value: 'webhook' },
|
||||
],
|
||||
initialValue: 'email',
|
||||
});
|
||||
const {
|
||||
model: userId,
|
||||
def: userIdDef,
|
||||
} = useMkSelect({
|
||||
items: computed(() => moderators.value.map(u => ({ label: u.name ? `${u.name}(${u.username})` : u.username, value: u.id as string | null }))),
|
||||
});
|
||||
const {
|
||||
model: systemWebhookId,
|
||||
def: systemWebhookIdDef,
|
||||
} = useMkSelect({
|
||||
items: computed(() => systemWebhooks.value.map(w => ({ label: w.name, value: w.id }))),
|
||||
});
|
||||
const isActive = ref<boolean>(true);
|
||||
|
||||
const moderators = ref<entities.User[]>([]);
|
||||
|
||||
@@ -13,11 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkButton>
|
||||
</div>
|
||||
<div :class="$style.subMenus" class="_gaps_s">
|
||||
<MkSelect v-model="filterMethod" style="flex: 1">
|
||||
<MkSelect v-model="filterMethod" :items="filterMethodDef" style="flex: 1">
|
||||
<template #label>{{ i18n.ts._abuseReport._notificationRecipient.recipientType }}</template>
|
||||
<option :value="null">-</option>
|
||||
<option :value="'email'">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.mail }}</option>
|
||||
<option :value="'webhook'">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.webhook }}</option>
|
||||
</MkSelect>
|
||||
<MkInput v-model="filterText" type="search" style="flex: 1">
|
||||
<template #label>{{ i18n.ts._abuseReport._notificationRecipient.keywords }}</template>
|
||||
@@ -51,10 +48,21 @@ import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os.js';
|
||||
import MkDivider from '@/components/MkDivider.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
const recipients = ref<entities.AbuseReportNotificationRecipient[]>([]);
|
||||
|
||||
const filterMethod = ref<string | null>(null);
|
||||
const {
|
||||
model: filterMethod,
|
||||
def: filterMethodDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: null },
|
||||
{ label: i18n.ts._abuseReport._notificationRecipient._recipientType.mail, value: 'email' },
|
||||
{ label: i18n.ts._abuseReport._notificationRecipient._recipientType.webhook, value: 'webhook' },
|
||||
],
|
||||
initialValue: null,
|
||||
});
|
||||
const filterText = ref<string>('');
|
||||
|
||||
const filteredRecipients = computed(() => {
|
||||
|
||||
@@ -16,23 +16,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkTip>
|
||||
|
||||
<div :class="$style.inputs" class="_gaps">
|
||||
<MkSelect v-model="state" style="margin: 0; flex: 1;">
|
||||
<MkSelect v-model="state" :items="stateDef" style="margin: 0; flex: 1;">
|
||||
<template #label>{{ i18n.ts.state }}</template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="unresolved">{{ i18n.ts.unresolved }}</option>
|
||||
<option value="resolved">{{ i18n.ts.resolved }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="targetUserOrigin" style="margin: 0; flex: 1;">
|
||||
<MkSelect v-model="targetUserOrigin" :items="targetUserOriginDef" style="margin: 0; flex: 1;">
|
||||
<template #label>{{ i18n.ts.reporteeOrigin }}</template>
|
||||
<option value="combined">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.local }}</option>
|
||||
<option value="remote">{{ i18n.ts.remote }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="reporterOrigin" style="margin: 0; flex: 1;">
|
||||
<MkSelect v-model="reporterOrigin" :items="reporterOriginDef" style="margin: 0; flex: 1;">
|
||||
<template #label>{{ i18n.ts.reporterOrigin }}</template>
|
||||
<option value="combined">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.local }}</option>
|
||||
<option value="remote">{{ i18n.ts.remote }}</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
|
||||
@@ -64,13 +55,44 @@ import MkPagination from '@/components/MkPagination.vue';
|
||||
import XAbuseReport from '@/components/MkAbuseReport.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { store } from '@/store.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const state = ref('unresolved');
|
||||
const reporterOrigin = ref('combined');
|
||||
const targetUserOrigin = ref('combined');
|
||||
const {
|
||||
model: state,
|
||||
def: stateDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'all' },
|
||||
{ label: i18n.ts.unresolved, value: 'unresolved' },
|
||||
{ label: i18n.ts.resolved, value: 'resolved' },
|
||||
],
|
||||
initialValue: 'unresolved',
|
||||
});
|
||||
const {
|
||||
model: reporterOrigin,
|
||||
def: reporterOriginDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'combined' },
|
||||
{ label: i18n.ts.local, value: 'local' },
|
||||
{ label: i18n.ts.remote, value: 'remote' },
|
||||
],
|
||||
initialValue: 'combined',
|
||||
});
|
||||
const {
|
||||
model: targetUserOrigin,
|
||||
def: targetUserOriginDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'combined' },
|
||||
{ label: i18n.ts.local, value: 'local' },
|
||||
{ label: i18n.ts.remote, value: 'remote' },
|
||||
],
|
||||
initialValue: 'combined',
|
||||
});
|
||||
const searchUsername = ref('');
|
||||
const searchHost = ref('');
|
||||
|
||||
|
||||
@@ -6,11 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 900px;">
|
||||
<MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems">
|
||||
<MkSelect v-model="filterType" :items="filterTypeDef" :class="$style.input" @update:modelValue="filterItems">
|
||||
<template #label>{{ i18n.ts.state }}</template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="publishing">{{ i18n.ts.publishing }}</option>
|
||||
<option value="expired">{{ i18n.ts.expired }}</option>
|
||||
</MkSelect>
|
||||
<div>
|
||||
<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
|
||||
@@ -95,6 +92,7 @@ import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
const ads = ref<Misskey.entities.Ad[]>([]);
|
||||
|
||||
@@ -102,7 +100,17 @@ const ads = ref<Misskey.entities.Ad[]>([]);
|
||||
const localTime = new Date();
|
||||
const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
|
||||
const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
|
||||
const filterType = ref('all');
|
||||
const {
|
||||
model: filterType,
|
||||
def: filterTypeDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'all' },
|
||||
{ label: i18n.ts.publishing, value: 'publishing' },
|
||||
{ label: i18n.ts.expired, value: 'expired' },
|
||||
],
|
||||
initialValue: 'all',
|
||||
});
|
||||
let publishing: boolean | null = null;
|
||||
|
||||
misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
||||
@@ -121,7 +129,7 @@ misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
||||
}
|
||||
});
|
||||
|
||||
const filterItems = (v) => {
|
||||
const filterItems = (v: typeof filterType.value) => {
|
||||
if (v === 'publishing') {
|
||||
publishing = true;
|
||||
} else if (v === 'expired') {
|
||||
@@ -134,7 +142,7 @@ const filterItems = (v) => {
|
||||
};
|
||||
|
||||
// 選択された曜日(index)のビットフラグを操作する
|
||||
function toggleDayOfWeek(ad, index) {
|
||||
function toggleDayOfWeek(ad: Misskey.entities.Ad, index: number) {
|
||||
ad.dayOfWeek ^= 1 << index;
|
||||
}
|
||||
|
||||
@@ -153,7 +161,7 @@ function add() {
|
||||
});
|
||||
}
|
||||
|
||||
function remove(ad) {
|
||||
function remove(ad: Misskey.entities.Ad) {
|
||||
os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.tsx.removeAreYouSure({ x: ad.url }),
|
||||
@@ -169,7 +177,7 @@ function remove(ad) {
|
||||
});
|
||||
}
|
||||
|
||||
function save(ad) {
|
||||
function save(ad: Misskey.entities.Ad) {
|
||||
if (ad.id === '') {
|
||||
misskeyApi('admin/ad/create', {
|
||||
...ad,
|
||||
|
||||
@@ -10,10 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo>
|
||||
<MkInfo v-if="announcements.length > 5" warn>{{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }}</MkInfo>
|
||||
|
||||
<MkSelect v-model="announcementsStatus">
|
||||
<MkSelect v-model="announcementsStatus" :items="announcementsStatusDef">
|
||||
<template #label>{{ i18n.ts.filter }}</template>
|
||||
<option value="active">{{ i18n.ts.active }}</option>
|
||||
<option value="archived">{{ i18n.ts.archived }}</option>
|
||||
</MkSelect>
|
||||
|
||||
<MkLoading v-if="loading"/>
|
||||
@@ -98,8 +96,18 @@ import { definePage } from '@/page.js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
const announcementsStatus = ref<'active' | 'archived'>('active');
|
||||
const {
|
||||
model: announcementsStatus,
|
||||
def: announcementsStatusDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.active, value: 'active' },
|
||||
{ label: i18n.ts.archived, value: 'archived' },
|
||||
],
|
||||
initialValue: 'active',
|
||||
});
|
||||
|
||||
const loading = ref(true);
|
||||
const loadingMore = ref(false);
|
||||
|
||||
@@ -56,20 +56,24 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkInput>
|
||||
<MkSelect
|
||||
v-model="model.sensitive"
|
||||
:items="[
|
||||
{ label: '-', value: null },
|
||||
{ label: 'true', value: 'true' },
|
||||
{ label: 'false', value: 'false' },
|
||||
]"
|
||||
>
|
||||
<template #label>sensitive</template>
|
||||
<option :value="null">-</option>
|
||||
<option :value="true">true</option>
|
||||
<option :value="false">false</option>
|
||||
</MkSelect>
|
||||
|
||||
<MkSelect
|
||||
v-model="model.localOnly"
|
||||
:items="[
|
||||
{ label: '-', value: null },
|
||||
{ label: 'true', value: 'true' },
|
||||
{ label: 'false', value: 'false' },
|
||||
]"
|
||||
>
|
||||
<template #label>localOnly</template>
|
||||
<option :value="null">-</option>
|
||||
<option :value="true">true</option>
|
||||
<option :value="false">false</option>
|
||||
</MkSelect>
|
||||
<MkInput
|
||||
v-model="model.updatedAtFrom"
|
||||
|
||||
@@ -12,11 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #caption>{{ i18n.ts._customEmojisManager._local._register.uploadSettingDescription }}</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSelect v-model="selectedFolderId">
|
||||
<MkSelect v-model="selectedFolderId" :items="selectedFolderIdDef">
|
||||
<template #label>{{ i18n.ts.uploadFolder }}</template>
|
||||
<option v-for="folder in uploadFolders" :key="folder.id" :value="folder.id">
|
||||
{{ folder.name }}
|
||||
</option>
|
||||
</MkSelect>
|
||||
|
||||
<MkSwitch v-model="directoryToCategory">
|
||||
@@ -63,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { onMounted, ref, useCssModule } from 'vue';
|
||||
import { computed, onMounted, ref, useCssModule } from 'vue';
|
||||
import type { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
|
||||
import type { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
|
||||
import type { DroppedFile } from '@/utility/file-drop.js';
|
||||
@@ -87,6 +84,7 @@ import { chooseDriveFile, chooseFileFromPcAndUpload } from '@/utility/drive.js';
|
||||
import { extractDroppedItems, flattenDroppedFiles } from '@/utility/file-drop.js';
|
||||
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue';
|
||||
import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
import { prefer } from '@/preferences.js';
|
||||
|
||||
@@ -229,7 +227,13 @@ function setupGrid(): GridSetting {
|
||||
|
||||
const uploadFolders = ref<FolderItem[]>([]);
|
||||
const gridItems = ref<GridItem[]>([]);
|
||||
const selectedFolderId = ref(prefer.s.uploadFolder);
|
||||
const {
|
||||
model: selectedFolderId,
|
||||
def: selectedFolderIdDef,
|
||||
} = useMkSelect({
|
||||
items: computed(() => uploadFolders.value.map(folder => ({ label: folder.name, value: folder.id || '' }))),
|
||||
initialValue: prefer.s.uploadFolder,
|
||||
});
|
||||
const directoryToCategory = ref<boolean>(false);
|
||||
const registerButtonDisabled = ref<boolean>(false);
|
||||
const requestLogs = ref<RequestLogItem[]>([]);
|
||||
|
||||
@@ -13,31 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label>{{ i18n.ts.host }}</template>
|
||||
</MkInput>
|
||||
<FormSplit style="margin-top: var(--MI-margin);">
|
||||
<MkSelect v-model="state">
|
||||
<MkSelect v-model="state" :items="stateDef">
|
||||
<template #label>{{ i18n.ts.state }}</template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="federating">{{ i18n.ts.federating }}</option>
|
||||
<option value="subscribing">{{ i18n.ts.subscribing }}</option>
|
||||
<option value="publishing">{{ i18n.ts.publishing }}</option>
|
||||
<option value="suspended">{{ i18n.ts.suspended }}</option>
|
||||
<option value="blocked">{{ i18n.ts.blocked }}</option>
|
||||
<option value="silenced">{{ i18n.ts.silence }}</option>
|
||||
<option value="notResponding">{{ i18n.ts.notResponding }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="sort">
|
||||
<MkSelect v-model="sort" :items="sortDef">
|
||||
<template #label>{{ i18n.ts.sort }}</template>
|
||||
<option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
</MkSelect>
|
||||
</FormSplit>
|
||||
</div>
|
||||
@@ -64,11 +44,46 @@ import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const host = ref('');
|
||||
const state = ref('federating');
|
||||
const sort = ref('+pubSub');
|
||||
const {
|
||||
model: state,
|
||||
def: stateDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'all' },
|
||||
{ label: i18n.ts.federating, value: 'federating' },
|
||||
{ label: i18n.ts.subscribing, value: 'subscribing' },
|
||||
{ label: i18n.ts.publishing, value: 'publishing' },
|
||||
{ label: i18n.ts.suspended, value: 'suspended' },
|
||||
{ label: i18n.ts.blocked, value: 'blocked' },
|
||||
{ label: i18n.ts.silence, value: 'silenced' },
|
||||
{ label: i18n.ts.notResponding, value: 'notResponding' },
|
||||
],
|
||||
initialValue: 'federating',
|
||||
});
|
||||
const {
|
||||
model: sort,
|
||||
def: sortDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: `${i18n.ts.pubSub} (${i18n.ts.descendingOrder})`, value: '+pubSub' },
|
||||
{ label: `${i18n.ts.pubSub} (${i18n.ts.ascendingOrder})`, value: '-pubSub' },
|
||||
{ label: `${i18n.ts.notes} (${i18n.ts.descendingOrder})`, value: '+notes' },
|
||||
{ label: `${i18n.ts.notes} (${i18n.ts.ascendingOrder})`, value: '-notes' },
|
||||
{ label: `${i18n.ts.users} (${i18n.ts.descendingOrder})`, value: '+users' },
|
||||
{ label: `${i18n.ts.users} (${i18n.ts.ascendingOrder})`, value: '-users' },
|
||||
{ label: `${i18n.ts.following} (${i18n.ts.descendingOrder})`, value: '+following' },
|
||||
{ label: `${i18n.ts.following} (${i18n.ts.ascendingOrder})`, value: '-following' },
|
||||
{ label: `${i18n.ts.followers} (${i18n.ts.descendingOrder})`, value: '+followers' },
|
||||
{ label: `${i18n.ts.followers} (${i18n.ts.ascendingOrder})`, value: '-followers' },
|
||||
{ label: `${i18n.ts.registeredAt} (${i18n.ts.descendingOrder})`, value: '+firstRetrievedAt' },
|
||||
{ label: `${i18n.ts.registeredAt} (${i18n.ts.ascendingOrder})`, value: '-firstRetrievedAt' },
|
||||
],
|
||||
initialValue: '+pubSub',
|
||||
});
|
||||
const paginator = markRaw(new Paginator('federation/instances', {
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
|
||||
@@ -8,11 +8,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_spacer" style="--MI_SPACER-w: 900px;">
|
||||
<div class="_gaps">
|
||||
<div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
|
||||
<MkSelect v-model="origin" style="margin: 0; flex: 1;">
|
||||
<MkSelect v-model="origin" :items="originDef" style="margin: 0; flex: 1;">
|
||||
<template #label>{{ i18n.ts.instance }}</template>
|
||||
<option value="combined">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.local }}</option>
|
||||
<option value="remote">{{ i18n.ts.remote }}</option>
|
||||
</MkSelect>
|
||||
<MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="paginator.computedParams?.value?.origin === 'local'">
|
||||
<template #label>{{ i18n.ts.host }}</template>
|
||||
@@ -42,9 +39,20 @@ import * as os from '@/os.js';
|
||||
import { lookupFile } from '@/utility/admin-lookup.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const origin = ref<NonNullable<Misskey.entities.AdminDriveFilesRequest['origin']>>('local');
|
||||
const {
|
||||
model: origin,
|
||||
def: originDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'combined' },
|
||||
{ label: i18n.ts.local, value: 'local' },
|
||||
{ label: i18n.ts.remote, value: 'remote' },
|
||||
],
|
||||
initialValue: 'local',
|
||||
});
|
||||
const type = ref<string | null>(null);
|
||||
const searchHost = ref('');
|
||||
const userId = ref('');
|
||||
|
||||
@@ -26,19 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkFolder>
|
||||
|
||||
<div :class="$style.inputs">
|
||||
<MkSelect v-model="type" :class="$style.input">
|
||||
<MkSelect v-model="type" :items="typeDef" :class="$style.input">
|
||||
<template #label>{{ i18n.ts.state }}</template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="unused">{{ i18n.ts.unused }}</option>
|
||||
<option value="used">{{ i18n.ts.used }}</option>
|
||||
<option value="expired">{{ i18n.ts.expired }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="sort" :class="$style.input">
|
||||
<MkSelect v-model="sort" :items="sortDef" :class="$style.input">
|
||||
<template #label>{{ i18n.ts.sort }}</template>
|
||||
<option value="+createdAt">{{ i18n.ts.createdAt }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="-createdAt">{{ i18n.ts.createdAt }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="+usedAt">{{ i18n.ts.usedAt }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="-usedAt">{{ i18n.ts.usedAt }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
<MkPagination :paginator="paginator">
|
||||
@@ -67,10 +59,33 @@ import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import MkInviteCode from '@/components/MkInviteCode.vue';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const type = ref<NonNullable<Misskey.entities.AdminInviteListRequest['type']>>('all');
|
||||
const sort = ref<NonNullable<Misskey.entities.AdminInviteListRequest['sort']>>('+createdAt');
|
||||
const {
|
||||
model: type,
|
||||
def: typeDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'all' },
|
||||
{ label: i18n.ts.unused, value: 'unused' },
|
||||
{ label: i18n.ts.used, value: 'used' },
|
||||
{ label: i18n.ts.expired, value: 'expired' },
|
||||
],
|
||||
initialValue: 'all',
|
||||
});
|
||||
const {
|
||||
model: sort,
|
||||
def: sortDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: `${i18n.ts.createdAt} (${i18n.ts.ascendingOrder})`, value: '+createdAt' },
|
||||
{ label: `${i18n.ts.createdAt} (${i18n.ts.descendingOrder})`, value: '-createdAt' },
|
||||
{ label: `${i18n.ts.usedAt} (${i18n.ts.ascendingOrder})`, value: '+usedAt' },
|
||||
{ label: `${i18n.ts.usedAt} (${i18n.ts.descendingOrder})`, value: '-usedAt' },
|
||||
],
|
||||
initialValue: '+createdAt',
|
||||
});
|
||||
|
||||
const paginator = markRaw(new Paginator('admin/invite/list', {
|
||||
limit: 10,
|
||||
|
||||
@@ -25,18 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['ugc', 'content', 'visibility', 'visitor', 'guest']">
|
||||
<MkSelect
|
||||
v-model="ugcVisibilityForVisitor" :items="[{
|
||||
value: 'all',
|
||||
label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all,
|
||||
}, {
|
||||
value: 'local',
|
||||
label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly + ' (' + i18n.ts.recommended + ')',
|
||||
}, {
|
||||
value: 'none',
|
||||
label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none,
|
||||
}] as const" @update:modelValue="onChange_ugcVisibilityForVisitor"
|
||||
>
|
||||
<MkSelect v-model="ugcVisibilityForVisitor" :items="ugcVisibilityForVisitorDef" @update:modelValue="onChange_ugcVisibilityForVisitor">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</SearchText></div>
|
||||
@@ -176,6 +165,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
@@ -185,7 +175,17 @@ const meta = await misskeyApi('admin/meta');
|
||||
|
||||
const enableRegistration = ref(!meta.disableRegistration);
|
||||
const emailRequiredForSignup = ref(meta.emailRequiredForSignup);
|
||||
const ugcVisibilityForVisitor = ref(meta.ugcVisibilityForVisitor);
|
||||
const {
|
||||
model: ugcVisibilityForVisitor,
|
||||
def: ugcVisibilityForVisitorDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all, value: 'all' },
|
||||
{ label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly, value: 'local' },
|
||||
{ label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none, value: 'none' },
|
||||
],
|
||||
initialValue: meta.ugcVisibilityForVisitor,
|
||||
});
|
||||
const sensitiveWords = ref(meta.sensitiveWords.join('\n'));
|
||||
const prohibitedWords = ref(meta.prohibitedWords.join('\n'));
|
||||
const prohibitedWordsForNameOfUser = ref(meta.prohibitedWordsForNameOfUser.join('\n'));
|
||||
@@ -221,7 +221,7 @@ function onChange_emailRequiredForSignup(value: boolean) {
|
||||
});
|
||||
}
|
||||
|
||||
function onChange_ugcVisibilityForVisitor(value: Misskey.entities.AdminUpdateMetaRequest['ugcVisibilityForVisitor']) {
|
||||
function onChange_ugcVisibilityForVisitor(value: typeof ugcVisibilityForVisitor.value) {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
ugcVisibilityForVisitor: value,
|
||||
}).then(() => {
|
||||
|
||||
@@ -8,10 +8,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_spacer" style="--MI_SPACER-w: 900px;">
|
||||
<div class="_gaps">
|
||||
<MkPaginationControl :paginator="paginator" canFilter>
|
||||
<MkSelect v-model="type" style="margin: 0; flex: 1;">
|
||||
<MkSelect v-model="type" :items="typeDef" style="margin: 0; flex: 1;">
|
||||
<template #label>{{ i18n.ts.type }}</template>
|
||||
<option :value="null">{{ i18n.ts.all }}</option>
|
||||
<option v-for="t in Misskey.moderationLogTypes" :key="t" :value="t">{{ i18n.ts._moderationLogTypes[t] ?? t }}</option>
|
||||
</MkSelect>
|
||||
|
||||
<MkInput v-model="moderatorId" style="margin: 0; flex: 1;">
|
||||
@@ -54,12 +52,22 @@ import MkTl from '@/components/MkTl.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkPaginationControl from '@/components/MkPaginationControl.vue';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const type = ref<string | null>(null);
|
||||
const {
|
||||
model: type,
|
||||
def: typeDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: null },
|
||||
...Misskey.moderationLogTypes.map(t => ({ label: i18n.ts._moderationLogTypes[t] ?? t, value: t })),
|
||||
],
|
||||
initialValue: null,
|
||||
});
|
||||
const moderatorId = ref('');
|
||||
|
||||
const paginator = markRaw(new Paginator('admin/show-moderation-logs', {
|
||||
|
||||
@@ -5,24 +5,30 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<template>
|
||||
<div class="_panel" :class="$style.root">
|
||||
<MkSelect v-model="src" style="margin: 0 0 12px 0;" small>
|
||||
<option value="active-users">Active users</option>
|
||||
<option value="notes">Notes</option>
|
||||
<option value="ap-requests-inbox-received">AP Requests: inboxReceived</option>
|
||||
<option value="ap-requests-deliver-succeeded">AP Requests: deliverSucceeded</option>
|
||||
<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option>
|
||||
<MkSelect v-model="src" :items="srcDef" style="margin: 0 0 12px 0;" small>
|
||||
</MkSelect>
|
||||
<MkHeatmap :src="src"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import MkHeatmap from '@/components/MkHeatmap.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import type { HeatmapSource } from '@/components/MkHeatmap.vue';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
const src = ref<HeatmapSource>('active-users');
|
||||
const {
|
||||
model: src,
|
||||
def: srcDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: 'Active users', value: 'active-users' },
|
||||
{ label: 'Notes', value: 'notes' },
|
||||
{ label: 'AP Requests: inboxReceived', value: 'ap-requests-inbox-received' },
|
||||
{ label: 'AP Requests: deliverSucceeded', value: 'ap-requests-deliver-succeeded' },
|
||||
{ label: 'AP Requests: deliverFailed', value: 'ap-requests-deliver-failed' },
|
||||
],
|
||||
initialValue: 'active-users',
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -30,19 +30,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #caption>{{ i18n.ts._role.descriptionOfDisplayOrder }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkSelect v-model="rolePermission" :readonly="readonly">
|
||||
<MkSelect v-model="rolePermission" :items="rolePermissionDef" :readonly="readonly">
|
||||
<template #label><i class="ti ti-shield-lock"></i> {{ i18n.ts._role.permission }}</template>
|
||||
<template #caption><div v-html="i18n.ts._role.descriptionOfPermission.replaceAll('\n', '<br>')"></div></template>
|
||||
<option value="normal">{{ i18n.ts.normalUser }}</option>
|
||||
<option value="moderator">{{ i18n.ts.moderator }}</option>
|
||||
<option value="administrator">{{ i18n.ts.administrator }}</option>
|
||||
</MkSelect>
|
||||
|
||||
<MkSelect v-model="role.target" :readonly="readonly">
|
||||
<MkSelect v-model="role.target" :items="[{ label: i18n.ts._role.manual, value: 'manual' }, { label: i18n.ts._role.conditional, value: 'conditional' }]" :readonly="readonly">
|
||||
<template #label><i class="ti ti-users"></i> {{ i18n.ts._role.assignTarget }}</template>
|
||||
<template #caption><div v-html="i18n.ts._role.descriptionOfAssignTarget.replaceAll('\n', '<br>')"></div></template>
|
||||
<option value="manual">{{ i18n.ts._role.manual }}</option>
|
||||
<option value="conditional">{{ i18n.ts._role.conditional }}</option>
|
||||
</MkSelect>
|
||||
|
||||
<MkFolder v-if="role.target === 'conditional'" defaultOpen>
|
||||
@@ -176,11 +171,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkSwitch v-model="role.policies.chatAvailability.useDefault" :readonly="readonly">
|
||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||
</MkSwitch>
|
||||
<MkSelect v-model="role.policies.chatAvailability.value" :disabled="role.policies.chatAvailability.useDefault" :readonly="readonly">
|
||||
<MkSelect
|
||||
v-model="role.policies.chatAvailability.value"
|
||||
:items="[
|
||||
{ label: i18n.ts.enabled, value: 'available' },
|
||||
{ label: i18n.ts.readonly, value: 'readonly' },
|
||||
{ label: i18n.ts.disabled, value: 'unavailable' },
|
||||
]"
|
||||
:disabled="role.policies.chatAvailability.useDefault"
|
||||
:readonly="readonly"
|
||||
>
|
||||
<template #label>{{ i18n.ts.enable }}</template>
|
||||
<option value="available">{{ i18n.ts.enabled }}</option>
|
||||
<option value="readonly">{{ i18n.ts.readonly }}</option>
|
||||
<option value="unavailable">{{ i18n.ts.disabled }}</option>
|
||||
</MkSelect>
|
||||
<MkRange v-model="role.policies.chatAvailability.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||
@@ -841,6 +842,7 @@ import FormSlot from '@/components/form/slot.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { deepClone } from '@/utility/clone.js';
|
||||
import type { MkSelectItem, GetMkSelectValueTypesFromDef } from '@/components/MkSelect.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', v: any): void;
|
||||
@@ -870,11 +872,17 @@ function updateAvatarDecorationLimit(value: string | number) {
|
||||
role.value.policies.avatarDecorationLimit.value = limited;
|
||||
}
|
||||
|
||||
const rolePermission = computed({
|
||||
const rolePermissionDef = [
|
||||
{ label: i18n.ts.normalUser, value: 'normal' },
|
||||
{ label: i18n.ts.moderator, value: 'moderator' },
|
||||
{ label: i18n.ts.administrator, value: 'administrator' },
|
||||
] as const satisfies MkSelectItem[];
|
||||
|
||||
const rolePermission = computed<GetMkSelectValueTypesFromDef<typeof rolePermissionDef>>({
|
||||
get: () => role.value.isAdministrator ? 'administrator' : role.value.isModerator ? 'moderator' : 'normal',
|
||||
set: (val) => {
|
||||
role.value.isAdministrator = val === 'administrator';
|
||||
role.value.isModerator = val === 'moderator';
|
||||
role.value.isAdministrator = (val === 'administrator');
|
||||
role.value.isModerator = (val === 'moderator');
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -115,15 +115,15 @@ async function assign() {
|
||||
const { canceled: canceled2, result: period } = await os.select({
|
||||
title: i18n.ts.period + ': ' + role.name,
|
||||
items: [{
|
||||
value: 'indefinitely', text: i18n.ts.indefinitely,
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'oneHour', text: i18n.ts.oneHour,
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', text: i18n.ts.oneDay,
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', text: i18n.ts.oneWeek,
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}, {
|
||||
value: 'oneMonth', text: i18n.ts.oneMonth,
|
||||
value: 'oneMonth', label: i18n.ts.oneMonth,
|
||||
}],
|
||||
default: 'indefinitely',
|
||||
});
|
||||
|
||||
@@ -52,11 +52,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.chatAvailability, 'chatAvailability'])">
|
||||
<template #label>{{ i18n.ts._role._options.chatAvailability }}</template>
|
||||
<template #suffix>{{ policies.chatAvailability === 'available' ? i18n.ts.yes : policies.chatAvailability === 'readonly' ? i18n.ts.readonly : i18n.ts.no }}</template>
|
||||
<MkSelect v-model="policies.chatAvailability">
|
||||
<MkSelect
|
||||
v-model="policies.chatAvailability"
|
||||
:items="[
|
||||
{ label: i18n.ts.enabled, value: 'available' },
|
||||
{ label: i18n.ts.readonly, value: 'readonly' },
|
||||
{ label: i18n.ts.disabled, value: 'unavailable' },
|
||||
]"
|
||||
>
|
||||
<template #label>{{ i18n.ts.enable }}</template>
|
||||
<option value="available">{{ i18n.ts.enabled }}</option>
|
||||
<option value="readonly">{{ i18n.ts.readonly }}</option>
|
||||
<option value="unavailable">{{ i18n.ts.disabled }}</option>
|
||||
</MkSelect>
|
||||
</MkFolder>
|
||||
|
||||
|
||||
@@ -11,26 +11,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton>
|
||||
</div>
|
||||
<div :class="$style.inputs">
|
||||
<MkSelect v-model="sort" style="flex: 1;">
|
||||
<MkSelect v-model="sort" :items="sortDef" style="flex: 1;">
|
||||
<template #label>{{ i18n.ts.sort }}</template>
|
||||
<option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
<option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option>
|
||||
<option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="state" style="flex: 1;">
|
||||
<MkSelect v-model="state" :items="stateDef" style="flex: 1;">
|
||||
<template #label>{{ i18n.ts.state }}</template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="available">{{ i18n.ts.normal }}</option>
|
||||
<option value="admin">{{ i18n.ts.administrator }}</option>
|
||||
<option value="moderator">{{ i18n.ts.moderator }}</option>
|
||||
<option value="suspended">{{ i18n.ts.suspend }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="origin" style="flex: 1;">
|
||||
<MkSelect v-model="origin" :items="originDef" style="flex: 1;">
|
||||
<template #label>{{ i18n.ts.instance }}</template>
|
||||
<option value="combined">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.local }}</option>
|
||||
<option value="remote">{{ i18n.ts.remote }}</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
<div :class="$style.inputs">
|
||||
@@ -67,23 +55,57 @@ import * as os from '@/os.js';
|
||||
import { lookupUser } from '@/utility/admin-lookup.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
import { dateString } from '@/filters/date.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
type SearchQuery = {
|
||||
sort?: string;
|
||||
state?: string;
|
||||
origin?: string;
|
||||
sort?: '-createdAt' | '+createdAt' | '-updatedAt' | '+updatedAt';
|
||||
state?: 'all' | 'available' | 'admin' | 'moderator' | 'suspended';
|
||||
origin?: 'combined' | 'local' | 'remote';
|
||||
username?: string;
|
||||
hostname?: string;
|
||||
};
|
||||
|
||||
const storedQuery = JSON.parse(defaultMemoryStorage.getItem('admin-users-query') ?? '{}') as SearchQuery;
|
||||
|
||||
const sort = ref(storedQuery.sort ?? '+createdAt');
|
||||
const state = ref(storedQuery.state ?? 'all');
|
||||
const origin = ref(storedQuery.origin ?? 'local');
|
||||
const {
|
||||
model: sort,
|
||||
def: sortDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: `${i18n.ts.registeredDate} (${i18n.ts.ascendingOrder})`, value: '-createdAt' },
|
||||
{ label: `${i18n.ts.registeredDate} (${i18n.ts.descendingOrder})`, value: '+createdAt' },
|
||||
{ label: `${i18n.ts.lastUsed} (${i18n.ts.ascendingOrder})`, value: '-updatedAt' },
|
||||
{ label: `${i18n.ts.lastUsed} (${i18n.ts.descendingOrder})`, value: '+updatedAt' },
|
||||
],
|
||||
initialValue: storedQuery.sort ?? '+createdAt',
|
||||
});
|
||||
const {
|
||||
model: state,
|
||||
def: stateDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'all' },
|
||||
{ label: i18n.ts.normal, value: 'available' },
|
||||
{ label: i18n.ts.administrator, value: 'admin' },
|
||||
{ label: i18n.ts.moderator, value: 'moderator' },
|
||||
{ label: i18n.ts.suspend, value: 'suspended' },
|
||||
],
|
||||
initialValue: storedQuery.state ?? 'all',
|
||||
});
|
||||
const {
|
||||
model: origin,
|
||||
def: originDef,
|
||||
} = useMkSelect({
|
||||
items: [
|
||||
{ label: i18n.ts.all, value: 'combined' },
|
||||
{ label: i18n.ts.local, value: 'local' },
|
||||
{ label: i18n.ts.remote, value: 'remote' },
|
||||
],
|
||||
initialValue: storedQuery.origin ?? 'local',
|
||||
});
|
||||
const searchUsername = ref(storedQuery.username ?? '');
|
||||
const searchHost = ref(storedQuery.hostname ?? '');
|
||||
const paginator = markRaw(new Paginator('admin/show-users', {
|
||||
|
||||
Reference in New Issue
Block a user