forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {memo} from 'react'
2import {type Insets} from 'react-native'
3import {type AppBskyFeedDefs} from '@atproto/api'
4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6import type React from 'react'
7
8import {useCleanError} from '#/lib/hooks/useCleanError'
9import {getTerminology} from '#/lib/strings/terminology'
10import {useTerminologyPreference} from '#/state/preferences'
11import {logger} from '#/logger'
12import {type Shadow} from '#/state/cache/post-shadow'
13import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation'
14import {useRequireAuth} from '#/state/session'
15import {useTheme} from '#/alf'
16import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
17import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
18import * as toast from '#/components/Toast'
19import {PostControlButton, PostControlButtonIcon} from './PostControlButton'
20
21export const BookmarkButton = memo(function BookmarkButton({
22 post,
23 big,
24 logContext,
25 hitSlop,
26}: {
27 post: Shadow<AppBskyFeedDefs.PostView>
28 big?: boolean
29 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
30 hitSlop?: Insets
31}): React.ReactNode {
32 const t = useTheme()
33 const {_} = useLingui()
34 const terminologyPreference = useTerminologyPreference()
35 const {mutateAsync: bookmark} = useBookmarkMutation()
36 const cleanError = useCleanError()
37 const requireAuth = useRequireAuth()
38
39 const {viewer} = post
40 const isBookmarked = !!viewer?.bookmarked
41
42 const undoLabel = _(
43 msg({
44 message: `Undo`,
45 context: `Button label to undo saving/removing a post from saved posts.`,
46 }),
47 )
48
49 const save = async ({disableUndo}: {disableUndo?: boolean} = {}) => {
50 try {
51 await bookmark({
52 action: 'create',
53 post,
54 })
55
56 logger.metric('post:bookmark', {logContext})
57
58 toast.show(
59 <toast.Outer>
60 <toast.Icon />
61 <toast.Text>
62 <Trans>{_(getTerminology(terminologyPreference, {
63 skeet: msg`Skeet saved`,
64 post: msg`Post saved`,
65 spell: msg`Spell saved`,
66 }))}</Trans>
67 </toast.Text>
68 {!disableUndo && (
69 <toast.Action
70 label={undoLabel}
71 onPress={() => remove({disableUndo: true})}>
72 {undoLabel}
73 </toast.Action>
74 )}
75 </toast.Outer>,
76 {
77 type: 'success',
78 },
79 )
80 } catch (e: any) {
81 const {raw, clean} = cleanError(e)
82 toast.show(clean || raw || e, {
83 type: 'error',
84 })
85 }
86 }
87
88 const remove = async ({disableUndo}: {disableUndo?: boolean} = {}) => {
89 try {
90 await bookmark({
91 action: 'delete',
92 uri: post.uri,
93 })
94
95 logger.metric('post:unbookmark', {logContext})
96
97 toast.show(
98 <toast.Outer>
99 <toast.Icon icon={TrashIcon} />
100 <toast.Text>
101 <Trans>{_(getTerminology(terminologyPreference, {
102 skeet: msg`Removed from saved skeets`,
103 post: msg`Removed from saved posts`,
104 spell: msg`Removed from saved spells`,
105 }))}</Trans>
106 </toast.Text>
107 {!disableUndo && (
108 <toast.Action
109 label={undoLabel}
110 onPress={() => save({disableUndo: true})}>
111 {undoLabel}
112 </toast.Action>
113 )}
114 </toast.Outer>,
115 )
116 } catch (e: any) {
117 const {raw, clean} = cleanError(e)
118 toast.show(clean || raw || e, {
119 type: 'error',
120 })
121 }
122 }
123
124 const onHandlePress = () =>
125 requireAuth(async () => {
126 if (isBookmarked) {
127 await remove()
128 } else {
129 await save()
130 }
131 })
132
133 return (
134 <PostControlButton
135 testID="postBookmarkBtn"
136 big={big}
137 label={
138 isBookmarked
139 ? _(getTerminology(terminologyPreference, {
140 skeet: msg`Remove from saved skeets`,
141 post: msg`Remove from saved posts`,
142 spell: msg`Remove from saved spells`,
143 }))
144 : _(getTerminology(terminologyPreference, {
145 skeet: msg`Add to saved skeets`,
146 post: msg`Add to saved posts`,
147 spell: msg`Add to saved spells`,
148 }))
149 }
150 onPress={onHandlePress}
151 hitSlop={hitSlop}>
152 <PostControlButtonIcon
153 fill={isBookmarked ? t.palette.primary_500 : undefined}
154 icon={isBookmarked ? BookmarkFilled : Bookmark}
155 />
156 </PostControlButton>
157 )
158})