Add image uploads to new post

This commit is contained in:
Jonathan Staab 2023-03-01 16:49:24 -06:00
parent f97d9f2171
commit 2f0317e8aa
13 changed files with 102 additions and 61 deletions

View File

@ -1,7 +1,7 @@
# Current # Current
- [ ] Strip zero width spaces from compose - [ ] Strip zero width spaces from compose
- [ ] Fix iOS - [ ] Fix iOS/safari/firefox
- [ ] Make the note relays button modal make sense, one relay with no explanation is not good - [ ] Make the note relays button modal make sense, one relay with no explanation is not good
# Image uploads # Image uploads
@ -28,6 +28,7 @@
# More # More
- [ ] Mute threads http://localhost:5173/nevent1qqsyz8x6r0cu7l6vwlcjhf8qhxyjtdykvuervkc3t3mfggse4qtwt0gpyfmhxue69uhkummnw3ezumrfvfjhyarpwdc8y6tddaexg6t4d5hxxmmdnhxvea
- [ ] Add webtorrent support - [ ] Add webtorrent support
- https://coracle.social/nevent1qqsxgxcsq5vevy4wdty5z5v88nhwp2fc5qgl0ws5rmamn6z72hwv3qcpyfmhxue69uhkummnw3ez6an9wf5kv6t9vsh8wetvd3hhyer9wghxuet5qk6c9q - https://coracle.social/nevent1qqsxgxcsq5vevy4wdty5z5v88nhwp2fc5qgl0ws5rmamn6z72hwv3qcpyfmhxue69uhkummnw3ez6an9wf5kv6t9vsh8wetvd3hhyer9wghxuet5qk6c9q
- [ ] Add coracle relay - [ ] Add coracle relay

View File

@ -3,72 +3,106 @@ import {get} from 'svelte/store'
import {error} from 'src/util/logger' import {error} from 'src/util/logger'
import {synced} from 'src/util/misc' import {synced} from 'src/util/misc'
const method = synced('agent/keys/method')
const pubkey = synced('agent/keys/pubkey') const pubkey = synced('agent/keys/pubkey')
const privkey = synced('agent/keys/privkey') const privkey = synced('agent/keys/privkey')
const getExtension = () => (window as {nostr?: any}).nostr const getExtension = () => (window as {nostr?: any}).nostr
const canSign = () => Boolean(getExtension() || get(privkey)) const canSign = () => ['privkey', 'extension'].includes(get(method))
const setPrivateKey = _privkey => { // For backwards compatibility, if method isn't set but we're logged in, set it
privkey.set(_privkey) setTimeout(() => {
pubkey.set(getPublicKey(_privkey)) method.update($method => {
} if ($method) {
return $method
}
const setPublicKey = _pubkey => { if (get(privkey)) {
pubkey.set(_pubkey) return 'privkey'
}
if (get(pubkey)) {
return getExtension() ? 'extension' : 'pubkey'
}
return null
})
}, 100)
const login = ($method, key) => {
method.set($method)
if ($method === 'privkey') {
privkey.set(key)
pubkey.set(getPublicKey(key))
} else {
privkey.set(null)
pubkey.set(key)
}
} }
const clear = () => { const clear = () => {
method.set(null)
pubkey.set(null) pubkey.set(null)
privkey.set(null) privkey.set(null)
} }
const sign = async event => { const sign = event => {
const ext = getExtension() const $method = get(method)
const key = get(privkey)
event.pubkey = get(pubkey) event.pubkey = get(pubkey)
event.id = getEventHash(event) event.id = getEventHash(event)
if (key) { if ($method === 'privkey') {
return Object.assign(event, { return Object.assign(event, {
sig: signEvent(event, get(privkey)), sig: signEvent(event, get(privkey)),
}) })
} else if (ext) {
return await ext.signEvent(event)
} else {
throw new Error('Unable to sign event')
} }
if ($method === 'extension') {
return getExtension().signEvent(event)
}
throw new Error(`Unable to sign event, method is ${$method}`)
} }
const getCrypt = () => { const getCrypt = () => {
const $privkey = get(privkey) const $method = get(method)
const nostr = getExtension()
if (!$privkey && !nostr) { if ($method === 'privkey') {
throw new Error('No encryption method available.') const $privkey = get(privkey)
return {
encrypt: (pubkey, message) => nip04.encrypt($privkey, pubkey, message),
decrypt: async (pubkey, message) => {
try {
return nip04.decrypt($privkey, pubkey, message)
} catch (e) {
error(e)
return `<Failed to decrypt message: ${e}>`
}
},
}
} }
return { if ($method === 'extension') {
encrypt: (pubkey, message) => { return {
return $privkey encrypt: (pubkey, message) => getExtension().nip04.encrypt(pubkey, message),
? nip04.encrypt($privkey, pubkey, message) decrypt: async (pubkey, message) => {
: nostr.nip04.encrypt(pubkey, message) try {
}, return await getExtension().nip04.decrypt(pubkey, message)
decrypt: async (pubkey, message) => { } catch (e) {
try { error(e)
return $privkey
? nip04.decrypt($privkey, pubkey, message)
: await nostr.nip04.decrypt(pubkey, message)
} catch (e) {
error(e)
return `<Failed to decrypt message: ${e}>` return `<Failed to decrypt message: ${e}>`
} }
}, },
}
} }
throw new Error('No encryption method available.')
} }
export default { export default {
pubkey, privkey, canSign, setPrivateKey, setPublicKey, clear, pubkey, privkey, canSign, login, clear, sign, getCrypt,
sign, getCrypt,
} }

View File

@ -21,18 +21,14 @@ export const loadAppData = async pubkey => {
} }
} }
export const login = ({privkey, pubkey}: {privkey?: string, pubkey?: string}) => { export const login = (method, key) => {
if (privkey) { keys.login(method, key)
keys.setPrivateKey(privkey)
} else {
keys.setPublicKey(pubkey)
}
modal.set({type: 'login/connect', noEscape: true}) modal.set({type: 'login/connect', noEscape: true})
} }
export const signup = privkey => { export const signup = privkey => {
keys.setPrivateKey(privkey) keys.login('privkey', privkey)
navigate('/notes/follows') navigate('/notes/follows')
} }

View File

@ -24,7 +24,7 @@
{/if} {/if}
{#if size === '2xl'} {#if size === '2xl'}
<div {...$$props} class={cx($$props.class, className, "p-4 pt-10 max-w-2xl")}> <div {...$$props} class={cx($$props.class, className, "p-4 max-w-2xl")}>
<slot /> <slot />
</div> </div>
{/if} {/if}

View File

@ -12,6 +12,7 @@
export let icon export let icon
export let maxWidth = null export let maxWidth = null
export let maxHeight = null export let maxHeight = null
export let hideInput = false
let input, file, listener, quote let input, file, listener, quote
let loading = false let loading = false
@ -55,9 +56,11 @@
</script> </script>
<div class="flex gap-2"> <div class="flex gap-2">
{#if !hideInput}
<Input type="text" wrapperClass="flex-grow" bind:value={value} placeholder="https://"> <Input type="text" wrapperClass="flex-grow" bind:value={value} placeholder="https://">
<i slot="before" class={`fa fa-${icon}`} /> <i slot="before" class={`fa fa-${icon}`} />
</Input> </Input>
{/if}
<Anchor type="button" on:click={() => { isOpen = true }}> <Anchor type="button" on:click={() => { isOpen = true }}>
<i class="fa fa-upload" /> <i class="fa fa-upload" />
</Anchor> </Anchor>

View File

@ -17,7 +17,7 @@
<div <div
class="fixed top-0 bg-dark flex justify-between items-center text-white w-full p-4 class="fixed top-0 bg-dark flex justify-between items-center text-white w-full p-4
border-b border-medium z-10" border-b border-medium z-10 h-16"
> >
<button class="lg:hidden fa fa-bars fa-2xl cursor-pointer" on:click={toggleMenu} /> <button class="lg:hidden fa fa-bars fa-2xl cursor-pointer" on:click={toggleMenu} />
<Anchor external type="unstyled" href="https://github.com/staab/coracle" class="flex items-center gap-2"> <Anchor external type="unstyled" href="https://github.com/staab/coracle" class="flex items-center gap-2">

View File

@ -12,7 +12,7 @@
const {nostr} = window as any const {nostr} = window as any
if (nostr) { if (nostr) {
login({pubkey: await nostr.getPublicKey()}) login('extension', await nostr.getPublicKey())
} else { } else {
modal.set({type: 'login/privkey'}) modal.set({type: 'login/privkey'})
} }

View File

@ -16,7 +16,7 @@
if (!privkey?.match(/[a-z0-9]{64}/)) { if (!privkey?.match(/[a-z0-9]{64}/)) {
toast.show("error", "Sorry, but that's an invalid private key.") toast.show("error", "Sorry, but that's an invalid private key.")
} else { } else {
login({privkey}) login('privkey', privkey)
} }
} }
</script> </script>

View File

@ -15,7 +15,7 @@
if (!pubkey?.match(/[a-z0-9]{64}/)) { if (!pubkey?.match(/[a-z0-9]{64}/)) {
toast.show("error", "Sorry, but that's an invalid public key.") toast.show("error", "Sorry, but that's an invalid public key.")
} else { } else {
login({pubkey}) login('pubkey', pubkey)
} }
} }
</script> </script>

View File

@ -8,6 +8,7 @@
import {displayPerson} from "src/util/nostr" import {displayPerson} from "src/util/nostr"
import Button from "src/partials/Button.svelte" import Button from "src/partials/Button.svelte"
import Compose from "src/partials/Compose.svelte" import Compose from "src/partials/Compose.svelte"
import ImageInput from "src/partials/ImageInput.svelte"
import Input from "src/partials/Input.svelte" import Input from "src/partials/Input.svelte"
import RelayCardSimple from "src/views/relays/RelayCardSimple.svelte" import RelayCardSimple from "src/views/relays/RelayCardSimple.svelte"
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
@ -21,6 +22,7 @@
export let pubkey = null export let pubkey = null
let image = null
let input = null let input = null
let relays = getUserWriteRelays() let relays = getUserWriteRelays()
let showSettings = false let showSettings = false
@ -38,6 +40,12 @@
) )
} }
$: {
if (image) {
input.type('\n' + image)
}
}
const onSubmit = async () => { const onSubmit = async () => {
const {content, mentions, topics} = input.parse() const {content, mentions, topics} = input.parse()
@ -99,7 +107,10 @@
<Compose bind:this={input} {onSubmit} /> <Compose bind:this={input} {onSubmit} />
</div> </div>
</div> </div>
<Button type="submit" class="text-center">Send</Button> <div class="flex gap-2">
<Button type="submit" class="text-center flex-grow">Send</Button>
<ImageInput bind:value={image} icon="image" hideInput />
</div>
<small <small
class="flex justify-end items-center gap-1 cursor-pointer" class="flex justify-end items-center gap-1 cursor-pointer"
on:click={() => { showSettings = true }}> on:click={() => { showSettings = true }}>

View File

@ -29,4 +29,5 @@
{/if} {/if}
</div> </div>
</Content> </Content>
<NewNoteButton /> <NewNoteButton />

View File

@ -57,7 +57,7 @@
actions.push({onClick: openAdvanced, label: 'Advanced', icon: 'sliders'}) actions.push({onClick: openAdvanced, label: 'Advanced', icon: 'sliders'})
} }
if (user.getPubkey() === pubkey) { if (user.getPubkey() === pubkey && $canPublish) {
actions.push({onClick: () => navigate('/profile'), label: 'Edit', icon: 'edit'}) actions.push({onClick: () => navigate('/profile'), label: 'Edit', icon: 'edit'})
} }
} }

View File

@ -7,11 +7,11 @@
import user from "src/agent/user" import user from "src/agent/user"
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays" import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
import database from "src/agent/database" import database from "src/agent/database"
import {routes, modal} from "src/app/ui" import {routes} from "src/app/ui"
export let pubkey export let pubkey
const {petnamePubkeys} = user const {petnamePubkeys, canPublish} = user
const getRelays = () => sampleRelays(getPubkeyWriteRelays(pubkey)) const getRelays = () => sampleRelays(getPubkeyWriteRelays(pubkey))
let following = false let following = false
@ -28,10 +28,6 @@
const unfollow = async () => { const unfollow = async () => {
user.removePetname(pubkey) user.removePetname(pubkey)
} }
const share = () => {
modal.set({type: 'person/share', person})
}
</script> </script>
<div class="flex flex-col gap-4 py-2 px-3 relative"> <div class="flex flex-col gap-4 py-2 px-3 relative">
@ -54,9 +50,7 @@
{/if} {/if}
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<Anchor class="tippy-close" type="button-circle" on:click={share}> {#if $canPublish}
<i class="fa fa-share-nodes" />
</Anchor>
{#if following} {#if following}
<Anchor type="button-circle" on:click={unfollow}> <Anchor type="button-circle" on:click={unfollow}>
<i class="fa fa-user-minus" /> <i class="fa fa-user-minus" />
@ -66,6 +60,7 @@
<i class="fa fa-user-plus" /> <i class="fa fa-user-plus" />
</Anchor> </Anchor>
{/if} {/if}
{/if}
</div> </div>
</div> </div>
<p>{@html renderContent(person?.kind0?.about || '')}</p> <p>{@html renderContent(person?.kind0?.about || '')}</p>