merge upstream

This commit is contained in:
Hazelnoot 2025-03-25 16:14:53 -04:00
commit d8908ef2d8
1065 changed files with 32953 additions and 20092 deletions

View file

@ -71,7 +71,7 @@ import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { deepClone } from '@/scripts/clone.js';
import { deepClone } from '@/utility/clone.js';
import { rolesCache } from '@/cache.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));

View file

@ -33,13 +33,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref, shallowRef, watch, nextTick } from 'vue';
import { computed, onMounted, onUnmounted, ref, useTemplateRef, watch, nextTick, inject } from 'vue';
import tinycolor from 'tinycolor2';
import { popupMenu } from '@/os.js';
import { scrollToTop } from '@@/js/scroll.js';
import { popupMenu } from '@/os.js';
import MkButton from '@/components/MkButton.vue';
import { globalEvents } from '@/events.js';
import { injectReactiveMetadata } from '@/scripts/page-metadata.js';
import { DI } from '@/di.js';
type Tab = {
key?: string | null;
@ -66,11 +66,11 @@ const emit = defineEmits<{
(ev: 'update:tab', key: string);
}>();
const pageMetadata = injectReactiveMetadata();
const pageMetadata = inject(DI.pageMetadata);
const el = shallowRef<HTMLElement>(null);
const el = useTemplateRef('el');
const tabHighlightEl = useTemplateRef('tabHighlightEl');
const tabRefs = {};
const tabHighlightEl = shallowRef<HTMLElement | null>(null);
const bg = ref<string | null>(null);
const height = ref(0);
const hasTabs = computed(() => {
@ -119,15 +119,15 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
}
const calcBg = () => {
const rawBg = pageMetadata.value?.bg ?? 'var(--MI_THEME-bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();
};
onMounted(() => {
calcBg();
globalEvents.on('themeChanged', calcBg);
globalEvents.on('themeChanging', calcBg);
watch(() => [props.tab, props.tabs], () => {
nextTick(() => {
@ -147,7 +147,7 @@ onMounted(() => {
});
onUnmounted(() => {
globalEvents.off('themeChanged', calcBg);
globalEvents.off('themeChanging', calcBg);
});
</script>

View file

@ -71,15 +71,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, shallowRef, toRefs } from 'vue';
import { computed, onMounted, ref, useTemplateRef, toRefs } from 'vue';
import { entities } from 'misskey-js';
import type { MkSystemWebhookResult } from '@/components/MkSystemWebhookEditor.impl.js';
import MkButton from '@/components/MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js';
import MkInput from '@/components/MkInput.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkSelect from '@/components/MkSelect.vue';
import { MkSystemWebhookResult, showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js';
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';
@ -99,7 +100,7 @@ const props = defineProps<{
const { mode, id } = toRefs(props);
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
const dialogEl = useTemplateRef('dialogEl');
const loading = ref<number>(0);

View file

@ -49,7 +49,7 @@ import { entities } from 'misskey-js';
import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
import XRecipient from './notification-recipient.item.vue';
import XHeader from '@/pages/admin/_header_.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';

View file

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton>
</div>
<MkInfo v-if="!defaultStore.reactiveState.abusesTutorial.value" closable @close="closeTutorial()">
<MkInfo v-if="!store.r.abusesTutorial.value" closable @close="closeTutorial()">
{{ i18n.ts._abuseUserReport.resolveTutorial }}
</MkInfo>
@ -59,18 +59,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, shallowRef, ref } from 'vue';
import { computed, useTemplateRef, ref } from 'vue';
import XHeader from './_header_.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
import XAbuseReport from '@/components/MkAbuseReport.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
const reports = shallowRef<InstanceType<typeof MkPagination>>();
const reports = useTemplateRef('reports');
const state = ref('unresolved');
const reporterOrigin = ref('combined');
@ -93,14 +93,14 @@ function resolved(reportId) {
}
function closeTutorial() {
defaultStore.set('abusesTutorial', false);
store.set('abusesTutorial', false);
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.abuseReports,
icon: 'ti ti-exclamation-circle',
}));

View file

@ -96,9 +96,9 @@ import MkFolder from '@/components/MkFolder.vue';
import MkSelect from '@/components/MkSelect.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
const ads = ref<Misskey.entities.Ad[]>([]);
@ -255,7 +255,7 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.ads,
icon: 'ti ti-ad',
}));

View file

@ -94,9 +94,9 @@ import MkSwitch from '@/components/MkSwitch.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkFolder from '@/components/MkFolder.vue';
import MkTextarea from '@/components/MkTextarea.vue';
@ -199,7 +199,7 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.announcements,
icon: 'ti ti-speakerphone',
}));

View file

@ -196,14 +196,14 @@ import MkRadios from '@/components/MkRadios.vue';
import MkInput from '@/components/MkInput.vue';
import FormSlot from '@/components/form/slot.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { useForm } from '@/scripts/use-form.js';
import { useForm } from '@/use/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkInfo from '@/components/MkInfo.vue';
import { ApiWithDialogCustomErrors } from '@/os.js';
import type { ApiWithDialogCustomErrors } from '@/os.js';
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));

View file

@ -128,10 +128,10 @@ import MkTextarea from '@/components/MkTextarea.vue';
import FromSlot from '@/components/form/slot.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { instance, fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkColorInput from '@/components/MkColorInput.vue';
import { host } from '@@/js/config.js';
@ -210,7 +210,7 @@ function chooseNewLike(ev: MouseEvent) {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.branding,
icon: 'ti ti-paint',
}));

View file

@ -71,25 +71,25 @@ export type EmojiSearchQuery = {
<script setup lang="ts">
import { computed, defineAsyncComponent, onMounted, ref, nextTick, useCssModule } from 'vue';
import * as Misskey from 'misskey-js';
import type { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
import type { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import type { GridSetting } from '@/components/grid/grid.js';
import * as os from '@/os.js';
import {
emptyStrToEmptyArray,
emptyStrToNull,
emptyStrToUndefined,
RequestLogItem,
roleIdsParser,
} from '@/pages/admin/custom-emojis-manager.impl.js';
import MkGrid from '@/components/grid/MkGrid.vue';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import { validators } from '@/components/grid/cell-validators.js';
import { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkPagingButtons from '@/components/MkPagingButtons.vue';
import { GridSetting } from '@/components/grid/grid.js';
import { selectFile } from '@/scripts/select-file.js';
import { selectFile } from '@/utility/select-file.js';
import { copyGridDataToClipboard, removeDataFromGrid } from '@/components/grid/grid-utils.js';
import { useLoading } from "@/components/hook/useLoading.js";
import { useLoading } from '@/components/hook/useLoading.js';
type GridItem = {
checked: boolean;
@ -108,7 +108,7 @@ type GridItem = {
publicUrl?: string | null;
originalUrl?: string | null;
type: string | null;
}
};
function setupGrid(): GridSetting {
const $style = useCssModule();
@ -464,8 +464,8 @@ async function refreshCustomEmojis() {
aliases: emptyStrToUndefined(searchQuery.value.aliases),
category: emptyStrToUndefined(searchQuery.value.category),
license: emptyStrToUndefined(searchQuery.value.license),
isSensitive: searchQuery.value.sensitive ? Boolean(searchQuery.value.sensitive).valueOf() : undefined,
localOnly: searchQuery.value.localOnly ? Boolean(searchQuery.value.localOnly).valueOf() : undefined,
isSensitive: searchQuery.value.sensitive != null ? Boolean(searchQuery.value.sensitive).valueOf() : undefined,
localOnly: searchQuery.value.localOnly != null ? Boolean(searchQuery.value.localOnly).valueOf() : undefined,
updatedAtFrom: emptyStrToUndefined(searchQuery.value.updatedAtFrom),
updatedAtTo: emptyStrToUndefined(searchQuery.value.updatedAtTo),
roleIds: searchQuery.value.roles.map(it => it.id),
@ -592,7 +592,7 @@ const headerActions = computed(() => [{
dispose();
},
});
}
},
}]);
</script>

View file

@ -78,30 +78,32 @@ SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as Misskey from 'misskey-js';
import { onMounted, ref, useCssModule } from 'vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
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';
import type { GridSetting } from '@/components/grid/grid.js';
import type { GridRow } from '@/components/grid/row.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import {
emptyStrToEmptyArray,
emptyStrToNull,
RequestLogItem,
roleIdsParser,
} from '@/pages/admin/custom-emojis-manager.impl.js';
import MkGrid from '@/components/grid/MkGrid.vue';
import { i18n } from '@/i18n.js';
import MkSelect from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { defaultStore } from '@/store.js';
import MkFolder from '@/components/MkFolder.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { validators } from '@/components/grid/cell-validators.js';
import { chooseFileFromDrive, chooseFileFromPc } from '@/scripts/select-file.js';
import { uploadFile } from '@/scripts/upload.js';
import { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import { DroppedFile, extractDroppedItems, flattenDroppedFiles } from '@/scripts/file-drop.js';
import { chooseFileFromDrive, chooseFileFromPc } from '@/utility/select-file.js';
import { uploadFile } from '@/utility/upload.js';
import { extractDroppedItems, flattenDroppedFiles } from '@/utility/file-drop.js';
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue';
import { GridSetting } from '@/components/grid/grid.js';
import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js';
import { GridRow } from '@/components/grid/row.js';
import { prefer } from '@/preferences.js';
const MAXIMUM_EMOJI_REGISTER_COUNT = 100;
@ -122,7 +124,7 @@ type GridItem = {
localOnly: boolean;
roleIdsThatCanBeUsedThisEmojiAsReaction: { id: string, name: string }[];
type: string | null;
}
};
function setupGrid(): GridSetting {
const $style = useCssModule();
@ -242,8 +244,8 @@ function setupGrid(): GridSetting {
const uploadFolders = ref<FolderItem[]>([]);
const gridItems = ref<GridItem[]>([]);
const selectedFolderId = ref(defaultStore.state.uploadFolder);
const keepOriginalUploading = ref(defaultStore.state.keepOriginalUploading);
const selectedFolderId = ref(prefer.s.uploadFolder);
const keepOriginalUploading = ref(prefer.s.keepOriginalUploading);
const directoryToCategory = ref<boolean>(false);
const registerButtonDisabled = ref<boolean>(false);
const requestLogs = ref<RequestLogItem[]>([]);

View file

@ -143,35 +143,32 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, onMounted, ref, useCssModule } from 'vue';
import * as Misskey from 'misskey-js';
import MkRemoteEmojiEditDialog from '@/components/MkRemoteEmojiEditDialog.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkGrid from '@/components/grid/MkGrid.vue';
import {
emptyStrToUndefined,
GridSortOrderKey,
gridSortOrderKeys,
RequestLogItem,
} from '@/pages/admin/custom-emojis-manager.impl.js';
import { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import { emptyStrToUndefined, gridSortOrderKeys } from '@/pages/admin/custom-emojis-manager.impl.js';
import MkFolder from '@/components/MkFolder.vue';
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue';
import * as os from '@/os.js';
import { GridSetting } from '@/components/grid/grid.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { deviceKind } from '@/utility/device-kind.js';
import MkPagingButtons from '@/components/MkPagingButtons.vue';
import MkSortOrderEditor from '@/components/MkSortOrderEditor.vue';
import { SortOrder } from '@/components/MkSortOrderEditor.define.js';
import { useLoading } from '@/components/hook/useLoading.js';
import type { GridSortOrderKey, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
import type { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import type { GridSetting } from '@/components/grid/grid.js';
import type { SortOrder } from '@/components/MkSortOrderEditor.define.js';
type GridItem = {
checked: boolean;
id: string;
url: string;
name: string;
host: string;
}
};
function setupGrid(): GridSetting {
const $style = useCssModule();

View file

@ -4,7 +4,7 @@
*/
import { delay, http, HttpResponse } from 'msw';
import { StoryObj } from '@storybook/vue3';
import type { StoryObj } from '@storybook/vue3';
import { entities } from 'misskey-js';
import { commonHandlers } from '../../../.storybook/mocks.js';
import { emoji } from '../../../.storybook/fakes.js';

View file

@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { computed, ref } from 'vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import XGridLocalComponent from '@/pages/admin/custom-emojis-manager.local.vue';
import XGridRemoteComponent from '@/pages/admin/custom-emojis-manager.remote.vue';
import MkPageHeader from '@/components/global/MkPageHeader.vue';
@ -36,7 +36,7 @@ const headerTabs = computed(() => [{
title: i18n.ts.remote,
}]);
definePageMetadata(computed(() => ({
definePage(computed(() => ({
title: i18n.ts.customEmojis,
icon: 'ti ti-icons',
needWideArea: true,

View file

@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
@ -14,18 +13,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue>
</FormSuspense>
</MkSpacer>
</MkStickyContainer>
</PageWithHeader>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import bytes from '@/filters/bytes.js';
import number from '@/filters/number.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
const databasePromiseFactory = () => misskeyApi('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
@ -33,7 +32,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.database,
icon: 'ti ti-database',
}));

View file

@ -73,10 +73,10 @@ import FormSuspense from '@/components/form/suspense.vue';
import FormSplit from '@/components/form/split.vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
const enableEmail = ref<boolean>(false);
@ -130,7 +130,7 @@ function save() {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.emailServer,
icon: 'ti ti-mail',
}));

View file

@ -9,6 +9,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
<FormSuspense :p="init">
<div class="_gaps_m">
<MkFolder>
<template #label>Google Analytics<span class="_beta">{{ i18n.ts.beta }}</span></template>
<div class="_gaps_m">
<MkInput v-model="googleAnalyticsMeasurementId">
<template #prefix><i class="ti ti-key"></i></template>
<template #label>Measurement ID</template>
</MkInput>
<MkButton primary @click="save_googleAnalytics">Save</MkButton>
</div>
</MkFolder>
<MkFolder>
<template #label>DeepL Translation</template>
@ -65,10 +77,10 @@ import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkFolder from '@/components/MkFolder.vue';
const deeplAuthKey = ref<string | null>('');
@ -78,14 +90,17 @@ const deeplFreeInstance = ref<string | null>('');
const libreTranslateURL = ref<string | null>('');
const libreTranslateKey = ref<string | null>('');
const googleAnalyticsMeasurementId = ref<string>('');
async function init() {
const meta = await misskeyApi('admin/meta');
deeplAuthKey.value = meta.deeplAuthKey;
deeplAuthKey.value = meta.deeplAuthKey ?? '';
deeplIsPro.value = meta.deeplIsPro;
deeplFreeMode.value = meta.deeplFreeMode;
deeplFreeInstance.value = meta.deeplFreeInstance;
libreTranslateURL.value = meta.libreTranslateURL;
libreTranslateKey.value = meta.libreTranslateKey;
googleAnalyticsMeasurementId.value = meta.googleAnalyticsMeasurementId ?? '';
}
function save_deepl() {
@ -108,11 +123,19 @@ function save_libre() {
});
}
function save_googleAnalytics() {
os.apiWithDialog('admin/update-meta', {
googleAnalyticsMeasurementId: googleAnalyticsMeasurementId.value,
}).then(() => {
fetchInstance(true);
});
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.externalServices,
icon: 'ph-arrow-square-out ph-bold ph-lg',
}));

View file

@ -69,7 +69,7 @@ import MkPagination from '@/components/MkPagination.vue';
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
import FormSplit from '@/components/form/split.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
const host = ref('');
const state = ref('federating');
@ -116,7 +116,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.federation,
icon: 'ti ti-whirl',
}));

View file

@ -42,9 +42,9 @@ import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import * as os from '@/os.js';
import { lookupFile } from '@/scripts/admin-lookup.js';
import { lookupFile } from '@/utility/admin-lookup.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
const origin = ref('local');
const type = ref<string | null>(null);
@ -85,7 +85,7 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.files,
icon: 'ti ti-cloud',
}));

View file

@ -27,24 +27,25 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSpacer>
</div>
<div v-if="!(narrow && currentPage?.route.name == null)" class="main">
<RouterView nested/>
<NestedRouterView/>
</div>
</div>
</template>
<script lang="ts" setup>
import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
import type { SuperMenuDef } from '@/components/MkSuperMenu.vue';
import type { PageMetadata } from '@/page.js';
import { i18n } from '@/i18n.js';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
import type { SuperMenuDef } from '@/components/MkSuperMenu.vue';
import MkInfo from '@/components/MkInfo.vue';
import { instance } from '@/instance.js';
import { lookup } from '@/scripts/lookup.js';
import { lookup } from '@/utility/lookup.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { lookupUser, lookupUserByEmail, lookupFile } from '@/scripts/admin-lookup.js';
import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import { useRouter } from '@/router/supplier.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { lookupUser, lookupUserByEmail, lookupFile } from '@/utility/admin-lookup.js';
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import { useRouter } from '@/router.js';
const isEmpty = (x: string | null) => x == null || x === '';
@ -339,7 +340,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => INFO.value);
definePage(() => INFO.value);
defineExpose({
header: {

View file

@ -55,21 +55,22 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, ref, shallowRef } from 'vue';
import { computed, ref, useTemplateRef } from 'vue';
import XHeader from './_header_.vue';
import type { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkInviteCode from '@/components/MkInviteCode.vue';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
const pagingComponent = useTemplateRef('pagingComponent');
const type = ref('all');
const sort = ref('+createdAt');
@ -113,7 +114,7 @@ function deleted(id: string) {
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.invite,
icon: 'ti ti-user-plus',
}));

View file

@ -162,10 +162,10 @@ import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import FormLink from '@/components/form/link.vue';
import MkFolder from '@/components/MkFolder.vue';
@ -319,7 +319,7 @@ function save_mediaSilencedHosts() {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.moderation,
icon: 'ti ti-shield',
}));

View file

@ -274,6 +274,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="log.type === 'removeRelay'">
<div>{{ i18n.ts.inboxUrl }}: {{ log.info.inbox }}</div>
</template>
<template v-else-if="log.type === 'updateProxyAccountDescription'">
<div :class="$style.diff">
<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
</div>
</template>
<details>
<summary>raw</summary>

View file

@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, shallowRef, ref } from 'vue';
import { computed, useTemplateRef, ref } from 'vue';
import * as Misskey from 'misskey-js';
import XHeader from './_header_.vue';
import XModLog from './modlog.ModLog.vue';
@ -38,10 +38,10 @@ import MkSelect from '@/components/MkSelect.vue';
import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
const logs = shallowRef<InstanceType<typeof MkPagination>>();
const logs = useTemplateRef('logs');
const type = ref<string | null>(null);
const moderatorId = ref('');
@ -59,7 +59,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.moderationLogs,
icon: 'ti ti-list-search',
}));

View file

@ -90,10 +90,10 @@ import MkInput from '@/components/MkInput.vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
const useObjectStorage = ref<boolean>(false);
@ -149,7 +149,7 @@ function save() {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.objectStorage,
icon: 'ti ti-cloud',
}));

View file

@ -13,18 +13,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, shallowRef, ref } from 'vue';
import { onMounted, useTemplateRef, ref } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { chartVLine } from '@/scripts/chart-vline.js';
import { initChart } from '@/scripts/init-chart.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { store } from '@/store.js';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
import { chartVLine } from '@/utility/chart-vline.js';
import { initChart } from '@/utility/init-chart.js';
initChart();
const chartEl = shallowRef<HTMLCanvasElement>(null);
const chartEl = useTemplateRef('chartEl');
const now = new Date();
let chartInstance: Chart = null;
const chartLimit = 7;
@ -54,7 +54,7 @@ async function renderChart() {
const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const colorRead = '#3498db';
const colorWrite = '#2ecc71';

View file

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { StoryObj } from '@storybook/vue3';
import type { StoryObj } from '@storybook/vue3';
import { http, HttpResponse } from 'msw';
import { action } from '@storybook/addon-actions';
import { commonHandlers } from '../../../.storybook/mocks.js';

View file

@ -20,22 +20,22 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, shallowRef, ref } from 'vue';
import { onMounted, useTemplateRef, ref } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
import isChromatic from 'chromatic';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { chartVLine } from '@/scripts/chart-vline.js';
import { defaultStore } from '@/store.js';
import { alpha } from '@/scripts/color.js';
import { initChart } from '@/scripts/init-chart.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
import { chartVLine } from '@/utility/chart-vline.js';
import { store } from '@/store.js';
import { alpha } from '@/utility/color.js';
import { initChart } from '@/utility/init-chart.js';
initChart();
const chartLimit = 50;
const chartEl = shallowRef<HTMLCanvasElement>();
const chartEl2 = shallowRef<HTMLCanvasElement>();
const chartEl = useTemplateRef('chartEl');
const chartEl2 = useTemplateRef('chartEl2');
const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
@ -68,7 +68,7 @@ onMounted(async () => {
const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const succColor = '#87e000';
const failColor = '#ff4400';

View file

@ -47,13 +47,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import XPie, { type InstanceForPie } from './overview.pie.vue';
import XPie from './overview.pie.vue';
import type { InstanceForPie } from './overview.pie.vue';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import { misskeyApiGet } from '@/utility/misskey-api.js';
import number from '@/filters/number.js';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
import { i18n } from '@/i18n.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
const topSubInstancesForPie = ref<InstanceForPie[] | null>(null);
const topPubInstancesForPie = ref<InstanceForPie[] | null>(null);

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" mode="out-in">
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" mode="out-in">
<MkLoading v-if="fetching"/>
<div v-else :class="$style.instances">
<MkA v-for="(instance, i) in instances" :key="instance.id" v-tooltip.mfm.noDelay="`${instance.name}\n${instance.host}\n${instance.softwareName} ${instance.softwareVersion}`" :to="`/instance-info/${instance.host}`" :class="$style.instance">
@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import * as Misskey from 'misskey-js';
import { useInterval } from '@@/js/use-interval.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
const instances = ref<Misskey.entities.FederationInstance[]>([]);
const fetching = ref(true);

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" mode="out-in">
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" mode="out-in">
<MkLoading v-if="fetching"/>
<div v-else :class="$style.root" class="_panel">
<MkA v-for="user in moderators" :key="user.id" class="user" :to="`/admin/user/${user.id}`">
@ -18,9 +18,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import * as Misskey from 'misskey-js';
import { defaultStore } from '@/store.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { prefer } from '@/preferences.js';
const moderators = ref<Misskey.entities.UserDetailed[] | null>(null);
const fetching = ref(true);

View file

@ -8,10 +8,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue';
import { onMounted, useTemplateRef } from 'vue';
import { Chart } from 'chart.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { initChart } from '@/scripts/init-chart.js';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
import { initChart } from '@/utility/init-chart.js';
export type InstanceForPie = {
name: string,
@ -26,7 +26,7 @@ const props = defineProps<{
data: InstanceForPie[];
}>();
const chartEl = shallowRef<HTMLCanvasElement>(null);
const chartEl = useTemplateRef('chartEl');
const { handler: externalTooltipHandler } = useChartTooltip({
position: 'middle',
@ -41,7 +41,7 @@ onMounted(() => {
labels: props.data.map(x => x.name),
datasets: [{
backgroundColor: props.data.map(x => x.color),
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'),
borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'),
borderWidth: 2,
hoverOffset: 0,
data: props.data.map(x => x.value),

View file

@ -8,13 +8,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue';
import { onMounted, useTemplateRef } from 'vue';
import { Chart } from 'chart.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { chartVLine } from '@/scripts/chart-vline.js';
import { alpha } from '@/scripts/color.js';
import { initChart } from '@/scripts/init-chart.js';
import { store } from '@/store.js';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
import { chartVLine } from '@/utility/chart-vline.js';
import { alpha } from '@/utility/color.js';
import { initChart } from '@/utility/init-chart.js';
initChart();
@ -22,7 +22,7 @@ const props = defineProps<{
type: string;
}>();
const chartEl = shallowRef<HTMLCanvasElement>(null);
const chartEl = useTemplateRef('chartEl');
const { handler: externalTooltipHandler } = useChartTooltip();
@ -67,7 +67,7 @@ const color =
'?' as never;
onMounted(() => {
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
chartInstance = new Chart(chartEl.value, {
type: 'line',

View file

@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
import { markRaw, onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import XChart from './overview.queue.chart.vue';
import type { ApQueueDomain } from '@/pages/admin/queue.vue';
@ -48,10 +48,10 @@ const activeSincePrevTick = ref(0);
const active = ref(0);
const delayed = ref(0);
const waiting = ref(0);
const chartProcess = shallowRef<InstanceType<typeof XChart>>();
const chartActive = shallowRef<InstanceType<typeof XChart>>();
const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
const chartWaiting = shallowRef<InstanceType<typeof XChart>>();
const chartProcess = useTemplateRef('chartProcess');
const chartActive = useTemplateRef('chartActive');
const chartDelayed = useTemplateRef('chartDelayed');
const chartWaiting = useTemplateRef('chartWaiting');
const props = defineProps<{
domain: ApQueueDomain;

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" mode="out-in">
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" mode="out-in">
<MkLoading v-if="fetching"/>
<div v-else :class="$style.root">
<div class="item _panel users">
@ -63,12 +63,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
import MkNumber from '@/components/MkNumber.vue';
import { i18n } from '@/i18n.js';
import { customEmojis } from '@/custom-emojis.js';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
const stats = ref<Misskey.entities.StatsResponse | null>(null);
const usersComparedToThePrevDay = ref<number>();

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" mode="out-in">
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" mode="out-in">
<MkLoading v-if="fetching"/>
<div v-else class="users">
<MkA v-for="(user, i) in newUsers" :key="user.id" :to="`/admin/user/${user.id}`" class="user">
@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import * as Misskey from 'misskey-js';
import { useInterval } from '@@/js/use-interval.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
const fetching = ref(true);

View file

@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed } from 'vue';
import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import XFederation from './overview.federation.vue';
import XInstances from './overview.instances.vue';
@ -79,13 +79,13 @@ import XModerators from './overview.moderators.vue';
import XHeatmap from './overview.heatmap.vue';
import type { InstanceForPie } from './overview.pie.vue';
import * as os from '@/os.js';
import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
const rootEl = shallowRef<HTMLElement>();
const rootEl = useTemplateRef('rootEl');
const serverInfo = ref<Misskey.entities.ServerInfoResponse | null>(null);
const topSubInstancesForPie = ref<InstanceForPie[] | null>(null);
const topPubInstancesForPie = ref<InstanceForPie[] | null>(null);
@ -184,7 +184,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.dashboard,
icon: 'ti ti-dashboard',
}));

View file

@ -111,15 +111,15 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkSwitch from '@/components/MkSwitch.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkInput from '@/components/MkInput.vue';
import MkLink from '@/components/MkLink.vue';
import { useForm } from '@/scripts/use-form.js';
import { useForm } from '@/use/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
const meta = await misskeyApi('admin/meta');
@ -202,7 +202,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.other,
icon: 'ti ti-adjustments',
}));

View file

@ -8,13 +8,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue';
import { onMounted, useTemplateRef } from 'vue';
import { Chart } from 'chart.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { chartVLine } from '@/scripts/chart-vline.js';
import { alpha } from '@/scripts/color.js';
import { initChart } from '@/scripts/init-chart.js';
import { store } from '@/store.js';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
import { chartVLine } from '@/utility/chart-vline.js';
import { alpha } from '@/utility/color.js';
import { initChart } from '@/utility/init-chart.js';
initChart();
@ -22,7 +22,7 @@ const props = defineProps<{
type: string;
}>();
const chartEl = shallowRef<HTMLCanvasElement>(null);
const chartEl = useTemplateRef('chartEl');
const { handler: externalTooltipHandler } = useChartTooltip();
@ -67,7 +67,7 @@ const color =
'?' as never;
onMounted(() => {
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
chartInstance = new Chart(chartEl.value, {
type: 'line',

View file

@ -48,12 +48,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
import { markRaw, onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import XChart from './queue.chart.chart.vue';
import type { ApQueueDomain } from '@/pages/admin/queue.vue';
import number from '@/filters/number.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
@ -65,10 +65,10 @@ const active = ref(0);
const delayed = ref(0);
const waiting = ref(0);
const jobs = ref<Misskey.Endpoints[`admin/queue/${ApQueueDomain}-delayed`]['res']>([]);
const chartProcess = shallowRef<InstanceType<typeof XChart>>();
const chartActive = shallowRef<InstanceType<typeof XChart>>();
const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
const chartWaiting = shallowRef<InstanceType<typeof XChart>>();
const chartProcess = useTemplateRef('chartProcess');
const chartActive = useTemplateRef('chartActive');
const chartDelayed = useTemplateRef('chartDelayed');
const chartWaiting = useTemplateRef('chartWaiting');
const props = defineProps<{
domain: ApQueueDomain;

View file

@ -16,13 +16,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { ref, computed, type Ref } from 'vue';
import { ref, computed } from 'vue';
import * as config from '@@/js/config.js';
import XQueue from './queue.chart.vue';
import XHeader from './_header_.vue';
import type { Ref } from 'vue';
import * as os from '@/os.js';
import * as config from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
export type ApQueueDomain = 'deliver' | 'inbox';
@ -53,14 +54,7 @@ function promoteAllQueues() {
});
}
const headerActions = computed(() => [{
asFullButton: true,
icon: 'ti ti-external-link',
text: i18n.ts.dashboard,
handler: () => {
window.open(config.url + '/queue', '_blank', 'noopener');
},
}]);
const headerActions = computed(() => []);
const headerTabs = computed(() => [{
key: 'deliver',
@ -70,7 +64,7 @@ const headerTabs = computed(() => [{
title: 'Inbox',
}]);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.jobQueue,
icon: 'ti ti-clock-play',
}));

View file

@ -29,9 +29,9 @@ import * as Misskey from 'misskey-js';
import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
const relays = ref<Misskey.entities.AdminRelaysListResponse>([]);
@ -84,7 +84,7 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.relays,
icon: 'ti ti-planet',
}));

View file

@ -28,12 +28,12 @@ import { v4 as uuid } from 'uuid';
import XHeader from './_header_.vue';
import XEditor from './roles.editor.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import { rolesCache } from '@/cache.js';
import { useRouter } from '@/router/supplier.js';
import { useRouter } from '@/router.js';
const router = useRouter();
@ -87,7 +87,7 @@ async function save() {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: role.value ? `${i18n.ts._role.edit}: ${role.value.name}` : i18n.ts._role.new,
icon: 'ti ti-badge',
}));

View file

@ -219,6 +219,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canChat, 'canChat'])">
<template #label>{{ i18n.ts._role._options.canChat }}</template>
<template #suffix>
<span v-if="role.policies.canChat.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.canChat.value ? i18n.ts.yes : i18n.ts.no }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canChat)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.canChat.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkSwitch v-model="role.policies.canChat.value" :disabled="role.policies.canChat.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
<MkRange v-model="role.policies.canChat.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>
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
<template #suffix>
@ -769,7 +789,7 @@ import MkRange from '@/components/MkRange.vue';
import FormSlot from '@/components/form/slot.vue';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { deepClone } from '@/scripts/clone.js';
import { deepClone } from '@/utility/clone.js';
const emit = defineEmits<{
(ev: 'update:modelValue', v: any): void;

View file

@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPagination :pagination="usersPagination" :displayLimit="50">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" class="_ghost"/>
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@ -67,15 +67,15 @@ import XHeader from './_header_.vue';
import XEditor from './roles.editor.vue';
import MkFolder from '@/components/MkFolder.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkPagination from '@/components/MkPagination.vue';
import { infoImageUrl } from '@/instance.js';
import { useRouter } from '@/router/supplier.js';
import { useRouter } from '@/router.js';
const router = useRouter();
@ -170,7 +170,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: `${i18n.ts.role}: ${role.name}`,
icon: 'ti ti-badge',
}));

View file

@ -77,6 +77,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canChat, 'canChat'])">
<template #label>{{ i18n.ts._role._options.canChat }}</template>
<template #suffix>{{ policies.canChat ? i18n.ts.yes : i18n.ts.no }}</template>
<MkSwitch v-model="policies.canChat">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
<template #suffix>{{ policies.mentionLimit }}</template>
@ -317,12 +325,12 @@ import MkRange from '@/components/MkRange.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkRolePreview from '@/components/MkRolePreview.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import { instance, fetchInstance } from '@/instance.js';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import { useRouter } from '@/router/supplier.js';
import { useRouter } from '@/router.js';
const router = useRouter();
const baseRoleQ = ref('');
@ -365,7 +373,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.roles,
icon: 'ti ti-badges',
}));

View file

@ -103,11 +103,11 @@ import MkRange from '@/components/MkRange.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { useForm } from '@/scripts/use-form.js';
import { definePage } from '@/page.js';
import { useForm } from '@/use/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
const meta = await misskeyApi('admin/meta');
@ -162,7 +162,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.security,
icon: 'ti ti-lock',
}));

View file

@ -46,7 +46,7 @@ import XHeader from './_header_.vue';
import * as os from '@/os.js';
import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
@ -67,7 +67,7 @@ const remove = (index: number): void => {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.serverRules,
icon: 'ti ti-checkbox',
}));
@ -122,7 +122,7 @@ definePageMetadata(() => ({
border-radius: var(--MI-radius-sm);
&:hover {
background: var(--MI_THEME-X5);
background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
}
}

View file

@ -270,15 +270,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder>
<template #icon><i class="ti ti-ghost"></i></template>
<template #label>{{ i18n.ts.proxyAccount }}</template>
<template v-if="proxyAccountForm.modified.value" #footer>
<MkFormFooter :form="proxyAccountForm"/>
</template>
<div class="_gaps">
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
<MkKeyValue>
<template #key>{{ i18n.ts.proxyAccount }}</template>
<template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template>
</MkKeyValue>
<MkButton primary @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</MkButton>
<MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
<template #label>{{ i18n.ts._profile.description }}</template>
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
</MkTextarea>
</div>
</MkFolder>
</div>
@ -288,7 +290,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { ref, computed, reactive } from 'vue';
import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
@ -296,20 +298,20 @@ import MkTextarea from '@/components/MkTextarea.vue';
import MkInfo from '@/components/MkInfo.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import { useForm } from '@/scripts/use-form.js';
import { useForm } from '@/use/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
import MkRadios from '@/components/MkRadios.vue';
const meta = await misskeyApi('admin/meta');
const proxyAccount = ref(meta.proxyAccountId ? await misskeyApi('users/show', { userId: meta.proxyAccountId }) : null);
const proxyAccount = await misskeyApi('users/show', { userId: meta.proxyAccountId });
const infoForm = useForm({
name: meta.name ?? '',
@ -425,16 +427,14 @@ const federationForm = useForm({
fetchInstance(true);
});
function chooseProxyAccount() {
os.selectUser({ localOnly: true }).then(user => {
proxyAccount.value = user;
os.apiWithDialog('admin/update-meta', {
proxyAccountId: user.id,
}).then(() => {
fetchInstance(true);
});
const proxyAccountForm = useForm({
description: proxyAccount.description,
}, async (state) => {
await os.apiWithDialog('admin/update-proxy-account', {
description: state.description,
});
}
fetchInstance(true);
});
async function genKeys() {
if (serviceWorkerForm.savedState.swPrivateKey) {
@ -450,7 +450,7 @@ async function genKeys() {
const headerTabs = computed(() => []);
definePageMetadata(() => ({
definePage(() => ({
title: i18n.ts.general,
icon: 'ti ti-settings',
}));

View file

@ -30,11 +30,11 @@ import { computed, onMounted, ref } from 'vue';
import { entities } from 'misskey-js';
import XItem from './system-webhook.item.vue';
import FormSection from '@/components/form/section.vue';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import XHeader from '@/pages/admin/_header_.vue';
import MkButton from '@/components/MkButton.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js';
import * as os from '@/os.js';
@ -82,7 +82,7 @@ onMounted(async () => {
await fetchWebhooks();
});
definePageMetadata(() => ({
definePage(() => ({
title: 'SystemWebhook',
icon: 'ti ti-webhook',
}));

View file

@ -9,6 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="900">
<div class="_gaps">
<div :class="$style.inputs">
<MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton>
</div>
<div :class="$style.inputs">
<MkSelect v-model="sort" style="flex: 1;">
<template #label>{{ i18n.ts.sort }}</template>
@ -58,25 +61,36 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, shallowRef, ref } from 'vue';
import { computed, useTemplateRef, ref, watchEffect } from 'vue';
import XHeader from './_header_.vue';
import { defaultMemoryStorage } from '@/memory-storage';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
import * as os from '@/os.js';
import { lookupUser } from '@/scripts/admin-lookup.js';
import { lookupUser } from '@/utility/admin-lookup.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { definePage } from '@/page.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { dateString } from '@/filters/date.js';
const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
type SearchQuery = {
sort?: string;
state?: string;
origin?: string;
username?: string;
hostname?: string;
};
const sort = ref('+createdAt');
const state = ref('all');
const origin = ref('local');
const searchUsername = ref('');
const searchHost = ref('');
const paginationComponent = useTemplateRef('paginationComponent');
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 searchUsername = ref(storedQuery.username ?? '');
const searchHost = ref(storedQuery.hostname ?? '');
const pagination = {
endpoint: 'admin/show-users' as const,
limit: 10,
@ -120,6 +134,14 @@ function show(user) {
os.pageWindow(`/admin/user/${user.id}`);
}
function resetQuery() {
sort.value = '+createdAt';
state.value = 'all';
origin.value = 'local';
searchUsername.value = '';
searchHost.value = '';
}
const headerActions = computed(() => [{
icon: 'ti ti-search',
text: i18n.ts.search,
@ -138,7 +160,17 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(() => ({
watchEffect(() => {
defaultMemoryStorage.setItem('admin-users-query', JSON.stringify({
sort: sort.value,
state: state.value,
origin: origin.value,
username: searchUsername.value,
hostname: searchHost.value,
}));
});
definePage(() => ({
title: i18n.ts.users,
icon: 'ti ti-users',
}));