diff --git a/packages/app/src/Element/Feed/Timeline.tsx b/packages/app/src/Element/Feed/Timeline.tsx index 7ca9cc5c..5cd110fd 100644 --- a/packages/app/src/Element/Feed/Timeline.tsx +++ b/packages/app/src/Element/Feed/Timeline.tsx @@ -34,14 +34,14 @@ const Timeline = (props: TimelineProps) => { }, [props]); const feed: TimelineFeed = useTimelineFeed(props.subject, feedOptions); - const { muted, isMuted } = useModeration(); + const { muted, isEventMuted } = useModeration(); const filterPosts = useCallback( (nts: readonly TaggedNostrEvent[]) => { const a = [...nts.filter(a => a.kind !== EventKind.LiveEvent)]; props.noSort || a.sort((a, b) => b.created_at - a.created_at); return a ?.filter(a => (props.postsOnly ? !a.tags.some(b => b[0] === "e") : true)) - .filter(a => props.ignoreModeration || !isMuted(a.pubkey)); + .filter(a => props.ignoreModeration || !isEventMuted(a)); }, [props.postsOnly, muted, props.ignoreModeration], ); diff --git a/packages/app/src/Element/Feed/TimelineFollows.tsx b/packages/app/src/Element/Feed/TimelineFollows.tsx index 0b99c517..b485ff6f 100644 --- a/packages/app/src/Element/Feed/TimelineFollows.tsx +++ b/packages/app/src/Element/Feed/TimelineFollows.tsx @@ -39,7 +39,7 @@ const TimelineFollows = (props: TimelineFollowsProps) => { ); const system = useContext(SnortContext); const login = useLogin(); - const { muted, isMuted } = useModeration(); + const { muted, isEventMuted } = useModeration(); const sortedFeed = useMemo(() => orderDescending(feed), [feed]); @@ -49,11 +49,11 @@ const TimelineFollows = (props: TimelineFollowsProps) => { ); const filterPosts = useCallback( - function (nts: Array) { + (nts: Array) => { const a = nts.filter(a => a.kind !== EventKind.LiveEvent); return a ?.filter(postsOnly) - .filter(a => !isMuted(a.pubkey) && login.follows.item.includes(a.pubkey) && (props.noteFilter?.(a) ?? true)); + .filter(a => !isEventMuted(a) && login.follows.item.includes(a.pubkey) && (props.noteFilter?.(a) ?? true)); }, [postsOnly, muted, login.follows.timestamp], ); diff --git a/packages/app/src/Hooks/useModeration.tsx b/packages/app/src/Hooks/useModeration.tsx index 3156b9bf..748421df 100644 --- a/packages/app/src/Hooks/useModeration.tsx +++ b/packages/app/src/Hooks/useModeration.tsx @@ -1,4 +1,4 @@ -import { HexKey, TaggedNostrEvent } from "@snort/system"; +import { HexKey, NostrEvent, TaggedNostrEvent } from "@snort/system"; import useEventPublisher from "Hooks/useEventPublisher"; import useLogin from "Hooks/useLogin"; import { setBlocked, setMuted } from "Login"; @@ -60,7 +60,7 @@ export default function useModeration() { return appData.item.mutedWords.includes(word.toLowerCase()); } - function isEventMuted(ev: TaggedNostrEvent) { + function isEventMuted(ev: TaggedNostrEvent | NostrEvent) { return isMuted(ev.pubkey) || appData.item.mutedWords.some(w => ev.content.toLowerCase().includes(w)); } diff --git a/packages/app/src/Pages/onboarding/moderation.tsx b/packages/app/src/Pages/onboarding/moderation.tsx index e7017608..032b99fc 100644 --- a/packages/app/src/Pages/onboarding/moderation.tsx +++ b/packages/app/src/Pages/onboarding/moderation.tsx @@ -1,16 +1,16 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { ReactNode, useState } from "react"; +import { useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; +import { unixNowMs } from "@snort/shared"; + import AsyncButton from "Element/AsyncButton"; -import classNames from "classnames"; import { appendDedupe } from "SnortUtils"; -import useEventPublisher from "Hooks/useEventPublisher"; -import { setMuted } from "Login"; import { ToggleSwitch } from "Icons/Toggle"; +import { updateAppData } from "Login"; +import useLogin from "Hooks/useLogin"; export const FixedModeration = { - hateSpeech: { + /*hateSpeech: { title: , words: [], canEdit: false, @@ -19,7 +19,7 @@ export const FixedModeration = { title: , words: [], canEdit: false, - }, + },*/ nsfw: { title: , words: [ @@ -43,6 +43,7 @@ export const FixedModeration = { "explicit language", "adult-only", "mature language", + "sex" ], canEdit: false, }, @@ -72,18 +73,50 @@ export const FixedModeration = { "crypto wallet", "satoshi nakamoto", ], - canEdit: true, + canEdit: false, }, politics: { title: , - words: [], - canEdit: true, + words: [ + "politics", + "election", + "democrat", + "republican", + "senate", + "congress", + "parliament", + "president", + "prime minister", + "policy", + "legislation", + "vote", + "campaign", + "government", + "political party", + "lobbying", + "referendum", + "bill", + "conservative", + "liberal", + "left-wing", + "right-wing", + "socialist", + "capitalist", + "diplomacy", + "sanction", + "geopolitics", + "activism", + "protest", + "rally" + ], + canEdit: false, }, }; export function Moderation() { - const { publisher, system } = useEventPublisher(); + const id = useLogin(s => s.id); const [topics, setTopics] = useState>(Object.keys(FixedModeration)); + const [extraTerms, setExtraTerms] = useState(""); const navigate = useNavigate(); return ( @@ -135,7 +168,7 @@ export function Moderation() { - + topics.includes(k)) .map(([, v]) => v.words) - .flat(); + .flat() + .concat(extraTerms.split(",").map(a => a.trim())); if (words.length > 0) { - // no + updateAppData(id, ad => { + return { + item: { + ...ad, + mutedWords: appendDedupe(ad.mutedWords, words) + }, + timestamp: unixNowMs() + } + }) } navigate("/"); }}> diff --git a/packages/app/src/Pages/onboarding/topics.tsx b/packages/app/src/Pages/onboarding/topics.tsx index e96ed4f1..ec3a9ffa 100644 --- a/packages/app/src/Pages/onboarding/topics.tsx +++ b/packages/app/src/Pages/onboarding/topics.tsx @@ -10,35 +10,248 @@ import { NostrHashtagLink } from "@snort/system"; export const FixedTopics = { life: { text: , - tags: ["life"], + tags: [ + "life", + "lifestyle", + "dailyinspiration", + "motivation", + "lifelessons", + "personaldevelopment", + "happiness", + "wellbeing", + "mindfulness", + "selfcare", + "positivity", + "growth", + "inspiration", + "lifegoals", + "mindset", + "joy", + "balance", + "fulfillment", + "purpose", + "living", + "lifetips", + "lifehacks", + "wellness", + "lifejourney", + "enjoylife", + "simplepleasures", + "gratitude", + "lifeadvice", + "lifecoaching", + "lifelove" + ], }, science: { text: , - tags: ["science"], + tags: [ + "science", + "research", + "innovation", + "technology", + "biology", + "physics", + "chemistry", + "astronomy", + "environment", + "ecology", + "geology", + "neuroscience", + "genetics", + "data", + "experiment", + "theory", + "discovery", + "engineering", + "mathematics", + "robotics", + "artificialintelligence", + "climate", + "space", + "quantum", + "microbiology", + "biotechnology", + "nanotechnology", + "pharmacology", + "astrophysics", + "scientificmethod" + ], }, nature: { text: , - tags: ["nature"], + tags: [ + "nature", + "wildlife", + "forest", + "mountains", + "rivers", + "oceans", + "flora", + "fauna", + "ecosystem", + "biodiversity", + "conservation", + "habitat", + "landscape", + "outdoors", + "environment", + "geography", + "earth", + "climate", + "naturalbeauty", + "wilderness", + "green", + "sustainability", + "wildlifeconservation", + "nationalpark", + "gardening", + "hiking", + "birdwatching", + "ecotourism", + "photography", + "naturelovers" + ], }, business: { text: , - tags: ["business"], + tags: [ + "business", + "entrepreneurship", + "marketing", + "finance", + "innovation", + "management", + "startup", + "leadership", + "economics", + "strategy", + "branding", + "sales", + "technology", + "investment", + "networking", + "growth", + "corporate", + "customer", + "market", + "productivity", + "advertising", + "ecommerce", + "analytics", + "humanresources", + "globalbusiness", + "digitalmarketing", + "socialmedia", + "sustainability", + "entrepreneur", + "businessdevelopment" + ], }, game: { text: , - tags: ["game", "gaming"], + tags: [ + "gaming", + "videogames", + "esports", + "multiplayer", + "onlinegaming", + "gameplay", + "streaming", + "gamer", + "console", + "pcgaming", + "mobilegaming", + "gamedevelopment", + "virtualreality", + "roleplaying", + "strategygames", + "actiongames", + "simulation", + "indiegames", + "adventuregames", + "puzzle", + "fantasy", + "scifi", + "horror", + "sports", + "racing", + "fighting", + "platformer", + "mmorpg", + "retrogaming", + "arcade" + ], }, sport: { text: , - tags: ["sport"], + tags: [ + "sports", + "athletics", + "soccer", + "basketball", + "baseball", + "football", + "tennis", + "golf", + "swimming", + "running", + "cycling", + "volleyball", + "hockey", + "skiing", + "boxing", + "martialarts", + "gymnastics", + "cricket", + "rugby", + "tabletennis", + "badminton", + "fishing", + "archery", + "bowling", + "surfing", + "skateboarding", + "motorsports", + "equestrian", + "fitness", + "yoga" + ], }, photography: { text: , - tags: ["photography"], - }, - bitcoin: { - text: , - tags: ["bitcoin"], + tags: [ + "photography", + "landscape", + "portrait", + "naturephotography", + "streetphotography", + "blackandwhite", + "travelphotography", + "macro", + "wildlifephotography", + "urbanphotography", + "nightphotography", + "fashionphotography", + "fineartphotography", + "documentary", + "sportsphotography", + "foodphotography", + "architecturalphotography", + "candid", + "aerialphotography", + "underwaterphotography", + "filmphotography", + "digitalphotography", + "photographytips", + "photoediting", + "photographygear", + "lighting", + "composition", + "exposure", + "photographyworkshop", + "photographyart" + ], }, };