add emoji reaction filtering

This commit is contained in:
notfire 2025-03-30 14:33:29 -04:00
parent c43ddf6c7b
commit 7c275adfc8
Signed by: notfire
GPG key ID: 3AFDACAAB4E56B16
10 changed files with 99 additions and 8 deletions

View file

@ -19,6 +19,9 @@
- polls can now have up to 20 options instead of 4
- polls can now last anywhere from 1 second to 1000 years
- optional alternate gravestone when using websockets that still shows post content but removes buttons
- filters for reactions:
- matching by regex, domain-specific, or catch-all
- filters the actual reactions to post as well as notifications
---

View file

@ -2,6 +2,8 @@ import UserAvatar from '../user_avatar/user_avatar.vue'
import UserListPopover from '../user_list_popover/user_list_popover.vue'
import StillImage from '../still-image/still-image.vue'
import { toRaw } from 'vue'
const EMOJI_REACTION_COUNT_CUTOFF = 12
const findEmojiByReplacement = (state, replacement) => {
@ -24,11 +26,6 @@ const EmojiReactions = {
tooManyReactions () {
return this.status.emoji_reactions.length > EMOJI_REACTION_COUNT_CUTOFF
},
emojiReactions () {
return this.showAll
? this.status.emoji_reactions
: this.status.emoji_reactions.slice(0, EMOJI_REACTION_COUNT_CUTOFF)
},
showMoreString () {
return `+${this.status.emoji_reactions.length - EMOJI_REACTION_COUNT_CUTOFF}`
},
@ -44,9 +41,50 @@ const EmojiReactions = {
},
loggedIn () {
return !!this.$store.state.users.currentUser
},
filteredReactions () {
// this is fucking Jank.
var finalReacts = this.emojiReactions()
var tempList = []
var reactionsFilterItems = this.$store.getters.mergedConfig.reactionsFilterItems.split("\n")
if (reactionsFilterItems.length > 0 && finalReacts.length > 0) {
reactionsFilterItems.forEach((item) => {
var toPush = []
finalReacts.forEach((react) => {
var rawReact = toRaw(react)["name"]
if (
// match regex
// >1 check is to prevent = just disabling all emoji reacts
item.charAt(0) == "=" && item.length > 1 && rawReact.match(item.substring(1))
// match everything explicit, if there's an @
|| rawReact == item.match(/.*@.*/) ? item : null
// match everything else
|| (rawReact.match(/.*@.*/) ? rawReact.split("@")[0] : rawReact) == item
) {
toPush.push(react)
}
})
toPush.forEach((nice) => {
tempList.push(nice)
})
})
}
tempList.forEach((item) => {
finalReacts.splice(finalReacts.indexOf(item), 1)
})
return finalReacts
}
},
methods: {
emojiReactions () {
return this.showAll
? this.status.emoji_reactions
: this.status.emoji_reactions.slice(0, EMOJI_REACTION_COUNT_CUTOFF)
},
toggleShowAll () {
this.showAll = !this.showAll
},

View file

@ -1,7 +1,7 @@
<template>
<div class="emoji-reactions">
<UserListPopover
v-for="(reaction) in emojiReactions"
v-for="(reaction) in filteredReactions"
:key="reaction.url || reaction.name"
:users="accountsForEmoji[reaction.url || reaction.name]"
>

View file

@ -112,6 +112,33 @@ const Notification = {
this.$store.dispatch('removeFollowRequest', this.user)
})
this.hideDenyConfirmDialog()
},
filtered (emoji) {
var isFiltered = false
if (emoji.match(/.*:.*/)) {
emoji = emoji.replaceAll(":", "")
}
var reactionsFilterItems = this.$store.getters.mergedConfig.reactionsFilterItems.split("\n")
if (reactionsFilterItems.length > 0)
reactionsFilterItems.forEach((item) => {
console.log(emoji)
if (
// match regex
// >1 check is to prevent = just disabling all emoji reacts
item.charAt(0) == "=" && item.length > 1 && emoji.match(item.substring(1))
// match everything explicit, if there's an @
|| emoji == item.match(/.*@.*/) ? item : null
// match everything else
|| (emoji.match(/.*@.*/) ? emoji.split("@")[0] : emoji) == item
) {
isFiltered = true
}
})
return isFiltered
}
},
computed: {

View file

@ -5,7 +5,7 @@
:compact="true"
:statusoid="notification.status"
/>
<div v-else>
<div v-else-if="(notification.emoji && !filtered(notification.emoji)) || !notification.emoji">
<div
v-if="needMute && !unmuted"
class="Notification container -muted"

View file

@ -2,6 +2,7 @@ import { filter, trim, debounce } from 'lodash'
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import TextSetting from '../helpers/text_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
@ -19,7 +20,8 @@ const FilteringTab = {
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting
IntegerSetting,
TextSetting
},
computed: {
...SharedComputedObject(),

View file

@ -115,6 +115,23 @@
</li>
</ul>
</div>
<div
class="setting-item"
>
<h2>{{ $t('settings.notification_setting_reactions') }}</h2>
<ul class="setting-list">
<li>
<h3>{{ $t('settings.reactions_filter') }}</h3>
<TextSetting
id="reactionsFilterItems"
path="reactionsFilterItems"
>
</TextSetting>
<!-- this is fucking stupid but for some reason the internationalization stuff (Not calling it i18n) hates this, so taking the liberty to also add styling -->
<div>Put reactions to remove in this box (formatted like reaction, no colons), one line for each. Results get matched like:<br><b><code>reaction</code></b> is a catch-all for all domains<br><b><code>reaction@domain.tld</code></b> is a reaction for a specific domain<br><b><code>=reaction</code></b> is a match via regex</div>
</li>
</ul>
</div>
</div>
</template>
<script src="./filtering_tab.js"></script>

View file

@ -686,6 +686,7 @@
"notification_setting_hide_if_cw": "Hide the contents of push notifications if under a Content Warning",
"notification_setting_hide_notification_contents": "Hide the sender and contents of push notifications",
"notification_setting_privacy": "Privacy",
"notification_setting_reactions": "Reactions",
"notification_setting_sounds": "Sounds",
"notification_visibility": "Types of notifications to show",
"notification_visibility_bites": "Bites",
@ -719,6 +720,7 @@
},
"profile_tab": "Profile",
"radii_help": "Set up interface edge rounding (in pixels)",
"reactions_filter": "Filter reactions by name",
"recurse_search": "When searching, run searches until no results are found anymore",
"recurse_search_limit": "Number of posts to stop recurse searching at (>300 will cause massive performance issues)",
"refresh_token": "Refresh token",

View file

@ -124,6 +124,7 @@ export const defaultState = {
boostsFollowDefVis: false,
searchPaginationLimit: 40,
sillyFeatures: false,
reactionsFilterItems: '',
recurseSearch: false,
recurseSearchLimit: 100,
renderMisskeyMarkdown: undefined,

View file

@ -80,6 +80,7 @@ const defaultState = {
deletePostsStreaming: true,
searchPaginationLimit: 40,
sillyFeatures: false,
reactionsFilterItems: '',
recurseSearch: false,
recurseSearchLimit: 100,
renderMisskeyMarkdown: true,