Stack modals explicitly

This commit is contained in:
Jonathan Staab 2023-04-12 16:27:50 -05:00
parent 82da83f5e1
commit f019b64695
25 changed files with 104 additions and 109 deletions

View File

@ -7,6 +7,7 @@
- [ ] Ability to create custom feeds
- [ ] Add global/following/network tabs to relay detail
- [ ] Some lnurls aren't working npub1y3k2nheva29y9ej8a22e07epuxrn04rvgy28wvs54y57j7vsxxuq0gvp4j
- [ ] Global search modal that searches within current feed
- [ ] Fix force relays on login: http://localhost:5173/messages/npub1l66wvfm7dxhd6wmvpukpjpyhvwtlxzu0qqajqxjfpr4rlfa8hl5qlkfr3q
- [ ] Image classification
- https://github.com/bhky/opennsfw2

View File

@ -5,8 +5,8 @@
import {onMount} from "svelte"
import {Router, links} from "svelte-routing"
import {globalHistory} from "svelte-routing/src/history"
import {identity, last} from "ramda"
import {first} from "hurdak/lib/hurdak"
import {identity} from "ramda"
import {warn} from "src/util/logger"
import {timedelta, hexToBech32, bech32ToHex, shuffle, now} from "src/util/misc"
import cmd from "src/agent/cmd"
@ -55,9 +55,9 @@
let scrollY
// Log modals
const unsubModal = modal.subscribe($modal => {
if ($modal) {
logUsage(btoa(["modal", $modal.type].join(":")))
const unsubModal = modal.stack.subscribe($stack => {
if ($stack.length > 0) {
logUsage(btoa(["modal", last($stack).type].join(":")))
}
})

View File

@ -11,7 +11,7 @@
const matches = $location.pathname.match(/people\/(npub1[0-9a-z]+)/)
const pubkey = matches ? nip19.decode(matches[1]).data : null
modal.set({type: "note/create", pubkey})
modal.push({type: "note/create", pubkey})
}
</script>

View File

@ -1,4 +1,5 @@
<script lang="ts">
import {last} from "ramda"
import {menuIsOpen} from "src/app/state"
import {modal} from "src/partials/state"
import Modal from "src/partials/Modal.svelte"
@ -15,53 +16,55 @@
import PersonProfileInfo from "src/app/views/PersonProfileInfo.svelte"
import PersonShare from "src/app/views/PersonShare.svelte"
import TopicFeed from "src/app/views/TopicFeed.svelte"
import AddRelay from "src/app/views/RelayAdd.svelte"
import RelayAdd from "src/app/views/RelayAdd.svelte"
const {stack} = modal
const closeModal = async () => {
modal.clear()
modal.pop()
menuIsOpen.set(false)
}
</script>
{#if $modal}
<Modal onEscape={$modal.noEscape ? null : closeModal}>
{#if $modal.type === "note/detail"}
{#key $modal.note.id}
<NoteDetail {...$modal} invertColors />
{#each $stack as m}
<Modal onEscape={m.noEscape || m !== last($stack) ? null : closeModal}>
{#if m.type === "note/detail"}
{#key m.note.id}
<NoteDetail {...m} invertColors />
{/key}
{:else if $modal.type === "note/create"}
<NoteCreate pubkey={$modal.pubkey} nevent={$modal.nevent} />
{:else if $modal.type === "relay/add"}
<AddRelay />
{:else if $modal.type === "onboarding"}
<Onboarding stage={$modal.stage} />
{:else if $modal.type === "room/edit"}
<ChatEdit {...$modal} />
{:else if $modal.type === "login/privkey"}
{:else if m.type === "note/create"}
<NoteCreate pubkey={m.pubkey} nevent={m.nevent} />
{:else if m.type === "relay/add"}
<RelayAdd url={m.url} />
{:else if m.type === "onboarding"}
<Onboarding stage={m.stage} />
{:else if m.type === "room/edit"}
<ChatEdit {...m} />
{:else if m.type === "login/privkey"}
<LoginPrivKey />
{:else if $modal.type === "login/pubkey"}
{:else if m.type === "login/pubkey"}
<LoginPubKey />
{:else if $modal.type === "login/connect"}
{:else if m.type === "login/connect"}
<LoginConnect />
{:else if $modal.type === "person/info"}
<PersonProfileInfo person={$modal.person} />
{:else if $modal.type === "person/share"}
<PersonShare person={$modal.person} />
{:else if $modal.type === "person/follows"}
<PersonList type="follows" pubkey={$modal.pubkey} />
{:else if $modal.type === "person/followers"}
<PersonList type="followers" pubkey={$modal.pubkey} />
{:else if $modal.type === "topic/feed"}
{#key $modal.topic}
<TopicFeed topic={$modal.topic} />
{:else if m.type === "person/info"}
<PersonProfileInfo person={m.person} />
{:else if m.type === "person/share"}
<PersonShare person={m.person} />
{:else if m.type === "person/follows"}
<PersonList type="follows" pubkey={m.pubkey} />
{:else if m.type === "person/followers"}
<PersonList type="followers" pubkey={m.pubkey} />
{:else if m.type === "topic/feed"}
{#key m.topic}
<TopicFeed topic={m.topic} />
{/key}
{:else if $modal.type === "message"}
{:else if m.type === "message"}
<Content size="lg">
<div class="text-center">{$modal.message}</div>
{#if $modal.spinner}
<div class="text-center">{m.message}</div>
{#if m.spinner}
<Spinner delay={0} />
{/if}
</Content>
{/if}
</Modal>
{/if}
{/each}

View File

@ -1,11 +1,10 @@
<script lang="ts">
import {onMount} from "svelte"
import {partition, always, propEq, uniqBy, sortBy, prop} from "ramda"
import {last, partition, always, propEq, uniqBy, sortBy, prop} from "ramda"
import {fly} from "svelte/transition"
import {quantify} from "hurdak/lib/hurdak"
import {createScroller, now, timedelta, Cursor} from "src/util/misc"
import {asDisplayEvent, mergeFilter} from "src/util/nostr"
import {modal} from "src/partials/state"
import Spinner from "src/partials/Spinner.svelte"
import Modal from "src/partials/Modal.svelte"
import Content from "src/partials/Content.svelte"
@ -17,7 +16,6 @@
export let filter
export let relays = []
export let inModal = false
export let delta = timedelta(6, "hours")
export let shouldDisplay = always(true)
export let parentsTimeout = 500
@ -32,17 +30,14 @@
const maxNotes = 100
const cursor = new Cursor({delta})
const seen = new Set()
const getModal = () => last(document.querySelectorAll(".modal-content"))
const setFeedRelay = relay => {
feedRelay = relay
setTimeout(() => {
feedScroller?.stop()
feedScroller = !relay
? null
: createScroller(loadMore, {
element: document.querySelector(".modal-content"),
})
feedScroller = !relay ? null : createScroller(loadMore, {element: getModal()})
}, 300)
}
@ -106,11 +101,6 @@
}
const loadMore = async () => {
console.log("here")
if ($modal && !inModal) {
return
}
// Wait for this page to load before trying again
await network.load({
relays: feedRelay ? [feedRelay] : relays,
@ -129,9 +119,7 @@
onChunk,
})
const scroller = createScroller(loadMore, {
element: inModal ? document.querySelector(".modal-content") : null,
})
const scroller = createScroller(loadMore, {element: getModal()})
return () => {
scroller.stop()

View File

@ -59,20 +59,20 @@
const target = e.target as HTMLElement
if (interactive && !["I"].includes(target.tagName) && !target.closest("a")) {
modal.set({type: "note/detail", note})
modal.push({type: "note/detail", note})
}
}
const goToParent = async () => {
const relays = getRelaysForEventParent(note)
modal.set({type: "note/detail", note: {id: findReplyId(note)}, relays})
modal.push({type: "note/detail", note: {id: findReplyId(note)}, relays})
}
const goToRoot = async () => {
const relays = getRelaysForEventParent(note)
modal.set({type: "note/detail", note: {id: findRootId(note)}, relays})
modal.push({type: "note/detail", note: {id: findRootId(note)}, relays})
}
const setBorderHeight = () => {

View File

@ -50,7 +50,7 @@
toast.show("info", "Note link copied to clipboard!")
}
const quote = () => modal.set({type: "note/create", nevent})
const quote = () => modal.push({type: "note/create", nevent})
const mute = () => user.addMute("e", note.id)
const unmute = () => user.removeMute(note.id)

View File

@ -106,11 +106,11 @@
}
const openQuote = id => {
modal.set({type: "note/detail", note: {id}})
modal.push({type: "note/detail", note: {id}})
}
const openTopic = topic => {
modal.set({type: "topic/feed", topic})
modal.push({type: "topic/feed", topic})
}
</script>

View File

@ -219,7 +219,7 @@ export const loadAppData = async pubkey => {
export const login = (method, key) => {
keys.login(method, key)
modal.set({type: "login/connect", noEscape: true})
modal.push({type: "login/connect", noEscape: true})
}
export const mergeParents = (notes: Array<DisplayEvent>) => {

View File

@ -36,7 +36,7 @@
})
const edit = () => {
modal.set({type: "room/edit", room: $room})
modal.push({type: "room/edit", room: $room})
}
const sendMessage = async content => {

View File

@ -48,7 +48,7 @@
rooms.patch({id: event.id, joined: true})
}
modal.set(null)
modal.pop()
}
}
</script>

View File

@ -40,7 +40,7 @@
<i class="fa fa-server fa-lg" />
<h2 class="staatliches text-2xl">Your rooms</h2>
</div>
<Anchor type="button-accent" on:click={() => modal.set({type: "room/edit"})}>
<Anchor type="button-accent" on:click={() => modal.push({type: "room/edit"})}>
<i class="fa-solid fa-plus" /> Create Room
</Anchor>
</div>

View File

@ -16,16 +16,16 @@
if (nostr) {
login("extension", await nostr.getPublicKey())
} else {
modal.set({type: "login/privkey"})
modal.push({type: "login/privkey"})
}
}
const signUp = () => {
modal.set({type: "onboarding", stage: "intro"})
modal.push({type: "onboarding", stage: "intro"})
}
const pubkeyLogIn = () => {
modal.set({type: "login/pubkey"})
modal.push({type: "login/pubkey"})
}
if (user.getPubkey()) {

View File

@ -43,7 +43,7 @@
<Card
interactive
class="flex flex-col gap-2 text-left"
on:click={() => modal.set({type: "note/detail", note})}>
on:click={() => modal.push({type: "note/detail", note})}>
<div class="relative flex w-full items-center justify-between gap-2" on:click|stopPropagation>
{#if !event.ref}
<div class="flex items-center gap-2">

View File

@ -65,7 +65,7 @@
loadAppData(user.getPubkey())
modal.set(null)
modal.pop()
navigate("/notes/follows")
}

View File

@ -40,7 +40,7 @@
</p>
<Anchor
type="button-accent"
on:click={() => modal.set({type: "onboarding", stage: "complete"})}>
on:click={() => modal.push({type: "onboarding", stage: "complete"})}>
Continue
</Anchor>
</Content>

View File

@ -19,7 +19,7 @@
When youre ready to dive in, click below and well guide you through the process of creating an
account.
</p>
<Anchor type="button-accent" on:click={() => modal.set({type: "onboarding", stage: "key"})}>
<Anchor type="button-accent" on:click={() => modal.push({type: "onboarding", stage: "key"})}>
Let's go!
</Anchor>
</Content>

View File

@ -30,7 +30,9 @@
<i slot="before" class="fa fa-lock" />
<button slot="after" class="fa fa-copy cursor-pointer" on:click={copyKey} />
</Input>
<Anchor type="button-accent" on:click={() => modal.set({type: "onboarding", stage: nextStage})}>
<Anchor
type="button-accent"
on:click={() => modal.push({type: "onboarding", stage: nextStage})}>
Log in
</Anchor>
</div>

View File

@ -39,7 +39,9 @@
Select your preferred storage relays below, or click "continue" to use some reasonable defaults.
You can change your selection any time.
</p>
<Anchor type="button-accent" on:click={() => modal.set({type: "onboarding", stage: "follows"})}>
<Anchor
type="button-accent"
on:click={() => modal.push({type: "onboarding", stage: "follows"})}>
Continue
</Anchor>
</Content>

View File

@ -132,11 +132,11 @@
const setActiveTab = tab => navigate(routes.person(pubkey, tab))
const showFollows = () => {
modal.set({type: "person/follows", pubkey})
modal.push({type: "person/follows", pubkey})
}
const showFollowers = () => {
modal.set({type: "person/followers", pubkey})
modal.push({type: "person/followers", pubkey})
}
const follow = async () => {
@ -158,11 +158,11 @@
}
const openProfileInfo = () => {
modal.set({type: "person/info", $person})
modal.push({type: "person/info", $person})
}
const share = () => {
modal.set({type: "person/share", $person})
modal.push({type: "person/share", $person})
}
</script>

View File

@ -8,7 +8,7 @@
import {toast, modal} from "src/partials/state"
import {loadAppData} from "src/app/state"
let url = $modal.url
export let url
const submit = async e => {
e.preventDefault()
@ -28,7 +28,7 @@
return toast.show("error", "That isn't a valid websocket url")
}
modal.set(null)
modal.pop()
await user.addRelay(url)

View File

@ -19,7 +19,7 @@
<i class="fa fa-server fa-lg" />
<h2 class="staatliches text-2xl">Your relays</h2>
</div>
<Anchor type="button-accent" on:click={() => modal.set({type: "relay/add"})}>
<Anchor type="button-accent" on:click={() => modal.push({type: "relay/add"})}>
<i class="fa-solid fa-plus" /> Add Relay
</Anchor>
</div>

View File

@ -11,5 +11,5 @@
<Content>
<Heading class="text-center">{topic}</Heading>
<Feed inModal {relays} {filter} />
<Feed {relays} {filter} />
</Content>

View File

@ -43,7 +43,7 @@
return {selection, node, offset, word}
}
const autocomplete = ({person, force = false}) => {
const autocomplete = ({person = null, force = false} = {}) => {
const {selection, node, offset, word} = getInfo()
const annotate = (prefix, text, value) => {
@ -70,7 +70,7 @@
}
// Mentions
if ((force || word.length > 1) && word.startsWith("@")) {
if ((force || word.length > 1) && word.startsWith("@") && person) {
annotate("@", displayPerson(person).trim(), pubkeyEncoder.encode(person.pubkey))
}
@ -102,6 +102,11 @@
if (["Enter"].includes(e.code)) {
autocomplete({person: suggestions.get()})
}
// Only autocomplete topics on space
if (["Space"].includes(e.code)) {
autocomplete()
}
}
const onKeyUp = e => {

View File

@ -1,10 +1,10 @@
import {prop, fromPairs, last} from "ramda"
import {prop, fromPairs} from "ramda"
import {uuid, switcher} from "hurdak/lib/hurdak"
import type {Writable} from "svelte/store"
import {navigate} from "svelte-routing"
import {writable, get} from "svelte/store"
import {globalHistory} from "svelte-routing/src/history"
import {sleep, synced} from "src/util/misc"
import {sleep, synced, WritableList} from "src/util/misc"
// Location
@ -47,38 +47,32 @@ toast.show = (type, message, timeout = 5) => {
export const openModals = writable(0)
export const modal = {
history: [],
set: data => {
if (data) {
modal.history.push(data)
navigate(window.location.pathname + `#m=${modal.history.length - 1}`)
} else {
modal.history = []
navigate(window.location.pathname)
}
stack: new WritableList([]),
sync: $stack => {
const hash = $stack.length > 0 ? `#m=${$stack.length}` : ""
navigate(window.location.pathname + hash)
return $stack
},
close: () => modal.set(null),
push: data => modal.stack.update($stack => modal.sync($stack.concat(data))),
pop: () => modal.stack.update($stack => modal.sync($stack.slice(0, -1))),
clear: async () => {
// Reverse history so the back button doesn't bring our modal back up
while (get(modal)) {
while (get(modal.stack)) {
history.back()
await sleep(30)
await sleep(100)
}
},
subscribe: cb => {
cb(last(modal.history))
return location.subscribe($location => {
const match = $location.hash.match(/\bm=(\d+)/)
const i = match ? parseInt(match[1]) : null
modal.history.splice(i === null ? -1 : i + 1)
cb(modal.history[i])
})
},
}
location.subscribe($location => {
const match = $location.hash.match(/\bm=(\d+)/)
const i = match ? parseInt(match[1]) : 0
modal.stack.update($stack => (i < $stack.length ? $stack.slice(0, i) : $stack))
})
// Themes
const parseTheme = s => fromPairs(s.split(",").map(x => x.split(":")))