mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Add basic notes
This commit is contained in:
parent
586b7853ac
commit
20f8d52c78
@ -1,7 +1,10 @@
|
||||
Bugs
|
||||
|
||||
- [ ] Remove dexie, or use it instead of localstorage for cached data
|
||||
- [ ] Add redirect to /notes, ditch / route
|
||||
- [ ] Remove hack for re-rendering rooms on url change
|
||||
- [ ] Memoize room list, every time the user switches chat rooms it pulls the full list
|
||||
- [ ] Fix toast, it gets in the way. Make it smaller and dismissable.
|
||||
|
||||
Features
|
||||
|
||||
|
@ -13,8 +13,8 @@
|
||||
import Profile from "src/routes/Profile.svelte"
|
||||
import RelayList from "src/routes/RelayList.svelte"
|
||||
import UserDetail from "src/routes/UserDetail.svelte"
|
||||
import NoteCreate from "src/routes/NoteCreate.svelte"
|
||||
import Chat from "src/routes/Chat.svelte"
|
||||
import ChatCreate from "src/routes/ChatCreate.svelte"
|
||||
import ChatRoom from "src/routes/ChatRoom.svelte"
|
||||
import ChatEdit from "src/routes/ChatEdit.svelte"
|
||||
|
||||
@ -49,14 +49,16 @@
|
||||
<div use:links class="h-full">
|
||||
<div class="pt-16 text-white h-full">
|
||||
<Route path="/" component={Feed} />
|
||||
<Route path="/login" component={Login} />
|
||||
<Route path="/relays" component={RelayList} />
|
||||
<Route path="/notes" component={Feed} />
|
||||
<Route path="/notes/new" component={NoteCreate} />
|
||||
<Route path="/chat" component={Chat} />
|
||||
<Route path="/chat/new" component={ChatCreate} />
|
||||
<Route path="/chat/new" component={ChatEdit} />
|
||||
<Route path="/chat/:room" component={ChatRoom} />
|
||||
<Route path="/chat/:room/edit" component={ChatEdit} />
|
||||
<Route path="/user/:pubkey" component={UserDetail} />
|
||||
<Route path="/settings/relays" component={RelayList} />
|
||||
<Route path="/settings/profile" component={Profile} />
|
||||
<Route path="/login" component={Login} />
|
||||
</div>
|
||||
|
||||
<ul
|
||||
@ -71,20 +73,10 @@
|
||||
style="background-image: url({$user.picture})" />
|
||||
<span class="text-lg font-bold">{$user.name}</span>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/user/{$user.pubkey}">
|
||||
<i class="fa-solid fa-user-astronaut mr-2" /> Profile
|
||||
</a>
|
||||
</li>
|
||||
{/if}
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/">
|
||||
<i class="fa-solid fa-home mr-2" /> Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/relays">
|
||||
<i class="fa-solid fa-server mr-2" /> Relays
|
||||
<i class="fa-solid fa-tag mr-2" /> Notes
|
||||
</a>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
@ -92,7 +84,18 @@
|
||||
<i class="fa-solid fa-message mr-2" /> Chat
|
||||
</a>
|
||||
</li>
|
||||
<li class="h-px mx-3 my-4 bg-medium" />
|
||||
{#if $user}
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/user/{$user.pubkey}">
|
||||
<i class="fa-solid fa-user-astronaut mr-2" /> Profile
|
||||
</a>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/settings/relays">
|
||||
<i class="fa-solid fa-server mr-2" /> Relays
|
||||
</a>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" on:click={logout}>
|
||||
<i class="fa-solid fa-right-from-bracket mr-2" /> Logout
|
||||
|
67
src/partials/chat/RoomList.svelte
Normal file
67
src/partials/chat/RoomList.svelte
Normal file
@ -0,0 +1,67 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte'
|
||||
import {writable} from 'svelte/store'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {nostr} from 'src/state/nostr'
|
||||
import {rooms} from 'src/state/app'
|
||||
import Input from "src/partials/Input.svelte"
|
||||
|
||||
let q = ""
|
||||
let search
|
||||
|
||||
$: search = fuzzy(Object.values($rooms), {keys: ["name", "about"]})
|
||||
|
||||
const createRoom = () => navigate(`/chat/new`)
|
||||
|
||||
// TODO hack: there should be a way to re-render a route when the url changes
|
||||
const setRoom = id => {
|
||||
navigate(`/chat`)
|
||||
setTimeout(() => navigate(`/chat/${id}`))
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const sub = nostr.sub({
|
||||
filter: {kinds: [40, 41]},
|
||||
cb: e => {
|
||||
const id = e.kind === 40 ? e.id : e.tags[0][1]
|
||||
|
||||
$rooms[id] = {id, pubkey: e.pubkey, ...$rooms[id], ...JSON.parse(e.content)}
|
||||
},
|
||||
})
|
||||
|
||||
return () => sub.unsub()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="hidden sm:flex flex-col bg-dark w-56 h-full fixed py-8 border-r border-solid border-r-medium">
|
||||
<div class="m-4">
|
||||
<Input bind:value={q} type="text" placeholder="Search rooms">
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
</div>
|
||||
<ul>
|
||||
{#each search(q).slice(0, 10) as r}
|
||||
<li
|
||||
class="flex flex-col gap-2 px-3 py-2 cursor-pointer hover:bg-accent transition-all"
|
||||
on:click={() => setRoom(r.id)}>
|
||||
<div class="flex gap-2 items-center">
|
||||
<div
|
||||
class="overflow-hidden w-4 h-4 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({r.picture})" />
|
||||
<span class="font-bold">{r.name}</span>
|
||||
</div>
|
||||
{#if r.about}
|
||||
<small class="pl-6 text-light whitespace-nowrap text-ellipsis overflow-hidden">
|
||||
{r.about}
|
||||
</small>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
<li class="bg-medium m-3 h-px" />
|
||||
<li class="cursor-pointer font-bold hover:bg-accent transition-all px-3 py-2" on:click={createRoom}>
|
||||
<i class="fa-solid fa-plus" /> Create Room
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
90
src/routes/ChatEdit.svelte
Normal file
90
src/routes/ChatEdit.svelte
Normal file
@ -0,0 +1,90 @@
|
||||
<script>
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from 'svelte/transition'
|
||||
import {navigate} from "svelte-routing"
|
||||
import pick from "ramda/src/pick"
|
||||
import {stripExifData} from "src/util/html"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Select from "src/partials/Select.svelte"
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import RoomList from "src/partials/chat/RoomList.svelte"
|
||||
import {user} from "src/state/user"
|
||||
import {rooms} from "src/state/app"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import toast from "src/state/toast"
|
||||
|
||||
export let room
|
||||
|
||||
let values = $rooms[room] || {}
|
||||
|
||||
onMount(async () => {
|
||||
document.querySelector('[name=picture]').addEventListener('change', async e => {
|
||||
const [file] = e.target.files
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => values.picture = reader.result
|
||||
reader.onerror = e => console.error(e)
|
||||
reader.readAsDataURL(await stripExifData(file))
|
||||
} else {
|
||||
values.picture = null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const submit = async e => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!values.name.match(/^\w[\w\-]+\w$/)) {
|
||||
toast.show("error", "Names must be comprised of letters, numbers, and dashes only.")
|
||||
} else {
|
||||
const event = await dispatch(values.id ? "room/update" : "room/create", values)
|
||||
|
||||
toast.show("info", `Your room has been ${values.id ? 'updated' : 'created'}!`)
|
||||
|
||||
navigate(`/chat/${room}`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-4 h-full">
|
||||
<div class="sm:ml-56 w-full">
|
||||
<form on:submit={submit} class="flex justify-center py-8 px-4" in:fly={{y: 20}}>
|
||||
<div class="flex flex-col gap-4 max-w-2xl">
|
||||
<div class="flex justify-center items-center flex-col mb-4">
|
||||
<h1 class="staatliches text-6xl">Name your room</h1>
|
||||
</div>
|
||||
<div class="flex flex-col gap-8 w-full">
|
||||
<div class="flex flex-col gap-1">
|
||||
<strong>Room name</strong>
|
||||
<Input type="text" name="name" wrapperClass="flex-grow" bind:value={values.name}>
|
||||
<i slot="before" class="fa-solid fa-tag" />
|
||||
</Input>
|
||||
<p class="text-sm text-light">
|
||||
The room's name can be changed anytime.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<strong>Room information</strong>
|
||||
<Textarea name="about" bind:value={values.about} />
|
||||
<p class="text-sm text-light">
|
||||
Give people an idea of what kind of conversations will be happening here.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<strong>Picture</strong>
|
||||
<input type="file" name="picture" />
|
||||
<p class="text-sm text-light">
|
||||
A picture to help people remember your room.
|
||||
</p>
|
||||
</div>
|
||||
<Button type="submit" class="text-center">Done</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<RoomList />
|
||||
</div>
|
||||
|
169
src/routes/ChatRoom.svelte
Normal file
169
src/routes/ChatRoom.svelte
Normal file
@ -0,0 +1,169 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {prop, last} from 'ramda'
|
||||
import {switcherFn} from 'hurdak/src/core'
|
||||
import {nostr} from 'src/state/nostr'
|
||||
import {rooms, accounts} from 'src/state/app'
|
||||
import {dispatch} from 'src/state/dispatch'
|
||||
import {user} from 'src/state/user'
|
||||
import RoomList from "src/partials/chat/RoomList.svelte"
|
||||
|
||||
export let room
|
||||
|
||||
let textarea
|
||||
let messages = []
|
||||
let annotatedMessages = []
|
||||
|
||||
$: {
|
||||
// Group messages so we're only showing the account once per chunk
|
||||
annotatedMessages = messages.reduce(
|
||||
(mx, m) => {
|
||||
const account = $accounts[m.pubkey]
|
||||
|
||||
// If we don't have an account yet, don't show the message
|
||||
if (!account) {
|
||||
return mx
|
||||
}
|
||||
|
||||
return mx.concat({
|
||||
...m,
|
||||
account,
|
||||
showAccount: account !== prop('account', last(mx)),
|
||||
})
|
||||
},
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const isVisible = $el => {
|
||||
const bodyRect = document.body.getBoundingClientRect()
|
||||
const {top, height} = $el.getBoundingClientRect()
|
||||
|
||||
return top + height < bodyRect.height
|
||||
}
|
||||
|
||||
const sub = nostr.sub({
|
||||
filter: {kinds: [42, 43, 44], '#e': [room]},
|
||||
cb: e => {
|
||||
switcherFn(e.kind, {
|
||||
42: () => {
|
||||
const $prevListItem = last(document.querySelectorAll('.chat-message'))
|
||||
|
||||
messages = messages.concat(e)
|
||||
|
||||
if (!$accounts[e.pubkey]) {
|
||||
const accountSub = nostr.sub({
|
||||
filter: {kinds: [0], authors: [e.pubkey]},
|
||||
cb: e => {
|
||||
$accounts[e.pubkey] = {
|
||||
...$accounts[e.pubkey],
|
||||
...JSON.parse(e.content),
|
||||
}
|
||||
|
||||
accountSub.unsub()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if ($prevListItem && isVisible($prevListItem)) {
|
||||
setTimeout(() => {
|
||||
const $li = last(document.querySelectorAll('.chat-message'))
|
||||
|
||||
$li.scrollIntoView({behavior: "smooth"})
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
43: () => null,
|
||||
44: () => null,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return () => sub.unsub()
|
||||
})
|
||||
|
||||
const edit = () => {
|
||||
navigate(`/chat/${room}/edit`)
|
||||
}
|
||||
|
||||
const sendMessage = () => {
|
||||
const content = textarea.value.trim()
|
||||
|
||||
if (content) {
|
||||
textarea.value = ''
|
||||
|
||||
dispatch("message/create", room, content)
|
||||
}
|
||||
}
|
||||
|
||||
const onKeyPress = e => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
sendMessage()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="flex gap-4 h-full">
|
||||
<div class="sm:ml-56 w-full">
|
||||
<div class="relative">
|
||||
<div class="flex flex-col pt-20 pb-32">
|
||||
<ul class="p-4 max-h-full flex-grow">
|
||||
{#each annotatedMessages as m}
|
||||
<li in:fly={{y: 20}} class="py-1 chat-message">
|
||||
{#if m.showAccount}
|
||||
<div class="flex gap-2 items-center mt-2">
|
||||
<div
|
||||
class="overflow-hidden w-4 h-4 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({m.account.picture})" />
|
||||
<span class="text-lg font-bold">{m.account.name}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ml-6">{m.content}</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{#if $rooms[room]}
|
||||
<div class="fixed top-0 pt-20 w-full -ml-56 pl-60 p-4 border-b border-solid border-medium bg-dark flex gap-4">
|
||||
<div
|
||||
class="overflow-hidden w-12 h-12 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({$rooms[room].picture})" />
|
||||
<div class="w-full">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div class="text-lg font-bold">{$rooms[room].name}</div>
|
||||
{#if $rooms[room].pubkey === $user.pubkey}
|
||||
<small class="cursor-pointer" on:click={edit}>
|
||||
<i class="fa-solid fa-edit" /> Edit
|
||||
</small>
|
||||
{/if}
|
||||
</div>
|
||||
<div>{$rooms[room].about || ''}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="fixed bottom-0 w-full -ml-56 pl-56 flex bg-light border-medium border-t border-solid border-dark">
|
||||
<textarea
|
||||
rows="4"
|
||||
autofocus
|
||||
placeholder="Type something..."
|
||||
bind:this={textarea}
|
||||
on:keypress={onKeyPress}
|
||||
class="w-full p-2 text-black bg-light
|
||||
placeholder:text-medium outline-0 resize-none" />
|
||||
<div
|
||||
on:click={sendMessage}
|
||||
class="flex flex-col py-8 p-4 justify-center gap-2 border-l border-solid border-dark
|
||||
hover:bg-accent transition-all cursor-pointer text-black ">
|
||||
<i class="fa-solid fa-paper-plane fa-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RoomList />
|
||||
</div>
|
||||
|
@ -1,39 +1,101 @@
|
||||
<script>
|
||||
import {liveQuery} from "dexie"
|
||||
import {onMount} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {prop, last} from 'ramda'
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import {nostr, relays} from "src/state/nostr"
|
||||
import {user} from "src/state/user"
|
||||
import {accounts} from "src/state/app"
|
||||
import {db} from "src/state/db"
|
||||
|
||||
const relays = liveQuery(() => db.relays.toArray())
|
||||
let notes = []
|
||||
let annotatedNotes = []
|
||||
|
||||
const createPost = () => {
|
||||
if ($user) {
|
||||
navigate("/post/new")
|
||||
} else {
|
||||
navigate("/login")
|
||||
}
|
||||
$: {
|
||||
// Group notes so we're only showing the account once per chunk
|
||||
annotatedNotes = notes.reduce(
|
||||
(mx, m) => {
|
||||
const account = $accounts[m.pubkey]
|
||||
|
||||
// If we don't have an account yet, don't show the message
|
||||
if (!account) {
|
||||
return mx
|
||||
}
|
||||
|
||||
return mx.concat({
|
||||
...m,
|
||||
account,
|
||||
showAccount: account !== prop('account', last(mx)),
|
||||
})
|
||||
},
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
const createNote = () => {
|
||||
navigate("/notes/new")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const sub = nostr.sub({
|
||||
filter: {kinds: [1], since: new Date().valueOf() / 1000 - 7 * 24 * 60 * 60},
|
||||
cb: e => {
|
||||
notes = notes.concat(e)
|
||||
|
||||
if (!$accounts[e.pubkey]) {
|
||||
const accountSub = nostr.sub({
|
||||
filter: {kinds: [0], authors: [e.pubkey]},
|
||||
cb: e => {
|
||||
$accounts[e.pubkey] = {
|
||||
...$accounts[e.pubkey],
|
||||
...JSON.parse(e.content),
|
||||
}
|
||||
|
||||
accountSub.unsub()
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return () => sub.unsub()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if $relays && $relays.length > 0}
|
||||
feed
|
||||
<div class="fixed bottom-0 right-0 p-8">
|
||||
<div
|
||||
class="rounded-full bg-accent color-white w-16 h-16 flex justify-center
|
||||
items-center border border-dark shadow-2xl cursor-pointer"
|
||||
on:click={createPost}
|
||||
>
|
||||
<span class="fa-sold fa-plus fa-2xl" />
|
||||
</div>
|
||||
<ul class="p-2">
|
||||
{#each annotatedNotes as n}
|
||||
<li in:fly={{y: 20}} class="py-1 chat-message">
|
||||
{#if n.showAccount}
|
||||
<div class="flex gap-2 items-center mt-2">
|
||||
<div
|
||||
class="overflow-hidden w-4 h-4 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({n.account.picture})" />
|
||||
<span class="text-lg font-bold">{n.account.name}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ml-6">{n.content}</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
{#if $relays.length > 0}
|
||||
<div class="fixed bottom-0 right-0 p-8">
|
||||
<div
|
||||
class="rounded-full bg-accent color-white w-16 h-16 flex justify-center
|
||||
items-center border border-dark shadow-2xl cursor-pointer"
|
||||
on:click={createNote}
|
||||
>
|
||||
<span class="fa-sold fa-plus fa-2xl" />
|
||||
</div>
|
||||
{:else if $relays}
|
||||
<div class="flex w-full justify-center items-center py-16">
|
||||
<div class="text-center max-w-md">
|
||||
You aren't yet connected to any relays. Please click <Anchor href="/relays"
|
||||
>here</Anchor
|
||||
> to get started.
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex w-full justify-center items-center py-16">
|
||||
<div class="text-center max-w-md">
|
||||
You aren't yet connected to any relays. Please click <Anchor href="/settings/relays"
|
||||
>here</Anchor
|
||||
> to get started.
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
39
src/routes/NoteCreate.svelte
Normal file
39
src/routes/NoteCreate.svelte
Normal file
@ -0,0 +1,39 @@
|
||||
<script>
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from 'svelte/transition'
|
||||
import {navigate} from "svelte-routing"
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import toast from "src/state/toast"
|
||||
|
||||
let values = {}
|
||||
|
||||
const submit = async e => {
|
||||
e.preventDefault()
|
||||
|
||||
const event = await dispatch("note/create", values.content)
|
||||
|
||||
toast.show("info", `Your note has been created!`)
|
||||
|
||||
navigate('/notes')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="m-auto">
|
||||
<form on:submit={submit} class="flex justify-center py-8 px-4" in:fly={{y: 20}}>
|
||||
<div class="flex flex-col gap-4 max-w-lg w-full">
|
||||
<div class="flex justify-center items-center flex-col mb-4">
|
||||
<h1 class="staatliches text-6xl">Create a note</h1>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<div class="flex flex-col gap-1">
|
||||
<strong>What do you want to say?</strong>
|
||||
<Textarea rows="8" name="content" bind:value={values.content} />
|
||||
</div>
|
||||
<Button type="submit" class="text-center">Send</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -5,11 +5,12 @@
|
||||
import {uniqBy, prop} from 'ramda'
|
||||
import {switcherFn} from 'hurdak/src/core'
|
||||
import {nostr} from 'src/state/nostr'
|
||||
import {user} from 'src/state/user'
|
||||
import {user as currentUser} from 'src/state/user'
|
||||
import {accounts} from 'src/state/app'
|
||||
|
||||
export let pubkey
|
||||
|
||||
let userData
|
||||
let user
|
||||
let notes = []
|
||||
|
||||
onMount(() => {
|
||||
@ -18,7 +19,11 @@
|
||||
cb: e => {
|
||||
switcherFn(e.kind, {
|
||||
[0]: () => {
|
||||
userData = JSON.parse(e.content)
|
||||
user = JSON.parse(e.content)
|
||||
|
||||
// Take this opportunity to sync account data. TODO this is a hack,
|
||||
// we should by syncing and caching everywhere we grab accounts
|
||||
$accounts[pubkey] = user
|
||||
},
|
||||
[1]: () => {
|
||||
notes = uniqBy(prop('id'), notes.concat(e))
|
||||
@ -41,23 +46,23 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if userData}
|
||||
{#if user}
|
||||
<div class="max-w-2xl m-auto flex flex-col gap-4 py-8 px-4">
|
||||
<div class="flex flex-col gap-4" in:fly={{y: 20}}>
|
||||
<div class="flex gap-4">
|
||||
<div
|
||||
class="overflow-hidden w-12 h-12 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({userData.picture})" />
|
||||
style="background-image: url({user.picture})" />
|
||||
<div class="flex-grow">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl">{userData.name}</h1>
|
||||
{#if $user?.pubkey === pubkey}
|
||||
<h1 class="text-2xl">{user.name}</h1>
|
||||
{#if $currentUser?.pubkey === pubkey}
|
||||
<a href="/settings/profile" class="cursor-pointer text-sm">
|
||||
<i class="fa-solid fa-edit" /> Edit
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<p>{userData.about || ''}</p>
|
||||
<p>{user.about || ''}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
21
src/state/app.js
Normal file
21
src/state/app.js
Normal file
@ -0,0 +1,21 @@
|
||||
import {writable} from 'svelte/store'
|
||||
import {getLocalJson, setLocalJson} from "src/util/misc"
|
||||
import {user} from 'src/state/user'
|
||||
|
||||
export const rooms = writable(getLocalJson("coracle/rooms") || {})
|
||||
|
||||
rooms.subscribe($rooms => {
|
||||
setLocalJson("coracle/rooms", $rooms)
|
||||
})
|
||||
|
||||
export const accounts = writable(getLocalJson("coracle/accounts") || {})
|
||||
|
||||
accounts.subscribe($accounts => {
|
||||
setLocalJson("coracle/accounts", $accounts)
|
||||
})
|
||||
|
||||
user.subscribe($user => {
|
||||
if ($user) {
|
||||
accounts.update($accounts => ({...$accounts, [$user.pubkey]: $user}))
|
||||
}
|
||||
})
|
@ -60,7 +60,7 @@ dispatch.addMethod("room/update", async (topic, {id, ...room}) => {
|
||||
return event
|
||||
})
|
||||
|
||||
dispatch.addMethod("message/create", async (topic, roomId, content, type = "root") => {
|
||||
dispatch.addMethod("message/create", async (topic, roomId, content) => {
|
||||
const [relay] = get(relays)
|
||||
const event = nostr.event(42, content, [["e", roomId, relay, type]])
|
||||
|
||||
@ -68,3 +68,11 @@ dispatch.addMethod("message/create", async (topic, roomId, content, type = "root
|
||||
|
||||
return event
|
||||
})
|
||||
|
||||
dispatch.addMethod("note/create", async (topic, content) => {
|
||||
const event = nostr.event(1, content)
|
||||
|
||||
await nostr.publish(event)
|
||||
|
||||
return event
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user