Merge tag '2025.2.0' into merge/2024-02-03
This commit is contained in:
commit
708debf5d5
35 changed files with 127 additions and 191 deletions
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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[]> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue