bring back profile settings

This commit is contained in:
kakkokari-gtyih 2024-08-03 21:21:45 +09:00
parent fae4e6cba8
commit 04ac9f35e2
6 changed files with 167 additions and 32 deletions

19
locales/index.d.ts vendored
View File

@ -5239,6 +5239,21 @@ export interface Locale extends ILocale {
*/ */
"description": string; "description": string;
}; };
"_profileSettings": {
/**
*
*/
"title": string;
/**
*
*/
"description": string;
/**
*
*
*/
"youCanChangeThemLater": string;
};
"_note": { "_note": {
/** /**
* *
@ -5487,7 +5502,7 @@ export interface Locale extends ILocale {
*/ */
"welcomeToX": ParameterizedString<"name">; "welcomeToX": ParameterizedString<"name">;
/** /**
* {name}使💦Misskeyの基本的な使い方を学びましょ * {name}使使
*/ */
"description": ParameterizedString<"name">; "description": ParameterizedString<"name">;
/** /**
@ -5519,7 +5534,7 @@ export interface Locale extends ILocale {
*/ */
"profile": string; "profile": string;
/** /**
* *
*/ */
"profileDescription": string; "profileDescription": string;
/** /**

View File

@ -1314,6 +1314,10 @@ _initialTutorial:
_landing: _landing:
title: "チュートリアルへようこそ" title: "チュートリアルへようこそ"
description: "ここでは、Misskeyの基本的な使い方や機能を確認できます。" description: "ここでは、Misskeyの基本的な使い方や機能を確認できます。"
_profileSettings:
title: "プロフィール設定"
description: "まずは基本的なプロフィールを設定して、ユーザーにあなたのことを知ってもらえるようにしましょう。"
youCanChangeThemLater: "ここで設定した項目は後でいつでも変更できます。\nチュートリアル終了後には、更に多彩なプロフィール設定をご利用いただけます"
_note: _note:
title: "ノートって何?" title: "ノートって何?"
description: "Misskeyでの投稿は「ート」と呼びます。ートはタイムラインに時系列で並んでいて、リアルタイムで更新されていきます。" description: "Misskeyでの投稿は「ート」と呼びます。ートはタイムラインに時系列で並んでいて、リアルタイムで更新されていきます。"
@ -1383,7 +1387,7 @@ _initialTutorial:
_onboardingLanding: _onboardingLanding:
accountCreated: "アカウントの作成が完了しました!" accountCreated: "アカウントの作成が完了しました!"
welcomeToX: "ようこそ、{name}へ!" welcomeToX: "ようこそ、{name}へ!"
description: "「{name}に登録したは良いものの、どう使えばいいか分からない…💦」といったことを防ぐために、まずはMisskeyの基本的な使い方を学びましょう。" description: "プロフィールを設定したり、{name}の基本的な使い方を学んだりして、すぐに使い始められるようにしましょう。"
takesAbout: "このチュートリアルの所要時間は{min}分程度です。\nチュートリアルを完了すると実績が解除されます。" takesAbout: "このチュートリアルの所要時間は{min}分程度です。\nチュートリアルを完了すると実績が解除されます。"
adminForcesToTakeTutorial: "このサーバーの管理者は新規ユーザーにチュートリアルを完了することを義務付けています。\nチュートリアルを完了するまでMisskeyを使い始めることはできません。" adminForcesToTakeTutorial: "このサーバーの管理者は新規ユーザーにチュートリアルを完了することを義務付けています。\nチュートリアルを完了するまでMisskeyを使い始めることはできません。"
_onboardingDone: _onboardingDone:
@ -1391,7 +1395,7 @@ _initialTutorial:
backToOriginalPath: "元のページに戻る" backToOriginalPath: "元のページに戻る"
backToOriginalPathDescription: "あなたがアクセスしようとしていたページに戻ります。" backToOriginalPathDescription: "あなたがアクセスしようとしていたページに戻ります。"
profile: "プロフィール設定" profile: "プロフィール設定"
profileDescription: "他のユーザーが親しみやすいように、プロフィールをつくりましょう。" profileDescription: "プロフィールをかんぺきにして、自分をアピールしましょう。"
exploreDescription: "人気のノートやユーザーを見つけて交流をはじめましょう。" exploreDescription: "人気のノートやユーザーを見つけて交流をはじめましょう。"
goToTimeline: "ホーム画面に進む" goToTimeline: "ホーム画面に進む"
goToTimelineDescription: "設定等を行わず、通常のホーム画面(タイムライン)に進みます。" goToTimelineDescription: "設定等を行わず、通常のホーム画面(タイムライン)に進みます。"

View File

@ -0,0 +1,102 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="_gaps">
<div style="word-break: auto-phrase; text-align: center; padding: 0 16px;">{{ i18n.ts._initialTutorial._profileSettings.description }}</div>
<FormSlot>
<template #label>{{ i18n.ts.avatar }}</template>
<div v-adaptive-bg :class="$style.avatarSection" class="_panel">
<MkAvatar :class="$style.avatar" :user="$i" @click="setAvatar"/>
<div style="margin-top: 16px;">
<MkButton primary rounded inline @click="setAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
</div>
</div>
</FormSlot>
<MkInput v-model="name" :max="30" manualSave>
<template #label>{{ i18n.ts._profile.name }}</template>
</MkInput>
<MkTextarea v-model="description" :max="500" tall manualSave>
<template #label>{{ i18n.ts._profile.description }}</template>
</MkTextarea>
<MkInfo>{{ i18n.ts._initialTutorial._profileSettings.youCanChangeThemLater }}</MkInfo>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import FormSlot from '@/components/form/slot.vue';
import MkInfo from '@/components/MkInfo.vue';
import { selectFile } from '@/scripts/select-file.js';
import * as os from '@/os.js';
import { signinRequired } from '@/account.js';
const $i = signinRequired();
const name = ref($i.name ?? '');
const description = ref($i.description ?? '');
watch(name, () => {
os.apiWithDialog('i/update', {
// null??使
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
name: name.value || null,
});
});
watch(description, () => {
os.apiWithDialog('i/update', {
// null??使
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
description: description.value || null,
});
});
function setAvatar(ev: MouseEvent) {
selectFile(ev.currentTarget ?? ev.target).then(async (file) => {
let originalOrCropped = file;
const { canceled } = await os.confirm({
type: 'question',
text: i18n.ts.cropImageAsk,
okText: i18n.ts.cropYes,
cancelText: i18n.ts.cropNo,
});
if (!canceled) {
originalOrCropped = await os.cropImage(file, {
aspectRatio: 1,
});
}
const i = await os.apiWithDialog('i/update', {
avatarId: originalOrCropped.id,
});
$i.avatarId = i.avatarId;
$i.avatarUrl = i.avatarUrl;
});
}
</script>
<style lang="scss" module>
.avatarSection {
text-align: center;
padding: 20px;
}
.avatar {
width: 100px;
height: 100px;
background: var(--bg);
}
</style>

View File

@ -34,11 +34,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-else-if="page === 1" key="tutorialPage_1" :class="$style.pageContainer"> <div v-else-if="page === 1" key="tutorialPage_1" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<XNote phase="aboutNote"/> <XProfileSettings/>
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<div v-else-if="page === 2" key="tutorialPage_2" :class="$style.pageContainer"> <div v-else-if="page === 2" key="tutorialPage_2" :class="$style.pageContainer">
<div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<XNote phase="aboutNote"/>
</MkSpacer>
</div>
</div>
<div v-else-if="page === 3" key="tutorialPage_3" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<div class="_gaps"> <div class="_gaps">
@ -48,28 +55,28 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<div v-else-if="page === 3" key="tutorialPage_3" :class="$style.pageContainer"> <div v-else-if="page === 4" key="tutorialPage_4" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<XTimeline/> <XTimeline/>
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<div v-else-if="page === 4" key="tutorialPage_4" :class="$style.pageContainer"> <div v-else-if="page === 5" key="tutorialPage_5" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<XFollowUsers/> <XFollowUsers/>
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<div v-else-if="page === 5" key="tutorialPage_5" :class="$style.pageContainer"> <div v-else-if="page === 6" key="tutorialPage_6" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<XPostNote/> <XPostNote/>
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<div v-else-if="page === 6" key="tutorialPage_6" :class="$style.pageContainer"> <div v-else-if="page === 7" key="tutorialPage_7" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<div class="_gaps"> <div class="_gaps">
@ -79,7 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<div v-else-if="page === 7" key="tutorialPage_7" :class="$style.pageContainer"> <div v-else-if="page === 8" key="tutorialPage_8" :class="$style.pageContainer">
<div :class="$style.pageRoot"> <div :class="$style.pageRoot">
<MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
<div class="_gaps"> <div class="_gaps">
@ -88,7 +95,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSpacer> </MkSpacer>
</div> </div>
</div> </div>
<slot v-else-if="page === 8" key="tutorialPage_8" name="finish" :close="() => emit('close')" :prev="prev"> <slot v-else-if="page === 9" key="tutorialPage_9" name="finish" :close="() => emit('close')" :prev="prev">
<div :class="$style.centerPage"> <div :class="$style.centerPage">
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/> <MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
<MkSpacer :marginMin="20" :marginMax="28"> <MkSpacer :marginMin="20" :marginMax="28">
@ -124,13 +131,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts"> <script lang="ts">
// //
export const MAX_PAGE = 8; export const MAX_PAGE = 9;
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch } from 'vue'; import { ref, computed, watch } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import XProfileSettings from '@/components/MkTutorial.ProfileSettings.vue';
import XNote from '@/components/MkTutorial.Note.vue'; import XNote from '@/components/MkTutorial.Note.vue';
import XTimeline from '@/components/MkTutorial.Timeline.vue'; import XTimeline from '@/components/MkTutorial.Timeline.vue';
import XFollowUsers from '@/components/MkTutorial.FollowUsers.vue'; import XFollowUsers from '@/components/MkTutorial.FollowUsers.vue';
@ -171,9 +179,9 @@ const isReactionTutorialPushed = ref<boolean>(isTest);
const isSensitiveTutorialSucceeded = ref<boolean>(isTest); const isSensitiveTutorialSucceeded = ref<boolean>(isTest);
const canContinue = computed(() => { const canContinue = computed(() => {
if (page.value === 2) { if (page.value === 3) {
return isReactionTutorialPushed.value; return isReactionTutorialPushed.value;
} else if (page.value === 6) { } else if (page.value === 7) {
return isSensitiveTutorialSucceeded.value; return isSensitiveTutorialSucceeded.value;
} else { } else {
return true; return true;
@ -181,9 +189,11 @@ const canContinue = computed(() => {
}); });
function next() { function next() {
if (page.value === 3 && !props.withSetup) { if (page.value === 0 && !props.withSetup) {
page.value += 2; page.value += 2;
} else if (page.value === 6 && !props.withSetup) { } else if (page.value === 4 && !props.withSetup) {
page.value += 2;
} else if (page.value === 7 && !props.withSetup) {
page.value += 2; page.value += 2;
} else { } else {
page.value++; page.value++;
@ -193,9 +203,11 @@ function next() {
} }
function prev() { function prev() {
if (page.value === 5 && !props.withSetup) { if (page.value === 2 && !props.withSetup) {
page.value -= 2; page.value -= 2;
} else if (page.value === 8 && !props.withSetup) { } else if (page.value === 6 && !props.withSetup) {
page.value -= 2;
} else if (page.value === 9 && !props.withSetup) {
page.value -= 2; page.value -= 2;
} else { } else {
page.value--; page.value--;

View File

@ -11,11 +11,11 @@ SPDX-License-Identifier: AGPL-3.0-only
@close="close(true)" @close="close(true)"
@closed="emit('closed')" @closed="emit('closed')"
> >
<template v-if="page === 1" #header><i class="ti ti-pencil"></i> {{ i18n.ts._initialTutorial._note.title }}</template> <template v-if="page === 2" #header><i class="ti ti-pencil"></i> {{ i18n.ts._initialTutorial._note.title }}</template>
<template v-else-if="page === 2" #header><i class="ti ti-mood-smile"></i> {{ i18n.ts._initialTutorial._reaction.title }}</template> <template v-else-if="page === 3" #header><i class="ti ti-mood-smile"></i> {{ i18n.ts._initialTutorial._reaction.title }}</template>
<template v-else-if="page === 3" #header><i class="ti ti-home"></i> {{ i18n.ts._initialTutorial._timeline.title }}</template> <template v-else-if="page === 4" #header><i class="ti ti-home"></i> {{ i18n.ts._initialTutorial._timeline.title }}</template>
<template v-else-if="page === 5" #header><i class="ti ti-pencil-plus"></i> {{ i18n.ts._initialTutorial._postNote.title }}</template> <template v-else-if="page === 6" #header><i class="ti ti-pencil-plus"></i> {{ i18n.ts._initialTutorial._postNote.title }}</template>
<template v-else-if="page === 6" #header><i class="ti ti-eye-exclamation"></i> {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.title }}</template> <template v-else-if="page === 7" #header><i class="ti ti-eye-exclamation"></i> {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.title }}</template>
<template v-else #header>{{ i18n.ts._initialTutorial.title }}</template> <template v-else #header>{{ i18n.ts._initialTutorial.title }}</template>
<XTutorial <XTutorial

View File

@ -9,13 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="[$style.onboardingContainer]"> <div :class="[$style.onboardingContainer]">
<div :class="[$style.tutorialTitle, { [$style.showing]: (page !== 0) }]"> <div :class="[$style.tutorialTitle, { [$style.showing]: (page !== 0) }]">
<div :class="$style.text"> <div :class="$style.text">
<span v-if="page === 1"><i class="ti ti-pencil"></i> {{ i18n.ts._initialTutorial._note.title }}</span> <span v-if="page === 1"><i class="ti ti-user-edit"></i> {{ i18n.ts._initialTutorial._profileSettings.title }}</span>
<span v-else-if="page === 2"><i class="ti ti-mood-smile"></i> {{ i18n.ts._initialTutorial._reaction.title }}</span> <span v-else-if="page === 2"><i class="ti ti-pencil"></i> {{ i18n.ts._initialTutorial._note.title }}</span>
<span v-else-if="page === 3"><i class="ti ti-home"></i> {{ i18n.ts._initialTutorial._timeline.title }}</span> <span v-else-if="page === 3"><i class="ti ti-mood-smile"></i> {{ i18n.ts._initialTutorial._reaction.title }}</span>
<span v-else-if="page === 4"><i class="ti ti-user-plus"></i> {{ i18n.ts.follow }}</span> <span v-else-if="page === 4"><i class="ti ti-home"></i> {{ i18n.ts._initialTutorial._timeline.title }}</span>
<span v-else-if="page === 5"><i class="ti ti-pencil-plus"></i> {{ i18n.ts._initialTutorial._postNote.title }}</span> <span v-else-if="page === 5"><i class="ti ti-user-plus"></i> {{ i18n.ts.follow }}</span>
<span v-else-if="page === 6"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.title }}</span> <span v-else-if="page === 6"><i class="ti ti-pencil-plus"></i> {{ i18n.ts._initialTutorial._postNote.title }}</span>
<span v-else-if="page === 7"><i class="ti ti-lock"></i> {{ i18n.ts.privacy }}</span> <span v-else-if="page === 7"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.title }}</span>
<span v-else-if="page === 8"><i class="ti ti-lock"></i> {{ i18n.ts.privacy }}</span>
<span v-else-if="page === MAX_PAGE"><!-- なんもなし --></span> <span v-else-if="page === MAX_PAGE"><!-- なんもなし --></span>
<span v-else>{{ i18n.ts._initialTutorial.title }}</span> <span v-else>{{ i18n.ts._initialTutorial.title }}</span>
</div> </div>
@ -219,7 +220,8 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
animationPhase.value = 4; animationPhase.value = 4;
confetti({ confetti({
spread: 70, spread: 75,
particleCount: 100,
origin: { y: 0.5 }, origin: { y: 0.5 },
}); });
}, 1000); }, 1000);
@ -232,7 +234,7 @@ onMounted(() => {
// #endregion // #endregion
definePageMetadata(() => ({ definePageMetadata(() => ({
title: 'Onboarding', title: i18n.tsx._initialTutorial._onboardingLanding.welcomeToX({ name: instance.name ?? host }),
description: 'Welcome to Misskey!', description: 'Welcome to Misskey!',
})); }));
</script> </script>