mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-13 13:37:31 -07:00
Refactor soft word mutes
This commit is contained in:
parent
d09fc8be2b
commit
39105d0c88
6 changed files with 128 additions and 34 deletions
|
@ -663,6 +663,9 @@ regexpErrorDescription: "An error occurred in the regular expression on line {li
|
|||
instanceMute: "Instance Mutes"
|
||||
userSaysSomething: "{name} said something"
|
||||
userSaysSomethingReason: "{name} said {reason}"
|
||||
userSaysSomethingReasonReply: "{name} replied to a post containing {reason}"
|
||||
userSaysSomethingReasonRenote: "{name} boosted a post containing {reason}"
|
||||
userSaysSomethingReasonQuote: "{name} quoted a post containing {reason}"
|
||||
makeActive: "Activate"
|
||||
display: "Display"
|
||||
copy: "Copy"
|
||||
|
|
|
@ -619,6 +619,9 @@ regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表
|
|||
instanceMute: "インスタンスミュート"
|
||||
userSaysSomething: "{name}が何かを言いました"
|
||||
userSaysSomethingReason: "{name}が{reason}と言いました"
|
||||
userSaysSomethingReasonReply: "{name}が{reason}を含む投稿に返信しました"
|
||||
userSaysSomethingReasonRenote: "{name}が{reason}を含む投稿をブーストしました"
|
||||
userSaysSomethingReasonQuote: "{name}が{reason}を含む投稿を引用しました"
|
||||
makeActive: "アクティブにする"
|
||||
display: "表示"
|
||||
copy: "コピー"
|
||||
|
|
|
@ -198,7 +198,7 @@
|
|||
</article>
|
||||
</div>
|
||||
<div v-else class="muted" @click="muted.muted = false">
|
||||
<I18n :src="i18n.ts.userSaysSomethingReason" tag="small">
|
||||
<I18n :src="softMuteReasonI18nSrc(muted.what)" tag="small">
|
||||
<template #name>
|
||||
<MkA
|
||||
v-user-preview="appearNote.userId"
|
||||
|
@ -261,6 +261,20 @@ const inChannel = inject("inChannel", null);
|
|||
|
||||
let note = $ref(deepClone(props.note));
|
||||
|
||||
const softMuteReasonI18nSrc = (what?: string) => {
|
||||
if (what === "note")
|
||||
return i18n.ts.userSaysSomethingReason;
|
||||
if (what === "reply")
|
||||
return i18n.ts.userSaysSomethingReasonReply;
|
||||
if (what === "renote")
|
||||
return i18n.ts.userSaysSomethingReasonRenote;
|
||||
if (what === "quote")
|
||||
return i18n.ts.userSaysSomethingReasonQuote;
|
||||
|
||||
// I don't think here is reachable, but just in case
|
||||
return i18n.ts.userSaysSomething;
|
||||
}
|
||||
|
||||
// plugin
|
||||
if (noteViewInterruptors.length > 0) {
|
||||
onMounted(async () => {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
/>
|
||||
</div>
|
||||
<div v-else class="_panel muted" @click="muted.muted = false">
|
||||
<I18n :src="i18n.ts.userSaysSomethingReason" tag="small">
|
||||
<I18n :src="softMuteReasonI18nSrc(muted.what)" tag="small">
|
||||
<template #name>
|
||||
<MkA
|
||||
v-user-preview="appearNote.userId"
|
||||
|
@ -110,6 +110,20 @@ const inChannel = inject("inChannel", null);
|
|||
|
||||
let note = $ref(deepClone(props.note));
|
||||
|
||||
const softMuteReasonI18nSrc = (what?: string) => {
|
||||
if (what === "note")
|
||||
return i18n.ts.userSaysSomethingReason;
|
||||
if (what === "reply")
|
||||
return i18n.ts.userSaysSomethingReasonReply;
|
||||
if (what === "renote")
|
||||
return i18n.ts.userSaysSomethingReasonRenote;
|
||||
if (what === "quote")
|
||||
return i18n.ts.userSaysSomethingReasonQuote;
|
||||
|
||||
// I don't think here is reachable, but just in case
|
||||
return i18n.ts.userSaysSomething;
|
||||
}
|
||||
|
||||
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
||||
|
||||
// plugin
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="!muted.muted || muted.what === 'reply'"
|
||||
ref="el"
|
||||
v-size="{ max: [450, 500] }"
|
||||
class="wrpstxzv"
|
||||
|
@ -161,6 +162,22 @@
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else class="muted" @click="muted.muted = false">
|
||||
<I18n :src="softMuteReasonI18nSrc(muted.what)" tag="small">
|
||||
<template #name>
|
||||
<MkA
|
||||
v-user-preview="appearNote.userId"
|
||||
class="name"
|
||||
:to="userPage(appearNote.user)"
|
||||
>
|
||||
<MkUserName :user="appearNote.user" />
|
||||
</MkA>
|
||||
</template>
|
||||
<template #reason>
|
||||
<b class="_blur_text">{{ muted.matched.join(", ") }}</b>
|
||||
</template>
|
||||
</I18n>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -176,10 +193,12 @@ import XRenoteButton from "@/components/MkRenoteButton.vue";
|
|||
import XQuoteButton from "@/components/MkQuoteButton.vue";
|
||||
import { pleaseLogin } from "@/scripts/please-login";
|
||||
import { getNoteMenu } from "@/scripts/get-note-menu";
|
||||
import { getWordMute } from "@/scripts/check-word-mute";
|
||||
import { notePage } from "@/filters/note";
|
||||
import { useRouter } from "@/router";
|
||||
import * as os from "@/os";
|
||||
import { reactionPicker } from "@/scripts/reaction-picker";
|
||||
import { $i } from "@/account";
|
||||
import { i18n } from "@/i18n";
|
||||
import { deepClone } from "@/scripts/clone";
|
||||
import { useNoteCapture } from "@/scripts/use-note-capture";
|
||||
|
@ -206,6 +225,20 @@ const props = withDefaults(
|
|||
|
||||
let note = $ref(deepClone(props.note));
|
||||
|
||||
const softMuteReasonI18nSrc = (what?: string) => {
|
||||
if (what === "note")
|
||||
return i18n.ts.userSaysSomethingReason;
|
||||
if (what === "reply")
|
||||
return i18n.ts.userSaysSomethingReasonReply;
|
||||
if (what === "renote")
|
||||
return i18n.ts.userSaysSomethingReasonRenote;
|
||||
if (what === "quote")
|
||||
return i18n.ts.userSaysSomethingReasonQuote;
|
||||
|
||||
// I don't think here is reachable, but just in case
|
||||
return i18n.ts.userSaysSomething;
|
||||
}
|
||||
|
||||
const isRenote =
|
||||
note.renote != null &&
|
||||
note.text == null &&
|
||||
|
@ -222,6 +255,7 @@ let appearNote = $computed(() =>
|
|||
isRenote ? (note.renote as misskey.entities.Note) : note
|
||||
);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(getWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
||||
const translation = ref(null);
|
||||
const translating = ref(false);
|
||||
const replies: misskey.entities.Note[] =
|
||||
|
@ -617,4 +651,10 @@ function noteClick(e) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.muted {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export type Muted = {
|
||||
muted: boolean;
|
||||
matched: string[];
|
||||
what?: string; // "note" || "reply" || "renote" || "quote"
|
||||
};
|
||||
|
||||
const NotMuted = { muted: false, matched: [] };
|
||||
|
@ -9,6 +10,42 @@ function escapeRegExp(x: string) {
|
|||
return x.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||
}
|
||||
|
||||
function checkWordMute(note: NoteLike): Muted {
|
||||
const text = ((note.cw ?? "") + " " + (note.text ?? "")).trim();
|
||||
if (text === "") return NotMuted;
|
||||
|
||||
for (const mutePattern of mutedWords) {
|
||||
let mute: RegExp;
|
||||
let matched: string[];
|
||||
if (Array.isArray(mutePattern)) {
|
||||
matched = mutePattern.filter((keyword) => keyword !== "");
|
||||
if (matched.length === 0) {
|
||||
continue;
|
||||
}
|
||||
mute = new RegExp(
|
||||
`\\b${matched.map(escapeRegExp).join("\\b.*\\b")}\\b`,
|
||||
"g",
|
||||
);
|
||||
} else {
|
||||
const regexp = mutePattern.match(/^\/(.+)\/(.*)$/);
|
||||
// This should never happen due to input sanitisation.
|
||||
if (!regexp) {
|
||||
console.warn(`Found invalid regex in word mutes: ${mutePattern}`);
|
||||
continue;
|
||||
}
|
||||
mute = new RegExp(regexp[1], regexp[2]);
|
||||
matched = [mutePattern];
|
||||
}
|
||||
try {
|
||||
if (mute.test(text)) {
|
||||
return { muted: true, matched };
|
||||
}
|
||||
} catch (err) {
|
||||
// This should never happen due to input sanitisation.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getWordMute(
|
||||
note: Record<string, any>,
|
||||
me: Record<string, any> | null | undefined,
|
||||
|
@ -20,42 +57,25 @@ export function getWordMute(
|
|||
}
|
||||
|
||||
if (mutedWords.length > 0) {
|
||||
const text = ((note.cw ?? "") + "\n" + (note.text ?? "")).trim();
|
||||
|
||||
if (text === "") {
|
||||
return NotMuted;
|
||||
let noteMuted = checkWordMute(note);
|
||||
if (noteMuted.muted) {
|
||||
noteMuted.what = "note";
|
||||
return noteMuted;
|
||||
}
|
||||
|
||||
for (const mutePattern of mutedWords) {
|
||||
let mute: RegExp;
|
||||
let matched: string[];
|
||||
if (Array.isArray(mutePattern)) {
|
||||
matched = mutePattern.filter((keyword) => keyword !== "");
|
||||
|
||||
if (matched.length === 0) {
|
||||
continue;
|
||||
}
|
||||
mute = new RegExp(
|
||||
`\\b${matched.map(escapeRegExp).join("\\b.*\\b")}\\b`,
|
||||
"g",
|
||||
);
|
||||
} else {
|
||||
const regexp = mutePattern.match(/^\/(.+)\/(.*)$/);
|
||||
// This should never happen due to input sanitisation.
|
||||
if (!regexp) {
|
||||
console.warn(`Found invalid regex in word mutes: ${mutePattern}`);
|
||||
continue;
|
||||
}
|
||||
mute = new RegExp(regexp[1], regexp[2]);
|
||||
matched = [mutePattern];
|
||||
if (note.reply) {
|
||||
let replyMuted = checkWordMute(note.reply);
|
||||
if (replyMuted.muted) {
|
||||
replyMuted.what = "reply";
|
||||
return replyMuted;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (mute.test(text)) {
|
||||
return { muted: true, matched };
|
||||
}
|
||||
} catch (err) {
|
||||
// This should never happen due to input sanitisation.
|
||||
if (note.renote) {
|
||||
let renoteMuted = checkWordMute(note.renote);
|
||||
if (renoteMuted.muted) {
|
||||
renoteMuted.what = (note.text == null ? "renote" : "quote");
|
||||
return renoteMuted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue