Fix a few bugs

This commit is contained in:
Jonathan Staab 2023-04-18 09:32:07 -05:00
parent eaa517d71a
commit dfb1916b02
11 changed files with 85 additions and 63 deletions

2
.env
View File

@ -1,3 +1,3 @@
VITE_THEME_DARK=transparent:transparent,black:#0f0f0e,white:#FFFFFF,accent:#EB5E28,accent-light:#FB652C,gray-1:#FFFFFF,gray-2:#FAF6F1,gray-3:#F2EBE1,gray-4:#E9E0D3,gray-5:#B3AA98,gray-6:#565249,gray-7:#393530,gray-8:#252422,danger:#ff0000,warning:#ebd112,success:#37ab51,input:#FAF6F1,input-hover:#F2EBE1
VITE_THEME_LIGHT=transparent:transparent,black:#0f0f0e,white:#FFFFFF,accent:#EB5E28,accent-light:#FB652C,gray-8:#FFFFFF,gray-7:#FAF6F1,gray-6:#F2EBE1,gray-5:#E9E0D3,gray-4:#B3AA98,gray-3:#565249,gray-2:#393530,gray-1:#252422,danger:#ff0000,warning:#ebd112,success:#37ab51,input:#FAF6F1,input-hover:#F2EBE1
VITE_THEME_LIGHT=transparent:transparent,black:#0f0f0e,white:#FFFFFF,accent:#EB5E28,accent-light:#FB652C,gray-8:#FFFFFF,gray-7:#FAF6F1,gray-6:#F2EBE1,gray-5:#B3AA98,gray-4:#B3AA98,gray-3:#565249,gray-2:#393530,gray-1:#252422,danger:#ff0000,warning:#ebd112,success:#37ab51,input:#FAF6F1,input-hover:#F2EBE1
VITE_DUFFLEPUD_URL=https://dufflepud.onrender.com

View File

@ -1,10 +1,11 @@
# Current
- [ ] Claim relays bounty
- [ ] Add real search, it's a big hurdle for first-timers/anons
- [ ] Remember message/chat status
- [ ] Fix note nesting
- [ ] Image classification
- https://github.com/bhky/opennsfw2
- [ ] Claim relays bounty
# Core
@ -71,6 +72,7 @@
- [ ] Share button for notes, shows qr code and nevent
- [ ] open web+nostr links like snort
- [ ] Pinned posts ala snort
- [ ] Scroll to top button that appears after scrolling a bit
- [ ] Likes list on note detail. Maybe a sidebar or header for note detail page?
- [ ] Add keyword mutes
- [ ] Show options on note detail for retrieving replies

View File

@ -2,6 +2,7 @@
import {nip19} from "nostr-tools"
import {find, last} from "ramda"
import {onMount} from "svelte"
import {get} from "svelte/store"
import {quantify} from "hurdak/lib/hurdak"
import {findRootId, findReplyId, displayPerson} from "src/util/nostr"
import {formatTimestamp} from "src/util/misc"
@ -55,24 +56,34 @@
return showContext ? true : !r.isContext
})
// If we're already in a note detail modal, don't infinitely nest. But if we're in
// some other modal, make it possible to go back.
const goToNote = data => {
if (modal.getCurrent()?.type === "note/detail") {
modal.replace({type: "note/detail", ...data})
} else {
modal.push({type: "note/detail", ...data})
}
}
const onClick = e => {
const target = e.target as HTMLElement
if (interactive && !["I"].includes(target.tagName) && !target.closest("a")) {
modal.push({type: "note/detail", note})
goToNote({note})
}
}
const goToParent = async () => {
const relays = getRelaysForEventParent(note)
modal.push({type: "note/detail", note: {id: findReplyId(note)}, relays})
goToNote({note: {id: findReplyId(note)}, relays})
}
const goToRoot = async () => {
const relays = getRelaysForEventParent(note)
modal.push({type: "note/detail", note: {id: findRootId(note)}, relays})
goToNote({note: {id: findRootId(note)}, relays})
}
const setBorderHeight = () => {

View File

@ -176,19 +176,19 @@
}
</script>
<div class="flex justify-between text-gray-1">
<div class="flex justify-between text-gray-1" on:click|stopPropagation>
<div
class={cx("flex", {
"pointer-events-none opacity-75": !$canPublish || muted,
})}>
<button class="w-16 text-left" on:click|stopPropagation={reply.start}>
<button class="w-16 text-left" on:click={reply.start}>
<i class="fa fa-reply cursor-pointer" />
{$repliesCount}
</button>
<button
class="w-16 text-left"
class:text-accent={like}
on:click|stopPropagation={() => (like ? deleteReaction(like) : react("+"))}>
on:click={() => (like ? deleteReaction(like) : react("+"))}>
<i
class={cx("fa fa-heart cursor-pointer", {
"fa-beat fa-beat-custom": like,
@ -200,12 +200,12 @@
"pointer-events-none opacity-50": !canZap,
})}
class:text-accent={zap}
on:click|stopPropagation={startZap}>
on:click={startZap}>
<i class="fa fa-bolt cursor-pointer" />
{formatSats($zapsTotal)}
</button>
</div>
<div on:click|stopPropagation class="flex items-center">
<div class="flex items-center">
{#if pool.forceUrls.length === 0}
<!-- Mobile version -->
<div

View File

@ -6,7 +6,7 @@
import {modal} from "src/partials/state"
import Popover from "src/partials/Popover.svelte"
import OverflowMenu from "src/partials/OverflowMenu.svelte"
import {getPubkeyWriteRelays} from "src/agent/relays"
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
import user from "src/agent/user"
import pool from "src/agent/pool"
import {addToList} from "src/app/state"
@ -61,7 +61,7 @@
}
const follow = async () => {
const [{url}] = getPubkeyWriteRelays(person.pubkey)
const [{url}] = sampleRelays(getPubkeyWriteRelays(person.pubkey))
user.addPetname(person.pubkey, url, displayPerson(person))
}

View File

@ -1,12 +1,14 @@
<script lang="ts">
import {last, nth} from "ramda"
import {displayPerson} from "src/util/nostr"
import Anchor from "src/partials/Anchor.svelte"
import user from "src/agent/user"
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
import {getPersonWithFallback} from "src/agent/db"
import {watch} from "src/agent/db"
import PersonCircle from "src/app/shared/PersonCircle.svelte"
import PersonAbout from "src/app/shared/PersonAbout.svelte"
import {routes} from "src/app/state"
export let pubkey
@ -32,16 +34,18 @@
<div class="relative flex flex-col gap-4 py-2 px-3">
<div class="flex gap-4">
<PersonCircle size={14} person={$person} />
<div class="flex flex-grow flex-col gap-2">
<h2 class="text-lg">{displayPerson($person)}</h2>
{#if $person.verified_as}
<div class="flex gap-1 text-sm">
<i class="fa fa-user-check text-accent" />
<span class="text-gray-1">{last($person.verified_as.split("@"))}</span>
</div>
{/if}
</div>
<Anchor type="unstyled" href={routes.person($person.pubkey)} class="flex gap-4">
<PersonCircle size={14} person={$person} />
<div class="flex flex-grow flex-col gap-2">
<h2 class="text-lg">{displayPerson($person)}</h2>
{#if $person.verified_as}
<div class="flex gap-1 text-sm">
<i class="fa fa-user-check text-accent" />
<span class="opacity-75">{last($person.verified_as.split("@"))}</span>
</div>
{/if}
</div>
</Anchor>
<div class="flex gap-4 py-2 text-lg">
{#if $canPublish}
{#if muted}

View File

@ -90,6 +90,10 @@ export const logUsage = async name => {
}
}
// Feed
export const feedsTab = writable("Follows")
// State
export const lastChecked = synced("app/alerts/lastChecked", {})
@ -150,7 +154,7 @@ const processChats = async (pubkey, events) => {
lastChecked.update($lastChecked => {
for (const message of messages) {
const id = Tags.from(message).type("e").values().first()
const id = Tags.from(message).getMeta("e")
if (message.pubkey === pubkey) {
$lastChecked[id] = Math.max($lastChecked[id] || 0, message.created_at)

View File

@ -1,7 +1,7 @@
<script lang="ts">
import cx from "classnames"
import {prop, uniq, indexBy, objOf, filter as _filter} from "ramda"
import {shuffle, synced} from "src/util/misc"
import {prop, indexBy, objOf, filter as _filter} from "ramda"
import {shuffle} from "src/util/misc"
import {Tags} from "src/util/nostr"
import {modal, theme} from "src/partials/state"
import Anchor from "src/partials/Anchor.svelte"
@ -12,38 +12,31 @@
import {getUserFollows, getUserNetwork} from "src/agent/social"
import {sampleRelays, getAllPubkeyWriteRelays, getUserReadRelays} from "src/agent/relays"
import user from "src/agent/user"
import {feedsTab} from "src/app/state"
const {lists, canPublish} = user
const activeTab = synced("views/Feeds/activeTab", "Follows")
const defaultTabs = ["Follows", "Network"]
let relays, filter, tabs
let relays, filter
$: listsByName = indexBy(l => Tags.from(l).getMeta("d"), $lists)
$: {
const defaultTabs = ["Follows", "Network"]
const customTabs = Object.keys(listsByName)
const validTabs = defaultTabs.concat(customTabs)
if (!validTabs.includes($activeTab)) {
$activeTab = validTabs[0]
}
tabs = uniq(defaultTabs.concat($activeTab).concat(customTabs)).slice(0, 3)
}
$: allTabs = defaultTabs.concat(Object.keys(listsByName))
$: $feedsTab = allTabs.includes($feedsTab) ? $feedsTab : defaultTabs[0]
$: visibleTabs = defaultTabs.includes($feedsTab) ? defaultTabs : [defaultTabs[0], $feedsTab]
$: {
if ($activeTab === "Follows") {
if ($feedsTab === "Follows") {
const authors = shuffle(getUserFollows()).slice(0, 256)
filter = {authors}
relays = sampleRelays(getAllPubkeyWriteRelays(authors))
} else if ($activeTab === "Network") {
} else if ($feedsTab === "Network") {
const authors = shuffle(getUserNetwork()).slice(0, 256)
filter = {authors}
relays = sampleRelays(getAllPubkeyWriteRelays(authors))
} else {
const list = listsByName[$activeTab]
const list = listsByName[$feedsTab]
const tags = Tags.from(list)
const authors = tags.type("p").values().all()
const topics = tags.type("t").values().all()
@ -59,14 +52,14 @@
}
const setActiveTab = tab => {
$activeTab = tab
$feedsTab = tab
}
const showLists = () => {
modal.push({type: "list/list"})
}
document.title = $activeTab
document.title = $feedsTab
</script>
<Content>
@ -79,7 +72,7 @@
</Content>
{/if}
<div>
<Tabs {tabs} activeTab={$activeTab} {setActiveTab}>
<Tabs tabs={visibleTabs} activeTab={$feedsTab} {setActiveTab}>
{#if $canPublish}
{#if $lists.length > 1}
<Popover placement="bottom" opts={{hideOnClick: true}} theme="transparent">
@ -89,19 +82,17 @@
class="flex flex-col items-start overflow-hidden rounded border border-solid border-gray-8 bg-black">
{#each $lists as e (e.id)}
{@const meta = Tags.from(e).asMeta()}
{#if meta.d !== $activeTab}
<button
class={cx("w-full py-2 px-3 text-left transition-colors", {
"hover:bg-gray-7": $theme === "dark",
"hover:bg-gray-1": $theme === "light",
})}
on:click={() => {
$activeTab = meta.d
}}>
<i class="fa fa-scroll fa-sm mr-1" />
{meta.d}
</button>
{/if}
<button
class={cx("w-full py-2 px-3 text-left transition-colors", {
"hover:bg-gray-7": $theme === "dark",
"hover:bg-gray-1": $theme === "light",
})}
on:click={() => {
$feedsTab = meta.d
}}>
<i class="fa fa-scroll fa-sm mr-1" />
{meta.d}
</button>
{/each}
<button
on:click={showLists}
@ -118,7 +109,7 @@
{/if}
{/if}
</Tabs>
{#key $activeTab}
{#key $feedsTab}
<Feed {relays} {filter} />
{/key}
</div>

View File

@ -16,6 +16,7 @@
const topics = t
.all()
.map(topic => ({type: "topic", id: topic.name, topic, text: "#" + topic.name}))
const people = p
.all({"kind0.name": {$type: "string"}, pubkey: {$ne: user.getPubkey()}})
.map(person => ({

View File

@ -24,7 +24,9 @@
}} />
<div class="modal fixed inset-0 z-30" bind:this={root} transition:fade>
<div class="fixed inset-0 cursor-pointer bg-black opacity-50" on:click={onEscape} />
<div
class="fixed inset-0 cursor-pointer bg-black opacity-50"
on:click|stopPropagation={onEscape} />
<div
class="modal-content h-full overflow-auto"
bind:this={content}

View File

@ -48,10 +48,18 @@ export const openModals = writable(0)
export const modal = {
stack: new WritableList([]) as WritableList<any>,
sync: ($stack, opts = {}) => {
getCurrent() {
const $stack = get(modal.stack)
const $openModals = get(openModals)
return $stack[$openModals - 1]
},
sync($stack, opts = {}) {
const hash = $stack.length > 0 ? `#m=${$stack.length}` : ""
navigate(window.location.pathname + hash, opts)
if (hash !== window.location.hash) {
navigate(window.location.pathname + hash, opts)
}
return $stack
},
@ -63,8 +71,7 @@ export const modal = {
await sleep(100)
},
async replace(data) {
await modal.pop()
modal.push(data)
modal.stack.update($stack => $stack.slice(0, -1).concat(data))
},
async clear() {
const stackSize = (get(modal.stack) as any).length