poll creation

This commit is contained in:
Kieran 2023-04-10 15:55:25 +01:00
parent 657f684c2c
commit bf31816051
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
6 changed files with 112 additions and 66 deletions

View File

@ -376,5 +376,14 @@
stroke-linejoin="round"
/>
</symbol>
<symbol id="pie-chart" viewBox="0 0 22 22" fill="none">
<path
d="M20.2104 14.8901C19.5742 16.3946 18.5792 17.7203 17.3123 18.7514C16.0454 19.7825 14.5452 20.4875 12.9428 20.8048C11.3405 21.1222 9.68483 21.0422 8.12055 20.5719C6.55627 20.1015 5.13103 19.2551 3.96942 18.1067C2.80782 16.9583 1.94522 15.5428 1.45704 13.984C0.968859 12.4252 0.869965 10.7706 1.169 9.1647C1.46804 7.55885 2.1559 6.0507 3.17245 4.7721C4.189 3.4935 5.50329 2.48339 7.0004 1.83007M20.2392 7.17323C20.6395 8.1397 20.8851 9.16143 20.9684 10.2009C20.989 10.4577 20.9993 10.5861 20.9483 10.7018C20.9057 10.7984 20.8213 10.8898 20.7284 10.94C20.6172 11.0001 20.4783 11.0001 20.2004 11.0001H11.8004C11.5204 11.0001 11.3804 11.0001 11.2734 10.9456C11.1793 10.8976 11.1028 10.8211 11.0549 10.7271C11.0004 10.6201 11.0004 10.4801 11.0004 10.2001V1.80007C11.0004 1.5222 11.0004 1.38327 11.0605 1.27205C11.1107 1.17915 11.2021 1.09476 11.2987 1.05216C11.4144 1.00117 11.5428 1.01146 11.7996 1.03205C12.839 1.11539 13.8608 1.36095 14.8272 1.76127C16.0405 2.26382 17.1429 3.00042 18.0715 3.929C19.0001 4.85759 19.7367 5.95998 20.2392 7.17323Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</symbol>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 (
<>
<h4>
<FormattedMessage defaultMessage="Poll Options" />
</h4>
{pollOptions?.map((a, i) => (
<div className="form-group w-max" key={`po-${i}`}>
<div>
<FormattedMessage defaultMessage="Option: {n}" values={{ n: i + 1 }} />
</div>
<div>
<input type="text" value={a} onChange={e => changePollOption(i, e.target.value)} />
{i > 1 && (
<button onClick={() => removePollOption(i)} className="ml5">
<Icon name="close" size={14} />
</button>
)}
</div>
</div>
))}
<button onClick={() => dispatch(setPollOptions([...pollOptions, ""]))}>
<Icon name="plus" size={14} />
</button>
</>
);
}
}
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 && <NotePreview note={replyTo} />}
{preview && getPreviewNote()}
{!preview && (
<div className={`flex note-creator ${replyTo ? "note-reply" : ""}`}>
<div className="flex f-col mr10 f-grow">
<div className={`flex note-creator${replyTo ? " note-reply" : ""}${pollOptions ? " poll" : ""}`}>
<div className="flex f-col f-grow">
<Textarea
autoFocus
className={`textarea ${active ? "textarea--focused" : ""}`}
@ -172,9 +226,17 @@ export function NoteCreator() {
}
}}
/>
<button type="button" className="attachment" onClick={attachFile}>
<Icon name="attachment" />
</button>
{renderPollOptions()}
<div className="insert">
{pollOptions === undefined && !replyTo && (
<button type="button" onClick={() => dispatch(setPollOptions(["A", "B"]))}>
<Icon name="pie-chart" />
</button>
)}
<button type="button" onClick={attachFile}>
<Icon name="attachment" />
</button>
</div>
</div>
{error && <span className="error">{error}</span>}
</div>

View File

@ -96,6 +96,7 @@ export default function useEventPublisher() {
},
broadcast: (ev: RawEvent | undefined) => {
if (ev) {
console.debug(ev);
System.BroadcastEvent(ev);
}
},
@ -179,9 +180,9 @@ export default function useEventPublisher() {
return await signEvent(ev);
}
},
note: async (msg: string, extraTags?: Array<Array<string>>) => {
note: async (msg: string, extraTags?: Array<Array<string>>, kind?: EventKind) => {
if (pubKey) {
const ev = EventExt.forPubKey(pubKey, EventKind.TextNote);
const ev = EventExt.forPubKey(pubKey, kind ?? EventKind.TextNote);
processContent(ev, msg);
if (extraTags) {
for (const et of extraTags) {
@ -218,9 +219,9 @@ export default function useEventPublisher() {
/**
* Reply to a note
*/
reply: async (replyTo: TaggedRawEvent, msg: string, extraTags?: Array<Array<string>>) => {
reply: async (replyTo: TaggedRawEvent, msg: string, extraTags?: Array<Array<string>>, kind?: EventKind) => {
if (pubKey) {
const ev = EventExt.forPubKey(pubKey, EventKind.TextNote);
const ev = EventExt.forPubKey(pubKey, kind ?? EventKind.TextNote);
const thread = EventExt.extractThread(ev);
if (thread) {

View File

@ -6,11 +6,12 @@ interface NoteCreatorStore {
note: string;
error: string;
active: boolean;
preview: RawEvent | null;
replyTo: TaggedRawEvent | null;
preview?: RawEvent;
replyTo?: TaggedRawEvent;
showAdvanced: boolean;
zapForward: string;
sensitive: string;
pollOptions?: Array<string>;
}
const InitState: NoteCreatorStore = {
@ -18,8 +19,6 @@ const InitState: NoteCreatorStore = {
note: "",
error: "",
active: false,
preview: null,
replyTo: null,
showAdvanced: false,
zapForward: "",
sensitive: "",
@ -41,10 +40,10 @@ const NoteCreatorSlice = createSlice({
setActive: (state, action: PayloadAction<boolean>) => {
state.active = action.payload;
},
setPreview: (state, action: PayloadAction<RawEvent | null>) => {
setPreview: (state, action: PayloadAction<RawEvent | undefined>) => {
state.preview = action.payload;
},
setReplyTo: (state, action: PayloadAction<TaggedRawEvent | null>) => {
setReplyTo: (state, action: PayloadAction<TaggedRawEvent | undefined>) => {
state.replyTo = action.payload;
},
setShowAdvanced: (state, action: PayloadAction<boolean>) => {
@ -56,6 +55,9 @@ const NoteCreatorSlice = createSlice({
setSensitive: (state, action: PayloadAction<string>) => {
state.sensitive = action.payload;
},
setPollOptions: (state, action: PayloadAction<Array<string> | undefined>) => {
state.pollOptions = action.payload;
},
reset: () => InitState,
},
});
@ -70,6 +72,7 @@ export const {
setShowAdvanced,
setZapForward,
setSensitive,
setPollOptions,
reset,
} = NoteCreatorSlice.actions;