From 01a5300be88665ae414c15e8e0667c93799d8f0e Mon Sep 17 00:00:00 2001 From: dakkar Date: Sat, 18 Jan 2025 12:51:38 +0000 Subject: handle more complex ruby from/to html - fixes #605 this is not exactly great, but it should be "good enough" note that the new `group` function should not escape in the wild, as we don't document it and only use it internally I tried using `$[scale foo bar]` instead of `$[group foo bar]`, but that would be rendered as `foo bar` when sent over the network to non-misskey instances, and we don't want that --- packages/backend/src/core/MfmService.ts | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'packages/backend/src') diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 42676d6f98..48995672c5 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -230,6 +230,67 @@ export class MfmService { break; } + case 'rp': break + case 'rt': { + appendChildren(node.childNodes); + break; + } + case 'ruby': { + if (node.childNodes) { + /* + we get: + ``` + + some text ( annotation ) + more text more annotation + + ``` + + and we want to produce: + ``` + $[ruby $[group some text] annotation] + $[ruby $[group more text] more annotation] + ``` + + that `group` is a hack, because when the `ruby` render + sees just text inside the `$[ruby]`, it splits on + whitespace, considers the first "word" to be the main + content, and the rest the annotation + + with that `group`, we force it to consider the whole + group as the main content + + (note that the `rp` are to be ignored, they only exist + for browsers who don't understand ruby) + */ + let nonRtNodes=[]; + // scan children, ignore `rp`, split on `rt` + for (const child of node.childNodes) { + if (treeAdapter.isTextNode(child)) { + nonRtNodes.push(child); + continue; + } + if (!treeAdapter.isElementNode(child)) { + continue; + } + if (child.nodeName == 'rp') { + continue; + } + if (child.nodeName == 'rt') { + text += '$[ruby $[group '; + appendChildren(nonRtNodes); + text += '] '; + analyze(child); + text += '] '; + nonRtNodes=[]; + continue; + } + nonRtNodes.push(child); + } + } + break; + } + default: // includes inline elements { appendChildren(node.childNodes); @@ -348,6 +409,12 @@ export class MfmService { } } + case 'group': { // this is mostly a hack for `ruby` + const el = doc.createElement('span'); + appendChildren(node.children, el); + return el; + } + default: { return fnDefault(node); } -- cgit v1.2.3-freya From 68adb6c05d9c019c16ad7ae0370922475c0914b4 Mon Sep 17 00:00:00 2001 From: dakkar Date: Sat, 18 Jan 2025 14:54:17 +0000 Subject: pick lints --- packages/backend/src/core/MfmService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'packages/backend/src') diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 48995672c5..bc624daaee 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -230,7 +230,7 @@ export class MfmService { break; } - case 'rp': break + case 'rp': break; case 'rt': { appendChildren(node.childNodes); break; @@ -263,7 +263,7 @@ export class MfmService { (note that the `rp` are to be ignored, they only exist for browsers who don't understand ruby) */ - let nonRtNodes=[]; + let nonRtNodes = []; // scan children, ignore `rp`, split on `rt` for (const child of node.childNodes) { if (treeAdapter.isTextNode(child)) { @@ -273,16 +273,16 @@ export class MfmService { if (!treeAdapter.isElementNode(child)) { continue; } - if (child.nodeName == 'rp') { + if (child.nodeName === 'rp') { continue; } - if (child.nodeName == 'rt') { + if (child.nodeName === 'rt') { text += '$[ruby $[group '; appendChildren(nonRtNodes); text += '] '; analyze(child); text += '] '; - nonRtNodes=[]; + nonRtNodes = []; continue; } nonRtNodes.push(child); -- cgit v1.2.3-freya From 408e2f824a686defd3ea96cc5782b6b59779b7b9 Mon Sep 17 00:00:00 2001 From: dakkar Date: Sun, 19 Jan 2025 11:15:01 +0000 Subject: format ruby for masto api --- packages/backend/src/core/MfmService.ts | 68 +++++++++++++++++++++++++++++--- packages/backend/test/unit/MfmService.ts | 38 ++++++++++++++++++ 2 files changed, 100 insertions(+), 6 deletions(-) (limited to 'packages/backend/src') diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index bc624daaee..dc47e38562 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -409,7 +409,9 @@ export class MfmService { } } - case 'group': { // this is mostly a hack for `ruby` + // hack for ruby, should never be needed because we should + // never send this out to other instances + case 'group': { const el = doc.createElement('span'); appendChildren(node.children, el); return el; @@ -593,11 +595,65 @@ export class MfmService { }, async fn(node) { - const el = doc.createElement('span'); - el.textContent = '*'; - await appendChildren(node.children, el); - el.textContent += '*'; - return el; + switch (node.props.name) { + case 'group': { // hack for ruby + const el = doc.createElement('span'); + await appendChildren(node.children, el); + return el; + } + case 'ruby': { + if (node.children.length === 1) { + const child = node.children[0]; + const text = child.type === 'text' ? child.props.text : ''; + const rubyEl = doc.createElement('ruby'); + const rtEl = doc.createElement('rt'); + + const rpStartEl = doc.createElement('rp'); + rpStartEl.appendChild(doc.createTextNode('(')); + const rpEndEl = doc.createElement('rp'); + rpEndEl.appendChild(doc.createTextNode(')')); + + rubyEl.appendChild(doc.createTextNode(text.split(' ')[0])); + rtEl.appendChild(doc.createTextNode(text.split(' ')[1])); + rubyEl.appendChild(rpStartEl); + rubyEl.appendChild(rtEl); + rubyEl.appendChild(rpEndEl); + return rubyEl; + } else { + const rt = node.children.at(-1); + + if (!rt) { + const el = doc.createElement('span'); + await appendChildren(node.children, el); + return el; + } + + const text = rt.type === 'text' ? rt.props.text : ''; + const rubyEl = doc.createElement('ruby'); + const rtEl = doc.createElement('rt'); + + const rpStartEl = doc.createElement('rp'); + rpStartEl.appendChild(doc.createTextNode('(')); + const rpEndEl = doc.createElement('rp'); + rpEndEl.appendChild(doc.createTextNode(')')); + + await appendChildren(node.children.slice(0, node.children.length - 1), rubyEl); + rtEl.appendChild(doc.createTextNode(text.trim())); + rubyEl.appendChild(rpStartEl); + rubyEl.appendChild(rtEl); + rubyEl.appendChild(rpEndEl); + return rubyEl; + } + } + + default: { + const el = doc.createElement('span'); + el.textContent = '*'; + await appendChildren(node.children, el); + el.textContent += '*'; + return el; + } + } }, blockCode(node) { diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts index 93ce0672dc..5c3ffba422 100644 --- a/packages/backend/test/unit/MfmService.ts +++ b/packages/backend/test/unit/MfmService.ts @@ -47,12 +47,50 @@ describe('MfmService', () => { }); test('ruby', () => { + const input = '$[ruby some text ignore me]'; + const output = '

some(text)

'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); + + test('ruby2', () => { + const input = '$[ruby *some text* ignore me]'; + const output = '

some text(ignore me)

'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); + + test('ruby 3', () => { const input = '$[ruby $[group *some* text] ignore me]'; const output = '

some text(ignore me)

'; assert.equal(mfmService.toHtml(mfm.parse(input)), output); }); }); + describe('toMastoApiHtml', () => { + test('br', async () => { + const input = 'foo\nbar\nbaz'; + const output = '

foo
bar
baz

'; + assert.equal(await mfmService.toMastoApiHtml(mfm.parse(input)), output); + }); + + test('br alt', async () => { + const input = 'foo\r\nbar\rbaz'; + const output = '

foo
bar
baz

'; + assert.equal(await mfmService.toMastoApiHtml(mfm.parse(input)), output); + }); + + test('escape', async () => { + const input = '```\n

Hello, world!

\n```'; + const output = '

<p>Hello, world!</p>

'; + assert.equal(await mfmService.toMastoApiHtml(mfm.parse(input)), output); + }); + + test('ruby', async () => { + const input = '$[ruby $[group *some* text] ignore me]'; + const output = '

*some* text(ignore me)

'; + assert.equal(await mfmService.toMastoApiHtml(mfm.parse(input)), output); + }); + }); + describe('fromHtml', () => { test('p', () => { assert.deepStrictEqual(mfmService.fromHtml('

a

b

'), 'a\n\nb'); -- cgit v1.2.3-freya From 1080f19e99ffccdebb09c39035d93d9b561ef0e6 Mon Sep 17 00:00:00 2001 From: piuvas Date: Fri, 24 Jan 2025 18:37:18 -0300 Subject: generalize current language so we match more broadly on fallback --- packages/backend/src/server/web/boot.embed.js | 2 +- packages/backend/src/server/web/boot.js | 2 +- packages/frontend/src/_dev_boot_.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/backend/src') diff --git a/packages/backend/src/server/web/boot.embed.js b/packages/backend/src/server/web/boot.embed.js index b07dce3ac4..1af1dc545b 100644 --- a/packages/backend/src/server/web/boot.embed.js +++ b/packages/backend/src/server/web/boot.embed.js @@ -48,7 +48,7 @@ if (supportedLangs.includes(navigator.language)) { lang = navigator.language; } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); + lang = supportedLangs.find(x => x.split('-')[0] === navigator.language.split('-')[0]); // Fallback if (lang == null) lang = 'en-US'; diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index bf83340bde..54750e26e5 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -39,7 +39,7 @@ if (supportedLangs.includes(navigator.language)) { lang = navigator.language; } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); + lang = supportedLangs.find(x => x.split('-')[0] === navigator.language.split('-')[0]); // Fallback if (lang == null) lang = 'en-US'; diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts index f312765dcf..ebce7e735f 100644 --- a/packages/frontend/src/_dev_boot_.ts +++ b/packages/frontend/src/_dev_boot_.ts @@ -25,7 +25,7 @@ async function main() { if (supportedLangs.includes(navigator.language)) { lang = navigator.language; } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); + lang = supportedLangs.find(x => x.split('-')[0] === navigator.language.split('-')[0]); // Fallback if (lang == null) lang = 'en-US'; -- cgit v1.2.3-freya From 83f2d93d30100f38df2ed34807ec5d8d80a19c4b Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sat, 1 Feb 2025 18:53:32 -0500 Subject: increase rate limit on federation/update-remote-user --- .../src/server/api/endpoints/federation/update-remote-user.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'packages/backend/src') diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 3ec9522c44..5217f79065 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -13,10 +13,11 @@ export const meta = { requireCredential: false, - // 2 calls per second + // Up to 10 calls, then 4 / second. + // This allows for reliable automation. limit: { - duration: 1000, - max: 2, + max: 10, + dripRate: 250, }, } as const; -- cgit v1.2.3-freya From bd716ed8377ab160abf23f60ccc43affebc87882 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sat, 1 Feb 2025 23:56:41 -0500 Subject: increase the rate limit for `/api/i` endpoint, preventing some 429 errors if multiple tabs reload simultaneously --- packages/backend/src/server/api/endpoints/i.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'packages/backend/src') diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 9347c9ca27..48a2e3b40a 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -31,10 +31,12 @@ export const meta = { }, }, - // 3 calls per second + // up to 20 calls, then 1 per second. + // This handles bursty traffic when all tabs reload as a group limit: { - duration: 1000, - max: 3, + max: 20, + dripSize: 1, + dripRate: 1000, }, } as const; -- cgit v1.2.3-freya From 1bedf954f296a7151e0e878cecb0154508c93967 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 27 Jan 2025 01:57:14 -0500 Subject: increase rate limit for `users/show` endpoint --- packages/backend/src/server/api/endpoints/users/show.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/backend/src') diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 7ebca78a7d..118362149d 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -57,10 +57,10 @@ export const meta = { }, }, - // 5 calls per 2 seconds + // up to 50 calls @ 4 per second limit: { - duration: 1000 * 2, - max: 5, + max: 50, + dripRate: 250, }, } as const; -- cgit v1.2.3-freya From 74407bc8ee43a8c7b4bc8b7e16bdfb8acd2c794c Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sat, 1 Feb 2025 17:07:34 -0500 Subject: add MiUserProfile.defaultCW property and API --- locales/index.d.ts | 2 +- .../1738446745738-add_user_profile_default_cw.js | 11 +++++++++++ packages/backend/src/core/entities/UserEntityService.ts | 1 + packages/backend/src/models/UserProfile.ts | 10 ++++++++-- packages/backend/src/models/json-schema/user.ts | 4 ++++ packages/backend/src/server/api/endpoints/i/update.ts | 17 +++++++++++++++++ packages/misskey-js/src/autogen/types.ts | 5 +++++ 7 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 packages/backend/migration/1738446745738-add_user_profile_default_cw.js (limited to 'packages/backend/src') diff --git a/locales/index.d.ts b/locales/index.d.ts index 70eba52ea0..af5faefe1a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -11631,7 +11631,7 @@ export interface Locale extends ILocale { */ "robotsTxt": string; /** - * Adding entries here will override the default robots.txt packaged with Sharkey. Maximum 2048 characters. + * Adding entries here will override the default robots.txt packaged with Sharkey. */ "robotsTxtDescription": string; } diff --git a/packages/backend/migration/1738446745738-add_user_profile_default_cw.js b/packages/backend/migration/1738446745738-add_user_profile_default_cw.js new file mode 100644 index 0000000000..205ca2087a --- /dev/null +++ b/packages/backend/migration/1738446745738-add_user_profile_default_cw.js @@ -0,0 +1,11 @@ +export class AddUserProfileDefaultCw1738446745738 { + name = 'AddUserProfileDefaultCw1738446745738' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "default_cw" text`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "default_cw"`); + } +} diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 6bfe865038..0ca784fa52 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -669,6 +669,7 @@ export class UserEntityService implements OnModuleInit { achievements: profile!.achievements, loggedInDays: profile!.loggedInDates.length, policies: this.roleService.getUserPolicies(user.id), + defaultCW: profile?.defaultCW ?? null, } : {}), ...(opts.includeSecrets ? { diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 751b1aff08..3c2362227e 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -36,10 +36,10 @@ export class MiUserProfile { }) public birthday: string | null; - @Column("varchar", { + @Column('varchar', { length: 128, nullable: true, - comment: "The ListenBrainz username of the User.", + comment: 'The ListenBrainz username of the User.', }) public listenbrainz: string | null; @@ -290,6 +290,12 @@ export class MiUserProfile { unlockedAt: number; }[]; + @Column('text', { + name: 'default_cw', + nullable: true, + }) + public defaultCW: string | null; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index f953008b3f..f6c7bd2151 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -752,6 +752,10 @@ export const packedMeDetailedOnlySchema = { }, }, //#endregion + defaultCW: { + type: 'string', + nullable: true, optional: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 09c06a108d..e487562687 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -133,6 +133,12 @@ export const meta = { id: '0b3f9f6a-2f4d-4b1f-9fb4-49d3a2fd7191', httpStatusCode: 422, }, + + maxCwLength: { + message: 'You tried setting a default content warning which is too long.', + code: 'MAX_CW_LENGTH', + id: '7004c478-bda3-4b4f-acb2-4316398c9d52', + }, }, res: { @@ -243,6 +249,7 @@ export const paramDef = { uniqueItems: true, items: { type: 'string' }, }, + defaultCW: { type: 'string', nullable: true }, }, } as const; @@ -494,6 +501,16 @@ export default class extends Endpoint { // eslint- updates.alsoKnownAs = newAlsoKnownAs.size > 0 ? Array.from(newAlsoKnownAs) : null; } + let defaultCW = ps.defaultCW; + if (defaultCW !== undefined) { + if (defaultCW === '') defaultCW = null; + if (defaultCW && defaultCW.length > this.config.maxCwLength) { + throw new ApiError(meta.errors.maxCwLength); + } + + profileUpdates.defaultCW = defaultCW; + } + //#region emojis/tags let emojis = [] as string[]; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 888e46e008..78dac5f08b 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4217,6 +4217,7 @@ export type components = { /** Format: date-time */ lastUsed: string; }[]; + defaultCW: string | null; }; UserDetailedNotMe: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly']; MeDetailed: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'] & components['schemas']['MeDetailedOnly']; @@ -5224,6 +5225,7 @@ export type components = { enableFC: boolean; fcSiteKey: string | null; enableAchievements: boolean | null; + robotsTxt: string | null; enableTestcaptcha: boolean; swPublickey: string | null; /** @default /assets/ai.png */ @@ -5434,6 +5436,7 @@ export type operations = { enableStatsForFederatedInstances: boolean; enableServerMachineStats: boolean; enableAchievements: boolean; + robotsTxt: string | null; enableIdenticonGeneration: boolean; manifestJsonOverride: string; policies: Record; @@ -10163,6 +10166,7 @@ export type operations = { enableStatsForFederatedInstances?: boolean; enableServerMachineStats?: boolean; enableAchievements?: boolean; + robotsTxt?: string | null; enableIdenticonGeneration?: boolean; serverRules?: string[]; bannedEmailDomains?: string[]; @@ -21631,6 +21635,7 @@ export type operations = { }; emailNotificationTypes?: string[]; alsoKnownAs?: string[]; + defaultCW?: string | null; }; }; }; -- cgit v1.2.3-freya From c8f8a61a00d07802dc5056eae48144e49bce742c Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sat, 1 Feb 2025 23:15:02 -0500 Subject: add MiUserProfile.defaultCWPriority property and API --- .../1738468079662-add_user_profile_default_cw_priority.js | 13 +++++++++++++ packages/backend/src/core/entities/UserEntityService.ts | 7 +++++-- packages/backend/src/models/UserProfile.ts | 9 ++++++++- packages/backend/src/models/json-schema/user.ts | 5 +++++ packages/backend/src/server/api/endpoints/i/update.ts | 8 ++++++++ packages/backend/src/types.ts | 2 ++ packages/misskey-js/src/autogen/types.ts | 4 ++++ 7 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 packages/backend/migration/1738468079662-add_user_profile_default_cw_priority.js (limited to 'packages/backend/src') diff --git a/packages/backend/migration/1738468079662-add_user_profile_default_cw_priority.js b/packages/backend/migration/1738468079662-add_user_profile_default_cw_priority.js new file mode 100644 index 0000000000..90de25e06f --- /dev/null +++ b/packages/backend/migration/1738468079662-add_user_profile_default_cw_priority.js @@ -0,0 +1,13 @@ +export class AddUserProfileDefaultCwPriority1738468079662 { + name = 'AddUserProfileDefaultCwPriority1738468079662' + + async up(queryRunner) { + await queryRunner.query(`CREATE TYPE "public"."user_profile_default_cw_priority_enum" AS ENUM ('default', 'parent', 'defaultParent', 'parentDefault')`); + await queryRunner.query(`ALTER TABLE "user_profile" ADD "default_cw_priority" "public"."user_profile_default_cw_priority_enum" NOT NULL DEFAULT 'parent'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "default_cw_priority"`); + await queryRunner.query(`DROP TYPE "public"."user_profile_default_cw_priority_enum"`); + } +} diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 0ca784fa52..6ea2d6629a 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -49,11 +49,13 @@ import { IdService } from '@/core/IdService.js'; import type { AnnouncementService } from '@/core/AnnouncementService.js'; import type { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; +import { isSystemAccount } from '@/misc/is-system-account.js'; import type { OnModuleInit } from '@nestjs/common'; import type { NoteEntityService } from './NoteEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; import type { PageEntityService } from './PageEntityService.js'; -import { isSystemAccount } from '@/misc/is-system-account.js'; + +/* eslint-disable @typescript-eslint/no-non-null-assertion */ const Ajv = _Ajv.default; const ajv = new Ajv(); @@ -669,7 +671,8 @@ export class UserEntityService implements OnModuleInit { achievements: profile!.achievements, loggedInDays: profile!.loggedInDates.length, policies: this.roleService.getUserPolicies(user.id), - defaultCW: profile?.defaultCW ?? null, + defaultCW: profile!.defaultCW, + defaultCWPriority: profile!.defaultCWPriority, } : {}), ...(opts.includeSecrets ? { diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 3c2362227e..449c2f370b 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -4,7 +4,7 @@ */ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; -import { obsoleteNotificationTypes, followingVisibilities, followersVisibilities, notificationTypes } from '@/types.js'; +import { obsoleteNotificationTypes, followingVisibilities, followersVisibilities, notificationTypes, noteVisibilities, defaultCWPriorities } from '@/types.js'; import { id } from './util/id.js'; import { MiUser } from './User.js'; import { MiPage } from './Page.js'; @@ -296,6 +296,13 @@ export class MiUserProfile { }) public defaultCW: string | null; + @Column('enum', { + name: 'default_cw_priority', + enum: defaultCWPriorities, + default: 'parent', + }) + public defaultCWPriority: typeof defaultCWPriorities[number]; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index f6c7bd2151..93b031e9c5 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -756,6 +756,11 @@ export const packedMeDetailedOnlySchema = { type: 'string', nullable: true, optional: false, }, + defaultCWPriority: { + type: 'string', + enum: ['default', 'parent', 'defaultParent', 'parentDefault'], + nullable: false, optional: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index e487562687..e1552fed8a 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -250,6 +250,11 @@ export const paramDef = { items: { type: 'string' }, }, defaultCW: { type: 'string', nullable: true }, + defaultCWPriority: { + type: 'string', + enum: ['default', 'parent', 'defaultParent', 'parentDefault'], + nullable: false, + }, }, } as const; @@ -510,6 +515,9 @@ export default class extends Endpoint { // eslint- profileUpdates.defaultCW = defaultCW; } + if (ps.defaultCWPriority !== undefined) { + profileUpdates.defaultCWPriority = ps.defaultCWPriority; + } //#region emojis/tags diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 37bed27fb1..067481d9da 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -58,6 +58,8 @@ export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const; export const followingVisibilities = ['public', 'followers', 'private'] as const; export const followersVisibilities = ['public', 'followers', 'private'] as const; +export const defaultCWPriorities = ['default', 'parent', 'defaultParent', 'parentDefault'] as const; + /** * ユーザーがエクスポートできるものの種類 * diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 78dac5f08b..c7268ade6a 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4218,6 +4218,8 @@ export type components = { lastUsed: string; }[]; defaultCW: string | null; + /** @enum {string} */ + defaultCWPriority: 'default' | 'parent' | 'defaultParent' | 'parentDefault'; }; UserDetailedNotMe: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly']; MeDetailed: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'] & components['schemas']['MeDetailedOnly']; @@ -21636,6 +21638,8 @@ export type operations = { emailNotificationTypes?: string[]; alsoKnownAs?: string[]; defaultCW?: string | null; + /** @enum {string} */ + defaultCWPriority?: 'default' | 'parent' | 'defaultParent' | 'parentDefault'; }; }; }; -- cgit v1.2.3-freya