mirror of
https://github.com/kokonect-link/cherrypick
synced 2024-11-24 07:06:26 +09:00
parent
40b82ba8a1
commit
35cd4adf86
@ -1,5 +1,7 @@
|
||||
---
|
||||
_lang_: "English"
|
||||
maxinumLayerError: "You cannot stack more than 6 layers. Please delete other layers."
|
||||
layer: "Layer"
|
||||
noteUpdatedAt: "Edited: {date} {time}"
|
||||
editReaction: "Edit reactions"
|
||||
removeReaction: "Remove reactions"
|
||||
|
@ -1,5 +1,7 @@
|
||||
_lang_: "日本語"
|
||||
|
||||
maxinumLayerError: "6枚以上重ねることはできません。他のレイヤーを削除してください。"
|
||||
layer: "レイヤー"
|
||||
noteUpdatedAt: "編集済み: {date} {time}"
|
||||
editReaction: "リアクションを編集"
|
||||
removeReaction: "リアクションを削除"
|
||||
|
@ -1,5 +1,7 @@
|
||||
---
|
||||
_lang_: "日本語 (関西弁)"
|
||||
maxinumLayerError: "6枚以上重ねられんで。他のレイヤーを削除してなー"
|
||||
layer: "レイヤー"
|
||||
headlineMisskey: "ノートでつながるネットワーク"
|
||||
introMisskey: "ようお越し!CherryPickは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作って、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「ツッコミ」機能で、皆のノートに素早く反応を追加したりもできるで✌\nほな、新しい世界を探検しよか🚀"
|
||||
poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>CherryPick</b>のサーバーのひとつなんやで。"
|
||||
|
@ -137,7 +137,7 @@ export const paramDef = {
|
||||
birthday: { ...birthdaySchema, nullable: true },
|
||||
lang: { type: 'string', enum: [null, ...Object.keys(langmap)] as string[], nullable: true },
|
||||
avatarId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
avatarDecorations: { type: 'array', maxItems: 1, items: {
|
||||
avatarDecorations: { type: 'array', maxItems: 5, items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', format: 'misskey:id' },
|
||||
|
@ -34,12 +34,24 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
v-if="showDecoration && (decoration || user.avatarDecorations.length > 0)"
|
||||
v-if="showDecoration && !decoration && user.avatarDecorations.length > 0"
|
||||
v-for="avatarDecoration in user.avatarDecorations"
|
||||
:key="avatarDecoration.id"
|
||||
:class="[$style.decoration]"
|
||||
:src="decoration?.url ?? user.avatarDecorations[0].url"
|
||||
:src="avatarDecoration.url"
|
||||
:style="{
|
||||
rotate: getDecorationAngle(),
|
||||
scale: getDecorationScale(),
|
||||
rotate: getDecorationAngle(avatarDecoration),
|
||||
scale: getDecorationScale(avatarDecoration),
|
||||
}"
|
||||
alt=""
|
||||
>
|
||||
<img
|
||||
v-else-if="showDecoration && decoration"
|
||||
:class="[$style.decoration]"
|
||||
:src="decoration?.url"
|
||||
:style="{
|
||||
rotate: getDecorationAngle(decoration),
|
||||
scale: getDecorationScale(decoration),
|
||||
}"
|
||||
alt=""
|
||||
>
|
||||
@ -105,27 +117,13 @@ function onClick(ev: MouseEvent): void {
|
||||
emit('click', ev);
|
||||
}
|
||||
|
||||
function getDecorationAngle() {
|
||||
let angle;
|
||||
if (props.decoration) {
|
||||
angle = props.decoration.angle ?? 0;
|
||||
} else if (props.user.avatarDecorations.length > 0) {
|
||||
angle = props.user.avatarDecorations[0].angle ?? 0;
|
||||
} else {
|
||||
angle = 0;
|
||||
}
|
||||
function getDecorationAngle(avatarDecoration) {
|
||||
let angle = avatarDecoration.angle ?? 0;
|
||||
return angle === 0 ? undefined : `${angle * 360}deg`;
|
||||
}
|
||||
|
||||
function getDecorationScale() {
|
||||
let scaleX;
|
||||
if (props.decoration) {
|
||||
scaleX = props.decoration.flipH ? -1 : 1;
|
||||
} else if (props.user.avatarDecorations.length > 0) {
|
||||
scaleX = props.user.avatarDecorations[0].flipH ? -1 : 1;
|
||||
} else {
|
||||
scaleX = 1;
|
||||
}
|
||||
function getDecorationScale(avatarDecoration) {
|
||||
let scaleX = avatarDecoration.flipH ? -1 : 1;
|
||||
return scaleX === 1 ? undefined : `${scaleX} 1`;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decoration="{ url: decoration.url, angle, flipH }" forceShowDecoration/>
|
||||
</div>
|
||||
<div class="_gaps_s">
|
||||
<MkRadios v-model="insertLayer">
|
||||
<template #label>{{ i18n.ts.layer }}</template>
|
||||
<option value="0">1</option>
|
||||
<option value="1">2</option>
|
||||
<option value="2">3</option>
|
||||
<option value="3">4</option>
|
||||
<option value="4">5</option>
|
||||
</MkRadios>
|
||||
<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
|
||||
<template #label>{{ i18n.ts.angle }}</template>
|
||||
</MkRange>
|
||||
@ -41,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef, ref, computed } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
@ -63,6 +72,18 @@ const emit = defineEmits<{
|
||||
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const using = computed(() => $i.avatarDecorations.some(x => x.id === props.decoration.id));
|
||||
const layerNum = (() => {
|
||||
let result = -1;
|
||||
$i.avatarDecorations.some((x, i) => {
|
||||
if (x.id === props.decoration.id) {
|
||||
result = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
})();
|
||||
const insertLayer = ref(layerNum === -1 ? String($i.avatarDecorations.length) : String(layerNum));
|
||||
const angle = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).angle ?? 0 : 0);
|
||||
const flipH = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).flipH ?? false : false);
|
||||
|
||||
@ -76,19 +97,22 @@ async function attach() {
|
||||
angle: angle.value,
|
||||
flipH: flipH.value,
|
||||
};
|
||||
const updatedDecorations = $i.avatarDecorations.toSpliced(layerNum, layerNum === -1 ? 0 : 1).toSpliced(Number(insertLayer.value), 0, decoration);
|
||||
await os.apiWithDialog('i/update', {
|
||||
avatarDecorations: [decoration],
|
||||
avatarDecorations: updatedDecorations,
|
||||
});
|
||||
$i.avatarDecorations = [decoration];
|
||||
$i.avatarDecorations = updatedDecorations;
|
||||
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
async function detach() {
|
||||
if (layerNum === -1) return;
|
||||
const deletedDecorations = $i.avatarDecorations.toSpliced(layerNum, 1);
|
||||
await os.apiWithDialog('i/update', {
|
||||
avatarDecorations: [],
|
||||
avatarDecorations: deletedDecorations,
|
||||
});
|
||||
$i.avatarDecorations = [];
|
||||
$i.avatarDecorations = deletedDecorations;
|
||||
|
||||
dialog.value.close();
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
|
||||
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decoration="{ url: avatarDecoration.url }" forceShowDecoration/>
|
||||
<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
|
||||
<span v-if="$i.avatarDecorations.some(x => x.id === avatarDecoration.id)" :class="$style.layerNum">{{ indexOfDecoration(v => v.id === avatarDecoration.id) + 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
@ -163,6 +164,18 @@ watch(() => profile, () => {
|
||||
deep: true,
|
||||
});
|
||||
|
||||
function indexOfDecoration(f) {
|
||||
let result = -1;
|
||||
$i.avatarDecorations.some((e, i) => {
|
||||
if (f(e)) {
|
||||
result = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
|
||||
const fieldEditMode = ref(false);
|
||||
|
||||
@ -277,6 +290,14 @@ function changeBanner(ev) {
|
||||
}
|
||||
|
||||
function openDecoration(avatarDecoration) {
|
||||
if (indexOfDecoration(v => v.id === avatarDecoration.id) === -1 && $i.avatarDecorations.length >= 5) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
title: i18n.ts.error,
|
||||
text: i18n.ts.maxinumLayerError
|
||||
});
|
||||
return;
|
||||
}
|
||||
os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration-dialog.vue')), {
|
||||
decoration: avatarDecoration,
|
||||
}, {}, 'closed');
|
||||
@ -415,4 +436,11 @@ definePageMetadata({
|
||||
bottom: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.layerNum {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user