SatsAllDay 0ee056b2a1
Toast (#431)
* Prototype of toast system

* More toast adoption

* share
* flag
* bookmark
* subscribe
* delete

* More toast usage:

* forms
* settings save

* Log error during flag failure

* Incorporate PR feedback:

1. return `toaster` from `useToast` hook, with simplified `success` and `danger` methods

2. remove toast header, move close button to body

3. change how toast ids are generated to use a global incrementing int

4. update toast messages

* PR feedback:

* reduce width of toast on narrow screens
* dynamic delete success toast message based on deleted type
* add toasts to auth methods deletion operations

* Dismiss all toasts upon page navigation

* refine style and use delay prop

* more styling

---------

Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
2023-08-25 18:21:51 -05:00

98 lines
2.5 KiB
JavaScript

import { useMutation } from '@apollo/client'
import { gql } from 'graphql-tag'
import { useState } from 'react'
import Alert from 'react-bootstrap/Alert'
import Button from 'react-bootstrap/Button'
import Dropdown from 'react-bootstrap/Dropdown'
import { useShowModal } from './modal'
import { useToast } from './toast'
export default function Delete ({ itemId, children, onDelete, type = 'post' }) {
const showModal = useShowModal()
const [deleteItem] = useMutation(
gql`
mutation deleteItem($id: ID!) {
deleteItem(id: $id) {
text
title
url
pollCost
deletedAt
}
}`, {
update (cache, { data: { deleteItem } }) {
cache.modify({
id: `Item:${itemId}`,
fields: {
text: () => deleteItem.text,
title: () => deleteItem.title,
url: () => deleteItem.url,
pollCost: () => deleteItem.pollCost,
deletedAt: () => deleteItem.deletedAt
}
})
}
}
)
return (
<span
className='pointer' onClick={() => {
showModal(onClose => {
return (
<DeleteConfirm
type={type}
onConfirm={async () => {
const { error } = await deleteItem({ variables: { id: itemId } })
if (error) {
throw new Error({ message: error.toString() })
}
if (onDelete) {
onDelete()
}
onClose()
}}
/>
)
})
}}
>{children}
</span>
)
}
function DeleteConfirm ({ onConfirm, type }) {
const [error, setError] = useState()
const toaster = useToast()
return (
<>
{error && <Alert variant='danger' onClose={() => setError(undefined)} dismissible>{error}</Alert>}
<p className='fw-bolder'>Are you sure? This is a gone forever kind of delete.</p>
<div className='d-flex justify-content-end'>
<Button
variant='danger' onClick={async () => {
try {
await onConfirm()
toaster.success(`deleted ${type.toLowerCase()}`)
} catch (e) {
setError(e.message || e)
}
}}
>delete
</Button>
</div>
</>
)
}
export function DeleteDropdownItem (props) {
return (
<Delete {...props}>
<Dropdown.Item>
delete
</Dropdown.Item>
</Delete>
)
}