merge: upstream

This commit is contained in:
Mar0xy 2023-10-03 15:20:49 +02:00
commit 38e35e1472
No known key found for this signature in database
GPG key ID: 56569BBE47D2C828
185 changed files with 4442 additions and 2501 deletions

View file

@ -83,6 +83,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #value><code class="_monospace">{{ code }}</code></template>
</MkKeyValue>
</div>
<MkButton primary rounded gradate @click="downloadBackupCodes"><i class="ti ti-download"></i> {{ i18n.ts.download }}</MkButton>
</div>
</MkFolder>
</div>
@ -108,6 +110,7 @@ import * as os from '@/os.js';
import MkFolder from '@/components/MkFolder.vue';
import MkInfo from '@/components/MkInfo.vue';
import { confetti } from '@/scripts/confetti.js';
import { $i } from '@/account.js';
defineProps<{
twoFactorData: {
@ -143,6 +146,16 @@ async function tokenDone() {
});
}
function downloadBackupCodes() {
if (backupCodes.value !== undefined) {
const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' });
const dummya = document.createElement('a');
dummya.href = URL.createObjectURL(txtBlob);
dummya.download = `${$i?.username}-2fa-backup-codes.txt`;
dummya.click();
}
}
function allDone() {
dialog.value.close();
}

View file

@ -5,13 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<MkTab v-model="tab">
<option value="renoteMute">{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</option>
<option value="mute">{{ i18n.ts.mutedUsers }}</option>
<option value="block">{{ i18n.ts.blockedUsers }}</option>
</MkTab>
<MkFolder>
<template #icon><i class="ti ti-repeat-off"></i></template>
<template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template>
<div v-if="tab === 'renoteMute'">
<MkPagination :pagination="renoteMutingPagination">
<template #empty>
<div class="_fullinfo">
@ -37,9 +34,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
</MkPagination>
</div>
</MkFolder>
<MkFolder>
<template #icon><i class="ti ti-eye-off"></i></template>
<template #label>{{ i18n.ts.mutedUsers }}</template>
<div v-else-if="tab === 'mute'">
<MkPagination :pagination="mutingPagination">
<template #empty>
<div class="_fullinfo">
@ -67,9 +67,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
</MkPagination>
</div>
</MkFolder>
<MkFolder>
<template #icon><i class="ti ti-ban"></i></template>
<template #label>{{ i18n.ts.blockedUsers }}</template>
<div v-else-if="tab === 'block'">
<MkPagination :pagination="blockingPagination">
<template #empty>
<div class="_fullinfo">
@ -97,24 +100,20 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
</MkPagination>
</div>
</MkFolder>
</div>
</template>
<script lang="ts" setup>
import { } from 'vue';
import MkPagination from '@/components/MkPagination.vue';
import MkTab from '@/components/MkTab.vue';
import FormInfo from '@/components/MkInfo.vue';
import FormLink from '@/components/form/link.vue';
import { userPage } from '@/filters/user.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import * as os from '@/os.js';
import { infoImageUrl } from '@/instance.js';
let tab = $ref('renoteMute');
import MkFolder from '@/components/MkFolder.vue';
const renoteMutingPagination = {
endpoint: 'renote-mute/list' as const,

View file

@ -0,0 +1,50 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="_gaps_m">
<MkSelect v-model="type">
<option value="all">{{ i18n.ts.all }}</option>
<option value="following">{{ i18n.ts.following }}</option>
<option value="follower">{{ i18n.ts.followers }}</option>
<option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option>
<option value="list">{{ i18n.ts.userList }}</option>
<option value="never">{{ i18n.ts.none }}</option>
</MkSelect>
<MkSelect v-if="type === 'list'" v-model="userListId">
<template #label>{{ i18n.ts.userList }}</template>
<option v-for="list in props.userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
</MkSelect>
<div class="_buttons">
<MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</div>
</template>
<script lang="ts" setup>
import { } from 'vue';
import * as Misskey from 'misskey-js';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
const props = defineProps<{
value: any;
userLists: Misskey.entities.UserList[];
}>();
const emit = defineEmits<{
(ev: 'update', result: any): void;
}>();
let type = $ref(props.value.type);
let userListId = $ref(props.value.userListId);
function save() {
emit('update', { type, userListId });
}
</script>

View file

@ -5,7 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<FormLink @click="configure"><template #icon><i class="ph-gear ph-bold pg-lg"></i></template>{{ i18n.ts.notificationSetting }}</FormLink>
<FormSection first>
<template #label>{{ i18n.ts.notificationRecieveConfig }}</template>
<div class="_gaps_s">
<MkFolder v-for="type in notificationTypes" :key="type">
<template #label>{{ i18n.t('_notification._types.' + type) }}</template>
<template #suffix>
{{
$i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none :
$i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
$i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
$i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
$i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
i18n.ts.all
}}
</template>
<XNotificationConfig :userLists="userLists" :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" @update="(res) => updateReceiveConfig(type, res)"/>
</MkFolder>
</div>
</FormSection>
<FormSection>
<div class="_gaps_m">
<FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
@ -37,19 +56,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
import XNotificationConfig from './notifications.notification-config.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import * as os from '@/os.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { notificationTypes } from '@/const';
import { notificationTypes } from '@/const.js';
let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
let sendReadMessage = $computed(() => pushRegistrationInServer?.sendReadMessage || false);
const userLists = await os.api('users/lists/list');
async function readAllUnreadNotes() {
await os.api('i/read-all-unread-notes');
@ -59,21 +81,15 @@ async function readAllNotifications() {
await os.api('notifications/mark-all-as-read');
}
function configure() {
const includingTypes = notificationTypes.filter(x => !$i!.mutingNotificationTypes.includes(x));
os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), {
includingTypes,
showGlobalToggle: false,
}, {
done: async (res) => {
const { includingTypes: value } = res;
await os.apiWithDialog('i/update', {
mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)),
}).then(i => {
$i!.mutingNotificationTypes = i.mutingNotificationTypes;
});
async function updateReceiveConfig(type, value) {
await os.apiWithDialog('i/update', {
notificationRecieveConfig: {
...$i!.notificationRecieveConfig,
[type]: value,
},
}, 'closed');
}).then(i => {
$i!.notificationRecieveConfig = i.notificationRecieveConfig;
});
}
function onChangeSendReadMessage(v: boolean) {

View file

@ -5,29 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<MkTab v-model="tab">
<option value="soft">{{ i18n.ts._wordMute.soft }}</option>
<option value="hard">{{ i18n.ts._wordMute.hard }}</option>
</MkTab>
<div>
<div v-show="tab === 'soft'" class="_gaps_m">
<MkInfo>{{ i18n.ts._wordMute.softDescription }}</MkInfo>
<MkTextarea v-model="softMutedWords">
<span>{{ i18n.ts._wordMute.muteWords }}</span>
<template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</MkTextarea>
</div>
<div v-show="tab === 'hard'" class="_gaps_m">
<MkInfo>{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo>
<MkTextarea v-model="hardMutedWords">
<span>{{ i18n.ts._wordMute.muteWords }}</span>
<template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</MkTextarea>
<MkKeyValue v-if="hardWordMutedNotesCount != null">
<template #key>{{ i18n.ts._wordMute.mutedNotes }}</template>
<template #value>{{ number(hardWordMutedNotesCount) }}</template>
</MkKeyValue>
</div>
<MkTextarea v-model="mutedWords">
<span>{{ i18n.ts._wordMute.muteWords }}</span>
<template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</MkTextarea>
</div>
<MkButton primary inline :disabled="!changed" @click="save()"><i class="ph-floppy-disk ph-bold pg-lg"></i> {{ i18n.ts.save }}</MkButton>
</div>
@ -56,25 +38,15 @@ const render = (mutedWords) => mutedWords.map(x => {
}).join('\n');
const tab = ref('soft');
const softMutedWords = ref(render(defaultStore.state.mutedWords));
const hardMutedWords = ref(render($i!.mutedWords));
const hardWordMutedNotesCount = ref(null);
const mutedWords = ref(render($i!.mutedWords));
const changed = ref(false);
os.api('i/get-word-muted-notes-count', {}).then(response => {
hardWordMutedNotesCount.value = response?.count;
});
watch(softMutedWords, () => {
changed.value = true;
});
watch(hardMutedWords, () => {
watch(mutedWords, () => {
changed.value = true;
});
async function save() {
const parseMutes = (mutes, tab) => {
const parseMutes = (mutes) => {
// split into lines, remove empty lines and unnecessary whitespace
let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== '');
@ -92,7 +64,7 @@ async function save() {
os.alert({
type: 'error',
title: i18n.ts.regexpError,
text: i18n.t('regexpErrorDescription', { tab, line: i + 1 }) + '\n' + err.toString(),
text: i18n.t('regexpErrorDescription', { tab: 'word mute', line: i + 1 }) + '\n' + err.toString(),
});
// re-throw error so these invalid settings are not saved
throw err;
@ -105,18 +77,16 @@ async function save() {
return lines;
};
let softMutes, hardMutes;
let parsed;
try {
softMutes = parseMutes(softMutedWords.value, i18n.ts._wordMute.soft);
hardMutes = parseMutes(hardMutedWords.value, i18n.ts._wordMute.hard);
parsed = parseMutes(mutedWords.value);
} catch (err) {
// already displayed error message in parseMutes
return;
}
defaultStore.set('mutedWords', softMutes);
await os.api('i/update', {
mutedWords: hardMutes,
mutedWords: parsed,
});
changed.value = false;