From e15a46192a73e4790421699f2a2316c14ca81270 Mon Sep 17 00:00:00 2001 From: verbiricha Date: Tue, 1 Aug 2023 12:43:26 +0200 Subject: [PATCH] fix: type errors --- src/element/badge.tsx | 2 +- src/element/chat-message.tsx | 21 +++-- src/element/emoji-pack.tsx | 28 ++++--- src/element/emoji-picker.tsx | 113 +++++++++++++------------ src/element/external-link.tsx | 32 +++++-- src/element/file-uploader.tsx | 19 ++++- src/element/follow-button.tsx | 18 ++-- src/element/hypertext.tsx | 6 +- src/element/live-chat.tsx | 2 +- src/element/markdown.tsx | 44 ++++++---- src/element/mute-button.tsx | 16 ++-- src/element/stream-cards.tsx | 144 +++++++++++++++++++------------- src/element/text.tsx | 4 +- src/element/textarea.tsx | 7 +- src/element/toggle.tsx | 3 + src/element/write-message.tsx | 20 ++--- src/hooks/badges.ts | 17 +++- src/hooks/cards.ts | 20 +++-- src/hooks/emoji.tsx | 8 +- src/hooks/live-streams.ts | 2 +- src/hooks/login.ts | 3 +- src/login.ts | 12 ++- src/pages/tag.tsx | 2 +- src/providers/index.ts | 152 +++++++++++++++++----------------- src/providers/owncast.ts | 146 ++++++++++++++++---------------- src/types.ts | 8 +- src/utils.ts | 11 ++- 27 files changed, 480 insertions(+), 380 deletions(-) diff --git a/src/element/badge.tsx b/src/element/badge.tsx index fb6865f..05db3c4 100644 --- a/src/element/badge.tsx +++ b/src/element/badge.tsx @@ -4,7 +4,7 @@ import { findTag } from "utils"; export function Badge({ ev }: { ev: NostrEvent }) { const name = findTag(ev, "name") || findTag(ev, "d"); - const description = findTag(ev, "description"); + const description = findTag(ev, "description") ?? ""; const thumb = findTag(ev, "thumb"); const image = findTag(ev, "image"); return ( diff --git a/src/element/chat-message.tsx b/src/element/chat-message.tsx index 5cf0574..5550336 100644 --- a/src/element/chat-message.tsx +++ b/src/element/chat-message.tsx @@ -8,18 +8,17 @@ import { useIntersectionObserver, } from "usehooks-ts"; -import { System } from "../index"; -import { formatSats } from "../number"; -import { EmojiPicker } from "./emoji-picker"; -import { Icon } from "./icon"; -import { Emoji as EmojiComponent } from "./emoji"; +import { EmojiPicker } from "element/emoji-picker"; +import { Icon } from "element/icon"; +import { Emoji as EmojiComponent } from "element/emoji"; import { Profile } from "./profile"; import { Text } from "element/text"; -import { SendZapsDialog } from "./send-zap"; -import { findTag } from "../utils"; -import type { EmojiPack } from "../hooks/emoji"; -import { useLogin } from "../hooks/login"; -import type { Badge, Emoji } from "types"; +import { SendZapsDialog } from "element/send-zap"; +import { useLogin } from "hooks/login"; +import { formatSats } from "number"; +import { findTag } from "utils"; +import type { Badge, Emoji, EmojiPack } from "types"; +import { System } from "index"; function emojifyReaction(reaction: string) { if (reaction === "+") { @@ -104,7 +103,7 @@ export function ChatMessage({ if (emoji.native) { reply = await pub?.react(ev, emoji.native || "+1"); } else { - const e = getEmojiById(emoji.id); + const e = getEmojiById(emoji.id!); if (e) { reply = await pub?.generic((eb) => { return eb diff --git a/src/element/emoji-pack.tsx b/src/element/emoji-pack.tsx index 532156e..b6dbca1 100644 --- a/src/element/emoji-pack.tsx +++ b/src/element/emoji-pack.tsx @@ -8,6 +8,7 @@ import { Mention } from "element/mention"; import { findTag } from "utils"; import { USER_EMOJIS } from "const"; import { Login, System } from "index"; +import type { EmojiPack as EmojiPackType } from "types"; export function EmojiPack({ ev }: { ev: NostrEvent }) { const login = useLogin(); @@ -18,13 +19,14 @@ export function EmojiPack({ ev }: { ev: NostrEvent }) { const emoji = ev.tags.filter((e) => e.at(0) === "emoji"); async function toggleEmojiPack() { - let newPacks = []; + let newPacks = [] as EmojiPackType[]; if (isUsed) { - newPacks = login.emojis.filter( - (e) => e.pubkey !== ev.pubkey && e.name !== name, - ); + newPacks = + login?.emojis.filter( + (e) => e.author !== ev.pubkey && e.name !== name, + ) ?? []; } else { - newPacks = [...login.emojis, toEmojiPack(ev)]; + newPacks = [...(login?.emojis ?? []), toEmojiPack(ev)]; } const pub = login?.publisher(); if (pub) { @@ -37,7 +39,7 @@ export function EmojiPack({ ev }: { ev: NostrEvent }) { }); console.debug(ev); System.BroadcastEvent(ev); - Login.setEmojis(newPacks, ev.created_at); + Login.setEmojis(newPacks); } } @@ -48,12 +50,14 @@ export function EmojiPack({ ev }: { ev: NostrEvent }) {

{name}

- - {isUsed ? "Remove" : "Add"} - + {login?.pubkey && ( + + {isUsed ? "Remove" : "Add"} + + )}
{emoji.map((e) => { diff --git a/src/element/emoji-picker.tsx b/src/element/emoji-picker.tsx index b27857c..cb0ac08 100644 --- a/src/element/emoji-picker.tsx +++ b/src/element/emoji-picker.tsx @@ -1,71 +1,70 @@ import data, { Emoji } from "@emoji-mart/data"; import Picker from "@emoji-mart/react"; import { RefObject } from "react"; - -import { EmojiPack } from "../hooks/emoji"; +import { EmojiPack } from "types"; interface EmojiPickerProps { - topOffset: number; - leftOffset: number; - emojiPacks?: EmojiPack[]; - onEmojiSelect: (e: Emoji) => void; - onClickOutside: () => void; - height?: number; - ref: RefObject; + topOffset: number; + leftOffset: number; + emojiPacks?: EmojiPack[]; + onEmojiSelect: (e: Emoji) => void; + onClickOutside: () => void; + height?: number; + ref: RefObject; } export function EmojiPicker({ - topOffset, - leftOffset, - onEmojiSelect, - onClickOutside, - emojiPacks = [], - height = 300, - ref, + topOffset, + leftOffset, + onEmojiSelect, + onClickOutside, + emojiPacks = [], + height = 300, + ref, }: EmojiPickerProps) { - const customEmojiList = emojiPacks.map((pack) => { + const customEmojiList = emojiPacks.map((pack) => { + return { + id: pack.address, + name: pack.name, + emojis: pack.emojis.map((e) => { + const [, name, url] = e; return { - id: pack.address, - name: pack.name, - emojis: pack.emojis.map((e) => { - const [, name, url] = e; - return { - id: name, - name, - skins: [{ src: url }], - }; - }), + id: name, + name, + skins: [{ src: url }], }; - }); - return ( - <> -
- - -
- - ); + + +
+ + ); } diff --git a/src/element/external-link.tsx b/src/element/external-link.tsx index 179c471..51c02bc 100644 --- a/src/element/external-link.tsx +++ b/src/element/external-link.tsx @@ -1,6 +1,28 @@ +import type { ReactNode } from "react"; import { Icon } from "element/icon"; -export function ExternalIconLink({ size = 32, href, ...rest }) { +interface ExternalLinkProps { + href: string; + children: ReactNode; +} + +export function ExternalLink({ children, href }: ExternalLinkProps) { + return ( + + {children} + + ); +} + +interface ExternalIconLinkProps extends Omit { + size?: number; +} + +export function ExternalIconLink({ + size = 32, + href, + ...rest +}: ExternalIconLinkProps) { return ( ); } - -export function ExternalLink({ children, href }) { - return ( - - {children} - - ); -} diff --git a/src/element/file-uploader.tsx b/src/element/file-uploader.tsx index 5226e86..03be8b5 100644 --- a/src/element/file-uploader.tsx +++ b/src/element/file-uploader.tsx @@ -1,4 +1,5 @@ import "./file-uploader.css"; +import type { ChangeEvent } from "react"; import { VoidApi } from "@void-cat/api"; import { useState } from "react"; @@ -38,12 +39,22 @@ async function voidCatUpload(file: File | Blob): Promise { } } -export function FileUploader({ defaultImage, onClear, onFileUpload }) { - const [img, setImg] = useState(defaultImage); +interface FileUploaderProps { + defaultImage?: string; + onClear(): void; + onFileUpload(url: string): void; +} + +export function FileUploader({ + defaultImage, + onClear, + onFileUpload, +}: FileUploaderProps) { + const [img, setImg] = useState(defaultImage ?? ""); const [isUploading, setIsUploading] = useState(false); - async function onFileChange(ev) { - const file = ev.target.files[0]; + async function onFileChange(ev: ChangeEvent) { + const file = ev.target.files && ev.target.files[0]; if (file) { try { setIsUploading(true); diff --git a/src/element/follow-button.tsx b/src/element/follow-button.tsx index 0e7750d..b14123b 100644 --- a/src/element/follow-button.tsx +++ b/src/element/follow-button.tsx @@ -12,7 +12,7 @@ export function LoggedInFollowButton({ value: string; }) { const login = useLogin(); - const tags = login.follows.tags; + const { tags, content, timestamp } = login!.follows; const follows = tags.filter((t) => t.at(0) === tag); const isFollowing = follows.find((t) => t.at(1) === value); @@ -21,7 +21,7 @@ export function LoggedInFollowButton({ if (pub) { const newFollows = tags.filter((t) => t.at(1) !== value); const ev = await pub.generic((eb) => { - eb.kind(EventKind.ContactList).content(login.follows.content); + eb.kind(EventKind.ContactList).content(content ?? ""); for (const t of newFollows) { eb.tag(t); } @@ -29,7 +29,7 @@ export function LoggedInFollowButton({ }); console.debug(ev); System.BroadcastEvent(ev); - Login.setFollows(newFollows, login.follows.content, ev.created_at); + Login.setFollows(newFollows, content ?? "", ev.created_at); } } @@ -38,7 +38,7 @@ export function LoggedInFollowButton({ if (pub) { const newFollows = [...tags, [tag, value]]; const ev = await pub.generic((eb) => { - eb.kind(EventKind.ContactList).content(login.follows.content); + eb.kind(EventKind.ContactList).content(content ?? ""); for (const tag of newFollows) { eb.tag(tag); } @@ -46,13 +46,13 @@ export function LoggedInFollowButton({ }); console.debug(ev); System.BroadcastEvent(ev); - Login.setFollows(newFollows, login.follows.content, ev.created_at); + Login.setFollows(newFollows, content ?? "", ev.created_at); } } return ( - ) : null; + return login?.pubkey ? : null; } export function FollowButton({ pubkey }: { pubkey: string }) { const login = useLogin(); return login?.pubkey ? ( - + ) : null; } diff --git a/src/element/hypertext.tsx b/src/element/hypertext.tsx index 8f378b8..34293eb 100644 --- a/src/element/hypertext.tsx +++ b/src/element/hypertext.tsx @@ -1,9 +1,11 @@ -import { NostrLink } from "./nostr-link"; +import type { ReactNode } from "react"; +import { NostrLink } from "element/nostr-link"; const FileExtensionRegex = /\.([\w]+)$/i; interface HyperTextProps { link: string; + children: ReactNode; } export function HyperText({ link, children }: HyperTextProps) { @@ -24,7 +26,7 @@ export function HyperText({ link, children }: HyperTextProps) { {url.toString()} ); } diff --git a/src/element/live-chat.tsx b/src/element/live-chat.tsx index b0bc05d..1418eba 100644 --- a/src/element/live-chat.tsx +++ b/src/element/live-chat.tsx @@ -37,7 +37,7 @@ export interface LiveChatOptions { } function BadgeAward({ ev }: { ev: NostrEvent }) { - const badge = findTag(ev, "a"); + const badge = findTag(ev, "a") ?? ""; const [k, pubkey, d] = badge.split(":"); const awardees = getTagValues(ev.tags, "p"); const event = useAddress(Number(k), pubkey, d); diff --git a/src/element/markdown.tsx b/src/element/markdown.tsx index c867686..9ddadb3 100644 --- a/src/element/markdown.tsx +++ b/src/element/markdown.tsx @@ -1,38 +1,46 @@ import "./markdown.css"; -import { createElement } from "react"; import { useMemo } from "react"; import ReactMarkdown from "react-markdown"; import { HyperText } from "element/hypertext"; -import { transformText } from "element/text"; +import { transformText, type Fragment } from "element/text"; +import type { Tags } from "types"; interface MarkdownProps { content: string; - tags?: string[]; + tags?: Tags; } -export function Markdown({ - content, - tags = [], - element = "div", -}: MarkdownProps) { +interface LinkProps { + href?: string; + children?: Array; +} + +interface ComponentProps { + children?: Array; +} + +export function Markdown({ content, tags = [] }: MarkdownProps) { const components = useMemo(() => { return { - li: ({ children, ...props }) => { + li: ({ children, ...props }: ComponentProps) => { return children &&
  • {transformText(children, tags)}
  • ; }, - td: ({ children }) => - children && {transformText(children, tags)}, - p: ({ children }) =>

    {transformText(children, tags)}

    , - a: (props) => { - return {props.children}; + td: ({ children }: ComponentProps) => { + return children && {transformText(children, tags)}; + }, + p: ({ children }: ComponentProps) => { + return children &&

    {transformText(children, tags)}

    ; + }, + a: ({ href, children }: LinkProps) => { + return href && {children}; }, }; }, [tags]); - return createElement( - element, - { className: "markdown" }, - {content}, + return ( +
    + {content} +
    ); } diff --git a/src/element/mute-button.tsx b/src/element/mute-button.tsx index 4206b29..c712df0 100644 --- a/src/element/mute-button.tsx +++ b/src/element/mute-button.tsx @@ -5,7 +5,7 @@ import { MUTED } from "const"; export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { const login = useLogin(); - const tags = login.muted.tags; + const { tags, content, timestamp } = login!.muted; const muted = tags.filter((t) => t.at(0) === "p"); const isMuted = muted.find((t) => t.at(1) === pubkey); @@ -14,7 +14,7 @@ export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { if (pub) { const newMuted = tags.filter((t) => t.at(1) !== pubkey); const ev = await pub.generic((eb) => { - eb.kind(MUTED).content(login.muted.content); + eb.kind(MUTED).content(content ?? ""); for (const t of newMuted) { eb.tag(t); } @@ -22,7 +22,7 @@ export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { }); console.debug(ev); System.BroadcastEvent(ev); - Login.setMuted(newMuted, login.muted.content, ev.created_at); + Login.setMuted(newMuted, content ?? "", ev.created_at); } } @@ -31,7 +31,7 @@ export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { if (pub) { const newMuted = [...tags, ["p", pubkey]]; const ev = await pub.generic((eb) => { - eb.kind(MUTED).content(login.muted.content); + eb.kind(MUTED).content(content ?? ""); for (const tag of newMuted) { eb.tag(tag); } @@ -39,13 +39,13 @@ export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { }); console.debug(ev); System.BroadcastEvent(ev); - Login.setMuted(newMuted, login.muted.content, ev.created_at); + Login.setMuted(newMuted, content ?? "", ev.created_at); } } return ( - ) : null; + return login?.pubkey ? : null; } diff --git a/src/element/stream-cards.tsx b/src/element/stream-cards.tsx index e7b8c83..65ccbda 100644 --- a/src/element/stream-cards.tsx +++ b/src/element/stream-cards.tsx @@ -5,50 +5,51 @@ import * as Dialog from "@radix-ui/react-dialog"; import { DndProvider, useDrag, useDrop } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; -import type { NostrEvent } from "@snort/system"; +import type { TaggedRawEvent } from "@snort/system"; import { Toggle } from "element/toggle"; +import { Icon } from "element/icon"; +import { ExternalLink } from "element/external-link"; +import { FileUploader } from "element/file-uploader"; +import { Markdown } from "element/markdown"; import { useLogin } from "hooks/login"; import { useCards, useUserCards } from "hooks/cards"; import { CARD, USER_CARDS } from "const"; -import { toTag } from "utils"; +import { toTag, findTag } from "utils"; import { Login, System } from "index"; -import { findTag } from "utils"; -import { Icon } from "./icon"; -import { ExternalLink } from "./external-link"; -import { FileUploader } from "./file-uploader"; -import { Markdown } from "./markdown"; +import type { Tags } from "types"; interface CardType { - identifier?: string; + identifier: string; + content: string; title?: string; image?: string; link?: string; - content: string; } -interface CardProps { - canEdit?: boolean; - ev: NostrEvent; - cards: NostrEvent[]; -} +type NewCard = Omit; function isEmpty(s?: string) { return !s || s.trim().length === 0; } +interface CardPreviewProps extends NewCard { + style: object; +} + const CardPreview = forwardRef( - ({ style, title, link, image, content }, ref) => { + ({ style, title, link, image, content }: CardPreviewProps, ref) => { const isImageOnly = !isEmpty(image) && isEmpty(content) && isEmpty(title); return (
    ' ref={ref} style={style} > {title &&

    {title}

    } {image && - (link?.length > 0 ? ( + (link && link?.length > 0 ? ( {title} @@ -61,9 +62,19 @@ const CardPreview = forwardRef( }, ); +interface CardProps { + canEdit?: boolean; + ev: TaggedRawEvent; + cards: TaggedRawEvent[]; +} + +interface CardItem { + identifier: string; +} + function Card({ canEdit, ev, cards }: CardProps) { const login = useLogin(); - const identifier = findTag(ev, "d"); + const identifier = findTag(ev, "d") ?? ""; const title = findTag(ev, "title") || findTag(ev, "subject"); const image = findTag(ev, "image"); const link = findTag(ev, "r"); @@ -73,9 +84,9 @@ function Card({ canEdit, ev, cards }: CardProps) { const [style, dragRef] = useDrag( () => ({ type: "card", - item: { identifier }, + item: { identifier } as CardItem, canDrag: () => { - return canEdit; + return Boolean(canEdit); }, collect: (monitor) => { const isDragging = monitor.isDragging(); @@ -88,15 +99,15 @@ function Card({ canEdit, ev, cards }: CardProps) { [canEdit, identifier], ); - function findTagByIdentifier(d) { - return tags.find((t) => t.at(1).endsWith(`:${d}`)); + function findTagByIdentifier(d: string) { + return tags.find((t) => t.at(1)!.endsWith(`:${d}`)); } const [dropStyle, dropRef] = useDrop( () => ({ accept: ["card"], canDrop: () => { - return canEdit; + return Boolean(canEdit); }, collect: (monitor) => { const isOvering = monitor.isOver({ shallow: true }); @@ -106,10 +117,11 @@ function Card({ canEdit, ev, cards }: CardProps) { }; }, async drop(item) { - if (identifier === item.identifier) { + const typed = item as CardItem; + if (identifier === typed.identifier) { return; } - const newItem = findTagByIdentifier(item.identifier); + const newItem = findTagByIdentifier(typed.identifier); const oldItem = findTagByIdentifier(identifier); const newTags = tags.map((t) => { if (t === oldItem) { @@ -119,18 +131,20 @@ function Card({ canEdit, ev, cards }: CardProps) { return oldItem; } return t; - }); + }) as Tags; const pub = login?.publisher(); - const userCardsEv = await pub.generic((eb) => { - eb.kind(USER_CARDS).content(""); - for (const tag of newTags) { - eb.tag(tag); - } - return eb; - }); - console.debug(userCardsEv); - System.BroadcastEvent(userCardsEv); - Login.setCards(newTags, userCardsEv.created_at); + if (pub) { + const userCardsEv = await pub.generic((eb) => { + eb.kind(USER_CARDS).content(""); + for (const tag of newTags) { + eb.tag(tag); + } + return eb; + }); + console.debug(userCardsEv); + System.BroadcastEvent(userCardsEv); + Login.setCards(newTags, userCardsEv.created_at); + } }, }), [canEdit, tags, identifier], @@ -166,7 +180,7 @@ interface CardDialogProps { cta?: string; cancelCta?: string; card?: CardType; - onSave(ev: CardType): void; + onSave(ev: NewCard): void; onCancel(): void; } @@ -187,7 +201,7 @@ function CardDialog({

    {header || "Add card"}

    - +
    - +
    - +
    - +