Bluesky app fork with some witchin' additions 💫

Improve notification localization (#5550)

* Improve notification localization (#3911)

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update PostMeta.tsx

* Update RightNav.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* a11y

* Update FeedItem.tsx

* Update PostThreadItem.tsx

* Update PostThreadItem.tsx

* revert

* Update FeedItem.tsx

* Update FeedItem.tsx

* Update FeedItem.tsx

* Revert "Merge remote-tracking branch 'upstream/main' into Improve-notification-localization"

This reverts commit f435d1e7ed083fac9d57cc1548b31c692d633c49, reversing
changes made to dae2aee6765c7983dfdd93599e388afc55e53843.

* Reapply "Merge remote-tracking branch 'upstream/main' into Improve-notification-localization"

This reverts commit c93ac1904852e0e96b9df14b168a7063ca36465d.

* Update ThreadgateBtn.tsx

* Rm import edits for now

* Cleanups

---------

Co-authored-by: Minseo Lee <itoupluk427@gmail.com>

authored by

Eric Bailey
Minseo Lee
and committed by
GitHub
41452de1 1e32327d

+168 -61
+168 -61
src/view/com/notifications/FeedItem.tsx
··· 1 - import React, {memo, useEffect, useMemo, useState} from 'react' 1 + import React, { 2 + memo, 3 + type ReactElement, 4 + useEffect, 5 + useMemo, 6 + useState, 7 + } from 'react' 2 8 import { 3 9 Animated, 4 10 Pressable, ··· 17 23 } from '@atproto/api' 18 24 import {AtUri} from '@atproto/api' 19 25 import {TID} from '@atproto/common-web' 20 - import {msg, plural, Trans} from '@lingui/macro' 26 + import {msg, Plural, plural, Trans} from '@lingui/macro' 21 27 import {useLingui} from '@lingui/react' 22 28 import {useNavigation} from '@react-navigation/native' 23 29 import {useQueryClient} from '@tanstack/react-query' ··· 167 173 ) 168 174 } 169 175 170 - let action = '' 176 + const niceTimestamp = niceDate(i18n, item.notification.indexedAt) 177 + const firstAuthor = authors[0] 178 + const firstAuthorName = sanitizeDisplayName( 179 + firstAuthor.profile.displayName || firstAuthor.profile.handle, 180 + ) 181 + const firstAuthorLink = ( 182 + <TextLink 183 + key={firstAuthor.href} 184 + style={[pal.text, s.bold]} 185 + href={firstAuthor.href} 186 + text={ 187 + <Text emoji style={[pal.text, s.bold]}> 188 + {forceLTR(firstAuthorName)} 189 + </Text> 190 + } 191 + disableMismatchWarning 192 + /> 193 + ) 194 + const additionalAuthorsCount = authors.length - 1 195 + const hasMultipleAuthors = additionalAuthorsCount > 0 196 + const formattedAuthorsCount = hasMultipleAuthors 197 + ? formatCount(i18n, additionalAuthorsCount) 198 + : '' 199 + 200 + let a11yLabel = '' 201 + let notificationContent: ReactElement 171 202 let icon = ( 172 203 <HeartIconFilled 173 204 size="xl" ··· 177 208 ]} 178 209 /> 179 210 ) 211 + 180 212 if (item.type === 'post-like') { 181 - action = _(msg`liked your post`) 213 + a11yLabel = hasMultipleAuthors 214 + ? _( 215 + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { 216 + one: `${formattedAuthorsCount} other`, 217 + other: `${formattedAuthorsCount} others`, 218 + })} liked your post`, 219 + ) 220 + : _(msg`${firstAuthorName} liked your post`) 221 + notificationContent = hasMultipleAuthors ? ( 222 + <Trans> 223 + {firstAuthorLink} and{' '} 224 + <Text style={[pal.text, s.bold]}> 225 + <Plural 226 + value={additionalAuthorsCount} 227 + one={`${formattedAuthorsCount} other`} 228 + other={`${formattedAuthorsCount} others`} 229 + /> 230 + </Text>{' '} 231 + liked your post 232 + </Trans> 233 + ) : ( 234 + <Trans>{firstAuthorLink} liked your post</Trans> 235 + ) 182 236 } else if (item.type === 'repost') { 183 - action = _(msg`reposted your post`) 237 + a11yLabel = hasMultipleAuthors 238 + ? _( 239 + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { 240 + one: `${formattedAuthorsCount} other`, 241 + other: `${formattedAuthorsCount} others`, 242 + })} reposted your post`, 243 + ) 244 + : _(msg`${firstAuthorName} reposted your post`) 245 + notificationContent = hasMultipleAuthors ? ( 246 + <Trans> 247 + {firstAuthorLink} and{' '} 248 + <Text style={[pal.text, s.bold]}> 249 + <Plural 250 + value={additionalAuthorsCount} 251 + one={`${formattedAuthorsCount} other`} 252 + other={`${formattedAuthorsCount} others`} 253 + /> 254 + </Text>{' '} 255 + reposted your post 256 + </Trans> 257 + ) : ( 258 + <Trans>{firstAuthorLink} reposted your post</Trans> 259 + ) 184 260 icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} /> 185 261 } else if (item.type === 'follow') { 186 262 let isFollowBack = false ··· 204 280 } 205 281 } 206 282 207 - if (isFollowBack) { 208 - action = _(msg`followed you back`) 283 + if (isFollowBack && !hasMultipleAuthors) { 284 + /* 285 + * Follow-backs are ungrouped, grouped follow-backs not supported atm, 286 + * see `src/state/queries/notifications/util.ts` 287 + */ 288 + a11yLabel = _(msg`${firstAuthorName} followed you back`) 289 + notificationContent = <Trans>{firstAuthorLink} followed you back</Trans> 209 290 } else { 210 - action = _(msg`followed you`) 291 + a11yLabel = hasMultipleAuthors 292 + ? _( 293 + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { 294 + one: `${formattedAuthorsCount} other`, 295 + other: `${formattedAuthorsCount} others`, 296 + })} followed you`, 297 + ) 298 + : _(msg`${firstAuthorName} followed you`) 299 + notificationContent = hasMultipleAuthors ? ( 300 + <Trans> 301 + {firstAuthorLink} and{' '} 302 + <Text style={[pal.text, s.bold]}> 303 + <Plural 304 + value={additionalAuthorsCount} 305 + one={`${formattedAuthorsCount} other`} 306 + other={`${formattedAuthorsCount} others`} 307 + /> 308 + </Text>{' '} 309 + followed you 310 + </Trans> 311 + ) : ( 312 + <Trans>{firstAuthorLink} followed you</Trans> 313 + ) 211 314 } 212 315 icon = <PersonPlusIcon size="xl" style={{color: t.palette.primary_500}} /> 213 316 } else if (item.type === 'feedgen-like') { 214 - action = _(msg`liked your custom feed`) 317 + a11yLabel = hasMultipleAuthors 318 + ? _( 319 + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { 320 + one: `${formattedAuthorsCount} other`, 321 + other: `${formattedAuthorsCount} others`, 322 + })} liked your custom feed`, 323 + ) 324 + : _(msg`${firstAuthorName} liked your custom feed`) 325 + notificationContent = hasMultipleAuthors ? ( 326 + <Trans> 327 + {firstAuthorLink} and{' '} 328 + <Text style={[pal.text, s.bold]}> 329 + <Plural 330 + value={additionalAuthorsCount} 331 + one={`${formattedAuthorsCount} other`} 332 + other={`${formattedAuthorsCount} others`} 333 + /> 334 + </Text>{' '} 335 + liked your custom feed 336 + </Trans> 337 + ) : ( 338 + <Trans>{firstAuthorLink} liked your custom feed</Trans> 339 + ) 215 340 } else if (item.type === 'starterpack-joined') { 341 + a11yLabel = hasMultipleAuthors 342 + ? _( 343 + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { 344 + one: `${formattedAuthorsCount} other`, 345 + other: `${formattedAuthorsCount} others`, 346 + })} signed up with your starter pack`, 347 + ) 348 + : _(msg`${firstAuthorName} signed up with your starter pack`) 349 + notificationContent = hasMultipleAuthors ? ( 350 + <Trans> 351 + {firstAuthorLink} and{' '} 352 + <Text style={[pal.text, s.bold]}> 353 + <Plural 354 + value={additionalAuthorsCount} 355 + one={`${formattedAuthorsCount} other`} 356 + other={`${formattedAuthorsCount} others`} 357 + /> 358 + </Text>{' '} 359 + signed up with your starter pack 360 + </Trans> 361 + ) : ( 362 + <Trans>{firstAuthorLink} signed up with your starter pack</Trans> 363 + ) 216 364 icon = ( 217 365 <View style={{height: 30, width: 30}}> 218 366 <StarterPack width={30} gradient="sky" /> 219 367 </View> 220 368 ) 221 - action = _(msg`signed up with your starter pack`) 222 369 } else { 223 370 return null 224 371 } 225 - 226 - const formattedCount = 227 - authors.length > 1 ? formatCount(i18n, authors.length - 1) : '' 228 - const firstAuthorName = sanitizeDisplayName( 229 - authors[0].profile.displayName || authors[0].profile.handle, 230 - ) 231 - const niceTimestamp = niceDate(i18n, item.notification.indexedAt) 232 - const a11yLabelUsers = 233 - authors.length > 1 234 - ? _(msg` and `) + 235 - plural(authors.length - 1, { 236 - one: `${formattedCount} other`, 237 - other: `${formattedCount} others`, 238 - }) 239 - : '' 240 - const a11yLabel = `${firstAuthorName}${a11yLabelUsers} ${action} ${niceTimestamp}` 372 + a11yLabel += ` · ${niceTimestamp}` 241 373 242 374 return ( 243 375 <Link ··· 260 392 accessibilityLabel={a11yLabel} 261 393 accessible={!isAuthorsExpanded} 262 394 accessibilityActions={ 263 - authors.length > 1 395 + hasMultipleAuthors 264 396 ? [ 265 397 { 266 398 name: 'toggleAuthorsExpanded', ··· 288 420 onToggleAuthorsExpanded() 289 421 } 290 422 }} 291 - onBeforePress={onBeforePress} 292 423 onPointerEnter={() => { 293 424 setHover(true) 294 425 }} ··· 303 434 </View> 304 435 <View style={styles.layoutContent}> 305 436 <ExpandListPressable 306 - hasMultipleAuthors={authors.length > 1} 437 + hasMultipleAuthors={hasMultipleAuthors} 307 438 onToggleAuthorsExpanded={onToggleAuthorsExpanded}> 308 439 <CondensedAuthorsList 309 440 visible={!isAuthorsExpanded} ··· 313 444 /> 314 445 <ExpandedAuthorsList visible={isAuthorsExpanded} authors={authors} /> 315 446 <Text 316 - style={[styles.meta, a.self_start]} 447 + style={[styles.meta, a.self_start, pal.text]} 317 448 accessibilityHint="" 318 449 accessibilityLabel={a11yLabel}> 319 - <TextLink 320 - key={authors[0].href} 321 - style={[pal.text, s.bold]} 322 - href={authors[0].href} 323 - text={ 324 - <Text emoji style={[pal.text, s.bold]}> 325 - {forceLTR(firstAuthorName)} 326 - </Text> 327 - } 328 - disableMismatchWarning 329 - /> 330 - {authors.length > 1 ? ( 331 - <> 332 - <Text style={[pal.text]}> 333 - {' '} 334 - <Trans>and</Trans>{' '} 335 - </Text> 336 - <Text style={[pal.text, s.bold]}> 337 - {plural(authors.length - 1, { 338 - one: `${formattedCount} other`, 339 - other: `${formattedCount} others`, 340 - })} 341 - </Text> 342 - </> 343 - ) : undefined} 344 - <Text style={[pal.text]}> {action}</Text> 450 + {notificationContent} 345 451 <TimeElapsed timestamp={item.notification.indexedAt}> 346 452 {({timeElapsed}) => ( 347 - <Text 348 - style={[pal.textLight, styles.pointer]} 349 - title={niceTimestamp}> 350 - {' ' + timeElapsed} 351 - </Text> 453 + <> 454 + <Text style={[a.ml_xs, pal.textLight]}>&middot;</Text> 455 + <Text style={[a.ml_xs, pal.textLight]} title={niceTimestamp}> 456 + {timeElapsed} 457 + </Text> 458 + </> 352 459 )} 353 460 </TimeElapsed> 354 461 </Text>