Bluesky app fork with some witchin' additions 💫

Merge branch 'main' of https://github.com/bluesky-social/social-app

Changed files
+224 -553
src
components
lib
locale
locales
screens
Login
Messages
components
Profile
Header
Search
Settings
components
VideoFeed
state
queries
session
view
+58 -24
src/components/AccountList.tsx
··· 5 5 import {useLingui} from '@lingui/react' 6 6 7 7 import {useActorStatus} from '#/lib/actor-status' 8 + import {isJwtExpired} from '#/lib/jwt' 8 9 import {sanitizeDisplayName} from '#/lib/strings/display-names' 9 10 import {sanitizeHandle} from '#/lib/strings/handles' 10 11 import {useProfilesQuery} from '#/state/queries/profile' 11 12 import {type SessionAccount, useSession} from '#/state/session' 12 13 import {UserAvatar} from '#/view/com/util/UserAvatar' 13 14 import {atoms as a, useTheme} from '#/alf' 14 - import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 15 - import {ChevronRight_Stroke2_Corner0_Rounded as Chevron} from '#/components/icons/Chevron' 15 + import {Button} from '#/components/Button' 16 + import {CheckThick_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' 17 + import {ChevronRight_Stroke2_Corner0_Rounded as ChevronIcon} from '#/components/icons/Chevron' 18 + import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' 19 + import {Text} from '#/components/Typography' 16 20 import {useSimpleVerificationState} from '#/components/verification' 17 21 import {VerificationCheck} from '#/components/verification/VerificationCheck' 18 - import {Button} from './Button' 19 - import {Text} from './Typography' 20 22 21 23 export function AccountList({ 22 24 onSelectAccount, ··· 44 46 <View 45 47 pointerEvents={pendingDid ? 'none' : 'auto'} 46 48 style={[ 47 - a.rounded_md, 49 + a.rounded_lg, 48 50 a.overflow_hidden, 49 - {borderWidth: 1}, 51 + a.border, 50 52 t.atoms.border_contrast_low, 51 53 ]}> 52 54 {accounts.map(account => ( ··· 58 60 isCurrentAccount={account.did === currentAccount?.did} 59 61 isPendingAccount={account.did === pendingDid} 60 62 /> 61 - <View style={[{borderBottomWidth: 1}, t.atoms.border_contrast_low]} /> 63 + <View style={[a.border_b, t.atoms.border_contrast_low]} /> 62 64 </React.Fragment> 63 65 ))} 64 66 <Button ··· 72 74 a.flex_1, 73 75 a.flex_row, 74 76 a.align_center, 75 - {height: 48}, 77 + a.p_lg, 78 + a.gap_sm, 76 79 (hovered || pressed) && t.atoms.bg_contrast_25, 77 80 ]}> 78 - <Text 81 + <View 79 82 style={[ 80 - a.font_semi_bold, 81 - a.flex_1, 82 - a.flex_row, 83 - a.py_sm, 84 - a.leading_tight, 85 - t.atoms.text_contrast_medium, 86 - {paddingLeft: 56}, 83 + t.atoms.bg_contrast_25, 84 + a.rounded_full, 85 + {width: 48, height: 48}, 86 + a.justify_center, 87 + a.align_center, 88 + (hovered || pressed) && t.atoms.bg_contrast_50, 87 89 ]}> 90 + <PlusIcon style={[t.atoms.text_contrast_low]} size="md" /> 91 + </View> 92 + <Text style={[a.flex_1, a.leading_tight, a.text_md, a.font_medium]}> 88 93 {otherLabel ?? <Trans>Other account</Trans>} 89 94 </Text> 90 - <Chevron size="sm" style={[t.atoms.text, a.mr_md]} /> 95 + <ChevronIcon size="md" style={[t.atoms.text_contrast_low]} /> 91 96 </View> 92 97 )} 93 98 </Button> ··· 116 121 const onPress = useCallback(() => { 117 122 onSelect(account) 118 123 }, [account, onSelect]) 124 + 125 + const isLoggedOut = !account.refreshJwt || isJwtExpired(account.refreshJwt) 119 126 120 127 return ( 121 128 <Button ··· 134 141 a.flex_1, 135 142 a.flex_row, 136 143 a.align_center, 137 - a.px_md, 144 + a.p_lg, 138 145 a.gap_sm, 139 - {height: 56}, 140 146 (hovered || pressed || isPendingAccount) && t.atoms.bg_contrast_25, 141 147 ]}> 142 148 <UserAvatar 143 149 avatar={profile?.avatar} 144 - size={36} 150 + size={48} 145 151 type={profile?.associated?.labeler ? 'labeler' : 'user'} 146 152 live={live} 147 153 hideLiveBadge ··· 151 157 <View style={[a.flex_row, a.align_center, a.gap_xs]}> 152 158 <Text 153 159 emoji 154 - style={[a.font_semi_bold, a.leading_tight]} 160 + style={[a.font_medium, a.leading_tight, a.text_md]} 155 161 numberOfLines={1}> 156 162 {sanitizeDisplayName( 157 163 profile?.displayName || profile?.handle || account.handle, ··· 166 172 </View> 167 173 )} 168 174 </View> 169 - <Text style={[a.leading_tight, t.atoms.text_contrast_medium]}> 175 + <Text 176 + style={[ 177 + a.leading_tight, 178 + t.atoms.text_contrast_medium, 179 + a.text_sm, 180 + ]}> 170 181 {sanitizeHandle(account.handle, '@')} 171 182 </Text> 183 + {isLoggedOut && ( 184 + <Text 185 + style={[ 186 + a.leading_tight, 187 + a.text_xs, 188 + a.italic, 189 + t.atoms.text_contrast_medium, 190 + ]}> 191 + <Trans>Logged out</Trans> 192 + </Text> 193 + )} 172 194 </View> 173 195 174 196 {isCurrentAccount ? ( 175 - <Check size="sm" style={[{color: t.palette.positive_500}]} /> 197 + <View 198 + style={[ 199 + { 200 + width: 20, 201 + height: 20, 202 + backgroundColor: t.palette.positive_500, 203 + }, 204 + a.rounded_full, 205 + a.justify_center, 206 + a.align_center, 207 + ]}> 208 + <CheckIcon size="xs" style={[{color: t.palette.white}]} /> 209 + </View> 176 210 ) : ( 177 - <Chevron size="sm" style={[t.atoms.text]} /> 211 + <ChevronIcon size="md" style={[t.atoms.text_contrast_low]} /> 178 212 )} 179 213 </View> 180 214 )}
+3 -6
src/components/Button.tsx
··· 435 435 436 436 if (shape === 'default') { 437 437 if (size === 'large') { 438 - baseStyles.push({ 438 + baseStyles.push(a.rounded_full, { 439 439 paddingVertical: 12, 440 440 paddingHorizontal: 25, 441 - borderRadius: 10, 442 441 gap: 3, 443 442 }) 444 443 } else if (size === 'small') { 445 - baseStyles.push({ 444 + baseStyles.push(a.rounded_full, { 446 445 paddingVertical: 8, 447 446 paddingHorizontal: 13, 448 - borderRadius: 8, 449 447 gap: 3, 450 448 }) 451 449 } else if (size === 'tiny') { 452 - baseStyles.push({ 450 + baseStyles.push(a.rounded_full, { 453 451 paddingVertical: 5, 454 452 paddingHorizontal: 9, 455 - borderRadius: 6, 456 453 gap: 2, 457 454 }) 458 455 }
-1
src/components/Layout/Header/index.tsx
··· 129 129 size="small" 130 130 variant="ghost" 131 131 color="secondary" 132 - shape="square" 133 132 onPress={onPressBack} 134 133 hitSlop={HITSLOP_30} 135 134 style={[
+1 -2
src/components/PostControls/RepostButton.tsx
··· 204 204 label={_(msg`Cancel quote post`)} 205 205 onPress={onPressClose} 206 206 size="large" 207 - variant="outline" 208 - color="primary"> 207 + color="secondary"> 209 208 <ButtonText> 210 209 <Trans>Cancel</Trans> 211 210 </ButtonText>
+2 -2
src/components/ageAssurance/AgeAssuranceAppealDialog.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 - import {ComAtprotoModerationDefs} from '@atproto/api' 3 + import {ToolsOzoneReportDefs} from '@atproto/api' 4 4 import {msg, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 import {useMutation} from '@tanstack/react-query' ··· 50 50 51 51 await agent.createModerationReport( 52 52 { 53 - reasonType: ComAtprotoModerationDefs.REASONAPPEAL, 53 + reasonType: ToolsOzoneReportDefs.REASONAPPEAL, 54 54 subject: { 55 55 $type: 'com.atproto.admin.defs#repoRef', 56 56 did: currentAccount?.did,
+1 -1
src/components/dialogs/SwitchAccount.tsx
··· 41 41 }, [setShowLoggedOut, control]) 42 42 43 43 return ( 44 - <Dialog.Outer control={control}> 44 + <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 45 45 <Dialog.Handle /> 46 46 <Dialog.ScrollableInner label={_(msg`Switch Account`)}> 47 47 <View style={[a.gap_lg]}>
+2 -2
src/components/dms/AfterReportDialog.tsx
··· 46 46 <Dialog.Handle /> 47 47 <Dialog.ScrollableInner 48 48 label={_( 49 - msg`Would you like to block this account or delete this conversation?`, 49 + msg`Would you like to block this user and/or delete this conversation?`, 50 50 )} 51 51 style={[web({maxWidth: 400})]}> 52 52 <DialogInner params={params} currentScreen={currentScreen} /> ··· 177 177 </Text> 178 178 </View> 179 179 <Toggle.Group 180 - label={_(msg`Block and/or delete this conversation`)} 180 + label={_(msg`Block user and/or delete this conversation`)} 181 181 values={actions} 182 182 onChange={setActions}> 183 183 <View style={[a.gap_md]}>
+2 -2
src/components/moderation/LabelsOnMeDialog.tsx
··· 1 1 import React, {useState} from 'react' 2 2 import {View} from 'react-native' 3 - import {type ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api' 3 + import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api' 4 4 import {XRPCError} from '@atproto/xrpc' 5 5 import {msg, Trans} from '@lingui/macro' 6 6 import {useLingui} from '@lingui/react' ··· 239 239 : 'com.atproto.admin.defs#repoRef' 240 240 await agent.createModerationReport( 241 241 { 242 - reasonType: ComAtprotoModerationDefs.REASONAPPEAL, 242 + reasonType: ToolsOzoneReportDefs.REASONAPPEAL, 243 243 subject: { 244 244 $type, 245 245 ...subject,
+23
src/lib/jwt.ts
··· 1 + import {jwtDecode} from 'jwt-decode' 2 + 3 + import {logger} from '#/logger' 4 + 5 + /** 6 + * Simple check if a JWT token has expired. Does *not* validate the token or check for revocation status, 7 + * just checks the expiration time. 8 + * 9 + * @param token The JWT token to check. 10 + * @returns `true` if the token has expired, `false` otherwise. 11 + */ 12 + export function isJwtExpired(token: string) { 13 + try { 14 + const payload = jwtDecode(token) 15 + 16 + if (!payload.exp) return true 17 + const now = Math.floor(Date.now() / 1000) 18 + return now >= payload.exp 19 + } catch { 20 + logger.error(`session: could not decode jwt`) 21 + return true // invalid token or parse error 22 + } 23 + }
-122
src/lib/moderation/useReportOptions.ts
··· 1 - import {useMemo} from 'react' 2 - import {ComAtprotoModerationDefs} from '@atproto/api' 3 - import {msg} from '@lingui/macro' 4 - import {useLingui} from '@lingui/react' 5 - 6 - export interface ReportOption { 7 - reason: string 8 - title: string 9 - description: string 10 - } 11 - 12 - interface ReportOptions { 13 - account: ReportOption[] 14 - post: ReportOption[] 15 - list: ReportOption[] 16 - starterpack: ReportOption[] 17 - feedgen: ReportOption[] 18 - other: ReportOption[] 19 - convoMessage: ReportOption[] 20 - } 21 - 22 - export function useReportOptions(): ReportOptions { 23 - const {_} = useLingui() 24 - return useMemo(() => { 25 - const other = { 26 - reason: ComAtprotoModerationDefs.REASONOTHER, 27 - title: _(msg`Other`), 28 - description: _(msg`An issue not included in these options`), 29 - } 30 - const common = [ 31 - { 32 - reason: ComAtprotoModerationDefs.REASONRUDE, 33 - title: _(msg`Anti-Social Behavior`), 34 - description: _(msg`Harassment, trolling, or intolerance`), 35 - }, 36 - { 37 - reason: ComAtprotoModerationDefs.REASONVIOLATION, 38 - title: _(msg`Illegal and Urgent`), 39 - description: _(msg`Glaring violations of law or terms of service`), 40 - }, 41 - other, 42 - ] 43 - return { 44 - account: [ 45 - { 46 - reason: ComAtprotoModerationDefs.REASONMISLEADING, 47 - title: _(msg`Misleading Account`), 48 - description: _( 49 - msg`Impersonation or false claims about identity or affiliation`, 50 - ), 51 - }, 52 - { 53 - reason: ComAtprotoModerationDefs.REASONSPAM, 54 - title: _(msg`Frequently Posts Unwanted Content`), 55 - description: _(msg`Spam; excessive mentions or replies`), 56 - }, 57 - { 58 - reason: ComAtprotoModerationDefs.REASONVIOLATION, 59 - title: _(msg`Name or Description Violates Community Standards`), 60 - description: _(msg`Terms used violate community standards`), 61 - }, 62 - other, 63 - ], 64 - post: [ 65 - { 66 - reason: ComAtprotoModerationDefs.REASONMISLEADING, 67 - title: _(msg`Misleading Post`), 68 - description: _(msg`Impersonation, misinformation, or false claims`), 69 - }, 70 - { 71 - reason: ComAtprotoModerationDefs.REASONSPAM, 72 - title: _(msg`Spam`), 73 - description: _(msg`Excessive mentions or replies`), 74 - }, 75 - { 76 - reason: ComAtprotoModerationDefs.REASONSEXUAL, 77 - title: _(msg`Unwanted Sexual Content`), 78 - description: _(msg`Nudity or adult content not labeled as such`), 79 - }, 80 - ...common, 81 - ], 82 - convoMessage: [ 83 - { 84 - reason: ComAtprotoModerationDefs.REASONSPAM, 85 - title: _(msg`Spam`), 86 - description: _(msg`Excessive or unwanted messages`), 87 - }, 88 - { 89 - reason: ComAtprotoModerationDefs.REASONSEXUAL, 90 - title: _(msg`Unwanted Sexual Content`), 91 - description: _(msg`Inappropriate messages or explicit links`), 92 - }, 93 - ...common, 94 - ], 95 - list: [ 96 - { 97 - reason: ComAtprotoModerationDefs.REASONVIOLATION, 98 - title: _(msg`Name or Description Violates Community Standards`), 99 - description: _(msg`Terms used violate community standards`), 100 - }, 101 - ...common, 102 - ], 103 - starterpack: [ 104 - { 105 - reason: ComAtprotoModerationDefs.REASONVIOLATION, 106 - title: _(msg`Name or Description Violates Community Standards`), 107 - description: _(msg`Terms used violate community standards`), 108 - }, 109 - ...common, 110 - ], 111 - feedgen: [ 112 - { 113 - reason: ComAtprotoModerationDefs.REASONVIOLATION, 114 - title: _(msg`Name or Description Violates Community Standards`), 115 - description: _(msg`Terms used violate community standards`), 116 - }, 117 - ...common, 118 - ], 119 - other: common, 120 - } 121 - }, [_]) 122 - }
+89 -181
src/locale/locales/en/messages.po
··· 1047 1047 msgstr "" 1048 1048 1049 1049 #: src/components/moderation/ReportDialog/utils/useReportOptions.ts:239 1050 - #: src/lib/moderation/useReportOptions.ts:28 1051 1050 msgid "An issue not included in these options" 1052 1051 msgstr "" 1053 1052 ··· 1063 1062 #: src/components/hooks/useFollowMethods.ts:50 1064 1063 #: src/components/ProfileCard.tsx:484 1065 1064 #: src/components/ProfileCard.tsx:505 1066 - #: src/view/com/profile/FollowButton.tsx:38 1067 - #: src/view/com/profile/FollowButton.tsx:48 1068 1065 msgid "An issue occurred, please try again." 1069 1066 msgstr "" 1070 1067 ··· 1105 1102 1106 1103 #: src/components/dialogs/nuxs/InitialVerificationAnnouncement.tsx:37 1107 1104 msgid "Announcing verification on Bluesky" 1108 - msgstr "" 1109 - 1110 - #: src/lib/moderation/useReportOptions.ts:33 1111 - msgid "Anti-Social Behavior" 1112 1105 msgstr "" 1113 1106 1114 1107 #: src/view/com/composer/threadgate/ThreadgateBtn.tsx:48 ··· 1304 1297 1305 1298 #: src/components/moderation/LabelsOnMeDialog.tsx:333 1306 1299 #: src/components/moderation/LabelsOnMeDialog.tsx:334 1307 - #: src/screens/Login/ChooseAccountForm.tsx:90 1308 - #: src/screens/Login/ChooseAccountForm.tsx:95 1300 + #: src/screens/Login/ChooseAccountForm.tsx:91 1301 + #: src/screens/Login/ChooseAccountForm.tsx:96 1309 1302 #: src/screens/Login/ForgotPasswordForm.tsx:123 1310 1303 #: src/screens/Login/ForgotPasswordForm.tsx:129 1311 1304 #: src/screens/Login/LoginForm.tsx:310 ··· 1412 1405 msgid "Block and Delete" 1413 1406 msgstr "" 1414 1407 1415 - #: src/components/dms/AfterReportDialog.tsx:180 1416 - msgid "Block and/or delete this conversation" 1417 - msgstr "" 1418 - 1419 1408 #: src/screens/ProfileList/components/SubscribeMenu.tsx:125 1420 1409 msgid "Block list" 1421 1410 msgstr "" ··· 1437 1426 msgid "Block User" 1438 1427 msgstr "" 1439 1428 1429 + #: src/components/dms/AfterReportDialog.tsx:180 1430 + msgid "Block user and/or delete this conversation" 1431 + msgstr "" 1432 + 1440 1433 #: src/components/Post/Embed/index.tsx:186 1441 1434 msgid "Blocked" 1442 1435 msgstr "" ··· 1592 1585 #: src/components/LabelingServiceCard/index.tsx:62 1593 1586 #: src/components/moderation/ReportDialog/index.tsx:834 1594 1587 #: src/screens/Search/components/StarterPackCard.tsx:106 1595 - #: src/screens/Search/Explore.tsx:937 1588 + #: src/screens/Search/Explore.tsx:936 1596 1589 msgid "By {0}" 1597 1590 msgstr "" 1598 1591 1599 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:451 1592 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:453 1600 1593 msgid "By <0>{0}</0>" 1601 1594 msgstr "" 1602 1595 ··· 1642 1635 #: src/components/live/GoLiveDialog.tsx:244 1643 1636 #: src/components/live/GoLiveDialog.tsx:250 1644 1637 #: src/components/Menu/index.tsx:350 1645 - #: src/components/PostControls/RepostButton.tsx:210 1638 + #: src/components/PostControls/RepostButton.tsx:209 1646 1639 #: src/components/Prompt.tsx:144 1647 1640 #: src/components/Prompt.tsx:146 1648 1641 #: src/screens/Deactivated.tsx:158 ··· 2260 2253 msgid "Continue" 2261 2254 msgstr "" 2262 2255 2263 - #: src/components/AccountList.tsx:128 2256 + #: src/components/AccountList.tsx:135 2264 2257 msgid "Continue as {0} (currently signed in)" 2265 2258 msgstr "" 2266 2259 ··· 2839 2832 msgid "Discover new custom feeds" 2840 2833 msgstr "" 2841 2834 2842 - #: src/screens/Search/Explore.tsx:432 2835 + #: src/screens/Search/Explore.tsx:431 2843 2836 #: src/view/screens/Feeds.tsx:730 2844 2837 msgid "Discover New Feeds" 2845 2838 msgstr "" ··· 2951 2944 msgid "Double tap to close the dialog" 2952 2945 msgstr "" 2953 2946 2954 - #: src/screens/VideoFeed/index.tsx:1087 2947 + #: src/screens/VideoFeed/index.tsx:1101 2955 2948 msgid "Double tap to like" 2956 2949 msgstr "" 2957 2950 ··· 3351 3344 msgid "Everything else" 3352 3345 msgstr "" 3353 3346 3354 - #: src/lib/moderation/useReportOptions.ts:73 3355 - msgid "Excessive mentions or replies" 3356 - msgstr "" 3357 - 3358 - #: src/lib/moderation/useReportOptions.ts:86 3359 - msgid "Excessive or unwanted messages" 3360 - msgstr "" 3361 - 3362 3347 #: src/components/dialogs/MutedWords.tsx:316 3363 3348 msgid "Exclude users you follow" 3364 3349 msgstr "" ··· 3395 3380 msgid "Expand post text" 3396 3381 msgstr "" 3397 3382 3398 - #: src/screens/VideoFeed/index.tsx:972 3383 + #: src/screens/VideoFeed/index.tsx:973 3399 3384 msgid "Expands or collapses post text" 3400 3385 msgstr "" 3401 3386 ··· 3536 3521 msgid "Failed to load conversations" 3537 3522 msgstr "" 3538 3523 3539 - #: src/screens/Search/Explore.tsx:468 3540 - #: src/screens/Search/Explore.tsx:520 3541 - #: src/screens/Search/Explore.tsx:558 3542 - #: src/screens/Search/Explore.tsx:597 3524 + #: src/screens/Search/Explore.tsx:467 3525 + #: src/screens/Search/Explore.tsx:519 3526 + #: src/screens/Search/Explore.tsx:557 3527 + #: src/screens/Search/Explore.tsx:596 3543 3528 msgid "Failed to load feeds preferences" 3544 3529 msgstr "" 3545 3530 ··· 3569 3554 msgid "Failed to load preference." 3570 3555 msgstr "" 3571 3556 3572 - #: src/screens/Search/Explore.tsx:461 3573 - #: src/screens/Search/Explore.tsx:513 3574 - #: src/screens/Search/Explore.tsx:551 3575 - #: src/screens/Search/Explore.tsx:590 3557 + #: src/screens/Search/Explore.tsx:460 3558 + #: src/screens/Search/Explore.tsx:512 3559 + #: src/screens/Search/Explore.tsx:550 3560 + #: src/screens/Search/Explore.tsx:589 3576 3561 msgid "Failed to load suggested feeds" 3577 3562 msgstr "" 3578 3563 3579 - #: src/screens/Search/Explore.tsx:371 3564 + #: src/screens/Search/Explore.tsx:370 3580 3565 msgid "Failed to load suggested follows" 3581 3566 msgstr "" 3582 3567 ··· 3703 3688 msgid "Feed identifier" 3704 3689 msgstr "" 3705 3690 3706 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:354 3691 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:355 3707 3692 msgid "Feed menu" 3708 3693 msgstr "" 3709 3694 ··· 3846 3831 #: src/components/ProfileHoverCard/index.web.tsx:507 3847 3832 #: src/screens/PostThread/components/ThreadItemAnchorFollowButton.tsx:131 3848 3833 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:258 3849 - #: src/screens/VideoFeed/index.tsx:856 3850 - msgid "Follow" 3851 - msgstr "" 3852 - 3853 - #: src/view/com/profile/FollowButton.tsx:72 3854 - msgctxt "action" 3834 + #: src/screens/VideoFeed/index.tsx:857 3855 3835 msgid "Follow" 3856 3836 msgstr "" 3857 3837 ··· 3860 3840 msgid "Follow {0}" 3861 3841 msgstr "" 3862 3842 3863 - #: src/screens/VideoFeed/index.tsx:833 3843 + #: src/screens/VideoFeed/index.tsx:834 3864 3844 msgid "Follow {handle}" 3865 3845 msgstr "" 3866 3846 ··· 3897 3877 msgid "Follow back" 3898 3878 msgstr "" 3899 3879 3900 - #: src/view/com/profile/FollowButton.tsx:81 3901 - msgctxt "action" 3902 - msgid "Follow back" 3903 - msgstr "" 3904 - 3905 3880 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:113 3906 3881 msgid "Followed all accounts!" 3907 3882 msgstr "" ··· 3937 3912 #: src/components/ProfileHoverCard/index.web.tsx:506 3938 3913 #: src/screens/PostThread/components/ThreadItemAnchorFollowButton.tsx:134 3939 3914 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:254 3940 - #: src/screens/VideoFeed/index.tsx:854 3915 + #: src/screens/VideoFeed/index.tsx:855 3941 3916 msgid "Following" 3942 3917 msgstr "" 3943 3918 ··· 3952 3927 msgid "Following {0}" 3953 3928 msgstr "" 3954 3929 3955 - #: src/screens/VideoFeed/index.tsx:832 3930 + #: src/screens/VideoFeed/index.tsx:833 3956 3931 msgid "Following {handle}" 3957 3932 msgstr "" 3958 3933 ··· 4031 4006 4032 4007 #: src/screens/Onboarding/StepFinished/ValuePropositionPager.shared.tsx:12 4033 4008 msgid "Free your feed" 4034 - msgstr "" 4035 - 4036 - #: src/lib/moderation/useReportOptions.ts:54 4037 - msgid "Frequently Posts Unwanted Content" 4038 4009 msgstr "" 4039 4010 4040 4011 #: src/screens/Settings/NotificationSettings/components/PreferenceControls.tsx:157 ··· 4131 4102 msgid "Give your profile a face" 4132 4103 msgstr "" 4133 4104 4134 - #: src/lib/moderation/useReportOptions.ts:39 4135 - msgid "Glaring violations of law or terms of service" 4136 - msgstr "" 4137 - 4138 4105 #: src/components/moderation/ReportDialog/utils/useReportOptions.ts:142 4139 4106 msgid "Glorification of violence" 4140 4107 msgstr "" ··· 4149 4116 #: src/screens/ProfileList/components/ErrorScreen.tsx:34 4150 4117 #: src/screens/ProfileList/components/ErrorScreen.tsx:40 4151 4118 #: src/screens/VideoFeed/components/Header.tsx:163 4152 - #: src/screens/VideoFeed/index.tsx:1148 4153 - #: src/screens/VideoFeed/index.tsx:1152 4119 + #: src/screens/VideoFeed/index.tsx:1162 4120 + #: src/screens/VideoFeed/index.tsx:1166 4154 4121 #: src/view/com/auth/LoggedOut.tsx:72 4155 4122 #: src/view/screens/NotFound.tsx:57 4156 4123 msgid "Go back" ··· 4285 4252 msgid "Harassment or hate" 4286 4253 msgstr "" 4287 4254 4288 - #: src/lib/moderation/useReportOptions.ts:34 4289 - msgid "Harassment, trolling, or intolerance" 4290 - msgstr "" 4291 - 4292 4255 #: src/components/moderation/ReportDialog/utils/useReportOptions.ts:189 4293 4256 msgid "Harmful or high-risk activities" 4294 4257 msgstr "" ··· 4344 4307 msgid "Hidden" 4345 4308 msgstr "" 4346 4309 4347 - #: src/screens/VideoFeed/index.tsx:630 4310 + #: src/screens/VideoFeed/index.tsx:631 4348 4311 msgid "Hidden by your moderation settings." 4349 4312 msgstr "" 4350 4313 ··· 4553 4516 msgid "If you're trying to change your handle or email, do so before you deactivate." 4554 4517 msgstr "" 4555 4518 4556 - #: src/lib/moderation/useReportOptions.ts:38 4557 - msgid "Illegal and Urgent" 4558 - msgstr "" 4559 - 4560 4519 #: src/view/com/util/images/Gallery.tsx:75 4561 4520 msgid "Image" 4562 4521 msgstr "" ··· 4583 4542 msgid "Impersonation" 4584 4543 msgstr "" 4585 4544 4586 - #: src/lib/moderation/useReportOptions.ts:49 4587 - msgid "Impersonation or false claims about identity or affiliation" 4588 - msgstr "" 4589 - 4590 - #: src/lib/moderation/useReportOptions.ts:68 4591 - msgid "Impersonation, misinformation, or false claims" 4592 - msgstr "" 4593 - 4594 4545 #: src/screens/Settings/NotificationSettings/index.tsx:284 4595 4546 msgid "In-app" 4596 4547 msgstr "" ··· 4617 4568 4618 4569 #: src/screens/Settings/NotificationSettings/index.tsx:273 4619 4570 msgid "In-app, Push, People you follow" 4620 - msgstr "" 4621 - 4622 - #: src/lib/moderation/useReportOptions.ts:91 4623 - msgid "Inappropriate messages or explicit links" 4624 4571 msgstr "" 4625 4572 4626 4573 #. Title message shown in chat requests inbox when it's empty ··· 4944 4891 msgid "Light" 4945 4892 msgstr "" 4946 4893 4947 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:518 4894 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:515 4948 4895 msgid "Like" 4949 4896 msgstr "" 4950 4897 ··· 4966 4913 msgid "Like notifications" 4967 4914 msgstr "" 4968 4915 4969 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:505 4916 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:502 4970 4917 msgid "Like this feed" 4971 4918 msgstr "" 4972 4919 ··· 4991 4938 msgstr "" 4992 4939 4993 4940 #: src/components/LabelingServiceCard/index.tsx:96 4994 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:493 4941 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:490 4995 4942 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:290 4996 4943 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:304 4997 4944 msgid "Liked by {likeCount, plural, one {# user} other {# users}}" ··· 5132 5079 msgid "Live link" 5133 5080 msgstr "" 5134 5081 5135 - #: src/screens/Search/Explore.tsx:84 5082 + #: src/screens/Search/Explore.tsx:83 5136 5083 msgid "Load more" 5137 5084 msgstr "" 5138 5085 5139 - #: src/screens/Search/Explore.tsx:502 5140 - #: src/screens/Search/Explore.tsx:579 5086 + #: src/screens/Search/Explore.tsx:501 5087 + #: src/screens/Search/Explore.tsx:578 5141 5088 msgid "Load more suggested feeds" 5142 5089 msgstr "" 5143 5090 ··· 5158 5105 5159 5106 #: src/Navigation.tsx:321 5160 5107 msgid "Log" 5108 + msgstr "" 5109 + 5110 + #: src/components/AccountList.tsx:191 5111 + msgid "Logged out" 5161 5112 msgstr "" 5162 5113 5163 5114 #: src/screens/Settings/PrivacyAndSecuritySettings.tsx:106 ··· 5325 5276 msgid "Misleading" 5326 5277 msgstr "" 5327 5278 5328 - #: src/lib/moderation/useReportOptions.ts:47 5329 - msgid "Misleading Account" 5330 - msgstr "" 5331 - 5332 - #: src/lib/moderation/useReportOptions.ts:67 5333 - msgid "Misleading Post" 5334 - msgstr "" 5335 - 5336 5279 #: src/Navigation.tsx:177 5337 5280 #: src/screens/Moderation/index.tsx:100 5338 5281 #: src/screens/Settings/Settings.tsx:188 ··· 5536 5479 5537 5480 #: src/components/dialogs/lists/CreateOrEditListDialog.tsx:399 5538 5481 msgid "Name" 5539 - msgstr "" 5540 - 5541 - #: src/lib/moderation/useReportOptions.ts:59 5542 - #: src/lib/moderation/useReportOptions.ts:98 5543 - #: src/lib/moderation/useReportOptions.ts:106 5544 - #: src/lib/moderation/useReportOptions.ts:114 5545 - msgid "Name or Description Violates Community Standards" 5546 5482 msgstr "" 5547 5483 5548 5484 #: src/lib/interests.ts:66 ··· 5585 5521 msgid "New" 5586 5522 msgstr "" 5587 5523 5588 - #: src/view/screens/Lists.tsx:79 5524 + #: src/view/screens/Lists.tsx:78 5589 5525 #: src/view/screens/ModerationModlists.tsx:79 5590 5526 msgctxt "action" 5591 5527 msgid "New" ··· 5805 5741 msgid "No results" 5806 5742 msgstr "" 5807 5743 5808 - #: src/screens/Search/Explore.tsx:794 5744 + #: src/screens/Search/Explore.tsx:793 5809 5745 msgid "No results for \"{0}\"." 5810 5746 msgstr "" 5811 5747 ··· 5823 5759 msgid "No results found for {query}" 5824 5760 msgstr "" 5825 5761 5826 - #: src/screens/Search/Explore.tsx:798 5762 + #: src/screens/Search/Explore.tsx:797 5827 5763 msgid "No results." 5828 5764 msgstr "" 5829 5765 ··· 5956 5892 msgid "Nudity" 5957 5893 msgstr "" 5958 5894 5959 - #: src/lib/moderation/useReportOptions.ts:78 5960 - msgid "Nudity or adult content not labeled as such" 5961 - msgstr "" 5962 - 5963 5895 #: src/lib/moderation/useLabelBehaviorDescription.ts:14 5964 5896 #: src/screens/Settings/NotificationSettings/index.tsx:291 5965 5897 msgid "Off" ··· 6067 5999 msgid "Open conversation options" 6068 6000 msgstr "" 6069 6001 6070 - #: src/components/Layout/Header/index.tsx:160 6002 + #: src/components/Layout/Header/index.tsx:159 6071 6003 msgid "Open drawer menu" 6072 6004 msgstr "" 6073 6005 ··· 6080 6012 msgid "Open feed info screen" 6081 6013 msgstr "" 6082 6014 6083 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:291 6084 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:296 6015 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:292 6016 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:297 6085 6017 msgid "Open feed options menu" 6086 6018 msgstr "" 6087 6019 ··· 6249 6181 6250 6182 #: src/components/moderation/ReportDialog/utils/useReportOptions.ts:238 6251 6183 #: src/components/moderation/ReportDialog/utils/useReportOptions.ts:242 6252 - #: src/lib/moderation/useReportOptions.ts:27 6253 6184 #: src/view/com/composer/labels/LabelsBtn.tsx:185 6254 6185 msgid "Other" 6255 6186 msgstr "" 6256 6187 6257 - #: src/components/AccountList.tsx:88 6188 + #: src/components/AccountList.tsx:93 6258 6189 msgid "Other account" 6259 6190 msgstr "" 6260 6191 ··· 6383 6314 msgid "Pictures meant for adults." 6384 6315 msgstr "" 6385 6316 6386 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:523 6387 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:530 6317 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:520 6318 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:527 6388 6319 #: src/screens/SavedFeeds.tsx:351 6389 6320 msgid "Pin feed" 6390 6321 msgstr "" ··· 6398 6329 msgid "Pin to home" 6399 6330 msgstr "" 6400 6331 6401 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:337 6332 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:338 6402 6333 msgid "Pin to Home" 6403 6334 msgstr "" 6404 6335 ··· 6647 6578 #: src/screens/PostThread/components/ThreadItemAnchor.tsx:134 6648 6579 #: src/screens/PostThread/components/ThreadItemPost.tsx:112 6649 6580 #: src/screens/PostThread/components/ThreadItemTreePost.tsx:108 6650 - #: src/screens/VideoFeed/index.tsx:534 6581 + #: src/screens/VideoFeed/index.tsx:535 6651 6582 msgid "Post has been deleted" 6652 6583 msgstr "" 6653 6584 ··· 6940 6871 msgid "Read blog post" 6941 6872 msgstr "" 6942 6873 6943 - #: src/screens/VideoFeed/index.tsx:973 6874 + #: src/screens/VideoFeed/index.tsx:974 6944 6875 msgid "Read less" 6945 6876 msgstr "" 6946 6877 6947 - #: src/screens/VideoFeed/index.tsx:973 6878 + #: src/screens/VideoFeed/index.tsx:974 6948 6879 msgid "Read more" 6949 6880 msgstr "" 6950 6881 ··· 7077 7008 msgid "Remove feed?" 7078 7009 msgstr "" 7079 7010 7080 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:319 7081 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:325 7011 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:320 7012 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:326 7082 7013 #: src/screens/ProfileList/components/MoreOptionsMenu.tsx:181 7083 7014 #: src/screens/ProfileList/components/MoreOptionsMenu.tsx:184 7084 7015 #: src/screens/SavedFeeds.tsx:340 ··· 7296 7227 msgid "Report dialog" 7297 7228 msgstr "" 7298 7229 7299 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:546 7300 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:552 7230 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:543 7231 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:549 7301 7232 msgid "Report feed" 7302 7233 msgstr "" 7303 7234 ··· 7523 7454 #: src/screens/Profile/ProfileFeed/index.tsx:93 7524 7455 #: src/screens/ProfileList/components/ErrorScreen.tsx:35 7525 7456 #: src/screens/Settings/components/ChangeHandleDialog.tsx:569 7526 - #: src/screens/VideoFeed/index.tsx:1149 7457 + #: src/screens/VideoFeed/index.tsx:1163 7527 7458 #: src/view/screens/NotFound.tsx:60 7528 7459 msgid "Returns to previous page" 7529 7460 msgstr "" ··· 7582 7513 msgid "Save QR code" 7583 7514 msgstr "" 7584 7515 7585 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:320 7586 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:326 7516 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:321 7517 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:327 7587 7518 msgid "Save to my feeds" 7588 7519 msgstr "" 7589 7520 ··· 7680 7611 msgid "Search for feeds that you want to suggest to others." 7681 7612 msgstr "" 7682 7613 7683 - #: src/screens/Search/Explore.tsx:358 7614 + #: src/screens/Search/Explore.tsx:357 7684 7615 msgid "Search for more accounts" 7685 7616 msgstr "" 7686 7617 7687 - #: src/screens/Search/Explore.tsx:435 7618 + #: src/screens/Search/Explore.tsx:434 7688 7619 msgid "Search for more feeds" 7689 7620 msgstr "" 7690 7621 ··· 8112 8043 msgid "Share QR code" 8113 8044 msgstr "" 8114 8045 8115 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:474 8046 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:471 8116 8047 msgid "Share this feed" 8117 8048 msgstr "" 8118 8049 ··· 8156 8087 #: src/components/moderation/ScreenHider.tsx:178 8157 8088 #: src/components/moderation/ScreenHider.tsx:181 8158 8089 #: src/screens/List/ListHiddenScreen.tsx:190 8159 - #: src/screens/VideoFeed/index.tsx:633 8160 - #: src/screens/VideoFeed/index.tsx:639 8090 + #: src/screens/VideoFeed/index.tsx:634 8091 + #: src/screens/VideoFeed/index.tsx:640 8161 8092 msgid "Show anyway" 8162 8093 msgstr "" 8163 8094 ··· 8272 8203 msgid "Sign in" 8273 8204 msgstr "" 8274 8205 8275 - #: src/components/AccountList.tsx:129 8206 + #: src/components/AccountList.tsx:136 8276 8207 msgid "Sign in as {0}" 8277 8208 msgstr "" 8278 8209 8279 - #: src/screens/Login/ChooseAccountForm.tsx:80 8210 + #: src/screens/Login/ChooseAccountForm.tsx:81 8280 8211 msgid "Sign in as..." 8281 8212 msgstr "" 8282 8213 ··· 8289 8220 msgid "Sign in or create your account to join the conversation!" 8290 8221 msgstr "" 8291 8222 8292 - #: src/components/AccountList.tsx:68 8223 + #: src/components/AccountList.tsx:70 8293 8224 msgid "Sign in to account that is not listed" 8294 8225 msgstr "" 8295 8226 ··· 8425 8356 msgid "Something went wrong. Please try again." 8426 8357 msgstr "" 8427 8358 8428 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:542 8359 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:539 8429 8360 msgid "Something wrong? Let us know." 8430 8361 msgstr "" 8431 8362 ··· 8456 8387 msgstr "" 8457 8388 8458 8389 #: src/components/moderation/ReportDialog/utils/useReportOptions.ts:39 8459 - #: src/lib/moderation/useReportOptions.ts:72 8460 - #: src/lib/moderation/useReportOptions.ts:85 8461 8390 msgid "Spam" 8462 8391 msgstr "" 8463 8392 ··· 8465 8394 msgid "Spam or other inauthentic behavior or deception" 8466 8395 msgstr "" 8467 8396 8468 - #: src/lib/moderation/useReportOptions.ts:55 8469 - msgid "Spam; excessive mentions or replies" 8470 - msgstr "" 8471 - 8472 8397 #: src/lib/interests.ts:72 8473 8398 #: src/screens/Onboarding/index.tsx:58 8474 8399 #: src/screens/Search/modules/ExploreTrendingTopics.tsx:230 ··· 8520 8445 msgid "Starter pack is invalid" 8521 8446 msgstr "" 8522 8447 8523 - #: src/screens/Search/Explore.tsx:626 8448 + #: src/screens/Search/Explore.tsx:625 8524 8449 #: src/view/screens/Profile.tsx:231 8525 8450 msgid "Starter Packs" 8526 8451 msgstr "" ··· 8607 8532 msgid "Successfully verified" 8608 8533 msgstr "" 8609 8534 8610 - #: src/screens/Search/Explore.tsx:355 8535 + #: src/screens/Search/Explore.tsx:354 8611 8536 msgid "Suggested Accounts" 8612 8537 msgstr "" 8613 8538 ··· 8754 8679 msgid "Terms of Service" 8755 8680 msgstr "" 8756 8681 8757 - #: src/lib/moderation/useReportOptions.ts:60 8758 - #: src/lib/moderation/useReportOptions.ts:99 8759 - #: src/lib/moderation/useReportOptions.ts:107 8760 - #: src/lib/moderation/useReportOptions.ts:115 8761 - msgid "Terms used violate community standards" 8762 - msgstr "" 8763 - 8764 8682 #: src/components/dialogs/MutedWords.tsx:271 8765 8683 msgid "Text & tags" 8766 8684 msgstr "" ··· 8804 8722 msgid "That's all, folks!" 8805 8723 msgstr "" 8806 8724 8807 - #: src/screens/VideoFeed/index.tsx:1121 8725 + #: src/screens/VideoFeed/index.tsx:1135 8808 8726 msgid "That's everything!" 8809 8727 msgstr "" 8810 8728 ··· 8932 8850 msgid "There was an issue contacting the server" 8933 8851 msgstr "" 8934 8852 8935 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:418 8853 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:419 8936 8854 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:109 8937 8855 msgid "There was an issue contacting the server, please check your internet connection and try again." 8938 8856 msgstr "" ··· 8941 8859 msgid "There was an issue fetching notifications. Tap here to try again." 8942 8860 msgstr "" 8943 8861 8944 - #: src/screens/Search/Explore.tsx:993 8862 + #: src/screens/Search/Explore.tsx:992 8945 8863 #: src/view/com/posts/PostFeed.tsx:712 8946 8864 msgid "There was an issue fetching posts. Tap here to try again." 8947 8865 msgstr "" ··· 9473 9391 msgid "Undo repost ({0, plural, one {# repost} other {# reposts}})" 9474 9392 msgstr "" 9475 9393 9476 - #: src/view/com/profile/FollowButton.tsx:63 9477 - msgctxt "action" 9478 - msgid "Unfollow" 9479 - msgstr "" 9480 - 9481 9394 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:242 9482 9395 msgid "Unfollow {0}" 9483 9396 msgstr "" ··· 9487 9400 msgid "Unfollow account" 9488 9401 msgstr "" 9489 9402 9490 - #: src/screens/VideoFeed/index.tsx:837 9403 + #: src/screens/VideoFeed/index.tsx:838 9491 9404 msgid "Unfollows the user" 9492 9405 msgstr "" 9493 9406 ··· 9511 9424 msgid "Unlabeled, abusive, or non-consensual adult content" 9512 9425 msgstr "" 9513 9426 9514 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:518 9427 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:515 9515 9428 msgid "Unlike" 9516 9429 msgstr "" 9517 9430 ··· 9566 9479 msgid "Unpin" 9567 9480 msgstr "" 9568 9481 9569 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:523 9570 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:530 9482 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:520 9483 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:527 9571 9484 #: src/screens/SavedFeeds.tsx:351 9572 9485 msgid "Unpin feed" 9573 9486 msgstr "" ··· 9576 9489 msgid "Unpin Feed" 9577 9490 msgstr "" 9578 9491 9579 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:310 9580 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:312 9492 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:311 9493 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:313 9581 9494 msgid "Unpin from home" 9582 9495 msgstr "" 9583 9496 ··· 9627 9540 9628 9541 #: src/view/com/composer/Composer.tsx:895 9629 9542 msgid "Unsupported video type: {mimeType}" 9630 - msgstr "" 9631 - 9632 - #: src/lib/moderation/useReportOptions.ts:77 9633 - #: src/lib/moderation/useReportOptions.ts:90 9634 - msgid "Unwanted Sexual Content" 9635 9543 msgstr "" 9636 9544 9637 9545 #: src/screens/Settings/components/OTAInfo.tsx:58 ··· 9951 9859 msgid "Video Games" 9952 9860 msgstr "" 9953 9861 9954 - #: src/screens/VideoFeed/index.tsx:1079 9862 + #: src/screens/VideoFeed/index.tsx:1093 9955 9863 msgid "Video is paused" 9956 9864 msgstr "" 9957 9865 9958 - #: src/screens/VideoFeed/index.tsx:1079 9866 + #: src/screens/VideoFeed/index.tsx:1093 9959 9867 msgid "Video is playing" 9960 9868 msgstr "" 9961 9869 ··· 9993 9901 msgstr "" 9994 9902 9995 9903 #: src/components/ProfileCard.tsx:124 9996 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:454 9904 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:456 9997 9905 #: src/screens/Search/components/SearchProfileCard.tsx:36 9998 - #: src/screens/VideoFeed/index.tsx:796 9906 + #: src/screens/VideoFeed/index.tsx:797 9999 9907 #: src/view/com/notifications/NotificationFeedItem.tsx:599 10000 9908 msgid "View {0}'s profile" 10001 9909 msgstr "" ··· 10016 9924 msgid "View debug entry" 10017 9925 msgstr "" 10018 9926 10019 - #: src/screens/VideoFeed/index.tsx:661 10020 - #: src/screens/VideoFeed/index.tsx:679 9927 + #: src/screens/VideoFeed/index.tsx:662 9928 + #: src/screens/VideoFeed/index.tsx:680 10021 9929 msgid "View details" 10022 9930 msgstr "" 10023 9931 ··· 10065 9973 msgid "View this user's verifications" 10066 9974 msgstr "" 10067 9975 10068 - #: src/screens/Profile/components/ProfileFeedHeader.tsx:489 9976 + #: src/screens/Profile/components/ProfileFeedHeader.tsx:486 10069 9977 msgid "View users who like this feed" 10070 9978 msgstr "" 10071 9979 ··· 10391 10299 msgstr "" 10392 10300 10393 10301 #: src/components/dms/AfterReportDialog.tsx:49 10394 - msgid "Would you like to block this account or delete this conversation?" 10302 + msgid "Would you like to block this user and/or delete this conversation?" 10395 10303 msgstr "" 10396 10304 10397 10305 #: src/screens/Messages/components/MessageInput.tsx:154 ··· 10861 10769 msgid "You've reached your daily limit for video uploads (too many videos)" 10862 10770 msgstr "" 10863 10771 10864 - #: src/screens/VideoFeed/index.tsx:1130 10772 + #: src/screens/VideoFeed/index.tsx:1144 10865 10773 msgid "You've run out of videos to watch. Maybe it's a good time to take a break?" 10866 10774 msgstr "" 10867 10775
+3 -2
src/screens/Login/ChooseAccountForm.tsx
··· 8 8 import {type SessionAccount, useSession, useSessionApi} from '#/state/session' 9 9 import {useLoggedOutViewControls} from '#/state/shell/logged-out' 10 10 import * as Toast from '#/view/com/util/Toast' 11 - import {atoms as a} from '#/alf' 11 + import {atoms as a, web} from '#/alf' 12 12 import {AccountList} from '#/components/AccountList' 13 13 import {Button, ButtonText} from '#/components/Button' 14 14 import * as TextField from '#/components/forms/TextField' ··· 74 74 return ( 75 75 <FormContainer 76 76 testID="chooseAccountForm" 77 - titleText={<Trans>Select account</Trans>}> 77 + titleText={<Trans>Select account</Trans>} 78 + style={web([a.py_2xl])}> 78 79 <View> 79 80 <TextField.LabelText> 80 81 <Trans>Sign in as...</Trans>
+2 -2
src/screens/Messages/components/ChatDisabled.tsx
··· 1 1 import {useCallback, useState} from 'react' 2 2 import {View} from 'react-native' 3 - import {ComAtprotoModerationDefs} from '@atproto/api' 3 + import {ToolsOzoneReportDefs} from '@atproto/api' 4 4 import {msg, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 import {useMutation} from '@tanstack/react-query' ··· 81 81 throw new Error('No current account, should be unreachable') 82 82 await agent.createModerationReport( 83 83 { 84 - reasonType: ComAtprotoModerationDefs.REASONAPPEAL, 84 + reasonType: ToolsOzoneReportDefs.REASONAPPEAL, 85 85 subject: { 86 86 $type: 'com.atproto.admin.defs#repoRef', 87 87 did: currentAccount.did,
+4 -4
src/screens/Profile/Header/Shell.tsx
··· 9 9 } from 'react-native-reanimated' 10 10 import {useSafeAreaInsets} from 'react-native-safe-area-context' 11 11 import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api' 12 + import {utils} from '@bsky.app/alf' 12 13 import {msg} from '@lingui/macro' 13 14 import {useLingui} from '@lingui/react' 14 15 import {useNavigation} from '@react-navigation/native' ··· 26 27 import {UserAvatar} from '#/view/com/util/UserAvatar' 27 28 import {UserBanner} from '#/view/com/util/UserBanner' 28 29 import {atoms as a, platform, useTheme} from '#/alf' 29 - import {transparentifyColor} from '#/alf/util/colorGeneration' 30 30 import {Button} from '#/components/Button' 31 31 import {useDialogControl} from '#/components/Dialog' 32 32 import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow' ··· 175 175 style={[ 176 176 a.align_center, 177 177 a.justify_center, 178 - a.rounded_sm, 178 + a.rounded_full, 179 179 { 180 180 width: 31, 181 181 height: 31, 182 - backgroundColor: transparentifyColor('#000', 0.5), 182 + backgroundColor: utils.alpha('#000', 0.5), 183 183 }, 184 184 hovered && { 185 - backgroundColor: transparentifyColor('#000', 0.75), 185 + backgroundColor: utils.alpha('#000', 0.75), 186 186 }, 187 187 ]}> 188 188 <ArrowLeftIcon size="lg" fill="white" />
+1 -2
src/screens/Search/Explore.tsx
··· 15 15 import {sanitizeHandle} from '#/lib/strings/handles' 16 16 import {logger} from '#/logger' 17 17 import {type MetricEvents} from '#/logger/metrics' 18 - import {isNative} from '#/platform/detection' 19 18 import {useLanguagePrefs} from '#/state/preferences/languages' 20 19 import {useModerationOpts} from '#/state/preferences/moderation-opts' 21 20 import {RQKEY_ROOT_PAGINATED as useActorSearchPaginatedQueryKeyRoot} from '#/state/queries/actor-search' ··· 924 923 } 925 924 case 'preview:header': { 926 925 return ( 927 - <ModuleHeader.Container style={[a.pt_xs]} bottomBorder={isNative}> 926 + <ModuleHeader.Container style={[a.pt_xs]} bottomBorder> 928 927 {/* Very non-scientific way to avoid small gap on scroll */} 929 928 <View style={[a.absolute, a.inset_0, t.atoms.bg, {top: -2}]} /> 930 929 <ModuleHeader.FeedLink feed={item.feed}>
+1 -1
src/screens/Search/Shell.tsx
··· 362 362 size="large" 363 363 variant="ghost" 364 364 color="secondary" 365 - style={[a.px_sm]} 365 + style={[a.px_sm, a.rounded_sm]} 366 366 onPress={onPressCancelSearch} 367 367 hitSlop={HITSLOP_10}> 368 368 <ButtonText>
+1 -1
src/screens/Search/modules/ExploreTrendingTopics.tsx
··· 97 97 PressableComponent={Pressable}> 98 98 {({hovered, pressed}) => ( 99 99 <> 100 - <SubtleHover hover={hovered || pressed} /> 100 + <SubtleHover hover={hovered || pressed} native /> 101 101 <View style={[gutters, a.w_full, a.py_lg, a.flex_row, a.gap_2xs]}> 102 102 <View style={[a.flex_1, a.gap_xs]}> 103 103 <View style={[a.flex_row]}>
+1 -1
src/screens/Settings/components/SettingsList.tsx
··· 252 252 <View 253 253 style={[ 254 254 a.border_t, 255 - t.atoms.border_contrast_medium, 255 + t.atoms.border_contrast_low, 256 256 a.w_full, 257 257 a.my_sm, 258 258 style,
+2 -2
src/screens/Takendown.tsx
··· 3 3 import {SystemBars} from 'react-native-edge-to-edge' 4 4 import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' 5 5 import {useSafeAreaInsets} from 'react-native-safe-area-context' 6 - import {type ComAtprotoAdminDefs, ComAtprotoModerationDefs} from '@atproto/api' 6 + import {type ComAtprotoAdminDefs, ToolsOzoneReportDefs} from '@atproto/api' 7 7 import {msg, Trans} from '@lingui/macro' 8 8 import {useLingui} from '@lingui/react' 9 9 import {useMutation} from '@tanstack/react-query' ··· 54 54 if (!currentAccount) throw new Error('No session') 55 55 await agent.com.atproto.moderation.createReport( 56 56 { 57 - reasonType: ComAtprotoModerationDefs.REASONAPPEAL, 57 + reasonType: ToolsOzoneReportDefs.REASONAPPEAL, 58 58 subject: { 59 59 $type: 'com.atproto.admin.defs#repoRef', 60 60 did: currentAccount.did,
+22 -8
src/screens/VideoFeed/index.tsx
··· 57 57 import {sanitizeDisplayName} from '#/lib/strings/display-names' 58 58 import {cleanError} from '#/lib/strings/errors' 59 59 import {sanitizeHandle} from '#/lib/strings/handles' 60 + import {logger} from '#/logger' 60 61 import {isAndroid} from '#/platform/detection' 61 62 import {useA11y} from '#/state/a11y' 62 63 import { ··· 204 205 }, [params]) 205 206 const feedUri = params.type === 'feedgen' ? params.uri : undefined 206 207 const {data: feedInfo} = useFeedInfo(feedUri) 207 - const feedFeedback = useFeedFeedback(feedInfo, hasSession) 208 + const feedFeedback = useFeedFeedback(feedInfo ?? undefined, hasSession) 208 209 const {data, error, hasNextPage, isFetchingNextPage, fetchNextPage} = 209 210 usePostFeedQuery( 210 211 feedDesc, ··· 1044 1045 const {isPlaying} = useEvent(player, 'playingChange', { 1045 1046 isPlaying: player.playing, 1046 1047 }) 1048 + const isMounted = useRef(false) 1047 1049 1048 - const togglePlayPause = () => { 1049 - if (!player) return 1050 + useEffect(() => { 1051 + isMounted.current = true 1052 + return () => { 1053 + isMounted.current = false 1054 + } 1055 + }, []) 1056 + 1057 + const togglePlayPause = useNonReactiveCallback(() => { 1058 + // gets called after a timeout, so guard against being called after unmount -sfn 1059 + if (!player || !isMounted.current) return 1050 1060 doubleTapRef.current = null 1051 - if (player.playing) { 1052 - player.pause() 1053 - } else { 1054 - player.play() 1061 + try { 1062 + if (player.playing) { 1063 + player.pause() 1064 + } else { 1065 + player.play() 1066 + } 1067 + } catch (err) { 1068 + logger.error('Could not toggle play/pause', {safeMessage: err}) 1055 1069 } 1056 - } 1070 + }) 1057 1071 1058 1072 const onPress = () => { 1059 1073 if (doubleTapRef.current) {
+1 -1
src/state/queries/feed.ts
··· 637 637 queryKey: [feedInfoQueryKeyRoot, feedUri], 638 638 queryFn: async () => { 639 639 if (!feedUri) { 640 - return undefined 640 + return null 641 641 } 642 642 643 643 const res = await agent.app.bsky.feed.getFeedGenerator({
+5 -12
src/state/session/util.ts
··· 1 1 import {jwtDecode} from 'jwt-decode' 2 2 3 + import {isJwtExpired} from '#/lib/jwt' 3 4 import {hasProp} from '#/lib/type-guards' 4 - import {logger} from '#/logger' 5 5 import * as persisted from '#/state/persisted' 6 6 import {type SessionAccount} from './types' 7 7 ··· 22 22 } 23 23 24 24 export function isSessionExpired(account: SessionAccount) { 25 - try { 26 - if (account.accessJwt) { 27 - const decoded = jwtDecode(account.accessJwt) 28 - if (decoded.exp) { 29 - const didExpire = Date.now() >= decoded.exp * 1000 30 - return didExpire 31 - } 32 - } 33 - } catch (e) { 34 - logger.error(`session: could not decode jwt`) 25 + if (account.accessJwt) { 26 + return isJwtExpired(account.accessJwt) 27 + } else { 28 + return true 35 29 } 36 - return true 37 30 }
-85
src/view/com/profile/FollowButton.tsx
··· 1 - import {type StyleProp, type TextStyle, View} from 'react-native' 2 - import {msg} from '@lingui/macro' 3 - import {useLingui} from '@lingui/react' 4 - 5 - import {type Shadow} from '#/state/cache/types' 6 - import {useProfileFollowMutationQueue} from '#/state/queries/profile' 7 - import type * as bsky from '#/types/bsky' 8 - import {Button, type ButtonType} from '../util/forms/Button' 9 - import * as Toast from '../util/Toast' 10 - 11 - export function FollowButton({ 12 - unfollowedType = 'inverted', 13 - followedType = 'default', 14 - profile, 15 - labelStyle, 16 - logContext, 17 - onFollow, 18 - }: { 19 - unfollowedType?: ButtonType 20 - followedType?: ButtonType 21 - profile: Shadow<bsky.profile.AnyProfileView> 22 - labelStyle?: StyleProp<TextStyle> 23 - logContext: 'ProfileCard' | 'StarterPackProfilesList' 24 - onFollow?: () => void 25 - }) { 26 - const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( 27 - profile, 28 - logContext, 29 - ) 30 - const {_} = useLingui() 31 - 32 - const onPressFollow = async () => { 33 - try { 34 - await queueFollow() 35 - onFollow?.() 36 - } catch (e: any) { 37 - if (e?.name !== 'AbortError') { 38 - Toast.show(_(msg`An issue occurred, please try again.`), 'xmark') 39 - } 40 - } 41 - } 42 - 43 - const onPressUnfollow = async () => { 44 - try { 45 - await queueUnfollow() 46 - } catch (e: any) { 47 - if (e?.name !== 'AbortError') { 48 - Toast.show(_(msg`An issue occurred, please try again.`), 'xmark') 49 - } 50 - } 51 - } 52 - 53 - if (!profile.viewer) { 54 - return <View /> 55 - } 56 - 57 - if (profile.viewer.following) { 58 - return ( 59 - <Button 60 - type={followedType} 61 - labelStyle={labelStyle} 62 - onPress={onPressUnfollow} 63 - label={_(msg({message: 'Unfollow', context: 'action'}))} 64 - /> 65 - ) 66 - } else if (!profile.viewer.followedBy) { 67 - return ( 68 - <Button 69 - type={unfollowedType} 70 - labelStyle={labelStyle} 71 - onPress={onPressFollow} 72 - label={_(msg({message: 'Follow', context: 'action'}))} 73 - /> 74 - ) 75 - } else { 76 - return ( 77 - <Button 78 - type={unfollowedType} 79 - labelStyle={labelStyle} 80 - onPress={onPressFollow} 81 - label={_(msg({message: 'Follow back', context: 'action'}))} 82 - /> 83 - ) 84 - } 85 - }
-88
src/view/com/util/EmptyStateWithButton.tsx
··· 1 - import {StyleSheet, View} from 'react-native' 2 - import {type IconProp} from '@fortawesome/fontawesome-svg-core' 3 - import { 4 - FontAwesomeIcon, 5 - type FontAwesomeIconStyle, 6 - } from '@fortawesome/react-native-fontawesome' 7 - 8 - import {usePalette} from '#/lib/hooks/usePalette' 9 - import {s} from '#/lib/styles' 10 - import {Button} from './forms/Button' 11 - import {Text} from './text/Text' 12 - 13 - interface Props { 14 - testID?: string 15 - icon: IconProp 16 - message: string 17 - buttonLabel: string 18 - onPress: () => void 19 - } 20 - 21 - export function EmptyStateWithButton(props: Props) { 22 - const pal = usePalette('default') 23 - const palInverted = usePalette('inverted') 24 - 25 - return ( 26 - <View testID={props.testID} style={styles.container}> 27 - <View style={styles.iconContainer}> 28 - <FontAwesomeIcon 29 - icon={props.icon} 30 - style={[styles.icon, pal.text]} 31 - size={62} 32 - /> 33 - </View> 34 - <Text type="xl-medium" style={[s.textCenter, pal.text]}> 35 - {props.message} 36 - </Text> 37 - <View style={styles.btns}> 38 - <Button 39 - testID={props.testID ? `${props.testID}-button` : undefined} 40 - type="inverted" 41 - style={styles.btn} 42 - onPress={props.onPress}> 43 - <FontAwesomeIcon 44 - icon="plus" 45 - style={palInverted.text as FontAwesomeIconStyle} 46 - size={14} 47 - /> 48 - <Text type="lg-medium" style={palInverted.text}> 49 - {props.buttonLabel} 50 - </Text> 51 - </Button> 52 - </View> 53 - </View> 54 - ) 55 - } 56 - const styles = StyleSheet.create({ 57 - container: { 58 - height: '100%', 59 - paddingVertical: 40, 60 - paddingHorizontal: 30, 61 - }, 62 - iconContainer: { 63 - marginBottom: 16, 64 - }, 65 - icon: { 66 - marginLeft: 'auto', 67 - marginRight: 'auto', 68 - }, 69 - btns: { 70 - flexDirection: 'row', 71 - justifyContent: 'center', 72 - }, 73 - btn: { 74 - gap: 10, 75 - marginVertical: 20, 76 - flexDirection: 'row', 77 - alignItems: 'center', 78 - paddingVertical: 14, 79 - paddingHorizontal: 24, 80 - borderRadius: 30, 81 - }, 82 - notice: { 83 - borderRadius: 12, 84 - paddingHorizontal: 12, 85 - paddingVertical: 10, 86 - marginHorizontal: 30, 87 - }, 88 - })
-1
src/view/screens/Lists.tsx
··· 71 71 label={_(msg`New list`)} 72 72 testID="newUserListBtn" 73 73 color="secondary" 74 - variant="solid" 75 74 size="small" 76 75 onPress={wrappedOnPressNewList}> 77 76 <ButtonIcon icon={PlusIcon} />