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..b886cc7bfd 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1036,6 +1036,21 @@ channelArchiveConfirmTitle: "{name}をアーカイブしますか?" channelArchiveConfirmDescription: "アーカイブすると、チャンネル一覧や検索結果に表示されなくなり、新たな書き込みもできなくなります。" thisChannelArchived: "このチャンネルはアーカイブされています。" displayOfNote: "ノートの表示" +initialAccountSetting: "初期設定" +youFollowing: "フォロー中" + +_initialAccountSetting: + accountCreated: "アカウントの作成が完了しました!" + letsFillYourProfile: "まずはあなたのプロフィールを設定しましょう。" + profileSetting: "プロフィール設定" + theseSettingsCanEditLater: "これらの設定は後から変更できます。" + youCanEditMoreSettingsInSettingsPageLater: "この他にも様々な設定を「設定」ページから行えます。ぜひ後で確認してみてください。" + followUsers: "タイムラインを構築するため、気になるユーザーをフォローしてみましょう。" + pushNotificationDescription: "プッシュ通知を有効にすると{name}の通知をお使いのデバイスで受け取ることができます。" + initialAccountSettingCompleted: "初期設定が完了しました!" + haveFun: "{name}をお楽しみください!" + ifYouNeedLearnMore: "{name}(Misskey)の使い方などを詳しく知るには{link}をご覧ください。" + skipAreYouSure: "初期設定をスキップしますか?" _serverRules: description: "新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。" @@ -1615,32 +1630,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 +1821,7 @@ _profile: metadataDescription: "プロフィールに表として追加情報を表示することができます。" metadataLabel: "ラベル" metadataContent: "内容" - changeAvatar: "アバター画像を変更" + changeAvatar: "アイコン画像を変更" changeBanner: "バナー画像を変更" _exportOrImport: diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 6556e6e105..8fae972cb1 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -44,7 +44,7 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder(), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId) .andWhere('channel.isArchived = FALSE') .andWhere({ userId: me.id }); 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/MkInput.vue b/packages/frontend/src/components/MkInput.vue index da6c338123..e48032d599 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -1,7 +1,7 @@ @@ -29,8 +29,8 @@ const props = withDefaults(defineProps<{ }); - 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..a9d117e073 --- /dev/null +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 8497b8443b..ad36dcabe4 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -222,7 +222,7 @@ watch(() => props.user.avatarBlurhash, () => { transform: rotate(37.5deg) skew(30deg); &, &::after { - border-radius: 0 75% 75%; + border-radius: 25% 75% 75%; } > .layer { @@ -251,7 +251,7 @@ watch(() => props.user.avatarBlurhash, () => { transform: rotate(-37.5deg) skew(-30deg); &, &::after { - border-radius: 75% 0 75% 75%; + border-radius: 75% 25% 75% 75%; } > .layer { diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 91a009e8f8..49e7bb4008 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -343,6 +343,16 @@ if ($i) { // only add post shortcuts if logged in hotkeys['p|n'] = post; + if (defaultStore.state.accountSetupWizard !== -1) { + // このウィザードが実装される前に登録したユーザーには表示させないため + // TODO: そのうち消す + if (Date.now() - new Date($i.createdAt).getTime() < 1000 * 60 * 60 * 24) { + popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {}, 'closed'); + } else { + defaultStore.set('accountSetupWizard', -1); + } + } + if ($i.isDeleted) { alert({ type: 'warning', diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 67acee5aca..c4f9d47d7d 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -19,6 +19,7 @@ import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu'; import copyToClipboard from './scripts/copy-to-clipboard'; import { showMovedDialog } from './scripts/show-moved-dialog'; +import { DriveFile } from 'misskey-js/built/entities'; export const openingWindowsCount = ref(0); @@ -420,7 +421,7 @@ export async function selectUser(opts: { includeSelf?: boolean } = {}) { }); } -export async function selectDriveFile(multiple: boolean) { +export async function selectDriveFile(multiple: boolean): Promise { return new Promise((resolve, reject) => { popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'file', @@ -428,7 +429,7 @@ export async function selectDriveFile(multiple: boolean) { }, { done: files => { if (files) { - resolve(multiple ? files : files[0]); + resolve(files); } }, }, 'closed'); diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue index ffeb8ba285..e97a4b07f1 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue @@ -33,8 +33,8 @@ const emit = defineEmits<{ let file: any = $ref(null); async function choose() { - os.selectDriveFile(false).then((fileResponse: any) => { - file = fileResponse; + os.selectDriveFile(false).then((fileResponse) => { + file = fileResponse[0]; emit('update:modelValue', { ...props.modelValue, fileId: fileResponse.id, 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 @@