This commit is contained in:
parent
24978f4e62
commit
81d6f41050
@ -34,14 +34,14 @@ const Timeline = (props: TimelineProps) => {
|
|||||||
}, [props]);
|
}, [props]);
|
||||||
const feed: TimelineFeed = useTimelineFeed(props.subject, feedOptions);
|
const feed: TimelineFeed = useTimelineFeed(props.subject, feedOptions);
|
||||||
|
|
||||||
const { muted, isMuted } = useModeration();
|
const { muted, isEventMuted } = useModeration();
|
||||||
const filterPosts = useCallback(
|
const filterPosts = useCallback(
|
||||||
(nts: readonly TaggedNostrEvent[]) => {
|
(nts: readonly TaggedNostrEvent[]) => {
|
||||||
const a = [...nts.filter(a => a.kind !== EventKind.LiveEvent)];
|
const a = [...nts.filter(a => a.kind !== EventKind.LiveEvent)];
|
||||||
props.noSort || a.sort((a, b) => b.created_at - a.created_at);
|
props.noSort || a.sort((a, b) => b.created_at - a.created_at);
|
||||||
return a
|
return a
|
||||||
?.filter(a => (props.postsOnly ? !a.tags.some(b => b[0] === "e") : true))
|
?.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],
|
[props.postsOnly, muted, props.ignoreModeration],
|
||||||
);
|
);
|
||||||
|
@ -39,7 +39,7 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
|
|||||||
);
|
);
|
||||||
const system = useContext(SnortContext);
|
const system = useContext(SnortContext);
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const { muted, isMuted } = useModeration();
|
const { muted, isEventMuted } = useModeration();
|
||||||
|
|
||||||
const sortedFeed = useMemo(() => orderDescending(feed), [feed]);
|
const sortedFeed = useMemo(() => orderDescending(feed), [feed]);
|
||||||
|
|
||||||
@ -49,11 +49,11 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const filterPosts = useCallback(
|
const filterPosts = useCallback(
|
||||||
function <T extends NostrEvent>(nts: Array<T>) {
|
(nts: Array<TaggedNostrEvent>) => {
|
||||||
const a = nts.filter(a => a.kind !== EventKind.LiveEvent);
|
const a = nts.filter(a => a.kind !== EventKind.LiveEvent);
|
||||||
return a
|
return a
|
||||||
?.filter(postsOnly)
|
?.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],
|
[postsOnly, muted, login.follows.timestamp],
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, TaggedNostrEvent } from "@snort/system";
|
import { HexKey, NostrEvent, TaggedNostrEvent } from "@snort/system";
|
||||||
import useEventPublisher from "Hooks/useEventPublisher";
|
import useEventPublisher from "Hooks/useEventPublisher";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { setBlocked, setMuted } from "Login";
|
import { setBlocked, setMuted } from "Login";
|
||||||
@ -60,7 +60,7 @@ export default function useModeration() {
|
|||||||
return appData.item.mutedWords.includes(word.toLowerCase());
|
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));
|
return isMuted(ev.pubkey) || appData.item.mutedWords.some(w => ev.content.toLowerCase().includes(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
import { useState } from "react";
|
||||||
import { ReactNode, useState } from "react";
|
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { unixNowMs } from "@snort/shared";
|
||||||
|
|
||||||
import AsyncButton from "Element/AsyncButton";
|
import AsyncButton from "Element/AsyncButton";
|
||||||
import classNames from "classnames";
|
|
||||||
import { appendDedupe } from "SnortUtils";
|
import { appendDedupe } from "SnortUtils";
|
||||||
import useEventPublisher from "Hooks/useEventPublisher";
|
|
||||||
import { setMuted } from "Login";
|
|
||||||
import { ToggleSwitch } from "Icons/Toggle";
|
import { ToggleSwitch } from "Icons/Toggle";
|
||||||
|
import { updateAppData } from "Login";
|
||||||
|
import useLogin from "Hooks/useLogin";
|
||||||
|
|
||||||
export const FixedModeration = {
|
export const FixedModeration = {
|
||||||
hateSpeech: {
|
/*hateSpeech: {
|
||||||
title: <FormattedMessage defaultMessage="Hate Speech" />,
|
title: <FormattedMessage defaultMessage="Hate Speech" />,
|
||||||
words: [],
|
words: [],
|
||||||
canEdit: false,
|
canEdit: false,
|
||||||
@ -19,7 +19,7 @@ export const FixedModeration = {
|
|||||||
title: <FormattedMessage defaultMessage="Derogatory" />,
|
title: <FormattedMessage defaultMessage="Derogatory" />,
|
||||||
words: [],
|
words: [],
|
||||||
canEdit: false,
|
canEdit: false,
|
||||||
},
|
},*/
|
||||||
nsfw: {
|
nsfw: {
|
||||||
title: <FormattedMessage defaultMessage="NSFW" />,
|
title: <FormattedMessage defaultMessage="NSFW" />,
|
||||||
words: [
|
words: [
|
||||||
@ -43,6 +43,7 @@ export const FixedModeration = {
|
|||||||
"explicit language",
|
"explicit language",
|
||||||
"adult-only",
|
"adult-only",
|
||||||
"mature language",
|
"mature language",
|
||||||
|
"sex"
|
||||||
],
|
],
|
||||||
canEdit: false,
|
canEdit: false,
|
||||||
},
|
},
|
||||||
@ -72,18 +73,50 @@ export const FixedModeration = {
|
|||||||
"crypto wallet",
|
"crypto wallet",
|
||||||
"satoshi nakamoto",
|
"satoshi nakamoto",
|
||||||
],
|
],
|
||||||
canEdit: true,
|
canEdit: false,
|
||||||
},
|
},
|
||||||
politics: {
|
politics: {
|
||||||
title: <FormattedMessage defaultMessage="Politics" />,
|
title: <FormattedMessage defaultMessage="Politics" />,
|
||||||
words: [],
|
words: [
|
||||||
canEdit: true,
|
"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() {
|
export function Moderation() {
|
||||||
const { publisher, system } = useEventPublisher();
|
const id = useLogin(s => s.id);
|
||||||
const [topics, setTopics] = useState<Array<string>>(Object.keys(FixedModeration));
|
const [topics, setTopics] = useState<Array<string>>(Object.keys(FixedModeration));
|
||||||
|
const [extraTerms, setExtraTerms] = useState("");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -135,7 +168,7 @@ export function Moderation() {
|
|||||||
<small className="font-medium">
|
<small className="font-medium">
|
||||||
<FormattedMessage defaultMessage="Use commas to separate words e.g. word1, word2, word3" />
|
<FormattedMessage defaultMessage="Use commas to separate words e.g. word1, word2, word3" />
|
||||||
</small>
|
</small>
|
||||||
<textarea></textarea>
|
<textarea onChange={e => setExtraTerms(e.target.value)} value={extraTerms}></textarea>
|
||||||
</div>
|
</div>
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
className="primary"
|
className="primary"
|
||||||
@ -143,9 +176,18 @@ export function Moderation() {
|
|||||||
const words = Object.entries(FixedModeration)
|
const words = Object.entries(FixedModeration)
|
||||||
.filter(([k]) => topics.includes(k))
|
.filter(([k]) => topics.includes(k))
|
||||||
.map(([, v]) => v.words)
|
.map(([, v]) => v.words)
|
||||||
.flat();
|
.flat()
|
||||||
|
.concat(extraTerms.split(",").map(a => a.trim()));
|
||||||
if (words.length > 0) {
|
if (words.length > 0) {
|
||||||
// no
|
updateAppData(id, ad => {
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
...ad,
|
||||||
|
mutedWords: appendDedupe(ad.mutedWords, words)
|
||||||
|
},
|
||||||
|
timestamp: unixNowMs()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
navigate("/");
|
navigate("/");
|
||||||
}}>
|
}}>
|
||||||
|
@ -10,35 +10,248 @@ import { NostrHashtagLink } from "@snort/system";
|
|||||||
export const FixedTopics = {
|
export const FixedTopics = {
|
||||||
life: {
|
life: {
|
||||||
text: <FormattedMessage defaultMessage="Life" />,
|
text: <FormattedMessage defaultMessage="Life" />,
|
||||||
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: {
|
science: {
|
||||||
text: <FormattedMessage defaultMessage="Science" />,
|
text: <FormattedMessage defaultMessage="Science" />,
|
||||||
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: {
|
nature: {
|
||||||
text: <FormattedMessage defaultMessage="Nature" />,
|
text: <FormattedMessage defaultMessage="Nature" />,
|
||||||
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: {
|
business: {
|
||||||
text: <FormattedMessage defaultMessage="Business" />,
|
text: <FormattedMessage defaultMessage="Business" />,
|
||||||
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: {
|
game: {
|
||||||
text: <FormattedMessage defaultMessage="Game" />,
|
text: <FormattedMessage defaultMessage="Game" />,
|
||||||
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: {
|
sport: {
|
||||||
text: <FormattedMessage defaultMessage="Sport" />,
|
text: <FormattedMessage defaultMessage="Sport" />,
|
||||||
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: {
|
photography: {
|
||||||
text: <FormattedMessage defaultMessage="Photography" />,
|
text: <FormattedMessage defaultMessage="Photography" />,
|
||||||
tags: ["photography"],
|
tags: [
|
||||||
},
|
"photography",
|
||||||
bitcoin: {
|
"landscape",
|
||||||
text: <FormattedMessage defaultMessage="Bitcoin" />,
|
"portrait",
|
||||||
tags: ["bitcoin"],
|
"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"
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user