diff --git a/lumina/app/profile/[pubkey]/page.tsx b/lumina/app/profile/[pubkey]/page.tsx index 91b628e..195b9b9 100644 --- a/lumina/app/profile/[pubkey]/page.tsx +++ b/lumina/app/profile/[pubkey]/page.tsx @@ -9,6 +9,7 @@ import { SectionIcon, GridIcon } from '@radix-ui/react-icons' import ProfileQuickViewFeed from "@/components/ProfileQuickViewFeed"; import ProfileTextFeed from "@/components/ProfileTextFeed"; import { NostrProvider } from "nostr-react"; +import ProfileGalleryViewFeed from "@/components/ProfileGalleryViewFeed"; export default function ProfilePage() { @@ -24,7 +25,7 @@ export default function ProfilePage() { const relayUrls = [ "wss://relay.lumina.rocks", ]; - + return ( <> @@ -37,6 +38,7 @@ export default function ProfilePage() { Notes + {/* Gallery */} @@ -47,6 +49,9 @@ export default function ProfilePage() { + {/* + + */} diff --git a/lumina/app/upload/page.tsx b/lumina/app/upload/page.tsx new file mode 100644 index 0000000..07a0029 --- /dev/null +++ b/lumina/app/upload/page.tsx @@ -0,0 +1,46 @@ +'use client'; + +import Head from "next/head"; +import ProfileInfoCard from "@/components/ProfileInfoCard"; +import ProfileFeed from "@/components/ProfileFeed"; +import { useParams } from 'next/navigation' +import { Event, NostrEvent, finalizeEvent, nip19 } from "nostr-tools"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { SectionIcon, GridIcon } from '@radix-ui/react-icons' +import TagFeed from "@/components/TagFeed"; +import { NostrProvider, useNostr } from "nostr-react"; +import { FormEvent } from "react"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { bytesToHex, hexToBytes } from '@noble/hashes/utils' +import UploadComponent from "@/components/UploadComponent"; + +export default function UploadPage() { + + // check if pubkey contains "npub" + // if so, then we need to convert it to a pubkey + // if (pubkey.includes("npub")) { + // // convert npub to pubkey + // pubkey = nip19.decode(pubkey.toString()).data.toString() + // } + + const relayUrls = [ + "wss://relay.lumina.rocks", + ]; + + return ( + <> + + + LUMINA.rocks + + + + +
+ +
+
+ + ); +} \ No newline at end of file diff --git a/lumina/components/BottomBar.tsx b/lumina/components/BottomBar.tsx index b5654d1..0dee703 100644 --- a/lumina/components/BottomBar.tsx +++ b/lumina/components/BottomBar.tsx @@ -1,27 +1,38 @@ "use client"; -/** - * v0 by Vercel. - * @see https://v0.dev/t/mwaJmHMv0vd - * Documentation: https://v0.dev/docs#integrating-generated-code-into-your-nextjs-app - */ -import { BellIcon, GlobeIcon, HomeIcon, RowsIcon } from "@radix-ui/react-icons" +import { BellIcon, GlobeIcon, HomeIcon, RowsIcon, UploadIcon } from "@radix-ui/react-icons" import Link from "next/link" -import { JSX, SVGProps, useEffect, useState } from "react" +import { FormEvent, JSX, SVGProps, useEffect, useState } from "react" import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar" import { useRouter, usePathname } from 'next/navigation' -import { SearchIcon } from "lucide-react"; +import { SearchIcon, Upload } from "lucide-react"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/ui/drawer" +import { Button } from "./ui/button"; +import { Textarea } from "./ui/textarea"; +import { useNostr } from "nostr-react"; export default function BottomBar() { - const router = useRouter(); const [pubkey, setPubkey] = useState(null); + const pathname = usePathname(); useEffect(() => { - return setPubkey(window.localStorage.getItem('pubkey') ?? null); + if (typeof window !== 'undefined') { + setPubkey(window.localStorage.getItem('pubkey') ?? null); + } }, []); - const pathname = usePathname(); + if (typeof window === 'undefined') return null; + const isActive = (path: string, currentPath: string) => currentPath === path ? 'text-purple-500' : ''; return ( @@ -36,10 +47,12 @@ export default function BottomBar() { Follower Feed )} - - - Global - + {pubkey && window.localStorage.getItem('loginType') != 'readOnly_npub' && ( + + + Upload + + )} Search diff --git a/lumina/components/CommentCard.tsx b/lumina/components/CommentCard.tsx index 86c766c..6a35025 100644 --- a/lumina/components/CommentCard.tsx +++ b/lumina/components/CommentCard.tsx @@ -43,8 +43,8 @@ const NoteCard: React.FC = ({ pubkey, text, eventId, tags, eve }); const title = userData?.username || userData?.display_name || userData?.name || userData?.npub || nip19.npubEncode(pubkey); - const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif)/g); - const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif)/g, ''); + const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif|jpeg)/g); + const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g, ''); const createdAt = new Date(event.created_at * 1000); const hrefProfile = `/profile/${nip19.npubEncode(pubkey)}`; const profileImageSrc = userData?.picture || "https://robohash.org/" + pubkey; diff --git a/lumina/components/FollowerFeed.tsx b/lumina/components/FollowerFeed.tsx index 4a36966..f577f03 100644 --- a/lumina/components/FollowerFeed.tsx +++ b/lumina/components/FollowerFeed.tsx @@ -32,7 +32,7 @@ const FollowerFeed: React.FC = ({ pubkey }) => { // const filteredEvents = events.filter((event) => event.content.includes(".jpg")); // filter events with regex that checks for png, jpg, or gif - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|jpeg)/g)?.[0]); // now filter all events with a tag[0] == t and tag[1] == nsfw filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw")); diff --git a/lumina/components/FollowerQuickViewFeed.tsx b/lumina/components/FollowerQuickViewFeed.tsx index 8f4c002..78c5ab0 100644 --- a/lumina/components/FollowerQuickViewFeed.tsx +++ b/lumina/components/FollowerQuickViewFeed.tsx @@ -31,7 +31,7 @@ const FollowerQuickViewFeed: React.FC = ({ pubkey }) }, }); - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g)?.[0]); // filter out all replies (tag[0] == e) filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' })); diff --git a/lumina/components/GalleryCard.tsx b/lumina/components/GalleryCard.tsx new file mode 100644 index 0000000..1fdffe8 --- /dev/null +++ b/lumina/components/GalleryCard.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { useProfile } from "nostr-react"; +import { + nip19, +} from "nostr-tools"; +import { + Card, + SmallCardContent, +} from "@/components/ui/card" +import Image from 'next/image'; +import Link from 'next/link'; +import { PlayIcon, StackIcon, VideoIcon } from '@radix-ui/react-icons'; + +interface GalleryCardProps { + pubkey: string; + eventId: string; + imageUrl: string; + linkToNote: boolean; +} + +const GalleryCard: React.FC = ({ pubkey, eventId, imageUrl, linkToNote }) => { + const { data: userData } = useProfile({ + pubkey, + }); + + const encodedNoteId = nip19.noteEncode(eventId); + + const card = ( + + +
+
+
+ {eventId} +
+
+
+
+
+ ); + + return ( + <> + {linkToNote ? ( + + {card} + + ) : ( + card + )} + + ); +} + +export default GalleryCard; \ No newline at end of file diff --git a/lumina/components/GlobalFeed.tsx b/lumina/components/GlobalFeed.tsx index e09a0fa..51aa17d 100644 --- a/lumina/components/GlobalFeed.tsx +++ b/lumina/components/GlobalFeed.tsx @@ -16,7 +16,7 @@ const GlobalFeed: React.FC = () => { // const filteredEvents = events.filter((event) => event.content.includes(".jpg")); // filter events with regex that checks for png, jpg, or gif - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g)?.[0]); // now filter all events with a tag[0] == t and tag[1] == nsfw // filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw")); diff --git a/lumina/components/NoteCard.tsx b/lumina/components/NoteCard.tsx index 3ba7913..b58f43a 100644 --- a/lumina/components/NoteCard.tsx +++ b/lumina/components/NoteCard.tsx @@ -49,9 +49,9 @@ const NoteCard: React.FC = ({ pubkey, text, eventId, tags, event, const title = userData?.username || userData?.display_name || userData?.name || userData?.npub || nip19.npubEncode(pubkey); // text = text.replaceAll('\n', '
'); text = text.replaceAll('\n', ' '); - const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif)/g); + const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif|jpeg)/g); const videoSrc = text.match(/https?:\/\/[^ ]*\.(mp4|webm|mov)/g); - const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov)/g, ''); + const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov|jpeg)/g, ''); const createdAt = new Date(event.created_at * 1000); const hrefProfile = `/profile/${nip19.npubEncode(pubkey)}`; const profileImageSrc = userData?.picture || "https://robohash.org/" + pubkey; diff --git a/lumina/components/ProfileFeed.tsx b/lumina/components/ProfileFeed.tsx index 182e6f4..b6ee766 100644 --- a/lumina/components/ProfileFeed.tsx +++ b/lumina/components/ProfileFeed.tsx @@ -20,7 +20,7 @@ const ProfileFeed: React.FC = ({ pubkey }) => { }, }); - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov|jpeg)/g)?.[0]); // filter out all replies (tag[0] == e) filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' })); diff --git a/lumina/components/ProfileGalleryViewFeed.tsx b/lumina/components/ProfileGalleryViewFeed.tsx new file mode 100644 index 0000000..361408f --- /dev/null +++ b/lumina/components/ProfileGalleryViewFeed.tsx @@ -0,0 +1,61 @@ +import { useRef } from "react"; +import { useNostrEvents } from "nostr-react"; +import { Skeleton } from "@/components/ui/skeleton"; +import GalleryCard from "./GalleryCard"; + +interface ProfileGalleryViewFeedProps { + pubkey: string; +} + +const ProfileGalleryViewFeed: React.FC = ({ pubkey }) => { + const now = useRef(new Date()); // Make sure current time isn't re-rendered + + const { isLoading, events } = useNostrEvents({ + filter: { + authors: [pubkey], + limit: 1, + kinds: [10011], + }, + }); + + const imagesAndIds = events.map((event) => { + return { + id: event.tags.filter((tag) => tag[0] === 'G').map((tag) => tag[1]), + images: event.tags.filter((tag) => tag[0] === 'G').map((tag) => tag[2]) + } + }); + + return ( + <> +
+ {imagesAndIds.length === 0 && isLoading ? ( + <> +
+ +
+
+ +
+
+ +
+ + ) : ( + imagesAndIds.map((galleryEntry) => ( + galleryEntry.images.map((imageUrl, index) => ( + + )) + )) + )} +
+ + ); +} + +export default ProfileGalleryViewFeed; \ No newline at end of file diff --git a/lumina/components/ProfileQuickViewFeed.tsx b/lumina/components/ProfileQuickViewFeed.tsx index 678a4c6..be4370e 100644 --- a/lumina/components/ProfileQuickViewFeed.tsx +++ b/lumina/components/ProfileQuickViewFeed.tsx @@ -20,7 +20,7 @@ const ProfileQuickViewFeed: React.FC = ({ pubkey }) = }, }); - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov|jpeg)/g)?.[0]); // filter out all replies (tag[0] == e) filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' })); diff --git a/lumina/components/ProfileTextFeed.tsx b/lumina/components/ProfileTextFeed.tsx index 56f6b71..ce93408 100644 --- a/lumina/components/ProfileTextFeed.tsx +++ b/lumina/components/ProfileTextFeed.tsx @@ -21,7 +21,7 @@ const ProfileTextFeed: React.FC = ({ pubkey }) => { }); // filter out all images since we only want text messages - let filteredEvents = events.filter((event) => !event.content.match(/https?:\/\/.*\.(?:png|jpg|gif)/g)?.[0]); + let filteredEvents = events.filter((event) => !event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g)?.[0]); // filter out all replies (tag[0] == e) filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' })); diff --git a/lumina/components/QuickViewNoteCard.tsx b/lumina/components/QuickViewNoteCard.tsx index 33e15db..362d008 100644 --- a/lumina/components/QuickViewNoteCard.tsx +++ b/lumina/components/QuickViewNoteCard.tsx @@ -27,9 +27,9 @@ const QuickViewNoteCard: React.FC = ({ pubkey, text, eventId, tag const title = userData?.username || userData?.display_name || userData?.name || userData?.npub || nip19.npubEncode(pubkey); text = text.replaceAll('\n', ' '); - const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif)/g); + const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif|jpeg)/g); const videoSrc = text.match(/https?:\/\/[^ ]*\.(mp4|webm|mov)/g); - const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov)/g, ''); + const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov|jpeg)/g, ''); const createdAt = new Date(event.created_at * 1000); const hrefProfile = `/profile/${nip19.npubEncode(pubkey)}`; const profileImageSrc = userData?.picture || "https://robohash.org/" + pubkey; diff --git a/lumina/components/ReelFeed.tsx b/lumina/components/ReelFeed.tsx index 7cf8f9f..8490427 100644 --- a/lumina/components/ReelFeed.tsx +++ b/lumina/components/ReelFeed.tsx @@ -16,7 +16,7 @@ const ReelFeed: React.FC = () => { // const filteredEvents = events.filter((event) => event.content.includes(".jpg")); // filter events with regex that checks for png, jpg, or gif - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g)?.[0]); // now filter all events with a tag[0] == t and tag[1] == nsfw // filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw")); diff --git a/lumina/components/TagFeed.tsx b/lumina/components/TagFeed.tsx index 2d38764..080f1c0 100644 --- a/lumina/components/TagFeed.tsx +++ b/lumina/components/TagFeed.tsx @@ -21,7 +21,7 @@ const TagFeed: React.FC = ({tag}) => { // const filteredEvents = events.filter((event) => event.content.includes(".jpg")); // filter events with regex that checks for png, jpg, or gif - let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm)/g)?.[0]); + let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|jpeg)/g)?.[0]); // now filter all events with a tag[0] == t and tag[1] == nsfw filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw")); diff --git a/lumina/components/TrendingImage.tsx b/lumina/components/TrendingImage.tsx index ecea5cc..b11b1f3 100644 --- a/lumina/components/TrendingImage.tsx +++ b/lumina/components/TrendingImage.tsx @@ -41,8 +41,8 @@ const TrendingImage: React.FC = ({ eventId, pubkey }) => { const createdAt = events && events.length > 0 ? new Date(events[0].created_at * 1000) : new Date(); const title = userData?.username || userData?.display_name || userData?.name || userData?.npub || npubShortened; text = text.replaceAll('\n', ' '); - const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif)/g); - const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif)/g, ''); + const imageSrc = text.match(/https?:\/\/[^ ]*\.(png|jpg|gif|jpeg)/g); + const textWithoutImage = text.replace(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g, ''); const hrefProfile = `/profile/${nip19.npubEncode(pubkey)}`; const profileImageSrc = userData?.picture || "https://robohash.org/" + pubkey; diff --git a/lumina/components/UploadComponent.tsx b/lumina/components/UploadComponent.tsx new file mode 100644 index 0000000..fc741f0 --- /dev/null +++ b/lumina/components/UploadComponent.tsx @@ -0,0 +1,231 @@ +import { useNostr } from 'nostr-react'; +import { finalizeEvent, nip19, NostrEvent } from 'nostr-tools'; +import React, { ChangeEvent, FormEvent, useState } from 'react'; +import { Button } from './ui/button'; +import { Textarea } from './ui/textarea'; +import { bytesToHex, hexToBytes } from '@noble/hashes/utils' +import { ReloadIcon } from '@radix-ui/react-icons'; +import { Label } from './ui/label'; +import { Input } from './ui/input'; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion" + +const UploadComponent: React.FC = () => { + + const { publish } = useNostr(); + const { createHash } = require('crypto'); + const loginType = typeof window !== 'undefined' ? window.localStorage.getItem('loginType') : null; + const [previewUrl, setPreviewUrl] = useState(''); + + const [isLoading, setIsLoading] = useState(false); + + const handleFileChange = (event: ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const url = URL.createObjectURL(file); + setPreviewUrl(url); + + // Optional: Bereinigung alter URLs + return () => URL.revokeObjectURL(url); + } + }; + + async function onSubmit(event: FormEvent) { + event.preventDefault(); + setIsLoading(true); + + const formData = new FormData(event.currentTarget); + const desc = formData.get('description') as string; + const file = formData.get('file') as File; + let sha256 = ''; + let finalNoteContent = desc; + let finalFileUrl = ''; + console.log('File:', file); + + if (!desc && !file.size) { + alert('Please enter a description and/or upload a file'); + setIsLoading(false); + return; + } + + // get every hashtag in desc and cut off the # symbol + let hashtags: string[] = desc.match(/#[a-zA-Z0-9]+/g) || []; + if (hashtags) { + hashtags = hashtags.map((hashtag) => hashtag.slice(1)); + } + + + // If file is is preent, upload it to the media server + if (file) { + const readFileAsArrayBuffer = (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as ArrayBuffer); + reader.onerror = () => reject(reader.error); + reader.readAsArrayBuffer(file); + }); + }; + + try { + const arrayBuffer = await readFileAsArrayBuffer(file); + const hashBuffer = createHash('sha256').update(Buffer.from(arrayBuffer)).digest(); + sha256 = hashBuffer.toString('hex'); + + const unixNow = () => Math.floor(Date.now() / 1000); + const newExpirationValue = () => (unixNow() + 60 * 5).toString(); + + const pubkey = window.localStorage.getItem('pubkey'); + const createdAt = Math.floor(Date.now() / 1000); + + // Create auth event for blossom auth via nostr + let authEvent = { + kind: 24242, + content: desc, + created_at: createdAt, + tags: [ + ['t', 'upload'], + ['x', sha256], + ['expiration', newExpirationValue()], + ], + }; + + console.log(authEvent); + + // Sign auth event + let authEventSigned = {}; + if (loginType === 'extension') { + authEventSigned = await window.nostr.signEvent(authEvent); + } else if (loginType === 'amber') { + // TODO: Sign event with amber + alert('Signing with Amber is not implemented yet, sorry!'); + } else if (loginType === 'raw_nsec') { + if (typeof window !== 'undefined') { + let nsecStr = null; + nsecStr = window.localStorage.getItem('nsec'); + if (nsecStr != null) { + authEventSigned = finalizeEvent(authEvent, hexToBytes(nsecStr)); + } + } + } + console.log(authEventSigned); + + // Actually upload the file + await fetch('https://media.lumina.rocks/upload', { + method: 'PUT', + body: file, + headers: { authorization: 'Nostr ' + btoa(JSON.stringify(authEventSigned)) }, + }).then(async (res) => { + if (res.ok) { + let responseText = await res.text(); + let responseJson = JSON.parse(responseText); + finalFileUrl = responseJson.url; + } else { + alert(await res.text()); + } + }); + } catch (error) { + console.error('Error reading file:', error); + } + } + + let noteTags = hashtags.map((tag) => ['t', tag]); + + // If we have a file, add the file url to the note content + // and also to the note tags imeta + // "tags": [ + // [ + // "imeta", + // "url https://nostr.build/i/my-image.jpg", + // "m image/jpeg", + // "blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$", + // "dim 3024x4032", + // "alt A scenic photo overlooking the coast of Costa Rica", + // "x ", + // "fallback https://nostrcheck.me/alt1.jpg", + // "fallback https://void.cat/alt1.jpg" + // ] + // ] + if (finalFileUrl) { + // convert file into image + const image = new Image(); + image.src = URL.createObjectURL(file); + + finalNoteContent = finalFileUrl + ' ' + desc; + noteTags.push(['imeta', 'url ' + finalFileUrl, 'm ' + file.type, 'x ' + sha256, 'ox ' + sha256]); + } + + const createdAt = Math.floor(Date.now() / 1000); + + + // Create the actual note + let noteEvent = { + kind: 1, + content: finalNoteContent, + created_at: createdAt, + tags: noteTags, + }; + + let signedEvent: NostrEvent | null = null; + + // Sign the actual note + if (loginType === 'extension') { + signedEvent = await window.nostr.signEvent(noteEvent); + } else if (loginType === 'amber') { + // TODO: Sign event with amber + alert('Signing with Amber is not implemented yet, sorry!'); + } else if (loginType === 'raw_nsec') { + if (typeof window !== 'undefined') { + let nsecStr = null; + nsecStr = window.localStorage.getItem('nsec'); + if (nsecStr != null) { + signedEvent = finalizeEvent(noteEvent, hexToBytes(nsecStr)); + } + } + } + + // If the got a signed event, publish it to nostr + if (signedEvent) { + console.log("final Event: ") + console.log(signedEvent) + publish(signedEvent); + } + + // Redirect to the note + setIsLoading(false); + if (signedEvent != null) { + window.location.href = '/note/' + nip19.noteEncode(signedEvent.id); + } + } + + return ( + <> +
+
+ + + + Image Upload + +
+ +
+ {previewUrl && Preview} +
+
+
+ {isLoading ? ( + + ) : ( + + )} +
+
+ + ); +} + +export default UploadComponent; \ No newline at end of file