mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Generalize notes/tabs
This commit is contained in:
parent
16884eeb99
commit
70ca44226c
@ -1,8 +1,6 @@
|
||||
Bugs
|
||||
|
||||
- [ ] Pubkeys expand the width of the page, hiding the plus post button
|
||||
- [ ] Permalink note detail (share/permalink button?)
|
||||
- [ ] Back button no longer works if a modal is closed normally
|
||||
- [ ] Prevent tabs from re-mounting (or at least re- animating)
|
||||
- [ ] Go "back" after adding a note
|
||||
- [ ] uniq and sortBy are sprinkled all over the place, figure out a better solution
|
||||
|
@ -92,11 +92,7 @@
|
||||
<Search {...params} />
|
||||
{/key}
|
||||
</Route>
|
||||
<Route path="/notes/:type" let:params>
|
||||
{#key params.type}
|
||||
<Notes {...params} />
|
||||
{/key}
|
||||
</Route>
|
||||
<Route path="/notes/:activeTab" component={Notes} />
|
||||
<Route path="/notes/new" component={NoteCreate} />
|
||||
<Route path="/chat" component={Chat} />
|
||||
<Route path="/chat/new" component={ChatEdit} />
|
||||
|
@ -121,7 +121,7 @@
|
||||
<Anchor on:click={() => deleteReaction(flag)}>Unflag</Anchor>
|
||||
</p>
|
||||
{:else}
|
||||
<p>
|
||||
<p class="text-ellipsis overflow-hidden">
|
||||
{#if note.content.length > 500 && !showEntire}
|
||||
{ellipsize(note.content, 500)}
|
||||
{:else}
|
||||
|
82
src/partials/Notes.svelte
Normal file
82
src/partials/Notes.svelte
Normal file
@ -0,0 +1,82 @@
|
||||
<script>
|
||||
import {onMount, onDestroy} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {uniqBy, reject, prop} from 'ramda'
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import Note from "src/partials/Note.svelte"
|
||||
import {Cursor, epoch} from 'src/state/nostr'
|
||||
import {createScroller, getMuffleValue, annotateNotes, notesListener, modal} from "src/state/app"
|
||||
|
||||
export let filter
|
||||
export let notes
|
||||
export let shouldMuffle = false
|
||||
|
||||
let cursor
|
||||
let listener
|
||||
let scroller
|
||||
let modalUnsub
|
||||
let interval
|
||||
let loading = true
|
||||
|
||||
onMount(async () => {
|
||||
cursor = new Cursor(filter)
|
||||
listener = await notesListener(notes, [filter, {kinds: [5, 7]}], {shouldMuffle})
|
||||
scroller = createScroller(cursor, async chunk => {
|
||||
// Remove a sampling of content if desired
|
||||
if (shouldMuffle) {
|
||||
chunk = reject(n => Math.random() > getMuffleValue(n.pubkey), chunk)
|
||||
}
|
||||
|
||||
const annotated = await annotateNotes(chunk, {showParents: true})
|
||||
|
||||
notes.update($notes => uniqBy(prop('id'), $notes.concat(annotated)))
|
||||
})
|
||||
|
||||
// Track loading based on cursor cutoff date
|
||||
interval = setInterval(() => {
|
||||
loading = cursor.since > epoch
|
||||
}, 1000)
|
||||
|
||||
// When a modal opens, suspend our subscriptions
|
||||
modalUnsub = modal.subscribe(async $modal => {
|
||||
if ($modal) {
|
||||
cursor.stop()
|
||||
listener.stop()
|
||||
scroller.stop()
|
||||
} else {
|
||||
cursor.start()
|
||||
listener.start()
|
||||
scroller.start()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
cursor?.stop()
|
||||
listener?.stop()
|
||||
scroller?.stop()
|
||||
modalUnsub?.()
|
||||
clearInterval(interval)
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:window on:scroll={scroller?.start} />
|
||||
|
||||
<ul class="py-4 flex flex-col gap-2 max-w-xl m-auto">
|
||||
{#each $notes as n (n.id)}
|
||||
<li>
|
||||
<Note interactive note={n} />
|
||||
{#each n.replies as r (r.id)}
|
||||
<div class="ml-6 border-l border-solid border-medium">
|
||||
<Note interactive isReply note={r} />
|
||||
</div>
|
||||
{/each}
|
||||
</li>
|
||||
{:else}
|
||||
{#if loading}
|
||||
<li><Spinner /></li>
|
||||
{:else}
|
||||
<li class="p-20 text-center" in:fly={{y: 20}}>No notes found.</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
19
src/partials/Tabs.svelte
Normal file
19
src/partials/Tabs.svelte
Normal file
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
import {fly} from 'svelte/transition'
|
||||
import {toTitle} from 'hurdak/lib/hurdak'
|
||||
|
||||
export let tabs
|
||||
export let activeTab
|
||||
export let setActiveTab
|
||||
</script>
|
||||
|
||||
<ul class="border-b border-solid border-dark flex max-w-xl m-auto pt-2" in:fly={{y: 20}}>
|
||||
{#each tabs as tab}
|
||||
<li
|
||||
class="cursor-pointer hover:border-b border-solid border-medium px-8 py-4"
|
||||
class:border-b={activeTab === tab}
|
||||
on:click={() => setActiveTab(tab)}>
|
||||
{toTitle(tab)}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
@ -1,65 +1,21 @@
|
||||
<script>
|
||||
import {onMount, onDestroy} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {writable} from 'svelte/store'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {uniqBy, reject, prop} from 'ramda'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import Note from "src/partials/Note.svelte"
|
||||
import {relays, Cursor} from "src/state/nostr"
|
||||
import Tabs from "src/partials/Tabs.svelte"
|
||||
import Notes from "src/partials/Notes.svelte"
|
||||
import {relays} from "src/state/nostr"
|
||||
import {user} from "src/state/user"
|
||||
import {createScroller, getMuffleValue, annotateNotes, notesListener, modal} from "src/state/app"
|
||||
|
||||
export let type
|
||||
export let activeTab
|
||||
|
||||
const notes = writable([])
|
||||
let cursor
|
||||
let listener
|
||||
let scroller
|
||||
let modalUnsub
|
||||
let authors = $user ? $user.petnames.map(t => t[1]) : []
|
||||
const globalNotes = writable([])
|
||||
const followNotes = writable([])
|
||||
const authors = $user ? $user.petnames.map(t => t[1]) : []
|
||||
|
||||
const createNote = () => {
|
||||
navigate("/notes/new")
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
cursor = new Cursor(type === 'global' ? {kinds: [1]} : {kinds: [1], authors})
|
||||
listener = await notesListener(notes, {kinds: [1, 5, 7]}, {shouldMuffle: true})
|
||||
scroller = createScroller(cursor, async chunk => {
|
||||
// Remove a sampling of content if desired
|
||||
chunk = reject(n => Math.random() > getMuffleValue(n.pubkey), chunk)
|
||||
|
||||
const annotated = await annotateNotes(chunk, {showParents: true})
|
||||
|
||||
notes.update($notes => uniqBy(prop('id'), $notes.concat(annotated)))
|
||||
})
|
||||
|
||||
// When a modal opens, suspend our subscriptions
|
||||
modalUnsub = modal.subscribe(async $modal => {
|
||||
if ($modal) {
|
||||
cursor.stop()
|
||||
listener.stop()
|
||||
scroller.stop()
|
||||
} else {
|
||||
cursor.start()
|
||||
listener.start()
|
||||
scroller.start()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
cursor?.stop()
|
||||
listener?.stop()
|
||||
scroller?.stop()
|
||||
modalUnsub?.()
|
||||
})
|
||||
const setActiveTab = tab => navigate(`/notes/${tab}`)
|
||||
</script>
|
||||
|
||||
<svelte:window on:scroll={scroller?.start} />
|
||||
|
||||
{#if $relays.length === 0}
|
||||
<div class="flex w-full justify-center items-center py-16">
|
||||
<div class="text-center max-w-md">
|
||||
@ -69,51 +25,25 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<ul class="border-b border-solid border-dark flex max-w-xl m-auto pt-2" in:fly={{y: 20}}>
|
||||
<li
|
||||
class="cursor-pointer hover:border-b border-solid border-medium"
|
||||
class:border-b={type === 'global'}>
|
||||
<a class="block px-8 py-4 " href="/notes/global">Global</a>
|
||||
</li>
|
||||
<li
|
||||
class="cursor-pointer hover:border-b border-solid border-medium"
|
||||
class:border-b={type === 'follows'}>
|
||||
<a class="block px-8 py-4 " href="/notes/follows">Follows</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{#if type === 'follows' && authors.length === 0}
|
||||
<Tabs tabs={['global', 'follows']} {activeTab} {setActiveTab} />
|
||||
{#if activeTab === 'follows' && authors.length === 0}
|
||||
<div class="flex w-full justify-center items-center py-16">
|
||||
<div class="text-center max-w-md">
|
||||
You haven't yet followed anyone. Visit a user's profile to follow them.
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'follows'}
|
||||
<Notes notes={followNotes} filter={{kinds: [1], authors}} shouldMuffle />
|
||||
{:else}
|
||||
<ul class="py-4 flex flex-col gap-2 max-w-xl m-auto">
|
||||
{#each (notes ? $notes : []) as n (n.id)}
|
||||
<li>
|
||||
<Note interactive note={n} />
|
||||
{#each n.replies as r (r.id)}
|
||||
<div class="ml-6 border-l border-solid border-medium">
|
||||
<Note interactive isReply note={r} />
|
||||
</div>
|
||||
{/each}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<!-- This will always be sitting at the bottom in case infinite scrolling can't keep up -->
|
||||
<Spinner />
|
||||
<Notes notes={globalNotes} filter={{kinds: [1]}} shouldMuffle />
|
||||
{/if}
|
||||
|
||||
<div class="fixed bottom-0 right-0 p-8">
|
||||
<div
|
||||
<a
|
||||
href="/notes/new"
|
||||
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}
|
||||
>
|
||||
items-center border border-dark shadow-2xl cursor-pointer">
|
||||
<span class="fa-sold fa-plus fa-2xl" />
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -1,73 +1,24 @@
|
||||
<script>
|
||||
import {onMount, onDestroy} from 'svelte'
|
||||
import {writable} from 'svelte/store'
|
||||
import {uniqBy, prop} from 'ramda'
|
||||
import {fly} from 'svelte/transition'
|
||||
import Note from "src/partials/Note.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import Notes from "src/partials/Notes.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import {Cursor, epoch} from 'src/state/nostr'
|
||||
import {user as currentUser} from 'src/state/user'
|
||||
import {t, dispatch} from 'src/state/dispatch'
|
||||
import {accounts, getFollow, createScroller, notesListener, modal, annotateNotes} from "src/state/app"
|
||||
import {accounts, getFollow, modal} from "src/state/app"
|
||||
|
||||
export let pubkey
|
||||
|
||||
const notes = writable([])
|
||||
let user
|
||||
let cursor
|
||||
let listener
|
||||
let scroller
|
||||
let interval
|
||||
let loading = true
|
||||
let modalUnsub
|
||||
const authorNotes = writable([])
|
||||
let following = getFollow(pubkey)
|
||||
let user
|
||||
|
||||
$: user = $accounts[pubkey]
|
||||
|
||||
onMount(async () => {
|
||||
cursor = new Cursor({kinds: [1], authors: [pubkey]})
|
||||
listener = await notesListener(notes, [{kinds: [1], authors: [pubkey]}, {kinds: [5, 7]}])
|
||||
scroller = createScroller(cursor, async chunk => {
|
||||
const annotated = await annotateNotes(chunk, {showParents: true})
|
||||
|
||||
notes.update($notes => uniqBy(prop('id'), $notes.concat(annotated)))
|
||||
})
|
||||
|
||||
// Populate our initial empty space
|
||||
scroller.start()
|
||||
|
||||
// Track loading based on cursor cutoff date
|
||||
interval = setInterval(() => {
|
||||
loading = cursor.since > epoch
|
||||
}, 1000)
|
||||
|
||||
// When a modal opens, suspend our subscriptions
|
||||
modalUnsub = modal.subscribe(async $modal => {
|
||||
if ($modal) {
|
||||
cursor.stop()
|
||||
listener.stop()
|
||||
} else {
|
||||
cursor.start()
|
||||
listener.start()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
cursor?.stop()
|
||||
listener?.stop()
|
||||
scroller?.stop()
|
||||
modalUnsub?.()
|
||||
clearInterval(interval)
|
||||
})
|
||||
|
||||
const follow = () => {
|
||||
const petnames = $currentUser.petnames
|
||||
.concat([t("p", user.pubkey, user.name)])
|
||||
|
||||
console.log(petnames)
|
||||
|
||||
dispatch('account/petnames', petnames)
|
||||
|
||||
following = true
|
||||
@ -87,8 +38,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:scroll={scroller?.start} />
|
||||
|
||||
{#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}}>
|
||||
@ -123,23 +72,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-px bg-medium" in:fly={{y: 20, delay: 200}} />
|
||||
<ul class="flex flex-col" in:fly={{y: 20, delay: 400}}>
|
||||
{#each (notes ? $notes : []) as n (n.id)}
|
||||
<li>
|
||||
<Note interactive note={n} />
|
||||
{#each n.replies as r (r.id)}
|
||||
<div class="ml-6 border-l border-solid border-medium">
|
||||
<Note interactive isReply note={r} />
|
||||
</div>
|
||||
{/each}
|
||||
</li>
|
||||
{:else}
|
||||
{#if loading}
|
||||
<li><Spinner /></li>
|
||||
{:else}
|
||||
<li class="p-20 text-center" in:fly={{y: 20}}>No notes found.</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
<Notes notes={authorNotes} filter={{kinds: [1], authors: [pubkey]}} />
|
||||
</div>
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user