Compare commits

..

20 Commits

Author SHA1 Message Date
syuilo
0ddd4bc545 Merge branch 'develop' into multiple-reactions 2024-08-18 13:22:22 +09:00
syuilo
3cd5f86510 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2024-08-18 10:12:51 +09:00
syuilo
9b78ce8047 🎨 2024-08-18 10:12:42 +09:00
syuilo
1629c0e50d New Crowdin updates (#14426)
* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)
2024-08-18 10:11:50 +09:00
syuilo
427f4a2cda Update about-misskey.vue 2024-08-18 10:10:06 +09:00
woxtu
ba9c5c37b8 Remove undefined style (#14427) 2024-08-18 03:40:21 +09:00
syuilo
e790aa0548 [skip ci] New Crowdin updates (#14423)
* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)
2024-08-17 20:25:15 +09:00
taichan
bf8c42eecd Fix(beckend): html content-type detection on signedGet (#14424)
* fix(backend): contenttype detection of html in signedGet

* code style

* fix by review
2024-08-17 19:51:56 +09:00
かっこかり
129af06198 Update packages/backend/src/core/activitypub/ApRequestService.ts 2024-08-17 18:25:46 +09:00
かっこかり
83c04c55ad fix(misskey-js): eliminate any (follow-up of #13523) (#14422) 2024-08-17 18:15:46 +09:00
かっこかり
0b98554319 Update CHANGELOG.md
Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com>
2024-08-17 18:05:29 +09:00
かっこかり
4e0d57000c fix(frontend): 以前のpopupの呼び出し方を修正 (#14421) 2024-08-17 18:04:52 +09:00
syuilo
c0de57c08d Update about-misskey.vue 2024-08-17 17:57:27 +09:00
syuilo
cb6a1c773e Update about-misskey.vue 2024-08-17 17:52:06 +09:00
syuilo
9df887ba93 Merge branch 'develop' into multiple-reactions 2024-08-17 17:23:02 +09:00
かっこかり
75b0315ace Update timeline.vue (refactor) 2024-08-17 15:24:29 +09:00
syuilo
a2769d0733 wip 2024-07-17 17:13:01 +09:00
syuilo
036f90133c Merge branch 'develop' into multiple-reactions 2024-07-16 17:05:19 +09:00
syuilo
f9bfff604d Merge branch 'develop' into multiple-reactions 2024-07-02 14:47:30 +09:00
syuilo
fd0e840138 wip 2024-07-02 09:54:57 +09:00
24 changed files with 107 additions and 37 deletions

View File

@@ -17,7 +17,7 @@
- Fix: 特定の条件下でノートの削除ボタンが出ないのを修正 - Fix: 特定の条件下でノートの削除ボタンが出ないのを修正
### Server ### Server
- enhance: 照会時にURLがhtmlかつheadタグ内に`rel="alternate"`, `type="application/activity+json"``link`タグがある場合に追ってリンク先を照会できるように - Enhance: 照会時にURLがhtmlかつheadタグ内に`rel="alternate"`, `type="application/activity+json"``link`タグがある場合に追ってリンク先を照会できるように
- Enhance: 凍結されたアカウントのフォローリクエストを表示しないように - Enhance: 凍結されたアカウントのフォローリクエストを表示しないように
- Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374 - Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374
- 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。 - 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。

View File

@@ -2499,6 +2499,7 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "Create a recipient for abuse reports" createAbuseReportNotificationRecipient: "Create a recipient for abuse reports"
updateAbuseReportNotificationRecipient: "Update recipients for abuse reports" updateAbuseReportNotificationRecipient: "Update recipients for abuse reports"
deleteAbuseReportNotificationRecipient: "Delete a recipient for abuse reports" deleteAbuseReportNotificationRecipient: "Delete a recipient for abuse reports"
deleteFlash: "Delete Play"
_fileViewer: _fileViewer:
title: "File details" title: "File details"
type: "File type" type: "File type"

4
locales/index.d.ts vendored
View File

@@ -6686,6 +6686,10 @@ export interface Locale extends ILocale {
* ノートのピン留めの最大数 * ノートのピン留めの最大数
*/ */
"pinMax": string; "pinMax": string;
/**
* 一つのノートに対する最大リアクション数
*/
"reactionsPerNoteLimit": string;
/** /**
* アンテナの作成可能数 * アンテナの作成可能数
*/ */

View File

@@ -1728,6 +1728,7 @@ _role:
alwaysMarkNsfw: "ファイルにNSFWを常に付与" alwaysMarkNsfw: "ファイルにNSFWを常に付与"
canUpdateBioMedia: "アイコンとバナーの更新を許可" canUpdateBioMedia: "アイコンとバナーの更新を許可"
pinMax: "ノートのピン留めの最大数" pinMax: "ノートのピン留めの最大数"
reactionsPerNoteLimit: "一つのノートに対する最大リアクション数"
antennaMax: "アンテナの作成可能数" antennaMax: "アンテナの作成可能数"
wordMuteMax: "ワードミュートの最大文字数" wordMuteMax: "ワードミュートの最大文字数"
webhookMax: "Webhookの作成可能数" webhookMax: "Webhookの作成可能数"

View File

@@ -2316,6 +2316,7 @@ _pages:
eyeCatchingImageSet: "设置封面图片" eyeCatchingImageSet: "设置封面图片"
eyeCatchingImageRemove: "删除封面图片" eyeCatchingImageRemove: "删除封面图片"
chooseBlock: "添加块" chooseBlock: "添加块"
enterSectionTitle: "输入会话标题"
selectType: "选择类型" selectType: "选择类型"
contentBlocks: "内容" contentBlocks: "内容"
inputBlocks: "输入" inputBlocks: "输入"
@@ -2499,6 +2500,10 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "新建了举报通知" createAbuseReportNotificationRecipient: "新建了举报通知"
updateAbuseReportNotificationRecipient: "更新了举报通知" updateAbuseReportNotificationRecipient: "更新了举报通知"
deleteAbuseReportNotificationRecipient: "删除了举报通知" deleteAbuseReportNotificationRecipient: "删除了举报通知"
deleteAccount: "删除了账户"
deletePage: "删除了页面"
deleteFlash: "删除了 Play"
deleteGalleryPost: "删除了图库稿件"
_fileViewer: _fileViewer:
title: "文件信息" title: "文件信息"
type: "文件类型" type: "文件类型"

View File

@@ -2316,6 +2316,7 @@ _pages:
eyeCatchingImageSet: "設定封面影像" eyeCatchingImageSet: "設定封面影像"
eyeCatchingImageRemove: "刪除封面影像" eyeCatchingImageRemove: "刪除封面影像"
chooseBlock: "新增方塊" chooseBlock: "新增方塊"
enterSectionTitle: "輸入區段的標題"
selectType: "選擇類型" selectType: "選擇類型"
contentBlocks: "內容" contentBlocks: "內容"
inputBlocks: "輸入" inputBlocks: "輸入"
@@ -2499,6 +2500,10 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "建立接收檢舉的通知對象" createAbuseReportNotificationRecipient: "建立接收檢舉的通知對象"
updateAbuseReportNotificationRecipient: "更新接收檢舉的通知對象" updateAbuseReportNotificationRecipient: "更新接收檢舉的通知對象"
deleteAbuseReportNotificationRecipient: "刪除接收檢舉的通知對象" deleteAbuseReportNotificationRecipient: "刪除接收檢舉的通知對象"
deleteAccount: "刪除帳戶"
deletePage: "刪除頁面"
deleteFlash: "刪除 Play"
deleteGalleryPost: "刪除相簿的貼文"
_fileViewer: _fileViewer:
title: "檔案詳細資訊" title: "檔案詳細資訊"
type: "檔案類型 " type: "檔案類型 "

View File

@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class MultipleReactions1721117896543 {
name = 'MultipleReactions1721117896543';
async up(queryRunner) {
await queryRunner.query('DROP INDEX "public"."IDX_ad0c221b25672daf2df320a817"');
await queryRunner.query('CREATE UNIQUE INDEX "IDX_a7751b74317122d11575bff31c" ON "note_reaction" ("userId", "noteId", "reaction") ');
await queryRunner.query('CREATE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") ');
}
async down(queryRunner) {
await queryRunner.query('DROP INDEX "public"."IDX_ad0c221b25672daf2df320a817"');
await queryRunner.query('DROP INDEX "public"."IDX_a7751b74317122d11575bff31c"');
await queryRunner.query('CREATE UNIQUE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") ');
}
}

View File

@@ -174,24 +174,12 @@ export class ReactionService {
reaction, reaction,
}; };
// Create reaction
try { try {
await this.noteReactionsRepository.insert(record); await this.noteReactionsRepository.insert(record);
} catch (e) { } catch (e) {
if (isDuplicateKeyValueError(e)) { if (isDuplicateKeyValueError(e)) {
const exists = await this.noteReactionsRepository.findOneByOrFail({ // 同じリアクションがすでにされていたらエラー
noteId: note.id, throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298');
userId: user.id,
});
if (exists.reaction !== reaction) {
// 別のリアクションがすでにされていたら置き換える
await this.delete(user, note);
await this.noteReactionsRepository.insert(record);
} else {
// 同じリアクションがすでにされていたらエラー
throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298');
}
} else { } else {
throw e; throw e;
} }
@@ -286,11 +274,12 @@ export class ReactionService {
} }
@bindThis @bindThis
public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote) { public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, _reaction?: string | null) {
// if already unreacted // if already unreacted
const exist = await this.noteReactionsRepository.findOneBy({ const exist = await this.noteReactionsRepository.findOneBy({
noteId: note.id, noteId: note.id,
userId: user.id, userId: user.id,
reaction: _reaction ?? FALLBACK,
}); });
if (exist == null) { if (exist == null) {

View File

@@ -49,6 +49,7 @@ export type RolePolicies = {
alwaysMarkNsfw: boolean; alwaysMarkNsfw: boolean;
canUpdateBioMedia: boolean; canUpdateBioMedia: boolean;
pinLimit: number; pinLimit: number;
reactionsPerNoteLimit: number;
antennaLimit: number; antennaLimit: number;
wordMuteLimit: number; wordMuteLimit: number;
webhookLimit: number; webhookLimit: number;
@@ -78,6 +79,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
alwaysMarkNsfw: false, alwaysMarkNsfw: false,
canUpdateBioMedia: true, canUpdateBioMedia: true,
pinLimit: 5, pinLimit: 5,
reactionsPerNoteLimit: 1,
antennaLimit: 5, antennaLimit: 5,
wordMuteLimit: 200, wordMuteLimit: 200,
webhookLimit: 3, webhookLimit: 3,
@@ -380,6 +382,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)), alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)), canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)), pinLimit: calc('pinLimit', vs => Math.max(...vs)),
reactionsPerNoteLimit: calc('reactionsPerNoteLimit', vs => Math.max(...vs)),
antennaLimit: calc('antennaLimit', vs => Math.max(...vs)), antennaLimit: calc('antennaLimit', vs => Math.max(...vs)),
wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)), wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)),
webhookLimit: calc('webhookLimit', vs => Math.max(...vs)), webhookLimit: calc('webhookLimit', vs => Math.max(...vs)),

View File

@@ -203,7 +203,9 @@ export class ApRequestService {
}); });
//#region リクエスト先がhtmlかつactivity+jsonへのalternate linkタグがあるとき //#region リクエスト先がhtmlかつactivity+jsonへのalternate linkタグがあるとき
if (res.headers.get('Content-type')?.startsWith('text/html;') && _followAlternate === true) { const contentType = res.headers.get('content-type');
if ((contentType ?? '').split(';')[0].trimEnd().toLowerCase() === 'text/html' && _followAlternate === true) {
const html = await res.text(); const html = await res.text();
const window = new Window(); const window = new Window();
const document = window.document; const document = window.document;

View File

@@ -170,10 +170,10 @@ export class NoteEntityService implements OnModuleInit {
@bindThis @bindThis
public async populateMyReaction(note: { id: MiNote['id']; reactions: MiNote['reactions']; reactionAndUserPairCache?: MiNote['reactionAndUserPairCache']; }, meId: MiUser['id'], _hint_?: { public async populateMyReaction(note: { id: MiNote['id']; reactions: MiNote['reactions']; reactionAndUserPairCache?: MiNote['reactionAndUserPairCache']; }, meId: MiUser['id'], _hint_?: {
myReactions: Map<MiNote['id'], string | null>; myReactionsMap: Map<MiNote['id'], string | null>;
}) { }) {
if (_hint_?.myReactions) { if (_hint_?.myReactionsMap) {
const reaction = _hint_.myReactions.get(note.id); const reaction = _hint_.myReactionsMap.get(note.id);
if (reaction) { if (reaction) {
return this.reactionService.convertLegacyReaction(reaction); return this.reactionService.convertLegacyReaction(reaction);
} else { } else {

View File

@@ -9,7 +9,8 @@ import { MiUser } from './User.js';
import { MiNote } from './Note.js'; import { MiNote } from './Note.js';
@Entity('note_reaction') @Entity('note_reaction')
@Index(['userId', 'noteId'], { unique: true }) @Index(['userId', 'noteId'])
@Index(['userId', 'noteId', 'reaction'], { unique: true })
export class MiNoteReaction { export class MiNoteReaction {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;

View File

@@ -236,6 +236,10 @@ export const packedRolePoliciesSchema = {
type: 'integer', type: 'integer',
optional: false, nullable: false, optional: false, nullable: false,
}, },
reactionsPerNoteLimit: {
type: 'integer',
optional: false, nullable: false,
},
antennaLimit: { antennaLimit: {
type: 'integer', type: 'integer',
optional: false, nullable: false, optional: false, nullable: false,

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div :class="$style.preview"> <div :class="$style.preview">
<div :class="$style.preview__content1"> <div>
<MkInput v-model="text"> <MkInput v-model="text">
<template #label>Text</template> <template #label>Text</template>
</MkInput> </MkInput>

View File

@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span :class="$style.bodyName">{{ role.name }}</span> <span :class="$style.bodyName">{{ role.name }}</span>
<template v-if="detailed"> <template v-if="detailed">
<span v-if="role.target === 'manual'" :class="$style.bodyUsers">{{ role.usersCount }} users</span> <span v-if="role.target === 'manual'" :class="$style.bodyUsers">{{ role.usersCount }} users</span>
<span v-else-if="role.target === 'conditional'" :class="$style.bodyUsers">({{ i18n.ts._role.conditional }})</span> <span v-else-if="role.target === 'conditional'" :class="$style.bodyUsers">? users</span>
</template> </template>
</div> </div>
<div :class="$style.bodyDescription">{{ role.description }}</div> <div :class="$style.bodyDescription">{{ role.description }}</div>

View File

@@ -89,6 +89,7 @@ export const ROLE_POLICIES = [
'alwaysMarkNsfw', 'alwaysMarkNsfw',
'canUpdateBioMedia', 'canUpdateBioMedia',
'pinLimit', 'pinLimit',
'reactionsPerNoteLimit',
'antennaLimit', 'antennaLimit',
'wordMuteLimit', 'wordMuteLimit',
'webhookLimit', 'webhookLimit',

View File

@@ -72,10 +72,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<img src="https://avatars.githubusercontent.com/u/4439005?v=4" :class="$style.contributorAvatar"> <img src="https://avatars.githubusercontent.com/u/4439005?v=4" :class="$style.contributorAvatar">
<span :class="$style.contributorUsername">@syuilo</span> <span :class="$style.contributorUsername">@syuilo</span>
</a> </a>
<a href="https://github.com/tamaina" target="_blank" :class="$style.contributor">
<img src="https://avatars.githubusercontent.com/u/7973572?v=4" :class="$style.contributorAvatar">
<span :class="$style.contributorUsername">@tamaina</span>
</a>
<a href="https://github.com/acid-chicken" target="_blank" :class="$style.contributor"> <a href="https://github.com/acid-chicken" target="_blank" :class="$style.contributor">
<img src="https://avatars.githubusercontent.com/u/20679825?v=4" :class="$style.contributorAvatar"> <img src="https://avatars.githubusercontent.com/u/20679825?v=4" :class="$style.contributorAvatar">
<span :class="$style.contributorUsername">@acid-chicken</span> <span :class="$style.contributorUsername">@acid-chicken</span>
@@ -267,6 +263,9 @@ const patronsWithIcon = [{
}, { }, {
name: 'Macop', name: 'Macop',
icon: 'https://assets.misskey-hub.net/patrons/ee052bf550014d36a643ce3dce595640.jpg', icon: 'https://assets.misskey-hub.net/patrons/ee052bf550014d36a643ce3dce595640.jpg',
}, {
name: 'なっかあ',
icon: 'https://assets.misskey-hub.net/patrons/c2f5f3e394e74a64912284a2f4ca710e.jpg',
}]; }];
const patrons = [ const patrons = [

View File

@@ -417,6 +417,25 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.reactionsPerNoteLimit, 'reactionsPerNoteLimit'])">
<template #label>{{ i18n.ts._role._options.reactionsPerNoteLimit }}</template>
<template #suffix>
<span v-if="role.policies.reactionsPerNoteLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.reactionsPerNoteLimit.value }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.reactionsPerNoteLimit)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.reactionsPerNoteLimit.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkInput v-model="role.policies.reactionsPerNoteLimit.value" :disabled="role.policies.reactionsPerNoteLimit.useDefault" type="number" :readonly="readonly">
</MkInput>
<MkRange v-model="role.policies.reactionsPerNoteLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
<template #label>{{ i18n.ts._role.priority }}</template>
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])">
<template #label>{{ i18n.ts._role._options.antennaMax }}</template> <template #label>{{ i18n.ts._role._options.antennaMax }}</template>
<template #suffix> <template #suffix>

View File

@@ -149,6 +149,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput> </MkInput>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.reactionsPerNoteLimit, 'reactionsPerNoteLimit'])">
<template #label>{{ i18n.ts._role._options.reactionsPerNoteLimit }}</template>
<template #suffix>{{ policies.reactionsPerNoteLimit }}</template>
<MkInput v-model="policies.reactionsPerNoteLimit" type="number">
</MkInput>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])">
<template #label>{{ i18n.ts._role._options.antennaMax }}</template> <template #label>{{ i18n.ts._role._options.antennaMax }}</template>
<template #suffix>{{ policies.antennaLimit }}</template> <template #suffix>{{ policies.antennaLimit }}</template>

View File

@@ -236,10 +236,12 @@ function reportAbuse() {
const pageUrl = `${url}/play/${flash.value.id}`; const pageUrl = `${url}/play/${flash.value.id}`;
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: flash.value.user, user: flash.value.user,
initialComment: `Play: ${pageUrl}\n-----\n`, initialComment: `Play: ${pageUrl}\n-----\n`,
}, {}, 'closed'); }, {
closed: () => dispose(),
});
} }
function showMenu(ev: MouseEvent) { function showMenu(ev: MouseEvent) {

View File

@@ -160,10 +160,12 @@ function reportAbuse() {
const pageUrl = `${url}/gallery/${post.value.id}`; const pageUrl = `${url}/gallery/${post.value.id}`;
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: post.value.user, user: post.value.user,
initialComment: `Post: ${pageUrl}\n-----\n`, initialComment: `Post: ${pageUrl}\n-----\n`,
}, {}, 'closed'); }, {
closed: () => dispose(),
});
} }
function showMenu(ev: MouseEvent) { function showMenu(ev: MouseEvent) {

View File

@@ -245,10 +245,12 @@ function reportAbuse() {
const pageUrl = `${url}/@${props.username}/pages/${props.pageName}`; const pageUrl = `${url}/@${props.username}/pages/${props.pageName}`;
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: page.value.user, user: page.value.user,
initialComment: `Page: ${pageUrl}\n-----\n`, initialComment: `Page: ${pageUrl}\n-----\n`,
}, {}, 'closed'); }, {
closed: () => dispose(),
});
} }
function showMenu(ev: MouseEvent) { function showMenu(ev: MouseEvent) {

View File

@@ -240,7 +240,7 @@ function closeTutorial(): void {
} }
function switchTlIfNeeded() { function switchTlIfNeeded() {
if (isBasicTimeline(src.value) && !availableBasicTimelines().includes(src.value)) { if (isBasicTimeline(src.value) && !isAvailableBasicTimeline(src.value)) {
src.value = availableBasicTimelines()[0]; src.value = availableBasicTimelines()[0];
} }
} }

View File

@@ -4,9 +4,12 @@ import type {
Ad, Ad,
Announcement, Announcement,
EmojiDetailed, EmojiDetailed,
Flash,
GalleryPost,
InviteCode, InviteCode,
MetaDetailed, MetaDetailed,
Note, Note,
Page,
Role, Role,
ReversiGameDetailed, ReversiGameDetailed,
SystemWebhook, SystemWebhook,
@@ -405,18 +408,18 @@ export type ModerationLogPayloads = {
pageId: string; pageId: string;
pageUserId: string; pageUserId: string;
pageUserUsername: string; pageUserUsername: string;
page: any; page: Page;
}; };
deleteFlash: { deleteFlash: {
flashId: string; flashId: string;
flashUserId: string; flashUserId: string;
flashUserUsername: string; flashUserUsername: string;
flash: any; flash: Flash;
}; };
deleteGalleryPost: { deleteGalleryPost: {
postId: string; postId: string;
postUserId: string; postUserId: string;
postUserUsername: string; postUserUsername: string;
post: any; post: GalleryPost;
}; };
}; };