mirror of
https://github.com/aljazceru/hypergolic.git
synced 2026-02-05 05:34:22 +01:00
add blossom uploads
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
import { Input } from '$lib/components/ui/input/index.js';
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import * as Alert from '@/components/ui/alert';
|
||||
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
||||
import { ndk } from '@/ndk';
|
||||
import { currentUser } from '@/stores/session';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
@@ -15,6 +14,9 @@
|
||||
import CalculateSats from './CalculateSats.svelte';
|
||||
import { isGitHubIssuesOrPullUrl, parseProblem } from '@/helpers';
|
||||
import Login from './Login.svelte';
|
||||
import RichTextArea from './RichTextArea.svelte';
|
||||
import UploadMediaLink from './UploadMediaLink.svelte';
|
||||
import type { BlobDescriptor } from 'blossom-client-sdk';
|
||||
|
||||
export let rocket: Rocket;
|
||||
|
||||
@@ -102,6 +104,10 @@
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function handleUploaded(event: CustomEvent<BlobDescriptor>) {
|
||||
solution += `\n${event.detail.url}\n`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open>
|
||||
@@ -123,7 +129,7 @@
|
||||
<div class="grid gap-4 overflow-auto py-4">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="name" class="text-right">Problem</Label>
|
||||
<Textarea
|
||||
<RichTextArea
|
||||
bind:value={problem}
|
||||
id="name"
|
||||
placeholder="Describe the problem you solved, links to github are also acceptable"
|
||||
@@ -132,12 +138,15 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="desc" class="text-right">Solution (proof of work)</Label>
|
||||
<Textarea
|
||||
bind:value={solution}
|
||||
id="desc"
|
||||
placeholder="Link to your solution (e.g. a merged PR or some other evidence)"
|
||||
class="col-span-3 {validateSolution(solution) ? 'border-green-700' : 'border-red-600'}"
|
||||
/>
|
||||
<div class="col-span-3">
|
||||
<RichTextArea
|
||||
bind:value={solution}
|
||||
id="desc"
|
||||
placeholder="Link to your solution (e.g. a merged PR or some other evidence)"
|
||||
class={validateSolution(solution) ? 'border-green-700' : 'border-red-600'}
|
||||
/>
|
||||
<UploadMediaLink on:uploaded={handleUploaded}>Upload Image</UploadMediaLink>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="sats" class="text-right">Value of your work (Sats)</Label>
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
import { base } from '$app/paths';
|
||||
import { getRocketURL } from '@/helpers';
|
||||
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
||||
import UploadMediaLink from './UploadMediaLink.svelte';
|
||||
import type { BlobDescriptor } from 'blossom-client-sdk';
|
||||
|
||||
export let rocketEvent: NDKEvent;
|
||||
|
||||
@@ -48,6 +50,10 @@
|
||||
goto(`${base}/rockets/${getRocketURL(rocketEvent)}`);
|
||||
});
|
||||
}
|
||||
|
||||
function handleUploaded(event: CustomEvent<BlobDescriptor>) {
|
||||
image = event.detail.url;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open={o}>
|
||||
@@ -86,7 +92,10 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="image" class="text-right">Cover Image</Label>
|
||||
<Input bind:value={image} id="name" placeholder="URL of cover image" class="col-span-3" />
|
||||
<div class="col-span-3">
|
||||
<Input bind:value={image} id="name" placeholder="URL of cover image" />
|
||||
<UploadMediaLink on:uploaded={handleUploaded}>Upload</UploadMediaLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Dialog.Footer>
|
||||
|
||||
48
src/components/RichTextArea.svelte
Normal file
48
src/components/RichTextArea.svelte
Normal file
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
||||
import Textarea from '../lib/components/ui/textarea/textarea.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fetchUsersServers, uploadBlob, userServers } from '../lib/blossom/servers';
|
||||
import { ndk } from '../lib/ndk';
|
||||
|
||||
type $$Props = HTMLTextareaAttributes;
|
||||
|
||||
export let value: $$Props['value'] = undefined;
|
||||
|
||||
// NOTE: this is kind of hacky and should be moved to some kind of "login" event
|
||||
onMount(() => {
|
||||
$ndk.signer?.user().then((user) => {
|
||||
if ($userServers.length === 0 && $ndk.signer) fetchUsersServers(user.pubkey);
|
||||
});
|
||||
});
|
||||
|
||||
const handlePaste = async (event: ClipboardEvent) => {
|
||||
const clipboardData = event.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
// find the first image item
|
||||
const item = Array.from(clipboardData.items).find((item) => item.type.startsWith('image/'));
|
||||
if (!item) return;
|
||||
|
||||
try {
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
const blob = await uploadBlob(file, $userServers);
|
||||
|
||||
// Insert the image URL into the textarea
|
||||
const insert = blob.url;
|
||||
const textarea = event.target as HTMLTextAreaElement;
|
||||
const startPos = textarea.selectionStart;
|
||||
const endPos = textarea.selectionEnd;
|
||||
|
||||
const current = String(value);
|
||||
value = current.substring(0, startPos) + insert + current.substring(endPos);
|
||||
}
|
||||
event.preventDefault();
|
||||
} catch (error) {
|
||||
if (error instanceof Error) alert(`Image upload failed: ${error.message}`);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Textarea bind:value on:paste={handlePaste} {...$$restProps} />
|
||||
46
src/components/UploadMediaLink.svelte
Normal file
46
src/components/UploadMediaLink.svelte
Normal file
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { fetchUsersServers, uploadBlob, userServers } from '../lib/blossom/servers';
|
||||
import { ndk } from '../lib/ndk';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let uploading = false;
|
||||
let uploadInput: HTMLInputElement;
|
||||
|
||||
// NOTE: this is kind of hacky and should be moved to some kind of "login" event
|
||||
onMount(() => {
|
||||
$ndk.signer?.user().then((user) => {
|
||||
if ($userServers.length === 0 && $ndk.signer) fetchUsersServers(user.pubkey);
|
||||
});
|
||||
});
|
||||
|
||||
const handleUpload = async (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
|
||||
const file = event.currentTarget.files?.item(0);
|
||||
|
||||
if (file) {
|
||||
try {
|
||||
dispatch('uploading');
|
||||
const blob = await uploadBlob(file, $userServers);
|
||||
dispatch('uploaded', blob);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) alert(`Failed to upload image: ${error.message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<input
|
||||
bind:this={uploadInput}
|
||||
type="file"
|
||||
class="hidden"
|
||||
on:change={handleUpload}
|
||||
disabled={uploading || null}
|
||||
/>
|
||||
<button on:click={() => uploadInput.click()} class="hover:underline">
|
||||
{#if uploading}
|
||||
Uploading...
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
</button>
|
||||
Reference in New Issue
Block a user