merge: Feat: Implement "Show Below Avatar" for Avatar Decorations (!645)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/645 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
commit
ac1e5a0fb5
|
@ -1279,6 +1279,7 @@ detach: "Remove"
|
||||||
detachAll: "Remove All"
|
detachAll: "Remove All"
|
||||||
angle: "Angle"
|
angle: "Angle"
|
||||||
flip: "Flip"
|
flip: "Flip"
|
||||||
|
showBelowAvatar: "Show Below Avatar"
|
||||||
showAvatarDecorations: "Show avatar decorations"
|
showAvatarDecorations: "Show avatar decorations"
|
||||||
releaseToRefresh: "Release to refresh"
|
releaseToRefresh: "Release to refresh"
|
||||||
refreshing: "Refreshing..."
|
refreshing: "Refreshing..."
|
||||||
|
|
|
@ -5133,6 +5133,10 @@ export interface Locale extends ILocale {
|
||||||
* 反転
|
* 反転
|
||||||
*/
|
*/
|
||||||
"flip": string;
|
"flip": string;
|
||||||
|
/**
|
||||||
|
* アイコンの後ろに表示
|
||||||
|
*/
|
||||||
|
"showBelowAvatar": string;
|
||||||
/**
|
/**
|
||||||
* アイコンのデコレーションを表示
|
* アイコンのデコレーションを表示
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1279,6 +1279,7 @@ detach: "外す"
|
||||||
detachAll: "全て外す"
|
detachAll: "全て外す"
|
||||||
angle: "角度"
|
angle: "角度"
|
||||||
flip: "反転"
|
flip: "反転"
|
||||||
|
showBelowAvatar: "アイコンの後ろに表示"
|
||||||
showAvatarDecorations: "アイコンのデコレーションを表示"
|
showAvatarDecorations: "アイコンのデコレーションを表示"
|
||||||
releaseToRefresh: "離してリロード"
|
releaseToRefresh: "離してリロード"
|
||||||
refreshing: "リロード中"
|
refreshing: "リロード中"
|
||||||
|
|
|
@ -525,6 +525,7 @@ export class UserEntityService implements OnModuleInit {
|
||||||
flipH: ud.flipH || undefined,
|
flipH: ud.flipH || undefined,
|
||||||
offsetX: ud.offsetX || undefined,
|
offsetX: ud.offsetX || undefined,
|
||||||
offsetY: ud.offsetY || undefined,
|
offsetY: ud.offsetY || undefined,
|
||||||
|
showBelow: ud.showBelow || undefined,
|
||||||
url: decorations.find(d => d.id === ud.id)!.url,
|
url: decorations.find(d => d.id === ud.id)!.url,
|
||||||
}))) : [],
|
}))) : [],
|
||||||
isBot: user.isBot,
|
isBot: user.isBot,
|
||||||
|
|
|
@ -170,6 +170,7 @@ export class MiUser {
|
||||||
flipH?: boolean;
|
flipH?: boolean;
|
||||||
offsetX?: number;
|
offsetX?: number;
|
||||||
offsetY?: number;
|
offsetY?: number;
|
||||||
|
showBelow?: boolean;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
|
|
|
@ -104,6 +104,10 @@ export const packedUserLiteSchema = {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
nullable: false, optional: true,
|
nullable: false, optional: true,
|
||||||
},
|
},
|
||||||
|
showBelow: {
|
||||||
|
type: 'boolean',
|
||||||
|
nullable: false, optional: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -159,6 +159,7 @@ export const paramDef = {
|
||||||
flipH: { type: 'boolean', nullable: true },
|
flipH: { type: 'boolean', nullable: true },
|
||||||
offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
|
offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
|
||||||
offsetY: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
|
offsetY: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
|
||||||
|
showBelow: { type: 'boolean', nullable: true },
|
||||||
},
|
},
|
||||||
required: ['id'],
|
required: ['id'],
|
||||||
} },
|
} },
|
||||||
|
@ -417,6 +418,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
flipH: d.flipH ?? false,
|
flipH: d.flipH ?? false,
|
||||||
offsetX: d.offsetX ?? 0,
|
offsetX: d.offsetX ?? 0,
|
||||||
offsetY: d.offsetY ?? 0,
|
offsetY: d.offsetY ?? 0,
|
||||||
|
showBelow: d.showBelow ?? false,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
rotate: getDecorationAngle(decoration),
|
rotate: getDecorationAngle(decoration),
|
||||||
scale: getDecorationScale(decoration),
|
scale: getDecorationScale(decoration),
|
||||||
translate: getDecorationOffset(decoration),
|
translate: getDecorationOffset(decoration),
|
||||||
|
zIndex: getDecorationZIndex(decoration),
|
||||||
}"
|
}"
|
||||||
alt=""
|
alt=""
|
||||||
>
|
>
|
||||||
|
@ -113,6 +114,10 @@ function getDecorationOffset(decoration: Omit<Misskey.entities.UserDetailed['ava
|
||||||
return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`;
|
return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDecorationZIndex(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
|
||||||
|
return decoration.showBelow ? '-1' : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const color = ref<string | undefined>();
|
const color = ref<string | undefined>();
|
||||||
|
|
||||||
watch(() => props.user.avatarBlurhash, () => {
|
watch(() => props.user.avatarBlurhash, () => {
|
||||||
|
@ -159,6 +164,7 @@ watch(() => props.user.avatarBlurhash, () => {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 100%; // sharkey: controlled by square avatars setting!
|
border-radius: 100%; // sharkey: controlled by square avatars setting!
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
|
z-index: 0; // sharkey: starts stacking context to help with showing decorations behind the avatar
|
||||||
}
|
}
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
|
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@click="emit('click')"
|
@click="emit('click')"
|
||||||
>
|
>
|
||||||
<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
|
<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
|
||||||
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY }]" forceShowDecoration/>
|
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY, showBelow }]" forceShowDecoration/>
|
||||||
<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
|
<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -32,6 +32,7 @@ const props = defineProps<{
|
||||||
flipH?: boolean;
|
flipH?: boolean;
|
||||||
offsetX?: number;
|
offsetX?: number;
|
||||||
offsetY?: number;
|
offsetY?: number;
|
||||||
|
showBelow?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -29,6 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkRange v-model="offsetY" continuousUpdate :min="-0.25" :max="0.25" :step="0.025" :textConverter="(v) => `${Math.floor(v * 100)}%`">
|
<MkRange v-model="offsetY" continuousUpdate :min="-0.25" :max="0.25" :step="0.025" :textConverter="(v) => `${Math.floor(v * 100)}%`">
|
||||||
<template #label>Y {{ i18n.ts.position }}</template>
|
<template #label>Y {{ i18n.ts.position }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
|
<MkSwitch v-model="showBelow">
|
||||||
|
<template #label>{{ i18n.ts.showBelowAvatar }}</template>
|
||||||
|
</MkSwitch>
|
||||||
<MkSwitch v-model="flipH">
|
<MkSwitch v-model="flipH">
|
||||||
<template #label>{{ i18n.ts.flip }}</template>
|
<template #label>{{ i18n.ts.flip }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
@ -71,12 +74,14 @@ const emit = defineEmits<{
|
||||||
flipH: boolean;
|
flipH: boolean;
|
||||||
offsetX: number;
|
offsetX: number;
|
||||||
offsetY: number;
|
offsetY: number;
|
||||||
|
showBelow: boolean;
|
||||||
}): void;
|
}): void;
|
||||||
(ev: 'update', payload: {
|
(ev: 'update', payload: {
|
||||||
angle: number;
|
angle: number;
|
||||||
flipH: boolean;
|
flipH: boolean;
|
||||||
offsetX: number;
|
offsetX: number;
|
||||||
offsetY: number;
|
offsetY: number;
|
||||||
|
showBelow: boolean;
|
||||||
}): void;
|
}): void;
|
||||||
(ev: 'detach'): void;
|
(ev: 'detach'): void;
|
||||||
}>();
|
}>();
|
||||||
|
@ -87,6 +92,7 @@ const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIn
|
||||||
const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
|
const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
|
||||||
const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0);
|
const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0);
|
||||||
const offsetY = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetY : null) ?? 0);
|
const offsetY = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetY : null) ?? 0);
|
||||||
|
const showBelow = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].showBelow : null) ?? false);
|
||||||
|
|
||||||
const decorationsForPreview = computed(() => {
|
const decorationsForPreview = computed(() => {
|
||||||
const decoration = {
|
const decoration = {
|
||||||
|
@ -96,6 +102,7 @@ const decorationsForPreview = computed(() => {
|
||||||
flipH: flipH.value,
|
flipH: flipH.value,
|
||||||
offsetX: offsetX.value,
|
offsetX: offsetX.value,
|
||||||
offsetY: offsetY.value,
|
offsetY: offsetY.value,
|
||||||
|
showBelow: showBelow.value,
|
||||||
};
|
};
|
||||||
const decorations = [...$i.avatarDecorations];
|
const decorations = [...$i.avatarDecorations];
|
||||||
if (props.usingIndex != null) {
|
if (props.usingIndex != null) {
|
||||||
|
@ -116,6 +123,7 @@ async function update() {
|
||||||
flipH: flipH.value,
|
flipH: flipH.value,
|
||||||
offsetX: offsetX.value,
|
offsetX: offsetX.value,
|
||||||
offsetY: offsetY.value,
|
offsetY: offsetY.value,
|
||||||
|
showBelow: showBelow.value,
|
||||||
});
|
});
|
||||||
dialog.value.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
@ -126,6 +134,7 @@ async function attach() {
|
||||||
flipH: flipH.value,
|
flipH: flipH.value,
|
||||||
offsetX: offsetX.value,
|
offsetX: offsetX.value,
|
||||||
offsetY: offsetY.value,
|
offsetY: offsetY.value,
|
||||||
|
showBelow: showBelow.value,
|
||||||
});
|
});
|
||||||
dialog.value.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:flipH="avatarDecoration.flipH"
|
:flipH="avatarDecoration.flipH"
|
||||||
:offsetX="avatarDecoration.offsetX"
|
:offsetX="avatarDecoration.offsetX"
|
||||||
:offsetY="avatarDecoration.offsetY"
|
:offsetY="avatarDecoration.offsetY"
|
||||||
|
:showBelow="avatarDecoration.showBelow"
|
||||||
:active="true"
|
:active="true"
|
||||||
@click="openDecoration(avatarDecoration, i)"
|
@click="openDecoration(avatarDecoration, i)"
|
||||||
/>
|
/>
|
||||||
|
@ -78,6 +79,7 @@ function openDecoration(avatarDecoration, index?: number) {
|
||||||
flipH: payload.flipH,
|
flipH: payload.flipH,
|
||||||
offsetX: payload.offsetX,
|
offsetX: payload.offsetX,
|
||||||
offsetY: payload.offsetY,
|
offsetY: payload.offsetY,
|
||||||
|
showBelow: payload.showBelow,
|
||||||
};
|
};
|
||||||
const update = [...$i.avatarDecorations, decoration];
|
const update = [...$i.avatarDecorations, decoration];
|
||||||
await os.apiWithDialog('i/update', {
|
await os.apiWithDialog('i/update', {
|
||||||
|
@ -92,6 +94,7 @@ function openDecoration(avatarDecoration, index?: number) {
|
||||||
flipH: payload.flipH,
|
flipH: payload.flipH,
|
||||||
offsetX: payload.offsetX,
|
offsetX: payload.offsetX,
|
||||||
offsetY: payload.offsetY,
|
offsetY: payload.offsetY,
|
||||||
|
showBelow: payload.showBelow,
|
||||||
};
|
};
|
||||||
const update = [...$i.avatarDecorations];
|
const update = [...$i.avatarDecorations];
|
||||||
update[index] = decoration;
|
update[index] = decoration;
|
||||||
|
|
|
@ -3814,6 +3814,7 @@ export type components = {
|
||||||
url: string;
|
url: string;
|
||||||
offsetX?: number;
|
offsetX?: number;
|
||||||
offsetY?: number;
|
offsetY?: number;
|
||||||
|
showBelow?: boolean;
|
||||||
}[];
|
}[];
|
||||||
/** @default false */
|
/** @default false */
|
||||||
isAdmin?: boolean;
|
isAdmin?: boolean;
|
||||||
|
@ -20227,6 +20228,7 @@ export type operations = {
|
||||||
flipH?: boolean | null;
|
flipH?: boolean | null;
|
||||||
offsetX?: number | null;
|
offsetX?: number | null;
|
||||||
offsetY?: number | null;
|
offsetY?: number | null;
|
||||||
|
showBelow?: boolean | null;
|
||||||
})[];
|
})[];
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
bannerId?: string | null;
|
bannerId?: string | null;
|
||||||
|
|
Loading…
Reference in New Issue