+111
src/components/PostControls/PostMenu/PostMenuItems.tsx
+111
src/components/PostControls/PostMenu/PostMenuItems.tsx
···
8
8
import * as Clipboard from 'expo-clipboard'
9
9
import {
10
10
type AppBskyEmbedExternal,
11
+
type AppBskyEmbedImages,
11
12
type AppBskyEmbedRecordWithMedia,
12
13
type AppBskyEmbedVideo,
13
14
type AppBskyFeedDefs,
···
15
16
type AppBskyFeedThreadgate,
16
17
AtUri,
17
18
type RichText as RichTextAPI,
19
+
AppBskyEmbedRecord,
18
20
} from '@atproto/api'
19
21
import {msg} from '@lingui/macro'
20
22
import {useLingui} from '@lingui/react'
···
79
81
import {Eye_Stroke2_Corner0_Rounded as Eye} from '#/components/icons/Eye'
80
82
import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
81
83
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
84
+
import {Pencil_Stroke2_Corner0_Rounded as Pen} from '#/components/icons/Pencil'
82
85
import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
83
86
import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
84
87
import {PersonX_Stroke2_Corner0_Rounded as PersonX} from '#/components/icons/Person'
···
96
99
} from '#/components/moderation/ReportDialog'
97
100
import * as Prompt from '#/components/Prompt'
98
101
import {IS_INTERNAL} from '#/env'
102
+
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
99
103
import * as bsky from '#/types/bsky'
100
104
101
105
let PostMenuItems = ({
···
141
145
const postInteractionSettingsDialogControl = useDialogControl()
142
146
const quotePostDetachConfirmControl = useDialogControl()
143
147
const hideReplyConfirmControl = useDialogControl()
148
+
const redraftPromptControl = useDialogControl()
144
149
const {mutateAsync: toggleReplyVisibility} =
145
150
useToggleReplyVisibilityMutation()
146
151
···
214
219
)
215
220
}
216
221
222
+
const {openComposer} = useOpenComposer()
223
+
const onRedraftPost = () => {
224
+
redraftPromptControl.open()
225
+
}
226
+
227
+
const onConfirmRedraft = () => {
228
+
let imageUris: {
229
+
uri: string
230
+
width: number
231
+
height: number
232
+
altText?: string
233
+
}[] = []
234
+
235
+
if (post.embed?.$type === 'app.bsky.embed.images#view') {
236
+
const embed = post.embed as AppBskyEmbedImages.View
237
+
imageUris = embed.images.map(img => ({
238
+
uri: img.fullsize,
239
+
width: img.aspectRatio?.width ?? 1000,
240
+
height: img.aspectRatio?.height ?? 1000,
241
+
altText: img.alt,
242
+
}))
243
+
} else if (post.embed?.$type === 'app.bsky.embed.recordWithMedia#view') {
244
+
const embed = post.embed as AppBskyEmbedRecordWithMedia.View
245
+
if (embed.media.$type === 'app.bsky.embed.images#view') {
246
+
const images = embed.media as AppBskyEmbedImages.View
247
+
imageUris = images.images.map(img => ({
248
+
uri: img.fullsize,
249
+
width: img.aspectRatio?.width ?? 1000,
250
+
height: img.aspectRatio?.height ?? 1000,
251
+
altText: img.alt,
252
+
}))
253
+
}
254
+
}
255
+
256
+
let quotePost: AppBskyFeedDefs.PostView | undefined
257
+
258
+
if (post.embed?.$type === 'app.bsky.embed.record#view') {
259
+
const embed = post.embed as AppBskyEmbedRecord.View
260
+
if (
261
+
AppBskyEmbedRecord.isViewRecord(embed.record) &&
262
+
AppBskyFeedPost.isRecord(embed.record.value)
263
+
) {
264
+
quotePost = {
265
+
uri: embed.record.uri,
266
+
cid: embed.record.cid,
267
+
author: embed.record.author,
268
+
record: embed.record.value,
269
+
indexedAt: embed.record.indexedAt,
270
+
} as AppBskyFeedDefs.PostView
271
+
}
272
+
} else if (post.embed?.$type === 'app.bsky.embed.recordWithMedia#view') {
273
+
const embed = post.embed as AppBskyEmbedRecordWithMedia.View
274
+
if (
275
+
AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
276
+
AppBskyFeedPost.isRecord(embed.record.record.value)
277
+
) {
278
+
const record = embed.record.record
279
+
quotePost = {
280
+
uri: record.uri,
281
+
cid: record.cid,
282
+
author: record.author,
283
+
record: record.value,
284
+
indexedAt: record.indexedAt,
285
+
} as AppBskyFeedDefs.PostView
286
+
}
287
+
}
288
+
289
+
let replyTo: any
290
+
if (record.reply) {
291
+
const parent = record.reply.parent || record.reply.root
292
+
if (parent) {
293
+
replyTo = {
294
+
uri: parent.uri,
295
+
cid: parent.cid,
296
+
}
297
+
}
298
+
}
299
+
300
+
openComposer({
301
+
text: record.text,
302
+
imageUris,
303
+
onPost: () => {
304
+
onDeletePost()
305
+
},
306
+
quote: quotePost,
307
+
replyTo,
308
+
})
309
+
}
310
+
217
311
const onToggleThreadMute = () => {
218
312
try {
219
313
if (isThreadMuted) {
···
508
602
509
603
return (
510
604
<>
605
+
<Prompt.Basic
606
+
control={redraftPromptControl}
607
+
title={_(msg`Redraft this post?`)}
608
+
description={_(
609
+
msg`This will delete the original post and open the composer with its content.`,
610
+
)}
611
+
onConfirm={onConfirmRedraft}
612
+
confirmButtonCta={_(msg`Redraft`)}
613
+
confirmButtonColor="primary"
614
+
/>
511
615
<Menu.Outer>
512
616
{isAuthor && (
513
617
<>
···
530
634
icon={isPinPending ? Loader : PinIcon}
531
635
position="right"
532
636
/>
637
+
</Menu.Item>
638
+
<Menu.Item
639
+
testID="redraftPostBtn"
640
+
label={_(msg`Redraft`)}
641
+
onPress={onRedraftPost}>
642
+
<Menu.ItemText>{_(msg`Redraft`)}</Menu.ItemText>
643
+
<Menu.ItemIcon icon={Pen} position="right" />
533
644
</Menu.Item>
534
645
</Menu.Group>
535
646
<Menu.Divider />
+3
-1
src/view/com/composer/Composer.tsx
+3
-1
src/view/com/composer/Composer.tsx
···
770
770
keyboardShouldPersistTaps="always"
771
771
onContentSizeChange={onScrollViewContentSizeChange}
772
772
onLayout={onScrollViewLayout}>
773
-
{replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined}
773
+
{replyTo && replyTo.text && replyTo.author ? (
774
+
<ComposerReplyTo replyTo={replyTo} />
775
+
) : undefined}
774
776
{thread.posts.map((post, index) => (
775
777
<React.Fragment key={post.id}>
776
778
<ComposerPost