mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-19 19:46:42 +00:00
Try opening feeds filtered by relay in a modal
This commit is contained in:
parent
5d6d3b1b6f
commit
dbbbec4d07
@ -1,13 +1,14 @@
|
|||||||
# Current
|
# Current
|
||||||
|
|
||||||
- [ ] Fix scrolling with embedded modals by registering open modals in the component
|
|
||||||
- [ ] Relays bounty
|
- [ ] Relays bounty
|
||||||
- [x] Ability to click into a relay's global feed
|
- [x] Ability to click into a relay's global feed
|
||||||
- [ ] Ability to filter feeds by relay
|
- [x] Ability to filter feeds by relay
|
||||||
- Global Mutes? Global Whitelist?
|
- [-] Global Mutes? Global Whitelist?
|
||||||
- Open in modal with "here's what this feed would look like with only this relay"
|
- [x] Open in modal with "here's what this feed would look like with only this relay"
|
||||||
|
- [ ] Fix scrolling with embedded modals by registering open modals in the component
|
||||||
- [ ] Ability to create custom feeds
|
- [ ] Ability to create custom feeds
|
||||||
- [ ] Fix tag-style event mentions. Probably transform all mentions into entities in parse
|
- [ ] Fix tag-style event mentions. Probably transform all mentions into entities in parse
|
||||||
|
- [ ] Some lnurls aren't working npub1y3k2nheva29y9ej8a22e07epuxrn04rvgy28wvs54y57j7vsxxuq0gvp4j
|
||||||
- [ ] Fix performance issues
|
- [ ] Fix performance issues
|
||||||
- [ ] https://github.com/techfort/LokiJS
|
- [ ] https://github.com/techfort/LokiJS
|
||||||
- Use indexed adapter github.com/techfort/LokiJS/blob/master/tutorials/Persistence%20Adapters.md and partitioning adapter
|
- Use indexed adapter github.com/techfort/LokiJS/blob/master/tutorials/Persistence%20Adapters.md and partitioning adapter
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
import {loadAppData} from "src/app"
|
import {loadAppData} from "src/app"
|
||||||
import {theme, getThemeVariables} from "src/app/ui"
|
import {theme, getThemeVariables} from "src/app/ui"
|
||||||
import {modal, routes, menuIsOpen, logUsage} from "src/app/ui"
|
import {modal, openModals, routes, menuIsOpen, logUsage} from "src/app/ui"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
import Modal from "src/partials/Modal.svelte"
|
import Modal from "src/partials/Modal.svelte"
|
||||||
@ -97,11 +97,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Keep scroll position on body, but don't allow scrolling
|
// Log modals
|
||||||
const unsubModal = modal.subscribe($modal => {
|
const unsubModal = modal.subscribe($modal => {
|
||||||
if ($modal) {
|
if ($modal) {
|
||||||
logUsage(btoa(["modal", $modal.type].join(":")))
|
logUsage(btoa(["modal", $modal.type].join(":")))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Keep scroll position on body, but don't allow scrolling
|
||||||
|
const unsubOpenModals = openModals.subscribe(n => {
|
||||||
|
if (n > 0) {
|
||||||
// This is not idempotent, so don't duplicate it
|
// This is not idempotent, so don't duplicate it
|
||||||
if (document.body.style.position !== "fixed") {
|
if (document.body.style.position !== "fixed") {
|
||||||
scrollY = window.scrollY
|
scrollY = window.scrollY
|
||||||
@ -133,6 +138,7 @@
|
|||||||
return () => {
|
return () => {
|
||||||
unsubHistory()
|
unsubHistory()
|
||||||
unsubModal()
|
unsubModal()
|
||||||
|
unsubOpenModals()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ export const getRelaysForEventParent = event => {
|
|||||||
// to read from the current user's network's read relays instead.
|
// to read from the current user's network's read relays instead.
|
||||||
export const getRelaysForEventChildren = event => {
|
export const getRelaysForEventChildren = event => {
|
||||||
return uniqByUrl(
|
return uniqByUrl(
|
||||||
getPubkeyReadRelays(event.pubkey).concat(event.seen_on.map(url => ({url, score: 1})))
|
getPubkeyReadRelays(event.pubkey).concat((event.seen_on || []).map(url => ({url, score: 1})))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ export const routes = new Table("routes", "id", {
|
|||||||
listener.connect()
|
listener.connect()
|
||||||
|
|
||||||
export const getPersonWithFallback = pubkey => people.get(pubkey) || {pubkey}
|
export const getPersonWithFallback = pubkey => people.get(pubkey) || {pubkey}
|
||||||
|
export const getRelayWithFallback = url => relays.get(url) || {url}
|
||||||
|
|
||||||
const ready = derived(pluck("ready", Object.values(registry)), all(identity))
|
const ready = derived(pluck("ready", Object.values(registry)), all(identity))
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import {navigate} from "svelte-routing"
|
|||||||
import {nip19} from "nostr-tools"
|
import {nip19} from "nostr-tools"
|
||||||
import {writable, get} from "svelte/store"
|
import {writable, get} from "svelte/store"
|
||||||
import {globalHistory} from "svelte-routing/src/history"
|
import {globalHistory} from "svelte-routing/src/history"
|
||||||
import {sleep, WritableList, synced, hash} from "src/util/misc"
|
import {sleep, synced, hash} from "src/util/misc"
|
||||||
import {warn} from "src/util/logger"
|
import {warn} from "src/util/logger"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
|
|
||||||
@ -48,6 +48,8 @@ export const menuIsOpen = writable(false)
|
|||||||
|
|
||||||
// Modals
|
// Modals
|
||||||
|
|
||||||
|
export const openModals = writable(0)
|
||||||
|
|
||||||
export const modal = {
|
export const modal = {
|
||||||
history: [],
|
history: [],
|
||||||
set: data => {
|
set: data => {
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import {onMount, onDestroy} from "svelte"
|
||||||
import {fly, fade} from "svelte/transition"
|
import {fly, fade} from "svelte/transition"
|
||||||
|
import {openModals} from "src/app/ui"
|
||||||
|
|
||||||
export let onEscape = null
|
export let onEscape = null
|
||||||
|
|
||||||
let root, content
|
let root, content
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
openModals.update(n => n + 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
openModals.update(n => n - 1)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:body
|
<svelte:body
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
export let triggerType = "click"
|
export let triggerType = "click"
|
||||||
export let placement = "top"
|
export let placement = "top"
|
||||||
export let interactive = true
|
export let interactive = true
|
||||||
|
export let arrow = false
|
||||||
|
|
||||||
let trigger
|
let trigger
|
||||||
let tooltip
|
let tooltip
|
||||||
@ -17,6 +18,7 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
instance = tippy(trigger, {
|
instance = tippy(trigger, {
|
||||||
theme,
|
theme,
|
||||||
|
arrow,
|
||||||
placement: placement as Placement,
|
placement: placement as Placement,
|
||||||
appendTo: () => document.body,
|
appendTo: () => document.body,
|
||||||
allowHTML: true,
|
allowHTML: true,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {objOf} from "ramda"
|
import {objOf, last} from "ramda"
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {nip19} from "nostr-tools"
|
import {nip19} from "nostr-tools"
|
||||||
import {warn} from "src/util/logger"
|
import {warn} from "src/util/logger"
|
||||||
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
export let entity
|
export let entity
|
||||||
|
|
||||||
|
entity = last(entity.split(":"))
|
||||||
|
|
||||||
let type, data, relays
|
let type, data, relays
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {displayRelay} from "src/util/nostr"
|
import {displayRelay} from "src/util/nostr"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
|
||||||
import Feed from "src/views/feed/Feed.svelte"
|
import Feed from "src/views/feed/Feed.svelte"
|
||||||
import RelayTitle from "src/views/relays/RelayTitle.svelte"
|
import RelayTitle from "src/views/relays/RelayTitle.svelte"
|
||||||
import RelayJoin from "src/views/relays/RelayJoin.svelte"
|
import RelayActions from "src/views/relays/RelayActions.svelte"
|
||||||
import {relays} from "src/agent/tables"
|
import {relays} from "src/agent/tables"
|
||||||
|
|
||||||
export let url
|
export let url
|
||||||
@ -17,7 +16,7 @@
|
|||||||
<Content>
|
<Content>
|
||||||
<div class="flex items-center justify-between gap-2">
|
<div class="flex items-center justify-between gap-2">
|
||||||
<RelayTitle {relay} />
|
<RelayTitle {relay} />
|
||||||
<RelayJoin {relay} />
|
<RelayActions {relay} />
|
||||||
</div>
|
</div>
|
||||||
{#if relay.description}
|
{#if relay.description}
|
||||||
<p>{relay.description}</p>
|
<p>{relay.description}</p>
|
||||||
|
@ -125,7 +125,7 @@ export const poll = (t, cb) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createScroller = (loadMore, {reverse = false} = {}) => {
|
export const createScroller = (loadMore, {reverse = false, element = document.body} = {}) => {
|
||||||
const THRESHOLD = 2000
|
const THRESHOLD = 2000
|
||||||
|
|
||||||
// NOTE TO FUTURE SELF
|
// NOTE TO FUTURE SELF
|
||||||
@ -136,10 +136,11 @@ export const createScroller = (loadMore, {reverse = false} = {}) => {
|
|||||||
const check = async () => {
|
const check = async () => {
|
||||||
// While we have empty space, fill it
|
// While we have empty space, fill it
|
||||||
const {scrollY, innerHeight} = window
|
const {scrollY, innerHeight} = window
|
||||||
const {scrollHeight} = document.body
|
const {scrollHeight, scrollTop} = element
|
||||||
|
const offset = scrollTop || scrollY
|
||||||
const shouldLoad = reverse
|
const shouldLoad = reverse
|
||||||
? scrollY < THRESHOLD
|
? offset < THRESHOLD
|
||||||
: scrollY + innerHeight + THRESHOLD > scrollHeight
|
: offset + innerHeight + THRESHOLD > scrollHeight
|
||||||
|
|
||||||
// Only trigger loading the first time we reach the threshold
|
// Only trigger loading the first time we reach the threshold
|
||||||
if (shouldLoad) {
|
if (shouldLoad) {
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {find, partition, always, propEq, uniqBy, sortBy, prop} from "ramda"
|
import {partition, always, propEq, uniqBy, sortBy, prop} from "ramda"
|
||||||
import {fly} from "svelte/transition"
|
import {fly} from "svelte/transition"
|
||||||
import {quantify} from "hurdak/lib/hurdak"
|
import {quantify} from "hurdak/lib/hurdak"
|
||||||
import {createScroller, now, timedelta, Cursor} from "src/util/misc"
|
import {createScroller, now, timedelta, Cursor} from "src/util/misc"
|
||||||
import {asDisplayEvent, mergeFilter, displayRelay} from "src/util/nostr"
|
import {asDisplayEvent, mergeFilter} from "src/util/nostr"
|
||||||
import Spinner from "src/partials/Spinner.svelte"
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
import Modal from "src/partials/Modal.svelte"
|
import Modal from "src/partials/Modal.svelte"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
import RelayTitle from "src/views/relays/RelayTitle.svelte"
|
import RelayFeed from "src/views/feed/RelayFeed.svelte"
|
||||||
import RelayJoin from "src/views/relays/RelayJoin.svelte"
|
|
||||||
import Note from "src/views/notes/Note.svelte"
|
import Note from "src/views/notes/Note.svelte"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
import network from "src/agent/network"
|
import network from "src/agent/network"
|
||||||
@ -25,6 +24,7 @@
|
|||||||
let notes = []
|
let notes = []
|
||||||
let notesBuffer = []
|
let notesBuffer = []
|
||||||
let feedRelay = null
|
let feedRelay = null
|
||||||
|
let feedScroller = null
|
||||||
|
|
||||||
// Add a short buffer so we can get the most possible results for recent notes
|
// Add a short buffer so we can get the most possible results for recent notes
|
||||||
const since = now()
|
const since = now()
|
||||||
@ -34,6 +34,15 @@
|
|||||||
|
|
||||||
const setFeedRelay = relay => {
|
const setFeedRelay = relay => {
|
||||||
feedRelay = relay
|
feedRelay = relay
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
feedScroller?.stop()
|
||||||
|
feedScroller = !relay
|
||||||
|
? null
|
||||||
|
: createScroller(loadMore, {
|
||||||
|
element: document.querySelector(".modal-content"),
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadBufferedNotes = () => {
|
const loadBufferedNotes = () => {
|
||||||
@ -95,6 +104,22 @@
|
|||||||
notes = uniqBy(prop("id"), notes.concat(bottom))
|
notes = uniqBy(prop("id"), notes.concat(bottom))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadMore = async () => {
|
||||||
|
if ($modal) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for this page to load before trying again
|
||||||
|
await network.load({
|
||||||
|
relays: feedRelay ? [feedRelay] : relays,
|
||||||
|
filter: mergeFilter(filter, cursor.getFilter()),
|
||||||
|
onChunk,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update our cursor
|
||||||
|
cursor.update(notes)
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const sub = network.listen({
|
const sub = network.listen({
|
||||||
relays,
|
relays,
|
||||||
@ -102,24 +127,11 @@
|
|||||||
onChunk,
|
onChunk,
|
||||||
})
|
})
|
||||||
|
|
||||||
const scroller = createScroller(async () => {
|
const scroller = createScroller(loadMore)
|
||||||
if ($modal) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for this page to load before trying again
|
|
||||||
await network.load({
|
|
||||||
relays: feedRelay ? [feedRelay] : relays,
|
|
||||||
filter: mergeFilter(filter, cursor.getFilter()),
|
|
||||||
onChunk,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update our cursor
|
|
||||||
cursor.update(notes)
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
scroller.stop()
|
scroller.stop()
|
||||||
|
feedScroller?.stop()
|
||||||
sub.then(s => s?.unsub())
|
sub.then(s => s?.unsub())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -141,7 +153,7 @@
|
|||||||
|
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
{#each notes as note (note.id)}
|
{#each notes as note (note.id)}
|
||||||
<Note depth={2} {note} setFeedRelay={setFeedRelay} />
|
<Note depth={2} {note} {feedRelay} {setFeedRelay} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -149,26 +161,7 @@
|
|||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
{#if feedRelay}
|
{#if feedRelay}
|
||||||
<Modal onEscape={() => setFeedRelay(null)}>
|
<Modal onEscape={() => setFeedRelay(null)}>
|
||||||
<Content>
|
<RelayFeed {feedRelay} {notes} depth={2} />
|
||||||
<div class="flex items-center justify-between gap-2">
|
</Modal>
|
||||||
<RelayTitle relay={feedRelay} />
|
|
||||||
<RelayJoin relay={feedRelay} />
|
|
||||||
</div>
|
|
||||||
{#if feedRelay.description}
|
|
||||||
<p>{feedRelay.description}</p>
|
|
||||||
{/if}
|
|
||||||
<p class="text-gray-4">
|
|
||||||
<i class="fa fa-info-circle" />
|
|
||||||
Below is your current feed including only notes seen on {displayRelay(feedRelay)}
|
|
||||||
</p>
|
|
||||||
<div class="flex flex-col gap-4">
|
|
||||||
{#each notes as note (note.id)}
|
|
||||||
{#if note.seen_on.includes(feedRelay.url)}
|
|
||||||
<Note depth={2} {note} />
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</Content>
|
|
||||||
</Modal>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
39
src/views/feed/RelayFeed.svelte
Normal file
39
src/views/feed/RelayFeed.svelte
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {displayRelay} from "src/util/nostr"
|
||||||
|
import Content from "src/partials/Content.svelte"
|
||||||
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
|
import RelayTitle from "src/views/relays/RelayTitle.svelte"
|
||||||
|
import RelayActions from "src/views/relays/RelayActions.svelte"
|
||||||
|
import Note from "src/views/notes/Note.svelte"
|
||||||
|
|
||||||
|
export let depth
|
||||||
|
export let showContext = false
|
||||||
|
export let feedRelay
|
||||||
|
export let notes
|
||||||
|
|
||||||
|
$: filteredNotes = notes.filter(n => n.seen_on.includes(feedRelay.url))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Content>
|
||||||
|
<div class="flex items-center justify-between gap-2">
|
||||||
|
<RelayTitle relay={feedRelay} />
|
||||||
|
<RelayActions relay={feedRelay} />
|
||||||
|
</div>
|
||||||
|
{#if feedRelay.description}
|
||||||
|
<p>{feedRelay.description}</p>
|
||||||
|
{/if}
|
||||||
|
<p class="text-gray-4">
|
||||||
|
<i class="fa fa-info-circle" />
|
||||||
|
Below is your current feed including only notes seen on {displayRelay(feedRelay)}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<!-- If someone clicks on a child note that was seen on a relay the parent was not
|
||||||
|
seen on, we get nothing, so just show everything - but pass down the filter -->
|
||||||
|
{#each filteredNotes.length > 0 ? filteredNotes : notes as note (note.id)}
|
||||||
|
<Note invertColors {depth} {note} {feedRelay} {showContext} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spinner />
|
||||||
|
</Content>
|
@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {fly} from "svelte/transition"
|
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
import {modal} from "src/app/ui"
|
import {modal} from "src/app/ui"
|
||||||
|
|
||||||
|
@ -50,7 +50,8 @@
|
|||||||
import NoteContent from "src/views/notes/NoteContent.svelte"
|
import NoteContent from "src/views/notes/NoteContent.svelte"
|
||||||
|
|
||||||
export let note
|
export let note
|
||||||
export let setFeedRelay
|
export let feedRelay
|
||||||
|
export let setFeedRelay = null
|
||||||
export let depth = 0
|
export let depth = 0
|
||||||
export let anchorId = null
|
export let anchorId = null
|
||||||
export let showParent = true
|
export let showParent = true
|
||||||
@ -134,8 +135,15 @@
|
|||||||
$: $likesCount = likes.length
|
$: $likesCount = likes.length
|
||||||
$: $zapsTotal = sum(zaps.map(zap => zap.invoiceAmount)) / 1000
|
$: $zapsTotal = sum(zaps.map(zap => zap.invoiceAmount)) / 1000
|
||||||
$: $repliesCount = note.replies.length
|
$: $repliesCount = note.replies.length
|
||||||
$: visibleNotes = note.replies.filter(r => (showContext ? true : !r.isContext))
|
|
||||||
$: canZap = $person?.zapper && $person?.pubkey !== user.getPubkey()
|
$: canZap = $person?.zapper && $person?.pubkey !== user.getPubkey()
|
||||||
|
$: visibleNotes = note.replies.filter(r => {
|
||||||
|
if (feedRelay && !r.seen_on.includes(feedRelay.url)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return showContext ? true : !r.isContext
|
||||||
|
})
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
actions = []
|
actions = []
|
||||||
|
|
||||||
@ -393,7 +401,7 @@
|
|||||||
<div slot="trigger">
|
<div slot="trigger">
|
||||||
<Anchor
|
<Anchor
|
||||||
type="unstyled"
|
type="unstyled"
|
||||||
class="flex items-center gap-2 text-lg font-bold"
|
class="flex items-center gap-2 pr-16 text-lg font-bold sm:pr-0"
|
||||||
href={isMobile ? null : routes.person($person.pubkey)}>
|
href={isMobile ? null : routes.person($person.pubkey)}>
|
||||||
<span>{displayPerson($person)}</span>
|
<span>{displayPerson($person)}</span>
|
||||||
{#if $person.verified_as}
|
{#if $person.verified_as}
|
||||||
@ -465,18 +473,31 @@
|
|||||||
</div>
|
</div>
|
||||||
<div on:click|stopPropagation class="flex items-center">
|
<div on:click|stopPropagation class="flex items-center">
|
||||||
{#if pool.forceUrls.length === 0}
|
{#if pool.forceUrls.length === 0}
|
||||||
|
<!-- Mobile version -->
|
||||||
<div
|
<div
|
||||||
class={cx("absolute top-0 right-0 m-3 sm:relative sm:m-0", {
|
style="transform: scale(-1, 1)"
|
||||||
"hidden group-hover:flex": !showEntire,
|
class="absolute top-0 right-0 m-3 grid grid-cols-3 gap-2 sm:hidden">
|
||||||
flex: showEntire,
|
{#each note.seen_on as url, i}
|
||||||
|
<div class={`cursor-pointer order-${3 - (i % 3)}`}>
|
||||||
|
<div
|
||||||
|
class="h-3 w-3 rounded-full border border-solid border-gray-6"
|
||||||
|
style={`background: ${hsl(stringToHue(url))}`}
|
||||||
|
on:click={() => setFeedRelay?.({url})} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<!-- Desktop version -->
|
||||||
|
<div
|
||||||
|
class={cx("hidden sm:flex transition-opacity", {
|
||||||
|
"opacity-0 group-hover:opacity-100": !showEntire,
|
||||||
})}>
|
})}>
|
||||||
{#each note.seen_on as url}
|
{#each note.seen_on as url, i}
|
||||||
<Popover triggerType="mouseenter" interactive={false}>
|
<Popover triggerType="mouseenter" interactive={false}>
|
||||||
<div slot="trigger" class="cursor-pointer p-1">
|
<div slot="trigger" class="cursor-pointer p-1">
|
||||||
<div
|
<div
|
||||||
class="h-3 w-3 rounded-full border border-solid border-gray-6"
|
class="h-3 w-3 rounded-full border border-solid border-gray-6"
|
||||||
style={`background: ${hsl(stringToHue(url))}`}
|
style={`background: ${hsl(stringToHue(url))}`}
|
||||||
on:click={() => setFeedRelay({url})} />
|
on:click={() => setFeedRelay?.({url})} />
|
||||||
</div>
|
</div>
|
||||||
<div slot="tooltip">{displayRelay({url})}</div>
|
<div slot="tooltip">{displayRelay({url})}</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
@ -596,6 +617,8 @@
|
|||||||
showParent={false}
|
showParent={false}
|
||||||
note={r}
|
note={r}
|
||||||
depth={depth - 1}
|
depth={depth - 1}
|
||||||
|
{feedRelay}
|
||||||
|
{setFeedRelay}
|
||||||
{invertColors}
|
{invertColors}
|
||||||
{anchorId}
|
{anchorId}
|
||||||
{showContext} />
|
{showContext} />
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
import {log} from "src/util/logger"
|
import {log} from "src/util/logger"
|
||||||
import {asDisplayEvent} from "src/util/nostr"
|
import {asDisplayEvent} from "src/util/nostr"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
|
import RelayFeed from "src/views/feed/RelayFeed.svelte"
|
||||||
|
import Modal from "src/partials/Modal.svelte"
|
||||||
import Spinner from "src/partials/Spinner.svelte"
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
import Note from "src/views/notes/Note.svelte"
|
import Note from "src/views/notes/Note.svelte"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
@ -18,6 +20,11 @@
|
|||||||
|
|
||||||
let sub = null
|
let sub = null
|
||||||
let loading = true
|
let loading = true
|
||||||
|
let feedRelay = null
|
||||||
|
|
||||||
|
const setFeedRelay = relay => {
|
||||||
|
feedRelay = relay
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!note.pubkey) {
|
if (!note.pubkey) {
|
||||||
@ -56,10 +63,23 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else if note.pubkey}
|
{:else if note.pubkey}
|
||||||
<div in:fly={{y: 20}} class="m-auto flex w-full max-w-2xl flex-col gap-4 p-4">
|
<div in:fly={{y: 20}} class="m-auto flex w-full max-w-2xl flex-col gap-4 p-4">
|
||||||
<Note showContext depth={6} anchorId={note.id} note={asDisplayEvent(note)} {invertColors} />
|
<Note
|
||||||
|
showContext
|
||||||
|
depth={6}
|
||||||
|
anchorId={note.id}
|
||||||
|
note={asDisplayEvent(note)}
|
||||||
|
{invertColors}
|
||||||
|
{feedRelay}
|
||||||
|
{setFeedRelay} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if feedRelay}
|
||||||
|
<Modal onEscape={() => setFeedRelay(null)}>
|
||||||
|
<RelayFeed {feedRelay} notes={[note]} depth={6} showContext />
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
56
src/views/relays/RelayActions.svelte
Normal file
56
src/views/relays/RelayActions.svelte
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {find, last, propEq} from "ramda"
|
||||||
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
|
import Popover from "src/partials/Popover.svelte"
|
||||||
|
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
||||||
|
import user from "src/agent/user"
|
||||||
|
import {getRelayWithFallback} from "src/agent/tables"
|
||||||
|
|
||||||
|
export let relay
|
||||||
|
|
||||||
|
relay = getRelayWithFallback(relay.url)
|
||||||
|
|
||||||
|
const {relays: userRelays} = user
|
||||||
|
|
||||||
|
let actions = []
|
||||||
|
|
||||||
|
$: joined = find(propEq("url", relay.url), $userRelays)
|
||||||
|
$: {
|
||||||
|
actions = []
|
||||||
|
|
||||||
|
if (!joined) {
|
||||||
|
actions.push({
|
||||||
|
onClick: () => user.addRelay(relay.url),
|
||||||
|
label: "Join",
|
||||||
|
icon: "right-to-bracket",
|
||||||
|
})
|
||||||
|
} else if ($userRelays.length > 1) {
|
||||||
|
actions.push({
|
||||||
|
onClick: () => user.removeRelay(relay.url),
|
||||||
|
label: "Leave",
|
||||||
|
icon: "right-from-bracket",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relay.contact) {
|
||||||
|
actions.push({
|
||||||
|
onClick: () => window.open("mailto:" + last(relay.contact.split(":"))),
|
||||||
|
label: "Contact",
|
||||||
|
icon: "envelope",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if actions.length > 0}
|
||||||
|
{#if actions.length === 1}
|
||||||
|
<Popover triggerType="mouseenter">
|
||||||
|
<Anchor slot="trigger" type="button-circle" on:click={actions[0].onClick}>
|
||||||
|
<i class={`fa fa-${actions[0].icon}`} />
|
||||||
|
</Anchor>
|
||||||
|
<p slot="tooltip">{actions[0].label}</p>
|
||||||
|
</Popover>
|
||||||
|
{:else}
|
||||||
|
<OverflowMenu {actions} />
|
||||||
|
{/if}
|
||||||
|
{/if}
|
@ -1,36 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import {find, propEq} from "ramda"
|
|
||||||
import Anchor from 'src/partials/Anchor.svelte'
|
|
||||||
import user from "src/agent/user"
|
|
||||||
|
|
||||||
export let relay
|
|
||||||
|
|
||||||
const {relays: userRelays} = user
|
|
||||||
|
|
||||||
$: joined = find(propEq("url", relay.url), $userRelays)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-3 whitespace-nowrap">
|
|
||||||
{#if relay.contact}
|
|
||||||
<Anchor type="button-circle" href={`mailto:${relay.contact}`}>
|
|
||||||
<i class="fa fa-envelope" />
|
|
||||||
</Anchor>
|
|
||||||
{/if}
|
|
||||||
{#if joined}
|
|
||||||
{#if $userRelays.length > 1}
|
|
||||||
<Anchor
|
|
||||||
type="button"
|
|
||||||
class="flex items-center gap-2 rounded-full"
|
|
||||||
on:click={() => user.removeRelay(relay.url)}>
|
|
||||||
<i class="fa fa-right-from-bracket" /> Leave
|
|
||||||
</Anchor>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<Anchor
|
|
||||||
type="button"
|
|
||||||
class="flex items-center gap-2 rounded-full"
|
|
||||||
on:click={() => user.addRelay(relay.url)}>
|
|
||||||
<i class="fa fa-right-to-bracket" /> Join
|
|
||||||
</Anchor>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
@ -3,7 +3,8 @@
|
|||||||
import {between} from "hurdak/lib/hurdak"
|
import {between} from "hurdak/lib/hurdak"
|
||||||
import {displayRelay} from "src/util/nostr"
|
import {displayRelay} from "src/util/nostr"
|
||||||
import {poll, stringToHue, hsl} from "src/util/misc"
|
import {poll, stringToHue, hsl} from "src/util/misc"
|
||||||
import pool from 'src/agent/pool'
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
|
import pool from "src/agent/pool"
|
||||||
|
|
||||||
export let relay
|
export let relay
|
||||||
|
|
||||||
@ -20,9 +21,13 @@
|
|||||||
|
|
||||||
<div class="flex items-center gap-2 text-xl">
|
<div class="flex items-center gap-2 text-xl">
|
||||||
<i class={relay.url.startsWith("wss") ? "fa fa-lock" : "fa fa-unlock"} />
|
<i class={relay.url.startsWith("wss") ? "fa fa-lock" : "fa fa-unlock"} />
|
||||||
<span class="border-b border-solid" style={`border-color: ${hsl(stringToHue(relay.url))}`}>
|
<Anchor
|
||||||
|
type="unstyled"
|
||||||
|
href={`/relays/${btoa(relay.url)}`}
|
||||||
|
class="border-b border-solid"
|
||||||
|
style={`border-color: ${hsl(stringToHue(relay.url))}`}>
|
||||||
{displayRelay(relay)}
|
{displayRelay(relay)}
|
||||||
</span>
|
</Anchor>
|
||||||
<span
|
<span
|
||||||
on:mouseout={() => {
|
on:mouseout={() => {
|
||||||
showStatus = false
|
showStatus = false
|
||||||
|
Loading…
Reference in New Issue
Block a user