diff --git a/client/src/App.tsx b/client/src/App.tsx index 87d02ab..bc0f5a1 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,13 +1,13 @@ import "./styles/App.css"; -import Home from "./components/Routes/Home"; -import Settings from "./components/Routes/Settings"; +import Home from "./Components/Routes/Home"; +import Settings from "./Components/Routes/Settings"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; -import Thread from "./components/Routes/Thread"; -import Header from "./components/Modals/Header"; -import AddToHomeScreenPrompt from "./components/Modals/CheckMobile/CheckMobile"; -import Notifications from "./components/Routes/Notifications"; -import Hashtags from "./components/Routes/Hashtags"; -import HashtagPage from "./components/Routes/HashtagPage"; +import Thread from "./Components/Routes/Thread"; +import Header from "./Components/Modals/Header"; +import AddToHomeScreenPrompt from "./Components/Modals/CheckMobile/CheckMobile"; +import Notifications from "./Components/Routes/Notifications"; +import Hashtags from "./Components/Routes/Hashtags"; +import HashtagPage from "./Components/Routes/HashtagPage"; function App() { return ( diff --git a/client/src/Components/Forms/Emojis/emoji-picker.tsx b/client/src/Components/Forms/Emojis/emoji-picker.tsx new file mode 100644 index 0000000..cd5f8fb --- /dev/null +++ b/client/src/Components/Forms/Emojis/emoji-picker.tsx @@ -0,0 +1,57 @@ +import data, { Emoji } from "@emoji-mart/data"; +import Picker from "@emoji-mart/react"; +import { RefObject } from "react"; +import customEmojis from "../custom_emojis.json"; + +interface EmojiPickerProps { + topOffset: number; + leftOffset: number; + onEmojiSelect: (e: Emoji) => void; + onClickOutside: () => void; + height?: number; + ref: RefObject; +} + +export function EmojiPicker({ + topOffset, + leftOffset, + onEmojiSelect, + onClickOutside, + height = 300, + ref, +}: EmojiPickerProps) { + const customEmojiList = customEmojis.map((pack) => { + return { + id: pack.id, + name: pack.name, + emojis: pack.emojis + .filter((e) => !e.static_url.endsWith('.svg')) + .map((e) => { + return { + id: e.shortcode, + name: e.shortcode, + skins: [{ src: e.static_url }], + }; + }), + }; + }); + + return ( + <> +
+ +
+ + ); +} diff --git a/client/src/Components/Forms/PostFormCard.tsx b/client/src/Components/Forms/PostFormCard.tsx new file mode 100644 index 0000000..46a55c3 --- /dev/null +++ b/client/src/Components/Forms/PostFormCard.tsx @@ -0,0 +1,215 @@ +import { + ServerIcon, + CpuChipIcon, + ArrowPathIcon, + PlusCircleIcon +} from "@heroicons/react/24/outline"; +import { XCircleIcon } from "@heroicons/react/24/solid"; +import { useState, useEffect, useRef } from "react"; +import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools"; +import { renderMedia, attachFile } from "../../utils/FileUpload"; +import EmojiPicker from "@emoji-mart/react"; +import customEmojis from './custom_emojis.json'; +import { useSubmitForm } from "./handleSubmit"; +import "../../styles/Form.css"; + +interface FormProps { + refEvent?: NostrEvent; + tagType?: 'Reply' | 'Quote' | ''; + hashtag?: string; +} + +const NewNoteCard = ({ + refEvent, + tagType, + hashtag, +}: FormProps) => { + const ref = useRef(null); + const [comment, setComment] = useState(""); + const [file, setFile] = useState(""); + const [unsigned, setUnsigned] = useState({ + kind: 1, + tags: [ + [ + "client", + "getwired.app" + ] + ], + content: "", + created_at: Math.floor(Date.now() / 1000), + pubkey: "", + }); + const [difficulty, setDifficulty] = useState( + localStorage.getItem("difficulty") || "21" + ); + const [fileSizeError, setFileSizeError] = useState(false); + const [uploadingFile, setUploadingFile] = useState(false); + + useEffect(() => { + if (hashtag) { + unsigned.tags.push(['t', hashtag as string]); + } + + if (refEvent && tagType) { + unsigned.tags = Array.from(new Set(unsigned.tags.concat(refEvent.tags))); + unsigned.tags.push(['p', refEvent.pubkey]); + + if (tagType === 'Reply') { + unsigned.tags.push(['e', refEvent.id, refEvent.tags.some(tag => tag[0] === 'e') ? 'root' : '']); + } else { + if (tagType === 'Quote') { + setComment(comment + '\nnostr:' + nip19.noteEncode(refEvent.id)); + unsigned.tags.push(['q', refEvent.id]); + } else { + unsigned.tags.push(['e', refEvent.id]); + } + } + } + + const handleDifficultyChange = (event: Event) => { + const customEvent = event as CustomEvent; + const { difficulty } = customEvent.detail; + setDifficulty(difficulty); + }; + + window.addEventListener("difficultyChanged", handleDifficultyChange); + + return () => { + window.removeEventListener("difficultyChanged", handleDifficultyChange); + }; + }, []); + + useEffect(() => { + setUnsigned(prevUnsigned => ({ + ...prevUnsigned, + content: `${comment} ${file}`, + created_at: Math.floor(Date.now() / 1000), + })); + }, [comment, file]); + + const { handleSubmit: originalHandleSubmit, doingWorkProp, doingWorkProgress } = useSubmitForm(unsigned, difficulty); + + const handleSubmit = async (event: React.FormEvent) => { + await originalHandleSubmit(event); + setComment(""); + setFile(""); + setUnsigned(prevUnsigned => ({ + ...prevUnsigned, + content: '', + created_at: Math.floor(Date.now() / 1000) + })); + }; + + //Emoji stuff + const emojiRef = useRef(null); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + + interface Emoji { + native?: string; + id?: string; + } + + const emojiNames = customEmojis.map(p => p.emojis).flat(); + function getEmojiById(id: string) { + return emojiNames.find(e => e.shortcode === id); + } + + async function onEmojiSelect(emoji: Emoji) { + setShowEmojiPicker(false); + try { + if (emoji.id) { + const e = getEmojiById(emoji.id); + if (e) { + setComment(comment + " :" + e.shortcode + ":"); + unsigned.tags.push(['emoji', e.shortcode, e.url]); + }; + } + } catch { + //ignore + } + } + + const topOffset = ref.current?.getBoundingClientRect().top; + const leftOffset = ref.current?.getBoundingClientRect().left; + + function pickEmoji(e: React.MouseEvent) { + e.stopPropagation(); + setShowEmojiPicker(!showEmojiPicker); + } + + return ( +
+ +
+