Introduce state controllers, avoid double submit on group create. Fixes #326

This commit is contained in:
Jon Staab 2024-05-20 15:20:17 -07:00
parent c0e870907d
commit b6027a608f
4 changed files with 69 additions and 30 deletions

View File

@ -29,6 +29,7 @@
- [x] Only tag parent event in reactions - [x] Only tag parent event in reactions
- [x] Fix url hashes, render code blocks - [x] Fix url hashes, render code blocks
- [x] Use bitcoin connect instead of webln - [x] Use bitcoin connect instead of webln
- [x] Fix double submit on group create dialog
# 0.4.4 # 0.4.4

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import {debounce} from "throttle-debounce" import {debounce} from "throttle-debounce"
import {isSearchFeed, makeSearchFeed, makeScopeFeed, Scope, getFeedArgs} from "@welshman/feeds" import {isSearchFeed, makeSearchFeed, makeScopeFeed, Scope, getFeedArgs} from "@welshman/feeds"
import {boolCtrl} from 'src/partials/utils'
import Modal from "src/partials/Modal.svelte" import Modal from "src/partials/Modal.svelte"
import Input from "src/partials/Input.svelte" import Input from "src/partials/Input.svelte"
import Popover2 from "src/partials/Popover2.svelte" import Popover2 from "src/partials/Popover2.svelte"
@ -17,25 +18,19 @@
feed.definition = normalizeFeedDefinition(feed.definition) feed.definition = normalizeFeedDefinition(feed.definition)
const form = boolCtrl()
const listMenu = boolCtrl()
const followsFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Follows))}) const followsFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Follows))})
const networkFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Network))}) const networkFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Network))})
const openListMenu = () => {
listMenuIsOpen = true
}
const closeListMenu = () => {
listMenuIsOpen = false
}
const openForm = () => { const openForm = () => {
savePoint = {...feed} savePoint = {...feed}
formIsOpen = true $form.enable()
} }
const closeForm = () => { const closeForm = () => {
feed = savePoint feed = savePoint
formIsOpen = false $form.disable()
} }
const getSearch = definition => (getFeedArgs(definition)?.find(isSearchFeed)?.[1] as string) || "" const getSearch = definition => (getFeedArgs(definition)?.find(isSearchFeed)?.[1] as string) || ""
@ -43,8 +38,8 @@
const setFeedDefinition = definition => { const setFeedDefinition = definition => {
feed.definition = definition feed.definition = definition
search = getSearch(definition) search = getSearch(definition)
formIsOpen = false $form.disable()
closeListMenu() $listMenu.disable()
updateFeed(feed) updateFeed(feed)
} }
@ -88,8 +83,6 @@
}) })
let savePoint let savePoint
let formIsOpen = false
let listMenuIsOpen = false
let search = getSearch(feed.definition) let search = getSearch(feed.definition)
$: subFeeds = getFeedArgs(feed.definition as any) $: subFeeds = getFeedArgs(feed.definition as any)
@ -111,11 +104,11 @@
<div class="relative lg:hidden"> <div class="relative lg:hidden">
<div <div
class="flex h-7 w-6 cursor-pointer items-center justify-center rounded bg-neutral-700 text-center text-neutral-50 transition-colors hover:bg-neutral-600" class="flex h-7 w-6 cursor-pointer items-center justify-center rounded bg-neutral-700 text-center text-neutral-50 transition-colors hover:bg-neutral-600"
on:click={openListMenu}> on:click={$listMenu.enable}>
<i class="fa fa-sm fa-ellipsis-v" /> <i class="fa fa-sm fa-ellipsis-v" />
</div> </div>
{#if listMenuIsOpen} {#if $listMenu.enabled}
<Popover2 absolute hideOnClick onClose={closeListMenu} class="right-0 top-8 w-60"> <Popover2 absolute hideOnClick onClose={$listMenu.disable} class="right-0 top-8 w-60">
<Menu> <Menu>
<MenuItem inert class="staatliches bg-neutral-800 text-lg shadow">Your Feeds</MenuItem> <MenuItem inert class="staatliches bg-neutral-800 text-lg shadow">Your Feeds</MenuItem>
<div class="max-h-96 overflow-auto"> <div class="max-h-96 overflow-auto">
@ -146,7 +139,7 @@
</div> </div>
</div> </div>
{#if formIsOpen} {#if $form.enabled}
<Modal onEscape={closeForm}> <Modal onEscape={closeForm}>
<FeedForm {feed} exit={exitForm} apply={() => setFeedDefinition(feed.definition)} /> <FeedForm {feed} exit={exitForm} apply={() => setFeedDefinition(feed.definition)} />
</Modal> </Modal>

View File

@ -20,6 +20,7 @@
import {ucFirst} from "hurdak" import {ucFirst} from "hurdak"
import {Address} from "@welshman/util" import {Address} from "@welshman/util"
import {fly} from "src/util/transition" import {fly} from "src/util/transition"
import {formCtrl} from "src/partials/utils"
import {showInfo, showWarning} from "src/partials/Toast.svelte" import {showInfo, showWarning} from "src/partials/Toast.svelte"
import Field from "src/partials/Field.svelte" import Field from "src/partials/Field.svelte"
import FieldInline from "src/partials/FieldInline.svelte" import FieldInline from "src/partials/FieldInline.svelte"
@ -62,23 +63,23 @@
values.feeds = values.feeds.toSpliced(i, 1) values.feeds = values.feeds.toSpliced(i, 1)
} }
const submit = async () => { const ctrl = formCtrl({
if (values.relays.length < 1) { submit: async () => {
showWarning("At least one relay is required.") if (values.relays.length < 1) {
showWarning("At least one relay is required.")
} else {
await onSubmit(values)
return showInfo("Your group has been saved!")
} }
},
await onSubmit(values) })
showInfo("Your group has been saved!")
}
let feedsInput let feedsInput
let showAdvanced = false let showAdvanced = false
</script> </script>
<form on:submit|preventDefault={submit} in:fly={{y: 20}}> <form on:submit|preventDefault={$ctrl.submit} in:fly={{y: 20}}>
<FlexColumn> <FlexColumn>
<div class="mb-4 flex flex-col items-center justify-center"> <div class="mb-4 flex flex-col items-center justify-center">
<Heading>{ucFirst(mode)} Group</Heading> <Heading>{ucFirst(mode)} Group</Heading>
@ -180,7 +181,7 @@
<span>Show Advanced Settings</span> <span>Show Advanced Settings</span>
</Anchor> </Anchor>
{/if} {/if}
<Anchor button tag="button" type="submit">{buttonText}</Anchor> <Anchor button loading={$ctrl.loading} tag="button" type="submit">{buttonText}</Anchor>
</div> </div>
</FlexColumn> </FlexColumn>
</form> </form>

44
src/partials/utils.ts Normal file
View File

@ -0,0 +1,44 @@
import {writable, assoc} from '@welshman/lib'
export const formCtrl = ({submit}) => {
const store = writable({
loading: false,
submit: async () => {
if (store.get().loading) {
return
}
store.update(assoc("loading", true))
try {
await submit()
} catch (e) {
// pass
}
store.update(assoc("loading", false))
},
})
return store
}
export const boolCtrl = ({defaultValue = false} = {}) => {
const store = writable({
enabled: defaultValue,
disable: () => store.update(assoc('enabled', false)),
enable: () => store.update(assoc('enabled', true)),
})
return store
}
export const valueCtrl = ({defaultValue = null} = {}) => {
const store = writable({
enabled: defaultValue,
clear: () => store.update(assoc('enabled', null)),
set: value => store.update(assoc('enabled', value)),
})
return store
}