Merge tag '2023.12.0-beta.6' into merge-upstream

This commit is contained in:
riku6460
2023-12-21 11:56:34 +09:00
251 changed files with 2328 additions and 1176 deletions

View File

@@ -221,12 +221,12 @@ import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os.js';
import { url } from '@/config.js';
import { userPage, acct } from '@/filters/user.js';
import { acct } from '@/filters/user.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
import { iAmAdmin, iAmModerator, $i } from '@/account.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import MkPagination from '@/components/MkPagination.vue';
const props = withDefaults(defineProps<{
userId: string;

View File

@@ -97,11 +97,8 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, computed } from 'vue';
import JSON5 from 'json5';
import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import FormSection from '@/components/form/section.vue';
import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { instance, fetchInstance } from '@/instance.js';

View File

@@ -69,8 +69,6 @@ import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import FormSection from '@/components/form/section.vue';
import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { fetchInstance } from '@/instance.js';

View File

@@ -123,9 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import * as Misskey from 'misskey-js';
import { CodeDiff } from 'v-code-diff';
import JSON5 from 'json5';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { dateString } from '@/filters/date.js';
import MkFolder from '@/components/MkFolder.vue';
const props = defineProps<{

View File

@@ -73,7 +73,7 @@ import { useRouter } from '@/router.js';
import MkButton from '@/components/MkButton.vue';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import MkPagination from '@/components/MkPagination.vue';
import { infoImageUrl } from '@/instance.js';
const router = useRouter();

View File

@@ -16,8 +16,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, watch } from 'vue';
import * as os from '@/os.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';

View File

@@ -38,9 +38,6 @@ import { ref, computed } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
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 { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';

View File

@@ -80,7 +80,7 @@ import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import FormSplit from '@/components/form/split.vue';
import { selectFile, selectFiles } from '@/scripts/select-file.js';
import { selectFile } from '@/scripts/select-file.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';

View File

@@ -103,7 +103,7 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { customEmojiCategories } from '@/custom-emojis.js';
import MkSwitch from '@/components/MkSwitch.vue';
import { selectFile, selectFiles } from '@/scripts/select-file.js';
import { selectFile } from '@/scripts/select-file.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
const props = defineProps<{

View File

@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
import { userListsCache } from '@/cache.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import MkPagination from '@/components/MkPagination.vue';
const {
enableInfiniteScroll,

View File

@@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
expanded?: boolean;

View File

@@ -42,18 +42,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from 'vue';
import { ref } from 'vue';
import MkNotes from '@/components/MkNotes.vue';
import MkInput from '@/components/MkInput.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import { $i } from '@/account.js';
import { instance } from '@/instance.js';
import MkInfo from '@/components/MkInfo.vue';
import { useRouter } from '@/router.js';
import MkFolder from '@/components/MkFolder.vue';

View File

@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
import { ref } from 'vue';
import MkUserList from '@/components/MkUserList.vue';
import MkInput from '@/components/MkInput.vue';
import MkRadios from '@/components/MkRadios.vue';
@@ -33,9 +33,6 @@ import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import { $i } from '@/account.js';
import { instance } from '@/instance.js';
import MkInfo from '@/components/MkInfo.vue';
import { useRouter } from '@/router.js';
const router = useRouter();

View File

@@ -23,10 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
import { computed, defineAsyncComponent, ref } from 'vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import * as os from '@/os.js';
import { $i } from '@/account.js';
import { instance } from '@/instance.js';
import MkInfo from '@/components/MkInfo.vue';

View File

@@ -72,7 +72,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { ref, defineAsyncComponent, computed } from 'vue';
import { defineAsyncComponent, computed } from 'vue';
import { supported as webAuthnSupported, create as webAuthnCreate, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';

View File

@@ -50,9 +50,6 @@ import MkButton from '@/components/MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import MkFolder from '@/components/MkFolder.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkRange from '@/components/MkRange.vue';
import { $i } from '@/account.js';

View File

@@ -4,51 +4,56 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div v-if="!loading" class="_gaps">
<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
<div>
<div v-if="!loading" class="_gaps">
<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
<div>{{ i18n.ts.inUse }}</div>
<MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration/>
<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
<div>{{ i18n.ts.inUse }}</div>
<div :class="$style.decorations">
<XDecoration
v-for="(avatarDecoration, i) in $i.avatarDecorations"
:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
:angle="avatarDecoration.angle"
:flipH="avatarDecoration.flipH"
:offsetX="avatarDecoration.offsetX"
:offsetY="avatarDecoration.offsetY"
:active="true"
@click="openDecoration(avatarDecoration, i)"
/>
</div>
<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
</div>
<div :class="$style.decorations">
<XDecoration
v-for="(avatarDecoration, i) in $i.avatarDecorations"
:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
:angle="avatarDecoration.angle"
:flipH="avatarDecoration.flipH"
:offsetX="avatarDecoration.offsetX"
:offsetY="avatarDecoration.offsetY"
:active="true"
@click="openDecoration(avatarDecoration, i)"
v-for="avatarDecoration in avatarDecorations"
:key="avatarDecoration.id"
:decoration="avatarDecoration"
@click="openDecoration(avatarDecoration)"
/>
</div>
<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
</div>
<div :class="$style.decorations">
<XDecoration
v-for="avatarDecoration in avatarDecorations"
:key="avatarDecoration.id"
:decoration="avatarDecoration"
@click="openDecoration(avatarDecoration)"
/>
<div v-else>
<MkLoading/>
</div>
</div>
<div v-else>
<MkLoading/>
</div>
</template>
<script lang="ts" setup>
import { ref, defineAsyncComponent } from 'vue';
import { ref, defineAsyncComponent, computed } from 'vue';
import * as Misskey from 'misskey-js';
import XDecoration from './profile.avatar-decoration.decoration.vue';
import XDecoration from './avatar-decoration.decoration.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import MkInfo from '@/components/MkInfo.vue';
import { definePageMetadata } from '@/scripts/page-metadata.js';
const loading = ref(true);
const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]);
@@ -59,7 +64,7 @@ os.api('get-avatar-decorations').then(_avatarDecorations => {
});
function openDecoration(avatarDecoration, index?: number) {
os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration.dialog.vue')), {
os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), {
decoration: avatarDecoration,
usingIndex: index,
}, {
@@ -115,9 +120,25 @@ function detachAllDecorations() {
$i.avatarDecorations = [];
});
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.avatarDecorations,
icon: 'ti ti-sparkles',
});
</script>
<style lang="scss" module>
.avatar {
display: inline-block;
width: 72px;
height: 72px;
margin: 16px auto;
}
.current {
padding: 16px;
border-radius: var(--radius);

View File

@@ -55,7 +55,6 @@ import MkPagination from '@/components/MkPagination.vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import { i18n } from '@/i18n.js';
import bytes from '@/filters/bytes.js';
import { dateString } from '@/filters/date.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkSelect from '@/components/MkSelect.vue';
import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';

View File

@@ -57,7 +57,6 @@ import { defaultStore } from '@/store.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { deepClone } from '@/scripts/clone.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));

View File

@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { defineAsyncComponent, shallowRef, computed } from 'vue';
import { shallowRef, computed } from 'vue';
import XNotificationConfig from './notifications.notification-config.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
@@ -68,7 +68,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { notificationTypes } from '@/const.js';
const nonConfigurableNotificationTypes = ['note'];
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'achievementEarned'];
const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);

View File

@@ -13,12 +13,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template>
</MkSwitch>
<MkSelect v-model="ffVisibility" @update:modelValue="save()">
<template #label>{{ i18n.ts.ffVisibility }}</template>
<MkSelect v-model="followingVisibility" @update:modelValue="save()">
<template #label>{{ i18n.ts.followingVisibility }}</template>
<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
</MkSelect>
<MkSelect v-model="followersVisibility" @update:modelValue="save()">
<template #label>{{ i18n.ts.followersVisibility }}</template>
<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
<template #caption>{{ i18n.ts.ffVisibilityDescription }}</template>
</MkSelect>
<MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
@@ -84,7 +90,8 @@ const preventAiLearning = ref($i.preventAiLearning);
const isExplorable = ref($i.isExplorable);
const hideOnlineStatus = ref($i.hideOnlineStatus);
const publicReactions = ref($i.publicReactions);
const ffVisibility = ref($i.ffVisibility);
const followingVisibility = ref($i?.followingVisibility);
const followersVisibility = ref($i?.followersVisibility);
const defaultNoteVisibility = computed(defaultStore.makeGetterSetter('defaultNoteVisibility'));
const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly'));
@@ -100,7 +107,8 @@ function save() {
isExplorable: !!isExplorable.value,
hideOnlineStatus: !!hideOnlineStatus.value,
publicReactions: !!publicReactions.value,
ffVisibility: ffVisibility.value,
followingVisibility: followingVisibility.value,
followersVisibility: followersVisibility.value,
});
}

View File

@@ -5,12 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<div :class="$style.avatarAndBanner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
<div class="_panel">
<div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
</div>
<div :class="$style.avatarContainer">
<MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration @click="changeAvatar"/>
<MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
<div class="_buttonsCenter">
<MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
<MkButton primary rounded link to="/settings/avatar-decoration">{{ i18n.ts.decorate }} <i class="ti ti-sparkles"></i></MkButton>
</div>
</div>
<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
</div>
<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
@@ -83,13 +88,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
</FormSlot>
<MkFolder>
<template #icon><i class="ti ti-sparkles"></i></template>
<template #label>{{ i18n.ts.avatarDecorations }}</template>
<XAvatarDecoration/>
</MkFolder>
<MkFolder>
<template #label>{{ i18n.ts.advancedSettings }}</template>
@@ -112,8 +110,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue';
import Misskey from 'misskey-js';
import XAvatarDecoration from './profile.avatar-decoration.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -264,19 +260,19 @@ definePageMetadata({
</script>
<style lang="scss" module>
.avatarAndBanner {
.banner {
position: relative;
height: 130px;
background-size: cover;
background-position: center;
border: solid 1px var(--divider);
border-radius: 10px;
border-bottom: solid 1px var(--divider);
overflow: clip;
}
.avatarContainer {
display: inline-block;
margin-top: -50px;
padding-bottom: 16px;
text-align: center;
padding: 16px;
}
.avatar {

View File

@@ -23,21 +23,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, reactive, watch } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
import FormSplit from '@/components/form/split.vue';
import MkFolder from '@/components/MkFolder.vue';
import FormSlot from '@/components/form/slot.vue';
import { computed } from 'vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { defaultStore } from '@/store.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
function save() {

View File

@@ -45,7 +45,6 @@ import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { $i } from '@/account.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js';
import { antennasCache, userListsCache } from '@/cache.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { MenuItem } from '@/types/menu.js';

View File

@@ -16,8 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, watch } from 'vue';
import * as os from '@/os.js';
import { computed } from 'vue';
import MkUserList from '@/components/MkUserList.vue';
import { definePageMetadata } from '@/scripts/page-metadata.js';

View File

@@ -110,11 +110,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<b>{{ number(user.notesCount) }}</b>
<span>{{ i18n.ts.notes }}</span>
</MkA>
<MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'following')">
<MkA v-if="isFollowingVisibleForMe(user)" :to="userPage(user, 'following')">
<b>{{ number(user.followingCount) }}</b>
<span>{{ i18n.ts.following }}</span>
</MkA>
<MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'followers')">
<MkA v-if="isFollowersVisibleForMe(user)" :to="userPage(user, 'followers')">
<b>{{ number(user.followersCount) }}</b>
<span>{{ i18n.ts.followers }}</span>
</MkA>
@@ -171,9 +171,8 @@ import { i18n } from '@/i18n.js';
import { $i, iAmModerator } from '@/account.js';
import { dateString } from '@/filters/date.js';
import { confetti } from '@/scripts/confetti.js';
import MkNotes from '@/components/MkNotes.vue';
import { api } from '@/os.js';
import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
function calcAge(birthdate: string): number {
const date = new Date(birthdate);

View File

@@ -39,13 +39,7 @@ import XTimeline from './welcome.timeline.vue';
import MarqueeText from '@/components/MkMarquee.vue';
import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
import misskeysvg from '/client-assets/misskey.svg';
import MkInfo from '@/components/MkInfo.vue';
import { instanceName } from '@/config.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import number from '@/filters/number.js';
import MkNumber from '@/components/MkNumber.vue';
import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
import { getProxiedImageUrl } from '@/scripts/media-proxy.js';

View File

@@ -34,7 +34,6 @@ import MkMediaList from '@/components/MkMediaList.vue';
import MkPoll from '@/components/MkPoll.vue';
import * as os from '@/os.js';
import { getScrollContainer } from '@/scripts/scroll.js';
import { $i } from '@/account.js';
const notes = ref<Misskey.entities.Note[]>([]);
const isScrolling = ref(false);