From bfdcbca08bf0188f694cf4b51a20766510151eb0 Mon Sep 17 00:00:00 2001 From: kieran Date: Mon, 22 Apr 2024 15:24:25 +0100 Subject: [PATCH] fix: build --- packages/app/.eslintrc.js | 37 +++++++++++++++ packages/app/eslint.config.mjs | 44 ----------------- .../Components/Event/Note/NoteContextMenu.tsx | 18 +++---- packages/app/src/Components/Feed/RootTabs.tsx | 4 +- .../Components/IrisAccount/IrisAccount.tsx | 2 +- packages/app/src/Hooks/useHistoryState.tsx | 20 ++++---- packages/app/src/Hooks/useModeration.tsx | 37 +++++++++++++-- .../src/Pages/Donate/ZapPoolDonateSection.tsx | 1 - packages/app/src/Pages/HashTagsPage.tsx | 2 +- packages/app/src/Pages/Layout/Header.tsx | 2 +- packages/app/src/Pages/Layout/RightColumn.tsx | 2 +- .../Notifications/getNotificationContext.tsx | 2 +- packages/app/src/Pages/TopicsPage.tsx | 6 +-- .../app/src/Pages/settings/Moderation.tsx | 47 ++++++------------- packages/app/src/Utils/Notifications.ts | 2 +- packages/app/src/Utils/ZapPoolController.ts | 8 ++-- packages/app/src/index.tsx | 1 + packages/app/src/service-worker.ts | 5 +- packages/system/src/nostr-link.ts | 16 ++++--- packages/system/src/user-state.ts | 7 ++- 20 files changed, 135 insertions(+), 128 deletions(-) create mode 100644 packages/app/.eslintrc.js delete mode 100644 packages/app/eslint.config.mjs diff --git a/packages/app/.eslintrc.js b/packages/app/.eslintrc.js new file mode 100644 index 00000000..f17f45cc --- /dev/null +++ b/packages/app/.eslintrc.js @@ -0,0 +1,37 @@ +/* eslint-disable import/no-anonymous-default-export */ +module.exports = { + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended"], + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint", "formatjs", "react-refresh", "simple-import-sort"], + rules: { + "formatjs/enforce-id": [ + "error", + { + idInterpolationPattern: "[sha512:contenthash:base64:6]", + }, + ], + "react/react-in-jsx-scope": "off", + "react-hooks/exhaustive-deps": "off", + "react-refresh/only-export-components": "error", + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + "@typescript-eslint/no-unused-vars": "error", + "max-lines": ["warn", { max: 300, skipBlankLines: true, skipComments: true }], + }, + overrides: [ + { + files: ["*.tsx"], + rules: { + "max-lines": ["warn", { max: 200, skipBlankLines: true, skipComments: true }], + }, + }, + ], + root: true, + ignorePatterns: ["build/", "*.test.ts", "*.js"], + env: { + browser: true, + worker: true, + commonjs: true, + node: false, + }, +}; diff --git a/packages/app/eslint.config.mjs b/packages/app/eslint.config.mjs deleted file mode 100644 index 23fa161c..00000000 --- a/packages/app/eslint.config.mjs +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-disable import/no-anonymous-default-export */ -export default [ - { - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - ], - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "formatjs", "react-refresh", "simple-import-sort"], - rules: { - "formatjs/enforce-id": [ - "error", - { - idInterpolationPattern: "[sha512:contenthash:base64:6]", - }, - ], - "react/react-in-jsx-scope": "off", - "react-hooks/exhaustive-deps": "off", - "react-refresh/only-export-components": "error", - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "@typescript-eslint/no-unused-vars": "error", - "max-lines": ["warn", { max: 300, skipBlankLines: true, skipComments: true }], - }, - overrides: [ - { - files: ["*.tsx"], - rules: { - "max-lines": ["warn", { max: 200, skipBlankLines: true, skipComments: true }], - }, - }, - ], - root: true, - ignores: ["build/", "*.test.ts", "*.js"], - env: { - browser: true, - worker: true, - commonjs: true, - node: false, - }, - }, -]; diff --git a/packages/app/src/Components/Event/Note/NoteContextMenu.tsx b/packages/app/src/Components/Event/Note/NoteContextMenu.tsx index fb773628..0fd96d17 100644 --- a/packages/app/src/Components/Event/Note/NoteContextMenu.tsx +++ b/packages/app/src/Components/Event/Note/NoteContextMenu.tsx @@ -1,4 +1,4 @@ -import { EventKind, HexKey, NostrLink, NostrPrefix } from "@snort/system"; +import { EventKind, NostrEvent, NostrLink } from "@snort/system"; import { Menu, MenuItem } from "@szhsin/react-menu"; import { useEffect, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -90,16 +90,12 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) { await navigator.clipboard.writeText(link); } - async function pin(id: HexKey) { - if (publisher) { - //todo: PIN note - } + async function pin(ev: NostrEvent) { + await login.state.addToList(EventKind.PinList, NostrLink.fromEvent(ev), true); } - async function bookmark(id: string) { - if (publisher) { - //todo: bookmark note - } + async function bookmark(ev: NostrEvent) { + await login.state.addToList(EventKind.BookmarksList, NostrLink.fromEvent(ev), true); } async function copyEvent() { @@ -129,13 +125,13 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) { {!login.state.isOnList(EventKind.PinList, link) && !login.readonly && ( - pin(ev.id)}> + pin(ev)}> )} {!login.state.isOnList(EventKind.BookmarksList, link) && !login.readonly && ( - bookmark(ev.id)}> + bookmark(ev)}> diff --git a/packages/app/src/Components/Feed/RootTabs.tsx b/packages/app/src/Components/Feed/RootTabs.tsx index 02301cc3..96b27e95 100644 --- a/packages/app/src/Components/Feed/RootTabs.tsx +++ b/packages/app/src/Components/Feed/RootTabs.tsx @@ -1,5 +1,7 @@ import "./RootTabs.css"; +import { unwrap } from "@snort/shared"; +import { EventKind } from "@snort/system"; import { Menu, MenuItem } from "@szhsin/react-menu"; import { useEffect, useMemo, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; @@ -9,8 +11,6 @@ import Icon from "@/Components/Icons/Icon"; import useLogin from "@/Hooks/useLogin"; import usePreferences from "@/Hooks/usePreferences"; import { RootTabRoutePath } from "@/Pages/Root/RootTabRoutes"; -import { EventKind } from "@snort/system"; -import { unwrap } from "@snort/shared"; export function RootTabs({ base = "/" }: { base: string }) { const navigate = useNavigate(); diff --git a/packages/app/src/Components/IrisAccount/IrisAccount.tsx b/packages/app/src/Components/IrisAccount/IrisAccount.tsx index b4c2baf7..48234b63 100644 --- a/packages/app/src/Components/IrisAccount/IrisAccount.tsx +++ b/packages/app/src/Components/IrisAccount/IrisAccount.tsx @@ -255,7 +255,7 @@ class IrisAccount extends Component { } async declineReserved() { - if (!confirm(`Are you sure you want to decline iris.to/${name}?`)) { + if (!window.confirm(`Are you sure you want to decline iris.to/${this.state.newUserName}?`)) { return; } const login = LoginStore.snapshot(); diff --git a/packages/app/src/Hooks/useHistoryState.tsx b/packages/app/src/Hooks/useHistoryState.tsx index 9085ec08..e2b70672 100644 --- a/packages/app/src/Hooks/useHistoryState.tsx +++ b/packages/app/src/Hooks/useHistoryState.tsx @@ -1,36 +1,36 @@ import { useEffect, useRef, useState } from "react"; -function useHistoryState(initialValue, key) { - const currentHistoryState = history.state ? history.state[key] : undefined; +function useHistoryState(initialValue: T, key: string) { + const currentHistoryState = globalThis.history.state ? globalThis.history.state[key] : undefined; const myInitialValue = currentHistoryState === undefined ? initialValue : currentHistoryState; const [state, setState] = useState(myInitialValue); const latestValue = useRef(state); - const setHistoryState = value => { - const newHistoryState = { ...history.state, [key]: value }; - history.replaceState(newHistoryState, ""); + const setHistoryState = (value: T) => { + const newHistoryState = { ...globalThis.history.state, [key]: value }; + globalThis.history.replaceState(newHistoryState, ""); latestValue.current = value; }; useEffect(() => { if (state !== latestValue.current) { setHistoryState(state); - const newHistoryState = { ...history.state, [key]: state }; - history.replaceState(newHistoryState, ""); + const newHistoryState = { ...globalThis.history.state, [key]: state }; + globalThis.history.replaceState(newHistoryState, ""); latestValue.current = state; } // Cleanup logic return () => { if (state !== latestValue.current) { - const newHistoryState = { ...history.state, [key]: state }; - history.replaceState(newHistoryState, ""); // Save the final state + const newHistoryState = { ...globalThis.history.state, [key]: state }; + globalThis.history.replaceState(newHistoryState, ""); // Save the final state } }; }, [state, key]); - const popStateListener = event => { + const popStateListener = (event: PopStateEvent) => { if (event.state && key in event.state) { setState(event.state[key]); } diff --git a/packages/app/src/Hooks/useModeration.tsx b/packages/app/src/Hooks/useModeration.tsx index f0b845f2..c8e0c185 100644 --- a/packages/app/src/Hooks/useModeration.tsx +++ b/packages/app/src/Hooks/useModeration.tsx @@ -1,7 +1,15 @@ -import { EventKind, NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system"; +import { dedupe } from "@snort/shared"; +import { EventKind, NostrEvent, NostrLink, TaggedNostrEvent, ToNostrEventTag } from "@snort/system"; import useLogin from "@/Hooks/useLogin"; -import { dedupe } from "@snort/shared"; + +export class MutedWordTag implements ToNostrEventTag { + constructor(readonly word: string) {} + + toEventTag(): string[] | undefined { + return ["word", this.word.toLowerCase()]; + } +} export default function useModeration() { const state = useLogin(s => s.state); @@ -30,13 +38,33 @@ export default function useModeration() { } function isMutedWord(word: string) { - return false; + const words = getMutedWords(); + return words.includes(word); + } + + async function addMutedWord(word: string) { + await state.addToList(EventKind.MuteList, new MutedWordTag(word.toLowerCase())); + } + + async function removeMutedWord(word: string) { + await state.removeFromList(EventKind.MuteList, new MutedWordTag(word.toLowerCase())); } function isEventMuted(ev: TaggedNostrEvent | NostrEvent) { return isMuted(ev.pubkey) || false; } + function getMutedWords() { + return state + .getList(EventKind.MuteList, o => { + if (o[0] === "word") { + return new MutedWordTag(o[1]); + } + }) + .filter(a => a instanceof MutedWordTag) + .map(a => (a as MutedWordTag).word); + } + return { muteList: state.muted, mute, @@ -45,5 +73,8 @@ export default function useModeration() { isMuted, isMutedWord, isEventMuted, + addMutedWord, + removeMutedWord, + getMutedWords, }; } diff --git a/packages/app/src/Pages/Donate/ZapPoolDonateSection.tsx b/packages/app/src/Pages/Donate/ZapPoolDonateSection.tsx index 8cea6f5f..4573024a 100644 --- a/packages/app/src/Pages/Donate/ZapPoolDonateSection.tsx +++ b/packages/app/src/Pages/Donate/ZapPoolDonateSection.tsx @@ -11,7 +11,6 @@ export function ZapPoolDonateSection() { if (!CONFIG.features.zapPool) { return; } - // eslint-disable-next-line react-hooks/rules-of-hooks const zapPool = useSyncExternalStore( c => unwrap(ZapPoolController).hook(c), () => unwrap(ZapPoolController).snapshot(), diff --git a/packages/app/src/Pages/HashTagsPage.tsx b/packages/app/src/Pages/HashTagsPage.tsx index 1d8942a9..a781889e 100644 --- a/packages/app/src/Pages/HashTagsPage.tsx +++ b/packages/app/src/Pages/HashTagsPage.tsx @@ -9,9 +9,9 @@ import { Link, useParams } from "react-router-dom"; import AsyncButton from "@/Components/Button/AsyncButton"; import Timeline from "@/Components/Feed/Timeline"; import ProfileImage from "@/Components/User/ProfileImage"; +import { TimelineSubject } from "@/Feed/TimelineFeed"; import useLogin from "@/Hooks/useLogin"; import { formatShort } from "@/Utils/Number"; -import { TimelineSubject } from "@/Feed/TimelineFeed"; const HashTagsPage = () => { const params = useParams(); diff --git a/packages/app/src/Pages/Layout/Header.tsx b/packages/app/src/Pages/Layout/Header.tsx index fbf4a094..006cfbc4 100644 --- a/packages/app/src/Pages/Layout/Header.tsx +++ b/packages/app/src/Pages/Layout/Header.tsx @@ -1,3 +1,4 @@ +import { unwrap } from "@snort/shared"; import { EventKind, NostrLink, NostrPrefix, parseNostrLink } from "@snort/system"; import { useEventFeed } from "@snort/system-react"; import classNames from "classnames"; @@ -13,7 +14,6 @@ import useLogin from "@/Hooks/useLogin"; import { LogoHeader } from "@/Pages/Layout/LogoHeader"; import NotificationsHeader from "@/Pages/Layout/NotificationsHeader"; import { bech32ToHex } from "@/Utils"; -import { unwrap } from "@snort/shared"; export function Header() { const navigate = useNavigate(); diff --git a/packages/app/src/Pages/Layout/RightColumn.tsx b/packages/app/src/Pages/Layout/RightColumn.tsx index d4f48844..3c843b54 100644 --- a/packages/app/src/Pages/Layout/RightColumn.tsx +++ b/packages/app/src/Pages/Layout/RightColumn.tsx @@ -9,7 +9,7 @@ import useLogin from "@/Hooks/useLogin"; export default function RightColumn() { const { pubkey } = useLogin(s => ({ pubkey: s.publicKey })); const hideRightColumnPaths = ["/login", "/new", "/messages"]; - const show = !hideRightColumnPaths.some(path => location.pathname.startsWith(path)); + const show = !hideRightColumnPaths.some(path => globalThis.location.pathname.startsWith(path)); const getTitleMessage = () => { return pubkey ? ( diff --git a/packages/app/src/Pages/Notifications/getNotificationContext.tsx b/packages/app/src/Pages/Notifications/getNotificationContext.tsx index f93497fe..8b459ffc 100644 --- a/packages/app/src/Pages/Notifications/getNotificationContext.tsx +++ b/packages/app/src/Pages/Notifications/getNotificationContext.tsx @@ -1,4 +1,4 @@ -import { EventKind, NostrLink, TaggedNostrEvent, Nip10 } from "@snort/system"; +import { EventKind, Nip10, NostrLink, TaggedNostrEvent } from "@snort/system"; export function getNotificationContext(ev: TaggedNostrEvent) { switch (ev.kind) { diff --git a/packages/app/src/Pages/TopicsPage.tsx b/packages/app/src/Pages/TopicsPage.tsx index c8db1a0e..5b78ca5a 100644 --- a/packages/app/src/Pages/TopicsPage.tsx +++ b/packages/app/src/Pages/TopicsPage.tsx @@ -1,10 +1,10 @@ +import { unwrap } from "@snort/shared"; +import { EventKind } from "@snort/system"; import { useMemo } from "react"; import Timeline from "@/Components/Feed/Timeline"; -import useLogin from "@/Hooks/useLogin"; -import { EventKind } from "@snort/system"; -import { unwrap } from "@snort/shared"; import { TimelineSubject } from "@/Feed/TimelineFeed"; +import useLogin from "@/Hooks/useLogin"; export function TopicsPage() { const { tags, pubKey } = useLogin(s => ({ diff --git a/packages/app/src/Pages/settings/Moderation.tsx b/packages/app/src/Pages/settings/Moderation.tsx index a30b1c64..f91a8340 100644 --- a/packages/app/src/Pages/settings/Moderation.tsx +++ b/packages/app/src/Pages/settings/Moderation.tsx @@ -1,39 +1,15 @@ import { useState } from "react"; import { FormattedMessage } from "react-intl"; -import useEventPublisher from "@/Hooks/useEventPublisher"; -import useLogin from "@/Hooks/useLogin"; -import { appendDedupe } from "@/Utils"; -import { SnortAppData, updateAppData } from "@/Utils/Login"; +import AsyncButton from "@/Components/Button/AsyncButton"; +import useModeration from "@/Hooks/useModeration"; import { useAllPreferences } from "@/Hooks/usePreferences"; export default function ModerationSettingsPage() { - const state = useAllPreferences(); + const { addMutedWord, removeMutedWord, getMutedWords } = useModeration(); + const preferences = useAllPreferences(); const [muteWord, setMuteWord] = useState(""); - function addMutedWord() { - updateAppData(login.id, ad => ({ - ...ad, - mutedWords: appendDedupe(appData.mutedWords, [muteWord]), - })); - setMuteWord(""); - } - - const handleToggle = (setting: keyof SnortAppData) => { - updateAppData(login.id, ad => ({ - ...ad, - [setting]: !appData[setting], - })); - }; - - function removeMutedWord(word: string) { - updateAppData(login.id, ad => ({ - ...ad, - mutedWords: appData.mutedWords.filter(a => a !== word), - })); - setMuteWord(""); - } - return ( <>

@@ -44,8 +20,13 @@ export default function ModerationSettingsPage() {
handleToggle("showContentWarningPosts")} + checked={preferences.preferences.showContentWarningPosts} + onChange={() => + preferences.update({ + ...preferences.preferences, + showContentWarningPosts: !preferences.preferences.showContentWarningPosts, + }) + } className="mr-2" id="showContentWarningPosts" /> @@ -67,11 +48,11 @@ export default function ModerationSettingsPage() { value={muteWord} onChange={e => setMuteWord(e.target.value.toLowerCase())} /> - +
- {appData.mutedWords.map(v => ( + {getMutedWords().map(v => (
{v}