Manage modals with a mega modal model

This commit is contained in:
Jonathan Staab 2023-04-18 12:22:14 -05:00
parent dfb1916b02
commit e43caba359
8 changed files with 85 additions and 75 deletions

View File

@ -19,7 +19,7 @@
import * as db from "src/agent/db"
import user from "src/agent/user"
import {loadAppData} from "src/app/state"
import {theme, getThemeVariables, modal, openModals} from "src/partials/state"
import {theme, getThemeVariables, modal} from "src/partials/state"
import {logUsage} from "src/app/state"
import SideNav from "src/app/SideNav.svelte"
import Routes from "src/app/Routes.svelte"
@ -54,16 +54,11 @@
onMount(() => {
let scrollY
// Log modals
// Log modals, keep scroll position on body, but don't allow scrolling
const unsubModal = modal.stack.subscribe($stack => {
if ($stack.length > 0) {
logUsage(btoa(["modal", last($stack).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
if (document.body.style.position !== "fixed") {
scrollY = window.scrollY
@ -71,9 +66,11 @@
document.body.style.top = `-${scrollY}px`
document.body.style.position = `fixed`
}
} else {
} else if (scrollY) {
document.body.setAttribute("style", "")
window.scrollTo(0, scrollY)
scrollY = null
}
})
@ -94,7 +91,6 @@
return () => {
unsubModal()
unsubOpenModals()
unsubHistory()
}
})

View File

@ -31,52 +31,54 @@
</script>
{#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 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 m.type === "login/pubkey"}
<LoginPubKey />
{:else if m.type === "login/connect"}
<LoginConnect />
{:else if m.type === "person/feed"}
<PersonFeed pubkey={m.pubkey} />
{: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 m.type === "list/list"}
<ListList />
{:else if m.type === "list/select"}
<ListSelect item={m.item} />
{:else if m.type === "list/edit"}
<ListEdit list={m.list} />
{:else if m.type === "message"}
<Content size="lg">
<div class="text-center">{m.message}</div>
{#if m.spinner}
<Spinner delay={0} />
{/if}
</Content>
{/if}
</Modal>
{#if !m.virtual}
<Modal virtual={false} onEscape={m.noEscape || m !== last($stack) ? null : closeModal}>
{#if m.type === "note/detail"}
{#key m.note.id}
<NoteDetail {...m} invertColors />
{/key}
{: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 m.type === "login/pubkey"}
<LoginPubKey />
{:else if m.type === "login/connect"}
<LoginConnect />
{:else if m.type === "person/feed"}
<PersonFeed pubkey={m.pubkey} />
{: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 m.type === "list/list"}
<ListList />
{:else if m.type === "list/select"}
<ListSelect item={m.item} />
{:else if m.type === "list/edit"}
<ListEdit list={m.list} />
{:else if m.type === "message"}
<Content size="lg">
<div class="text-center">{m.message}</div>
{#if m.spinner}
<Spinner delay={0} />
{/if}
</Content>
{/if}
</Modal>
{/if}
{/each}

View File

@ -2,7 +2,6 @@
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"

View File

@ -10,7 +10,7 @@
export let feedRelay
export let notes
$: filteredNotes = notes.filter(n => n.seen_on.includes(feedRelay.url))
$: filteredNotes = notes.filter(n => n.seen_on?.includes(feedRelay.url))
</script>
<Content>

View File

@ -1,8 +1,9 @@
<script>
import {pluck, find} from "ramda"
import {Tags, displayPerson, displayRelay} from "src/util/nostr"
import {Tags, displayRelay} from "src/util/nostr"
import {modal, toast} from "src/partials/state"
import Heading from "src/partials/Heading.svelte"
import PersonBadge from "src/app/shared/PersonBadge.svelte"
import Content from "src/partials/Content.svelte"
import Button from "src/partials/Button.svelte"
import Input from "src/partials/Input.svelte"
@ -22,10 +23,14 @@
const search = q => {
if (q.startsWith("#")) {
return $searchTopics(q).map(({name}) => ["t", name])
return $searchTopics(q)
.slice(0, 5)
.map(({name}) => ["t", name])
}
return $searchPeople(q).map(({pubkey}) => ["p", pubkey])
return $searchPeople(q)
.slice(0, 5)
.map(({pubkey}) => ["p", pubkey])
}
const _searchRelays = q => pluck("url", $searchRelays(q)).map(url => ["r", url])
@ -63,9 +68,11 @@
<MultiSelect {search} bind:value={values.params}>
<div slot="item" let:item>
{#if item[0] === "p"}
{displayPerson(getPersonWithFallback(item[1]))}
<div class="-my-1">
<PersonBadge inert person={getPersonWithFallback(item[1])} />
</div>
{:else}
#{item[1]}
<strong>#{item[1]}</strong>
{/if}
</div>
</MultiSelect>

View File

@ -1,18 +1,26 @@
<script>
import {randomId} from "hurdak/lib/hurdak"
import {onMount, onDestroy} from "svelte"
import {fly, fade} from "svelte/transition"
import {openModals} from "src/partials/state"
import {modal} from "src/partials/state"
export let virtual = true
export let onEscape = null
let root, content
const id = randomId()
onMount(() => {
openModals.update(n => n + 1)
if (virtual) {
modal.push({id, virtual: true})
}
})
onDestroy(() => {
openModals.update(n => n - 1)
if (virtual) {
modal.remove(id)
}
})
</script>

View File

@ -36,7 +36,7 @@
<div class="mt-2 flex flex-col rounded border border-solid border-gray-6" in:fly={{y: 20}}>
{#each data as item, i}
<button
class="cursor-pointer border-l-2 border-solid border-black py-2 px-4 text-left text-white"
class="cursor-pointer border-l-2 border-solid border-black py-2 px-4 text-left text-gray-1 hover:border-accent hover:bg-gray-7"
class:bg-gray-8={index !== i}
class:bg-gray-7={index === i}
class:border-accent={index === i}

View File

@ -1,4 +1,4 @@
import {prop, fromPairs} from "ramda"
import {prop, whereEq, reject, last, fromPairs} from "ramda"
import {uuid, switcher} from "hurdak/lib/hurdak"
import type {Writable} from "svelte/store"
import {navigate} from "svelte-routing"
@ -44,15 +44,10 @@ toast.show = (type, message, timeout = 5) => {
// Modals
export const openModals = writable(0)
export const modal = {
stack: new WritableList([]) as WritableList<any>,
getCurrent() {
const $stack = get(modal.stack)
const $openModals = get(openModals)
return $stack[$openModals - 1]
return last(get(modal.stack))
},
sync($stack, opts = {}) {
const hash = $stack.length > 0 ? `#m=${$stack.length}` : ""
@ -63,6 +58,9 @@ export const modal = {
return $stack
},
remove(id) {
modal.stack.update($stack => modal.sync(reject(whereEq({id}), $stack)))
},
push(data) {
modal.stack.update($stack => modal.sync($stack.concat(data)))
},