Merge tag '2025.2.0' into merge/2024-02-03

This commit is contained in:
dakkar 2025-02-06 10:22:57 +00:00
commit 708debf5d5
35 changed files with 127 additions and 191 deletions

View file

@ -141,6 +141,7 @@ function onPasskeyDone(credential: AuthenticationPublicKeyCredential): void {
return;
}
emit('login', res.signinResponse);
onLoginSucceeded(res.signinResponse);
}).catch(onSigninApiError);
} else if (userInfo.value != null) {
tryLogin({

View file

@ -39,32 +39,18 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<!-- MFMで上位レイヤーに表示されるためリンクをクリックできるようにstyleにpointer-events: none;を付与 -->
<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -32px; left: -32px; pointer-events: none;">
<!-- SVGのanimateTransformを使用するとChromeで描画できなくなるためCSSアニメーションを使用している (Issue 14155) -->
<path
style="transform-origin: center; transform-box: fill-box;"
:transform="`translate(${particle.x} ${particle.y})`"
:style="{
'--translateX': particle.x + 'px',
'--translateY': particle.y + 'px',
'--duration': particle.dur + 'ms',
'--size': particle.size,
}"
:class="$style.particle"
:fill="particle.color"
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
>
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 0 0"
to="360 0 0"
:dur="`${particle.dur}ms`"
repeatCount="1"
additive="sum"
/>
<animateTransform
attributeName="transform"
attributeType="XML"
type="scale"
:values="`0; ${particle.size}; 0`"
:dur="`${particle.dur}ms`"
repeatCount="1"
additive="sum"
/>
</path>
></path>
</svg>
</span>
</template>
@ -130,4 +116,25 @@ onUnmounted(() => {
position: relative;
display: inline-block;
}
.particle {
transform-origin: center;
transform-box: fill-box;
translate: var(--translateX) var(--translateY);
animation: particleAnimation var(--duration) linear infinite;
}
@keyframes particleAnimation {
0% {
rotate: 0deg;
scale: 0;
}
50% {
scale: var(--size);
}
100% {
rotate: 360deg;
scale: 0;
}
}
</style>

View file

@ -96,7 +96,7 @@ const summary = ref<string | null>(null);
const name = ref(Date.now().toString());
const eyeCatchingImage = ref<Misskey.entities.DriveFile | null>(null);
const eyeCatchingImageId = ref<string | null>(null);
const font = ref('sans-serif');
const font = ref<'sans-serif' | 'serif'>('sans-serif');
const content = ref<Misskey.entities.Page['content']>([]);
const alignCenter = ref(false);
const hideTitleWhenPinned = ref(false);
@ -113,7 +113,7 @@ watch(eyeCatchingImageId, async () => {
}
});
function getSaveOptions() {
function getSaveOptions(): Misskey.entities.PagesCreateRequest {
return {
title: title.value.trim(),
name: name.value.trim(),
@ -128,80 +128,69 @@ function getSaveOptions() {
};
}
function save() {
async function save() {
const options = getSaveOptions();
const onError = err => {
if (err.id === '3d81ceae-475f-4600-b2a8-2bc116157532') {
if (err.info.param === 'name') {
os.alert({
type: 'error',
title: i18n.ts._pages.invalidNameTitle,
text: i18n.ts._pages.invalidNameText,
});
}
} else if (err.code === 'NAME_ALREADY_EXISTS') {
os.alert({
type: 'error',
text: i18n.ts._pages.nameAlreadyExists,
});
}
};
if (pageId.value) {
options.pageId = pageId.value;
misskeyApi('pages/update', options)
.then(page => {
currentName.value = name.value.trim();
os.alert({
type: 'success',
text: i18n.ts._pages.updated,
});
}).catch(onError);
const updateOptions: Misskey.entities.PagesUpdateRequest = {
pageId: pageId.value,
...options,
};
await os.apiWithDialog('pages/update', updateOptions, undefined, {
'2298a392-d4a1-44c5-9ebb-ac1aeaa5a9ab': {
title: i18n.ts.somethingHappened,
text: i18n.ts._pages.nameAlreadyExists,
},
});
currentName.value = name.value.trim();
} else {
misskeyApi('pages/create', options)
.then(created => {
pageId.value = created.id;
currentName.value = name.value.trim();
os.alert({
type: 'success',
text: i18n.ts._pages.created,
});
mainRouter.push(`/pages/edit/${pageId.value}`);
}).catch(onError);
const created = await os.apiWithDialog('pages/create', options, undefined, {
'4650348e-301c-499a-83c9-6aa988c66bc1': {
title: i18n.ts.somethingHappened,
text: i18n.ts._pages.nameAlreadyExists,
},
});
pageId.value = created.id;
currentName.value = name.value.trim();
mainRouter.replace(`/pages/edit/${pageId.value}`);
}
}
function del() {
os.confirm({
async function del() {
if (!pageId.value) return;
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.tsx.removeAreYouSure({ x: title.value.trim() }),
}).then(({ canceled }) => {
if (canceled) return;
misskeyApi('pages/delete', {
pageId: pageId.value,
}).then(() => {
os.alert({
type: 'success',
text: i18n.ts._pages.deleted,
});
mainRouter.push('/pages');
});
});
if (canceled) return;
await os.apiWithDialog('pages/delete', {
pageId: pageId.value,
});
mainRouter.replace('/pages');
}
function duplicate() {
async function duplicate() {
title.value = title.value + ' - copy';
name.value = name.value + '-copy';
misskeyApi('pages/create', getSaveOptions()).then(created => {
pageId.value = created.id;
currentName.value = name.value.trim();
os.alert({
type: 'success',
text: i18n.ts._pages.created,
});
mainRouter.push(`/pages/edit/${pageId.value}`);
const created = await os.apiWithDialog('pages/create', getSaveOptions(), undefined, {
'4650348e-301c-499a-83c9-6aa988c66bc1': {
title: i18n.ts.somethingHappened,
text: i18n.ts._pages.nameAlreadyExists,
},
});
pageId.value = created.id;
currentName.value = name.value.trim();
mainRouter.push(`/pages/edit/${pageId.value}`);
}
async function add() {
@ -216,7 +205,7 @@ async function add() {
content.value.push({ id, type });
}
function setEyeCatchingImage(img) {
function setEyeCatchingImage(img: Event) {
selectFile(img.currentTarget ?? img.target, null).then(file => {
eyeCatchingImageId.value = file.id;
});

View file

@ -266,7 +266,7 @@ function showMenu(ev: MouseEvent) {
if ($i && $i.id === page.value.userId) {
menuItems.push({
icon: 'ti ti-pencil',
text: i18n.ts._pages.editThisPage,
text: i18n.ts.edit,
action: () => router.push(`/pages/edit/${page.value.id}`),
});
@ -285,10 +285,6 @@ function showMenu(ev: MouseEvent) {
}
} else if ($i && $i.id !== page.value.userId) {
menuItems.push({
icon: 'ti ti-code',
text: i18n.ts._pages.viewSource,
action: () => router.push(`/@${props.username}/pages/${props.pageName}/view-source`),
}, {
icon: 'ti ti-exclamation-circle',
text: i18n.ts.reportAbuse,
action: reportAbuse,

View file

@ -17,10 +17,7 @@ export const page = (loader: AsyncComponentLoader) => defineAsyncComponent({
});
const routes: RouteDef[] = [{
path: '/@:initUser/pages/:initPageName/view-source',
component: page(() => import('@/pages/page-editor/page-editor.vue')),
}, {
path: '/@:username/pages/:pageName',
path: '/@:username/pages/:pageName(*)',
component: page(() => import('@/pages/page.vue')),
}, {
path: '/@:acct/following',

View file

@ -100,7 +100,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, defineAsyncComponent, ref, watch, shallowRef } from 'vue';
import { v4 as uuid } from 'uuid';
import XCommon from './_common_/common.vue';
import { deckStore, columnTypes, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
import { deckStore, columnTypes, addColumn as addColumnToStore, forceSaveDeck, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
import type { ColumnType } from './deck/deck-store.js';
import type { MenuItem } from '@/types/menu.js';
import XSidebar from '@/ui/_common_/navbar.vue';
@ -240,10 +240,15 @@ function changeProfile(ev: MouseEvent) {
title: i18n.ts._deck.profile,
minLength: 1,
});
if (canceled || name == null) return;
deckStore.set('profile', name);
unisonReload();
os.promiseDialog((async () => {
await deckStore.set('profile', name);
await forceSaveDeck();
})(), () => {
unisonReload();
});
},
});
}).then(() => {
@ -258,9 +263,18 @@ async function deleteProfile() {
});
if (canceled) return;
deleteProfile_(deckStore.state.profile);
deckStore.set('profile', 'default');
unisonReload();
os.promiseDialog((async () => {
if (deckStore.state.profile === 'default') {
await deckStore.set('columns', []);
await deckStore.set('layout', []);
await forceSaveDeck();
} else {
await deleteProfile_(deckStore.state.profile);
}
await deckStore.set('profile', 'default');
})(), () => {
unisonReload();
});
}
</script>

View file

@ -113,8 +113,7 @@ export const loadDeck = async () => {
deckStore.set('layout', deck.layout);
};
// TODO: deckがloadされていない状態でsaveすると意図せず上書きが発生するので対策する
export const saveDeck = throttle(1000, async () => {
export async function forceSaveDeck() {
await misskeyApi('i/registry/set', {
scope: ['client', 'deck', 'profiles'],
key: deckStore.state.profile,
@ -123,6 +122,11 @@ export const saveDeck = throttle(1000, async () => {
layout: deckStore.reactiveState.layout.value,
},
});
}
// TODO: deckがloadされていない状態でsaveすると意図せず上書きが発生するので対策する
export const saveDeck = throttle(1000, () => {
forceSaveDeck();
});
export async function getProfiles(): Promise<string[]> {