diff --git a/locales/en-US.yml b/locales/en-US.yml index f2ef64beb..7ee357e37 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1677,6 +1677,8 @@ _role: iconUrl: "Icon URL" asBadge: "Show as badge" descriptionOfAsBadge: "This role's icon will be displayed next to the username of users with this role if turned on." + badgeBehavior: "Badge behavior" + descriptionOfBadgeBehavior: "Set the behavior of the badge icon." isExplorable: "Make role explorable" descriptionOfIsExplorable: "This role's timeline and the list of users with this will be made public if enabled." displayOrder: "Position" diff --git a/locales/index.d.ts b/locales/index.d.ts index ecce896b6..6c51ffc44 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -6558,6 +6558,14 @@ export interface Locale extends ILocale { * オンにすると、ユーザー名の横にロールのアイコンが表示されます。 */ "descriptionOfAsBadge": string; + /** + * バッジの挙動 + */ + "badgeBehavior": string; + /** + * バッジの挙動を設定します。 + */ + "descriptionOfBadgeBehavior": string; /** * ユーザーを見つけやすくする */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6ac606959..632d13c2f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1695,6 +1695,8 @@ _role: iconUrl: "アイコン画像のURL" asBadge: "バッジとして表示" descriptionOfAsBadge: "オンにすると、ユーザー名の横にロールのアイコンが表示されます。" + badgeBehavior: "バッジの挙動" + descriptionOfBadgeBehavior: "バッジの挙動を設定します。" isExplorable: "ユーザーを見つけやすくする" descriptionOfIsExplorable: "オンにすると、「みつける」でメンバー一覧が公開されるほか、ロールのタイムラインが利用可能になります。" displayOrder: "表示順" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 7f5fb8eb7..9364e15b5 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1661,6 +1661,8 @@ _role: iconUrl: "아이콘 URL" asBadge: "뱃지로 표시" descriptionOfAsBadge: "활성화하면 유저명 옆에 역할의 아이콘이 표시됩니다." + badgeBehavior: "뱃지 동작" + descriptionOfBadgeBehavior: "뱃지의 동작 방식을 설정합니다." isExplorable: "역할 타임라인 공개" descriptionOfIsExplorable: "활성화하면 역할 타임라인을 공개합니다. 비활성화 시 타임라인이 공개되지 않습니다." displayOrder: "표시 순서" diff --git a/packages/backend/migration/1711946753142-role-badge-behavior.js b/packages/backend/migration/1711946753142-role-badge-behavior.js new file mode 100644 index 000000000..3a73e806d --- /dev/null +++ b/packages/backend/migration/1711946753142-role-badge-behavior.js @@ -0,0 +1,11 @@ +export class RoleBadgeBehavior1711946753142 { + name = 'RoleBadgeBehavior1711946753142' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" ADD "badgeBehavior" character varying(256)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "badgeBehavior"`); + } +} diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 8502d1ef1..caf14e5aa 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -72,6 +72,7 @@ type Source = { headers: { [x: string]: string }; parameters: { [x: string]: string }; userIdParameterName: string; + roleId: string; } proxy?: string; @@ -154,6 +155,7 @@ export type Config = { headers: { [x: string]: string }; parameters: { [x: string]: string }; userIdParameterName: string; + roleId: string; } | undefined; proxy: string | undefined; proxySmtp: string | undefined; diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 7607c711a..bc92baeeb 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -577,6 +577,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { isModerator: values.isModerator, isExplorable: values.isExplorable, asBadge: values.asBadge, + badgeBehavior: values.badgeBehavior, canEditMembersByModerator: values.canEditMembersByModerator, displayOrder: values.displayOrder, policies: values.policies, diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 49dee1138..3e186e315 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -68,6 +68,7 @@ export class RoleEntityService { isModerator: role.isModerator, isExplorable: role.isExplorable, asBadge: role.asBadge, + badgeBehavior: role.badgeBehavior, canEditMembersByModerator: role.canEditMembersByModerator, displayOrder: role.displayOrder, policies: policies, diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 892529f7c..4b6b032a8 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -484,6 +484,7 @@ export class UserEntityService implements OnModuleInit { name: r.name, iconUrl: r.iconUrl, displayOrder: r.displayOrder, + behavior: r.badgeBehavior ?? undefined, }))) : undefined, ...(isDetailed ? { diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts index 06bf43c32..f7f166528 100644 --- a/packages/backend/src/models/Role.ts +++ b/packages/backend/src/models/Role.ts @@ -155,6 +155,11 @@ export class MiRole { }) public asBadge: boolean; + @Column('varchar', { + length: 256, nullable: true, + }) + public badgeBehavior: string | null; + @Column('boolean', { default: false, }) diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index 070814211..eed65a365 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -384,6 +384,10 @@ export const packedRoleSchema = { optional: false, nullable: false, example: false, }, + badgeBehavior: { + type: 'string', + optional: false, nullable: true, + }, canEditMembersByModerator: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 333cae121..4531fcee0 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -176,6 +176,10 @@ export const packedUserLiteSchema = { type: 'number', nullable: false, optional: false, }, + behavior: { + type: 'string', + nullable: false, optional: true, + } }, }, }, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts index e0c02f7a5..b9658fa89 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -36,6 +36,7 @@ export const paramDef = { isAdministrator: { type: 'boolean' }, isExplorable: { type: 'boolean', default: false }, // optional for backward compatibility asBadge: { type: 'boolean' }, + badgeBehavior: { type: 'string', nullable: true }, canEditMembersByModerator: { type: 'boolean' }, displayOrder: { type: 'number' }, policies: { diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 5242e0be2..997a0a693 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -42,6 +42,7 @@ export const paramDef = { isAdministrator: { type: 'boolean' }, isExplorable: { type: 'boolean' }, asBadge: { type: 'boolean' }, + badgeBehavior: { type: 'string', nullable: true }, canEditMembersByModerator: { type: 'boolean' }, displayOrder: { type: 'number' }, policies: { @@ -92,6 +93,7 @@ export default class extends Endpoint { // eslint- isAdministrator: ps.isAdministrator, isExplorable: ps.isExplorable, asBadge: ps.asBadge, + badgeBehavior: ps.badgeBehavior, canEditMembersByModerator: ps.canEditMembersByModerator, displayOrder: ps.displayOrder, policies: ps.policies, diff --git a/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts b/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts index 79b23ae73..f2e103665 100644 --- a/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts +++ b/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { RoleService } from '@/core/RoleService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { Config } from '@/config.js'; @@ -88,6 +89,7 @@ export default class extends Endpoint { // eslint- @Inject(DI.config) private config: Config, + private roleService: RoleService, private loggerService: LoggerService, private httpRequestService: HttpRequestService, ) { @@ -128,13 +130,22 @@ export default class extends Endpoint { // eslint- error?: unknown, }; - if (res.status > 399 || (json.error ?? json.ban_reason)) { + const hasSkebRole = await this.roleService.getUserRoles(ps.userId).then(roles => roles.some(r => r.id === this.config.skebStatus?.roleId)); + + if (res.status > 299 || (json.error ?? json.ban_reason)) { logger.error('Skeb status response error', { url: url.href, userId: ps.userId, status: res.status, statusText: res.statusText, error: json.error ?? json.ban_reason }); + if (res.status === 404 && hasSkebRole) await this.roleService.unassign(ps.userId, this.config.skebStatus.roleId); throw new ApiError(meta.errors.noSuchUser); } logger.info('Skeb status response', { url: url.href, userId: ps.userId, status: res.status, statusText: res.statusText, skebStatus: json }); + if (json.is_acceptable) { + if (!hasSkebRole) await this.roleService.assign(ps.userId, this.config.skebStatus.roleId); + } else if (hasSkebRole) { + await this.roleService.unassign(ps.userId, this.config.skebStatus.roleId); + } + return { screenName: json.screen_name, isCreator: json.is_creator, diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts index c027eebf3..479d24b7d 100644 --- a/packages/backend/test/e2e/note.ts +++ b/packages/backend/test/e2e/note.ts @@ -467,6 +467,7 @@ describe('Note', () => { isPublic: false, isExplorable: false, asBadge: false, + badgeBehavior: null, canEditMembersByModerator: false, policies: { alwaysMarkNsfw: { @@ -780,6 +781,7 @@ describe('Note', () => { isPublic: false, isExplorable: false, asBadge: false, + badgeBehavior: null, canEditMembersByModerator: false, policies: { mentionLimit: { @@ -834,6 +836,7 @@ describe('Note', () => { isPublic: false, isExplorable: false, asBadge: false, + badgeBehavior: null, canEditMembersByModerator: false, policies: { mentionLimit: { @@ -890,6 +893,7 @@ describe('Note', () => { isPublic: false, isExplorable: false, asBadge: false, + badgeBehavior: null, canEditMembersByModerator: false, policies: { mentionLimit: { diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index f56b73b24..1d0d53846 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -651,11 +651,20 @@ describe('ユーザー', () => { }); test('を取得することができ、バッヂロールがセットされていること', async () => { const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRoleBadge.id }, user: alice }); - assert.deepStrictEqual(response.badgeRoles, [{ - name: roleBadge.name, - iconUrl: roleBadge.iconUrl, - displayOrder: roleBadge.displayOrder, - }]); + if (roleBadge.badgeBehavior) { + assert.deepStrictEqual(response.badgeRoles, [{ + name: roleBadge.name, + iconUrl: roleBadge.iconUrl, + displayOrder: roleBadge.displayOrder, + behavior: roleBadge.badgeBehavior, + }]); + } else { + assert.deepStrictEqual(response.badgeRoles, [{ + name: roleBadge.name, + iconUrl: roleBadge.iconUrl, + displayOrder: roleBadge.displayOrder, + }]); + } assert.deepStrictEqual(response.roles, []); // バッヂだからといってrolesが取れるとは限らない }); test('をID指定のリスト形式で取得することができる(空)', async () => { diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 342501d09..9e99517f0 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -251,6 +251,7 @@ export const channel = async (user: UserToken, channel: Partial = {}, policies: any = {}): Promise => { const res = await api('admin/roles/create', { asBadge: false, + badgeBehavior: null, canEditMembersByModerator: false, color: null, condFormula: { diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue index 40e67fb4e..eb240da75 100644 --- a/packages/frontend/src/components/MkModal.vue +++ b/packages/frontend/src/components/MkModal.vue @@ -175,8 +175,8 @@ const align = () => { let left; let top; - const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset); - const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset); + const x = srcRect.left + (fixed.value ? 0 : window.scrollX); + const y = srcRect.top + (fixed.value ? 0 : window.scrollY); if (props.anchor.x === 'center') { left = x + (props.src.offsetWidth / 2) - (width / 2); @@ -220,24 +220,24 @@ const align = () => { } } else { // 画面から横にはみ出る場合 - if (left + width - window.pageXOffset > (window.innerWidth - SCROLLBAR_THICKNESS)) { - left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset - 1; + if (left + width - window.scrollX > (window.innerWidth - SCROLLBAR_THICKNESS)) { + left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.scrollX - 1; } - const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.pageYOffset); + const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.scrollY); const upperSpace = (srcRect.top - MARGIN); // 画面から縦にはみ出る場合 - if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { + if (top + height - window.scrollY > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (props.noOverlap && props.anchor.x === 'center') { if (underSpace >= (upperSpace / 3)) { maxHeight.value = underSpace; } else { maxHeight.value = upperSpace; - top = window.pageYOffset + ((upperSpace + MARGIN) - height); + top = window.scrollY + ((upperSpace + MARGIN) - height); } } else { - top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1; + top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.scrollY - 1; } } else { maxHeight.value = underSpace; @@ -255,15 +255,15 @@ const align = () => { let transformOriginX = 'center'; let transformOriginY = 'center'; - if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) { + if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.scrollY)) { transformOriginY = 'top'; - } else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) { + } else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.scrollY)) { transformOriginY = 'bottom'; } - if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) { + if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.scrollX)) { transformOriginX = 'left'; - } else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) { + } else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.scrollX)) { transformOriginX = 'right'; } diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index be5829d92..2f5132c28 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
bot
- +
@@ -40,6 +40,7 @@ import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import { notePage } from '@/filters/note.js'; import { userPage } from '@/filters/user.js'; +import MkRoleBadgeIcon from '@/components/MkRoleBadgeIcon.vue'; defineProps<{ note: Misskey.entities.Note; diff --git a/packages/frontend/src/components/MkRoleBadgeIcon.vue b/packages/frontend/src/components/MkRoleBadgeIcon.vue new file mode 100644 index 000000000..e4bafe6bc --- /dev/null +++ b/packages/frontend/src/components/MkRoleBadgeIcon.vue @@ -0,0 +1,57 @@ + + + diff --git a/packages/frontend/src/components/MkSkebStatusPopup.vue b/packages/frontend/src/components/MkSkebStatusPopup.vue new file mode 100644 index 000000000..bb8c46d96 --- /dev/null +++ b/packages/frontend/src/components/MkSkebStatusPopup.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue index 004944e57..2d5b54cd9 100644 --- a/packages/frontend/src/components/MkUrlPreviewPopup.vue +++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue @@ -34,8 +34,8 @@ const left = ref(0); onMounted(() => { try { const rect = props.source.getBoundingClientRect(); - const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset; - const y = rect.top + props.source.offsetHeight + window.pageYOffset; + const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.scrollX; + const y = rect.top + props.source.offsetHeight + window.scrollY; top.value = y; left.value = x; diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue index 60f06d50b..d572f197e 100644 --- a/packages/frontend/src/pages/admin/roles.edit.vue +++ b/packages/frontend/src/pages/admin/roles.edit.vue @@ -63,6 +63,7 @@ if (props.id) { isPublic: false, isExplorable: false, asBadge: false, + badgeBehavior: null, canEditMembersByModerator: false, displayOrder: 0, policies: {}, diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 7143bc4de..d4aa50d9e 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -67,6 +67,11 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + @@ -848,6 +853,7 @@ const save = throttle(100, () => { isPublic: role.value.isPublic, isExplorable: role.value.isExplorable, asBadge: role.value.asBadge, + badgeBehavior: role.value.badgeBehavior, canEditMembersByModerator: role.value.canEditMembersByModerator, policies: role.value.policies, }; diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 9831f4050..735b7d2f5 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -319,7 +319,6 @@ async function fetchSkebStatus() { return; } - console.log('fetching skeb status'); userSkebStatus.value = await misskeyApiGet('users/get-skeb-status', { userId: props.user.id }); } diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts index 8c9e3c02c..3dad41a8b 100644 --- a/packages/frontend/src/scripts/popup-position.ts +++ b/packages/frontend/src/scripts/popup-position.ts @@ -26,8 +26,8 @@ export function calcPopupPosition(el: HTMLElement, props: { let top: number; if (props.anchorElement) { - left = rect.left + window.pageXOffset + (props.anchorElement.offsetWidth / 2); - top = (rect.top + window.pageYOffset - contentHeight) - props.innerMargin; + left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2); + top = (rect.top + window.scrollY - contentHeight) - props.innerMargin; } else { left = props.x; top = (props.y - contentHeight) - props.innerMargin; @@ -35,8 +35,8 @@ export function calcPopupPosition(el: HTMLElement, props: { left -= (el.offsetWidth / 2); - if (left + contentWidth - window.pageXOffset > window.innerWidth) { - left = window.innerWidth - contentWidth + window.pageXOffset - 1; + if (left + contentWidth - window.scrollX > window.innerWidth) { + left = window.innerWidth - contentWidth + window.scrollX - 1; } return [left, top]; @@ -47,8 +47,8 @@ export function calcPopupPosition(el: HTMLElement, props: { let top: number; if (props.anchorElement) { - left = rect.left + window.pageXOffset + (props.anchorElement.offsetWidth / 2); - top = (rect.top + window.pageYOffset + props.anchorElement.offsetHeight) + props.innerMargin; + left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2); + top = (rect.top + window.scrollY + props.anchorElement.offsetHeight) + props.innerMargin; } else { left = props.x; top = (props.y) + props.innerMargin; @@ -56,8 +56,8 @@ export function calcPopupPosition(el: HTMLElement, props: { left -= (el.offsetWidth / 2); - if (left + contentWidth - window.pageXOffset > window.innerWidth) { - left = window.innerWidth - contentWidth + window.pageXOffset - 1; + if (left + contentWidth - window.scrollX > window.innerWidth) { + left = window.innerWidth - contentWidth + window.scrollX - 1; } return [left, top]; @@ -68,8 +68,8 @@ export function calcPopupPosition(el: HTMLElement, props: { let top: number; if (props.anchorElement) { - left = (rect.left + window.pageXOffset - contentWidth) - props.innerMargin; - top = rect.top + window.pageYOffset + (props.anchorElement.offsetHeight / 2); + left = (rect.left + window.scrollX - contentWidth) - props.innerMargin; + top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2); } else { left = (props.x - contentWidth) - props.innerMargin; top = props.y; @@ -77,8 +77,8 @@ export function calcPopupPosition(el: HTMLElement, props: { top -= (el.offsetHeight / 2); - if (top + contentHeight - window.pageYOffset > window.innerHeight) { - top = window.innerHeight - contentHeight + window.pageYOffset - 1; + if (top + contentHeight - window.scrollY > window.innerHeight) { + top = window.innerHeight - contentHeight + window.scrollY - 1; } return [left, top]; @@ -89,15 +89,15 @@ export function calcPopupPosition(el: HTMLElement, props: { let top: number; if (props.anchorElement) { - left = (rect.left + props.anchorElement.offsetWidth + window.pageXOffset) + props.innerMargin; + left = (rect.left + props.anchorElement.offsetWidth + window.scrollX) + props.innerMargin; if (props.align === 'top') { - top = rect.top + window.pageYOffset; + top = rect.top + window.scrollY; if (props.alignOffset != null) top += props.alignOffset; } else if (props.align === 'bottom') { // TODO } else { // center - top = rect.top + window.pageYOffset + (props.anchorElement.offsetHeight / 2); + top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2); top -= (el.offsetHeight / 2); } } else { @@ -106,8 +106,8 @@ export function calcPopupPosition(el: HTMLElement, props: { top -= (el.offsetHeight / 2); } - if (top + contentHeight - window.pageYOffset > window.innerHeight) { - top = window.innerHeight - contentHeight + window.pageYOffset - 1; + if (top + contentHeight - window.scrollY > window.innerHeight) { + top = window.innerHeight - contentHeight + window.scrollY - 1; } return [left, top]; @@ -123,7 +123,7 @@ export function calcPopupPosition(el: HTMLElement, props: { const [left, top] = calcPosWhenTop(); // ツールチップを上に向かって表示するスペースがなければ下に向かって出す - if (top - window.pageYOffset < 0) { + if (top - window.scrollY < 0) { const [left, top] = calcPosWhenBottom(); return { left, top, transformOrigin: 'center top' }; } @@ -141,7 +141,7 @@ export function calcPopupPosition(el: HTMLElement, props: { const [left, top] = calcPosWhenLeft(); // ツールチップを左に向かって表示するスペースがなければ右に向かって出す - if (left - window.pageXOffset < 0) { + if (left - window.scrollX < 0) { const [left, top] = calcPosWhenRight(); return { left, top, transformOrigin: 'left center' }; } diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts index 7e4bf5c9c..bed221a62 100644 --- a/packages/frontend/src/scripts/use-chart-tooltip.ts +++ b/packages/frontend/src/scripts/use-chart-tooltip.ts @@ -53,11 +53,11 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio const rect = context.chart.canvas.getBoundingClientRect(); tooltipShowing.value = true; - tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX; + tooltipX.value = rect.left + window.scrollX + context.tooltip.caretX; if (opts.position === 'top') { - tooltipY.value = rect.top + window.pageYOffset; + tooltipY.value = rect.top + window.scrollY; } else if (opts.position === 'middle') { - tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY; + tooltipY.value = rect.top + window.scrollY + context.tooltip.caretY; } } diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index f4e504711..b2bca962f 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3748,6 +3748,7 @@ export type components = { name: string; iconUrl: string | null; displayOrder: number; + behavior?: string; })[]; }; UserDetailedNotMeOnly: { @@ -4843,6 +4844,7 @@ export type components = { isExplorable: boolean; /** @example false */ asBadge: boolean; + badgeBehavior: string | null; /** @example false */ canEditMembersByModerator: boolean; policies: { @@ -9827,6 +9829,7 @@ export type operations = { /** @default false */ isExplorable?: boolean; asBadge: boolean; + badgeBehavior?: string | null; canEditMembersByModerator: boolean; displayOrder: number; policies: Record; @@ -10048,6 +10051,7 @@ export type operations = { isAdministrator: boolean; isExplorable?: boolean; asBadge: boolean; + badgeBehavior?: string | null; canEditMembersByModerator: boolean; displayOrder: number; policies: Record;