Merge remote-tracking branch 'misskey/master' into feature/2024.9.0

This commit is contained in:
dakkar 2024-10-09 15:17:22 +01:00
commit f00576bce6
564 changed files with 19993 additions and 8169 deletions

View file

@ -103,6 +103,7 @@ const decorationsForPreview = computed(() => {
offsetX: offsetX.value,
offsetY: offsetY.value,
showBelow: showBelow.value,
blink: true,
};
const decorations = [...$i.avatarDecorations];
if (props.usingIndex != null) {

View file

@ -124,10 +124,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<option :value="4">{{ i18n.ts.large }}+</option>
</MkRadios>
<MkSwitch v-model="emojiPickerUseDrawerForMobile">
{{ i18n.ts.useDrawerReactionPickerForMobile }}
<MkSelect v-model="emojiPickerStyle">
<template #label>{{ i18n.ts.style }}</template>
<template #caption>{{ i18n.ts.needReloadToApply }}</template>
</MkSwitch>
<option value="auto">{{ i18n.ts.auto }}</option>
<option value="popup">{{ i18n.ts.popup }}</option>
<option value="drawer">{{ i18n.ts.drawer }}</option>
</MkSelect>
</div>
</FormSection>
</div>
@ -140,7 +143,7 @@ import MkRadios from '@/components/MkRadios.vue';
import MkButton from '@/components/MkButton.vue';
import FormSection from '@/components/form/section.vue';
import FromSlot from '@/components/form/slot.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
@ -159,7 +162,7 @@ const pinnedEmojis: Ref<string[]> = ref(deepClone(defaultStore.state.pinnedEmoji
const emojiPickerScale = computed(defaultStore.makeGetterSetter('emojiPickerScale'));
const emojiPickerWidth = computed(defaultStore.makeGetterSetter('emojiPickerWidth'));
const emojiPickerHeight = computed(defaultStore.makeGetterSetter('emojiPickerHeight'));
const emojiPickerUseDrawerForMobile = computed(defaultStore.makeGetterSetter('emojiPickerUseDrawerForMobile'));
const emojiPickerStyle = computed(defaultStore.makeGetterSetter('emojiPickerStyle'));
const removeReaction = (reaction: string, ev: MouseEvent) => remove(pinnedEmojisForReaction, reaction, ev);
const chooseReaction = (ev: MouseEvent) => pickEmoji(pinnedEmojisForReaction, ev);

View file

@ -24,13 +24,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkSelect>
<MkRadios v-model="hemisphere">
<template #label>{{ i18n.ts.hemisphere }}</template>
<option value="N">{{ i18n.ts._hemisphere.N }}</option>
<option value="S">{{ i18n.ts._hemisphere.S }}</option>
<template #caption>{{ i18n.ts._hemisphere.caption }}</template>
</MkRadios>
<MkRadios v-model="overridedDeviceKind">
<template #label>{{ i18n.ts.overridedDeviceKind }}</template>
<option :value="null">{{ i18n.ts.auto }}</option>
@ -194,12 +187,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="squareAvatars">{{ i18n.ts.squareAvatars }}</MkSwitch>
<MkSwitch v-model="showAvatarDecorations">{{ i18n.ts.showAvatarDecorations }}</MkSwitch>
<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
<MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch>
<MkSwitch v-model="oneko">{{ i18n.ts.oneko }}</MkSwitch>
<MkSwitch v-model="enableSeasonalScreenEffect">{{ i18n.ts.seasonalScreenEffect }}</MkSwitch>
<MkSwitch v-model="useNativeUIForVideoAudioPlayer">{{ i18n.ts.useNativeUIForVideoAudioPlayer }}</MkSwitch>
</div>
<MkSelect v-model="menuStyle">
<template #label>{{ i18n.ts.menuStyle }}</template>
<option value="auto">{{ i18n.ts.auto }}</option>
<option value="popup">{{ i18n.ts.popup }}</option>
<option value="drawer">{{ i18n.ts.drawer }}</option>
</MkSelect>
<div>
<MkRadios v-model="emojiStyle">
<template #label>{{ i18n.ts.emojiStyle }}</template>
@ -314,6 +314,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.other }}</template>
<div class="_gaps">
<MkRadios v-model="hemisphere">
<template #label>{{ i18n.ts.hemisphere }}</template>
<option value="N">{{ i18n.ts._hemisphere.N }}</option>
<option value="S">{{ i18n.ts._hemisphere.S }}</option>
<template #caption>{{ i18n.ts._hemisphere.caption }}</template>
</MkRadios>
<MkFolder>
<template #label>{{ i18n.ts.additionalEmojiDictionary }}</template>
<div class="_buttons">
@ -333,6 +339,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
import { langs } from '@@/js/config.js';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkRadios from '@/components/MkRadios.vue';
@ -344,12 +351,11 @@ import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import MkLink from '@/components/MkLink.vue';
import MkInfo from '@/components/MkInfo.vue';
import { langs } from '@/config.js';
import { searchEngineMap } from '@/scripts/search-engine-map.js';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { reloadAsk } from '@/scripts/reload-ask.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js';
@ -364,16 +370,6 @@ const cornerRadius = ref(miLocalStorage.getItem('cornerRadius'));
const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
const dataSaver = ref(defaultStore.state.dataSaver);
async function reloadAsk() {
const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.reloadToApplySetting,
});
if (canceled) return;
unisonReload();
}
const hemisphere = computed(defaultStore.makeGetterSetter('hemisphere'));
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
@ -403,7 +399,7 @@ const advancedMfm = computed(defaultStore.makeGetterSetter('advancedMfm'));
const showReactionsCount = computed(defaultStore.makeGetterSetter('showReactionsCount'));
const enableQuickAddMfmFunction = computed(defaultStore.makeGetterSetter('enableQuickAddMfmFunction'));
const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle'));
const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
const menuStyle = computed(defaultStore.makeGetterSetter('menuStyle'));
const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds'));
const oneko = computed(defaultStore.makeGetterSetter('oneko'));
@ -506,7 +502,7 @@ watch([
confirmWhenRevealingSensitiveMedia,
contextMenu,
], async () => {
await reloadAsk();
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const;

View file

@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</div>
</MkFolder>
<MkFolder v-if="$i && !$i.movedTo">
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportFollowing">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkSwitch v-model="withReplies">
@ -78,7 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
<MkFolder v-if="$i && !$i.movedTo">
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -93,7 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
<MkFolder v-if="$i && !$i.movedTo">
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -108,7 +108,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
<MkFolder v-if="$i && !$i.movedTo">
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -123,7 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
<MkFolder v-if="$i && !$i.movedTo">
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>

View file

@ -54,7 +54,7 @@ import MkContainer from '@/components/MkContainer.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { defaultStore } from '@/store.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { reloadAsk } from '@/scripts/reload-ask.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
@ -67,16 +67,6 @@ const items = ref(defaultStore.state.menu.map(x => ({
const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
async function reloadAsk() {
const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.reloadToApplySetting,
});
if (canceled) return;
unisonReload();
}
async function addItem() {
const menu = Object.keys(navbarItemDef).filter(k => !defaultStore.state.menu.includes(k));
const { canceled, result: item } = await os.select({
@ -100,7 +90,7 @@ function removeItem(index: number) {
async function save() {
defaultStore.set('menu', items.value.map(x => x.type));
await reloadAsk();
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
}
function reset() {
@ -111,7 +101,7 @@ function reset() {
}
watch(menuDisplay, async () => {
await reloadAsk();
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
const headerActions = computed(() => []);

View file

@ -69,12 +69,12 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { notificationTypes } from '@/const.js';
import { notificationTypes } from '@@/js/const.js';
const $i = signinRequired();
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted'];
const notificationTypesWithoutSender = ['achievementEarned'];
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] as const satisfies (typeof notificationTypes[number])[];
const notificationTypesWithoutSender = ['achievementEarned'] as const satisfies (typeof notificationTypes[number])[];
const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);

View file

@ -62,8 +62,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.experimentalFeatures }}</template>
<div class="_gaps_m">
<MkSwitch v-model="enableCondensedLineForAcct">
<template #label>Enable condensed line for acct</template>
<MkSwitch v-model="enableCondensedLine">
<template #label>Enable condensed line</template>
</MkSwitch>
</div>
</MkFolder>
@ -109,13 +109,13 @@ import { defaultStore } from '@/store.js';
import { signout, signinRequired } from '@/account.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { reloadAsk } from '@/scripts/reload-ask.js';
import FormSection from '@/components/form/section.vue';
const $i = signinRequired();
const reportError = computed(defaultStore.makeGetterSetter('reportError'));
const enableCondensedLineForAcct = computed(defaultStore.makeGetterSetter('enableCondensedLineForAcct'));
const enableCondensedLine = computed(defaultStore.makeGetterSetter('enableCondensedLine'));
const devMode = computed(defaultStore.makeGetterSetter('devMode'));
const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies'));
@ -143,16 +143,6 @@ async function deleteAccount() {
await signout();
}
async function reloadAsk() {
const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.reloadToApplySetting,
});
if (canceled) return;
unisonReload();
}
async function updateRepliesAll(withReplies: boolean) {
const { canceled } = await os.confirm({
type: 'warning',
@ -178,9 +168,9 @@ const exportData = () => {
};
watch([
enableCondensedLineForAcct,
enableCondensedLineForAcct,
], async () => {
await reloadAsk();
await reloadAsk();
});
const headerActions = computed(() => []);

View file

@ -6,12 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<div :class="$style.buttons">
<MkButton inline primary @click="saveNew">{{ ts._preferencesBackups.saveNew }}</MkButton>
<MkButton inline @click="loadFile">{{ ts._preferencesBackups.loadFile }}</MkButton>
<MkButton inline primary @click="saveNew">{{ i18n.ts._preferencesBackups.saveNew }}</MkButton>
<MkButton inline @click="loadFile">{{ i18n.ts._preferencesBackups.loadFile }}</MkButton>
</div>
<FormSection>
<template #label>{{ ts._preferencesBackups.list }}</template>
<template #label>{{ i18n.ts._preferencesBackups.list }}</template>
<template v-if="profiles && Object.keys(profiles).length > 0">
<div class="_gaps_s">
<div
@ -23,13 +23,13 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.prevent.stop="$event => menu($event, id)"
>
<div :class="$style.profileName">{{ profile.name }}</div>
<div :class="$style.profileTime">{{ t('_preferencesBackups.createdAt', { date: (new Date(profile.createdAt)).toLocaleDateString(), time: (new Date(profile.createdAt)).toLocaleTimeString() }) }}</div>
<div v-if="profile.updatedAt" :class="$style.profileTime">{{ t('_preferencesBackups.updatedAt', { date: (new Date(profile.updatedAt)).toLocaleDateString(), time: (new Date(profile.updatedAt)).toLocaleTimeString() }) }}</div>
<div :class="$style.profileTime">{{ i18n.tsx._preferencesBackups.createdAt({ date: (new Date(profile.createdAt)).toLocaleDateString(), time: (new Date(profile.createdAt)).toLocaleTimeString() }) }}</div>
<div v-if="profile.updatedAt" :class="$style.profileTime">{{ i18n.tsx._preferencesBackups.updatedAt({ date: (new Date(profile.updatedAt)).toLocaleDateString(), time: (new Date(profile.updatedAt)).toLocaleTimeString() }) }}</div>
</div>
</div>
</template>
<div v-else-if="profiles">
<MkInfo>{{ ts._preferencesBackups.noBackups }}</MkInfo>
<MkInfo>{{ i18n.ts._preferencesBackups.noBackups }}</MkInfo>
</div>
<MkLoading v-else/>
</FormSection>
@ -39,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { v4 as uuid } from 'uuid';
import { version, host } from '@@/js/config.js';
import FormSection from '@/components/form/section.vue';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
@ -49,10 +50,8 @@ import { unisonReload } from '@/scripts/unison-reload.js';
import { useStream } from '@/stream.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { version, host } from '@/config.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js';
const { t, ts } = i18n;
const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'collapseRenotes',
@ -80,7 +79,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'disableCatSpeak',
'disableShowingAnimatedImages',
'emojiStyle',
'disableDrawer',
'menuStyle',
'useBlurEffectForModal',
'useBlurEffect',
'showFixedPostForm',
@ -92,7 +91,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'emojiPickerScale',
'emojiPickerWidth',
'emojiPickerHeight',
'emojiPickerUseDrawerForMobile',
'emojiPickerStyle',
'defaultSideView',
'menuDisplay',
'reportError',
@ -110,7 +109,6 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'mediaListWithOneImageAppearance',
'notificationPosition',
'notificationStackAxis',
'enableCondensedLineForAcct',
'keepScreenOn',
'defaultWithReplies',
'disableStreamingTimeline',
@ -211,15 +209,15 @@ async function saveNew(): Promise<void> {
if (!profiles.value) return;
const { canceled, result: name } = await os.inputText({
title: ts._preferencesBackups.inputName,
title: i18n.ts._preferencesBackups.inputName,
default: '',
});
if (canceled) return;
if (Object.values(profiles.value).some(x => x.name === name)) {
return os.alert({
title: ts._preferencesBackups.cannotSave,
text: t('_preferencesBackups.nameAlreadyExists', { name }),
title: i18n.ts._preferencesBackups.cannotSave,
text: i18n.tsx._preferencesBackups.nameAlreadyExists({ name }),
});
}
@ -248,8 +246,8 @@ function loadFile(): void {
if (file.type !== 'application/json') {
return os.alert({
type: 'error',
title: ts._preferencesBackups.cannotLoad,
text: ts._preferencesBackups.invalidFile,
title: i18n.ts._preferencesBackups.cannotLoad,
text: i18n.ts._preferencesBackups.invalidFile,
});
}
@ -260,7 +258,7 @@ function loadFile(): void {
} catch (err) {
return os.alert({
type: 'error',
title: ts._preferencesBackups.cannotLoad,
title: i18n.ts._preferencesBackups.cannotLoad,
text: (err as any)?.message ?? '',
});
}
@ -286,8 +284,8 @@ async function applyProfile(id: string): Promise<void> {
const { canceled: cancel1 } = await os.confirm({
type: 'warning',
title: ts._preferencesBackups.apply,
text: t('_preferencesBackups.applyConfirm', { name: profile.name }),
title: i18n.ts._preferencesBackups.apply,
text: i18n.tsx._preferencesBackups.applyConfirm({ name: profile.name }),
});
if (cancel1) return;
@ -346,7 +344,7 @@ async function applyProfile(id: string): Promise<void> {
const { canceled: cancel2 } = await os.confirm({
type: 'info',
text: ts.reloadToApplySetting,
text: i18n.ts.reloadToApplySetting,
});
if (cancel2) return;
@ -358,8 +356,8 @@ async function deleteProfile(id: string): Promise<void> {
const { canceled } = await os.confirm({
type: 'info',
title: ts.delete,
text: t('deleteAreYouSure', { x: profiles.value[id].name }),
title: i18n.ts.delete,
text: i18n.tsx.deleteAreYouSure({ x: profiles.value[id].name }),
});
if (canceled) return;
@ -374,8 +372,8 @@ async function save(id: string): Promise<void> {
const { canceled } = await os.confirm({
type: 'info',
title: ts._preferencesBackups.save,
text: t('_preferencesBackups.saveConfirm', { name }),
title: i18n.ts._preferencesBackups.save,
text: i18n.tsx._preferencesBackups.saveConfirm({ name }),
});
if (canceled) return;
@ -394,15 +392,15 @@ async function rename(id: string): Promise<void> {
if (!profiles.value) return;
const { canceled: cancel1, result: name } = await os.inputText({
title: ts._preferencesBackups.inputName,
title: i18n.ts._preferencesBackups.inputName,
default: '',
});
if (cancel1 || profiles.value[id].name === name) return;
if (Object.values(profiles.value).some(x => x.name === name)) {
return os.alert({
title: ts._preferencesBackups.cannotSave,
text: t('_preferencesBackups.nameAlreadyExists', { name }),
title: i18n.ts._preferencesBackups.cannotSave,
text: i18n.tsx._preferencesBackups.nameAlreadyExists({ name }),
});
}
@ -410,8 +408,8 @@ async function rename(id: string): Promise<void> {
const { canceled: cancel2 } = await os.confirm({
type: 'info',
title: ts.rename,
text: t('_preferencesBackups.renameConfirm', { old: registry.name, new: name }),
title: i18n.ts.rename,
text: i18n.tsx._preferencesBackups.renameConfirm({ old: registry.name, new: name }),
});
if (cancel2) return;
@ -423,25 +421,25 @@ function menu(ev: MouseEvent, profileId: string) {
if (!profiles.value) return;
return os.popupMenu([{
text: ts._preferencesBackups.apply,
text: i18n.ts._preferencesBackups.apply,
icon: 'ti ti-check',
action: () => applyProfile(profileId),
}, {
type: 'a',
text: ts.download,
text: i18n.ts.download,
icon: 'ti ti-download',
href: URL.createObjectURL(new Blob([JSON.stringify(profiles.value[profileId], null, 2)], { type: 'application/json' })),
download: `${profiles.value[profileId].name}.json`,
}, { type: 'divider' }, {
text: ts.rename,
text: i18n.ts.rename,
icon: 'ti ti-forms',
action: () => rename(profileId),
}, {
text: ts._preferencesBackups.save,
text: i18n.ts._preferencesBackups.save,
icon: 'ti ti-device-floppy',
action: () => save(profileId),
}, { type: 'divider' }, {
text: ts.delete,
text: i18n.ts.delete,
icon: 'ti ti-trash',
action: () => deleteProfile(profileId),
danger: true,
@ -463,7 +461,7 @@ onUnmounted(() => {
});
definePageMetadata(() => ({
title: ts.preferencesBackups,
title: i18n.ts.preferencesBackups,
icon: 'ti ti-device-floppy',
}));
</script>

View file

@ -94,15 +94,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
</FormSlot>
<MkFolder>
<template #label>{{ i18n.ts.advancedSettings }}</template>
<div class="_gaps_m">
<MkSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></MkSwitch>
<MkSwitch v-if="profile.isCat" v-model="profile.speakAsCat">{{ i18n.ts.flagSpeakAsCat }}<template #caption>{{ i18n.ts.flagSpeakAsCatDescription }}</template></MkSwitch>
<MkSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></MkSwitch>
</div>
</MkFolder>
<MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false">
<template #label>{{ i18n.ts._profile.followedMessage }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
<template #caption>
<div>{{ i18n.ts._profile.followedMessageDescription }}</div>
<div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div>
</template>
</MkInput>
<MkSelect v-model="reactionAcceptance">
<template #label>{{ i18n.ts.reactionAcceptance }}</template>
@ -112,6 +110,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="nonSensitiveOnlyForLocalLikeOnlyForRemote">{{ i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }}</option>
<option value="likeOnly">{{ i18n.ts.likeOnly }}</option>
</MkSelect>
<MkFolder>
<template #label>{{ i18n.ts.advancedSettings }}</template>
<div class="_gaps_m">
<MkSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></MkSwitch>
<MkSwitch v-if="profile.isCat" v-model="profile.speakAsCat">{{ i18n.ts.flagSpeakAsCat }}<template #caption>{{ i18n.ts.flagSpeakAsCatDescription }}</template></MkSwitch>
<MkSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></MkSwitch>
</div>
</MkFolder>
</div>
</template>
@ -153,6 +161,7 @@ const setMaxBirthDate = () => {
const profile = reactive({
name: $i.name,
description: $i.description,
followedMessage: $i.followedMessage,
location: $i.location,
birthday: $i.birthday,
listenbrainz: $i.listenbrainz,
@ -209,6 +218,8 @@ function save() {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
description: profile.description || null,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
followedMessage: profile.followedMessage || null,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
location: profile.location || null,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
birthday: profile.birthday || null,

View file

@ -88,19 +88,9 @@ import { uniqueBy } from '@/scripts/array.js';
import { fetchThemes, getThemes } from '@/theme-store.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { reloadAsk } from '@/scripts/reload-ask.js';
import * as os from '@/os.js';
async function reloadAsk() {
const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.reloadToApplySetting,
});
if (canceled) return;
unisonReload();
}
const installedThemes = ref(getThemes());
const builtinThemes = getBuiltinThemesRef();
@ -148,13 +138,13 @@ watch(syncDeviceDarkMode, () => {
}
});
watch(wallpaper, () => {
watch(wallpaper, async () => {
if (wallpaper.value == null) {
miLocalStorage.removeItem('wallpaper');
} else {
miLocalStorage.setItem('wallpaper', wallpaper.value);
}
reloadAsk();
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
onActivated(() => {

View file

@ -21,14 +21,41 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection>
<template #label>{{ i18n.ts._webhookSettings.trigger }}</template>
<div class="_gaps_s">
<MkSwitch v-model="event_follow">{{ i18n.ts._webhookSettings._events.follow }}</MkSwitch>
<MkSwitch v-model="event_followed">{{ i18n.ts._webhookSettings._events.followed }}</MkSwitch>
<MkSwitch v-model="event_note">{{ i18n.ts._webhookSettings._events.note }}</MkSwitch>
<MkSwitch v-model="event_reply">{{ i18n.ts._webhookSettings._events.reply }}</MkSwitch>
<MkSwitch v-model="event_renote">{{ i18n.ts._webhookSettings._events.renote }}</MkSwitch>
<MkSwitch v-model="event_reaction">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
<MkSwitch v-model="event_mention">{{ i18n.ts._webhookSettings._events.mention }}</MkSwitch>
<div class="_gaps">
<div class="_gaps_s">
<div :class="$style.switchBox">
<MkSwitch v-model="event_follow">{{ i18n.ts._webhookSettings._events.follow }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_follow)" @click="test('follow')"><i class="ti ti-send"></i></MkButton>
</div>
<div :class="$style.switchBox">
<MkSwitch v-model="event_followed">{{ i18n.ts._webhookSettings._events.followed }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_followed)" @click="test('followed')"><i class="ti ti-send"></i></MkButton>
</div>
<div :class="$style.switchBox">
<MkSwitch v-model="event_note">{{ i18n.ts._webhookSettings._events.note }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_note)" @click="test('note')"><i class="ti ti-send"></i></MkButton>
</div>
<div :class="$style.switchBox">
<MkSwitch v-model="event_reply">{{ i18n.ts._webhookSettings._events.reply }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_reply)" @click="test('reply')"><i class="ti ti-send"></i></MkButton>
</div>
<div :class="$style.switchBox">
<MkSwitch v-model="event_renote">{{ i18n.ts._webhookSettings._events.renote }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_renote)" @click="test('renote')"><i class="ti ti-send"></i></MkButton>
</div>
<div :class="$style.switchBox">
<MkSwitch v-model="event_reaction">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_reaction)" @click="test('reaction')"><i class="ti ti-send"></i></MkButton>
</div>
<div :class="$style.switchBox">
<MkSwitch v-model="event_mention">{{ i18n.ts._webhookSettings._events.mention }}</MkSwitch>
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_mention)" @click="test('mention')"><i class="ti ti-send"></i></MkButton>
</div>
</div>
<div :class="$style.description">
{{ i18n.ts._webhookSettings.testRemarks }}
</div>
</div>
</FormSection>
@ -43,6 +70,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed } from 'vue';
import * as Misskey from 'misskey-js';
import MkInput from '@/components/MkInput.vue';
import FormSection from '@/components/form/section.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@ -76,8 +104,8 @@ const event_renote = ref(webhook.on.includes('renote'));
const event_reaction = ref(webhook.on.includes('reaction'));
const event_mention = ref(webhook.on.includes('mention'));
async function save(): Promise<void> {
const events = [];
function save() {
const events: Misskey.entities.UserWebhook['on'] = [];
if (event_follow.value) events.push('follow');
if (event_followed.value) events.push('followed');
if (event_note.value) events.push('note');
@ -110,8 +138,21 @@ async function del(): Promise<void> {
router.push('/settings/webhook');
}
async function test(type: Misskey.entities.UserWebhook['on'][number]): Promise<void> {
await os.apiWithDialog('i/webhooks/test', {
webhookId: props.webhookId,
type,
override: {
secret: secret.value,
url: url.value,
},
});
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const headerActions = computed(() => []);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const headerTabs = computed(() => []);
definePageMetadata(() => ({
@ -119,3 +160,30 @@ definePageMetadata(() => ({
icon: 'ti ti-webhook',
}));
</script>
<style module lang="scss">
.switchBox {
display: flex;
align-items: center;
justify-content: start;
.testButton {
$buttonSize: 28px;
padding: 0;
width: $buttonSize;
min-width: $buttonSize;
max-width: $buttonSize;
height: $buttonSize;
margin-left: auto;
line-height: inherit;
font-size: 90%;
border-radius: 9999px;
}
}
.description {
font-size: 0.85em;
padding: 8px 0 0 0;
color: var(--fgTransparentWeak);
}
</style>