diff --git a/locales/index.d.ts b/locales/index.d.ts index a43203783a..383c454ca9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1188,6 +1188,11 @@ export interface Locale { "launchTutorial": string; "title": string; "wellDone": string; + "skipAreYouSure": string; + "_landing": { + "title": string; + "description": string; + }; "_note": { "title": string; "description": string; @@ -1202,6 +1207,7 @@ export interface Locale { "description": string; "letsTryReacting": string; "reactToContinue": string; + "reactNotification": string; "reactDone": string; }; "_timeline": { @@ -1212,6 +1218,41 @@ export interface Locale { "social": string; "global": string; "description2": string; + "description3": string; + }; + "_postNote": { + "title": string; + "description1": string; + "_visibility": { + "description": string; + "public": string; + "home": string; + "followers": string; + "direct": string; + "doNotSendConfidencialOnDirect1": string; + "doNotSendConfidencialOnDirect2": string; + "localOnly": string; + }; + "_cw": { + "title": string; + "description": string; + "_exampleNote": { + "cw": string; + "note": string; + }; + "useCases": string; + }; + }; + "_howToMakeAttachmentsSensitive": { + "title": string; + "description": string; + "tryThisFile": string; + "_exampleNote": { + "note": string; + }; + "method": string; + "sensitiveSucceeded": string; + "doItToContinue": string; }; "_done": { "title": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index afd13b0a9e..664143d170 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1186,6 +1186,10 @@ _initialTutorial: launchTutorial: "チュートリアルを見る" title: "チュートリアル" wellDone: "お見事!" + skipAreYouSure: "チュートリアルを終了しますか?" + _landing: + title: "チュートリアルへようこそ!" + description: "ここでは、Misskeyの基本的な使い方や機能を確認できます。「次へ」を押して早速始はじめましょう!" _note: title: "ノートって何?" description: "Misskeyでの投稿は「ノート」と呼びます。ノートはタイムラインに時系列で並んでいて、リアルタイムで更新されていきます。" @@ -1199,6 +1203,7 @@ _initialTutorial: description: "ノートには「リアクション」をつけることができます。「いいね」では伝わらないニュアンスも、リアクションで簡単・気軽に表現できます。" letsTryReacting: "リアクションは、ノートの「+」ボタンをクリックするとつけられます。試しにこのノートに好きなリアクションをつけてみましょう!" reactToContinue: "リアクションをつけると先に進めるようになります。" + reactNotification: "あなたのノートが誰かにリアクションされると、通知が入ります。もちろん、これもリアルタイムです!" reactDone: "リアクションを外すときは、「ー」ボタンを押します。実際に試してみてくださいね。" _timeline: title: "タイムラインのしくみ" @@ -1207,7 +1212,36 @@ _initialTutorial: local: "このサーバーにいるユーザー全員の投稿を見られます。" social: "ホームタイムラインとローカルタイムラインの投稿が両方表示されます。" global: "接続している他のすべてのサーバーからの投稿を見られます。" - description2: "その他にも、リストタイムラインやチャンネルタイムラインなどがあります。詳しくは{link}をご覧ください。" + description2: "それぞれのタイムラインは、画面上部でいつでも切り替えられます。" + description3: "その他にも、リストタイムラインやチャンネルタイムラインなどがあります。詳しくは{link}をご覧ください。" + _postNote: + title: "ノートの投稿設定" + description1: "Misskeyにノートを投稿する際には、細かい設定ができるようになっています。ノートの設定は、以下のような投稿フォームから行えます。" + _visibility: + description: "ノートを表示できる相手を制限できます。" + public: "すべてのユーザーに公開。" + home: "ホームタイムラインのみに公開。フォロワー・プロフィールを見に来た人・リノートから、他のユーザーも見ることができます。" + followers: "フォロワーにのみ公開。本人以外がリノートすることはできず、またフォロワー以外は閲覧できません。" + direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。" + doNotSendConfidencialOnDirect1: "ダイレクトであっても、機密情報は送信しないようにしてください。" + doNotSendConfidencialOnDirect2: "指定したユーザー以外には公開されないとはいえ、悪意のある他のソフトウェアに連合された際に、連合先で公開設定が無視されるおそれがあります。" + localOnly: "他のサーバーに投稿を連合しません。上記の公開範囲に関わらず、他のサーバーのユーザーは、この設定がついたノートを直接閲覧することができなくなります。" + _cw: + title: "内容を隠す(CW)" + description: "本文のかわりに「注釈」に書いた内容が表示されます。「もっと見る」を押すと本文が表示されます。" + _exampleNote: + cw: "飯テロ注意" + note: "チョコのかかったドーナツを食べました🍩😋" + useCases: "サーバーのガイドラインにより必要とされるノートに指定したり、ネタバレ投稿やセンシティブな文章を自主規制したりするときに使います。" + _howToMakeAttachmentsSensitive: + title: "添付ファイルをセンシティブにするには?" + description: "サーバーのガイドラインにより必要とされる際や、そのまま見れる状態にしておくべきではない添付ファイルには、「センシティブ」設定を付けます。" + tryThisFile: "試しに、このノートについている画像をセンシティブにしてみましょう。" + _exampleNote: + note: "納豆のフタ開けるのミスったわね… なんかグロい" + method: "添付ファイルをセンシティブにする際は、そのファイルをクリックしてメニューを開き、「センシティブとして設定」をクリックします。" + sensitiveSucceeded: "今後ファイルを添付する際は、お使いのサーバーのガイドラインに従って、センシティブを適切に設定してください!" + doItToContinue: "画像をセンシティブに設定すると先に進めるようになります。" _done: title: "チュートリアルは終了です🎉" description: "ここで紹介した機能は、ほんの一部です!Misskeyの使い方をより詳しく知るには、{link}をご覧ください。" diff --git a/packages/frontend/assets/tutorial/ai.webp b/packages/frontend/assets/tutorial/ai.webp new file mode 100644 index 0000000000..d9d4564942 Binary files /dev/null and b/packages/frontend/assets/tutorial/ai.webp differ diff --git a/packages/frontend/assets/tutorial/natto_failed.webp b/packages/frontend/assets/tutorial/natto_failed.webp new file mode 100644 index 0000000000..87db5f7732 Binary files /dev/null and b/packages/frontend/assets/tutorial/natto_failed.webp differ diff --git a/packages/frontend/assets/tutorial/syuilo.webp b/packages/frontend/assets/tutorial/syuilo.webp new file mode 100644 index 0000000000..316601f0fa Binary files /dev/null and b/packages/frontend/assets/tutorial/syuilo.webp differ diff --git a/packages/frontend/assets/tutorial/timeline_tab.png b/packages/frontend/assets/tutorial/timeline_tab.png new file mode 100644 index 0000000000..b52ad5fb51 Binary files /dev/null and b/packages/frontend/assets/tutorial/timeline_tab.png differ diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue index 4d139f17f2..19402a44ce 100644 --- a/packages/frontend/src/components/MkInfo.vue +++ b/packages/frontend/src/components/MkInfo.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 598846b166..492306f5d9 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ maxTextLength - textLength }}
- +
@@ -143,15 +143,20 @@ const props = withDefaults(defineProps<{ fixed?: boolean; autofocus?: boolean; freezeAfterPosted?: boolean; + mock?: boolean; }>(), { initialVisibleUsers: () => [], autofocus: true, + mock: false, }); const emit = defineEmits<{ (ev: 'posted'): void; (ev: 'cancel'): void; (ev: 'esc'): void; + + // Mock用 + (ev: 'fileChangeSensitive', fileId: string, to: boolean): void; }>(); const textareaEl = $shallowRef(null); @@ -239,7 +244,7 @@ const maxTextLength = $computed((): number => { }); const canPost = $computed((): boolean => { - return !posting && !posted && + return !props.mock && !posting && !posted && (1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && (textLength <= maxTextLength) && (!poll || poll.choices.length >= 2); @@ -396,6 +401,8 @@ function focus() { } function chooseFileFrom(ev) { + if (props.mock) return; + selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { for (const file of files_) { files.push(file); @@ -408,6 +415,9 @@ function detachFile(id) { } function updateFileSensitive(file, sensitive) { + if (props.mock) { + emit('fileChangeSensitive', file.id, sensitive); + } files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive; } @@ -420,6 +430,8 @@ function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities } function upload(file: File, name?: string): void { + if (props.mock) return; + uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { files.push(res); }); @@ -545,6 +557,8 @@ function onCompositionEnd(ev: CompositionEvent) { } async function onPaste(ev: ClipboardEvent) { + if (props.mock) return; + for (const { item, i } of Array.from(ev.clipboardData.items, (item, i) => ({ item, i }))) { if (item.kind === 'file') { const file = item.getAsFile(); @@ -629,7 +643,7 @@ function onDrop(ev): void { } function saveDraft() { - if (props.instant) return; + if (props.instant || props.mock) return; const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); @@ -666,6 +680,8 @@ async function post(ev?: MouseEvent) { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } + if (props.mock) return; + const annoying = text.includes('$[x2') || text.includes('$[x3') || @@ -831,6 +847,8 @@ function showActions(ev) { let postAccount = $ref(null); function openAccountMenu(ev: MouseEvent) { + if (props.mock) return; + openAccountMenu_({ withExtraOperation: false, includeCurrentAccount: true, @@ -861,7 +879,7 @@ onMounted(() => { nextTick(() => { // 書きかけの投稿を復元 - if (!props.instant && !props.mention && !props.specified) { + if (!props.instant && !props.mention && !props.specified && !props.mock) { const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey]; if (draft) { text = draft.data.text; diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index d499a22ed6..ea62262551 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -31,6 +31,7 @@ const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.d const props = defineProps<{ modelValue: any[]; detachMediaFn?: (id: string) => void; + mock?: boolean; }>(); const emit = defineEmits<{ @@ -44,6 +45,8 @@ const emit = defineEmits<{ let menuShowing = false; function detachMedia(id: string) { + if (props.mock) return; + if (props.detachMediaFn) { props.detachMediaFn(id); } else { @@ -52,6 +55,11 @@ function detachMedia(id: string) { } function toggleSensitive(file) { + if (props.mock) { + emit('changeSensitive', file, !file.isSensitive); + return; + } + os.api('drive/files/update', { fileId: file.id, isSensitive: !file.isSensitive, @@ -61,6 +69,8 @@ function toggleSensitive(file) { } async function rename(file) { + if (props.mock) return; + const { canceled, result } = await os.inputText({ title: i18n.ts.enterFileName, default: file.name, @@ -77,6 +87,8 @@ async function rename(file) { } async function describe(file) { + if (props.mock) return; + os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { default: file.comment !== null ? file.comment : '', file: file, @@ -94,6 +106,8 @@ async function describe(file) { } async function crop(file: Misskey.entities.DriveFile): Promise { + if (props.mock) return; + const newFile = await os.cropImage(file, { aspectRatio: NaN }); emit('replaceFile', file, newFile); } diff --git a/packages/frontend/src/components/MkUserSetupDialog.NoteTutorial.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue similarity index 66% rename from packages/frontend/src/components/MkUserSetupDialog.NoteTutorial.vue rename to packages/frontend/src/components/MkTutorialDialog.Note.vue index bf7df105d6..46d35c42df 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.NoteTutorial.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue @@ -8,25 +8,17 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._initialTutorial._note.description }}
-
… {{ i18n.ts._initialTutorial._note.date }}
+
{{ i18n.ts._initialTutorial._note.date }}
{{ i18n.ts.reply }} … {{ i18n.ts._initialTutorial._note.reply }}
{{ i18n.ts.renote }} … {{ i18n.ts._initialTutorial._note.renote }}
{{ i18n.ts.reaction }} … {{ i18n.ts._initialTutorial._note.reaction }}
-
-
{{ i18n.ts._initialTutorial._note.howToNote }}
-
-
- {{ i18n.ts.note }} -
-
-
{{ i18n.ts._initialTutorial._reaction.description }}
{{ i18n.ts._initialTutorial._reaction.letsTryReacting }}
-
{{ i18n.ts._initialTutorial.wellDone }} {{ i18n.ts._initialTutorial._reaction.reactDone }}
+
{{ i18n.ts._initialTutorial.wellDone }} {{ i18n.ts._initialTutorial._reaction.reactNotification }}
{{ i18n.ts._initialTutorial._reaction.reactDone }}
@@ -34,6 +26,8 @@ SPDX-License-Identifier: AGPL-3.0-only import * as Misskey from 'misskey-js'; import { ref, reactive } from 'vue'; import { i18n } from '@/i18n.js'; +import { globalEvents } from '@/events.js'; +import { $i } from '@/account.js'; import MkTime from '@/components/global/MkTime.vue'; import MkNote from '@/components/MkNote.vue'; @@ -51,12 +45,12 @@ const exampleNote = reactive({ userId: '0000000001', user: { id: '0000000001', - name: 'しゅいろ', - username: 'syuilo', + name: '藍', + username: 'ai', host: null, avatarDecorations: [], - avatarUrl: 'https://proxy.misskeyusercontent.com/avatar.webp?url=https%3A%2F%2Fs3.arkjp.net%2Fmisskey%2Fwebpublic-b2dc591e-58b6-4df7-b7c9-1cba199f6619.png&avatar=1', - avatarBlurhash: 'yFF5Kq0L00?a^*IBNG01^j-pV@D*o|xt58WB}@9at7s.Ip~AWB57%Laes:xaOEoLnis:ofIpoJr?NHtRV@oLoeNHNI%1M{kCWCjuxZ', + avatarUrl: './client-assets/tutorial/ai.webp', + avatarBlurhash: 'eiKmhHIByXxZ~qWXs:-pR*NbR*s:xuRjoL-oR*WCt6WWf6WVf6oeWB', isBot: false, isCat: true, emojis: {}, @@ -84,6 +78,24 @@ function addReaction(emoji) { emit('reacted'); exampleNote.reactions[emoji] = 1; exampleNote.myReaction = emoji; + doNotification(emoji); +} + +function doNotification(emoji: string): void { + if (!$i || !emoji) return; + + const notification: Misskey.entities.Notification = { + id: Math.random().toString(), + createdAt: new Date().toUTCString(), + isRead: false, + type: 'reaction', + reaction: emoji, + user: $i, + userId: $i.id, + note: exampleNote, + }; + + globalEvents.emit('clientNotification', notification); } function removeReaction(emoji) { @@ -103,42 +115,4 @@ function removeReaction(emoji) { height: 1px; background: var(--divider); } - -.post { - position: relative; - display: block; - width: 100%; - height: 40px; - color: var(--fgOnAccent); - font-weight: bold; - text-align: left; - - &:before { - content: ""; - display: block; - width: calc(100% - 38px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - } - -} - -.postIcon { - position: relative; - margin-left: 30px; - margin-right: 8px; - width: 32px; -} - -.postText { - position: relative; - line-height: 40px; -} diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue new file mode 100644 index 0000000000..441594e42b --- /dev/null +++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue @@ -0,0 +1,135 @@ + + + + + + + diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue new file mode 100644 index 0000000000..659fd7c0fc --- /dev/null +++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue @@ -0,0 +1,156 @@ + + + + + + + diff --git a/packages/frontend/src/components/MkUserSetupDialog.TimelineTutorial.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue similarity index 85% rename from packages/frontend/src/components/MkUserSetupDialog.TimelineTutorial.vue rename to packages/frontend/src/components/MkTutorialDialog.Timeline.vue index a9be4a5991..75b917f33c 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.TimelineTutorial.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue @@ -12,7 +12,12 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._timelines.social }} … {{ i18n.ts._initialTutorial._timeline.social }}
{{ i18n.ts._timelines.global }} … {{ i18n.ts._initialTutorial._timeline.global }}
- +
+
{{ i18n.ts._initialTutorial._timeline.description2 }}
+ +
+
+ @@ -37,6 +42,11 @@ import { i18n } from '@/i18n.js'; background: var(--divider); } +.image { + max-width: 300px; + margin: 0 auto; +} + .post { position: relative; display: block; diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue new file mode 100644 index 0000000000..54ffb0911e --- /dev/null +++ b/packages/frontend/src/components/MkTutorialDialog.vue @@ -0,0 +1,252 @@ + + + + + + + diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index 0dc0af5853..05b55f77a7 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -16,14 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only - - - - +
-
+
{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}
{{ i18n.t('_initialAccountSetting.youCanContinueTutorial', { name: instance.name ?? host }) }}
- {{ i18n.ts._initialAccountSetting.startTutorial }} + {{ i18n.ts._initialAccountSetting.startTutorial }}
{{ i18n.ts.goBack }} - {{ i18n.ts.close }} -
-
- - - - - - - diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index a50ae9d9c7..159e1d633d 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only