merge: misskey 2025.5.0 (!1028)

View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1028

Approved-by: Hazelnoot <acomputerdog@gmail.com>
Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
dakkar 2025-06-29 09:54:12 +00:00
commit 13d045d813
152 changed files with 1690 additions and 842 deletions

View file

@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLoading v-if="!loaded"/>
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" appear>
<div v-show="loaded" :class="$style.root">
<img :src="serverErrorImageUrl" draggable="false" :class="$style.img"/>
<img v-if="instance.serverErrorImageUrl" :src="instance.serverErrorImageUrl" draggable="false" :class="$style.img"/>
<div class="_gaps">
<div><b><i class="ti ti-alert-triangle"></i> {{ i18n.ts.pageLoadError }}</b></div>
<div v-if="meta && (version === meta.version)">{{ i18n.ts.pageLoadErrorDescription }}</div>
@ -36,7 +36,7 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { miLocalStorage } from '@/local-storage.js';
import { prefer } from '@/preferences.js';
import { serverErrorImageUrl } from '@/instance.js';
import { instance } from '@/instance.js';
const props = withDefaults(defineProps<{
error?: Error;

View file

@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTl :events="timeline" :displayLimit="50" style="margin-top: var(--MI-margin);">
<template #left="{ event }">
<div>
<MkAvatar :user="event.user" style="width: 24px; height: 24px;"/>
<MkAvatar :user="event.user" style="width: 26px; height: 26px;"/>
</div>
</template>
<template #right="{ event, timestamp, delta }">

View file

@ -25,12 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
<MkPagination :pagination="usersPagination" :displayLimit="50">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
<template #empty><MkResult type="empty" :text="i18n.ts.noUsers"/></template>
<template #default="{ items }">
<div class="_gaps_s">
@ -71,7 +66,6 @@ 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.js';
const router = useRouter();

View file

@ -262,6 +262,31 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
<template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
</MkTextarea>
<MkFolder>
<template #icon><i class="ti ti-list"></i></template>
<template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
<template #footer>
<div class="_buttons">
<MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
</div>
</template>
<div :class="$style.metadataRoot" class="_gaps_s">
<MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
<div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
<button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
<div :class="$style.dragItemForm">
<FormSplit :minWidth="200">
<MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
</MkInput>
<MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
</MkInput>
</FormSplit>
</div>
</div>
</div>
</MkFolder>
</div>
</MkFolder>
@ -420,10 +445,12 @@ const urlPreviewForm = useForm({
const federationForm = useForm({
federation: meta.federation,
federationHosts: meta.federationHosts.join('\n'),
deliverSuspendedSoftware: meta.deliverSuspendedSoftware,
}, async (state) => {
await os.apiWithDialog('admin/update-meta', {
federation: state.federation,
federationHosts: state.federationHosts.split('\n'),
deliverSuspendedSoftware: state.deliverSuspendedSoftware,
});
fetchInstance(true);
});
@ -470,4 +497,53 @@ definePage(() => ({
font-size: 0.85em;
color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
}
.metadataRoot {
container-type: inline-size;
}
.fieldDragItem {
display: flex;
padding: 10px;
align-items: flex-end;
border-radius: 6px;
/* (drag button) 32px + (drag button margin) 8px + (input width) 200px * 2 + (input gap) 12px = 452px */
@container (max-width: 452px) {
align-items: center;
}
}
.dragItemHandle {
cursor: grab;
width: 32px;
height: 32px;
margin: 0 8px 0 0;
opacity: 0.5;
flex-shrink: 0;
&:active {
cursor: grabbing;
}
}
.dragItemRemove {
@extend .dragItemHandle;
color: #ff2a2a;
opacity: 1;
cursor: pointer;
&:hover, &:focus {
opacity: .7;
}
&:active {
cursor: pointer;
}
}
.dragItemForm {
flex-grow: 1;
}
</style>

View file

@ -27,9 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</div>
<div v-if="!fetching && invitations.length == 0" class="_fullinfo">
<div>{{ i18n.ts._chat.noInvitations }}</div>
</div>
<MkResult v-if="!fetching && invitations.length == 0" type="empty" :text="i18n.ts._chat.noInvitations"/>
<MkLoading v-if="fetching"/>
</div>
</template>

View file

@ -8,9 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="memberships.length > 0" class="_gaps_s">
<XRoom v-for="membership in memberships" :key="membership.id" :room="membership.room!"/>
</div>
<div v-if="!fetching && memberships.length == 0" class="_fullinfo">
<div>{{ i18n.ts._chat.noRooms }}</div>
</div>
<MkResult v-if="!fetching && memberships.length == 0" type="empty" :text="i18n.ts._chat.noRooms"/>
<MkLoading v-if="fetching"/>
</div>
</template>

View file

@ -8,9 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="rooms.length > 0" class="_gaps_s">
<XRoom v-for="room in rooms" :key="room.id" :room="room"/>
</div>
<div v-if="!fetching && rooms.length == 0" class="_fullinfo">
<div>{{ i18n.ts._chat.noRooms }}</div>
</div>
<MkResult v-if="!fetching && rooms.length == 0" type="empty" :text="i18n.ts._chat.noRooms"/>
<MkLoading v-if="fetching"/>
</div>
</template>

View file

@ -24,10 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XMessage :message="message" :user="message.fromUser" :isSearchResult="true"/>
</div>
</div>
<div v-else class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.notFound }}</div>
</div>
<MkResult v-else type="notFound"/>
</MkFoldableSection>
</div>
</template>
@ -38,7 +35,6 @@ import * as Misskey from 'misskey-js';
import XMessage from './XMessage.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkInput from '@/components/MkInput.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';

View file

@ -0,0 +1,66 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<PageWithHeader>
<div class="_spacer" style="--MI_SPACER-w: 600px;">
<div class="_gaps_m">
<MkResult v-if="resultType === 'empty'" type="empty"/>
<MkResult v-if="resultType === 'notFound'" type="notFound"/>
<MkResult v-if="resultType === 'error'" type="error"/>
<MkSelect
v-model="resultType" :items="[
{ label: 'empty', value: 'empty' },
{ label: 'notFound', value: 'notFound' },
{ label: 'error', value: 'error' },
]"
></MkSelect>
<MkSystemIcon v-if="iconType === 'info'" type="info" style="width: 60px;"/>
<MkSystemIcon v-if="iconType === 'question'" type="question" style="width: 60px;"/>
<MkSystemIcon v-if="iconType === 'success'" type="success" style="width: 60px;"/>
<MkSystemIcon v-if="iconType === 'warn'" type="warn" style="width: 60px;"/>
<MkSystemIcon v-if="iconType === 'error'" type="error" style="width: 60px;"/>
<MkSelect
v-model="iconType" :items="[
{ label: 'info', value: 'info' },
{ label: 'question', value: 'question' },
{ label: 'success', value: 'success' },
{ label: 'warn', value: 'warn' },
{ label: 'error', value: 'error' },
]"
></MkSelect>
<div class="_buttons">
<MkButton @click="os.alert({ type: 'error', title: 'Error', text: 'error' })">Error</MkButton>
<MkButton @click="os.alert({ type: 'warning', title: 'Warning', text: 'warning' })">Warning</MkButton>
<MkButton @click="os.alert({ type: 'info', title: 'Info', text: 'info' })">Info</MkButton>
<MkButton @click="os.alert({ type: 'success', title: 'Success', text: 'success' })">Success</MkButton>
<MkButton @click="os.alert({ type: 'question', title: 'Question', text: 'question' })">Question</MkButton>
</div>
</div>
</div>
</PageWithHeader>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { definePage } from '@/page.js';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkLink from '@/components/MkLink.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
const resultType = ref('empty');
const iconType = ref('info');
definePage(() => ({
title: 'DEBUG ROOM',
icon: 'ti ti-help-circle',
}));
</script>

View file

@ -68,10 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue>
</div>
</div>
<div v-else class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
<MkResult v-else type="empty"/>
</div>
</template>
@ -82,7 +79,6 @@ import MkInfo from '@/components/MkInfo.vue';
import MkMediaList from '@/components/MkMediaList.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import bytes from '@/filters/bytes.js';
import { infoImageUrl } from '@/instance.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';

View file

@ -7,12 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<PageWithHeader>
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkPagination :pagination="pagination">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noNotes }}</div>
</div>
</template>
<template #empty><MkResult type="empty" :text="i18n.ts.noNotes"/></template>
<template #default="{ items }">
<!-- TODO replace with SkDateSeparatedList when merged -->
@ -31,7 +26,6 @@ import DynamicNote from '@/components/DynamicNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { infoImageUrl } from '@/instance.js';
const pagination = {
endpoint: 'i/favorites' as const,

View file

@ -7,12 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkPagination ref="paginationComponent" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noFollowRequests }}</div>
</div>
</template>
<template #empty><MkResult type="empty" :text="i18n.ts.noFollowRequests"/></template>
<template #default="{items}">
<div class="mk-follow-requests _gaps">
<div v-for="req in items" :key="req.id" class="user _panel">
@ -48,7 +43,6 @@ import { userPage, acct } from '@/filters/user.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { infoImageUrl } from '@/instance.js';
import { $i } from '@/i.js';
const paginationComponent = useTemplateRef('paginationComponent');

View file

@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps">
<MkInfo v-if="isBaseSilenced" warn>{{ i18n.ts.silencedByBase }}</MkInfo>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance || isBaseSilenced" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
<MkSwitch v-model="isSuspended" :disabled="!instance" @update:modelValue="toggleSuspended">{{ i18n.ts._delivery.stop }}</MkSwitch>
<MkSwitch v-model="isSuspended" :disabled="!instance || suspensionState == 'softwareSuspended'" @update:modelValue="toggleSuspended">{{ i18n.ts._delivery.stop }}</MkSwitch>
<MkInfo v-if="isBaseBlocked" warn>{{ i18n.ts.blockedByBase }}</MkInfo>
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance || isBaseBlocked" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
<MkSwitch v-model="rejectQuotes" :disabled="!instance" @update:modelValue="toggleRejectQuotes">{{ i18n.ts.rejectQuotesInstance }}</MkSwitch>
@ -249,7 +249,7 @@ const tab = ref('overview');
const chartSrc = ref<ChartSrc>('instance-requests');
const meta = ref<Misskey.entities.AdminMetaResponse | null>(null);
const instance = ref<Misskey.entities.FederationInstance | null>(null);
const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding'>('none');
const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding' | 'softwareSuspended'>('none');
const isSuspended = ref(false);
const isBlocked = ref(false);
const isSilenced = ref(false);
@ -436,6 +436,7 @@ async function toggleMediaSilenced(): Promise<void> {
async function toggleSuspended(): Promise<void> {
if (!iAmModerator) return;
if (suspensionState.value === 'softwareSuspended') return;
await os.promiseDialog(async () => {
if (!instance.value) throw new Error('No instance?');
suspensionState.value = isSuspended.value ? 'manuallySuspended' : 'none';

View file

@ -6,13 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
<div v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div :class="$style.root">
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
<div :class="$style.text">
<i class="ti ti-alert-triangle"></i>
{{ i18n.ts.nothing }}
</div>
</div>
<MkResult type="empty"/>
</div>
<div v-else class="_spacer" style="--MI_SPACER-w: 800px;">
<div class="_gaps_m" style="text-align: center;">
@ -43,7 +37,7 @@ import MkButton from '@/components/MkButton.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkInviteCode from '@/components/MkInviteCode.vue';
import { definePage } from '@/page.js';
import { serverErrorImageUrl, instance } from '@/instance.js';
import { instance } from '@/instance.js';
import { $i } from '@/i.js';
const pagingComponent = useTemplateRef('pagingComponent');
@ -96,23 +90,3 @@ definePage(() => ({
icon: 'ti ti-user-plus',
}));
</script>
<style lang="scss" module>
.root {
padding: 32px;
text-align: center;
align-items: center;
}
.text {
margin: 0 0 8px 0;
}
.img {
vertical-align: bottom;
width: 128px;
height: 128px;
margin-bottom: 16px;
border-radius: var(--MI-radius-md);
}
</style>

View file

@ -6,13 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div v-if="error != null" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div :class="$style.root">
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
<p :class="$style.text">
<i class="ti ti-alert-triangle"></i>
{{ i18n.ts.nothing }}
</p>
</div>
<MkResult type="error"/>
</div>
<div v-else-if="list" class="_spacer" style="--MI_SPACER-w: 700px;">
<div v-if="list" class="members _margin">
@ -42,7 +36,6 @@ import { i18n } from '@/i18n.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkButton from '@/components/MkButton.vue';
import { definePage } from '@/page.js';
import { serverErrorImageUrl } from '@/instance.js';
const props = defineProps<{
listId: string;

View file

@ -7,12 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px;">
<div>
<div v-if="antennas.length === 0" class="empty">
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</div>
<MkResult v-if="antennas.length === 0" type="empty"/>
<MkButton :link="true" to="/my/antennas/create" primary :class="$style.add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
@ -32,7 +27,6 @@ import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { antennasCache } from '@/cache.js';
import { infoImageUrl } from '@/instance.js';
const antennas = computed(() => antennasCache.value.value ?? []);

View file

@ -7,12 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<div v-if="items.length === 0" class="empty">
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</div>
<MkResult v-if="items.length === 0" type="empty"/>
<MkButton primary rounded style="margin: 0 auto;" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton>
@ -35,7 +30,6 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { userListsCache } from '@/cache.js';
import { infoImageUrl } from '@/instance.js';
import { ensureSignin } from '@/i.js';
const $i = ensureSignin();

View file

@ -4,11 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div class="_fullinfo">
<img :src="notFoundImageUrl" draggable="false"/>
<div>{{ i18n.ts.notFoundDescription }}</div>
</div>
<div style="align-content: center; height: 100cqh;">
<MkResult type="notFound" :text="i18n.ts.notFoundDescription"/>
</div>
</template>
@ -17,7 +14,6 @@ import { computed } from 'vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { pleaseLogin } from '@/utility/please-login.js';
import { notFoundImageUrl } from '@/instance.js';
const props = defineProps<{
showLoginPopup?: boolean;

View file

@ -6,30 +6,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs">
<div v-if="error != null" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div :class="$style.root">
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
<p :class="$style.text">
<i class="ti ti-alert-triangle"></i>
{{ error }}
</p>
</div>
<MkResult type="error" :text="error"/>
</div>
<div v-else-if="tab === 'users'" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div class="_gaps_s">
<div v-if="role">{{ role.description }}</div>
<MkUserList v-if="visible" :pagination="users" :extractor="(item) => item.user"/>
<div v-else-if="!visible" class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
<MkResult v-else-if="!visible" type="empty" :text="i18n.ts.nothing"/>
</div>
</div>
<div v-else-if="tab === 'timeline'" class="_spacer" style="--MI_SPACER-w: 700px;">
<MkTimeline v-if="visible" ref="timeline" src="role" :role="props.roleId"/>
<div v-else-if="!visible" class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
<MkResult v-else-if="!visible" type="empty" :text="i18n.ts.nothing"/>
</div>
</PageWithHeader>
</template>
@ -37,13 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, watch, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { instanceName } from '@@/js/config.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkUserList from '@/components/MkUserList.vue';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import MkTimeline from '@/components/MkTimeline.vue';
import { serverErrorImageUrl, infoImageUrl } from '@/instance.js';
const props = withDefaults(defineProps<{
roleId: string;
@ -97,23 +83,3 @@ definePage(() => ({
icon: 'ti ti-badge',
}));
</script>
<style lang="scss" module>
.root {
padding: 32px;
text-align: center;
align-items: center;
}
.text {
margin: 0 0 8px 0;
}
.img {
vertical-align: bottom;
width: 128px;
height: 128px;
margin-bottom: 16px;
border-radius: var(--MI-radius-md);
}
</style>

View file

@ -6,12 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<FormPagination ref="list" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</template>
<template #empty><MkResult type="empty"/></template>
<template #default="{items}">
<div class="_gaps">
<MkFolder v-for="token in items" :key="token.id" :defaultOpen="true">
@ -63,7 +58,6 @@ import { definePage } from '@/page.js';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import { infoImageUrl } from '@/instance.js';
const list = ref<InstanceType<typeof FormPagination>>();

View file

@ -78,12 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label><SearchLabel>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</SearchLabel></template>
<MkPagination :pagination="renoteMutingPagination">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
<template #empty><MkResult type="empty" :text="i18n.ts.noUsers"/></template>
<template #default="{ items }">
<div class="_gaps_s">
@ -115,12 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.mutedUsers }}</template>
<MkPagination :pagination="mutingPagination">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
<template #empty><MkResult type="empty" :text="i18n.ts.noUsers"/></template>
<template #default="{ items }">
<div class="_gaps_s">
@ -154,12 +144,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.blockedUsers }}</template>
<MkPagination :pagination="blockingPagination">
<template #empty>
<div class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
<template #empty><MkResult type="empty" :text="i18n.ts.noUsers"/></template>
<template #default="{ items }">
<div class="_gaps_s">
@ -197,7 +182,7 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import * as os from '@/os.js';
import { instance, infoImageUrl } from '@/instance.js';
import { instance } from '@/instance.js';
import { ensureSignin } from '@/i.js';
import MkInfo from '@/components/MkInfo.vue';
import MkFolder from '@/components/MkFolder.vue';

View file

@ -628,6 +628,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['swipe', 'pull', 'refresh']">
<MkPreferenceContainer k="enablePullToRefresh">
<MkSwitch v-model="enablePullToRefresh">
<template #label><SearchLabel>{{ i18n.ts._settings.enablePullToRefresh }}</SearchLabel></template>
<template #caption><SearchKeyword>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchKeyword></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['keep', 'screen', 'display', 'on']">
<MkPreferenceContainer k="keepScreenOn">
<MkSwitch v-model="keepScreenOn">
@ -1032,6 +1041,7 @@ const animatedMfm = prefer.model('animatedMfm');
const disableShowingAnimatedImages = prefer.model('disableShowingAnimatedImages');
const keepScreenOn = prefer.model('keepScreenOn');
const enableHorizontalSwipe = prefer.model('enableHorizontalSwipe');
const enablePullToRefresh = prefer.model('enablePullToRefresh');
const useNativeUiForVideoAudioPlayer = prefer.model('useNativeUiForVideoAudioPlayer');
const contextMenu = prefer.model('contextMenu');
const menuStyle = prefer.model('menuStyle');
@ -1088,6 +1098,8 @@ watch([
keepScreenOn,
contextMenu,
makeEveryTextElementsSelectable,
enableHorizontalSwipe,
enablePullToRefresh,
noteDesign,
], async () => {
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });

View file

@ -0,0 +1,47 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<SearchMarker path="/settings/profiles" :label="i18n.ts._preferencesProfile.manageProfiles" :keywords="['profile', 'settings', 'preferences', 'manage']" icon="ti ti-settings-cog">
<div class="_gaps">
<MkFolder v-for="backup in backups">
<template #label>{{ backup.name }}</template>
<MkButton danger @click="del(backup)">{{ i18n.ts.delete }}</MkButton>
</MkFolder>
</div>
</SearchMarker>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import type { MenuItem } from '@/types/menu.js';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
import { deleteCloudBackup, listCloudBackups } from '@/preferences/utility.js';
const backups = await listCloudBackups();
function del(backup) {
deleteCloudBackup(backup.name);
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePage(() => ({
title: i18n.ts._preferencesProfile.manageProfiles,
icon: 'ti ti-settings-cog',
}));
</script>
<style lang="scss" module>
</style>

View file

@ -173,8 +173,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLazy>
<div v-if="noteview === 'pinned'" class="_gaps">
<div v-if="user.pinnedNotes.length < 1" class="_fullinfo">
<img :src="infoImageUrl" draggable="false" aria-hidden="true" :alt="i18n.ts.noNotes"/>
<div>{{ i18n.ts.noNotes }}</div>
<MkResult type="empty" :text="i18n.ts.noNotes"/>
</div>
<div v-else class="_panel">
<DynamicNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/>
@ -220,7 +219,6 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/utility/isFfVisibleForMe.js';
import { useRouter } from '@/router.js';
import { getStaticImageUrl } from '@/utility/media-proxy.js';
import { infoImageUrl } from '@/instance.js';
import MkSparkle from '@/components/MkSparkle.vue';
import { prefer } from '@/preferences.js';
import DynamicNote from '@/components/DynamicNote.vue';

View file

@ -16,8 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div v-if="tab === 'pinned'" class="_gaps">
<div v-if="user.pinnedNotes.length < 1" class="_fullinfo">
<img :src="infoImageUrl" draggable="false" aria-hidden="true" :alt="i18n.ts.noNotes"/>
<div>{{ i18n.ts.noNotes }}</div>
<MkResult type="empty" :text="i18n.ts.noNotes"/>
</div>
<div v-else class="_panel">
<DynamicNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/>
@ -33,7 +32,6 @@ import * as Misskey from 'misskey-js';
import MkNotes from '@/components/MkNotes.vue';
import MkTab from '@/components/MkTab.vue';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';
import DynamicNote from '@/components/DynamicNote.vue';
const props = defineProps<{