summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorおさむのひと <46447427+samunohito@users.noreply.github.com>2023-11-03 17:34:23 +0900
committerGitHub <noreply@github.com>2023-11-03 17:34:23 +0900
commit39a3f4ae98ebe436ed023fab737a823717da5e0b (patch)
treecadb9e1bb9d24f16527fe6fb912d6a746cc59748 /packages
parentfix(frontend): In deck layout, replies option is not saved after refresh (diff)
downloadmisskey-39a3f4ae98ebe436ed023fab737a823717da5e0b.tar.gz
misskey-39a3f4ae98ebe436ed023fab737a823717da5e0b.tar.bz2
misskey-39a3f4ae98ebe436ed023fab737a823717da5e0b.zip
feat: チャンネル内→チャンネル外へのリノート制限機能追加 (#12230)
* チャンネル内→チャンネル外へのリノート制限機能追加 * fix CHANGELOG.md * コメント対応(canRenoteSwitch→allowRenoteToExternal) * コメント対応(別チャンネルへのリノート対策) * コメント対応(canRenote->allowRenoteToExternal) * fix comment * Update misskey-js.api.md * :v: --------- Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Diffstat (limited to 'packages')
-rw-r--r--packages/backend/migration/1698840138000-add-allow-renote-to-external.js16
-rw-r--r--packages/backend/src/core/entities/ChannelEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts1
-rw-r--r--packages/backend/src/models/Channel.ts5
-rw-r--r--packages/backend/src/models/json-schema/channel.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/channels/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/create.ts19
-rw-r--r--packages/frontend/src/components/MkNote.vue97
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue69
-rw-r--r--packages/frontend/src/pages/channel-editor.vue9
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts120
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md18
-rw-r--r--packages/misskey-js/src/entities.ts17
14 files changed, 219 insertions, 161 deletions
diff --git a/packages/backend/migration/1698840138000-add-allow-renote-to-external.js b/packages/backend/migration/1698840138000-add-allow-renote-to-external.js
new file mode 100644
index 0000000000..0edf298841
--- /dev/null
+++ b/packages/backend/migration/1698840138000-add-allow-renote-to-external.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class AddAllowRenoteToExternal1698840138000 {
+ name = 'AddAllowRenoteToExternal1698840138000'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" ADD "allowRenoteToExternal" boolean NOT NULL DEFAULT true`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "allowRenoteToExternal"`);
+ }
+}
diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts
index 9e66834dfa..305946b8a6 100644
--- a/packages/backend/src/core/entities/ChannelEntityService.ts
+++ b/packages/backend/src/core/entities/ChannelEntityService.ts
@@ -85,6 +85,7 @@ export class ChannelEntityService {
usersCount: channel.usersCount,
notesCount: channel.notesCount,
isSensitive: channel.isSensitive,
+ allowRenoteToExternal: channel.allowRenoteToExternal,
...(me ? {
isFollowing,
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 6fde1c3830..c49dad8e79 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -350,6 +350,7 @@ export class NoteEntityService implements OnModuleInit {
name: channel.name,
color: channel.color,
isSensitive: channel.isSensitive,
+ allowRenoteToExternal: channel.allowRenoteToExternal,
} : undefined,
mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri ?? undefined,
diff --git a/packages/backend/src/models/Channel.ts b/packages/backend/src/models/Channel.ts
index f90f8c03d8..a7f9e262b1 100644
--- a/packages/backend/src/models/Channel.ts
+++ b/packages/backend/src/models/Channel.ts
@@ -93,4 +93,9 @@ export class MiChannel {
default: false,
})
public isSensitive: boolean;
+
+ @Column('boolean', {
+ default: true,
+ })
+ public allowRenoteToExternal: boolean;
}
diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts
index f1019d1461..8f9770cdc5 100644
--- a/packages/backend/src/models/json-schema/channel.ts
+++ b/packages/backend/src/models/json-schema/channel.ts
@@ -76,5 +76,9 @@ export const packedChannelSchema = {
type: 'boolean',
optional: false, nullable: false,
},
+ allowRenoteToExternal: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
},
} as const;
diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts
index 3ba411d28c..3dd1eddd01 100644
--- a/packages/backend/src/server/api/endpoints/channels/create.ts
+++ b/packages/backend/src/server/api/endpoints/channels/create.ts
@@ -50,6 +50,7 @@ export const paramDef = {
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
color: { type: 'string', minLength: 1, maxLength: 16 },
isSensitive: { type: 'boolean', nullable: true },
+ allowRenoteToExternal: { type: 'boolean', nullable: true },
},
required: ['name'],
} as const;
@@ -87,6 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
bannerId: banner ? banner.id : null,
isSensitive: ps.isSensitive ?? false,
...(ps.color !== undefined ? { color: ps.color } : {}),
+ allowRenoteToExternal: ps.allowRenoteToExternal ?? true,
} as MiChannel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0]));
return await this.channelEntityService.pack(channel, me);
diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts
index ab69f62a7b..93d02e4a12 100644
--- a/packages/backend/src/server/api/endpoints/channels/update.ts
+++ b/packages/backend/src/server/api/endpoints/channels/update.ts
@@ -61,6 +61,7 @@ export const paramDef = {
},
color: { type: 'string', minLength: 1, maxLength: 16 },
isSensitive: { type: 'boolean', nullable: true },
+ allowRenoteToExternal: { type: 'boolean', nullable: true },
},
required: ['channelId'],
} as const;
@@ -115,6 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
...(typeof ps.isArchived === 'boolean' ? { isArchived: ps.isArchived } : {}),
...(banner ? { bannerId: banner.id } : {}),
...(typeof ps.isSensitive === 'boolean' ? { isSensitive: ps.isSensitive } : {}),
+ ...(typeof ps.allowRenoteToExternal === 'boolean' ? { allowRenoteToExternal: ps.allowRenoteToExternal } : {}),
});
return await this.channelEntityService.pack(channel.id, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index fb650f69ff..df02d3acb7 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -99,6 +99,12 @@ export const meta = {
code: 'NO_SUCH_FILE',
id: 'b6992544-63e7-67f0-fa7f-32444b1b5306',
},
+
+ cannotRenoteOutsideOfChannel: {
+ message: 'Cannot renote outside of channel.',
+ code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL',
+ id: '33510210-8452-094c-6227-4a6c05d99f00',
+ },
},
} as const;
@@ -246,6 +252,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// specified / direct noteはreject
throw new ApiError(meta.errors.cannotRenoteDueToVisibility);
}
+
+ if (renote.channelId && renote.channelId !== ps.channelId) {
+ // チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック
+ // リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する
+ const renoteChannel = await this.channelsRepository.findOneById(renote.channelId);
+ if (renoteChannel == null) {
+ // リノートしたいノートが書き込まれているチャンネルが無い
+ throw new ApiError(meta.errors.noSuchChannel);
+ } else if (!renoteChannel.allowRenoteToExternal) {
+ // リノート作成のリクエストだが、対象チャンネルがリノート禁止だった場合
+ throw new ApiError(meta.errors.cannotRenoteOutsideOfChannel);
+ }
+ }
}
let reply: MiNote | null = null;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index d71b07c51b..30a68f38f2 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -159,7 +159,7 @@ import { reactionPicker } from '@/scripts/reaction-picker.js';
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
-import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
+import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js';
import { useNoteCapture } from '@/scripts/use-note-capture.js';
import { deepClone } from '@/scripts/clone.js';
import { useTooltip } from '@/scripts/use-tooltip.js';
@@ -275,103 +275,14 @@ if (!props.mock) {
});
}
-type Visibility = 'public' | 'home' | 'followers' | 'specified';
-
-// defaultStore.state.visibilityがstringなためstringも受け付けている
-function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility {
- if (a === 'specified' || b === 'specified') return 'specified';
- if (a === 'followers' || b === 'followers') return 'followers';
- if (a === 'home' || b === 'home') return 'home';
- // if (a === 'public' || b === 'public')
- return 'public';
-}
-
function renote(viaKeyboard = false) {
pleaseLogin();
showMovedDialog();
- let items = [] as MenuItem[];
-
- if (appearNote.channel) {
- items = items.concat([{
- text: i18n.ts.inChannelRenote,
- icon: 'ti ti-repeat',
- action: () => {
- const el = renoteButton.value as HTMLElement | null | undefined;
- if (el) {
- const rect = el.getBoundingClientRect();
- const x = rect.left + (el.offsetWidth / 2);
- const y = rect.top + (el.offsetHeight / 2);
- os.popup(MkRippleEffect, { x, y }, {}, 'end');
- }
-
- if (!props.mock) {
- os.api('notes/create', {
- renoteId: appearNote.id,
- channelId: appearNote.channelId,
- }).then(() => {
- os.toast(i18n.ts.renoted);
- });
- }
- },
- }, {
- text: i18n.ts.inChannelQuote,
- icon: 'ti ti-quote',
- action: () => {
- if (!props.mock) {
- os.post({
- renote: appearNote,
- channel: appearNote.channel,
- });
- }
- },
- }, null]);
- }
-
- items = items.concat([{
- text: i18n.ts.renote,
- icon: 'ti ti-repeat',
- action: () => {
- const el = renoteButton.value as HTMLElement | null | undefined;
- if (el) {
- const rect = el.getBoundingClientRect();
- const x = rect.left + (el.offsetWidth / 2);
- const y = rect.top + (el.offsetHeight / 2);
- os.popup(MkRippleEffect, { x, y }, {}, 'end');
- }
-
- const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
- const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
-
- let visibility = appearNote.visibility;
- visibility = smallerVisibility(visibility, configuredVisibility);
- if (appearNote.channel?.isSensitive) {
- visibility = smallerVisibility(visibility, 'home');
- }
-
- if (!props.mock) {
- os.api('notes/create', {
- localOnly,
- visibility,
- renoteId: appearNote.id,
- }).then(() => {
- os.toast(i18n.ts.renoted);
- });
- }
- },
- }, (props.mock) ? undefined : {
- text: i18n.ts.quote,
- icon: 'ti ti-quote',
- action: () => {
- os.post({
- renote: appearNote,
- });
- },
- }]);
-
- os.popupMenu(items, renoteButton.value, {
+ const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock });
+ os.popupMenu(menu, renoteButton.value, {
viaKeyboard,
- });
+ }).then(focus);
}
function reply(viaKeyboard = false): void {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index d34d35a0c3..9e9b1035d7 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -206,7 +206,7 @@ import { reactionPicker } from '@/scripts/reaction-picker.js';
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
-import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
+import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js';
import { useNoteCapture } from '@/scripts/use-note-capture.js';
import { deepClone } from '@/scripts/clone.js';
import { useTooltip } from '@/scripts/use-tooltip.js';
@@ -325,71 +325,10 @@ function renote(viaKeyboard = false) {
pleaseLogin();
showMovedDialog();
- let items = [] as MenuItem[];
-
- if (appearNote.channel) {
- items = items.concat([{
- text: i18n.ts.inChannelRenote,
- icon: 'ti ti-repeat',
- action: () => {
- const el = renoteButton.value as HTMLElement | null | undefined;
- if (el) {
- const rect = el.getBoundingClientRect();
- const x = rect.left + (el.offsetWidth / 2);
- const y = rect.top + (el.offsetHeight / 2);
- os.popup(MkRippleEffect, { x, y }, {}, 'end');
- }
-
- os.api('notes/create', {
- renoteId: appearNote.id,
- channelId: appearNote.channelId,
- }).then(() => {
- os.toast(i18n.ts.renoted);
- });
- },
- }, {
- text: i18n.ts.inChannelQuote,
- icon: 'ti ti-quote',
- action: () => {
- os.post({
- renote: appearNote,
- channel: appearNote.channel,
- });
- },
- }, null]);
- }
-
- items = items.concat([{
- text: i18n.ts.renote,
- icon: 'ti ti-repeat',
- action: () => {
- const el = renoteButton.value as HTMLElement | null | undefined;
- if (el) {
- const rect = el.getBoundingClientRect();
- const x = rect.left + (el.offsetWidth / 2);
- const y = rect.top + (el.offsetHeight / 2);
- os.popup(MkRippleEffect, { x, y }, {}, 'end');
- }
-
- os.api('notes/create', {
- renoteId: appearNote.id,
- }).then(() => {
- os.toast(i18n.ts.renoted);
- });
- },
- }, {
- text: i18n.ts.quote,
- icon: 'ti ti-quote',
- action: () => {
- os.post({
- renote: appearNote,
- });
- },
- }]);
-
- os.popupMenu(items, renoteButton.value, {
+ const { menu } = getRenoteMenu({ note: note, renoteButton });
+ os.popupMenu(menu, renoteButton.value, {
viaKeyboard,
- });
+ }).then(focus);
}
function reply(viaKeyboard = false): void {
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index faef8fdb1f..5256ea4f11 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -24,6 +24,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.sensitive }}</template>
</MkSwitch>
+ <MkSwitch v-model="allowRenoteToExternal">
+ <template #label>{{ i18n.ts._channel.allowRenoteToExternal }}</template>
+ </MkSwitch>
+
<div>
<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="ti ti-plus"></i> {{ i18n.ts._channel.setBanner }}</MkButton>
<div v-else-if="bannerUrl">
@@ -76,7 +80,7 @@ import { useRouter } from '@/router.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
-import MkSwitch from "@/components/MkSwitch.vue";
+import MkSwitch from '@/components/MkSwitch.vue';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@@ -93,6 +97,7 @@ let bannerUrl = $ref<string | null>(null);
let bannerId = $ref<string | null>(null);
let color = $ref('#000');
let isSensitive = $ref(false);
+let allowRenoteToExternal = $ref(true);
const pinnedNotes = ref([]);
watch(() => bannerId, async () => {
@@ -121,6 +126,7 @@ async function fetchChannel() {
id,
}));
color = channel.color;
+ allowRenoteToExternal = channel.allowRenoteToExternal;
}
fetchChannel();
@@ -150,6 +156,7 @@ function save() {
pinnedNoteIds: pinnedNotes.value.map(x => x.id),
color: color,
isSensitive: isSensitive,
+ allowRenoteToExternal: allowRenoteToExternal,
};
if (props.channelId) {
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index e399145fc9..d0753872ff 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -17,6 +17,7 @@ import { miLocalStorage } from '@/local-storage.js';
import { getUserMenu } from '@/scripts/get-user-menu.js';
import { clipsCache } from '@/cache.js';
import { MenuItem } from '@/types/menu.js';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
export async function getNoteClipMenu(props: {
note: Misskey.entities.Note;
@@ -418,3 +419,122 @@ export function getNoteMenu(props: {
cleanup,
};
}
+
+type Visibility = 'public' | 'home' | 'followers' | 'specified';
+
+// defaultStore.state.visibilityがstringなためstringも受け付けている
+function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility {
+ if (a === 'specified' || b === 'specified') return 'specified';
+ if (a === 'followers' || b === 'followers') return 'followers';
+ if (a === 'home' || b === 'home') return 'home';
+ // if (a === 'public' || b === 'public')
+ return 'public';
+}
+
+export function getRenoteMenu(props: {
+ note: Misskey.entities.Note;
+ renoteButton: Ref<HTMLElement>;
+ mock?: boolean;
+}) {
+ const isRenote = (
+ props.note.renote != null &&
+ props.note.text == null &&
+ props.note.fileIds.length === 0 &&
+ props.note.poll == null
+ );
+
+ const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
+
+ const channelRenoteItems: MenuItem[] = [];
+ const normalRenoteItems: MenuItem[] = [];
+
+ if (appearNote.channel) {
+ channelRenoteItems.push(...[{
+ text: i18n.ts.inChannelRenote,
+ icon: 'ti ti-repeat',
+ action: () => {
+ const el = props.renoteButton.value as HTMLElement | null | undefined;
+ if (el) {
+ const rect = el.getBoundingClientRect();
+ const x = rect.left + (el.offsetWidth / 2);
+ const y = rect.top + (el.offsetHeight / 2);
+ os.popup(MkRippleEffect, { x, y }, {}, 'end');
+ }
+
+ if (!props.mock) {
+ os.api('notes/create', {
+ renoteId: appearNote.id,
+ channelId: appearNote.channelId,
+ }).then(() => {
+ os.toast(i18n.ts.renoted);
+ });
+ }
+ },
+ }, {
+ text: i18n.ts.inChannelQuote,
+ icon: 'ti ti-quote',
+ action: () => {
+ if (!props.mock) {
+ os.post({
+ renote: appearNote,
+ channel: appearNote.channel,
+ });
+ }
+ },
+ }]);
+ }
+
+ if (!appearNote.channel || appearNote.channel?.allowRenoteToExternal) {
+ normalRenoteItems.push(...[{
+ text: i18n.ts.renote,
+ icon: 'ti ti-repeat',
+ action: () => {
+ const el = props.renoteButton.value as HTMLElement | null | undefined;
+ if (el) {
+ const rect = el.getBoundingClientRect();
+ const x = rect.left + (el.offsetWidth / 2);
+ const y = rect.top + (el.offsetHeight / 2);
+ os.popup(MkRippleEffect, { x, y }, {}, 'end');
+ }
+
+ const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
+ const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+
+ let visibility = appearNote.visibility;
+ visibility = smallerVisibility(visibility, configuredVisibility);
+ if (appearNote.channel?.isSensitive) {
+ visibility = smallerVisibility(visibility, 'home');
+ }
+
+ if (!props.mock) {
+ os.api('notes/create', {
+ localOnly,
+ visibility,
+ renoteId: appearNote.id,
+ }).then(() => {
+ os.toast(i18n.ts.renoted);
+ });
+ }
+ },
+ }, (props.mock) ? undefined : {
+ text: i18n.ts.quote,
+ icon: 'ti ti-quote',
+ action: () => {
+ os.post({
+ renote: appearNote,
+ });
+ },
+ }]);
+ }
+
+ // nullを挟むことで区切り線を出せる
+ const renoteItems = [
+ ...normalRenoteItems,
+ ...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [null] : [],
+ ...channelRenoteItems,
+ ];
+
+ return {
+ menu: renoteItems,
+ };
+}
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index a15e5888e8..87922ba791 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -134,6 +134,20 @@ type Blocking = {
// @public (undocumented)
type Channel = {
id: ID;
+ lastNotedAt: Date | null;
+ userId: User['id'] | null;
+ user: User | null;
+ name: string;
+ description: string | null;
+ bannerId: DriveFile['id'] | null;
+ banner: DriveFile | null;
+ pinnedNoteIds: string[];
+ color: string;
+ isArchived: boolean;
+ notesCount: number;
+ usersCount: number;
+ isSensitive: boolean;
+ allowRenoteToExternal: boolean;
};
// Warning: (ae-forgotten-export) The symbol "AnyOf" needs to be exported by the entry point index.d.ts
@@ -2683,6 +2697,8 @@ type Note = {
fileIds: DriveFile['id'][];
visibility: 'public' | 'home' | 'followers' | 'specified';
visibleUserIds?: User['id'][];
+ channel?: Channel;
+ channelId?: Channel['id'];
localOnly?: boolean;
myReaction?: string;
reactions: Record<string, number>;
@@ -3021,7 +3037,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
// src/api.types.ts:632:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
// src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
-// src/entities.ts:612:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 029bf48c84..a0d0b7528d 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -198,6 +198,8 @@ export type Note = {
fileIds: DriveFile['id'][];
visibility: 'public' | 'home' | 'followers' | 'specified';
visibleUserIds?: User['id'][];
+ channel?: Channel;
+ channelId?: Channel['id'];
localOnly?: boolean;
myReaction?: string;
reactions: Record<string, number>;
@@ -514,7 +516,20 @@ export type FollowRequest = {
export type Channel = {
id: ID;
- // TODO
+ lastNotedAt: Date | null;
+ userId: User['id'] | null;
+ user: User | null;
+ name: string;
+ description: string | null;
+ bannerId: DriveFile['id'] | null;
+ banner: DriveFile | null;
+ pinnedNoteIds: string[];
+ color: string;
+ isArchived: boolean;
+ notesCount: number;
+ usersCount: number;
+ isSensitive: boolean;
+ allowRenoteToExternal: boolean;
};
export type Following = {