fix: build
This commit is contained in:
parent
80a4b5d8e6
commit
bfdcbca08b
37
packages/app/.eslintrc.js
Normal file
37
packages/app/.eslintrc.js
Normal file
@ -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,
|
||||||
|
},
|
||||||
|
};
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
@ -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 { Menu, MenuItem } from "@szhsin/react-menu";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
@ -90,16 +90,12 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
|||||||
await navigator.clipboard.writeText(link);
|
await navigator.clipboard.writeText(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pin(id: HexKey) {
|
async function pin(ev: NostrEvent) {
|
||||||
if (publisher) {
|
await login.state.addToList(EventKind.PinList, NostrLink.fromEvent(ev), true);
|
||||||
//todo: PIN note
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bookmark(id: string) {
|
async function bookmark(ev: NostrEvent) {
|
||||||
if (publisher) {
|
await login.state.addToList(EventKind.BookmarksList, NostrLink.fromEvent(ev), true);
|
||||||
//todo: bookmark note
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyEvent() {
|
async function copyEvent() {
|
||||||
@ -129,13 +125,13 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
|||||||
<FormattedMessage {...messages.Share} />
|
<FormattedMessage {...messages.Share} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{!login.state.isOnList(EventKind.PinList, link) && !login.readonly && (
|
{!login.state.isOnList(EventKind.PinList, link) && !login.readonly && (
|
||||||
<MenuItem onClick={() => pin(ev.id)}>
|
<MenuItem onClick={() => pin(ev)}>
|
||||||
<Icon name="pin" />
|
<Icon name="pin" />
|
||||||
<FormattedMessage {...messages.Pin} />
|
<FormattedMessage {...messages.Pin} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{!login.state.isOnList(EventKind.BookmarksList, link) && !login.readonly && (
|
{!login.state.isOnList(EventKind.BookmarksList, link) && !login.readonly && (
|
||||||
<MenuItem onClick={() => bookmark(ev.id)}>
|
<MenuItem onClick={() => bookmark(ev)}>
|
||||||
<Icon name="bookmark" />
|
<Icon name="bookmark" />
|
||||||
<FormattedMessage {...messages.Bookmark} />
|
<FormattedMessage {...messages.Bookmark} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import "./RootTabs.css";
|
import "./RootTabs.css";
|
||||||
|
|
||||||
|
import { unwrap } from "@snort/shared";
|
||||||
|
import { EventKind } from "@snort/system";
|
||||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
@ -9,8 +11,6 @@ import Icon from "@/Components/Icons/Icon";
|
|||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
import usePreferences from "@/Hooks/usePreferences";
|
import usePreferences from "@/Hooks/usePreferences";
|
||||||
import { RootTabRoutePath } from "@/Pages/Root/RootTabRoutes";
|
import { RootTabRoutePath } from "@/Pages/Root/RootTabRoutes";
|
||||||
import { EventKind } from "@snort/system";
|
|
||||||
import { unwrap } from "@snort/shared";
|
|
||||||
|
|
||||||
export function RootTabs({ base = "/" }: { base: string }) {
|
export function RootTabs({ base = "/" }: { base: string }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -255,7 +255,7 @@ class IrisAccount extends Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async declineReserved() {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const login = LoginStore.snapshot();
|
const login = LoginStore.snapshot();
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
function useHistoryState(initialValue, key) {
|
function useHistoryState<T>(initialValue: T, key: string) {
|
||||||
const currentHistoryState = history.state ? history.state[key] : undefined;
|
const currentHistoryState = globalThis.history.state ? globalThis.history.state[key] : undefined;
|
||||||
const myInitialValue = currentHistoryState === undefined ? initialValue : currentHistoryState;
|
const myInitialValue = currentHistoryState === undefined ? initialValue : currentHistoryState;
|
||||||
const [state, setState] = useState(myInitialValue);
|
const [state, setState] = useState(myInitialValue);
|
||||||
|
|
||||||
const latestValue = useRef(state);
|
const latestValue = useRef(state);
|
||||||
|
|
||||||
const setHistoryState = value => {
|
const setHistoryState = (value: T) => {
|
||||||
const newHistoryState = { ...history.state, [key]: value };
|
const newHistoryState = { ...globalThis.history.state, [key]: value };
|
||||||
history.replaceState(newHistoryState, "");
|
globalThis.history.replaceState(newHistoryState, "");
|
||||||
latestValue.current = value;
|
latestValue.current = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state !== latestValue.current) {
|
if (state !== latestValue.current) {
|
||||||
setHistoryState(state);
|
setHistoryState(state);
|
||||||
const newHistoryState = { ...history.state, [key]: state };
|
const newHistoryState = { ...globalThis.history.state, [key]: state };
|
||||||
history.replaceState(newHistoryState, "");
|
globalThis.history.replaceState(newHistoryState, "");
|
||||||
latestValue.current = state;
|
latestValue.current = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup logic
|
// Cleanup logic
|
||||||
return () => {
|
return () => {
|
||||||
if (state !== latestValue.current) {
|
if (state !== latestValue.current) {
|
||||||
const newHistoryState = { ...history.state, [key]: state };
|
const newHistoryState = { ...globalThis.history.state, [key]: state };
|
||||||
history.replaceState(newHistoryState, ""); // Save the final state
|
globalThis.history.replaceState(newHistoryState, ""); // Save the final state
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [state, key]);
|
}, [state, key]);
|
||||||
|
|
||||||
const popStateListener = event => {
|
const popStateListener = (event: PopStateEvent) => {
|
||||||
if (event.state && key in event.state) {
|
if (event.state && key in event.state) {
|
||||||
setState(event.state[key]);
|
setState(event.state[key]);
|
||||||
}
|
}
|
||||||
|
@ -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 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() {
|
export default function useModeration() {
|
||||||
const state = useLogin(s => s.state);
|
const state = useLogin(s => s.state);
|
||||||
@ -30,13 +38,33 @@ export default function useModeration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isMutedWord(word: string) {
|
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) {
|
function isEventMuted(ev: TaggedNostrEvent | NostrEvent) {
|
||||||
return isMuted(ev.pubkey) || false;
|
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 {
|
return {
|
||||||
muteList: state.muted,
|
muteList: state.muted,
|
||||||
mute,
|
mute,
|
||||||
@ -45,5 +73,8 @@ export default function useModeration() {
|
|||||||
isMuted,
|
isMuted,
|
||||||
isMutedWord,
|
isMutedWord,
|
||||||
isEventMuted,
|
isEventMuted,
|
||||||
|
addMutedWord,
|
||||||
|
removeMutedWord,
|
||||||
|
getMutedWords,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ export function ZapPoolDonateSection() {
|
|||||||
if (!CONFIG.features.zapPool) {
|
if (!CONFIG.features.zapPool) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
const zapPool = useSyncExternalStore(
|
const zapPool = useSyncExternalStore(
|
||||||
c => unwrap(ZapPoolController).hook(c),
|
c => unwrap(ZapPoolController).hook(c),
|
||||||
() => unwrap(ZapPoolController).snapshot(),
|
() => unwrap(ZapPoolController).snapshot(),
|
||||||
|
@ -9,9 +9,9 @@ import { Link, useParams } from "react-router-dom";
|
|||||||
import AsyncButton from "@/Components/Button/AsyncButton";
|
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||||
import Timeline from "@/Components/Feed/Timeline";
|
import Timeline from "@/Components/Feed/Timeline";
|
||||||
import ProfileImage from "@/Components/User/ProfileImage";
|
import ProfileImage from "@/Components/User/ProfileImage";
|
||||||
|
import { TimelineSubject } from "@/Feed/TimelineFeed";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
import { formatShort } from "@/Utils/Number";
|
import { formatShort } from "@/Utils/Number";
|
||||||
import { TimelineSubject } from "@/Feed/TimelineFeed";
|
|
||||||
|
|
||||||
const HashTagsPage = () => {
|
const HashTagsPage = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { unwrap } from "@snort/shared";
|
||||||
import { EventKind, NostrLink, NostrPrefix, parseNostrLink } from "@snort/system";
|
import { EventKind, NostrLink, NostrPrefix, parseNostrLink } from "@snort/system";
|
||||||
import { useEventFeed } from "@snort/system-react";
|
import { useEventFeed } from "@snort/system-react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
@ -13,7 +14,6 @@ import useLogin from "@/Hooks/useLogin";
|
|||||||
import { LogoHeader } from "@/Pages/Layout/LogoHeader";
|
import { LogoHeader } from "@/Pages/Layout/LogoHeader";
|
||||||
import NotificationsHeader from "@/Pages/Layout/NotificationsHeader";
|
import NotificationsHeader from "@/Pages/Layout/NotificationsHeader";
|
||||||
import { bech32ToHex } from "@/Utils";
|
import { bech32ToHex } from "@/Utils";
|
||||||
import { unwrap } from "@snort/shared";
|
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -9,7 +9,7 @@ import useLogin from "@/Hooks/useLogin";
|
|||||||
export default function RightColumn() {
|
export default function RightColumn() {
|
||||||
const { pubkey } = useLogin(s => ({ pubkey: s.publicKey }));
|
const { pubkey } = useLogin(s => ({ pubkey: s.publicKey }));
|
||||||
const hideRightColumnPaths = ["/login", "/new", "/messages"];
|
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 = () => {
|
const getTitleMessage = () => {
|
||||||
return pubkey ? (
|
return pubkey ? (
|
||||||
|
@ -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) {
|
export function getNotificationContext(ev: TaggedNostrEvent) {
|
||||||
switch (ev.kind) {
|
switch (ev.kind) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
import { unwrap } from "@snort/shared";
|
||||||
|
import { EventKind } from "@snort/system";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
import Timeline from "@/Components/Feed/Timeline";
|
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 { TimelineSubject } from "@/Feed/TimelineFeed";
|
||||||
|
import useLogin from "@/Hooks/useLogin";
|
||||||
|
|
||||||
export function TopicsPage() {
|
export function TopicsPage() {
|
||||||
const { tags, pubKey } = useLogin(s => ({
|
const { tags, pubKey } = useLogin(s => ({
|
||||||
|
@ -1,39 +1,15 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useModeration from "@/Hooks/useModeration";
|
||||||
import { appendDedupe } from "@/Utils";
|
|
||||||
import { SnortAppData, updateAppData } from "@/Utils/Login";
|
|
||||||
import { useAllPreferences } from "@/Hooks/usePreferences";
|
import { useAllPreferences } from "@/Hooks/usePreferences";
|
||||||
|
|
||||||
export default function ModerationSettingsPage() {
|
export default function ModerationSettingsPage() {
|
||||||
const state = useAllPreferences();
|
const { addMutedWord, removeMutedWord, getMutedWords } = useModeration();
|
||||||
|
const preferences = useAllPreferences();
|
||||||
const [muteWord, setMuteWord] = useState("");
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2>
|
<h2>
|
||||||
@ -44,8 +20,13 @@ export default function ModerationSettingsPage() {
|
|||||||
<div className="flex items-center mb-2">
|
<div className="flex items-center mb-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={appData.showContentWarningPosts}
|
checked={preferences.preferences.showContentWarningPosts}
|
||||||
onChange={() => handleToggle("showContentWarningPosts")}
|
onChange={() =>
|
||||||
|
preferences.update({
|
||||||
|
...preferences.preferences,
|
||||||
|
showContentWarningPosts: !preferences.preferences.showContentWarningPosts,
|
||||||
|
})
|
||||||
|
}
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
id="showContentWarningPosts"
|
id="showContentWarningPosts"
|
||||||
/>
|
/>
|
||||||
@ -67,11 +48,11 @@ export default function ModerationSettingsPage() {
|
|||||||
value={muteWord}
|
value={muteWord}
|
||||||
onChange={e => setMuteWord(e.target.value.toLowerCase())}
|
onChange={e => setMuteWord(e.target.value.toLowerCase())}
|
||||||
/>
|
/>
|
||||||
<button type="button" onClick={addMutedWord}>
|
<AsyncButton type="button" onClick={() => addMutedWord(muteWord)}>
|
||||||
<FormattedMessage defaultMessage="Add" id="2/2yg+" />
|
<FormattedMessage defaultMessage="Add" id="2/2yg+" />
|
||||||
</button>
|
</AsyncButton>
|
||||||
</div>
|
</div>
|
||||||
{appData.mutedWords.map(v => (
|
{getMutedWords().map(v => (
|
||||||
<div key={v} className="p br b flex items-center justify-between">
|
<div key={v} className="p br b flex items-center justify-between">
|
||||||
<div>{v}</div>
|
<div>{v}</div>
|
||||||
<button type="button" onClick={() => removeMutedWord(v)}>
|
<button type="button" onClick={() => removeMutedWord(v)}>
|
||||||
|
@ -100,7 +100,7 @@ export async function subscribeToNotifications(publisher: EventPublisher) {
|
|||||||
endpoint: sub.endpoint,
|
endpoint: sub.endpoint,
|
||||||
p256dh: base64.encode(new Uint8Array(unwrap(sub.getKey("p256dh")))),
|
p256dh: base64.encode(new Uint8Array(unwrap(sub.getKey("p256dh")))),
|
||||||
auth: base64.encode(new Uint8Array(unwrap(sub.getKey("auth")))),
|
auth: base64.encode(new Uint8Array(unwrap(sub.getKey("auth")))),
|
||||||
scope: `${location.protocol}//${location.hostname}`,
|
scope: `${globalThis.location.protocol}//${globalThis.location.hostname}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,12 +134,12 @@ class ZapPool extends ExternalStore<Array<ZapPoolRecipient>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#save() {
|
#save() {
|
||||||
self.localStorage.setItem("zap-pool", JSON.stringify(this.takeSnapshot()));
|
globalThis.localStorage.setItem("zap-pool", JSON.stringify(this.takeSnapshot()));
|
||||||
self.localStorage.setItem("zap-pool-last-payout", this.#lastPayout.toString());
|
globalThis.localStorage.setItem("zap-pool-last-payout", this.#lastPayout.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
#load() {
|
#load() {
|
||||||
const existing = self.localStorage.getItem("zap-pool");
|
const existing = globalThis.localStorage.getItem("zap-pool");
|
||||||
if (existing) {
|
if (existing) {
|
||||||
const arr = JSON.parse(existing) as Array<ZapPoolRecipient>;
|
const arr = JSON.parse(existing) as Array<ZapPoolRecipient>;
|
||||||
this.#store = new Map(arr.map(a => [`${a.pubkey}-${a.type}`, a]));
|
this.#store = new Map(arr.map(a => [`${a.pubkey}-${a.type}`, a]));
|
||||||
@ -157,7 +157,7 @@ class ZapPool extends ExternalStore<Array<ZapPoolRecipient>> {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastPayout = self.localStorage.getItem("zap-pool-last-payout");
|
const lastPayout = globalThis.localStorage.getItem("zap-pool-last-payout");
|
||||||
if (lastPayout) {
|
if (lastPayout) {
|
||||||
this.#lastPayout = Number(lastPayout);
|
this.#lastPayout = Number(lastPayout);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import { storeRefCode, unwrap } from "@/Utils";
|
|||||||
import { hasWasm, wasmInit, WasmPath } from "@/Utils/wasm";
|
import { hasWasm, wasmInit, WasmPath } from "@/Utils/wasm";
|
||||||
import { Wallets } from "@/Wallet";
|
import { Wallets } from "@/Wallet";
|
||||||
import { setupWebLNWalletConfig } from "@/Wallet";
|
import { setupWebLNWalletConfig } from "@/Wallet";
|
||||||
|
|
||||||
import { LoginStore } from "./Utils/Login";
|
import { LoginStore } from "./Utils/Login";
|
||||||
|
|
||||||
async function initSite() {
|
async function initSite() {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/// <reference lib="webworker" />
|
/// <reference lib="webworker" />
|
||||||
import { CacheableResponsePlugin } from "workbox-cacheable-response";
|
|
||||||
|
|
||||||
import { encodeTLVEntries, NostrLink, NostrPrefix, TLVEntryType, tryParseNostrLink } from "@snort/system";
|
import { encodeTLVEntries, NostrLink, NostrPrefix, TLVEntryType, tryParseNostrLink } from "@snort/system";
|
||||||
|
import { CacheableResponsePlugin } from "workbox-cacheable-response";
|
||||||
import { clientsClaim } from "workbox-core";
|
import { clientsClaim } from "workbox-core";
|
||||||
import { ExpirationPlugin } from "workbox-expiration";
|
import { ExpirationPlugin } from "workbox-expiration";
|
||||||
import { precacheAndRoute, PrecacheEntry } from "workbox-precaching";
|
import { precacheAndRoute, PrecacheEntry } from "workbox-precaching";
|
||||||
@ -181,7 +180,7 @@ self.addEventListener("notificationclick", event => {
|
|||||||
if (mention.event) {
|
if (mention.event) {
|
||||||
return `/${new NostrLink(NostrPrefix.Note, mention.event).encode()}`;
|
return `/${new NostrLink(NostrPrefix.Note, mention.event).encode()}`;
|
||||||
}
|
}
|
||||||
} else if (ev.type == PushType.DirectMessage) {
|
} else if (ev.type === PushType.DirectMessage) {
|
||||||
const reaction = ev.data as CompactReaction;
|
const reaction = ev.data as CompactReaction;
|
||||||
return `/messages/${encodeTLVEntries("chat4" as NostrPrefix, {
|
return `/messages/${encodeTLVEntries("chat4" as NostrPrefix, {
|
||||||
type: TLVEntryType.Author,
|
type: TLVEntryType.Author,
|
||||||
|
@ -24,6 +24,13 @@ export class NostrHashtagLink implements ToNostrEventTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class UnknownTag implements ToNostrEventTag {
|
||||||
|
constructor(readonly value: Array<string>) {}
|
||||||
|
toEventTag(): string[] | undefined {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class NostrLink implements ToNostrEventTag {
|
export class NostrLink implements ToNostrEventTag {
|
||||||
constructor(
|
constructor(
|
||||||
readonly type: NostrPrefix,
|
readonly type: NostrPrefix,
|
||||||
@ -188,7 +195,7 @@ export class NostrLink implements ToNostrEventTag {
|
|||||||
tag: Array<string>,
|
tag: Array<string>,
|
||||||
author?: string,
|
author?: string,
|
||||||
kind?: number,
|
kind?: number,
|
||||||
fnOther?: (tag: Array<string>) => T,
|
fnOther?: (tag: Array<string>) => T | undefined,
|
||||||
) {
|
) {
|
||||||
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
||||||
switch (tag[0]) {
|
switch (tag[0]) {
|
||||||
@ -203,15 +210,12 @@ export class NostrLink implements ToNostrEventTag {
|
|||||||
return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays, tag[3]);
|
return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays, tag[3]);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
if (fnOther) {
|
return fnOther?.(tag) ?? new UnknownTag(tag);
|
||||||
return fnOther(tag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error(`Unknown tag kind ${tag[0]}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromTags<T = NostrLink>(tags: ReadonlyArray<Array<string>>, fnOther?: (tag: Array<string>) => T) {
|
static fromTags<T = NostrLink>(tags: ReadonlyArray<Array<string>>, fnOther?: (tag: Array<string>) => T | undefined) {
|
||||||
return removeUndefined(
|
return removeUndefined(
|
||||||
tags.map(a => {
|
tags.map(a => {
|
||||||
try {
|
try {
|
||||||
|
@ -396,9 +396,12 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getList(kind: EventKind): Array<ToNostrEventTag> {
|
getList<T extends ToNostrEventTag>(
|
||||||
|
kind: EventKind,
|
||||||
|
fnOther?: (tag: Array<string>) => T | undefined,
|
||||||
|
): Array<ToNostrEventTag> {
|
||||||
const list = this.#standardLists.get(kind);
|
const list = this.#standardLists.get(kind);
|
||||||
return NostrLink.fromTags(list?.tags ?? []);
|
return NostrLink.fromTags<T>(list?.tags ?? [], fnOther);
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): UserStateObject<TAppData> {
|
serialize(): UserStateObject<TAppData> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user