diff --git a/locales/index.d.ts b/locales/index.d.ts index c8a7b4c70de54bbe93f10af818a6a5ad72af0314..cd15bd968f127e64b4870e6ae9272aacd170cdd1 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1183,6 +1183,7 @@ export interface Locale { "remainingN": string; "overwriteContentConfirm": string; "seasonalScreenEffect": string; + "decorate": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 25a734ef4f4c6a33dd647fe00f2d0772f046d135..5537db9d56d01ccacb5f558d8930ab31ff101690 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1180,6 +1180,7 @@ reloadRequiredToApplySettings: "è¨å®šã®åæ˜ ã«ã¯ãƒªãƒãƒ¼ãƒ‰ãŒå¿…è¦ã§ã™ remainingN: "残り: {n}" overwriteContentConfirm: "ç¾åœ¨ã®å†…容ã«ä¸Šæ›¸ãã•ã‚Œã¾ã™ãŒã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ" seasonalScreenEffect: "å£ç¯€ã«å¿œã˜ãŸç”»é¢ã®æ¼”出" +decorate: "デコる" _announcement: forExistingUsers: "æ—¢å˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿" diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue similarity index 100% rename from packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue rename to packages/frontend/src/pages/settings/avatar-decoration.decoration.vue diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue similarity index 100% rename from packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue rename to packages/frontend/src/pages/settings/avatar-decoration.dialog.vue diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue similarity index 58% rename from packages/frontend/src/pages/settings/profile.avatar-decoration.vue rename to packages/frontend/src/pages/settings/avatar-decoration.vue index 8579acfed8d3afef27de4c7c9b117377a9fd1edb..6551fc917e50e7a337af0e5799fb305eac00c04f 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -4,51 +4,56 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="!loading" class="_gaps"> - <MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo> +<div> + <div v-if="!loading" class="_gaps"> + <MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo> - <div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s"> - <div>{{ i18n.ts.inUse }}</div> + <MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration/> + + <div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s"> + <div>{{ i18n.ts.inUse }}</div> + + <div :class="$style.decorations"> + <XDecoration + v-for="(avatarDecoration, i) in $i.avatarDecorations" + :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)" + :angle="avatarDecoration.angle" + :flipH="avatarDecoration.flipH" + :offsetX="avatarDecoration.offsetX" + :offsetY="avatarDecoration.offsetY" + :active="true" + @click="openDecoration(avatarDecoration, i)" + /> + </div> + + <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> + </div> <div :class="$style.decorations"> <XDecoration - v-for="(avatarDecoration, i) in $i.avatarDecorations" - :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)" - :angle="avatarDecoration.angle" - :flipH="avatarDecoration.flipH" - :offsetX="avatarDecoration.offsetX" - :offsetY="avatarDecoration.offsetY" - :active="true" - @click="openDecoration(avatarDecoration, i)" + v-for="avatarDecoration in avatarDecorations" + :key="avatarDecoration.id" + :decoration="avatarDecoration" + @click="openDecoration(avatarDecoration)" /> </div> - - <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> </div> - - <div :class="$style.decorations"> - <XDecoration - v-for="avatarDecoration in avatarDecorations" - :key="avatarDecoration.id" - :decoration="avatarDecoration" - @click="openDecoration(avatarDecoration)" - /> + <div v-else> + <MkLoading/> </div> </div> -<div v-else> - <MkLoading/> -</div> </template> <script lang="ts" setup> -import { ref, defineAsyncComponent } from 'vue'; +import { ref, defineAsyncComponent, computed } from 'vue'; import * as Misskey from 'misskey-js'; -import XDecoration from './profile.avatar-decoration.decoration.vue'; +import XDecoration from './avatar-decoration.decoration.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import MkInfo from '@/components/MkInfo.vue'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; const loading = ref(true); const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]); @@ -59,7 +64,7 @@ os.api('get-avatar-decorations').then(_avatarDecorations => { }); function openDecoration(avatarDecoration, index?: number) { - os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration.dialog.vue')), { + os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), { decoration: avatarDecoration, usingIndex: index, }, { @@ -115,9 +120,25 @@ function detachAllDecorations() { $i.avatarDecorations = []; }); } + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePageMetadata({ + title: i18n.ts.avatarDecorations, + icon: 'ti ti-sparkles', +}); </script> <style lang="scss" module> +.avatar { + display: inline-block; + width: 72px; + height: 72px; + margin: 16px auto; +} + .current { padding: 16px; border-radius: var(--radius); diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 83cd698e66d3e93f4a83c5270c5340e9d376528a..2ee19b96715133c02b98bf6ce05b6f76447c05e2 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -5,12 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps_m"> - <div :class="$style.avatarAndBanner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> + <div class="_panel"> + <div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> + <MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton> + </div> <div :class="$style.avatarContainer"> <MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration @click="changeAvatar"/> - <MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton> + <div class="_buttonsCenter"> + <MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton> + <MkButton primary rounded link to="/settings/avatar-decoration">{{ i18n.ts.decorate }} <i class="ti ti-sparkles"></i></MkButton> + </div> </div> - <MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton> </div> <MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']"> @@ -83,13 +88,6 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts._profile.metadataDescription }}</template> </FormSlot> - <MkFolder> - <template #icon><i class="ti ti-sparkles"></i></template> - <template #label>{{ i18n.ts.avatarDecorations }}</template> - - <XAvatarDecoration/> - </MkFolder> - <MkFolder> <template #label>{{ i18n.ts.advancedSettings }}</template> @@ -264,19 +262,19 @@ definePageMetadata({ </script> <style lang="scss" module> -.avatarAndBanner { +.banner { position: relative; + height: 130px; background-size: cover; background-position: center; - border: solid 1px var(--divider); - border-radius: 10px; + border-bottom: solid 1px var(--divider); overflow: clip; } .avatarContainer { - display: inline-block; + margin-top: -50px; + padding-bottom: 16px; text-align: center; - padding: 16px; } .avatar { diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index a7a53e97e601091db785604e3dda28253a53ca72..baee85866cfc45095399507e89a020d160a8adb6 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -54,6 +54,10 @@ export const routes = [{ path: '/profile', name: 'profile', component: page(() => import('./pages/settings/profile.vue')), + }, { + path: '/avatar-decoration', + name: 'avatarDecoration', + component: page(() => import('./pages/settings/avatar-decoration.vue')), }, { path: '/roles', name: 'roles',