summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-10-22 13:02:24 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2023-10-22 13:02:24 +0900
commit4eaa02d25f83eff38cecd6db1724c8626dc3af2e (patch)
treebeedb76bfeae7a11109f25758f55f097f4c66d24 /packages/frontend/src
parentUpdate CHANGELOG.md (diff)
downloadsharkey-4eaa02d25f83eff38cecd6db1724c8626dc3af2e.tar.gz
sharkey-4eaa02d25f83eff38cecd6db1724c8626dc3af2e.tar.bz2
sharkey-4eaa02d25f83eff38cecd6db1724c8626dc3af2e.zip
enhance: improve avatar decoration
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkRange.vue5
-rw-r--r--packages/frontend/src/components/global/MkAvatar.vue43
-rw-r--r--packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue114
-rw-r--r--packages/frontend/src/pages/settings/profile.vue20
4 files changed, 166 insertions, 16 deletions
diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue
index 2cfc27ceee..04390c6f0c 100644
--- a/packages/frontend/src/components/MkRange.vue
+++ b/packages/frontend/src/components/MkRange.vue
@@ -34,6 +34,7 @@ const props = withDefaults(defineProps<{
textConverter?: (value: number) => string,
showTicks?: boolean;
easing?: boolean;
+ continuousUpdate?: boolean;
}>(), {
step: 1,
textConverter: (v) => v.toString(),
@@ -123,6 +124,10 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => {
const pointerX = ev.touches && ev.touches.length > 0 ? ev.touches[0].clientX : ev.clientX;
const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth / 2));
rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth)));
+
+ if (props.continuousUpdate) {
+ emit('update:modelValue', finalValue.value);
+ }
};
let beforeValue = finalValue.value;
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index de684425a2..e22ed29b7e 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -23,7 +23,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- <img v-if="decoration || user.avatarDecorations.length > 0" :class="[$style.decoration]" :src="decoration ?? user.avatarDecorations[0].url" alt="">
+ <img
+ v-if="decoration || user.avatarDecorations.length > 0"
+ :class="[$style.decoration]"
+ :src="decoration?.url ?? user.avatarDecorations[0].url"
+ :style="{
+ rotate: getDecorationAngle(),
+ scale: getDecorationScale(),
+ }"
+ alt=""
+ >
</component>
</template>
@@ -48,12 +57,18 @@ const props = withDefaults(defineProps<{
link?: boolean;
preview?: boolean;
indicator?: boolean;
- decoration?: string;
+ decoration?: {
+ url: string;
+ angle?: number;
+ flipH?: boolean;
+ flipV?: boolean;
+ };
}>(), {
target: null,
link: false,
preview: false,
indicator: false,
+ decoration: undefined,
});
const emit = defineEmits<{
@@ -73,6 +88,30 @@ 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;
+ }
+ 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;
+ }
+ return scaleX === 1 ? undefined : `${scaleX} 1`;
+}
+
let color = $ref<string | undefined>();
watch(() => props.user.avatarBlurhash, () => {
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
new file mode 100644
index 0000000000..c4bdf4a49b
--- /dev/null
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
@@ -0,0 +1,114 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+ ref="dialog"
+ :width="400"
+ :height="450"
+ @close="cancel"
+ @closed="emit('closed')"
+>
+ <template #header>{{ i18n.ts.avatarDecorations }}</template>
+
+ <div>
+ <MkSpacer :marginMin="20" :marginMax="28">
+ <div style="text-align: center;">
+ <div :class="$style.name">{{ decoration.name }}</div>
+ <MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decoration="{ url: decoration.url, angle, flipH }"/>
+ </div>
+ <div class="_gaps_s">
+ <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>
+ <MkSwitch v-model="flipH">
+ <template #label>{{ i18n.ts.flip }}</template>
+ </MkSwitch>
+ </div>
+ </MkSpacer>
+
+ <div :class="$style.footer" class="_buttonsCenter">
+ <MkButton v-if="using" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
+ <MkButton v-if="using" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
+ <MkButton v-else primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
+ </div>
+ </div>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, ref, computed } from 'vue';
+import MkButton from '@/components/MkButton.vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import MkFolder from '@/components/MkFolder.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import MkRange from '@/components/MkRange.vue';
+import { $i } from '@/account.js';
+
+const props = defineProps<{
+ decoration: {
+ id: string;
+ url: string;
+ }
+}>();
+
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+const using = computed(() => $i.avatarDecorations.some(x => x.id === props.decoration.id));
+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);
+
+function cancel() {
+ dialog.value.close();
+}
+
+async function attach() {
+ const decoration = {
+ id: props.decoration.id,
+ angle: angle.value,
+ flipH: flipH.value,
+ };
+ await os.apiWithDialog('i/update', {
+ avatarDecorations: [decoration],
+ });
+ $i.avatarDecorations = [decoration];
+
+ dialog.value.close();
+}
+
+async function detach() {
+ await os.apiWithDialog('i/update', {
+ avatarDecorations: [],
+ });
+ $i.avatarDecorations = [];
+
+ dialog.value.close();
+}
+</script>
+
+<style lang="scss" module>
+.name {
+ position: relative;
+ z-index: 10;
+ font-weight: bold;
+ margin-bottom: 28px;
+}
+
+.footer {
+ position: sticky;
+ bottom: 0;
+ left: 0;
+ padding: 12px;
+ border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--blur, blur(15px));
+ backdrop-filter: var(--blur, blur(15px));
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index f3d0c12dce..8d9c3cf730 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -92,10 +92,10 @@ SPDX-License-Identifier: AGPL-3.0-only
v-for="avatarDecoration in avatarDecorations"
:key="avatarDecoration.id"
:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
- @click="toggleDecoration(avatarDecoration)"
+ @click="openDecoration(avatarDecoration)"
>
<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="2 / 3">{{ avatarDecoration.name }}</MkCondensedLine></div>
- <MkAvatar style="width: 64px; height: 64px;" :user="$i" :decoration="avatarDecoration.url"/>
+ <MkAvatar style="width: 64px; height: 64px;" :user="$i" :decoration="{ url: avatarDecoration.url }"/>
</div>
</div>
</MkFolder>
@@ -266,18 +266,10 @@ function changeBanner(ev) {
});
}
-function toggleDecoration(avatarDecoration) {
- if ($i.avatarDecorations.some(x => x.id === avatarDecoration.id)) {
- os.apiWithDialog('i/update', {
- avatarDecorations: [],
- });
- $i.avatarDecorations = [];
- } else {
- os.apiWithDialog('i/update', {
- avatarDecorations: [avatarDecoration.id],
- });
- $i.avatarDecorations.push(avatarDecoration);
- }
+function openDecoration(avatarDecoration) {
+ os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration-dialog.vue')), {
+ decoration: avatarDecoration,
+ }, {}, 'closed');
}
const headerActions = $computed(() => []);