+
@@ -238,7 +238,8 @@ let showEl = $ref(false);
const props = withDefaults(defineProps<{
note: Misskey.entities.Note;
pinned?: boolean;
- mock?: boolean;
+ mock?: boolean;
+ withHardMute?: boolean;
notification?: boolean;
}>(), {
mock: false,
@@ -299,7 +300,8 @@ const isLong = shouldCollapsed(appearNote, urls ?? []);
const isMFM = shouldMfmCollapsed(appearNote);
const collapsed = ref(appearNote.cw == null && (isLong || (isMFM && defaultStore.state.collapseDefault) || defaultStore.state.allMediaNoteCollapse));
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(null);
const translating = ref(false);
const viewTextSource = ref(false);
@@ -313,6 +315,15 @@ const collapseLabel = computed(() => {
] as string[][]).join(' / ');
});
+function checkMute(note: Misskey.entities.Note, mutedWords: Array | 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 = {
'r': () => reply(true),
'e|a|plus': () => react(true),
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index 373720abd9..80368bc3ff 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:ad="true"
:class="$style.notes"
>
-
+
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 9705b92127..ad083f8013 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 1882584a87..fcc1887ed3 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: (canToggle || alternative), [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
@click="toggleReaction()"
>
-
+
{{ count }}
@@ -211,7 +211,7 @@ if (!mock) {
}
}
-.icon {
+.limitWidth {
max-width: 150px;
}
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index bbafd5ee40..8f4d76f9dd 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -29,12 +29,25 @@ const props = withDefaults(defineProps<{
mode: 'relative',
});
-const _time = props.time == null ? NaN :
- typeof props.time === 'number' ? props.time :
- (props.time instanceof Date ? props.time : new Date(props.time)).getTime();
+function getDateSafe(n: Date | string | number) {
+ try {
+ if (n instanceof Date) {
+ return n;
+ }
+ return new Date(n);
+ } catch (err) {
+ return {
+ getTime: () => NaN,
+ };
+ }
+}
+
+// eslint-disable-next-line vue/no-setup-props-destructure
+const _time = props.time == null ? NaN : getDateSafe(props.time).getTime();
const invalid = Number.isNaN(_time);
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
+// eslint-disable-next-line vue/no-setup-props-destructure
let now = $ref((props.origin ?? new Date()).getTime());
const ago = $computed(() => (now - _time) / 1000/*ms*/);
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 702fdf783d..ecc9a7a463 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -205,13 +205,13 @@ SPDX-License-Identifier: AGPL-3.0-only
- Manual roles
+ {{ i18n.ts._role.manualRoles }}
- Conditional roles
+ {{ i18n.ts._role.conditionalRoles }}
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 78f60936e5..adb6c3fe5f 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -66,6 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+ {{ i18n.ts.limitWidthOfReaction }}
{{ i18n.ts.hideAvatarsInNote }} CherryPick
{{ i18n.ts.enableAbsoluteTime }} CherryPick
{{ i18n.ts.enableMarkByDate }} CherryPick
@@ -338,6 +339,7 @@ const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serve
const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover'));
const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter'));
const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize'));
+const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction'));
const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes'));
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
@@ -433,6 +435,7 @@ watch([
overridedDeviceKind,
mediaListWithOneImageAppearance,
reactionsDisplaySize,
+ limitWidthOfReaction,
highlightSensitiveMedia,
keepScreenOn,
disableStreamingTimeline,
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 5601bfd22f..c2dafe5a62 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -9,7 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.wordMute }}
-
+
+
+
+
+
+ {{ i18n.ts.hardWordMute }}
+
+
@@ -129,6 +136,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import * as os from '@/os.js';
import { infoImageUrl } from '@/instance.js';
+import { $i } from '@/account.js';
import MkFolder from '@/components/MkFolder.vue';
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 headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue
index 6d5857accd..1f097cc24d 100644
--- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue
+++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue
@@ -18,16 +18,17 @@ SPDX-License-Identifier: AGPL-3.0-only