diff --git a/packages/app/public/icons.svg b/packages/app/public/icons.svg index aa7fafc1..ea768eed 100644 --- a/packages/app/public/icons.svg +++ b/packages/app/public/icons.svg @@ -376,5 +376,14 @@ stroke-linejoin="round" /> + + + \ No newline at end of file diff --git a/packages/app/src/Element/Modal.css b/packages/app/src/Element/Modal.css index 4ce97359..c26c5d9f 100644 --- a/packages/app/src/Element/Modal.css +++ b/packages/app/src/Element/Modal.css @@ -7,7 +7,6 @@ background-color: var(--modal-bg-color); display: flex; justify-content: center; - align-items: center; z-index: 42; overflow-y: auto; } @@ -17,12 +16,7 @@ padding: 10px; border-radius: 10px; width: 500px; - min-height: 10vh; -} - -@media (max-width: 720px) { - .modal-body { - width: 100vw; - margin: 0 10px; - } + border: 1px solid var(--font-tertiary-color); + margin-top: auto; + margin-bottom: auto; } diff --git a/packages/app/src/Element/Note.css b/packages/app/src/Element/Note.css index 597fb135..2be8075e 100644 --- a/packages/app/src/Element/Note.css +++ b/packages/app/src/Element/Note.css @@ -38,6 +38,7 @@ display: flex; align-items: center; } + .note > .header > .info .saved svg { margin-right: 8px; } @@ -117,6 +118,7 @@ border-top-left-radius: 16px; border-top-right-radius: 16px; } + .note > .footer .ctx-menu li:last-of-type { padding-bottom: 12px; border-bottom-left-radius: 16px; @@ -147,6 +149,36 @@ margin-left: 56px; } +.note .poll-body { + padding: 5px; + user-select: none; +} + +.note .poll-body > div { + border: 1px solid var(--font-secondary-color); + border-radius: 5px; + margin-bottom: 3px; + position: relative; + overflow: hidden; +} + +.note .poll-body > div > div { + padding: 5px 10px; + z-index: 2; +} + +.note .poll-body > div:hover { + cursor: pointer; + border: 1px solid var(--highlight); +} + +.note .poll-body > div > .progress { + background-color: var(--gray); + height: stretch; + position: absolute; + z-index: 1; +} + .reaction-pill { display: flex; min-width: 1rem; diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 5585d296..91276548 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -28,6 +28,7 @@ import useModeration from "Hooks/useModeration"; import { setPinned, setBookmarked } from "State/Login"; import type { RootState } from "State/Store"; import { UserCache } from "Cache/UserCache"; +import Poll from "Element/Poll"; import messages from "./messages"; import { EventExt } from "System/EventExt"; @@ -270,7 +271,8 @@ export default function Note(props: NoteProps) { ); } - if (ev.kind !== EventKind.TextNote) { + const canRenderAsTextNote = [EventKind.TextNote, EventKind.Polls]; + if (!canRenderAsTextNote.includes(ev.kind)) { return ( <>

@@ -300,6 +302,12 @@ export default function Note(props: NoteProps) { } } + function pollOptions() { + if (ev.kind !== EventKind.Polls) return; + + return ; + } + function content() { if (!inView) return undefined; return ( @@ -332,6 +340,7 @@ export default function Note(props: NoteProps) {
goToEvent(e, ev, true)}> {transformBody()} {translation()} + {pollOptions()} {options.showReactionsLink && (
setShowReactions(true)}> diff --git a/packages/app/src/Element/NoteCreator.css b/packages/app/src/Element/NoteCreator.css index d2909f27..b19f6d48 100644 --- a/packages/app/src/Element/NoteCreator.css +++ b/packages/app/src/Element/NoteCreator.css @@ -17,7 +17,7 @@ resize: none; background-color: var(--note-bg); border-radius: 10px 10px 0 0; - min-height: 120px; + min-height: 100px; max-width: stretch; min-width: stretch; max-height: 210px; @@ -41,6 +41,9 @@ } } +.note-creator.poll textarea { + min-height: 120px; +} .note-creator-actions { width: 100%; display: flex; @@ -50,19 +53,22 @@ margin-bottom: 5px; } -.note-creator .attachment { - cursor: pointer; - position: absolute; - right: 16px; - bottom: 12px; +.note-creator .insert { + display: flex; + justify-content: flex-end; + width: stretch; +} + +.note-creator .insert > button { width: 48px; height: 36px; background: var(--gray-dark); color: white; - border-radius: 100px; + border-radius: 17px; + margin-right: 5px; display: flex; - align-items: center; justify-content: center; + align-items: center; } .note-creator .attachment:hover { @@ -87,19 +93,11 @@ position: absolute; left: 16px; bottom: 12px; - font-color: var(--error); + color: var(--error); margin-right: 12px; font-size: 16px; } -.note-creator .btn { - border-radius: 20px; - font-weight: bold; - background-color: var(--bg-color); - color: var(--font-color); - font-size: var(--font-size); -} - .note-create-button { width: 48px; height: 48px; @@ -114,31 +112,10 @@ justify-content: center; } -@media (min-width: 520px) { - .note-create-button { - right: 10vw; - } -} - -@media (min-width: 1020px) { - .note-create-button { - right: calc(50% - 360px); - } -} - .note-creator-modal .modal-body { background: var(--modal-bg-color); } -@media (max-width: 720px) { - .note-creator-modal { - align-items: flex-start; - } - .note-creator-modal .modal-body { - margin-top: 20vh; - } -} - .note-preview { word-break: break-all; } diff --git a/packages/app/src/Element/NoteCreator.tsx b/packages/app/src/Element/NoteCreator.tsx index 69c22104..a338c5a2 100644 --- a/packages/app/src/Element/NoteCreator.tsx +++ b/packages/app/src/Element/NoteCreator.tsx @@ -1,7 +1,7 @@ import "./NoteCreator.css"; import { FormattedMessage, useIntl } from "react-intl"; import { useDispatch, useSelector } from "react-redux"; -import { TaggedRawEvent } from "@snort/nostr"; +import { EventKind, TaggedRawEvent } from "@snort/nostr"; import Icon from "Icons/Icon"; import useEventPublisher from "Feed/EventPublisher"; @@ -21,6 +21,7 @@ import { setZapForward, setSensitive, reset, + setPollOptions, } from "State/NoteCreator"; import type { RootState } from "State/Store"; import { LNURL } from "LNURL"; @@ -56,6 +57,7 @@ export function NoteCreator() { const showAdvanced = useSelector((s: RootState) => s.noteCreator.showAdvanced); const zapForward = useSelector((s: RootState) => s.noteCreator.zapForward); const sensitive = useSelector((s: RootState) => s.noteCreator.sensitive); + const pollOptions = useSelector((s: RootState) => s.noteCreator.pollOptions); const dispatch = useDispatch(); async function sendNote() { @@ -81,8 +83,14 @@ export function NoteCreator() { extraTags ??= []; extraTags.push(["content-warning", sensitive]); } - const ev = replyTo ? await publisher.reply(replyTo, note, extraTags) : await publisher.note(note, extraTags); - console.debug("Sending note: ", ev); + const kind = pollOptions ? EventKind.Polls : EventKind.TextNote; + if (pollOptions) { + extraTags ??= []; + extraTags.push(...pollOptions.map((a, i) => ["poll_option", i.toString(), a])); + } + const ev = replyTo + ? await publisher.reply(replyTo, note, extraTags, kind) + : await publisher.note(note, extraTags, kind); publisher.broadcast(ev); dispatch(reset()); } @@ -127,7 +135,7 @@ export function NoteCreator() { async function loadPreview() { if (preview) { - dispatch(setPreview(null)); + dispatch(setPreview(undefined)); } else { const tmpNote = await publisher.note(note); if (tmpNote) { @@ -151,6 +159,52 @@ export function NoteCreator() { } } + function renderPollOptions() { + if (pollOptions) { + return ( + <> +

+ +

+ {pollOptions?.map((a, i) => ( +
+
+ +
+
+ changePollOption(i, e.target.value)} /> + {i > 1 && ( + + )} +
+
+ ))} + + + ); + } + } + + function changePollOption(i: number, v: string) { + if (pollOptions) { + const copy = [...pollOptions]; + copy[i] = v; + dispatch(setPollOptions(copy)); + } + } + + function removePollOption(i: number) { + if (pollOptions) { + const copy = [...pollOptions]; + copy.splice(i, 1); + dispatch(setPollOptions(copy)); + } + } + return ( <> {show && ( @@ -158,8 +212,8 @@ export function NoteCreator() { {replyTo && } {preview && getPreviewNote()} {!preview && ( -
-
+
+