mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-17 13:34:22 +01:00
add blossom uploads
This commit is contained in:
62
package-lock.json
generated
62
package-lock.json
generated
@@ -19,6 +19,7 @@
|
|||||||
"bitcoin-address-validation": "^2.2.3",
|
"bitcoin-address-validation": "^2.2.3",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.10",
|
||||||
"bloomfilter": "^0.0.18",
|
"bloomfilter": "^0.0.18",
|
||||||
|
"blossom-client-sdk": "^0.9.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk-sv": "^0.0.17",
|
"cmdk-sv": "^0.0.17",
|
||||||
"embla-carousel-svelte": "^8.1.3",
|
"embla-carousel-svelte": "^8.1.3",
|
||||||
@@ -1888,6 +1889,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bloomfilter/-/bloomfilter-0.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/bloomfilter/-/bloomfilter-0.0.18.tgz",
|
||||||
"integrity": "sha512-CbnyHE78gY1tpXS/Ap+B0RJxKdRWCDzjBnX97UJSG8rdLv1PK8GiTWc/CCQyWu6PWVD4lUceeFrqC6Mf3nMgOA=="
|
"integrity": "sha512-CbnyHE78gY1tpXS/Ap+B0RJxKdRWCDzjBnX97UJSG8rdLv1PK8GiTWc/CCQyWu6PWVD4lUceeFrqC6Mf3nMgOA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/blossom-client-sdk": {
|
||||||
|
"version": "0.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/blossom-client-sdk/-/blossom-client-sdk-0.9.1.tgz",
|
||||||
|
"integrity": "sha512-lEZ4uNzM09rhp7mjzmgLDC3OEgFd76GkmR90fye/IT5HVCofIT6ldBfyqBY9DcuM1S+XNa1Cu14wFg95CyH8Ag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "^1.4.0",
|
||||||
|
"cross-fetch": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@@ -2259,6 +2270,35 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-fetch": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-fetch/node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
@@ -5480,6 +5520,12 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/trough": {
|
"node_modules/trough": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
|
||||||
@@ -5823,6 +5869,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/webln": {
|
"node_modules/webln": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/webln/-/webln-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/webln/-/webln-0.3.2.tgz",
|
||||||
@@ -5869,6 +5921,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
"bitcoin-address-validation": "^2.2.3",
|
"bitcoin-address-validation": "^2.2.3",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.10",
|
||||||
"bloomfilter": "^0.0.18",
|
"bloomfilter": "^0.0.18",
|
||||||
|
"blossom-client-sdk": "^0.9.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk-sv": "^0.0.17",
|
"cmdk-sv": "^0.0.17",
|
||||||
"embla-carousel-svelte": "^8.1.3",
|
"embla-carousel-svelte": "^8.1.3",
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import { Input } from '$lib/components/ui/input/index.js';
|
import { Input } from '$lib/components/ui/input/index.js';
|
||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
import * as Alert from '@/components/ui/alert';
|
import * as Alert from '@/components/ui/alert';
|
||||||
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
|
||||||
import { ndk } from '@/ndk';
|
import { ndk } from '@/ndk';
|
||||||
import { currentUser } from '@/stores/session';
|
import { currentUser } from '@/stores/session';
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
@@ -15,6 +14,9 @@
|
|||||||
import CalculateSats from './CalculateSats.svelte';
|
import CalculateSats from './CalculateSats.svelte';
|
||||||
import { isGitHubIssuesOrPullUrl, parseProblem } from '@/helpers';
|
import { isGitHubIssuesOrPullUrl, parseProblem } from '@/helpers';
|
||||||
import Login from './Login.svelte';
|
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;
|
export let rocket: Rocket;
|
||||||
|
|
||||||
@@ -102,6 +104,10 @@
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUploaded(event: CustomEvent<BlobDescriptor>) {
|
||||||
|
solution += `\n${event.detail.url}\n`;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog.Root bind:open>
|
<Dialog.Root bind:open>
|
||||||
@@ -123,7 +129,7 @@
|
|||||||
<div class="grid gap-4 overflow-auto py-4">
|
<div class="grid gap-4 overflow-auto py-4">
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="name" class="text-right">Problem</Label>
|
<Label for="name" class="text-right">Problem</Label>
|
||||||
<Textarea
|
<RichTextArea
|
||||||
bind:value={problem}
|
bind:value={problem}
|
||||||
id="name"
|
id="name"
|
||||||
placeholder="Describe the problem you solved, links to github are also acceptable"
|
placeholder="Describe the problem you solved, links to github are also acceptable"
|
||||||
@@ -132,12 +138,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="desc" class="text-right">Solution (proof of work)</Label>
|
<Label for="desc" class="text-right">Solution (proof of work)</Label>
|
||||||
<Textarea
|
<div class="col-span-3">
|
||||||
|
<RichTextArea
|
||||||
bind:value={solution}
|
bind:value={solution}
|
||||||
id="desc"
|
id="desc"
|
||||||
placeholder="Link to your solution (e.g. a merged PR or some other evidence)"
|
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'}"
|
class={validateSolution(solution) ? 'border-green-700' : 'border-red-600'}
|
||||||
/>
|
/>
|
||||||
|
<UploadMediaLink on:uploaded={handleUploaded}>Upload Image</UploadMediaLink>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="sats" class="text-right">Value of your work (Sats)</Label>
|
<Label for="sats" class="text-right">Value of your work (Sats)</Label>
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
import { getRocketURL } from '@/helpers';
|
import { getRocketURL } from '@/helpers';
|
||||||
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
||||||
|
import UploadMediaLink from './UploadMediaLink.svelte';
|
||||||
|
import type { BlobDescriptor } from 'blossom-client-sdk';
|
||||||
|
|
||||||
export let rocketEvent: NDKEvent;
|
export let rocketEvent: NDKEvent;
|
||||||
|
|
||||||
@@ -48,6 +50,10 @@
|
|||||||
goto(`${base}/rockets/${getRocketURL(rocketEvent)}`);
|
goto(`${base}/rockets/${getRocketURL(rocketEvent)}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUploaded(event: CustomEvent<BlobDescriptor>) {
|
||||||
|
image = event.detail.url;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog.Root bind:open={o}>
|
<Dialog.Root bind:open={o}>
|
||||||
@@ -86,7 +92,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="image" class="text-right">Cover Image</Label>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Footer>
|
<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>
|
||||||
33
src/lib/blossom/pasteImage.ts
Normal file
33
src/lib/blossom/pasteImage.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
export function pasteImage(node: HTMLTextAreaElement) {
|
||||||
|
const handlePaste = (event: ClipboardEvent) => {
|
||||||
|
const clipboardData = event.clipboardData;
|
||||||
|
if (!clipboardData) return;
|
||||||
|
|
||||||
|
const items = clipboardData.items;
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
|
if (item.type.startsWith('image/')) {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
// Create a URL for the image file
|
||||||
|
const imageUrl = URL.createObjectURL(file);
|
||||||
|
// Insert the image URL into the textarea
|
||||||
|
node.value += `\n\n`;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attach the paste event listener
|
||||||
|
node.addEventListener('paste', handlePaste);
|
||||||
|
|
||||||
|
// Clean up the event listener on unmount
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('paste', handlePaste);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
37
src/lib/blossom/servers.ts
Normal file
37
src/lib/blossom/servers.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { get, writable } from 'svelte/store';
|
||||||
|
import { BlossomClient, getServersFromServerListEvent } from 'blossom-client-sdk';
|
||||||
|
import { ndk } from '../ndk';
|
||||||
|
import { signEventTemplate } from './signer';
|
||||||
|
|
||||||
|
export const userServers = writable<URL[]>([]);
|
||||||
|
|
||||||
|
export async function fetchUsersServers(pubkey: string) {
|
||||||
|
const ndkSvelte = get(ndk);
|
||||||
|
|
||||||
|
const event = await ndkSvelte.fetchEvent({ kinds: [10063 as number], authors: [pubkey] });
|
||||||
|
if (!event) userServers.set([]);
|
||||||
|
else userServers.set(getServersFromServerListEvent(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadBlob(file: File, servers?: URL[]) {
|
||||||
|
if (!servers) servers = get(userServers);
|
||||||
|
if (servers.length === 0) throw new Error('User does not have any blossom servers');
|
||||||
|
|
||||||
|
const sha256 = await BlossomClient.getFileSha256(file);
|
||||||
|
const auth = await BlossomClient.createUploadAuth(sha256, signEventTemplate, 'Upload Image');
|
||||||
|
const blob = await BlossomClient.uploadBlob(servers[0], file, auth);
|
||||||
|
|
||||||
|
// mirror blob to other servers in background
|
||||||
|
for (const server of servers.slice(1)) {
|
||||||
|
BlossomClient.mirrorBlob(server, blob.url, auth)
|
||||||
|
.catch((err) => {
|
||||||
|
// not all servers support mirroring, so attempt to upload
|
||||||
|
return BlossomClient.uploadBlob(server, file, auth);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// upload filed, ignore error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
16
src/lib/blossom/signer.ts
Normal file
16
src/lib/blossom/signer.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { get } from 'svelte/store';
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
|
import type { EventTemplate, SignedEvent } from 'blossom-client-sdk';
|
||||||
|
|
||||||
|
import { ndk } from '../ndk';
|
||||||
|
|
||||||
|
export async function signEventTemplate(template: EventTemplate): Promise<SignedEvent> {
|
||||||
|
const _ndk = get(ndk);
|
||||||
|
const e = new NDKEvent(_ndk);
|
||||||
|
e.kind = template.kind;
|
||||||
|
e.content = template.content;
|
||||||
|
e.tags = template.tags;
|
||||||
|
e.created_at = template.created_at;
|
||||||
|
await e.sign();
|
||||||
|
return e.rawEvent() as SignedEvent;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user