Hard mute (#12376)
* feat(backend,misskey-js): hard mute storage in backend * fix(backend,misskey-js): mute word record type * chore(frontend): generalize XWordMute * feat(frontend): configure hard mute * feat(frontend): hard mute notes on the timelines * lint(backend,frontend): fix lint failure * chore(misskey-js): update api.md * fix(backend): test failure * chore(frontend): check word mute for reply * chore: limit hard mute count
This commit is contained in:
parent
ded328fb43
commit
864827f788
|
@ -642,6 +642,7 @@ export interface Locale {
|
||||||
"smtpSecureInfo": string;
|
"smtpSecureInfo": string;
|
||||||
"testEmail": string;
|
"testEmail": string;
|
||||||
"wordMute": string;
|
"wordMute": string;
|
||||||
|
"hardWordMute": string;
|
||||||
"regexpError": string;
|
"regexpError": string;
|
||||||
"regexpErrorDescription": string;
|
"regexpErrorDescription": string;
|
||||||
"instanceMute": string;
|
"instanceMute": string;
|
||||||
|
|
|
@ -639,6 +639,7 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
|
||||||
smtpSecureInfo: "STARTTLS使用時はオフにします。"
|
smtpSecureInfo: "STARTTLS使用時はオフにします。"
|
||||||
testEmail: "配信テスト"
|
testEmail: "配信テスト"
|
||||||
wordMute: "ワードミュート"
|
wordMute: "ワードミュート"
|
||||||
|
hardWordMute: "ハードワードミュート"
|
||||||
regexpError: "正規表現エラー"
|
regexpError: "正規表現エラー"
|
||||||
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:"
|
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:"
|
||||||
instanceMute: "サーバーミュート"
|
instanceMute: "サーバーミュート"
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
export class HardMute1700383825690 {
|
||||||
|
name = 'HardMute1700383825690'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ADD "hardMutedWords" jsonb NOT NULL DEFAULT '[]'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "hardMutedWords"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -473,6 +473,7 @@ export class UserEntityService implements OnModuleInit {
|
||||||
hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
|
hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
|
||||||
unreadNotificationsCount: notificationsInfo?.unreadCount,
|
unreadNotificationsCount: notificationsInfo?.unreadCount,
|
||||||
mutedWords: profile!.mutedWords,
|
mutedWords: profile!.mutedWords,
|
||||||
|
hardMutedWords: profile!.hardMutedWords,
|
||||||
mutedInstances: profile!.mutedInstances,
|
mutedInstances: profile!.mutedInstances,
|
||||||
mutingNotificationTypes: [], // 後方互換性のため
|
mutingNotificationTypes: [], // 後方互換性のため
|
||||||
notificationRecieveConfig: profile!.notificationRecieveConfig,
|
notificationRecieveConfig: profile!.notificationRecieveConfig,
|
||||||
|
|
|
@ -215,7 +215,12 @@ export class MiUserProfile {
|
||||||
@Column('jsonb', {
|
@Column('jsonb', {
|
||||||
default: [],
|
default: [],
|
||||||
})
|
})
|
||||||
public mutedWords: string[][];
|
public mutedWords: (string[] | string)[];
|
||||||
|
|
||||||
|
@Column('jsonb', {
|
||||||
|
default: [],
|
||||||
|
})
|
||||||
|
public hardMutedWords: (string[] | string)[];
|
||||||
|
|
||||||
@Column('jsonb', {
|
@Column('jsonb', {
|
||||||
default: [],
|
default: [],
|
||||||
|
|
|
@ -530,6 +530,18 @@ export const packedMeDetailedOnlySchema = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
hardMutedWords: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
mutedInstances: {
|
mutedInstances: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
nullable: true, optional: false,
|
nullable: true, optional: false,
|
||||||
|
|
|
@ -123,6 +123,11 @@ export const meta = {
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
const muteWords = { type: 'array', items: { oneOf: [
|
||||||
|
{ type: 'array', items: { type: 'string' } },
|
||||||
|
{ type: 'string' }
|
||||||
|
] } } as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -171,7 +176,8 @@ export const paramDef = {
|
||||||
autoSensitive: { type: 'boolean' },
|
autoSensitive: { type: 'boolean' },
|
||||||
ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
||||||
pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true },
|
pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
mutedWords: { type: 'array' },
|
mutedWords: muteWords,
|
||||||
|
hardMutedWords: muteWords,
|
||||||
mutedInstances: { type: 'array', items: {
|
mutedInstances: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
|
@ -234,16 +240,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (ps.location !== undefined) profileUpdates.location = ps.location;
|
if (ps.location !== undefined) profileUpdates.location = ps.location;
|
||||||
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
|
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
|
||||||
if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility;
|
if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility;
|
||||||
if (ps.mutedWords !== undefined) {
|
|
||||||
|
function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
|
||||||
// TODO: ちゃんと数える
|
// TODO: ちゃんと数える
|
||||||
const length = JSON.stringify(ps.mutedWords).length;
|
const length = JSON.stringify(ps.mutedWords).length;
|
||||||
if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) {
|
if (length > limit) {
|
||||||
throw new ApiError(meta.errors.tooManyMutedWords);
|
throw new ApiError(meta.errors.tooManyMutedWords);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate regular expression syntax
|
function validateMuteWordRegex(mutedWords: (string[] | string)[]) {
|
||||||
ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => {
|
for (const mutedWord of mutedWords) {
|
||||||
const regexp = x.match(/^\/(.+)\/(.*)$/);
|
if (typeof mutedWord !== "string") continue;
|
||||||
|
|
||||||
|
const regexp = mutedWord.match(/^\/(.+)\/(.*)$/);
|
||||||
if (!regexp) throw new ApiError(meta.errors.invalidRegexp);
|
if (!regexp) throw new ApiError(meta.errors.invalidRegexp);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -251,11 +261,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new ApiError(meta.errors.invalidRegexp);
|
throw new ApiError(meta.errors.invalidRegexp);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.mutedWords !== undefined) {
|
||||||
|
checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit);
|
||||||
|
validateMuteWordRegex(ps.mutedWords);
|
||||||
|
|
||||||
profileUpdates.mutedWords = ps.mutedWords;
|
profileUpdates.mutedWords = ps.mutedWords;
|
||||||
profileUpdates.enableWordMute = ps.mutedWords.length > 0;
|
profileUpdates.enableWordMute = ps.mutedWords.length > 0;
|
||||||
}
|
}
|
||||||
|
if (ps.hardMutedWords !== undefined) {
|
||||||
|
checkMuteWordCount(ps.hardMutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit);
|
||||||
|
validateMuteWordRegex(ps.hardMutedWords);
|
||||||
|
profileUpdates.hardMutedWords = ps.hardMutedWords;
|
||||||
|
}
|
||||||
if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances;
|
if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances;
|
||||||
if (ps.notificationRecieveConfig !== undefined) profileUpdates.notificationRecieveConfig = ps.notificationRecieveConfig;
|
if (ps.notificationRecieveConfig !== undefined) profileUpdates.notificationRecieveConfig = ps.notificationRecieveConfig;
|
||||||
if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked;
|
if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked;
|
||||||
|
|
|
@ -168,6 +168,7 @@ describe('ユーザー', () => {
|
||||||
hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest,
|
hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest,
|
||||||
unreadAnnouncements: user.unreadAnnouncements,
|
unreadAnnouncements: user.unreadAnnouncements,
|
||||||
mutedWords: user.mutedWords,
|
mutedWords: user.mutedWords,
|
||||||
|
hardMutedWords: user.hardMutedWords,
|
||||||
mutedInstances: user.mutedInstances,
|
mutedInstances: user.mutedInstances,
|
||||||
mutingNotificationTypes: user.mutingNotificationTypes,
|
mutingNotificationTypes: user.mutingNotificationTypes,
|
||||||
notificationRecieveConfig: user.notificationRecieveConfig,
|
notificationRecieveConfig: user.notificationRecieveConfig,
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="!muted"
|
v-if="!hardMuted && !muted"
|
||||||
v-show="!isDeleted"
|
v-show="!isDeleted"
|
||||||
ref="el"
|
ref="el"
|
||||||
v-hotkey="keymap"
|
v-hotkey="keymap"
|
||||||
|
@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<div v-else :class="$style.muted" @click="muted = false">
|
<div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false">
|
||||||
<I18n :src="i18n.ts.userSaysSomething" tag="small">
|
<I18n :src="i18n.ts.userSaysSomething" tag="small">
|
||||||
<template #name>
|
<template #name>
|
||||||
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
|
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
|
||||||
|
@ -183,6 +183,7 @@ const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
mock?: boolean;
|
mock?: boolean;
|
||||||
|
withHardMute?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
mock: false,
|
mock: false,
|
||||||
});
|
});
|
||||||
|
@ -239,13 +240,23 @@ const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null);
|
||||||
const isLong = shouldCollapsed(appearNote, urls ?? []);
|
const isLong = shouldCollapsed(appearNote, urls ?? []);
|
||||||
const collapsed = ref(appearNote.cw == null && isLong);
|
const collapsed = ref(appearNote.cw == null && isLong);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
|
const muted = ref(checkMute(appearNote, $i?.mutedWords));
|
||||||
|
const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords));
|
||||||
const translation = ref<any>(null);
|
const translation = ref<any>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
|
||||||
let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
|
let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
|
||||||
|
|
||||||
|
function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
|
||||||
|
if (mutedWords == null) return false;
|
||||||
|
|
||||||
|
if (checkWordMute(note, $i, mutedWords)) return true;
|
||||||
|
if (note.reply && checkWordMute(note.reply, $i, mutedWords)) return true;
|
||||||
|
if (note.renote && checkWordMute(note.renote, $i, mutedWords)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const keymap = {
|
const keymap = {
|
||||||
'r': () => reply(true),
|
'r': () => reply(true),
|
||||||
'e|a|plus': () => react(true),
|
'e|a|plus': () => react(true),
|
||||||
|
|
|
@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:ad="true"
|
:ad="true"
|
||||||
:class="$style.notes"
|
:class="$style.notes"
|
||||||
>
|
>
|
||||||
<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note"/>
|
<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/>
|
||||||
</MkDateSeparatedList>
|
</MkDateSeparatedList>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template #default="{ items: notifications }">
|
<template #default="{ items: notifications }">
|
||||||
<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
|
<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
|
||||||
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/>
|
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note" :withHardMute="true"/>
|
||||||
<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/>
|
<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/>
|
||||||
</MkDateSeparatedList>
|
</MkDateSeparatedList>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -9,7 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #icon><i class="ti ti-message-off"></i></template>
|
<template #icon><i class="ti ti-message-off"></i></template>
|
||||||
<template #label>{{ i18n.ts.wordMute }}</template>
|
<template #label>{{ i18n.ts.wordMute }}</template>
|
||||||
|
|
||||||
<XWordMute/>
|
<XWordMute :muted="$i!.mutedWords" @save="saveMutedWords"/>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder>
|
||||||
|
<template #icon><i class="ti ti-message-off"></i></template>
|
||||||
|
<template #label>{{ i18n.ts.hardWordMute }}</template>
|
||||||
|
|
||||||
|
<XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
|
@ -129,6 +136,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
|
import { $i } from '@/account.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
|
||||||
const renoteMutingPagination = {
|
const renoteMutingPagination = {
|
||||||
|
@ -207,6 +215,14 @@ async function toggleBlockItem(item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function saveMutedWords(mutedWords: (string | string[])[]) {
|
||||||
|
await os.api('i/update', { mutedWords });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveHardMutedWords(hardMutedWords: (string | string[])[]) {
|
||||||
|
await os.api('i/update', { hardMutedWords });
|
||||||
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = $computed(() => []);
|
||||||
|
|
|
@ -18,16 +18,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
|
||||||
import MkTab from '@/components/MkTab.vue';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import number from '@/filters/number.js';
|
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|
||||||
|
const props = defineProps<{
|
||||||
|
muted: (string[] | string)[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'save', value: (string[] | string)[]): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
const render = (mutedWords) => mutedWords.map(x => {
|
const render = (mutedWords) => mutedWords.map(x => {
|
||||||
if (Array.isArray(x)) {
|
if (Array.isArray(x)) {
|
||||||
|
@ -37,8 +38,7 @@ const render = (mutedWords) => mutedWords.map(x => {
|
||||||
}
|
}
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
|
|
||||||
const tab = ref('soft');
|
const mutedWords = ref(render(props.muted));
|
||||||
const mutedWords = ref(render($i!.mutedWords));
|
|
||||||
const changed = ref(false);
|
const changed = ref(false);
|
||||||
|
|
||||||
watch(mutedWords, () => {
|
watch(mutedWords, () => {
|
||||||
|
@ -85,9 +85,7 @@ async function save() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await os.api('i/update', {
|
emit('save', parsed);
|
||||||
mutedWords: parsed,
|
|
||||||
});
|
|
||||||
|
|
||||||
changed.value = false;
|
changed.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1565,7 +1565,8 @@ export type Endpoints = {
|
||||||
injectFeaturedNote?: boolean;
|
injectFeaturedNote?: boolean;
|
||||||
receiveAnnouncementEmail?: boolean;
|
receiveAnnouncementEmail?: boolean;
|
||||||
alwaysMarkNsfw?: boolean;
|
alwaysMarkNsfw?: boolean;
|
||||||
mutedWords?: string[][];
|
mutedWords?: (string[] | string)[];
|
||||||
|
hardMutedWords?: (string[] | string)[];
|
||||||
notificationRecieveConfig?: any;
|
notificationRecieveConfig?: any;
|
||||||
emailNotificationTypes?: string[];
|
emailNotificationTypes?: string[];
|
||||||
alsoKnownAs?: string[];
|
alsoKnownAs?: string[];
|
||||||
|
@ -2516,7 +2517,8 @@ type MeDetailed = UserDetailed & {
|
||||||
integrations: Record<string, any>;
|
integrations: Record<string, any>;
|
||||||
isDeleted: boolean;
|
isDeleted: boolean;
|
||||||
isExplorable: boolean;
|
isExplorable: boolean;
|
||||||
mutedWords: string[][];
|
mutedWords: (string[] | string)[];
|
||||||
|
hardMutedWords: (string[] | string)[];
|
||||||
notificationRecieveConfig: {
|
notificationRecieveConfig: {
|
||||||
[notificationType in typeof notificationTypes_2[number]]?: {
|
[notificationType in typeof notificationTypes_2[number]]?: {
|
||||||
type: 'all';
|
type: 'all';
|
||||||
|
@ -3053,9 +3055,9 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
|
||||||
//
|
//
|
||||||
// src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
|
// src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
|
||||||
// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
|
// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
|
||||||
// src/api.types.ts:634:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
|
// src/api.types.ts:635: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:117:2 - (ae-forgotten-export) The symbol "notificationTypes_2" 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/entities.ts:628: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
|
// 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)
|
// (No @packageDocumentation comment for this package)
|
||||||
|
|
|
@ -432,7 +432,8 @@ export type Endpoints = {
|
||||||
injectFeaturedNote?: boolean;
|
injectFeaturedNote?: boolean;
|
||||||
receiveAnnouncementEmail?: boolean;
|
receiveAnnouncementEmail?: boolean;
|
||||||
alwaysMarkNsfw?: boolean;
|
alwaysMarkNsfw?: boolean;
|
||||||
mutedWords?: string[][];
|
mutedWords?: (string[] | string)[];
|
||||||
|
hardMutedWords?: (string[] | string)[];
|
||||||
notificationRecieveConfig?: any;
|
notificationRecieveConfig?: any;
|
||||||
emailNotificationTypes?: string[];
|
emailNotificationTypes?: string[];
|
||||||
alsoKnownAs?: string[];
|
alsoKnownAs?: string[];
|
||||||
|
|
|
@ -112,7 +112,8 @@ export type MeDetailed = UserDetailed & {
|
||||||
integrations: Record<string, any>;
|
integrations: Record<string, any>;
|
||||||
isDeleted: boolean;
|
isDeleted: boolean;
|
||||||
isExplorable: boolean;
|
isExplorable: boolean;
|
||||||
mutedWords: string[][];
|
mutedWords: (string[] | string)[];
|
||||||
|
hardMutedWords: (string[] | string)[];
|
||||||
notificationRecieveConfig: {
|
notificationRecieveConfig: {
|
||||||
[notificationType in typeof notificationTypes[number]]?: {
|
[notificationType in typeof notificationTypes[number]]?: {
|
||||||
type: 'all';
|
type: 'all';
|
||||||
|
|
Loading…
Reference in New Issue