Fix multiple choice poll selections not saving, bring back base64 profile image fallback but compressed
This commit is contained in:
parent
e575be921d
commit
27252f543a
4 changed files with 77 additions and 22 deletions
|
|
@ -7,7 +7,6 @@
|
|||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import {uploadFile} from "@app/uploads"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
||||
interface Props {
|
||||
file?: File | undefined
|
||||
|
|
@ -18,6 +17,10 @@
|
|||
|
||||
const id = randomId()
|
||||
|
||||
// Profile pictures are shown small (avatar-sized), so compress hard: this keeps
|
||||
// the base64 fallback below small enough to embed directly in the profile event.
|
||||
const compressOptions = {maxWidth: 128, maxHeight: 128, quality: 0.6, mimeType: "image/jpeg"}
|
||||
|
||||
const onDragEnter = () => {
|
||||
active = true
|
||||
}
|
||||
|
|
@ -33,11 +36,11 @@
|
|||
const onDrop = async (e: any) => {
|
||||
active = false
|
||||
|
||||
file = await compressFile(e.dataTransfer.files[0])
|
||||
file = await compressFile(e.dataTransfer.files[0], compressOptions)
|
||||
}
|
||||
|
||||
const onChange = async (e: any) => {
|
||||
file = await compressFile(e.target.files[0])
|
||||
file = await compressFile(e.target.files[0], compressOptions)
|
||||
}
|
||||
|
||||
const onClear = () => {
|
||||
|
|
@ -55,15 +58,24 @@
|
|||
if (file) {
|
||||
loading = true
|
||||
|
||||
const {error, result} = await uploadFile(file)
|
||||
const {result} = await uploadFile(file)
|
||||
|
||||
loading = false
|
||||
|
||||
if (result?.url) {
|
||||
url = result.url
|
||||
} else {
|
||||
file = undefined
|
||||
pushToast({theme: "error", message: error || "Failed to upload image."})
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
url = reader.result as string
|
||||
},
|
||||
false,
|
||||
)
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
} else {
|
||||
url = initialUrl
|
||||
|
|
@ -73,7 +85,12 @@
|
|||
</script>
|
||||
|
||||
<form>
|
||||
<input {id} type="file" accept="image/*" onchange={onChange} class="hidden" />
|
||||
<input
|
||||
{id}
|
||||
type="file"
|
||||
accept="image/jpeg,image/png,image/webp"
|
||||
onchange={onChange}
|
||||
class="hidden" />
|
||||
<label
|
||||
for={id}
|
||||
aria-label="Drag and drop files here."
|
||||
|
|
@ -85,7 +102,7 @@
|
|||
ondragleave={stopPropagation(preventDefault(onDragLeave))}
|
||||
ondrop={stopPropagation(preventDefault(onDrop))}>
|
||||
<button
|
||||
onclick={url && stopPropagation(onClear)}
|
||||
onclick={url ? stopPropagation(onClear) : undefined}
|
||||
class="absolute right-0 top-0 h-5 w-5 overflow-hidden rounded-full flex items-center justify-center"
|
||||
style="background: {url ? 'var(--error)' : 'var(--primary)'};">
|
||||
{#if url}
|
||||
|
|
|
|||
47
src/app/components/PollActions.svelte
Normal file
47
src/app/components/PollActions.svelte
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<script lang="ts">
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {getTagValue} from "@welshman/util"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
import EventActions from "@app/components/EventActions.svelte"
|
||||
import {publishDelete} from "@app/deletes"
|
||||
import {publishReaction} from "@app/reactions"
|
||||
import {canEnforceNip70} from "@app/relays"
|
||||
import {makePollPath, makeSpacePath} from "@app/routes"
|
||||
|
||||
interface Props {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
|
||||
const h = getTagValue("h", event.tags)
|
||||
const path = makePollPath(url, event.id)
|
||||
const shouldProtect = canEnforceNip70(url)
|
||||
|
||||
const deleteReaction = async (event: TrustedEvent) =>
|
||||
publishDelete({relays: [url], event, protect: await shouldProtect})
|
||||
|
||||
const createReaction = async (template: EventContent) =>
|
||||
publishReaction({...template, event, relays: [url], protect: await shouldProtect})
|
||||
</script>
|
||||
|
||||
<div class="flex grow flex-wrap justify-end gap-2">
|
||||
{#if h && showRoom}
|
||||
<Link href={makeSpacePath(url, h)} class="button button-neutral button-xs rounded-full">
|
||||
Posted in #<RoomName {h} {url} />
|
||||
</Link>
|
||||
{/if}
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ThunkStatusOrDeleted {event} />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
<EventActions {url} {event} noun="Poll" />
|
||||
</div>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import {getTagValue} from "@welshman/util"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
import CommentActions from "@app/components/CommentActions.svelte"
|
||||
import PollActions from "@app/components/PollActions.svelte"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import {makePollPath} from "@app/routes"
|
||||
|
|
@ -29,6 +29,6 @@
|
|||
in <RoomLink {url} {h} />
|
||||
{/if}
|
||||
</span>
|
||||
<CommentActions segment="polls" showActivity {url} {event} />
|
||||
<PollActions showActivity {url} {event} />
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
import {pubkey, publishThunk, abortThunk} from "@welshman/app"
|
||||
import {formatTimestampRelative} from "@welshman/lib"
|
||||
import {deriveEvents} from "@app/repository"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {makePollResponse} from "@app/polls"
|
||||
import PollOption from "@app/components/PollOption.svelte"
|
||||
import {
|
||||
|
|
@ -30,7 +29,6 @@
|
|||
const options = getPollOptions(event)
|
||||
const closed = isPollClosed(event)
|
||||
const endsAt = getPollEndsAt(event)
|
||||
const publishDelay = pollType === "multiplechoice" ? 10_000 : undefined
|
||||
|
||||
const getOwnResponse = (responses: TrustedEvent[]) => {
|
||||
let latest: TrustedEvent | undefined
|
||||
|
|
@ -61,19 +59,12 @@
|
|||
activeThunk = publishThunk({
|
||||
relays: [url],
|
||||
event: makePollResponse({event, selectedIds: selection}),
|
||||
delay: publishDelay,
|
||||
delay: pollType === "multiplechoice" ? 1000 : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const publishCurrentSelection = () => {
|
||||
const selection = pollType === "singlechoice" ? selectedIds.slice(0, 1) : selectedIds
|
||||
|
||||
if (selection.length === 0) {
|
||||
return pushToast({theme: "error", message: "Please select at least one option."})
|
||||
}
|
||||
|
||||
publishSelection(selection)
|
||||
}
|
||||
const publishCurrentSelection = () =>
|
||||
publishSelection(pollType === "singlechoice" ? selectedIds.slice(0, 1) : selectedIds)
|
||||
|
||||
const results = $derived(getPollResults(event, $responses))
|
||||
const ownResponse = $derived(getOwnResponse($responses))
|
||||
|
|
|
|||
Loading…
Reference in a new issue