Add undo buttons to toasts

This commit is contained in:
Eric Bailey
2025-08-26 16:12:20 -05:00
parent dd00a3a765
commit 9ba43051ce
2 changed files with 78 additions and 21 deletions
+73 -19
View File
@@ -1,6 +1,6 @@
import {memo, useCallback} from 'react' import {memo} from 'react'
import {type AppBskyFeedDefs} from '@atproto/api' import {type AppBskyFeedDefs} from '@atproto/api'
import {msg} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import type React from 'react' import type React from 'react'
@@ -9,6 +9,7 @@ import {type Shadow} from '#/state/cache/post-shadow'
import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation' import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation'
import {useTheme} from '#/alf' import {useTheme} from '#/alf'
import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
import * as toast from '#/components/Toast' import * as toast from '#/components/Toast'
import {PostControlButton, PostControlButtonIcon} from './PostControlButton' import {PostControlButton, PostControlButtonIcon} from './PostControlButton'
@@ -27,31 +28,84 @@ export const BookmarkButton = memo(function BookmarkButton({
const {uri, cid, viewer} = post const {uri, cid, viewer} = post
const isBookmarked = !!viewer?.bookmarked const isBookmarked = !!viewer?.bookmarked
const onHandlePress = useCallback(async () => { const undoLabel = _(
msg({
message: `Undo`,
context: `Button label to undo saving/removing a post from saved posts.`,
}),
)
const save = async ({disableUndo}: {disableUndo?: boolean} = {}) => {
try { try {
if (isBookmarked) { await bookmark({
await bookmark({ action: 'create',
action: 'delete', uri,
uri, cid,
}) })
toast.show(_(msg`Removed from saved posts`))
} else { toast.show(
await bookmark({ <toast.Outer>
action: 'create', <toast.Icon />
uri, <toast.Text>
cid, <Trans>Post saved</Trans>
}) </toast.Text>
toast.show(_(msg`Post saved`), { {!disableUndo && (
<toast.Action
label={undoLabel}
onPress={() => remove({disableUndo: true})}>
{undoLabel}
</toast.Action>
)}
</toast.Outer>,
{
type: 'success', type: 'success',
}) },
} )
} catch (e: any) { } catch (e: any) {
const {raw, clean} = cleanError(e) const {raw, clean} = cleanError(e)
toast.show(clean || raw || e, { toast.show(clean || raw || e, {
type: 'error', type: 'error',
}) })
} }
}, [_, uri, cid, isBookmarked, bookmark, cleanError]) }
const remove = async ({disableUndo}: {disableUndo?: boolean} = {}) => {
try {
await bookmark({
action: 'delete',
uri,
})
toast.show(
<toast.Outer>
<toast.Icon icon={TrashIcon} />
<toast.Text>
<Trans>Removed from saved posts</Trans>
</toast.Text>
{!disableUndo && (
<toast.Action
label={undoLabel}
onPress={() => save({disableUndo: true})}>
{undoLabel}
</toast.Action>
)}
</toast.Outer>,
)
} catch (e: any) {
const {raw, clean} = cleanError(e)
toast.show(clean || raw || e, {
type: 'error',
})
}
}
const onHandlePress = async () => {
if (isBookmarked) {
await remove()
} else {
await save()
}
}
return ( return (
<PostControlButton <PostControlButton
+5 -2
View File
@@ -216,17 +216,20 @@ function BookmarkNotFound({
const cleanError = useCleanError() const cleanError = useCleanError()
const [removed, setRemoved] = useState(false) const [removed, setRemoved] = useState(false)
const remove = useCallback(async () => { const remove = async () => {
try { try {
setRemoved(true) setRemoved(true)
await bookmark({action: 'delete', uri: post.uri}) await bookmark({action: 'delete', uri: post.uri})
toast.show(_(msg`Removed from saved posts`), {
type: 'error',
})
} catch (e: any) { } catch (e: any) {
const {raw, clean} = cleanError(e) const {raw, clean} = cleanError(e)
toast.show(clean || raw || e, { toast.show(clean || raw || e, {
type: 'error', type: 'error',
}) })
} }
}, [post.uri, bookmark, cleanError]) }
if (removed) return null if (removed) return null