Spiff up channels

This commit is contained in:
Jonathan Staab 2023-01-21 11:49:09 -08:00
parent ef3b7f2d3d
commit 07564ef29e
5 changed files with 79 additions and 46 deletions

View File

@ -17,7 +17,8 @@ If you like Coracle and want to support its development, you can donate sats via
- [x] Mentions - [x] Mentions
- [x] Persist and load relay list - [x] Persist and load relay list
- [x] NIP 05 - [x] NIP 05
- [ ] Direct messages using NIP 04 - [x] Direct messages using NIP 04
- [ ] Add petnames for channels
- [ ] Deploy coracle relay, set better defaults - [ ] Deploy coracle relay, set better defaults
- [ ] Image uploads - [ ] Image uploads
- Use dufflepud. Default will charge via lightning and have a tos, others can self-host and skip that. - Use dufflepud. Default will charge via lightning and have a tos, others can self-host and skip that.
@ -53,19 +54,9 @@ If you like Coracle and want to support its development, you can donate sats via
- [ ] Figure out migrations from previous version - [ ] Figure out migrations from previous version
- [ ] Fix notes search - [ ] Fix notes search
- [ ] Chat
- [ ] Figure out which relays to use
- [ ] Add petnames for channels
- [ ] Add back button
- [ ] Create Room -> open modal, choose dm or public room
- [x] Add DM button to profile pages
- [ ] Linkify bech32 entities - [ ] Linkify bech32 entities
- [ ] linkify dm page header
- [ ] Add lock/unlock icon to channel header
- [ ] Add notification for dms - [ ] Add notification for dms
- [ ] Default to network/following - [ ] Default to network/following
- [ ] Add analytics
- [ ] Allow disabling error reporting/analytics
## 0.2.7 ## 0.2.7
@ -97,6 +88,8 @@ If you like Coracle and want to support its development, you can donate sats via
- [x] Added error tracking with bugsnag - [x] Added error tracking with bugsnag
- [x] Upgraded nostr-tools - [x] Upgraded nostr-tools
- [x] Added support for NIP-05 verfication - [x] Added support for NIP-05 verfication
- [x] Added analytics and error reporting (opt out supported)
- [x] Added direct messages and group chat
## 0.2.6 ## 0.2.6

View File

@ -1,5 +1,5 @@
import Bugsnag from "@bugsnag/js" import Bugsnag from "@bugsnag/js"
import {prop, nthArg} from "ramda" import {prop} from "ramda"
import {uuid} from "hurdak/lib/hurdak" import {uuid} from "hurdak/lib/hurdak"
import {navigate} from "svelte-routing" import {navigate} from "svelte-routing"
import {nip19} from 'nostr-tools' import {nip19} from 'nostr-tools'

View File

@ -6,11 +6,13 @@
import {prop, path as getPath, reverse, uniqBy, sortBy, last} from 'ramda' import {prop, path as getPath, reverse, uniqBy, sortBy, last} from 'ramda'
import {formatTimestamp, sleep, createScroller, Cursor} from 'src/util/misc' import {formatTimestamp, sleep, createScroller, Cursor} from 'src/util/misc'
import Badge from 'src/partials/Badge.svelte' import Badge from 'src/partials/Badge.svelte'
import Anchor from 'src/partials/Anchor.svelte'
import Spinner from 'src/partials/Spinner.svelte' import Spinner from 'src/partials/Spinner.svelte'
import {user, getPerson} from 'src/agent' import {user, getPerson} from 'src/agent'
import {render} from 'src/app' import {render} from 'src/app'
export let name export let name
export let link
export let about export let about
export let picture export let picture
export let loadMessages export let loadMessages
@ -18,11 +20,10 @@
export let sendMessage export let sendMessage
export let editRoom = null export let editRoom = null
export let type export let type
console.log(editRoom)
let textarea let textarea
let messages = [] let messages = []
let loading = sleep(10_000) let loading = sleep(30_000)
let annotatedMessages = [] let annotatedMessages = []
let showNewMessages = false let showNewMessages = false
let cursor = new Cursor() let cursor = new Cursor()
@ -64,14 +65,14 @@
return navigate('/login') return navigate('/login')
} }
const sub = listenForMessages( const sub = await listenForMessages(
newMessages => stickToBottom('smooth', () => { newMessages => stickToBottom('smooth', () => {
loading = sleep(10_000) loading = sleep(30_000)
messages = messages.concat(newMessages) messages = messages.concat(newMessages)
}) })
) )
const scroller = createScroller( const scroller = await createScroller(
async () => { async () => {
const events = await loadMessages(cursor) const events = await loadMessages(cursor)
@ -79,7 +80,7 @@
cursor.onChunk(events) cursor.onChunk(events)
stickToBottom('auto', () => { stickToBottom('auto', () => {
loading = sleep(10_000) loading = sleep(30_000)
messages = events.concat(messages) messages = events.concat(messages)
}) })
} }
@ -121,8 +122,8 @@
<div class="flex gap-4 h-full"> <div class="flex gap-4 h-full">
<div class="relative w-full"> <div class="relative w-full">
<div class="flex flex-col py-32 h-full"> <div class="flex flex-col pt-20 pb-28 h-full">
<ul class="p-4 h-full flex-grow flex flex-col-reverse justify-start" name="messages"> <ul class="pb-6 p-4 overflow-auto flex-grow flex flex-col-reverse justify-start" name="messages">
{#each annotatedMessages as m (m.id)} {#each annotatedMessages as m (m.id)}
<li in:fly={{y: 20}} class="py-1 flex flex-col gap-2"> <li in:fly={{y: 20}} class="py-1 flex flex-col gap-2">
{#if type === 'chat' && m.showPerson} {#if type === 'chat' && m.showPerson}
@ -143,22 +144,42 @@
{/each} {/each}
{#await loading} {#await loading}
<Spinner>Looking for messages...</Spinner> <Spinner>Looking for messages...</Spinner>
{:then}
<div in:fly={{y: 20}} class="text-center py-20">End of message history</div>
{/await} {/await}
</ul> </ul>
</div> </div>
<div class="fixed z-10 top-16 w-full lg:-ml-56 lg:pl-56 border-b border-solid border-medium bg-dark"> <div class="fixed z-10 top-16 w-full lg:-ml-56 lg:pl-56 border-b border-solid border-medium bg-dark">
<div class="p-4 flex gap-4"> <div class="p-4 flex gap-4">
<div <div class="flex items-center gap-4">
class="overflow-hidden w-12 h-12 rounded-full bg-cover bg-center shrink-0 border border-solid border-white" <i class="fa fa-arrow-left text-2xl cursor-pointer" on:click={() => navigate("/chat")} />
style="background-image: url({picture})" /> <div
<div class="w-full"> class="overflow-hidden w-12 h-12 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
style="background-image: url({picture})" />
</div>
<div class="w-full flex flex-col gap-2">
<div class="flex items-center justify-between w-full"> <div class="flex items-center justify-between w-full">
<div class="text-lg font-bold">{name || ''}</div> <div class="flex items-center gap-4">
{#if editRoom} {#if link}
<small class="cursor-pointer" on:click={editRoom}> <Anchor type="unstyled" href={link} class="text-lg font-bold">{name || ''}</Anchor>
<i class="fa-solid fa-edit" /> Edit {:else}
</small> <div class="text-lg font-bold">{name || ''}</div>
{/if} {/if}
{#if editRoom}
<small class="cursor-pointer" on:click={editRoom}>
<i class="fa-solid fa-edit" /> Edit
</small>
{/if}
</div>
<div class="flex items-center gap-2">
{#if type === 'dm'}
<i class="fa fa-lock text-light" />
<span class="text-light">Encrypted</span>
{:else}
<i class="fa fa-lock-open text-light" />
<span class="text-light">Public</span>
{/if}
</div>
</div> </div>
<div>{about || ''}</div> <div>{about || ''}</div>
</div> </div>

View File

@ -4,7 +4,7 @@
import {nip19} from 'nostr-tools' import {nip19} from 'nostr-tools'
import {now, batch} from 'src/util/misc' import {now, batch} from 'src/util/misc'
import Channel from 'src/partials/Channel.svelte' import Channel from 'src/partials/Channel.svelte'
import {getRelays, db, listen, load} from 'src/agent' import {getRelays, user, db, listen, load} from 'src/agent'
import {modal} from 'src/app' import {modal} from 'src/app'
import loaders from 'src/app/loaders' import loaders from 'src/app/loaders'
import cmd from 'src/app/cmd' import cmd from 'src/app/cmd'
@ -14,25 +14,42 @@
let {data: roomId} = nip19.decode(entity) let {data: roomId} = nip19.decode(entity)
let room = liveQuery(() => db.rooms.where('id').equals(roomId).first()) let room = liveQuery(() => db.rooms.where('id').equals(roomId).first())
const listenForMessages = cb => listen( const getRoomRelays = $room => {
getRelays(), let relays = getRelays()
// Listen for updates to the room in case we didn't get them before
[{kinds: [40, 41], ids: [roomId]},
{kinds: [42], '#e': [roomId], since: now()}],
batch(300, events => {
const newMessages = events.filter(e => e.kind === 42)
loaders.loadPeople(getRelays(), pluck('pubkey', events)) if ($room) {
relays = relays.concat(getRelays($room.pubkey))
}
cb(newMessages) return relays
}) }
)
const listenForMessages = async cb => {
// Make sure we have our room so we can calculate relays
const $room = await db.rooms.where('id').equals(roomId).first()
const relays = getRoomRelays($room)
return listen(
relays,
// Listen for updates to the room in case we didn't get them before
[{kinds: [40, 41], ids: [roomId]},
{kinds: [42], '#e': [roomId], since: now()}],
batch(300, events => {
const newMessages = events.filter(e => e.kind === 42)
loaders.loadPeople(relays, pluck('pubkey', events))
cb(newMessages)
})
)
}
const loadMessages = async ({until, limit}) => { const loadMessages = async ({until, limit}) => {
const events = await load(getRelays(), {kinds: [42], '#e': [roomId], until, limit}) const relays = getRoomRelays($room)
const events = await load(relays, {kinds: [42], '#e': [roomId], until, limit})
if (events.length) { if (events.length) {
await loaders.loadPeople(getRelays(), pluck('pubkey', events)) await loaders.loadPeople(relays, pluck('pubkey', events))
} }
return events return events
@ -51,8 +68,8 @@
name={$room?.name} name={$room?.name}
about={$room?.about} about={$room?.about}
picture={$room?.picture} picture={$room?.picture}
editRoom={$room?.pubkey === $user.pubkey && editRoom}
{loadMessages} {loadMessages}
{listenForMessages} {listenForMessages}
{sendMessage} {sendMessage}
{editRoom}
/> />

View File

@ -6,6 +6,7 @@
import {batch} from 'src/util/misc' import {batch} from 'src/util/misc'
import Channel from 'src/partials/Channel.svelte' import Channel from 'src/partials/Channel.svelte'
import {getRelays, user, db, listen, keys} from 'src/agent' import {getRelays, user, db, listen, keys} from 'src/agent'
import {routes} from 'src/app/ui'
import cmd from 'src/app/cmd' import cmd from 'src/app/cmd'
export let entity export let entity
@ -26,7 +27,7 @@
} }
const listenForMessages = cb => listen( const listenForMessages = cb => listen(
getRelays(), getRelays().concat(getRelays(pubkey)),
[{kinds: personKinds, authors: [pubkey]}, [{kinds: personKinds, authors: [pubkey]},
{kinds: [4], authors: [$user.pubkey], '#p': [pubkey]}, {kinds: [4], authors: [$user.pubkey], '#p': [pubkey]},
{kinds: [4], authors: [pubkey], '#p': [$user.pubkey]}], {kinds: [4], authors: [pubkey], '#p': [$user.pubkey]}],
@ -62,6 +63,7 @@
name={$person?.name} name={$person?.name}
about={$person?.about} about={$person?.about}
picture={$person?.picture} picture={$person?.picture}
link={$person ? routes.person($person.pubkey) : null}
{loadMessages} {loadMessages}
{listenForMessages} {listenForMessages}
{sendMessage} {sendMessage}