-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* enhance notices view * UI refinements * multiple select button * align button to the left * filter out undefined selected * refactor connectionstatus code * add subscription Notices * track subscriptions notices and display them * clean up and refactor * - add fuzzy search - Publishes view refactor * log verbs are configurable via environment variable * have notice verb settings hardcoded * tighten the fuzzy search threshold
- Loading branch information
Showing
8 changed files
with
420 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,22 @@ | ||
<script lang="ts"> | ||
import {pluralize, seconds} from "hurdak" | ||
import {assoc, now, remove, sortBy} from "@welshman/lib" | ||
import {LOCAL_RELAY_URL} from "@welshman/util" | ||
import {PublishStatus} from "@welshman/net" | ||
import Tile from "src/partials/Tile.svelte" | ||
import PublishesConnections from "src/app/views/PublishesConnections.svelte" | ||
import PublishesNotices from "src/app/views/PublishesNotices.svelte" | ||
import PublishesEvents from "src/app/views/PublishesEvents.svelte" | ||
import Subheading from "src/partials/Subheading.svelte" | ||
import PublishCard from "src/app/shared/PublishCard.svelte" | ||
import {thunks, type Thunk} from "@welshman/app" | ||
import {get} from "svelte/store" | ||
import Tabs from "src/partials/Tabs.svelte" | ||
const hasStatus = (thunk: Thunk, statuses: PublishStatus[]) => | ||
Object.values(get(thunk.status)).some(s => statuses.includes(s.status)) | ||
const tabs = ["events", "connections", "notices"] | ||
let activeTab = "events" | ||
$: recent = (Object.values($thunks) as Thunk[]).filter( | ||
t => | ||
remove(LOCAL_RELAY_URL, t.request.relays).length > 0 && | ||
t.event.created_at > now() - seconds(24, "hour"), | ||
) | ||
$: relays = new Set( | ||
remove( | ||
LOCAL_RELAY_URL, | ||
recent.flatMap(({request}) => request.relays), | ||
), | ||
) | ||
$: success = recent.filter(t => hasStatus(t, [PublishStatus.Success])) | ||
$: pending = recent.filter( | ||
t => hasStatus(t, [PublishStatus.Pending]) && !hasStatus(t, [PublishStatus.Success]), | ||
) | ||
// If the page gets refreshed before pending finishes, it hangs. Set stuff to failed | ||
$: { | ||
for (const t of recent) { | ||
if (t.event.created_at < now() - seconds(1, "minute")) { | ||
for (const [url, s] of Object.entries(t.status)) { | ||
if (s.status === PublishStatus.Pending) { | ||
t.status.update(assoc(url, {status: PublishStatus.Failure, message: ""})) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
let selected: string | ||
</script> | ||
|
||
<Subheading>Published Events</Subheading> | ||
<div class="grid grid-cols-4 justify-between gap-2 sm:grid-cols-5"> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{recent.length}</p> | ||
<span class="text-sm">{pluralize(recent.length, "Event")}</span> | ||
</Tile> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{relays.size}</p> | ||
<span class="text-sm">{pluralize(relays.size, "Relay")}</span> | ||
</Tile> | ||
<Tile background lass="hidden sm:block"> | ||
<p class="text-lg sm:text-2xl">{pending.length}</p> | ||
<span class="text-sm">Pending</span> | ||
</Tile> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{success.length}</p> | ||
<span class="text-sm">Succeeded</span> | ||
</Tile> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{recent.length - pending.length - success.length}</p> | ||
<span class="text-sm">Failed</span> | ||
</Tile> | ||
</div> | ||
{#each sortBy(t => -t.event.created_at, recent) as thunk (thunk.event.id)} | ||
<PublishCard {thunk} /> | ||
{/each} | ||
<Tabs {tabs} {activeTab} setActiveTab={tab => (activeTab = tab)} /> | ||
{#if activeTab === "events"} | ||
<PublishesEvents /> | ||
{:else if activeTab === "connections"} | ||
<PublishesConnections bind:selected bind:activeTab /> | ||
{:else if activeTab === "notices"} | ||
<PublishesNotices search={selected} /> | ||
{/if} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<script lang="ts"> | ||
import {relaysByUrl} from "@welshman/app" | ||
import {addToMapKey, ctx} from "@welshman/lib" | ||
import {displayRelayUrl} from "@welshman/util" | ||
import {quantify} from "hurdak" | ||
import {onMount} from "svelte" | ||
import AltColor from "src/partials/AltColor.svelte" | ||
import SelectButton from "src/partials/SelectButton.svelte" | ||
import {ConnectionType, displayConnectionType, getConnectionStatus} from "src/domain/connection" | ||
export let selected: string | ||
export let activeTab: string | ||
let selectedOptions: ConnectionType[] = [] | ||
let connectionsStatus: Map<ConnectionType, Set<string>> = new Map() | ||
const options = [ | ||
ConnectionType.Connected, | ||
ConnectionType.Logging, | ||
ConnectionType.LoginFailed, | ||
ConnectionType.ConnectFailed, | ||
ConnectionType.WaitReconnect, | ||
ConnectionType.NotConnected, | ||
ConnectionType.UnstableConnection, | ||
] | ||
$: connections = Array.from(ctx.net.pool.data.keys()).filter(url => | ||
selectedOptions.length ? selectedOptions.some(s => connectionsStatus.get(s)?.has(url)) : true, | ||
) | ||
function fetchConnectionStatus() { | ||
const newConnectionStatus: Map<ConnectionType, Set<string>> = new Map() | ||
for (const [url, cxn] of ctx.net.pool.data.entries()) { | ||
addToMapKey(newConnectionStatus, getConnectionStatus(cxn), url) | ||
} | ||
connectionsStatus = newConnectionStatus | ||
} | ||
onMount(() => { | ||
fetchConnectionStatus() | ||
const interval = setInterval(fetchConnectionStatus, 800) | ||
return () => { | ||
clearInterval(interval) | ||
} | ||
}) | ||
</script> | ||
|
||
<SelectButton {options} bind:value={selectedOptions} multiple class="text-left"> | ||
<div class="flex items-center gap-2" slot="item" let:option> | ||
{connectionsStatus.get(option)?.size || 0} | ||
{displayConnectionType(option)} | ||
</div> | ||
</SelectButton> | ||
{#each connections as url (url)} | ||
{@const relay = $relaysByUrl.get(url)} | ||
<AltColor | ||
background | ||
class="cursor-pointer justify-between rounded-md p-6 shadow" | ||
on:click={() => { | ||
selected = url | ||
activeTab = "notices" | ||
}}> | ||
<div class="flex min-w-0 shrink-0 items-start gap-3"> | ||
{#if relay?.profile?.icon} | ||
<img class="h-9 w-9 shrink-0 rounded-full border" src={relay.profile.icon} /> | ||
{:else} | ||
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border"> | ||
<i class="fa fa-server text-xl text-neutral-100"></i> | ||
</div> | ||
{/if} | ||
<div class="shrink-0"> | ||
<div class="flex items-center gap-2"> | ||
<div class="text-md overflow-hidden text-ellipsis whitespace-nowrap"> | ||
{displayRelayUrl(url)} | ||
</div> | ||
</div> | ||
<div class="flex gap-4 text-xs text-neutral-400"> | ||
{#if relay?.profile?.supported_nips} | ||
<span> | ||
{relay.profile.supported_nips.length} NIPs | ||
</span> | ||
{/if} | ||
<span> | ||
Connected {quantify(relay?.stats?.open_count || 0, "time")} | ||
</span> | ||
</div> | ||
</div> | ||
<div class="flex w-full items-center justify-end gap-2 text-sm"> | ||
{#each options.filter(o => connectionsStatus.get(o)?.has(url)) as o} | ||
{@const opt = displayConnectionType(o)} | ||
<div class="flex items-center gap-2"> | ||
<span>{opt}</span> | ||
<div | ||
class:!bg-danger={opt.includes("Failed") || opt.includes("Not")} | ||
class:!bg-warning={opt == "Logging in" || | ||
o == ConnectionType.WaitReconnect || | ||
o == ConnectionType.UnstableConnection} | ||
class="h-3 w-3 rounded-full bg-success" /> | ||
</div> | ||
{/each} | ||
</div> | ||
</div> | ||
</AltColor> | ||
{/each} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<script lang="ts"> | ||
import {thunks, type Thunk} from "@welshman/app" | ||
import {assoc, now, remove, sortBy} from "@welshman/lib" | ||
import {pluralize, seconds} from "hurdak" | ||
import Tile from "src/partials/Tile.svelte" | ||
import PublishCard from "src/app/shared/PublishCard.svelte" | ||
import {LOCAL_RELAY_URL} from "@welshman/util" | ||
import {PublishStatus} from "@welshman/net" | ||
import {get} from "svelte/store" | ||
const hasStatus = (thunk: Thunk, statuses: PublishStatus[]) => | ||
Object.values(get(thunk.status)).some(s => statuses.includes(s.status)) | ||
$: recent = (Object.values($thunks) as Thunk[]).filter( | ||
t => | ||
remove(LOCAL_RELAY_URL, t.request.relays).length > 0 && | ||
t.event.created_at > now() - seconds(24, "hour"), | ||
) | ||
$: relays = new Set( | ||
remove( | ||
LOCAL_RELAY_URL, | ||
recent.flatMap(({request}) => request.relays), | ||
), | ||
) | ||
$: success = recent.filter(t => hasStatus(t, [PublishStatus.Success])) | ||
$: pending = recent.filter( | ||
t => hasStatus(t, [PublishStatus.Pending]) && !hasStatus(t, [PublishStatus.Success]), | ||
) | ||
// If the page gets refreshed before pending finishes, it hangs. Set stuff to failed | ||
$: { | ||
for (const t of recent) { | ||
if (t.event.created_at < now() - seconds(1, "minute")) { | ||
for (const [url, s] of Object.entries(t.status)) { | ||
if (s.status === PublishStatus.Pending) { | ||
t.status.update(assoc(url, {status: PublishStatus.Failure, message: ""})) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<div class="grid grid-cols-4 justify-between gap-2 sm:grid-cols-5"> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{recent.length}</p> | ||
<span class="text-sm">{pluralize(recent.length, "Event")}</span> | ||
</Tile> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{relays.size}</p> | ||
<span class="text-sm">{pluralize(relays.size, "Relay")}</span> | ||
</Tile> | ||
<Tile background lass="hidden sm:block"> | ||
<p class="text-lg sm:text-2xl">{pending.length}</p> | ||
<span class="text-sm">Pending</span> | ||
</Tile> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{success.length}</p> | ||
<span class="text-sm">Succeeded</span> | ||
</Tile> | ||
<Tile background> | ||
<p class="text-lg sm:text-2xl">{recent.length - pending.length - success.length}</p> | ||
<span class="text-sm">Failed</span> | ||
</Tile> | ||
</div> | ||
{#each sortBy(t => -t.event.created_at, recent) as thunk (thunk.event.id)} | ||
<PublishCard {thunk} /> | ||
{/each} |
Oops, something went wrong.