Bluesky app fork with some witchin' additions 💫

implement redrafting

authored by scanash.com and committed by Tangled dc02ee76 cfc7acce

Changed files
+114 -1
src
components
PostControls
view
com
composer
+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
··· 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