From c026822072bb1837022ee2f8df6f17adb4b2a471 Mon Sep 17 00:00:00 2001 From: NoriDev Date: Thu, 6 Feb 2025 04:18:46 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=AA=A8=EC=96=91=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=97=B0=ED=95=A9=20=20=20-=20=EC=9E=90=EC=8B=A0=EC=9D=98=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EB=AA=A8=EC=96=91(=EC=9B=90=ED=98=95,=20=EC=82=AC?= =?UTF-8?q?=EA=B0=81=ED=98=95)=EC=9D=84=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EB=B0=8F=20=EC=9B=90=EA=B2=A9=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EC=97=90=EA=B2=8C=20=EC=97=B0?= =?UTF-8?q?=ED=95=A9=EC=8B=9C=ED=82=AC=20=EC=88=98=20=EC=9E=88=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.=20=20=20=20=20-=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EB=BF=90=EB=A7=8C=20=EC=95=84?= =?UTF-8?q?=EB=8B=88=EB=9D=BC,=20CherryPick=20`4.15.0`=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81=20=EB=B2=84=EC=A0=84=EC=9D=84=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=84=9C=EB=B2=84=EC=97=90=EC=84=9C?= =?UTF-8?q?=EB=8A=94=20=ED=95=B4=EB=8B=B9=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EC=A7=80=EC=A0=95=ED=95=9C=20=EB=AA=A8=EC=96=91?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=94=84=EB=A1=9C=ED=95=84=EC=9D=B4=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=EB=90=A9=EB=8B=88=EB=8B=A4.=20=20=20-=20Cher?= =?UTF-8?q?ryPick=20`4.15.0`=20=EC=9D=B4=EC=83=81=20=EB=B2=84=EC=A0=84?= =?UTF-8?q?=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EC=97=90=EC=84=9C=EB=A7=8C=20=EC=9D=B4=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EC=8A=B5=EB=8B=88=EB=8B=A4.=20=20=20-=20=EC=9D=B4=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EC=9D=80=20=EC=84=9C=EB=B2=84=EB=A7=88?= =?UTF-8?q?=EB=8B=A4=20=EC=B6=94=EA=B5=AC=ED=95=98=EB=8A=94=20=EB=B0=A9?= =?UTF-8?q?=ED=96=A5=EC=9D=B4=20=EB=8B=A4=EB=A5=BC=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EA=B8=B0=20=EB=95=8C=EB=AC=B8=EC=97=90,=20=EC=97=AD=ED=95=A0?= =?UTF-8?q?=EC=97=90=EC=84=9C=20`=ED=94=84=EB=A1=9C=ED=95=84=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=AA=A8=EC=96=91=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=97=B0=ED=95=A9=20=ED=97=88=EC=9A=A9`=EC=9D=B4=20=EC=A0=9C?= =?UTF-8?q?=ED=95=9C=EB=90=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=97=90=EC=84=9C=EB=A7=8C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EC=8A=B5=EB=8B=88=EB=8B=A4.?= =?UTF-8?q?=20=20=20=20=20-=20=EC=9D=B4=20=EC=97=AD=ED=95=A0=EC=9D=B4=20?= =?UTF-8?q?=EA=BA=BC=EC=A0=B8=EC=9E=88=EC=9C=BC=EB=A9=B4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EB=AA=A8?= =?UTF-8?q?=EC=96=91=20=EC=84=A4=EC=A0=95=EC=9D=B4=20=EC=97=B0=ED=95=A9?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EC=9C=BC=EB=A9=B0,=20=EB=AA=A8?= =?UTF-8?q?=EB=93=A0=20=EC=82=AC=EC=9A=A9=EC=9E=90=EC=9D=98=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EB=AA=A8?= =?UTF-8?q?=EC=96=91=EC=9D=B4=20`=ED=94=84=EB=A1=9C=ED=95=84=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=EC=9D=84=20=EC=82=AC=EA=B0=81=ED=98=95?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=91=9C=EC=8B=9C`=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=ED=91=9C=EC=8B=9C=EB=90=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG_CHERRYPICK.md | 6 +++ locales/en-US.yml | 3 ++ locales/index.d.ts | 12 ++++++ locales/ja-JP.yml | 3 ++ locales/ko-KR.yml | 3 ++ .../1738776889000-setRemoteAvatarShape.js | 18 +++++++++ packages/backend/src/core/RoleService.ts | 3 ++ .../activitypub/models/ApPersonService.ts | 4 +- packages/backend/src/core/activitypub/type.ts | 2 + .../src/core/entities/UserEntityService.ts | 2 + packages/backend/src/models/User.ts | 10 +++++ .../backend/src/models/json-schema/role.ts | 4 ++ .../backend/src/models/json-schema/user.ts | 8 ++++ .../server/api/endpoints/admin/show-user.ts | 10 +++++ .../src/server/api/endpoints/i/update.ts | 5 +++ packages/cherrypick-js/src/autogen/types.ts | 7 ++++ packages/frontend-shared/js/const.ts | 1 + packages/frontend/src/boot/main-boot.ts | 10 +++++ .../frontend/src/components/MkUserPopup.vue | 2 +- .../src/components/global/MkAvatar.vue | 3 +- .../frontend/src/pages/admin/roles.editor.vue | 20 ++++++++++ packages/frontend/src/pages/admin/roles.vue | 8 ++++ .../src/pages/settings/appearance.vue | 38 +++++++++++++++---- .../pages/settings/preferences-backups.vue | 1 + packages/frontend/src/store.ts | 4 ++ 25 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 packages/backend/migration/1738776889000-setRemoteAvatarShape.js diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md index 1854bc6693..b16b7ef4a9 100644 --- a/CHANGELOG_CHERRYPICK.md +++ b/CHANGELOG_CHERRYPICK.md @@ -31,6 +31,12 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2024xx](CHANGE ### General - Feat: 계정 정리 기능 ([yodangang-express/cherrypick@dc51c907](https://github.com/yodangang-express/cherrypick/commit/dc51c907236570d6f072409832d312c937239514)) - `다이렉트 메시지` 및 `고정된 노트`와 관련된 파일을 제외한 모든 노트와 파일을 자동으로 삭제할 수 있음 +- Feat: 프로필 아이콘 모양 설정 연합 + - 자신의 프로필 아이콘 모양(원형, 사각형)을 로컬 사용자 및 원격 사용자에게 연합시킬 수 있습니다. + - 로컬 사용자 뿐만 아니라, CherryPick `4.15.0` 이상 버전을 사용하는 서버에서는 해당 사용자가 지정한 모양으로 프로필이 표시됩니다. + - CherryPick `4.15.0` 이상 버전을 사용하는 서버에서만 이 기능을 사용할 수 있습니다. + - 이 설정은 서버마다 추구하는 방향이 다를 수 있기 때문에, 역할에서 `프로필 아이콘 모양 설정 연합 허용`이 제한되지 않은 상태에서만 사용할 수 있습니다. + - 이 역할이 꺼져있으면 프로필 아이콘 모양 설정이 연합되지 않으며, 모든 사용자의 프로필 아이콘 모양이 `프로필 아이콘을 사각형으로 표시` 설정에 따라 표시됩니다. ### Client - Enhance: 사용자 페이지에서 `이름`, `자기소개`, `팔로우 메시지`, `추가 정보`에 포함된 외부 이모지를 가져올 수 있음 diff --git a/locales/en-US.yml b/locales/en-US.yml index 39857b646e..bc4d1aed5d 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1,5 +1,7 @@ --- _lang_: "English" +setFederationAvatarShape: "Federation of avatar shape settings" +setFederationAvatarShapeDescription: "You can federation the shape of the avatar shape settings(circle, square) for your local and remote users." keyboardShortcuts: "Keyboard shortcuts" truncateAccount: "Truncate account" truncateAccountConfirm: "All notes and files will be deleted except those associated with direct messages and pinned notes. Still continue?" @@ -2073,6 +2075,7 @@ _role: canImportFollowing: "Allow importing following" canImportMuting: "Allow importing muting" canImportUserLists: "Allow importing lists" + canSetFederationAvatarShape: "Allow federation of avatar shape settings" _condition: roleAssignedTo: "Assigned to manual roles" isLocal: "Local user" diff --git a/locales/index.d.ts b/locales/index.d.ts index 47a405f578..727bd157a6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -13,6 +13,14 @@ export interface Locale extends ILocale { * 日本語 */ "_lang_": string; + /** + * アイコンの形設定の連合 + */ + "setFederationAvatarShape": string; + /** + * 自分のアイコンの形(円形、四角形)をローカルユーザおよびリモートユーザに連合させることができます。 + */ + "setFederationAvatarShapeDescription": string; /** * キーボードショートカット */ @@ -8102,6 +8110,10 @@ export interface Locale extends ILocale { * リストのインポートを許可 */ "canImportUserLists": string; + /** + * アイコンの形設定の連合を許可 + */ + "canSetFederationAvatarShape": string; }; "_condition": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0f038133c7..0143984316 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1,5 +1,7 @@ _lang_: "日本語" +setFederationAvatarShape: "アイコンの形設定の連合" +setFederationAvatarShapeDescription: "自分のアイコンの形(円形、四角形)をローカルユーザおよびリモートユーザに連合させることができます。" keyboardShortcuts: "キーボードショートカット" truncateAccount: "アカウント整理" truncateAccountConfirm: "ダイレクトメッセージと固定されたノートに関連付けられているファイルを除くすべてのノートとファイルが削除されます。それでも続行しますか?" @@ -2095,6 +2097,7 @@ _role: canImportFollowing: "フォローのインポートを許可" canImportMuting: "ミュートのインポートを許可" canImportUserLists: "リストのインポートを許可" + canSetFederationAvatarShape: "アイコンの形設定の連合を許可" _condition: roleAssignedTo: "マニュアルロールにアサイン済み" isLocal: "ローカルユーザー" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 733ceebdcb..3dda6fe21e 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1,5 +1,7 @@ --- _lang_: "한국어" +setFederationAvatarShape: "프로필 아이콘 모양 설정 연합" +setFederationAvatarShapeDescription: "내 프로필 아이콘의 모양(원형, 사각형)을 로컬 사용자 및 원격 사용자에게 연합시킬 수 있어요." keyboardShortcuts: "키보드 단축키" truncateAccount: "계정 정리" truncateAccountConfirm: "다이렉트 메시지 및 고정된 노트와 관련된 파일을 제외한 모든 노트와 파일이 삭제돼요. 그래도 계속할까요?" @@ -2073,6 +2075,7 @@ _role: canImportFollowing: "팔로우 가져오기 허용" canImportMuting: "뮤트 목록 가져오기 허용" canImportUserLists: "리스트 목록 가져오기 허용" + canSetFederationAvatarShape: "프로필 아이콘 모양 설정 연합 허용" _condition: roleAssignedTo: "수동 역할에 이미 할당됨" isLocal: "로컬 사용자" diff --git a/packages/backend/migration/1738776889000-setRemoteAvatarShape.js b/packages/backend/migration/1738776889000-setRemoteAvatarShape.js new file mode 100644 index 0000000000..1f4ce5f317 --- /dev/null +++ b/packages/backend/migration/1738776889000-setRemoteAvatarShape.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class setFederationAvatarShape1738776889000 { + name = 'setFederationAvatarShape1738776889000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "setFederationAvatarShape" boolean NOT NULL DEFAULT true`); + await queryRunner.query(`ALTER TABLE "user" ADD "isSquareAvatars" boolean NOT NULL DEFAULT true`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "setFederationAvatarShape"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isSquareAvatars"`); + } +} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index e7fa9e677e..db0f011090 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -67,6 +67,7 @@ export type RolePolicies = { canImportFollowing: boolean; canImportMuting: boolean; canImportUserLists: boolean; + canSetFederationAvatarShape: boolean; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -105,6 +106,7 @@ export const DEFAULT_POLICIES: RolePolicies = { canImportFollowing: true, canImportMuting: true, canImportUserLists: true, + canSetFederationAvatarShape: true, }; @Injectable() @@ -416,6 +418,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { canImportFollowing: calc('canImportFollowing', vs => vs.some(v => v === true)), canImportMuting: calc('canImportMuting', vs => vs.some(v => v === true)), canImportUserLists: calc('canImportUserLists', vs => vs.some(v => v === true)), + canSetFederationAvatarShape: calc('canSetFederationAvatarShape', vs => vs.some(v => v === true)), }; } diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 9dce8c410d..bb1d962f5d 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -470,6 +470,8 @@ export class ApPersonService implements OnModuleInit { makeNotesFollowersOnlyBefore: (person as any).makeNotesFollowersOnlyBefore ?? null, makeNotesHiddenBefore: (person as any).makeNotesHiddenBefore ?? null, emojis, + setFederationAvatarShape: person.setFederationAvatarShape, + isSquareAvatars: person.isSquareAvatars, })) as MiRemoteUser; let _description: string | null = null; @@ -719,7 +721,7 @@ export class ApPersonService implements OnModuleInit { alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), - } as Partial & Pick; + } as Partial & Pick; const moving = ((): boolean => { // 移行先がない→ある diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index e4930dd9d8..5c8911ca20 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -202,6 +202,8 @@ export interface IActor extends IObject { }; 'vcard:bday'?: string; 'vcard:Address'?: string; + setFederationAvatarShape?: boolean, + isSquareAvatars?: boolean, } export const isCollection = (object: IObject): object is ICollection => diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index d678a9dc4c..972576aa8f 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -553,6 +553,8 @@ export class UserEntityService implements OnModuleInit { displayOrder: r.displayOrder, })), ) : undefined, + setFederationAvatarShape: user.setFederationAvatarShape, + isSquareAvatars: user.isSquareAvatars, ...(isDetailed ? { url: profile!.url, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index a5b1faab9d..79c93f9f0b 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -278,6 +278,16 @@ export class MiUser { }) public token: string | null; + @Column('boolean', { + default: true, + }) + public setFederationAvatarShape: boolean; + + @Column('boolean', { + default: true, + }) + public isSquareAvatars: boolean; + constructor(data: Partial) { if (data == null) return; diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index 3b11ade641..0f0aa2a0a2 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -304,6 +304,10 @@ export const packedRolePoliciesSchema = { type: 'integer', optional: false, nullable: false, }, + canSetFederationAvatarShape: { + type: 'boolean', + optional: false, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index a67d5af935..8a0e82514a 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -667,6 +667,14 @@ export const packedMeDetailedOnlySchema = { nullable: false, optional: false, default: false, }, + setFederationAvatarShape: { + type: 'boolean', + nullable: false, optional: false, + }, + isSquareAvatars: { + type: 'boolean', + nullable: false, optional: false, + }, //#region secrets email: { type: 'string', diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index f1cc3324a5..22d324b5b4 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -177,6 +177,14 @@ export const meta = { }, }, }, + setFederationAvatarShape: { + type: 'boolean', + optional: false, nullable: false, + }, + isSquareAvatars: { + type: 'boolean', + optional: false, nullable: false, + }, }, }, } as const; @@ -257,6 +265,8 @@ export default class extends Endpoint { // eslint- expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null, roleId: a.roleId, })), + setFederationAvatarShape: user.setFederationAvatarShape, + isSquareAvatars: user.isSquareAvatars, }; }); } diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 7694e21914..576d9adca6 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -228,6 +228,8 @@ export const paramDef = { uniqueItems: true, items: { type: 'string' }, }, + setFederationAvatarShape: { type: 'boolean' }, + isSquareAvatars: { type: 'boolean' }, }, } as const; @@ -462,6 +464,9 @@ export default class extends Endpoint { // eslint- updates.alsoKnownAs = newAlsoKnownAs.size > 0 ? Array.from(newAlsoKnownAs) : null; } + if (typeof ps.setFederationAvatarShape === 'boolean') updates.setFederationAvatarShape = ps.setFederationAvatarShape; + if (typeof ps.isSquareAvatars === 'boolean') updates.isSquareAvatars = ps.isSquareAvatars; + //#region emojis/tags let emojis = [] as string[]; diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index a4961b096f..88bd0816e2 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -4383,6 +4383,8 @@ export type components = { usePasswordLessLogin: boolean; /** @default false */ securityKeys: boolean; + setFederationAvatarShape: boolean; + isSquareAvatars: boolean; email?: string | null; emailVerified?: boolean | null; securityKeysList?: { @@ -5356,6 +5358,7 @@ export type components = { canImportUserLists: boolean; canEditNote: boolean; scheduleNoteMax: number; + canSetFederationAvatarShape: boolean; }; ReversiGameLite: { /** Format: id */ @@ -10719,6 +10722,8 @@ export type operations = { expiresAt: string | null; roleId: string; })[]; + setFederationAvatarShape: boolean; + isSquareAvatars: boolean; }; }; }; @@ -22237,6 +22242,8 @@ export type operations = { }; emailNotificationTypes?: string[]; alsoKnownAs?: string[]; + setFederationAvatarShape?: boolean; + isSquareAvatars?: boolean; }; }; }; diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 6ae8cd7a79..9f898890a0 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -112,6 +112,7 @@ export const ROLE_POLICIES = [ 'canImportFollowing', 'canImportMuting', 'canImportUserLists', + 'canSetFederationAvatarShape', ] as const; // なんか動かない diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 80f9fd3e28..3bf507fc16 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -27,6 +27,7 @@ import { type Keymap, makeHotkey } from '@/scripts/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; import { userName } from '@/filters/user.js'; import { vibrate } from '@/scripts/vibrate.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; export async function mainBoot() { const { isClientUpdated, isClientMigrated } = await common(() => { @@ -423,6 +424,15 @@ export async function mainBoot() { main.on('myTokenRegenerated', () => { signout(); }); + + // 프로필 아이콘 모양 설정 연합 + if (!$i.policies.canSetFederationAvatarShape) { + await defaultStore.set('setFederationAvatarShape', false); + await misskeyApi('i/update', { + setFederationAvatarShape: false, + isSquareAvatars: defaultStore.state.squareAvatars, + }); + } } // shortcut diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index df1dcd9bd8..355b0dc014 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.followsYou }}
- + diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 27f0f80d63..4a9d91a3fd 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -108,7 +108,6 @@ import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue'; import { defaultStore } from '@/store.js'; const animation = ref(defaultStore.state.animation); -const squareAvatars = ref(defaultStore.state.squareAvatars); const props = withDefaults(defineProps<{ user: Misskey.entities.User; @@ -129,6 +128,8 @@ const props = withDefaults(defineProps<{ noteClick: false, }); +const squareAvatars = ref((!defaultStore.state.setFederationAvatarShape && defaultStore.state.squareAvatars) || (defaultStore.state.setFederationAvatarShape && props.user.setFederationAvatarShape && props.user.isSquareAvatars)); + const emit = defineEmits<{ (ev: 'click', v: MouseEvent): void; }>(); diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 9bc2fa7223..46c1aa5fcf 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -769,6 +769,26 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + {{ i18n.ts._role.useBaseValue }} + {{ role.policies.canSetFederationAvatarShape.value ? i18n.ts.yes : i18n.ts.no }} + + +
+ + + + + + + + + +
+ diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 7b3ab9beb5..c899544a43 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -290,6 +290,14 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + +
diff --git a/packages/frontend/src/pages/settings/appearance.vue b/packages/frontend/src/pages/settings/appearance.vue index 18ad79d22c..0d824a3d91 100644 --- a/packages/frontend/src/pages/settings/appearance.vue +++ b/packages/frontend/src/pages/settings/appearance.vue @@ -92,6 +92,10 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.highlightSensitiveMedia }} {{ i18n.ts.squareAvatars }} + + {{ i18n.ts.setFederationAvatarShape }} CherryPick + + {{ i18n.ts.showAvatarDecorations }} {{ i18n.ts.useSystemFont }} {{ i18n.ts.forceShowAds }} @@ -289,6 +293,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; import { globalEvents } from '@/events.js'; import { claimAchievement } from '@/scripts/achievements.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { $i } from '@/account.js'; // const fontSize = ref(miLocalStorage.getItem('fontSize')); const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); @@ -296,14 +302,6 @@ const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); const fontSizeBefore = ref(miLocalStorage.getItem('fontSize')); const useBoldFont = ref(miLocalStorage.getItem('useBoldFont')); -function reloadTimeline() { - globalEvents.emit('reloadTimeline'); -} - -function reloadNotification() { - globalEvents.emit('reloadNotification'); -} - const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover')); const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter')); const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); @@ -364,6 +362,7 @@ const showDoReactionButtonInNoteFooter = computed(defaultStore.makeGetterSetter( const showQuoteButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showQuoteButtonInNoteFooter')); const showMoreButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showMoreButtonInNoteFooter')); const selectReaction = computed(defaultStore.makeGetterSetter('selectReaction')); +const setFederationAvatarShape = computed(defaultStore.makeGetterSetter('setFederationAvatarShape')); watch(fontSize, () => { if (fontSize.value == null) { @@ -389,6 +388,16 @@ watch(useSystemFont, () => { } }); +watch([ + squareAvatars, + setFederationAvatarShape, +], () => { + misskeyApi('i/update', { + setFederationAvatarShape: setFederationAvatarShape.value, + isSquareAvatars: defaultStore.state.squareAvatars, + }); +}); + watch([ useBlurEffect, useBlurEffectForModal, @@ -397,6 +406,7 @@ watch([ useBoldFont, useSystemFont, squareAvatars, + setFederationAvatarShape, showGapBetweenNotesInTimeline, showUnreadNotificationsCount, filesGridLayoutInUserPage, @@ -496,6 +506,18 @@ function getHTMLElement(ev: MouseEvent): HTMLElement { return target as HTMLElement; // イベント発生元の HTML 要素を取得 } +function reloadTimeline() { + globalEvents.emit('reloadTimeline'); +} + +function reloadNotification() { + globalEvents.emit('reloadNotification'); +} + +async function cantUseSetFederationAvatarShape() { + if ($i && !$i.policies.canSetFederationAvatarShape) setFederationAvatarShape.value = $i.policies.canSetFederationAvatarShape; +} + onMounted(() => { if (fontSizeBefore.value == null) { fontSizeBefore.value = fontSize.value as string; diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 0c5f5a3948..8d85f1882d 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -188,6 +188,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'showQuoteButtonInNoteFooter', 'showMoreButtonInNoteFooter', 'selectReaction', + 'setFederationAvatarShape', // #endregion CherryPick ]; const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index e3b94dfd9f..10744829d0 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -712,6 +712,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: '❤️' as string, }, + setFederationAvatarShape: { + where: 'device', + default: true, + }, // - Settings/Navigation bar showMenuButtonInNavbar: {