Bluesky app fork with some witchin' additions 💫

add reqId to feed feedback (#8396)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

authored by hailey.at

Samuel Newman and committed by
GitHub
c16cd36b e6c867a9

+200 -183
+2 -2
package.json
··· 69 69 "icons:optimize": "svgo -f ./assets/icons" 70 70 }, 71 71 "dependencies": { 72 - "@atproto/api": "^0.15.7", 72 + "@atproto/api": "^0.15.8", 73 73 "@bitdrift/react-native": "^0.6.8", 74 74 "@braintree/sanitize-url": "^6.0.2", 75 75 "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", ··· 219 219 "zod": "^3.20.2" 220 220 }, 221 221 "devDependencies": { 222 - "@atproto/dev-env": "^0.3.131", 222 + "@atproto/dev-env": "^0.3.132", 223 223 "@babel/core": "^7.26.0", 224 224 "@babel/preset-env": "^7.26.0", 225 225 "@babel/runtime": "^7.26.0",
+6 -2
src/lib/api/feed-manip.ts
··· 1 1 import { 2 - AppBskyActorDefs, 2 + type AppBskyActorDefs, 3 3 AppBskyEmbedRecord, 4 4 AppBskyEmbedRecordWithMedia, 5 5 AppBskyFeedDefs, ··· 9 9 import * as bsky from '#/types/bsky' 10 10 import {isPostInLanguage} from '../../locale/helpers' 11 11 import {FALLBACK_MARKER_POST} from './feed/home' 12 - import {ReasonFeedSource} from './feed/types' 12 + import {type ReasonFeedSource} from './feed/types' 13 13 14 14 type FeedViewPost = AppBskyFeedDefs.FeedViewPost 15 15 ··· 185 185 186 186 get feedContext() { 187 187 return this._feedPost.feedContext 188 + } 189 + 190 + get reqId() { 191 + return this._feedPost.reqId 188 192 } 189 193 190 194 get isRepost() {
+1
src/screens/Search/Explore.tsx
··· 882 882 record={subItem.record} 883 883 reason={indexInSlice === 0 ? slice.reason : undefined} 884 884 feedContext={slice.feedContext} 885 + reqId={slice.reqId} 885 886 moderation={subItem.moderation} 886 887 parentAuthor={subItem.parentAuthor} 887 888 showReplyTo={item.showReplyTo}
+15 -1
src/screens/VideoFeed/index.tsx
··· 178 178 post: AppBskyFeedDefs.PostView 179 179 video: AppBskyEmbedVideo.View 180 180 feedContext: string | undefined 181 + reqId: string | undefined 181 182 } 182 183 183 184 function Feed() { ··· 216 217 post: AppBskyFeedDefs.PostView 217 218 video: AppBskyEmbedVideo.View 218 219 feedContext: string | undefined 220 + reqId: string | undefined 219 221 }[] = [] 220 222 for (const slice of page.slices) { 221 223 const feedPost = slice.items.find( ··· 228 230 post: feedPost.post, 229 231 video: feedPost.post.embed, 230 232 feedContext: slice.feedContext, 233 + reqId: slice.reqId, 231 234 }) 232 235 } 233 236 } ··· 274 277 moderation={item.moderation} 275 278 scrollGesture={scrollGesture} 276 279 feedContext={item.feedContext} 280 + reqId={item.reqId} 277 281 /> 278 282 ) 279 283 }, ··· 470 474 scrollGesture, 471 475 moderation, 472 476 feedContext, 477 + reqId, 473 478 }: { 474 479 player?: VideoPlayer 475 480 post: AppBskyFeedDefs.PostView ··· 479 484 scrollGesture: NativeGesture 480 485 moderation?: ModerationDecision 481 486 feedContext: string | undefined 487 + reqId: string | undefined 482 488 }): React.ReactNode => { 483 489 const postShadow = usePostShadow(post) 484 490 const {width, height} = useSafeAreaFrame() ··· 490 496 item: post.uri, 491 497 event: 'app.bsky.feed.defs#interactionSeen', 492 498 feedContext, 499 + reqId, 493 500 }) 494 501 } 495 - }, [active, post.uri, feedContext, sendInteraction]) 502 + }, [active, post.uri, feedContext, reqId, sendInteraction]) 496 503 497 504 // TODO: high-performance android phones should also 498 505 // be capable of rendering 3 video players, but currently ··· 537 544 scrollGesture={scrollGesture} 538 545 moderation={moderation} 539 546 feedContext={feedContext} 547 + reqId={reqId} 540 548 /> 541 549 )} 542 550 </> ··· 682 690 scrollGesture, 683 691 moderation, 684 692 feedContext, 693 + reqId, 685 694 }: { 686 695 player?: VideoPlayer 687 696 post: Shadow<AppBskyFeedDefs.PostView> ··· 690 699 scrollGesture: NativeGesture 691 700 moderation: ModerationDecision 692 701 feedContext: string | undefined 702 + reqId: string | undefined 693 703 }) { 694 704 const {_} = useLingui() 695 705 const t = useTheme() ··· 760 770 player={player} 761 771 post={post} 762 772 feedContext={feedContext} 773 + reqId={reqId} 763 774 /> 764 775 )} 765 776 </View> ··· 1002 1013 player, 1003 1014 post, 1004 1015 feedContext, 1016 + reqId, 1005 1017 }: { 1006 1018 player: VideoPlayer 1007 1019 post: Shadow<AppBskyFeedDefs.PostView> 1008 1020 feedContext: string | undefined 1021 + reqId: string | undefined 1009 1022 }) { 1010 1023 const {_} = useLingui() 1011 1024 const doubleTapRef = useRef<ReturnType<typeof setTimeout> | null>(null) ··· 1036 1049 item: post.uri, 1037 1050 event: 'app.bsky.feed.defs#interactionLike', 1038 1051 feedContext, 1052 + reqId, 1039 1053 }) 1040 1054 } else { 1041 1055 doubleTapRef.current = setTimeout(togglePlayPause, 200)
+11 -7
src/state/feed-feedback.tsx
··· 1 1 import React from 'react' 2 - import {AppState, AppStateStatus} from 'react-native' 3 - import {AppBskyFeedDefs} from '@atproto/api' 2 + import {AppState, type AppStateStatus} from 'react-native' 3 + import {type AppBskyFeedDefs} from '@atproto/api' 4 4 import throttle from 'lodash.throttle' 5 5 6 6 import {FEEDBACK_FEEDS, STAGING_FEEDS} from '#/lib/constants' 7 7 import {logEvent} from '#/lib/statsig/statsig' 8 8 import {logger} from '#/logger' 9 - import {FeedDescriptor, FeedPostSliceItem} from '#/state/queries/post-feed' 9 + import { 10 + type FeedDescriptor, 11 + type FeedPostSliceItem, 12 + } from '#/state/queries/post-feed' 10 13 import {getItemsForFeedback} from '#/view/com/posts/PostFeed' 11 14 import {useAgent} from './session' 12 15 ··· 103 106 return 104 107 } 105 108 const items = getItemsForFeedback(feedItem) 106 - for (const {item: postItem, feedContext} of items) { 109 + for (const {item: postItem, feedContext, reqId} of items) { 107 110 if (!history.current.has(postItem)) { 108 111 history.current.add(postItem) 109 112 queue.current.add( ··· 111 114 item: postItem.uri, 112 115 event: 'app.bsky.feed.defs#interactionSeen', 113 116 feedContext, 117 + reqId, 114 118 }), 115 119 ) 116 120 sendToFeed() ··· 164 168 function toString(interaction: AppBskyFeedDefs.Interaction): string { 165 169 return `${interaction.item}|${interaction.event}|${ 166 170 interaction.feedContext || '' 167 - }` 171 + }|${interaction.reqId || ''}` 168 172 } 169 173 170 174 function toInteraction(str: string): AppBskyFeedDefs.Interaction { 171 - const [item, event, feedContext] = str.split('|') 172 - return {item, event, feedContext} 175 + const [item, event, feedContext, reqId] = str.split('|') 176 + return {item, event, feedContext, reqId} 173 177 } 174 178 175 179 type AggregatedStats = {
+1
src/state/queries/explore-feed-previews.tsx
··· 215 215 isFallbackMarker: false, 216 216 isIncompleteThread: item.isIncompleteThread, 217 217 feedContext: item.feedContext, 218 + reqId: item.reqId, 218 219 reason: item.reason, 219 220 feedPostUri: item.feedPostUri, 220 221 items: item.items
+3
src/state/queries/post-feed.ts
··· 92 92 isIncompleteThread: boolean 93 93 isFallbackMarker: boolean 94 94 feedContext: string | undefined 95 + reqId: string | undefined 95 96 feedPostUri: string 96 97 reason?: 97 98 | AppBskyFeedDefs.ReasonRepost ··· 316 317 userActionHistory.seen( 317 318 slice.items.map(item => ({ 318 319 feedContext: slice.feedContext, 320 + reqId: slice.reqId, 319 321 likeCount: item.post.likeCount ?? 0, 320 322 repostCount: item.post.repostCount ?? 0, 321 323 replyCount: item.post.replyCount ?? 0, ··· 333 335 isIncompleteThread: slice.isIncompleteThread, 334 336 isFallbackMarker: slice.isFallbackMarker, 335 337 feedContext: slice.feedContext, 338 + reqId: slice.reqId, 336 339 reason: slice.reason, 337 340 feedPostUri: slice.feedPostUri, 338 341 items: slice.items.map((item, i) => {
+18 -2
src/view/com/posts/PostFeed.tsx
··· 103 103 items: FeedPostSliceItem[] 104 104 sourceFeedUri: string 105 105 feedContexts: (string | undefined)[] 106 + reqIds: (string | undefined)[] 106 107 } 107 108 | { 108 109 type: 'sliceViewFullThread' ··· 134 135 | { 135 136 item: FeedPostSliceItem 136 137 feedContext: string | undefined 138 + reqId: string | undefined 137 139 }[] { 138 140 if (feedRow.type === 'sliceItem') { 139 141 return feedRow.slice.items.map(item => ({ 140 142 item, 141 143 feedContext: feedRow.slice.feedContext, 144 + reqId: feedRow.slice.reqId, 142 145 })) 143 146 } else if (feedRow.type === 'videoGridRow') { 144 147 return feedRow.items.map((item, i) => ({ 145 148 item, 146 149 feedContext: feedRow.feedContexts[i], 150 + reqId: feedRow.reqIds[i], 147 151 })) 148 152 } else { 149 153 return [] ··· 398 402 const videos: { 399 403 item: FeedPostSliceItem 400 404 feedContext: string | undefined 405 + reqId: string | undefined 401 406 }[] = [] 402 407 for (const page of data.pages) { 403 408 for (const slice of page.slices) { ··· 405 410 item => item.uri === slice.feedPostUri, 406 411 ) 407 412 if (item && AppBskyEmbedVideo.isView(item.post.embed)) { 408 - videos.push({item, feedContext: slice.feedContext}) 413 + videos.push({ 414 + item, 415 + feedContext: slice.feedContext, 416 + reqId: slice.reqId, 417 + }) 409 418 } 410 419 } 411 420 } ··· 413 422 const rows: { 414 423 item: FeedPostSliceItem 415 424 feedContext: string | undefined 425 + reqId: string | undefined 416 426 }[][] = [] 417 427 for (let i = 0; i < videos.length; i++) { 418 428 const video = videos[i] 419 429 const item = video.item 420 430 const cols = gtMobile ? 3 : 2 421 - const rowItem = {item, feedContext: video.feedContext} 431 + const rowItem = { 432 + item, 433 + feedContext: video.feedContext, 434 + reqId: video.reqId, 435 + } 422 436 if (i % cols === 0) { 423 437 rows.push([rowItem]) 424 438 } else { ··· 434 448 items: row.map(r => r.item), 435 449 sourceFeedUri: feedUriOrActorDid, 436 450 feedContexts: row.map(r => r.feedContext), 451 + reqIds: row.map(r => r.reqId), 437 452 }) 438 453 } 439 454 } else { ··· 685 700 record={item.record} 686 701 reason={indexInSlice === 0 ? slice.reason : undefined} 687 702 feedContext={slice.feedContext} 703 + reqId={slice.reqId} 688 704 moderation={item.moderation} 689 705 parentAuthor={item.parentAuthor} 690 706 showReplyTo={row.showReplyTo}
+20 -10
src/view/com/posts/PostFeedItem.tsx
··· 70 70 isThreadLastChild?: boolean 71 71 isThreadParent?: boolean 72 72 feedContext: string | undefined 73 + reqId: string | undefined 73 74 hideTopBorder?: boolean 74 75 isParentBlocked?: boolean 75 76 isParentNotFound?: boolean ··· 80 81 record, 81 82 reason, 82 83 feedContext, 84 + reqId, 83 85 moderation, 84 86 parentAuthor, 85 87 showReplyTo, ··· 117 119 record={record} 118 120 reason={reason} 119 121 feedContext={feedContext} 122 + reqId={reqId} 120 123 richText={richText} 121 124 parentAuthor={parentAuthor} 122 125 showReplyTo={showReplyTo} ··· 140 143 record, 141 144 reason, 142 145 feedContext, 146 + reqId, 143 147 richText, 144 148 moderation, 145 149 parentAuthor, ··· 171 175 }, [post.uri, post.author]) 172 176 const {sendInteraction} = useFeedFeedbackContext() 173 177 174 - const onPressReply = useCallback(() => { 178 + const onPressReply = () => { 175 179 sendInteraction({ 176 180 item: post.uri, 177 181 event: 'app.bsky.feed.defs#interactionReply', 178 182 feedContext, 183 + reqId, 179 184 }) 180 185 openComposer({ 181 186 replyTo: { ··· 187 192 moderation, 188 193 }, 189 194 }) 190 - }, [post, record, openComposer, moderation, sendInteraction, feedContext]) 195 + } 191 196 192 - const onOpenAuthor = useCallback(() => { 197 + const onOpenAuthor = () => { 193 198 sendInteraction({ 194 199 item: post.uri, 195 200 event: 'app.bsky.feed.defs#clickthroughAuthor', 196 201 feedContext, 202 + reqId, 197 203 }) 198 - }, [sendInteraction, post, feedContext]) 204 + } 199 205 200 - const onOpenReposter = useCallback(() => { 206 + const onOpenReposter = () => { 201 207 sendInteraction({ 202 208 item: post.uri, 203 209 event: 'app.bsky.feed.defs#clickthroughReposter', 204 210 feedContext, 211 + reqId, 205 212 }) 206 - }, [sendInteraction, post, feedContext]) 213 + } 207 214 208 - const onOpenEmbed = useCallback(() => { 215 + const onOpenEmbed = () => { 209 216 sendInteraction({ 210 217 item: post.uri, 211 218 event: 'app.bsky.feed.defs#clickthroughEmbed', 212 219 feedContext, 220 + reqId, 213 221 }) 214 - }, [sendInteraction, post, feedContext]) 222 + } 215 223 216 - const onBeforePress = useCallback(() => { 224 + const onBeforePress = () => { 217 225 sendInteraction({ 218 226 item: post.uri, 219 227 event: 'app.bsky.feed.defs#clickthroughItem', 220 228 feedContext, 229 + reqId, 221 230 }) 222 231 precacheProfile(queryClient, post.author) 223 - }, [queryClient, post, sendInteraction, feedContext]) 232 + } 224 233 225 234 const outerStyles = [ 226 235 styles.outer, ··· 437 446 onPressReply={onPressReply} 438 447 logContext="FeedItem" 439 448 feedContext={feedContext} 449 + reqId={reqId} 440 450 threadgateRecord={threadgateRecord} 441 451 onShowLess={onShowLess} 442 452 />
+3
src/view/com/util/forms/PostDropdownBtn.tsx
··· 28 28 testID, 29 29 post, 30 30 postFeedContext, 31 + postReqId, 31 32 record, 32 33 richText, 33 34 style, ··· 40 41 testID: string 41 42 post: Shadow<AppBskyFeedDefs.PostView> 42 43 postFeedContext: string | undefined 44 + postReqId: string | undefined 43 45 record: AppBskyFeedPost.Record 44 46 richText: RichTextAPI 45 47 style?: StyleProp<ViewStyle> ··· 99 101 testID={testID} 100 102 post={post} 101 103 postFeedContext={postFeedContext} 104 + postReqId={postReqId} 102 105 record={record} 103 106 richText={richText} 104 107 timestamp={timestamp}
+46 -63
src/view/com/util/forms/PostDropdownBtnMenuItems.tsx
··· 1 - import React, {memo, useCallback} from 'react' 1 + import React, {memo} from 'react' 2 2 import { 3 3 Platform, 4 4 type PressableProps, ··· 97 97 let PostDropdownMenuItems = ({ 98 98 post, 99 99 postFeedContext, 100 + postReqId, 100 101 record, 101 102 richText, 102 103 timestamp, ··· 106 107 testID: string 107 108 post: Shadow<AppBskyFeedDefs.PostView> 108 109 postFeedContext: string | undefined 110 + postReqId: string | undefined 109 111 record: AppBskyFeedPost.Record 110 112 richText: RichTextAPI 111 113 style?: StyleProp<ViewStyle> ··· 189 191 langPrefs.primaryLanguage, 190 192 ) 191 193 192 - const onDeletePost = React.useCallback(() => { 194 + const onDeletePost = () => { 193 195 deletePostMutate({uri: postUri}).then( 194 196 () => { 195 197 Toast.show(_(msg({message: 'Post deleted', context: 'toast'}))) ··· 215 217 Toast.show(_(msg`Failed to delete post, please try again`), 'xmark') 216 218 }, 217 219 ) 218 - }, [ 219 - navigation, 220 - postUri, 221 - deletePostMutate, 222 - postAuthor, 223 - currentAccount, 224 - isAuthor, 225 - href, 226 - _, 227 - ]) 220 + } 228 221 229 - const onToggleThreadMute = React.useCallback(() => { 222 + const onToggleThreadMute = () => { 230 223 try { 231 224 if (isThreadMuted) { 232 225 unmuteThread() ··· 246 239 ) 247 240 } 248 241 } 249 - }, [isThreadMuted, unmuteThread, _, muteThread]) 242 + } 250 243 251 - const onCopyPostText = React.useCallback(() => { 244 + const onCopyPostText = () => { 252 245 const str = richTextToString(richText, true) 253 246 254 247 Clipboard.setStringAsync(str) 255 248 Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') 256 - }, [_, richText]) 249 + } 257 250 258 - const onPressTranslate = React.useCallback(async () => { 251 + const onPressTranslate = async () => { 259 252 await openLink(translatorUrl, true) 260 253 261 254 if ( ··· 270 263 textLength: post.record.text.length, 271 264 }) 272 265 } 273 - }, [openLink, translatorUrl, langPrefs, post]) 266 + } 274 267 275 - const onHidePost = React.useCallback(() => { 268 + const onHidePost = () => { 276 269 hidePost({uri: postUri}) 277 - }, [postUri, hidePost]) 270 + } 278 271 279 - const hideInPWI = React.useMemo(() => { 280 - return !!postAuthor.labels?.find( 281 - label => label.val === '!no-unauthenticated', 282 - ) 283 - }, [postAuthor]) 272 + const hideInPWI = !!postAuthor.labels?.find( 273 + label => label.val === '!no-unauthenticated', 274 + ) 284 275 285 276 const showLoggedOutWarning = 286 277 postAuthor.did !== currentAccount?.did && hideInPWI 287 278 288 - const onSharePost = React.useCallback(() => { 279 + const onSharePost = () => { 289 280 const url = toShareUrl(href) 290 281 shareUrl(url) 291 - }, [href]) 282 + } 292 283 293 - const onPressShowMore = React.useCallback(() => { 284 + const onPressShowMore = () => { 294 285 feedFeedback.sendInteraction({ 295 286 event: 'app.bsky.feed.defs#requestMore', 296 287 item: postUri, 297 288 feedContext: postFeedContext, 289 + reqId: postReqId, 298 290 }) 299 291 Toast.show(_(msg({message: 'Feedback sent!', context: 'toast'}))) 300 - }, [feedFeedback, postUri, postFeedContext, _]) 292 + } 301 293 302 - const onPressShowLess = React.useCallback(() => { 294 + const onPressShowLess = () => { 303 295 feedFeedback.sendInteraction({ 304 296 event: 'app.bsky.feed.defs#requestLess', 305 297 item: postUri, 306 298 feedContext: postFeedContext, 299 + reqId: postReqId, 307 300 }) 308 301 if (onShowLess) { 309 302 onShowLess({ ··· 313 306 } else { 314 307 Toast.show(_(msg({message: 'Feedback sent!', context: 'toast'}))) 315 308 } 316 - }, [feedFeedback, postUri, postFeedContext, _, onShowLess]) 309 + } 317 310 318 - const onSelectChatToShareTo = React.useCallback( 319 - (conversation: string) => { 320 - navigation.navigate('MessagesConversation', { 321 - conversation, 322 - embed: postUri, 323 - }) 324 - }, 325 - [navigation, postUri], 326 - ) 311 + const onSelectChatToShareTo = (conversation: string) => { 312 + navigation.navigate('MessagesConversation', { 313 + conversation, 314 + embed: postUri, 315 + }) 316 + } 327 317 328 - const onToggleQuotePostAttachment = React.useCallback(async () => { 318 + const onToggleQuotePostAttachment = async () => { 329 319 if (!quoteEmbed) return 330 320 331 321 const action = quoteEmbed.isDetached ? 'reattach' : 'detach' ··· 348 338 ) 349 339 logger.error(`Failed to ${action} quote`, {safeMessage: e.message}) 350 340 } 351 - }, [_, quoteEmbed, post, toggleQuoteDetachment]) 341 + } 352 342 353 343 const canHidePostForMe = !isAuthor && !isPostHidden 354 344 const canEmbed = isWeb && gtMobile && !hideInPWI ··· 356 346 !isAuthor && isRootPostAuthor && !isPostHidden && isReply 357 347 const canDetachQuote = quoteEmbed && quoteEmbed.isOwnedByViewer 358 348 359 - const onToggleReplyVisibility = React.useCallback(async () => { 349 + const onToggleReplyVisibility = async () => { 360 350 // TODO no threadgate? 361 351 if (!canHideReplyForEveryone) return 362 352 ··· 380 370 ) 381 371 logger.error(`Failed to ${action} reply`, {safeMessage: e.message}) 382 372 } 383 - }, [ 384 - _, 385 - isReplyHiddenByThreadgate, 386 - rootUri, 387 - postUri, 388 - canHideReplyForEveryone, 389 - toggleReplyVisibility, 390 - ]) 373 + } 391 374 392 - const onPressPin = useCallback(() => { 375 + const onPressPin = () => { 393 376 logEvent(isPinned ? 'post:unpin' : 'post:pin', {}) 394 377 pinPostMutate({ 395 378 postUri, 396 379 postCid, 397 380 action: isPinned ? 'unpin' : 'pin', 398 381 }) 399 - }, [isPinned, pinPostMutate, postCid, postUri]) 382 + } 400 383 401 - const onBlockAuthor = useCallback(async () => { 384 + const onBlockAuthor = async () => { 402 385 try { 403 386 await queueBlock() 404 387 Toast.show(_(msg({message: 'Account blocked', context: 'toast'}))) ··· 408 391 Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') 409 392 } 410 393 } 411 - }, [_, queueBlock]) 394 + } 412 395 413 - const onMuteAuthor = useCallback(async () => { 396 + const onMuteAuthor = async () => { 414 397 if (postAuthor.viewer?.muted) { 415 398 try { 416 399 await queueUnmute() ··· 432 415 } 433 416 } 434 417 } 435 - }, [_, queueMute, queueUnmute, postAuthor.viewer?.muted]) 418 + } 436 419 437 - const onShareATURI = useCallback(() => { 420 + const onShareATURI = () => { 438 421 shareText(postUri) 439 - }, [postUri]) 422 + } 440 423 441 - const onShareAuthorDID = useCallback(() => { 424 + const onShareAuthorDID = () => { 442 425 shareText(postAuthor.did) 443 - }, [postAuthor.did]) 426 + } 444 427 445 - const onReportMisclassification = useCallback(() => { 428 + const onReportMisclassification = () => { 446 429 const url = `https://docs.google.com/forms/d/e/1FAIpQLSd0QPqhNFksDQf1YyOos7r1ofCLvmrKAH1lU042TaS3GAZaWQ/viewform?entry.1756031717=${toShareUrl( 447 430 href, 448 431 )}` 449 432 openLink(url) 450 - }, [href, openLink]) 433 + } 451 434 452 435 return ( 453 436 <>
+16 -37
src/view/com/util/post-ctrls/PostCtrls.tsx
··· 1 - import React, {memo, useCallback} from 'react' 1 + import React, {memo} from 'react' 2 2 import { 3 3 Pressable, 4 4 type PressableStateCallbackType, ··· 55 55 record, 56 56 richText, 57 57 feedContext, 58 + reqId, 58 59 style, 59 60 onPressReply, 60 61 onPostReply, ··· 67 68 record: AppBskyFeedPost.Record 68 69 richText: RichTextAPI 69 70 feedContext?: string | undefined 71 + reqId?: string | undefined 70 72 style?: StyleProp<ViewStyle> 71 73 onPressReply: () => void 72 74 onPostReply?: (postUri: string | undefined) => void ··· 117 119 const [hasLikeIconBeenToggled, setHasLikeIconBeenToggled] = 118 120 React.useState(false) 119 121 120 - const onPressToggleLike = React.useCallback(async () => { 122 + const onPressToggleLike = async () => { 121 123 if (isBlocked) { 122 124 Toast.show( 123 125 _(msg`Cannot interact with a blocked user`), ··· 134 136 item: post.uri, 135 137 event: 'app.bsky.feed.defs#interactionLike', 136 138 feedContext, 139 + reqId, 137 140 }) 138 141 captureAction(ProgressGuideAction.Like) 139 142 await queueLike() ··· 145 148 throw e 146 149 } 147 150 } 148 - }, [ 149 - _, 150 - playHaptic, 151 - post.uri, 152 - post.viewer?.like, 153 - queueLike, 154 - queueUnlike, 155 - sendInteraction, 156 - captureAction, 157 - feedContext, 158 - isBlocked, 159 - ]) 151 + } 160 152 161 - const onRepost = useCallback(async () => { 153 + const onRepost = async () => { 162 154 if (isBlocked) { 163 155 Toast.show( 164 156 _(msg`Cannot interact with a blocked user`), ··· 173 165 item: post.uri, 174 166 event: 'app.bsky.feed.defs#interactionRepost', 175 167 feedContext, 168 + reqId, 176 169 }) 177 170 await queueRepost() 178 171 } else { ··· 183 176 throw e 184 177 } 185 178 } 186 - }, [ 187 - _, 188 - post.uri, 189 - post.viewer?.repost, 190 - queueRepost, 191 - queueUnrepost, 192 - sendInteraction, 193 - feedContext, 194 - isBlocked, 195 - ]) 179 + } 196 180 197 - const onQuote = useCallback(() => { 181 + const onQuote = () => { 198 182 if (isBlocked) { 199 183 Toast.show( 200 184 _(msg`Cannot interact with a blocked user`), ··· 207 191 item: post.uri, 208 192 event: 'app.bsky.feed.defs#interactionQuote', 209 193 feedContext, 194 + reqId, 210 195 }) 211 196 openComposer({ 212 197 quote: post, 213 198 onPost: onPostReply, 214 199 }) 215 - }, [ 216 - _, 217 - sendInteraction, 218 - post, 219 - feedContext, 220 - openComposer, 221 - onPostReply, 222 - isBlocked, 223 - ]) 200 + } 224 201 225 - const onShare = useCallback(() => { 202 + const onShare = () => { 226 203 const urip = new AtUri(post.uri) 227 204 const href = makeProfileLink(post.author, 'post', urip.rkey) 228 205 const url = toShareUrl(href) ··· 231 208 item: post.uri, 232 209 event: 'app.bsky.feed.defs#interactionShare', 233 210 feedContext, 211 + reqId, 234 212 }) 235 - }, [post.uri, post.author, sendInteraction, feedContext]) 213 + } 236 214 237 215 const btnStyle = React.useCallback( 238 216 ({pressed, hovered}: PressableStateCallbackType) => [ ··· 374 352 testID="postDropdownBtn" 375 353 post={post} 376 354 postFeedContext={feedContext} 355 + postReqId={reqId} 377 356 record={record} 378 357 richText={richText} 379 358 style={{padding: 5}}
+1
src/view/screens/DebugMod.tsx
··· 829 829 showReplyTo={false} 830 830 reason={undefined} 831 831 feedContext={''} 832 + reqId={undefined} 832 833 rootPost={post} 833 834 /> 834 835 )
+57 -59
yarn.lock
··· 20 20 "@jridgewell/gen-mapping" "^0.3.0" 21 21 "@jridgewell/trace-mapping" "^0.3.9" 22 22 23 - "@atproto-labs/fetch-node@0.1.8": 24 - version "0.1.8" 25 - resolved "https://registry.yarnpkg.com/@atproto-labs/fetch-node/-/fetch-node-0.1.8.tgz#687fc8be6107f10a4247c17989792862affd838b" 26 - integrity sha512-OOTIhZNPEDDm7kaYU8iYRgzM+D5n3mP2iiBSyKuLakKTaZBL5WwYlUsJVsqX26SnUXtGEroOJEVJ6f66OcG80w== 23 + "@atproto-labs/fetch-node@0.1.9": 24 + version "0.1.9" 25 + resolved "https://registry.yarnpkg.com/@atproto-labs/fetch-node/-/fetch-node-0.1.9.tgz#5df902413cc2ebfff914999ad3fbbc13b20e1dd0" 26 + integrity sha512-8sHDDXZEzQptLu8ddUU/8U+THS6dumgPynVX0/1PjUYd4S/FWyPcz6yMIiVChTfzKnZvYRRz47+qvOKhydrHQw== 27 27 dependencies: 28 - "@atproto-labs/fetch" "0.2.2" 29 - "@atproto-labs/pipe" "0.1.0" 28 + "@atproto-labs/fetch" "0.2.3" 29 + "@atproto-labs/pipe" "0.1.1" 30 30 ipaddr.js "^2.1.0" 31 - psl "^1.9.0" 32 31 undici "^6.14.1" 33 32 34 - "@atproto-labs/fetch@0.2.2": 35 - version "0.2.2" 36 - resolved "https://registry.yarnpkg.com/@atproto-labs/fetch/-/fetch-0.2.2.tgz#c65acfd7b2265a8fe7d4ba3997126cce07bafe26" 37 - integrity sha512-QyafkedbFeVaN20DYUpnY2hcArYxjdThPXbYMqOSoZhcvkrUqaw4xDND4wZB5TBD9cq2yqe9V6mcw9P4XQKQuQ== 33 + "@atproto-labs/fetch@0.2.3": 34 + version "0.2.3" 35 + resolved "https://registry.yarnpkg.com/@atproto-labs/fetch/-/fetch-0.2.3.tgz#d47afec078f630c50e291c56264cc0ff13d0c6cc" 36 + integrity sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw== 38 37 dependencies: 39 - "@atproto-labs/pipe" "0.1.0" 38 + "@atproto-labs/pipe" "0.1.1" 40 39 41 - "@atproto-labs/pipe@0.1.0": 42 - version "0.1.0" 43 - resolved "https://registry.yarnpkg.com/@atproto-labs/pipe/-/pipe-0.1.0.tgz#c8d86923b6d8e900d39efe6fdcdf0d897c434086" 44 - integrity sha512-ghOqHFyJlQVFPESzlVHjKroP0tPzbmG5Jms0dNI9yLDEfL8xp4OFPWLX4f6T8mRq69wWs4nIDM3sSsFbFqLa1w== 40 + "@atproto-labs/pipe@0.1.1": 41 + version "0.1.1" 42 + resolved "https://registry.yarnpkg.com/@atproto-labs/pipe/-/pipe-0.1.1.tgz#1c4232d16bf95f251e993cb6ee440f9aa4e87ce6" 43 + integrity sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg== 45 44 46 45 "@atproto-labs/simple-store-memory@0.1.3": 47 46 version "0.1.3" ··· 64 63 "@atproto/xrpc" "^0.7.0" 65 64 "@atproto/xrpc-server" "^0.7.18" 66 65 67 - "@atproto/api@^0.15.7": 68 - version "0.15.7" 69 - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.7.tgz#8436162d9fa5dac627bdd5c0f5c9598309ec1383" 70 - integrity sha512-YRETLcOwDCYfGs7Sl9ObqPwhOlVWrPkw4f1AYGIrXLQS58WHe/vz1lZbqOqMsC6gvCnyZnOuKlhsRHZ14rBLzg== 66 + "@atproto/api@^0.15.8": 67 + version "0.15.8" 68 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.8.tgz#f284a9c225191ebd35b46f5695932ab649c04a61" 69 + integrity sha512-PsCgmV4zPjN8VuJMruxqauhn88PuS0b8t2Xsjl4617+bCPpY513jVlxgNH/XExxO7TSVvJM7EzdLY4o3fqh/xQ== 71 70 dependencies: 72 71 "@atproto/common-web" "^0.4.2" 73 72 "@atproto/lexicon" "^0.4.11" ··· 95 94 multiformats "^9.9.0" 96 95 uint8arrays "3.0.0" 97 96 98 - "@atproto/bsky@^0.0.149": 99 - version "0.0.149" 100 - resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.149.tgz#3e9cfb999b9958e9a61776eddb72d424905ec3be" 101 - integrity sha512-7j2KgWHm1nOTQDmtEcNwtldTArS9WwZS3M+aw7OmGH8wCa8vEljNxP6HETjtktDMNTrSipHmmyqh25+Rc5+Ziw== 97 + "@atproto/bsky@^0.0.150": 98 + version "0.0.150" 99 + resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.150.tgz#6626095875d805d0d3f38fa4e184b9f7d274c80f" 100 + integrity sha512-dn1jzP1EId842+g78Q6EMdOmgEZxa9bSq20HMdd5/R8uu559mPs8zigFuddqCoT1fRaJXFC8ZP7Jk5asvBQhrA== 102 101 dependencies: 103 - "@atproto-labs/fetch-node" "0.1.8" 102 + "@atproto-labs/fetch-node" "0.1.9" 104 103 "@atproto-labs/xrpc-utils" "0.0.14" 105 - "@atproto/api" "^0.15.7" 104 + "@atproto/api" "^0.15.8" 106 105 "@atproto/common" "^0.4.11" 107 106 "@atproto/crypto" "^0.4.4" 108 107 "@atproto/did" "^0.1.5" ··· 219 218 "@noble/hashes" "^1.6.1" 220 219 uint8arrays "3.0.0" 221 220 222 - "@atproto/dev-env@^0.3.131": 223 - version "0.3.131" 224 - resolved "https://registry.yarnpkg.com/@atproto/dev-env/-/dev-env-0.3.131.tgz#b3b4cee5f367766d542515b1713523423ecb5a71" 225 - integrity sha512-Tijqc/vq7qKGTpgoKm1BwyvP2QfoOQRjNm9Ro5CDAMXsKqHfXxPiytxYqxj6QR/PptC27aDUqgmexluZN6XbWg== 221 + "@atproto/dev-env@^0.3.132": 222 + version "0.3.132" 223 + resolved "https://registry.yarnpkg.com/@atproto/dev-env/-/dev-env-0.3.132.tgz#78d55ef08a368a752c55b1ee7b7c08a41f27b5ac" 224 + integrity sha512-RFd/9kgvmbP859N6NLu/FxCzLsj01iq22P9jNpL+dQNXbWXHYwGMUa6edf/ZrljNi3dFBNxabdDZJ2q+8uvBJQ== 226 225 dependencies: 227 - "@atproto/api" "^0.15.7" 228 - "@atproto/bsky" "^0.0.149" 226 + "@atproto/api" "^0.15.8" 227 + "@atproto/bsky" "^0.0.150" 229 228 "@atproto/bsync" "^0.0.19" 230 229 "@atproto/common-web" "^0.4.2" 231 230 "@atproto/crypto" "^0.4.4" 232 231 "@atproto/identity" "^0.4.8" 233 232 "@atproto/lexicon" "^0.4.11" 234 - "@atproto/ozone" "^0.1.110" 235 - "@atproto/pds" "^0.4.137" 233 + "@atproto/ozone" "^0.1.111" 234 + "@atproto/pds" "^0.4.138" 236 235 "@atproto/sync" "^0.1.23" 237 236 "@atproto/syntax" "^0.4.0" 238 237 "@atproto/xrpc-server" "^0.7.18" ··· 302 301 optionalDependencies: 303 302 "@atproto/oauth-provider-api" "0.1.2" 304 303 305 - "@atproto/oauth-provider-ui@0.1.4": 306 - version "0.1.4" 307 - resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-ui/-/oauth-provider-ui-0.1.4.tgz#5e092d30afa583fdab54fc78371aecb1cbfa017d" 308 - integrity sha512-GTQnB7OUBFSeXcdRseAGYzKe9UUFB/kGjRcIA8+pO5pCMD7JdXI+WliUhsbdmQ2I+OK78aAlCrmygNWpLtpZgg== 304 + "@atproto/oauth-provider-ui@0.1.5": 305 + version "0.1.5" 306 + resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-ui/-/oauth-provider-ui-0.1.5.tgz#b080c5e814975821689c5976c27ac1081211106f" 307 + integrity sha512-pW0Vx3kvIWH1Mu3SOImNHP9JbmhSj2e3ChDvtfXCWI1oC03fiaMlJfdxrx9Plq5Z+DajnCzPzrf1Lvbopjf94Q== 309 308 optionalDependencies: 310 309 "@atproto/oauth-provider-api" "0.1.2" 311 310 312 - "@atproto/oauth-provider@^0.7.6": 313 - version "0.7.6" 314 - resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.7.6.tgz#68bc37303611d548bae9f653d41bc89bd8890152" 315 - integrity sha512-4YcnddACznmpuRmHlt9G+kccdv2Gct5qQOF9Yyjse8cl2Td+Rg1gkchpRdWUnyr9fgZzmCsSBYzEfVXge3eUiQ== 311 + "@atproto/oauth-provider@^0.7.7": 312 + version "0.7.7" 313 + resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.7.7.tgz#dbbdeb405ab1d239fd926340f83fb41e13455011" 314 + integrity sha512-ElphzmOjw1hr42HN4dD6sMAQFtpTkaJ8bBDAsbL9YBVJDEGhmHsF3Ye8mDUO4nhEdg7PUTWiCzXyqnaorAjiTA== 316 315 dependencies: 317 - "@atproto-labs/fetch" "0.2.2" 318 - "@atproto-labs/fetch-node" "0.1.8" 319 - "@atproto-labs/pipe" "0.1.0" 316 + "@atproto-labs/fetch" "0.2.3" 317 + "@atproto-labs/fetch-node" "0.1.9" 318 + "@atproto-labs/pipe" "0.1.1" 320 319 "@atproto-labs/simple-store" "0.2.0" 321 320 "@atproto-labs/simple-store-memory" "0.1.3" 322 321 "@atproto/common" "^0.4.11" ··· 324 323 "@atproto/jwk-jose" "0.1.6" 325 324 "@atproto/oauth-provider-api" "0.1.2" 326 325 "@atproto/oauth-provider-frontend" "0.1.4" 327 - "@atproto/oauth-provider-ui" "0.1.4" 326 + "@atproto/oauth-provider-ui" "0.1.5" 328 327 "@atproto/oauth-types" "0.2.7" 329 328 "@atproto/syntax" "0.4.0" 330 329 "@hapi/accept" "^6.0.3" ··· 337 336 http-errors "^2.0.0" 338 337 ioredis "^5.3.2" 339 338 jose "^5.2.0" 340 - psl "^1.9.0" 341 339 zod "^3.23.8" 342 340 343 341 "@atproto/oauth-types@0.2.7": ··· 348 346 "@atproto/jwk" "0.1.5" 349 347 zod "^3.23.8" 350 348 351 - "@atproto/ozone@^0.1.110": 352 - version "0.1.110" 353 - resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.1.110.tgz#78ad57961b4699c8aa3e6f7d5b6f215d7760a723" 354 - integrity sha512-X7VU7QAkwJrwpgmAuAHqvVDX9CEW0Ts5R4ovATgEt2lbxyxtJtYIm1dG346fAlOfC9f3RGN+HI8vBMWrrrLKAQ== 349 + "@atproto/ozone@^0.1.111": 350 + version "0.1.111" 351 + resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.1.111.tgz#7ef4a02f1af045ab44254fb9d44ab0e50fd94ba9" 352 + integrity sha512-NY+Cn/3dY4tPFkMUoJR1KMZN/v9ZIxjx6EQBMwn/nqTiHk0E3rtGEbyL2jLQ7x+FxpPTjDgpnn3K725+8XUaAg== 355 353 dependencies: 356 - "@atproto/api" "^0.15.7" 354 + "@atproto/api" "^0.15.8" 357 355 "@atproto/common" "^0.4.11" 358 356 "@atproto/crypto" "^0.4.4" 359 357 "@atproto/identity" "^0.4.8" ··· 378 376 undici "^6.14.1" 379 377 ws "^8.12.0" 380 378 381 - "@atproto/pds@^0.4.137": 382 - version "0.4.137" 383 - resolved "https://registry.yarnpkg.com/@atproto/pds/-/pds-0.4.137.tgz#87468703b02bf42681ddd50049ee906331655731" 384 - integrity sha512-DRUck9CgOdK0cP6B6/1Cku2gb5t31Vhh9su2TcqF9eymZP1dNSI6nfTIEp+cuwpW/VpDeu7AfHCSgYfnJeZ5yg== 379 + "@atproto/pds@^0.4.138": 380 + version "0.4.138" 381 + resolved "https://registry.yarnpkg.com/@atproto/pds/-/pds-0.4.138.tgz#437d785c83f710bf37bef8baf687b0a46ce9dc68" 382 + integrity sha512-WLzDhmguTgs2wQNKoGxCbpKNegDnRiemSslenMbPrB7kSiXYj+XZobLyoIXHv1EnAd2pbThwNEL8z8EfkM0mDg== 385 383 dependencies: 386 - "@atproto-labs/fetch-node" "0.1.8" 384 + "@atproto-labs/fetch-node" "0.1.9" 387 385 "@atproto-labs/xrpc-utils" "0.0.14" 388 - "@atproto/api" "^0.15.7" 386 + "@atproto/api" "^0.15.8" 389 387 "@atproto/aws" "^0.2.21" 390 388 "@atproto/common" "^0.4.11" 391 389 "@atproto/crypto" "^0.4.4" 392 390 "@atproto/identity" "^0.4.8" 393 391 "@atproto/lexicon" "^0.4.11" 394 - "@atproto/oauth-provider" "^0.7.6" 392 + "@atproto/oauth-provider" "^0.7.7" 395 393 "@atproto/repo" "^0.8.1" 396 394 "@atproto/syntax" "^0.4.0" 397 395 "@atproto/xrpc" "^0.7.0"