From 85a4c8dbb1cd455ac538d8004dafc47d4d6d400f Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 8 May 2023 17:29:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E5=88=9D=E6=9C=9F=E8=A8=AD=E5=AE=9A=E3=82=A6?= =?UTF-8?q?=E3=82=A3=E3=82=B6=E3=83=BC=E3=83=89=20(#10799)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * :art: * :art: * wip * wip * :art: * Update CHANGELOG.md * wip * Update MkUserSetupDialog.vue * add stories Co-authored-by: Acid Chicken (硫酸鶏) * update stories * Update MkUserSetupDialog.Follow.stories.impl.ts * test: load mock user account * :v: * :v: * test: reset on each render * test: use id to identify --------- Co-authored-by: Acid Chicken (硫酸鶏) --- CHANGELOG.md | 1 + locales/ja-JP.yml | 50 ++++--- packages/frontend/.storybook/generate.tsx | 2 + packages/frontend/.storybook/preview.ts | 31 +++- .../frontend/src/components/MkModalWindow.vue | 2 +- .../MkSignupDialog.rules.stories.impl.ts | 2 +- .../MkUserSetupDialog.Follow.stories.impl.ts | 51 +++++++ .../components/MkUserSetupDialog.Follow.vue | 63 ++++++++ .../MkUserSetupDialog.Profile.stories.impl.ts | 31 ++++ .../components/MkUserSetupDialog.Profile.vue | 101 +++++++++++++ .../MkUserSetupDialog.User.stories.impl.ts | 32 +++++ .../src/components/MkUserSetupDialog.User.vue | 101 +++++++++++++ .../MkUserSetupDialog.stories.impl.ts | 51 +++++++ .../src/components/MkUserSetupDialog.vue | 135 ++++++++++++++++++ packages/frontend/src/init.ts | 4 + .../frontend/src/pages/timeline.tutorial.vue | 69 +++------ packages/frontend/src/pages/timeline.vue | 2 +- packages/frontend/src/store.ts | 6 +- 18 files changed, 651 insertions(+), 83 deletions(-) create mode 100644 packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts create mode 100644 packages/frontend/src/components/MkUserSetupDialog.Follow.vue create mode 100644 packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts create mode 100644 packages/frontend/src/components/MkUserSetupDialog.Profile.vue create mode 100644 packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts create mode 100644 packages/frontend/src/components/MkUserSetupDialog.User.vue create mode 100644 packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts create mode 100644 packages/frontend/src/components/MkUserSetupDialog.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 027c369817..97d5ec0347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - Fix: フォローリクエストの通知が残る問題を修正 ### Client +- アカウント作成時に初期設定ウィザードを表示するように - チャンネル内検索ができるように - チャンネル検索ですべてのチャンネルの取得/表示ができるように - 通知の表示をカスタマイズできるように diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b87f4d9cb4..c3dc2172c3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1036,6 +1036,20 @@ channelArchiveConfirmTitle: "{name}をアーカイブしますか?" channelArchiveConfirmDescription: "アーカイブすると、チャンネル一覧や検索結果に表示されなくなり、新たな書き込みもできなくなります。" thisChannelArchived: "このチャンネルはアーカイブされています。" displayOfNote: "ノートの表示" +initialAccountSetting: "初期設定" +youFollowing: "フォロー中" + +_initialAccountSetting: + accountCreated: "アカウントの作成が完了しました!" + letsFillYourProfile: "まずはあなたのプロフィールを設定しましょう。" + profileSetting: "プロフィール設定" + theseSettingsCanEditLater: "これらの設定は後から変更できます。" + youCanEditMoreSettingsInSettingsPageLater: "この他にも様々な設定を「設定」ページから行えます。ぜひ後で確認してみてください。" + followUsers: "タイムラインを構築するため、気になるユーザーをフォローしてみましょう。" + pushNotificationDescription: "プッシュ通知を有効にすると{name}の通知をお使いのデバイスで受け取ることができます。" + initialAccountSettingCompleted: "初期設定が完了しました!" + haveFun: "{name}をお楽しみください!" + ifYouNeedLearnMore: "{name}(Misskey)の使い方などを詳しく知るには{link}をご覧ください。" _serverRules: description: "新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。" @@ -1615,32 +1629,16 @@ _time: hour: "時間" day: "日" -_tutorial: +_timelineTutorial: title: "Misskeyの使い方" - step1_1: "ようこそ。" - step1_2: "この画面は「タイムライン」と呼ばれ、あなたや、あなたが「フォロー」する人の「ノート」が時系列で表示されます。" - step1_3: "あなたはまだ何もノートを投稿しておらず、誰もフォローしていないので、タイムラインには何も表示されていないはずです。" - step2_1: "ノートを作成したり誰かをフォローしたりする前に、まずあなたのプロフィールを完成させましょう。" - step2_2: "あなたがどんな人かわかると、多くの人にノートを見てもらえたり、フォローしてもらいやすくなります。" - step3_1: "プロフィール設定はうまくできましたか?" - step3_2: "では試しに、何かノートを投稿してみてください。画面上にある鉛筆マークのボタンを押すとフォームが開きます。" - step3_3: "内容を書いたら、フォーム右上のボタンを押すと投稿できます。" - step3_4: "内容が思いつかない?「Misskey始めました」というのはいかがでしょう。" - step4_1: "投稿できましたか?" - step4_2: "あなたのノートがタイムラインに表示されていれば成功です。" - step5_1: "次は、他の人をフォローしてタイムラインを賑やかにしたいところです。" - step5_2: "{featured}で人気のノートが見れるので、その中から気になった人を選んでフォローしたり、{explore}で人気のユーザーを探すこともできます。" - step5_3: "ユーザーをフォローするには、ユーザーのアイコンをクリックしてユーザーページを表示し、「フォロー」ボタンを押します。" - step5_4: "ユーザーによっては、フォローが承認されるまで時間がかかる場合があります。" - step6_1: "タイムラインに他のユーザーのノートが表示されていれば成功です。" - step6_2: "他の人のノートには、「リアクション」を付けることができ、簡単にあなたの反応を伝えられます。" - step6_3: "リアクションを付けるには、ノートの「+」マークをクリックして、好きなリアクションを選択します。" - step7_1: "これで、Misskeyの基本的な使い方の説明は終わりました。お疲れ様でした。" - step7_2: "もっとMisskeyについて知りたいときは、{help}を見てみてください。" - step7_3: "では、Misskeyをお楽しみください🚀" - step8_1: "最後に、プッシュ通知を有効化してみませんか?" - step8_2: "プッシュ通知を受け取ることで、Misskeyを開いていない時にもリアクションやフォロー、メンションなどに気づけます。" - step8_3: "通知の設定は後から変更できます。" + step1_1: "この画面は「タイムライン」です。{name}に投稿された「ノート」が時系列で表示されます。" + step1_2: "タイムラインにはいくつか種類があり、例えば「ホームタイムライン」にはあなたがフォローしている人のノートが流れ、「ローカルタイムライン」には{name}全体のノートが流れます。" + step2_1: "試しに、何かノートを投稿してみましょう。画面上にある鉛筆マークのボタンを押すとフォームが開きます。" + step2_2: "初めてのノートの内容は、あなたの自己紹介や「{name}始めました」などがおすすめです。" + step3_1: "投稿できましたか?" + step3_2: "あなたのノートがタイムラインに表示されていれば成功です。" + step4_1: "ノートには、「リアクション」を付けることができます。" + step4_2: "リアクションを付けるには、ノートの「+」マークをクリックして、好きな絵文字を選択します。" _2fa: alreadyRegistered: "既に設定は完了しています。" @@ -1822,7 +1820,7 @@ _profile: metadataDescription: "プロフィールに表として追加情報を表示することができます。" metadataLabel: "ラベル" metadataContent: "内容" - changeAvatar: "アバター画像を変更" + changeAvatar: "アイコン画像を変更" changeBanner: "バナー画像を変更" _exportOrImport: diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index dbe9729170..7c51d4c00c 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -399,6 +399,8 @@ Promise.all([ glob('src/components/Mk{A,B}*.vue'), glob('src/components/MkGalleryPostPreview.vue'), glob('src/components/MkSignupServerRules.vue'), + glob('src/components/MkUserSetupDialog.vue'), + glob('src/components/MkUserSetupDialog.*.vue'), glob('src/pages/user/home.vue'), ]) .then((globs) => globs.flat()) diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts index 06aa7b6682..d91d76ff0f 100644 --- a/packages/frontend/.storybook/preview.ts +++ b/packages/frontend/.storybook/preview.ts @@ -3,6 +3,7 @@ import { FORCE_REMOUNT } from '@storybook/core-events'; import { type Preview, setup } from '@storybook/vue3'; import isChromatic from 'chromatic/isChromatic'; import { initialize, mswDecorator } from 'msw-storybook-addon'; +import { userDetailed } from './fakes'; import locale from './locale'; import { commonHandlers, onUnhandledRequest } from './mocks'; import themes from './themes'; @@ -10,6 +11,7 @@ import '../src/style.scss'; const appInitialized = Symbol(); +let lastStory = null; let moduleInitialized = false; let unobserve = () => {}; let misskeyOS = null; @@ -42,10 +44,16 @@ function loadTheme(applyTheme: typeof import('../src/scripts/theme')['applyTheme unobserve = () => observer.disconnect(); } +function initLocalStorage() { + localStorage.clear(); + localStorage.setItem('account', JSON.stringify(userDetailed())); + localStorage.setItem('locale', JSON.stringify(locale)); +} + initialize({ onUnhandledRequest, }); -localStorage.setItem("locale", JSON.stringify(locale)); +initLocalStorage(); queueMicrotask(() => { Promise.all([ import('../src/components'), @@ -76,6 +84,27 @@ queueMicrotask(() => { const preview = { decorators: [ (Story, context) => { + if (lastStory === context.id) { + lastStory = null; + } else { + lastStory = context.id; + const channel = addons.getChannel(); + const resetIndexedDBPromise = globalThis.indexedDB?.databases + ? indexedDB.databases().then((r) => { + for (var i = 0; i < r.length; i++) { + indexedDB.deleteDatabase(r[i].name!); + } + }).catch(() => {}) + : Promise.resolve(); + const resetDefaultStorePromise = import('../src/store').then(({ defaultStore }) => { + // @ts-expect-error + defaultStore.init(); + }).catch(() => {}); + Promise.all([resetIndexedDBPromise, resetDefaultStorePromise]).then(() => { + initLocalStorage(); + channel.emit(FORCE_REMOUNT, { storyId: context.id }); + }); + } const story = Story(); if (!moduleInitialized) { const channel = addons.getChannel(); diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue index 2d2f8411f1..f983f77750 100644 --- a/packages/frontend/src/components/MkModalWindow.vue +++ b/packages/frontend/src/components/MkModalWindow.vue @@ -89,7 +89,6 @@ defineExpose({ display: flex; flex-direction: column; contain: content; - container-type: inline-size; border-radius: var(--radius); --root-margin: 24px; @@ -142,6 +141,7 @@ defineExpose({ flex: 1; overflow: auto; background: var(--panel); + container-type: size; } } diff --git a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts index 1308dfff9a..2d95455730 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts +++ b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts @@ -3,7 +3,7 @@ import { expect } from '@storybook/jest'; import { userEvent, waitFor, within } from '@storybook/testing-library'; import { StoryObj } from '@storybook/vue3'; import { onBeforeUnmount } from 'vue'; -import MkSignupServerRules from './MkSignupDialog,rules.vue'; +import MkSignupServerRules from './MkSignupDialog.rules.vue'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; export const Empty = { diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts new file mode 100644 index 0000000000..7d5a65f41a --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import { rest } from 'msw'; +import { commonHandlers } from '../../.storybook/mocks'; +import { userDetailed } from '../../.storybook/fakes'; +import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue'; +export const Default = { + render(args) { + return { + components: { + MkUserSetupDialog_Follow, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + ...commonHandlers, + rest.post('/api/users', (req, res, ctx) => { + return res(ctx.json([ + userDetailed('44'), + userDetailed('49'), + ])); + }), + rest.post('/api/pinned-users', (req, res, ctx) => { + return res(ctx.json([ + userDetailed('44'), + userDetailed('49'), + ])); + }), + ], + }, + }, +} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue new file mode 100644 index 0000000000..b89e3e4c9d --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts new file mode 100644 index 0000000000..f4930aa26b --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import MkUserSetupDialog_Profile from './MkUserSetupDialog.Profile.vue'; +export const Default = { + render(args) { + return { + components: { + MkUserSetupDialog_Profile, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + + }, + parameters: { + layout: 'centered', + }, +} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue new file mode 100644 index 0000000000..373e2cf8dc --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts new file mode 100644 index 0000000000..7413f4884b --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import { userDetailed } from '../../.storybook/fakes'; +import MkUserSetupDialog_User from './MkUserSetupDialog.User.vue'; +export const Default = { + render(args) { + return { + components: { + MkUserSetupDialog_User, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + user: userDetailed(), + }, + parameters: { + layout: 'centered', + }, +} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue new file mode 100644 index 0000000000..d66f34f165 --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts new file mode 100644 index 0000000000..55790602d5 --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import { rest } from 'msw'; +import { commonHandlers } from '../../.storybook/mocks'; +import { userDetailed } from '../../.storybook/fakes'; +import MkUserSetupDialog from './MkUserSetupDialog.vue'; +export const Default = { + render(args) { + return { + components: { + MkUserSetupDialog, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + ...commonHandlers, + rest.post('/api/users', (req, res, ctx) => { + return res(ctx.json([ + userDetailed('44'), + userDetailed('49'), + ])); + }), + rest.post('/api/pinned-users', (req, res, ctx) => { + return res(ctx.json([ + userDetailed('44'), + userDetailed('49'), + ])); + }), + ], + }, + }, +} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue new file mode 100644 index 0000000000..58afe09b61 --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 91a009e8f8..13fbd08c56 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -343,6 +343,10 @@ if ($i) { // only add post shortcuts if logged in hotkeys['p|n'] = post; + if (defaultStore.state.accountSetupWizard !== -1) { + popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {}, 'closed'); + } + if ($i.isDeleted) { alert({ type: 'warning', diff --git a/packages/frontend/src/pages/timeline.tutorial.vue b/packages/frontend/src/pages/timeline.tutorial.vue index 0d0c932a5c..32228d28f4 100644 --- a/packages/frontend/src/pages/timeline.tutorial.vue +++ b/packages/frontend/src/pages/timeline.tutorial.vue @@ -1,7 +1,7 @@