From 0d24c8843ca725a84cc900411129f98f6c5c2e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:21:50 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix(MisskeyIO#866):=20=E3=83=A6=E3=83=BC?= =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=83=AD=E3=83=BC=E3=83=AB=E3=81=AE=E5=89=B2=E3=82=8A?= =?UTF-8?q?=E5=BD=93=E3=81=A6=E6=99=82=E3=83=A1=E3=83=A2=E3=82=92=E5=85=A5?= =?UTF-8?q?=E5=8A=9B=E3=81=97=E3=81=AA=E3=81=84=E3=81=A8=E3=82=AD=E3=83=A3?= =?UTF-8?q?=E3=83=B3=E3=82=BB=E3=83=AB=E6=89=B1=E3=81=84=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(Missk?= =?UTF-8?q?eyIO#877)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/get-user-menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 758a04d373..753a4ee8bb 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -298,7 +298,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter const { canceled: canceled3, result: memo } = await os.inputText({ title: i18n.ts.addMemo, type: 'textarea', - placeholder: i18n.ts.memo, + default: '', }); if (canceled3) return; From 8bd78848736672cb85371f3f4c0290eb9d442fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:17:14 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix(frontend/mobile):=20=E3=83=A2=E3=83=90?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=A7kawaii=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=8C=E9=81=A9=E7=94=A8=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(MisskeyIO#878)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/ui/_common_/navbar-for-mobile.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 5d0e065f09..3f4e5bad68 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -8,7 +8,8 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -57,7 +58,9 @@ import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; +import { miLocalStorage } from "@/local-storage.js"; +const kawaiiMode = miLocalStorage.getItem('kawaii') === 'true'; const menu = toRef(defaultStore.state, 'menu'); const otherMenuItemIndicated = computed(() => { for (const def in navbarItemDef) { @@ -120,6 +123,11 @@ function more() { aspect-ratio: 1; } +.instanceIconAlt { + display: inline-block; + width: 85%; +} + .bottom { position: sticky; bottom: 0; From 535a6bc756f22ef3118c0077841d94844841c1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:54:32 +0900 Subject: [PATCH 3/6] =?UTF-8?q?spec(notes/create):=20=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9Fnote=E3=82=92=E8=BF=94=E3=81=95?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(MisskeyIO#879)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/notes/create.ts | 9 ++++++--- packages/backend/src/server/web/cli.js | 3 ++- packages/frontend/src/components/MkPostForm.vue | 1 + packages/frontend/src/pages/reversi/game.vue | 1 + packages/frontend/src/scripts/get-note-menu.ts | 3 +++ packages/misskey-js/src/autogen/types.ts | 6 ++++++ packages/sw/src/sw.ts | 2 +- 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 5bff7f718b..963f3bbfdb 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -40,7 +40,7 @@ export const meta = { res: { type: 'object', - optional: false, nullable: false, + optional: true, nullable: false, properties: { createdNote: { type: 'object', @@ -207,6 +207,7 @@ export const paramDef = { }, required: ['choices'], }, + noCreatedNote: { type: 'boolean', default: false }, }, // (re)note with text, files and poll are optional if: { @@ -281,7 +282,8 @@ export default class extends Endpoint { // eslint- const note = await this.notesRepository.findOneBy({ id: idempotent }); if (note) { logger.info('The request has already been processed.', { noteId: note.id }); - return { createdNote: await this.noteEntityService.pack(note, me) }; + if (ps.noCreatedNote) return; + else return { createdNote: await this.noteEntityService.pack(note, me) }; } } @@ -453,7 +455,8 @@ export default class extends Endpoint { // eslint- await this.redisForTimelines.set(`note:idempotent:${me.id}:${hash}`, note.id, 'EX', 60); logger.info('Successfully created a note.', { noteId: note.id }); - return { + if (ps.noCreatedNote) return; + else return { createdNote: await this.noteEntityService.pack(note, me), }; } catch (err) { diff --git a/packages/backend/src/server/web/cli.js b/packages/backend/src/server/web/cli.js index 30ee77f4d9..2cffd60638 100644 --- a/packages/backend/src/server/web/cli.js +++ b/packages/backend/src/server/web/cli.js @@ -41,7 +41,8 @@ window.onload = async () => { document.getElementById('submit').addEventListener('click', () => { api('notes/create', { - text: document.getElementById('text').value + text: document.getElementById('text').value, + noCreatedNote: true, }).then(() => { location.reload(); }); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 00e5f39665..8eacf4f177 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -773,6 +773,7 @@ async function post(ev?: MouseEvent) { visibility: visibility.value, visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined, reactionAcceptance: reactionAcceptance.value, + noCreatedNote: true, }; if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { diff --git a/packages/frontend/src/pages/reversi/game.vue b/packages/frontend/src/pages/reversi/game.vue index d95dce18a2..865b8425d2 100644 --- a/packages/frontend/src/pages/reversi/game.vue +++ b/packages/frontend/src/pages/reversi/game.vue @@ -46,6 +46,7 @@ function start(_game: Misskey.entities.ReversiGameDetailed) { misskeyApi('notes/create', { text: i18n.ts._reversi.iStartedAGame + '\n' + location.href, visibility: 'home', + noCreatedNote: true, }); } diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 92e421cda1..b7d3c1abf4 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -543,6 +543,7 @@ export function getRenoteMenu(props: { misskeyApi('notes/create', { renoteId: appearNote.id, channelId: appearNote.channelId, + noCreatedNote: true, }).then(() => { os.toast(i18n.ts.renoted); }); @@ -589,6 +590,7 @@ export function getRenoteMenu(props: { localOnly, visibility, renoteId: appearNote.id, + noCreatedNote: true, }).then(() => { os.toast(i18n.ts.renoted); }); @@ -630,6 +632,7 @@ export function getRenoteMenu(props: { misskeyApi('notes/create', { renoteId: appearNote.id, channelId: channel.id, + noCreatedNote: true, }).then(() => { os.toast(i18n.tsx.renotedToX({ name: channel.name })); }); diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 8a5d1829af..63e97c0f64 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -23333,6 +23333,8 @@ export type operations = { expiresAt?: number | null; expiredAfter?: number | null; }) | null; + /** @default false */ + noCreatedNote?: boolean; }; }; }; @@ -23345,6 +23347,10 @@ export type operations = { }; }; }; + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index cc79d88713..e2f6ab86cd 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -114,7 +114,7 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv if ('note' in data.body) client = await swos.openPost({ reply: data.body.note }, loginId); break; case 'renote': - if ('note' in data.body) await swos.api('notes/create', loginId, { renoteId: data.body.note.id }); + if ('note' in data.body) await swos.api('notes/create', loginId, { renoteId: data.body.note.id, noCreatedNote: true }); break; case 'accept': switch (data.body.type) { From 8a0b98aa26801889110feb063076288648ef4dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:16:28 +0900 Subject: [PATCH 4/6] Sync charts one-at-a-time to reduce database contention and timeouts (MisskeyIO#880) (cherry picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/830) Co-authored-by: Hazelnoot --- .../src/core/chart/ChartManagementService.ts | 10 +++---- .../processors/CleanChartsProcessorService.ts | 26 +++++++++---------- .../ResyncChartsProcessorService.ts | 8 +++--- .../processors/TickChartsProcessorService.ts | 26 +++++++++---------- 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 79681370a1..f04c561063 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -58,9 +58,9 @@ export class ChartManagementService implements OnApplicationShutdown { @bindThis public async start() { // 20分おきにメモリ情報をDBに書き込み - this.saveIntervalId = setInterval(() => { + this.saveIntervalId = setInterval(async () => { for (const chart of this.charts) { - chart.save(); + await chart.save(); } }, 1000 * 60 * 20); } @@ -69,9 +69,9 @@ export class ChartManagementService implements OnApplicationShutdown { public async dispose(): Promise { clearInterval(this.saveIntervalId); if (process.env.NODE_ENV !== 'test') { - await Promise.all( - this.charts.map(chart => chart.save()), - ); + for (const chart of this.charts) { + await chart.save(); + } } } diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 110468801c..19f98c0d51 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -48,20 +48,18 @@ export class CleanChartsProcessorService { public async process(): Promise { this.logger.info('Clean charts...'); - await Promise.all([ - this.federationChart.clean(), - this.notesChart.clean(), - this.usersChart.clean(), - this.activeUsersChart.clean(), - this.instanceChart.clean(), - this.perUserNotesChart.clean(), - this.perUserPvChart.clean(), - this.driveChart.clean(), - this.perUserReactionsChart.clean(), - this.perUserFollowingChart.clean(), - this.perUserDriveChart.clean(), - this.apRequestChart.clean(), - ]); + await this.federationChart.clean(); + await this.notesChart.clean(); + await this.usersChart.clean(); + await this.activeUsersChart.clean(); + await this.instanceChart.clean(); + await this.perUserNotesChart.clean(); + await this.perUserPvChart.clean(); + await this.driveChart.clean(); + await this.perUserReactionsChart.clean(); + await this.perUserFollowingChart.clean(); + await this.perUserDriveChart.clean(); + await this.apRequestChart.clean(); this.logger.succ('All charts successfully cleaned.'); } diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 570cdf9a75..46e1adf173 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -31,11 +31,9 @@ export class ResyncChartsProcessorService { // TODO: ユーザーごとのチャートも更新する // TODO: インスタンスごとのチャートも更新する - await Promise.all([ - this.driveChart.resync(), - this.notesChart.resync(), - this.usersChart.resync(), - ]); + await this.driveChart.resync(); + await this.notesChart.resync(); + await this.usersChart.resync(); this.logger.succ('All charts successfully resynced.'); } diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 93ec34162d..c09cbccc57 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -48,20 +48,18 @@ export class TickChartsProcessorService { public async process(): Promise { this.logger.info('Tick charts...'); - await Promise.all([ - this.federationChart.tick(false), - this.notesChart.tick(false), - this.usersChart.tick(false), - this.activeUsersChart.tick(false), - this.instanceChart.tick(false), - this.perUserNotesChart.tick(false), - this.perUserPvChart.tick(false), - this.driveChart.tick(false), - this.perUserReactionsChart.tick(false), - this.perUserFollowingChart.tick(false), - this.perUserDriveChart.tick(false), - this.apRequestChart.tick(false), - ]); + await this.federationChart.tick(false); + await this.notesChart.tick(false); + await this.usersChart.tick(false); + await this.activeUsersChart.tick(false); + await this.instanceChart.tick(false); + await this.perUserNotesChart.tick(false); + await this.perUserPvChart.tick(false); + await this.driveChart.tick(false); + await this.perUserReactionsChart.tick(false); + await this.perUserFollowingChart.tick(false); + await this.perUserDriveChart.tick(false); + await this.apRequestChart.tick(false); this.logger.succ('All charts successfully ticked.'); } From 31d57f270cc16937cfcd428aa4bcc9342daa399a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 12 Jan 2025 18:36:16 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat(frontend/draft):=20=E4=B8=8B=E6=9B=B8?= =?UTF-8?q?=E3=81=8D=E6=A9=9F=E8=83=BD=E3=81=AE=E6=94=B9=E8=89=AF=E3=83=BB?= =?UTF-8?q?=E5=BC=B7=E5=8C=96=20(MisskeyIO#881)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 2 + locales/index.d.ts | 8 + locales/ja-JP.yml | 2 + locales/ko-KR.yml | 2 + .../src/components/MkDraftsDialog.vue | 183 +++++++++++++++++ .../frontend/src/components/MkPostForm.vue | 194 ++++++++++++------ .../frontend/src/pages/settings/general.vue | 2 + .../pages/settings/preferences-backups.vue | 1 + packages/frontend/src/store.ts | 4 + .../frontend/src/types/note-draft-item.ts | 38 ++++ 10 files changed, 377 insertions(+), 59 deletions(-) create mode 100644 packages/frontend/src/components/MkDraftsDialog.vue create mode 100644 packages/frontend/src/types/note-draft-item.ts diff --git a/locales/en-US.yml b/locales/en-US.yml index c202d53d18..28652cdbe8 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1317,6 +1317,8 @@ consentAll: "Allow All Items" consentSelected: "Allow Selected Items" emailAddressLogin: "Login with email address" usernameLogin: "Login with username" +autoloadDrafts: "Automatically load drafts when opening the posting form" +drafts: "Drafts" _bubbleGame: howToPlay: "How to play" diff --git a/locales/index.d.ts b/locales/index.d.ts index 045db3c84d..0143fadeed 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5330,6 +5330,14 @@ export interface Locale extends ILocale { * ユーザー名でログイン */ "usernameLogin": string; + /** + * 投稿フォームを開いたときに下書きを自動で読み込む + */ + "autoloadDrafts": string; + /** + * 下書き + */ + "drafts": string; "_bubbleGame": { /** * 遊び方 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 7465bdbb08..df14061cd3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1326,6 +1326,8 @@ consentAll: "全て許可" consentSelected: "選択した項目のみ許可" emailAddressLogin: "メールアドレスでログイン" usernameLogin: "ユーザー名でログイン" +autoloadDrafts: "投稿フォームを開いたときに下書きを自動で読み込む" +drafts: "下書き" _bubbleGame: howToPlay: "遊び方" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index b25f4ec781..929e6ab6ef 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1314,6 +1314,8 @@ consentAll: "모두 허용" consentSelected: "선택한 항목만 허용" emailAddressLogin: "이메일 주소로 로그인" usernameLogin: "사용자명으로 로그인" +autoloadDrafts: "글 작성 시 자동으로 임시 저장된 글 불러오기" +drafts: "임시 저장" _bubbleGame: howToPlay: "설명" diff --git a/packages/frontend/src/components/MkDraftsDialog.vue b/packages/frontend/src/components/MkDraftsDialog.vue new file mode 100644 index 0000000000..755bb2cf12 --- /dev/null +++ b/packages/frontend/src/components/MkDraftsDialog.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 8eacf4f177..94da01eb77 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -41,6 +41,9 @@ SPDX-License-Identifier: AGPL-3.0-only +