Update mutewords and hiddenposts to use the new sdk
This commit is contained in:
@@ -59,7 +59,7 @@ export function TagMenu({
|
|||||||
const displayTag = '#' + tag
|
const displayTag = '#' + tag
|
||||||
|
|
||||||
const isMuted = Boolean(
|
const isMuted = Boolean(
|
||||||
(preferences?.mutedWords?.find(
|
(preferences?.moderationPrefs.mutedWords?.find(
|
||||||
m => m.value === tag && m.targets.includes('tag'),
|
m => m.value === tag && m.targets.includes('tag'),
|
||||||
) ??
|
) ??
|
||||||
optimisticUpsert?.find(
|
optimisticUpsert?.find(
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function TagMenu({
|
|||||||
const {mutateAsync: removeMutedWord, variables: optimisticRemove} =
|
const {mutateAsync: removeMutedWord, variables: optimisticRemove} =
|
||||||
useRemoveMutedWordMutation()
|
useRemoveMutedWordMutation()
|
||||||
const isMuted = Boolean(
|
const isMuted = Boolean(
|
||||||
(preferences?.mutedWords?.find(
|
(preferences?.moderationPrefs.mutedWords?.find(
|
||||||
m => m.value === tag && m.targets.includes('tag'),
|
m => m.value === tag && m.targets.includes('tag'),
|
||||||
) ??
|
) ??
|
||||||
optimisticUpsert?.find(
|
optimisticUpsert?.find(
|
||||||
|
|||||||
@@ -233,8 +233,8 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : preferences.mutedWords.length ? (
|
) : preferences.moderationPrefs.mutedWords.length ? (
|
||||||
[...preferences.mutedWords]
|
[...preferences.moderationPrefs.mutedWords]
|
||||||
.reverse()
|
.reverse()
|
||||||
.map((word, i) => (
|
.map((word, i) => (
|
||||||
<MutedWordRow
|
<MutedWordRow
|
||||||
|
|||||||
@@ -94,15 +94,12 @@ function ModerationDetailsDialogInner({
|
|||||||
name = _(msg`Account Muted`)
|
name = _(msg`Account Muted`)
|
||||||
description = _(msg`You have muted this user.`)
|
description = _(msg`You have muted this user.`)
|
||||||
}
|
}
|
||||||
// TODO
|
} else if (modcause.type === 'mute-word') {
|
||||||
// } else if (cause.type === 'muted-word') {
|
name = _(msg`Post Hidden by Muted Word`)
|
||||||
// return {
|
description = _(msg`You've chosen to hide a word or tag within this post.`)
|
||||||
// icon: EyeSlash,
|
} else if (modcause.type === 'hidden') {
|
||||||
// name: _(msg`Post hidden by muted word`),
|
name = _(msg`Post Hidden by You`)
|
||||||
// description: _(
|
description = _(msg`You have hidden this post.`)
|
||||||
// msg`You've chosen to hide a word or tag within this post.`,
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
} else if (modcause.type === 'label') {
|
} else if (modcause.type === 'label') {
|
||||||
name = desc.name
|
name = desc.name
|
||||||
description = desc.description
|
description = desc.description
|
||||||
|
|||||||
@@ -1,692 +0,0 @@
|
|||||||
import {describe, it, expect} from '@jest/globals'
|
|
||||||
import {RichText} from '@atproto/api'
|
|
||||||
|
|
||||||
import {hasMutedWord} from '../moderatePost_wrapped'
|
|
||||||
|
|
||||||
describe(`hasMutedWord`, () => {
|
|
||||||
describe(`tags`, () => {
|
|
||||||
it(`match: outline tag`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `This is a post #inlineTag`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'outlineTag', targets: ['tag']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: ['outlineTag'],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: inline tag`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `This is a post #inlineTag`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'inlineTag', targets: ['tag']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: ['outlineTag'],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: content target matches inline tag`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `This is a post #inlineTag`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'inlineTag', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: ['outlineTag'],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`no match: only tag targets`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `This is a post`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'inlineTag', targets: ['tag']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`early exits`, () => {
|
|
||||||
it(`match: single character 希`, () => {
|
|
||||||
/**
|
|
||||||
* @see https://bsky.app/profile/mukuuji.bsky.social/post/3klji4fvsdk2c
|
|
||||||
*/
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `改善希望です`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: '希', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`no match: long muted word, short post`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `hey`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'politics', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: exact text`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `javascript`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'javascript', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`general content`, () => {
|
|
||||||
it(`match: word within post`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `This is a post about javascript`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'javascript', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`no match: partial word`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Use your brain, Eric`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'ai', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: multiline`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Use your\n\tbrain, Eric`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'brain', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: :)`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `So happy :)`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `:)`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`punctuation semi-fuzzy`, () => {
|
|
||||||
describe(`yay!`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `We're federating, yay!`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: yay!`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'yay!', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: yay`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'yay', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`y!ppee!!`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `We're federating, y!ppee!!`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: y!ppee`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'y!ppee', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
// single exclamation point, source has double
|
|
||||||
it(`no match: y!ppee!`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'y!ppee!', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`Why so S@assy?`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Why so S@assy?`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: S@assy`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'S@assy', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: s@assy`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 's@assy', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`New York Times`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `New York Times`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
// case insensitive
|
|
||||||
it(`match: new york times`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'new york times', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`!command`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Idk maybe a bot !command`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: !command`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `!command`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: command`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `command`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`no match: !command`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Idk maybe a bot command`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `!command`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`e/acc`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `I'm e/acc pilled`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: e/acc`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `e/acc`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: acc`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `acc`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`super-bad`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `I'm super-bad`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: super-bad`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `super-bad`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: super`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `super`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: super bad`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `super bad`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: superbad`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `superbad`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`idk_what_this_would_be`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Weird post with idk_what_this_would_be`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: idk what this would be`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `idk what this would be`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`no match: idk what this would be for`, () => {
|
|
||||||
// extra word
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [
|
|
||||||
{value: `idk what this would be for`, targets: ['content']},
|
|
||||||
],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: idk`, () => {
|
|
||||||
// extra word
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `idk`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: idkwhatthiswouldbe`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `idkwhatthiswouldbe`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`parentheses`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Post with context(iykyk)`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: context(iykyk)`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `context(iykyk)`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: context`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `context`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: iykyk`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `iykyk`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: (iykyk)`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `(iykyk)`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`🦋`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Post with 🦋`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: 🦋`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: `🦋`, targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`phrases`, () => {
|
|
||||||
describe(`I like turtles, or how I learned to stop worrying and love the internet.`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `I like turtles, or how I learned to stop worrying and love the internet.`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
it(`match: stop worrying`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'stop worrying', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`match: turtles, or how`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'turtles, or how', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`languages without spaces`, () => {
|
|
||||||
// I love turtles, or how I learned to stop worrying and love the internet
|
|
||||||
describe(`私はカメが好きです、またはどのようにして心配するのをやめてインターネットを愛するようになったのか`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `私はカメが好きです、またはどのようにして心配するのをやめてインターネットを愛するようになったのか`,
|
|
||||||
})
|
|
||||||
rt.detectFacetsWithoutResolution()
|
|
||||||
|
|
||||||
// internet
|
|
||||||
it(`match: インターネット`, () => {
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'インターネット', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
languages: ['ja'],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe(`doesn't mute own post`, () => {
|
|
||||||
it(`does mute if it isn't own post`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Mute words!`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'words', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`doesn't mute own post when muted word is in text`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Mute words!`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'words', targets: ['content']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it(`doesn't mute own post when muted word is in tags`, () => {
|
|
||||||
const rt = new RichText({
|
|
||||||
text: `Mute #words!`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const match = hasMutedWord({
|
|
||||||
mutedWords: [{value: 'words', targets: ['tags']}],
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
outlineTags: [],
|
|
||||||
isOwnPost: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(match).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,385 +1,16 @@
|
|||||||
import {
|
import {moderatePost} from '@atproto/api'
|
||||||
// AppBskyEmbedRecord,
|
|
||||||
// AppBskyEmbedRecordWithMedia,
|
// TODO
|
||||||
moderatePost,
|
// do we think we'll need this again?
|
||||||
AppBskyActorDefs,
|
// if not we can ditch it!
|
||||||
// AppBskyFeedPost,
|
// -prf
|
||||||
AppBskyRichtextFacet,
|
|
||||||
// AppBskyEmbedImages,
|
|
||||||
// AppBskyEmbedExternal,
|
|
||||||
} from '@atproto/api'
|
|
||||||
|
|
||||||
type ModeratePost = typeof moderatePost
|
type ModeratePost = typeof moderatePost
|
||||||
type Options = Parameters<ModeratePost>[1] & {
|
type Options = Parameters<ModeratePost>[1]
|
||||||
hiddenPosts?: string[]
|
|
||||||
mutedWords?: AppBskyActorDefs.MutedWord[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const REGEX = {
|
|
||||||
LEADING_TRAILING_PUNCTUATION: /(?:^\p{P}+|\p{P}+$)/gu,
|
|
||||||
ESCAPE: /[[\]{}()*+?.\\^$|\s]/g,
|
|
||||||
SEPARATORS: /[\/\-\–\—\(\)\[\]\_]+/g,
|
|
||||||
WORD_BOUNDARY: /[\s\n\t\r\f\v]+?/g,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of 2-letter lang codes for languages that either don't use spaces, or
|
|
||||||
* don't use spaces in a way conducive to word-based filtering.
|
|
||||||
*
|
|
||||||
* For these, we use a simple `String.includes` to check for a match.
|
|
||||||
*/
|
|
||||||
const LANGUAGE_EXCEPTIONS = [
|
|
||||||
'ja', // Japanese
|
|
||||||
'zh', // Chinese
|
|
||||||
'ko', // Korean
|
|
||||||
'th', // Thai
|
|
||||||
'vi', // Vietnamese
|
|
||||||
]
|
|
||||||
|
|
||||||
export function hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text,
|
|
||||||
facets,
|
|
||||||
outlineTags,
|
|
||||||
languages,
|
|
||||||
isOwnPost,
|
|
||||||
}: {
|
|
||||||
mutedWords: AppBskyActorDefs.MutedWord[]
|
|
||||||
text: string
|
|
||||||
facets?: AppBskyRichtextFacet.Main[]
|
|
||||||
outlineTags?: string[]
|
|
||||||
languages?: string[]
|
|
||||||
isOwnPost: boolean
|
|
||||||
}) {
|
|
||||||
if (isOwnPost) return false
|
|
||||||
|
|
||||||
const exception = LANGUAGE_EXCEPTIONS.includes(languages?.[0] || '')
|
|
||||||
const tags = ([] as string[])
|
|
||||||
.concat(outlineTags || [])
|
|
||||||
.concat(
|
|
||||||
facets
|
|
||||||
?.filter(facet => {
|
|
||||||
return facet.features.find(feature =>
|
|
||||||
AppBskyRichtextFacet.isTag(feature),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map(t => t.features[0].tag as string) || [],
|
|
||||||
)
|
|
||||||
.map(t => t.toLowerCase())
|
|
||||||
|
|
||||||
for (const mute of mutedWords) {
|
|
||||||
const mutedWord = mute.value.toLowerCase()
|
|
||||||
const postText = text.toLowerCase()
|
|
||||||
|
|
||||||
// `content` applies to tags as well
|
|
||||||
if (tags.includes(mutedWord)) return true
|
|
||||||
// rest of the checks are for `content` only
|
|
||||||
if (!mute.targets.includes('content')) continue
|
|
||||||
// single character or other exception, has to use includes
|
|
||||||
if ((mutedWord.length === 1 || exception) && postText.includes(mutedWord))
|
|
||||||
return true
|
|
||||||
// too long
|
|
||||||
if (mutedWord.length > postText.length) continue
|
|
||||||
// exact match
|
|
||||||
if (mutedWord === postText) return true
|
|
||||||
// any muted phrase with space or punctuation
|
|
||||||
if (/(?:\s|\p{P})+?/u.test(mutedWord) && postText.includes(mutedWord))
|
|
||||||
return true
|
|
||||||
|
|
||||||
// check individual character groups
|
|
||||||
const words = postText.split(REGEX.WORD_BOUNDARY)
|
|
||||||
for (const word of words) {
|
|
||||||
if (word === mutedWord) return true
|
|
||||||
|
|
||||||
// compare word without leading/trailing punctuation, but allow internal
|
|
||||||
// punctuation (such as `s@ssy`)
|
|
||||||
const wordTrimmedPunctuation = word.replace(
|
|
||||||
REGEX.LEADING_TRAILING_PUNCTUATION,
|
|
||||||
'',
|
|
||||||
)
|
|
||||||
|
|
||||||
if (mutedWord === wordTrimmedPunctuation) return true
|
|
||||||
if (mutedWord.length > wordTrimmedPunctuation.length) continue
|
|
||||||
|
|
||||||
// handle hyphenated, slash separated words, etc
|
|
||||||
if (REGEX.SEPARATORS.test(wordTrimmedPunctuation)) {
|
|
||||||
// check against full normalized phrase
|
|
||||||
const wordNormalizedSeparators = wordTrimmedPunctuation.replace(
|
|
||||||
REGEX.SEPARATORS,
|
|
||||||
' ',
|
|
||||||
)
|
|
||||||
const mutedWordNormalizedSeparators = mutedWord.replace(
|
|
||||||
REGEX.SEPARATORS,
|
|
||||||
' ',
|
|
||||||
)
|
|
||||||
// hyphenated (or other sep) to spaced words
|
|
||||||
if (wordNormalizedSeparators === mutedWordNormalizedSeparators)
|
|
||||||
return true
|
|
||||||
|
|
||||||
/* Disabled for now e.g. `super-cool` to `supercool`
|
|
||||||
const wordNormalizedCompressed = wordNormalizedSeparators.replace(
|
|
||||||
REGEX.WORD_BOUNDARY,
|
|
||||||
'',
|
|
||||||
)
|
|
||||||
const mutedWordNormalizedCompressed =
|
|
||||||
mutedWordNormalizedSeparators.replace(/\s+?/g, '')
|
|
||||||
// hyphenated (or other sep) to non-hyphenated contiguous word
|
|
||||||
if (mutedWordNormalizedCompressed === wordNormalizedCompressed)
|
|
||||||
return true
|
|
||||||
*/
|
|
||||||
|
|
||||||
// then individual parts of separated phrases/words
|
|
||||||
const wordParts = wordTrimmedPunctuation.split(REGEX.SEPARATORS)
|
|
||||||
for (const wp of wordParts) {
|
|
||||||
// still retain internal punctuation
|
|
||||||
if (wp === mutedWord) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export function moderatePost_wrapped(
|
export function moderatePost_wrapped(
|
||||||
subject: Parameters<ModeratePost>[0],
|
subject: Parameters<ModeratePost>[0],
|
||||||
opts: Options,
|
opts: Options,
|
||||||
) {
|
) {
|
||||||
const moderation = moderatePost(subject, opts)
|
return moderatePost(subject, opts)
|
||||||
moderation.addHidden(opts.hiddenPosts?.includes(subject.uri) || false)
|
|
||||||
return moderation
|
|
||||||
/* TODO
|
|
||||||
const {hiddenPosts = [], mutedWords = [], ...options} = opts
|
|
||||||
const moderations = moderatePost(subject, options)
|
|
||||||
const isOwnPost = subject.author.did === opts.userDid
|
|
||||||
|
|
||||||
if (hiddenPosts.includes(subject.uri)) {
|
|
||||||
moderations.content.filter = true
|
|
||||||
moderations.content.blur = true
|
|
||||||
if (!moderations.content.cause) {
|
|
||||||
moderations.content.cause = {
|
|
||||||
// @ts-ignore Temporary extension to the moderation system -prf
|
|
||||||
type: 'post-hidden',
|
|
||||||
source: {type: 'user'},
|
|
||||||
priority: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyFeedPost.isRecord(subject.record)) {
|
|
||||||
let muted = hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: subject.record.text,
|
|
||||||
facets: subject.record.facets || [],
|
|
||||||
outlineTags: subject.record.tags || [],
|
|
||||||
languages: subject.record.langs,
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (
|
|
||||||
subject.record.embed &&
|
|
||||||
AppBskyEmbedImages.isMain(subject.record.embed)
|
|
||||||
) {
|
|
||||||
for (const image of subject.record.embed.images) {
|
|
||||||
muted =
|
|
||||||
muted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: image.alt,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: subject.record.langs,
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (muted) {
|
|
||||||
moderations.content.filter = true
|
|
||||||
moderations.content.blur = true
|
|
||||||
if (!moderations.content.cause) {
|
|
||||||
moderations.content.cause = {
|
|
||||||
// @ts-ignore Temporary extension to the moderation system -prf
|
|
||||||
type: 'muted-word',
|
|
||||||
source: {type: 'user'},
|
|
||||||
priority: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subject.embed) {
|
|
||||||
let embedHidden = false
|
|
||||||
let embedMuted = false
|
|
||||||
let externalMuted = false
|
|
||||||
|
|
||||||
if (AppBskyEmbedRecord.isViewRecord(subject.embed.record)) {
|
|
||||||
embedHidden = hiddenPosts.includes(subject.embed.record.uri)
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
AppBskyEmbedRecordWithMedia.isView(subject.embed) &&
|
|
||||||
AppBskyEmbedRecord.isViewRecord(subject.embed.record.record)
|
|
||||||
) {
|
|
||||||
embedHidden = hiddenPosts.includes(subject.embed.record.record.uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedRecord.isViewRecord(subject.embed.record)) {
|
|
||||||
if (AppBskyFeedPost.isRecord(subject.embed.record.value)) {
|
|
||||||
const embeddedPost = subject.embed.record.value
|
|
||||||
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: embeddedPost.text,
|
|
||||||
facets: embeddedPost.facets,
|
|
||||||
outlineTags: embeddedPost.tags,
|
|
||||||
languages: embeddedPost.langs,
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (AppBskyEmbedImages.isMain(embeddedPost.embed)) {
|
|
||||||
for (const image of embeddedPost.embed.images) {
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: image.alt,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: embeddedPost.langs,
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedExternal.isMain(embeddedPost.embed)) {
|
|
||||||
const {external} = embeddedPost.embed
|
|
||||||
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: external.title + ' ' + external.description,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: [],
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedRecordWithMedia.isMain(embeddedPost.embed)) {
|
|
||||||
if (AppBskyEmbedExternal.isMain(embeddedPost.embed.media)) {
|
|
||||||
const {external} = embeddedPost.embed.media
|
|
||||||
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: external.title + ' ' + external.description,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: [],
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedImages.isMain(embeddedPost.embed.media)) {
|
|
||||||
for (const image of embeddedPost.embed.media.images) {
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: image.alt,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: AppBskyFeedPost.isRecord(embeddedPost.record)
|
|
||||||
? embeddedPost.langs
|
|
||||||
: [],
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedExternal.isView(subject.embed)) {
|
|
||||||
const {external} = subject.embed
|
|
||||||
|
|
||||||
externalMuted =
|
|
||||||
externalMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: external.title + ' ' + external.description,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: [],
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
AppBskyEmbedRecordWithMedia.isView(subject.embed) &&
|
|
||||||
AppBskyEmbedRecord.isViewRecord(subject.embed.record.record)
|
|
||||||
) {
|
|
||||||
if (AppBskyFeedPost.isRecord(subject.embed.record.record.value)) {
|
|
||||||
const post = subject.embed.record.record.value
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: post.text,
|
|
||||||
facets: post.facets,
|
|
||||||
outlineTags: post.tags,
|
|
||||||
languages: post.langs,
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedImages.isView(subject.embed.media)) {
|
|
||||||
for (const image of subject.embed.media.images) {
|
|
||||||
embedMuted =
|
|
||||||
embedMuted ||
|
|
||||||
hasMutedWord({
|
|
||||||
mutedWords,
|
|
||||||
text: image.alt,
|
|
||||||
facets: [],
|
|
||||||
outlineTags: [],
|
|
||||||
languages: AppBskyFeedPost.isRecord(subject.record)
|
|
||||||
? subject.record.langs
|
|
||||||
: [],
|
|
||||||
isOwnPost,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (embedHidden) {
|
|
||||||
moderations.embed.filter = true
|
|
||||||
moderations.embed.blur = true
|
|
||||||
if (!moderations.embed.cause) {
|
|
||||||
moderations.embed.cause = {
|
|
||||||
// @ts-ignore Temporary extension to the moderation system -prf
|
|
||||||
type: 'post-hidden',
|
|
||||||
source: {type: 'user'},
|
|
||||||
priority: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (externalMuted || embedMuted) {
|
|
||||||
moderations.content.filter = true
|
|
||||||
moderations.content.blur = true
|
|
||||||
if (!moderations.content.cause) {
|
|
||||||
moderations.content.cause = {
|
|
||||||
// @ts-ignore Temporary extension to the moderation system -prf
|
|
||||||
type: 'muted-word',
|
|
||||||
source: {type: 'user'},
|
|
||||||
priority: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return moderations
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,17 +84,15 @@ export function useModerationCauseDescription(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO
|
if (cause.type === 'mute-word') {
|
||||||
// if (cause.type === 'muted-word') {
|
return {
|
||||||
// return {
|
icon: EyeSlash,
|
||||||
// icon: EyeSlash,
|
name: _(msg`Post Hidden by Muted Word`),
|
||||||
// name: _(msg`Post hidden by muted word`),
|
description: _(
|
||||||
// description: _(
|
msg`You've chosen to hide a word or tag within this post.`,
|
||||||
// msg`You've chosen to hide a word or tag within this post.`,
|
),
|
||||||
// ),
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
// @ts-ignore Temporary extension to the moderation system -prf
|
|
||||||
if (cause.type === 'hidden') {
|
if (cause.type === 'hidden') {
|
||||||
return {
|
return {
|
||||||
icon: EyeSlash,
|
icon: EyeSlash,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
usePreferencesSetAdultContentMutation,
|
usePreferencesSetAdultContentMutation,
|
||||||
} from '#/state/queries/preferences'
|
} from '#/state/queries/preferences'
|
||||||
import {useLabelersDetailedInfoQuery} from '#/state/queries/labeler'
|
import {useLabelersDetailedInfoQuery} from '#/state/queries/labeler'
|
||||||
|
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
||||||
|
|
||||||
import {useTheme, atoms as a, useBreakpoints} from '#/alf'
|
import {useTheme, atoms as a, useBreakpoints} from '#/alf'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
@@ -34,6 +35,7 @@ import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Perso
|
|||||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import * as Toggle from '#/components/forms/Toggle'
|
import * as Toggle from '#/components/forms/Toggle'
|
||||||
|
import {Button} from '#/components/Button'
|
||||||
import {InlineLink, Link} from '#/components/Link'
|
import {InlineLink, Link} from '#/components/Link'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {getModerationServiceTitle} from '#/lib/moderation'
|
import {getModerationServiceTitle} from '#/lib/moderation'
|
||||||
@@ -132,10 +134,12 @@ export function ModerationScreenInner({
|
|||||||
preferences: UsePreferencesQueryResponse
|
preferences: UsePreferencesQueryResponse
|
||||||
labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
|
labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
|
||||||
}) {
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
const {screen} = useAnalytics()
|
const {screen} = useAnalytics()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
@@ -186,6 +190,29 @@ export function ModerationScreenInner({
|
|||||||
a.overflow_hidden,
|
a.overflow_hidden,
|
||||||
t.atoms.bg_contrast_25,
|
t.atoms.bg_contrast_25,
|
||||||
]}>
|
]}>
|
||||||
|
<Button
|
||||||
|
testID="mutewordsBtn"
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_between,
|
||||||
|
a.p_lg,
|
||||||
|
a.gap_sm,
|
||||||
|
]}
|
||||||
|
label={_(msg`Muted words & tags`)}
|
||||||
|
onPress={() => mutedWordsDialogControl.open()}>
|
||||||
|
<View style={[a.flex_row, a.align_center, a.gap_md]}>
|
||||||
|
<Group size="md" style={[t.atoms.text_contrast_medium]} />
|
||||||
|
<Text style={[a.text_md]}>
|
||||||
|
<Trans>Muted words & tags</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<ChevronRight
|
||||||
|
size="sm"
|
||||||
|
style={[t.atoms.text_contrast_low, a.self_end]}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<Divider />
|
||||||
<Link
|
<Link
|
||||||
testID="moderationlistsBtn"
|
testID="moderationlistsBtn"
|
||||||
style={[
|
style={[
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = {
|
|||||||
adultContentEnabled: false,
|
adultContentEnabled: false,
|
||||||
labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES,
|
labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES,
|
||||||
labelers: [],
|
labelers: [],
|
||||||
|
mutedWords: [],
|
||||||
|
hiddenPosts: [],
|
||||||
},
|
},
|
||||||
feedViewPrefs: DEFAULT_HOME_FEED_PREFS,
|
feedViewPrefs: DEFAULT_HOME_FEED_PREFS,
|
||||||
threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS,
|
threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS,
|
||||||
userAge: 13, // TODO(pwi)
|
userAge: 13, // TODO(pwi)
|
||||||
interests: {tags: []},
|
interests: {tags: []},
|
||||||
mutedWords: [],
|
|
||||||
hiddenPosts: [],
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
DEFAULT_LOGGED_OUT_PREFERENCES,
|
DEFAULT_LOGGED_OUT_PREFERENCES,
|
||||||
} from '#/state/queries/preferences/const'
|
} from '#/state/queries/preferences/const'
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {useHiddenPosts} from '#/state/preferences/hidden-posts'
|
|
||||||
import {useLabelDefinitions} from '#/state/queries/preferences/moderation'
|
import {useLabelDefinitions} from '#/state/queries/preferences/moderation'
|
||||||
|
|
||||||
export * from '#/state/queries/preferences/types'
|
export * from '#/state/queries/preferences/types'
|
||||||
@@ -78,7 +77,6 @@ export function useModerationOpts() {
|
|||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const prefs = usePreferencesQuery()
|
const prefs = usePreferencesQuery()
|
||||||
const {labelDefs} = useLabelDefinitions()
|
const {labelDefs} = useLabelDefinitions()
|
||||||
const hiddenPosts = useHiddenPosts()
|
|
||||||
const opts = useMemo<ModerationOpts | undefined>(() => {
|
const opts = useMemo<ModerationOpts | undefined>(() => {
|
||||||
if (override) {
|
if (override) {
|
||||||
return override
|
return override
|
||||||
@@ -86,17 +84,12 @@ export function useModerationOpts() {
|
|||||||
if (!prefs.data) {
|
if (!prefs.data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const moderationPrefs = prefs.data.moderationPrefs
|
|
||||||
return {
|
return {
|
||||||
userDid: currentAccount?.did,
|
userDid: currentAccount?.did,
|
||||||
prefs: {
|
prefs: prefs.data.moderationPrefs,
|
||||||
...moderationPrefs,
|
|
||||||
hiddenPosts,
|
|
||||||
},
|
|
||||||
mutedWords: prefs.data.mutedWords || [], // TODO
|
|
||||||
labelDefs,
|
labelDefs,
|
||||||
}
|
}
|
||||||
}, [override, currentAccount, labelDefs, prefs.data, hiddenPosts])
|
}, [override, currentAccount, labelDefs, prefs.data])
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,8 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
labels: {[label[0]]: visibility[0] as LabelPreference},
|
labels: {[label[0]]: visibility[0] as LabelPreference},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
mutedWords: [],
|
||||||
|
hiddenPosts: [],
|
||||||
},
|
},
|
||||||
labelDefs: {
|
labelDefs: {
|
||||||
'did:plc:fake-labeler': [
|
'did:plc:fake-labeler': [
|
||||||
|
|||||||
Reference in New Issue
Block a user