Merge branch 'scroll-refactor' into web-layout
This commit is contained in:
+2
-2
@@ -9,12 +9,12 @@ module.exports = function () {
|
||||
/**
|
||||
* iOS build number. Must be incremented for each TestFlight version.
|
||||
*/
|
||||
const IOS_BUILD_NUMBER = '1'
|
||||
const IOS_BUILD_NUMBER = '2'
|
||||
|
||||
/**
|
||||
* Android build number. Must be incremented for each release.
|
||||
*/
|
||||
const ANDROID_VERSION_CODE = 48
|
||||
const ANDROID_VERSION_CODE = 49
|
||||
|
||||
/**
|
||||
* Uses built-in Expo env vars
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bsky.app",
|
||||
"version": "1.58.0",
|
||||
"version": "1.59.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prepare": "is-ci || husky install",
|
||||
@@ -35,7 +35,7 @@
|
||||
"intl:compile": "lingui compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "^0.7.2",
|
||||
"@atproto/api": "^0.7.3",
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, {createContext, useContext, useMemo} from 'react'
|
||||
import {ScrollHandlers} from 'react-native-reanimated'
|
||||
|
||||
const ScrollContext = createContext<ScrollHandlers<any>>({
|
||||
onBeginDrag: undefined,
|
||||
onEndDrag: undefined,
|
||||
onScroll: undefined,
|
||||
})
|
||||
|
||||
export function useScrollHandlers(): ScrollHandlers<any> {
|
||||
return useContext(ScrollContext)
|
||||
}
|
||||
|
||||
type ProviderProps = {children: React.ReactNode} & ScrollHandlers<any>
|
||||
|
||||
// Note: this completely *overrides* the parent handlers.
|
||||
// It's up to you to compose them with the parent ones via useScrollHandlers() if needed.
|
||||
export function ScrollProvider({
|
||||
children,
|
||||
onBeginDrag,
|
||||
onEndDrag,
|
||||
onScroll,
|
||||
}: ProviderProps) {
|
||||
const handlers = useMemo(
|
||||
() => ({
|
||||
onBeginDrag,
|
||||
onEndDrag,
|
||||
onScroll,
|
||||
}),
|
||||
[onBeginDrag, onEndDrag, onScroll],
|
||||
)
|
||||
return (
|
||||
<ScrollContext.Provider value={handlers}>{children}</ScrollContext.Provider>
|
||||
)
|
||||
}
|
||||
+38
-34
@@ -1,3 +1,4 @@
|
||||
import {useMemo} from 'react'
|
||||
import {TextStyle, ViewStyle} from 'react-native'
|
||||
import {useTheme, PaletteColorName, PaletteColor} from '../ThemeContext'
|
||||
|
||||
@@ -15,38 +16,41 @@ export interface UsePaletteValue {
|
||||
icon: TextStyle
|
||||
}
|
||||
export function usePalette(color: PaletteColorName): UsePaletteValue {
|
||||
const palette = useTheme().palette[color]
|
||||
return {
|
||||
colors: palette,
|
||||
view: {
|
||||
backgroundColor: palette.background,
|
||||
},
|
||||
viewLight: {
|
||||
backgroundColor: palette.backgroundLight,
|
||||
},
|
||||
btn: {
|
||||
backgroundColor: palette.backgroundLight,
|
||||
},
|
||||
border: {
|
||||
borderColor: palette.border,
|
||||
},
|
||||
borderDark: {
|
||||
borderColor: palette.borderDark,
|
||||
},
|
||||
text: {
|
||||
color: palette.text,
|
||||
},
|
||||
textLight: {
|
||||
color: palette.textLight,
|
||||
},
|
||||
textInverted: {
|
||||
color: palette.textInverted,
|
||||
},
|
||||
link: {
|
||||
color: palette.link,
|
||||
},
|
||||
icon: {
|
||||
color: palette.icon,
|
||||
},
|
||||
}
|
||||
const theme = useTheme()
|
||||
return useMemo(() => {
|
||||
const palette = theme.palette[color]
|
||||
return {
|
||||
colors: palette,
|
||||
view: {
|
||||
backgroundColor: palette.background,
|
||||
},
|
||||
viewLight: {
|
||||
backgroundColor: palette.backgroundLight,
|
||||
},
|
||||
btn: {
|
||||
backgroundColor: palette.backgroundLight,
|
||||
},
|
||||
border: {
|
||||
borderColor: palette.border,
|
||||
},
|
||||
borderDark: {
|
||||
borderColor: palette.borderDark,
|
||||
},
|
||||
text: {
|
||||
color: palette.text,
|
||||
},
|
||||
textLight: {
|
||||
color: palette.textLight,
|
||||
},
|
||||
textInverted: {
|
||||
color: palette.textInverted,
|
||||
},
|
||||
link: {
|
||||
color: palette.link,
|
||||
},
|
||||
icon: {
|
||||
color: palette.icon,
|
||||
},
|
||||
}
|
||||
}, [theme, color])
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ msgstr ""
|
||||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/desktop/RightNav.tsx:160
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
@@ -33,7 +33,7 @@ msgstr ""
|
||||
msgid "{0} {purposeLabel} List"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/desktop/RightNav.tsx:143
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
@@ -175,7 +175,7 @@ msgstr ""
|
||||
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:110
|
||||
#: src/view/com/modals/VerifyEmail.tsx:118
|
||||
msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below."
|
||||
msgstr ""
|
||||
|
||||
@@ -232,7 +232,7 @@ msgstr ""
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:185
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:188
|
||||
msgid "Are you sure? This cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
@@ -287,7 +287,7 @@ msgstr ""
|
||||
msgid "Block these accounts?"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:121
|
||||
#: src/view/screens/Moderation.tsx:123
|
||||
msgid "Blocked accounts"
|
||||
msgstr ""
|
||||
|
||||
@@ -331,7 +331,7 @@ msgstr ""
|
||||
msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:222
|
||||
#: src/view/screens/Moderation.tsx:225
|
||||
msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private."
|
||||
msgstr ""
|
||||
|
||||
@@ -344,7 +344,7 @@ msgid "Build version {0} {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/composer/photos/OpenCameraBtn.tsx:60
|
||||
#: src/view/com/util/UserAvatar.tsx:217
|
||||
#: src/view/com/util/UserAvatar.tsx:221
|
||||
#: src/view/com/util/UserBanner.tsx:38
|
||||
msgid "Camera"
|
||||
msgstr ""
|
||||
@@ -420,7 +420,7 @@ msgstr ""
|
||||
msgid "Change Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:133
|
||||
#: src/view/com/modals/VerifyEmail.tsx:141
|
||||
msgid "Change my email"
|
||||
msgstr ""
|
||||
|
||||
@@ -508,7 +508,7 @@ msgstr ""
|
||||
#: src/view/com/modals/AppealLabel.tsx:98
|
||||
#: src/view/com/modals/Confirm.tsx:75
|
||||
#: src/view/com/modals/SelfLabel.tsx:154
|
||||
#: src/view/com/modals/VerifyEmail.tsx:217
|
||||
#: src/view/com/modals/VerifyEmail.tsx:225
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:299
|
||||
#: src/view/screens/PreferencesThreads.tsx:153
|
||||
msgid "Confirm"
|
||||
@@ -529,7 +529,7 @@ msgstr ""
|
||||
|
||||
#: src/view/com/modals/ChangeEmail.tsx:157
|
||||
#: src/view/com/modals/DeleteAccount.tsx:176
|
||||
#: src/view/com/modals/VerifyEmail.tsx:151
|
||||
#: src/view/com/modals/VerifyEmail.tsx:159
|
||||
msgid "Confirmation code"
|
||||
msgstr ""
|
||||
|
||||
@@ -538,7 +538,7 @@ msgstr ""
|
||||
msgid "Connecting..."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:79
|
||||
#: src/view/screens/Moderation.tsx:81
|
||||
msgid "Content filtering"
|
||||
msgstr ""
|
||||
|
||||
@@ -577,7 +577,7 @@ msgstr ""
|
||||
msgid "Copy link to list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:126
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:129
|
||||
msgid "Copy link to post"
|
||||
msgstr ""
|
||||
|
||||
@@ -585,7 +585,7 @@ msgstr ""
|
||||
msgid "Copy link to profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:112
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:115
|
||||
msgid "Copy post text"
|
||||
msgstr ""
|
||||
|
||||
@@ -656,11 +656,11 @@ msgstr ""
|
||||
msgid "Delete my account…"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:180
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:183
|
||||
msgid "Delete post"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:184
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:187
|
||||
msgid "Delete this post?"
|
||||
msgstr ""
|
||||
|
||||
@@ -691,7 +691,7 @@ msgstr ""
|
||||
msgid "Discard draft"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:204
|
||||
#: src/view/screens/Moderation.tsx:207
|
||||
msgid "Discourage apps from showing my account to logged-out users"
|
||||
msgstr ""
|
||||
|
||||
@@ -840,7 +840,7 @@ msgstr ""
|
||||
msgid "Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:292
|
||||
msgid "Feedback"
|
||||
msgstr ""
|
||||
@@ -936,7 +936,7 @@ msgstr ""
|
||||
msgid "Gallery"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:175
|
||||
#: src/view/com/modals/VerifyEmail.tsx:183
|
||||
msgid "Get Started"
|
||||
msgstr ""
|
||||
|
||||
@@ -964,7 +964,7 @@ msgstr ""
|
||||
msgid "Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:302
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
@@ -1031,7 +1031,7 @@ msgstr ""
|
||||
msgid "Hosting provider address"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:200
|
||||
#: src/view/com/modals/VerifyEmail.tsx:208
|
||||
msgid "I have a code"
|
||||
msgstr ""
|
||||
|
||||
@@ -1047,7 +1047,7 @@ msgstr ""
|
||||
msgid "Image alt text"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/UserAvatar.tsx:304
|
||||
#: src/view/com/util/UserAvatar.tsx:308
|
||||
#: src/view/com/util/UserBanner.tsx:116
|
||||
msgid "Image options"
|
||||
msgstr ""
|
||||
@@ -1121,7 +1121,7 @@ msgstr ""
|
||||
msgid "Learn more about this warning"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:239
|
||||
#: src/view/screens/Moderation.tsx:242
|
||||
msgid "Learn more about what is public on Bluesky."
|
||||
msgstr ""
|
||||
|
||||
@@ -1138,7 +1138,7 @@ msgstr ""
|
||||
msgid "Let's get your password reset!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/UserAvatar.tsx:241
|
||||
#: src/view/com/util/UserAvatar.tsx:245
|
||||
#: src/view/com/util/UserBanner.tsx:60
|
||||
msgid "Library"
|
||||
msgstr ""
|
||||
@@ -1208,7 +1208,7 @@ msgstr ""
|
||||
#~ msgid "Logged-out users"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:134
|
||||
#: src/view/screens/Moderation.tsx:136
|
||||
msgid "Logged-out visibility"
|
||||
msgstr ""
|
||||
|
||||
@@ -1245,7 +1245,7 @@ msgstr ""
|
||||
msgid "Message from server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:63
|
||||
#: src/view/screens/Moderation.tsx:64
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/shell/desktop/LeftNav.tsx:391
|
||||
#: src/view/shell/Drawer.tsx:490
|
||||
@@ -1253,7 +1253,7 @@ msgstr ""
|
||||
msgid "Moderation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:93
|
||||
#: src/view/screens/Moderation.tsx:95
|
||||
msgid "Moderation lists"
|
||||
msgstr ""
|
||||
|
||||
@@ -1291,11 +1291,11 @@ msgstr ""
|
||||
msgid "Mute these accounts?"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:144
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||
msgid "Mute thread"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:107
|
||||
#: src/view/screens/Moderation.tsx:109
|
||||
msgid "Muted accounts"
|
||||
msgstr ""
|
||||
|
||||
@@ -1421,7 +1421,7 @@ msgstr ""
|
||||
#~ msgid "Note: Bluesky is an open and public network, and enabling this will not make your profile private or limit the ability of logged in users to see your posts. This setting only limits the visibility of posts on the Bluesky app and website; third-party apps that display Bluesky content may not respect this setting, and could show your content to logged-out users."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:229
|
||||
#: src/view/screens/Moderation.tsx:232
|
||||
msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites."
|
||||
msgstr ""
|
||||
|
||||
@@ -1462,7 +1462,7 @@ msgstr ""
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/desktop/RightNav.tsx:148
|
||||
#: src/view/shell/Drawer.tsx:622
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
@@ -1611,7 +1611,7 @@ msgstr ""
|
||||
msgid "Prioritize Your Follows"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
|
||||
@@ -1671,7 +1671,7 @@ msgstr ""
|
||||
#: src/view/com/modals/ListAddRemoveUsers.tsx:264
|
||||
#: src/view/com/modals/SelfLabel.tsx:83
|
||||
#: src/view/com/modals/UserAddRemoveLists.tsx:193
|
||||
#: src/view/com/util/UserAvatar.tsx:278
|
||||
#: src/view/com/util/UserAvatar.tsx:282
|
||||
#: src/view/com/util/UserBanner.tsx:89
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
@@ -1744,7 +1744,7 @@ msgid "Report List"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/report/SendReportButton.tsx:37
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:162
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:165
|
||||
msgid "Report post"
|
||||
msgstr ""
|
||||
|
||||
@@ -1898,7 +1898,7 @@ msgstr ""
|
||||
msgid "Select your preferred language for translations in your feed."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:188
|
||||
#: src/view/com/modals/VerifyEmail.tsx:196
|
||||
msgid "Send Confirmation Email"
|
||||
msgstr ""
|
||||
|
||||
@@ -1955,7 +1955,7 @@ msgid "Sexual activity or erotic nudity."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:338
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:126
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:129
|
||||
#: src/view/screens/ProfileList.tsx:407
|
||||
msgid "Share"
|
||||
msgstr ""
|
||||
@@ -2075,7 +2075,7 @@ msgstr ""
|
||||
|
||||
#: src/view/com/modals/AppealLabel.tsx:101
|
||||
msgid "Submit"
|
||||
msgstr ">>>>>>> cb8a33b6 (Fix translations)"
|
||||
msgstr "Submit"
|
||||
|
||||
#: src/view/screens/ProfileList.tsx:597
|
||||
msgid "Subscribe"
|
||||
@@ -2110,7 +2110,7 @@ msgstr ""
|
||||
msgid "Tall"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr ""
|
||||
|
||||
@@ -2175,7 +2175,7 @@ msgstr ""
|
||||
msgid "This information is not shared with other users."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:105
|
||||
#: src/view/com/modals/VerifyEmail.tsx:113
|
||||
msgid "This is important in case you ever need to change your email or reset your password."
|
||||
msgstr ""
|
||||
|
||||
@@ -2212,9 +2212,9 @@ msgstr ""
|
||||
msgid "Transformations"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:704
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:706
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:98
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:708
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:101
|
||||
msgid "Translate"
|
||||
msgstr ""
|
||||
|
||||
@@ -2258,7 +2258,7 @@ msgstr ""
|
||||
msgid "Unmute Account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:144
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||
msgid "Unmute thread"
|
||||
msgstr ""
|
||||
|
||||
@@ -2493,7 +2493,7 @@ msgstr ""
|
||||
msgid "Your email has been updated but not verified. As a next step, please verify your new email."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:100
|
||||
#: src/view/com/modals/VerifyEmail.tsx:108
|
||||
msgid "Your email has not yet been verified. This is an important security step which we recommend."
|
||||
msgstr ""
|
||||
|
||||
@@ -2507,7 +2507,7 @@ msgid "Your hosting provider"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:402
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/desktop/RightNav.tsx:129
|
||||
#: src/view/shell/Drawer.tsx:636
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ msgstr ""
|
||||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr "यह चेतावनी केवल मीडिया वाले पोस्ट के लिए उपलब्ध है।"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/desktop/RightNav.tsx:160
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
@@ -33,7 +33,7 @@ msgstr "{0}"
|
||||
msgid "{0} {purposeLabel} List"
|
||||
msgstr "{0} {purposeLabel} सूची"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/desktop/RightNav.tsx:143
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
@@ -175,7 +175,7 @@ msgstr "वैकल्पिक पाठ"
|
||||
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
|
||||
msgstr "ऑल्ट टेक्स्ट अंधा और कम दृश्य लोगों के लिए छवियों का वर्णन करता है, और हर किसी को संदर्भ देने में मदद करता है।।"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:110
|
||||
#: src/view/com/modals/VerifyEmail.tsx:118
|
||||
msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below."
|
||||
msgstr "{0} को ईमेल भेजा गया है। इसमें एक OTP कोड शामिल है जिसे आप नीचे दर्ज कर सकते हैं।।"
|
||||
|
||||
@@ -232,7 +232,7 @@ msgstr "क्या आप वाकई इस ड्राफ्ट को ह
|
||||
msgid "Are you sure?"
|
||||
msgstr "क्या आप वास्तव में इसे करना चाहते हैं?"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:185
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:188
|
||||
msgid "Are you sure? This cannot be undone."
|
||||
msgstr "क्या आप वास्तव में इसे करना चाहते हैं? इसे असंपादित नहीं किया जा सकता है।"
|
||||
|
||||
@@ -287,7 +287,7 @@ msgstr ""
|
||||
msgid "Block these accounts?"
|
||||
msgstr "खाता ब्लॉक करें?"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:121
|
||||
#: src/view/screens/Moderation.tsx:123
|
||||
msgid "Blocked accounts"
|
||||
msgstr "ब्लॉक किए गए खाते"
|
||||
|
||||
@@ -331,7 +331,7 @@ msgstr "Bluesky सार्वजनिक है।।"
|
||||
msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon."
|
||||
msgstr "ब्लूस्की एक स्वस्थ समुदाय बनाने के लिए आमंत्रित करता है। यदि आप किसी को आमंत्रित नहीं करते हैं, तो आप प्रतीक्षा सूची के लिए साइन अप कर सकते हैं और हम जल्द ही एक भेज देंगे।।"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:222
|
||||
#: src/view/screens/Moderation.tsx:225
|
||||
msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private."
|
||||
msgstr ""
|
||||
|
||||
@@ -344,7 +344,7 @@ msgid "Build version {0} {1}"
|
||||
msgstr "Build version {0} {1}"
|
||||
|
||||
#: src/view/com/composer/photos/OpenCameraBtn.tsx:60
|
||||
#: src/view/com/util/UserAvatar.tsx:217
|
||||
#: src/view/com/util/UserAvatar.tsx:221
|
||||
#: src/view/com/util/UserBanner.tsx:38
|
||||
msgid "Camera"
|
||||
msgstr "कैमरा"
|
||||
@@ -420,7 +420,7 @@ msgstr "हैंडल बदलें"
|
||||
msgid "Change Handle"
|
||||
msgstr "हैंडल बदलें"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:133
|
||||
#: src/view/com/modals/VerifyEmail.tsx:141
|
||||
msgid "Change my email"
|
||||
msgstr "मेरा ईमेल बदलें"
|
||||
|
||||
@@ -504,7 +504,7 @@ msgstr "जवाब लिखो"
|
||||
#: src/view/com/modals/AppealLabel.tsx:98
|
||||
#: src/view/com/modals/Confirm.tsx:75
|
||||
#: src/view/com/modals/SelfLabel.tsx:154
|
||||
#: src/view/com/modals/VerifyEmail.tsx:217
|
||||
#: src/view/com/modals/VerifyEmail.tsx:225
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:299
|
||||
#: src/view/screens/PreferencesThreads.tsx:153
|
||||
msgid "Confirm"
|
||||
@@ -525,7 +525,7 @@ msgstr "खाते को हटा दें"
|
||||
|
||||
#: src/view/com/modals/ChangeEmail.tsx:157
|
||||
#: src/view/com/modals/DeleteAccount.tsx:176
|
||||
#: src/view/com/modals/VerifyEmail.tsx:151
|
||||
#: src/view/com/modals/VerifyEmail.tsx:159
|
||||
msgid "Confirmation code"
|
||||
msgstr "OTP कोड"
|
||||
|
||||
@@ -534,7 +534,7 @@ msgstr "OTP कोड"
|
||||
msgid "Connecting..."
|
||||
msgstr "कनेक्टिंग ..।"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:79
|
||||
#: src/view/screens/Moderation.tsx:81
|
||||
msgid "Content filtering"
|
||||
msgstr "सामग्री फ़िल्टरिंग"
|
||||
|
||||
@@ -573,7 +573,7 @@ msgstr "कॉपी"
|
||||
msgid "Copy link to list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:126
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:129
|
||||
msgid "Copy link to post"
|
||||
msgstr ""
|
||||
|
||||
@@ -581,7 +581,7 @@ msgstr ""
|
||||
msgid "Copy link to profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:112
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:115
|
||||
msgid "Copy post text"
|
||||
msgstr "पोस्ट टेक्स्ट कॉपी करें"
|
||||
|
||||
@@ -652,11 +652,11 @@ msgstr "मेरा खाता हटाएं"
|
||||
msgid "Delete my account…"
|
||||
msgstr "मेरा खाता हटाएं…"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:180
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:183
|
||||
msgid "Delete post"
|
||||
msgstr "पोस्ट को हटाएं"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:184
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:187
|
||||
msgid "Delete this post?"
|
||||
msgstr "इस पोस्ट को डीलीट करें?"
|
||||
|
||||
@@ -687,7 +687,7 @@ msgstr ""
|
||||
msgid "Discard draft"
|
||||
msgstr "ड्राफ्ट हटाएं"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:204
|
||||
#: src/view/screens/Moderation.tsx:207
|
||||
msgid "Discourage apps from showing my account to logged-out users"
|
||||
msgstr ""
|
||||
|
||||
@@ -836,7 +836,7 @@ msgstr "फ़ीड ऑफ़लाइन है"
|
||||
msgid "Feed Preferences"
|
||||
msgstr "फ़ीड प्राथमिकता"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:292
|
||||
msgid "Feedback"
|
||||
msgstr "प्रतिक्रिया"
|
||||
@@ -928,7 +928,7 @@ msgstr "पासवर्ड भूल गए"
|
||||
msgid "Gallery"
|
||||
msgstr "गैलरी"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:175
|
||||
#: src/view/com/modals/VerifyEmail.tsx:183
|
||||
msgid "Get Started"
|
||||
msgstr "प्रारंभ करें"
|
||||
|
||||
@@ -956,7 +956,7 @@ msgstr "अगला"
|
||||
msgid "Handle"
|
||||
msgstr "हैंडल"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:302
|
||||
msgid "Help"
|
||||
msgstr "सहायता"
|
||||
@@ -1023,7 +1023,7 @@ msgstr "होस्टिंग प्रदाता"
|
||||
msgid "Hosting provider address"
|
||||
msgstr "होस्टिंग प्रदाता पता"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:200
|
||||
#: src/view/com/modals/VerifyEmail.tsx:208
|
||||
msgid "I have a code"
|
||||
msgstr "मेरे पास एक OTP कोड है"
|
||||
|
||||
@@ -1039,7 +1039,7 @@ msgstr "यदि किसी को चुना जाता है, तो
|
||||
msgid "Image alt text"
|
||||
msgstr "छवि alt पाठ"
|
||||
|
||||
#: src/view/com/util/UserAvatar.tsx:304
|
||||
#: src/view/com/util/UserAvatar.tsx:308
|
||||
#: src/view/com/util/UserBanner.tsx:116
|
||||
msgid "Image options"
|
||||
msgstr "छवि विकल्प"
|
||||
@@ -1113,7 +1113,7 @@ msgstr "अधिक जानें"
|
||||
msgid "Learn more about this warning"
|
||||
msgstr "इस चेतावनी के बारे में अधिक जानें"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:239
|
||||
#: src/view/screens/Moderation.tsx:242
|
||||
msgid "Learn more about what is public on Bluesky."
|
||||
msgstr ""
|
||||
|
||||
@@ -1130,7 +1130,7 @@ msgstr "लीविंग Bluesky"
|
||||
msgid "Let's get your password reset!"
|
||||
msgstr "चलो अपना पासवर्ड रीसेट करें!"
|
||||
|
||||
#: src/view/com/util/UserAvatar.tsx:241
|
||||
#: src/view/com/util/UserAvatar.tsx:245
|
||||
#: src/view/com/util/UserBanner.tsx:60
|
||||
msgid "Library"
|
||||
msgstr "चित्र पुस्तकालय"
|
||||
@@ -1200,7 +1200,7 @@ msgstr "स्थानीय देव सर्वर"
|
||||
#~ msgid "Logged-out users"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:134
|
||||
#: src/view/screens/Moderation.tsx:136
|
||||
msgid "Logged-out visibility"
|
||||
msgstr ""
|
||||
|
||||
@@ -1237,7 +1237,7 @@ msgstr "मेनू"
|
||||
msgid "Message from server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:63
|
||||
#: src/view/screens/Moderation.tsx:64
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/shell/desktop/LeftNav.tsx:391
|
||||
#: src/view/shell/Drawer.tsx:490
|
||||
@@ -1245,7 +1245,7 @@ msgstr ""
|
||||
msgid "Moderation"
|
||||
msgstr "मॉडरेशन"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:93
|
||||
#: src/view/screens/Moderation.tsx:95
|
||||
msgid "Moderation lists"
|
||||
msgstr "मॉडरेशन सूचियाँ"
|
||||
|
||||
@@ -1283,11 +1283,11 @@ msgstr ""
|
||||
msgid "Mute these accounts?"
|
||||
msgstr "इन खातों को म्यूट करें?"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:144
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||
msgid "Mute thread"
|
||||
msgstr "थ्रेड म्यूट करें"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:107
|
||||
#: src/view/screens/Moderation.tsx:109
|
||||
msgid "Muted accounts"
|
||||
msgstr "म्यूट किए गए खाते"
|
||||
|
||||
@@ -1413,7 +1413,7 @@ msgstr "लागू नहीं।"
|
||||
#~ msgid "Note: Bluesky is an open and public network, and enabling this will not make your profile private or limit the ability of logged in users to see your posts. This setting only limits the visibility of posts on the Bluesky app and website; third-party apps that display Bluesky content may not respect this setting, and could show your content to logged-out users."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:229
|
||||
#: src/view/screens/Moderation.tsx:232
|
||||
msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites."
|
||||
msgstr ""
|
||||
|
||||
@@ -1454,7 +1454,7 @@ msgstr "ओपन नेविगेशन"
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr "भाषा सेटिंग्स खोलें"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/desktop/RightNav.tsx:148
|
||||
#: src/view/shell/Drawer.tsx:622
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
@@ -1603,7 +1603,7 @@ msgstr "प्राथमिक भाषा"
|
||||
msgid "Prioritize Your Follows"
|
||||
msgstr "अपने फ़ॉलोअर्स को प्राथमिकता दें"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr "गोपनीयता"
|
||||
|
||||
@@ -1663,7 +1663,7 @@ msgstr "अनुशंसित लोग"
|
||||
#: src/view/com/modals/ListAddRemoveUsers.tsx:264
|
||||
#: src/view/com/modals/SelfLabel.tsx:83
|
||||
#: src/view/com/modals/UserAddRemoveLists.tsx:193
|
||||
#: src/view/com/util/UserAvatar.tsx:278
|
||||
#: src/view/com/util/UserAvatar.tsx:282
|
||||
#: src/view/com/util/UserBanner.tsx:89
|
||||
msgid "Remove"
|
||||
msgstr "निकालें"
|
||||
@@ -1736,7 +1736,7 @@ msgid "Report List"
|
||||
msgstr "रिपोर्ट सूची"
|
||||
|
||||
#: src/view/com/modals/report/SendReportButton.tsx:37
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:162
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:165
|
||||
msgid "Report post"
|
||||
msgstr "रिपोर्ट पोस्ट"
|
||||
|
||||
@@ -1890,7 +1890,7 @@ msgstr "ऐप में प्रदर्शित होने वाले
|
||||
msgid "Select your preferred language for translations in your feed."
|
||||
msgstr "अपने फ़ीड में अनुवाद के लिए अपनी पसंदीदा भाषा चुनें।"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:188
|
||||
#: src/view/com/modals/VerifyEmail.tsx:196
|
||||
msgid "Send Confirmation Email"
|
||||
msgstr "पुष्टिकरण ईमेल भेजें"
|
||||
|
||||
@@ -1947,7 +1947,7 @@ msgid "Sexual activity or erotic nudity."
|
||||
msgstr "यौन गतिविधि या कामुक नग्नता।।"
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:338
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:126
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:129
|
||||
#: src/view/screens/ProfileList.tsx:407
|
||||
msgid "Share"
|
||||
msgstr "शेयर"
|
||||
@@ -2102,7 +2102,7 @@ msgstr "सिस्टम लॉग"
|
||||
msgid "Tall"
|
||||
msgstr "लंबा"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr "शर्तें"
|
||||
|
||||
@@ -2167,7 +2167,7 @@ msgstr ""
|
||||
msgid "This information is not shared with other users."
|
||||
msgstr "यह जानकारी अन्य उपयोगकर्ताओं के साथ साझा नहीं की जाती है।।"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:105
|
||||
#: src/view/com/modals/VerifyEmail.tsx:113
|
||||
msgid "This is important in case you ever need to change your email or reset your password."
|
||||
msgstr "अगर आपको कभी अपना ईमेल बदलने या पासवर्ड रीसेट करने की आवश्यकता है तो यह महत्वपूर्ण है।।"
|
||||
|
||||
@@ -2204,9 +2204,9 @@ msgstr "ड्रॉपडाउन टॉगल करें"
|
||||
msgid "Transformations"
|
||||
msgstr "परिवर्तन"
|
||||
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:704
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:706
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:98
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:708
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:101
|
||||
msgid "Translate"
|
||||
msgstr "अनुवाद"
|
||||
|
||||
@@ -2250,7 +2250,7 @@ msgstr ""
|
||||
msgid "Unmute Account"
|
||||
msgstr "अनम्यूट खाता"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:144
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||
msgid "Unmute thread"
|
||||
msgstr "थ्रेड को अनम्यूट करें"
|
||||
|
||||
@@ -2485,7 +2485,7 @@ msgstr "आपका ईमेल बचाया गया है! हम ज
|
||||
msgid "Your email has been updated but not verified. As a next step, please verify your new email."
|
||||
msgstr "आपका ईमेल अद्यतन किया गया है लेकिन सत्यापित नहीं किया गया है। अगले चरण के रूप में, कृपया अपना नया ईमेल सत्यापित करें।।"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:100
|
||||
#: src/view/com/modals/VerifyEmail.tsx:108
|
||||
msgid "Your email has not yet been verified. This is an important security step which we recommend."
|
||||
msgstr "आपका ईमेल अभी तक सत्यापित नहीं हुआ है। यह एक महत्वपूर्ण सुरक्षा कदम है जिसे हम अनुशंसा करते हैं।।"
|
||||
|
||||
@@ -2499,7 +2499,7 @@ msgid "Your hosting provider"
|
||||
msgstr "आपका होस्टिंग प्रदाता"
|
||||
|
||||
#: src/view/screens/Settings.tsx:402
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/desktop/RightNav.tsx:129
|
||||
#: src/view/shell/Drawer.tsx:636
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
@@ -13,7 +13,7 @@ msgstr ""
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/desktop/RightNav.tsx:160
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
@@ -25,7 +25,7 @@ msgstr "{0}"
|
||||
msgid "{0} {purposeLabel} List"
|
||||
msgstr "{0} {purposeLabel} リスト"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/desktop/RightNav.tsx:143
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
@@ -155,7 +155,7 @@ msgstr "ALTテキスト"
|
||||
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
|
||||
msgstr "ALTテキストは、視覚障害者や低視力者のために画像を説明し、すべての人に文脈を与えるのに役立ちます。"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:110
|
||||
#: src/view/com/modals/VerifyEmail.tsx:118
|
||||
msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below."
|
||||
msgstr "Eメールが{0}に送信されました。以下に入力できる確認コードが含まれています。"
|
||||
|
||||
@@ -208,7 +208,7 @@ msgstr "本当にこの下書きを破棄しますか?"
|
||||
msgid "Are you sure?"
|
||||
msgstr "本当ですか?"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:185
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:188
|
||||
msgid "Are you sure? This cannot be undone."
|
||||
msgstr "本当ですか?これは元に戻せません。"
|
||||
|
||||
@@ -259,7 +259,7 @@ msgstr ""
|
||||
msgid "Block these accounts?"
|
||||
msgstr "これらのアカウントをブロックしますか?"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:121
|
||||
#: src/view/screens/Moderation.tsx:123
|
||||
msgid "Blocked accounts"
|
||||
msgstr "ブロックされたブロック"
|
||||
|
||||
@@ -303,7 +303,7 @@ msgstr "Blueskyはオープンです。"
|
||||
msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon."
|
||||
msgstr "Blueskyはより健全なコミュニティを構築するために招待状を使用します。招待状をお持ちでない方の場合、waitlistに申し込めば招待状をお送りします。"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:222
|
||||
#: src/view/screens/Moderation.tsx:225
|
||||
msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private."
|
||||
msgstr ""
|
||||
|
||||
@@ -316,7 +316,7 @@ msgid "Build version {0} {1}"
|
||||
msgstr "ビルドバージョン {0} {1}"
|
||||
|
||||
#: src/view/com/composer/photos/OpenCameraBtn.tsx:60
|
||||
#: src/view/com/util/UserAvatar.tsx:217
|
||||
#: src/view/com/util/UserAvatar.tsx:221
|
||||
#: src/view/com/util/UserBanner.tsx:38
|
||||
msgid "Camera"
|
||||
msgstr "カメラ"
|
||||
@@ -392,7 +392,7 @@ msgstr "ハンドルを変更"
|
||||
msgid "Change Handle"
|
||||
msgstr "ハンドルを変更"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:133
|
||||
#: src/view/com/modals/VerifyEmail.tsx:141
|
||||
msgid "Change my email"
|
||||
msgstr "Eメールの変更"
|
||||
|
||||
@@ -476,7 +476,7 @@ msgstr "返信を作成"
|
||||
#: src/view/com/modals/AppealLabel.tsx:98
|
||||
#: src/view/com/modals/Confirm.tsx:75
|
||||
#: src/view/com/modals/SelfLabel.tsx:154
|
||||
#: src/view/com/modals/VerifyEmail.tsx:217
|
||||
#: src/view/com/modals/VerifyEmail.tsx:225
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:299
|
||||
#: src/view/screens/PreferencesThreads.tsx:153
|
||||
msgid "Confirm"
|
||||
@@ -497,7 +497,7 @@ msgstr "アカウントの削除を確認"
|
||||
|
||||
#: src/view/com/modals/ChangeEmail.tsx:157
|
||||
#: src/view/com/modals/DeleteAccount.tsx:176
|
||||
#: src/view/com/modals/VerifyEmail.tsx:151
|
||||
#: src/view/com/modals/VerifyEmail.tsx:159
|
||||
msgid "Confirmation code"
|
||||
msgstr "確認コード"
|
||||
|
||||
@@ -506,7 +506,7 @@ msgstr "確認コード"
|
||||
msgid "Connecting..."
|
||||
msgstr "接続中..."
|
||||
|
||||
#: src/view/screens/Moderation.tsx:79
|
||||
#: src/view/screens/Moderation.tsx:81
|
||||
msgid "Content filtering"
|
||||
msgstr "コンテンツフィルタリング"
|
||||
|
||||
@@ -545,7 +545,7 @@ msgstr "コピー"
|
||||
msgid "Copy link to list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:126
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:129
|
||||
msgid "Copy link to post"
|
||||
msgstr ""
|
||||
|
||||
@@ -553,7 +553,7 @@ msgstr ""
|
||||
msgid "Copy link to profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:112
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:115
|
||||
msgid "Copy post text"
|
||||
msgstr "投稿テキストをコピー"
|
||||
|
||||
@@ -620,11 +620,11 @@ msgstr "マイアカウントを削除"
|
||||
msgid "Delete my account…"
|
||||
msgstr "マイアカウントを削除…"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:180
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:183
|
||||
msgid "Delete post"
|
||||
msgstr "投稿を削除"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:184
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:187
|
||||
msgid "Delete this post?"
|
||||
msgstr "この投稿を削除しますか?"
|
||||
|
||||
@@ -655,7 +655,7 @@ msgstr "破棄"
|
||||
msgid "Discard draft"
|
||||
msgstr "ドラフトを破棄"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:204
|
||||
#: src/view/screens/Moderation.tsx:207
|
||||
msgid "Discourage apps from showing my account to logged-out users"
|
||||
msgstr ""
|
||||
|
||||
@@ -804,7 +804,7 @@ msgstr "フィードはオフライン"
|
||||
msgid "Feed Preferences"
|
||||
msgstr "フィード設定"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:292
|
||||
msgid "Feedback"
|
||||
msgstr "フィードバック"
|
||||
@@ -896,7 +896,7 @@ msgstr "パスワードを失念"
|
||||
msgid "Gallery"
|
||||
msgstr "ギャラリー"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:175
|
||||
#: src/view/com/modals/VerifyEmail.tsx:183
|
||||
msgid "Get Started"
|
||||
msgstr "はじめに"
|
||||
|
||||
@@ -924,7 +924,7 @@ msgstr "次へ"
|
||||
msgid "Handle"
|
||||
msgstr "ハンドル"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:302
|
||||
msgid "Help"
|
||||
msgstr "ヘルプ"
|
||||
@@ -983,7 +983,7 @@ msgstr "ホスティングプロバイダー"
|
||||
msgid "Hosting provider address"
|
||||
msgstr "ホスティングプロバイダーアドレス"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:200
|
||||
#: src/view/com/modals/VerifyEmail.tsx:208
|
||||
msgid "I have a code"
|
||||
msgstr "コードを持っている"
|
||||
|
||||
@@ -999,7 +999,7 @@ msgstr "選択されていない場合は、すべての年齢に適していま
|
||||
msgid "Image alt text"
|
||||
msgstr "画像のALTテキスト"
|
||||
|
||||
#: src/view/com/util/UserAvatar.tsx:304
|
||||
#: src/view/com/util/UserAvatar.tsx:308
|
||||
#: src/view/com/util/UserBanner.tsx:116
|
||||
msgid "Image options"
|
||||
msgstr "イメージオプション"
|
||||
@@ -1068,7 +1068,7 @@ msgstr "詳細"
|
||||
msgid "Learn more about this warning"
|
||||
msgstr "この警告の詳細"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:239
|
||||
#: src/view/screens/Moderation.tsx:242
|
||||
msgid "Learn more about what is public on Bluesky."
|
||||
msgstr ""
|
||||
|
||||
@@ -1085,7 +1085,7 @@ msgstr "Blueskyから離れる"
|
||||
msgid "Let's get your password reset!"
|
||||
msgstr "パスワードをリセットしましょう!"
|
||||
|
||||
#: src/view/com/util/UserAvatar.tsx:241
|
||||
#: src/view/com/util/UserAvatar.tsx:245
|
||||
#: src/view/com/util/UserBanner.tsx:60
|
||||
msgid "Library"
|
||||
msgstr "ライブラリー"
|
||||
@@ -1147,7 +1147,7 @@ msgstr "ローカル開発者サーバー"
|
||||
#~ msgid "Logged-out users"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:134
|
||||
#: src/view/screens/Moderation.tsx:136
|
||||
msgid "Logged-out visibility"
|
||||
msgstr ""
|
||||
|
||||
@@ -1184,7 +1184,7 @@ msgstr "メニュー"
|
||||
msgid "Message from server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:63
|
||||
#: src/view/screens/Moderation.tsx:64
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/shell/desktop/LeftNav.tsx:391
|
||||
#: src/view/shell/Drawer.tsx:490
|
||||
@@ -1192,7 +1192,7 @@ msgstr ""
|
||||
msgid "Moderation"
|
||||
msgstr "モデレート"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:93
|
||||
#: src/view/screens/Moderation.tsx:95
|
||||
msgid "Moderation lists"
|
||||
msgstr "モデレートリスト"
|
||||
|
||||
@@ -1226,11 +1226,11 @@ msgstr ""
|
||||
msgid "Mute these accounts?"
|
||||
msgstr "これらのアカウントをミュートしますか?"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:144
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||
msgid "Mute thread"
|
||||
msgstr "スレッドをミュート"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:107
|
||||
#: src/view/screens/Moderation.tsx:109
|
||||
msgid "Muted accounts"
|
||||
msgstr "ミュート済みアカウント"
|
||||
|
||||
@@ -1343,7 +1343,7 @@ msgstr "該当なし。"
|
||||
#~ msgid "Note: Bluesky is an open and public network, and enabling this will not make your profile private or limit the ability of logged in users to see your posts. This setting only limits the visibility of posts on the Bluesky app and website; third-party apps that display Bluesky content may not respect this setting, and could show your content to logged-out users."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:229
|
||||
#: src/view/screens/Moderation.tsx:232
|
||||
msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites."
|
||||
msgstr ""
|
||||
|
||||
@@ -1380,7 +1380,7 @@ msgstr "ナビゲーションを開く"
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr "構成可能な言語設定を開く"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/desktop/RightNav.tsx:148
|
||||
#: src/view/shell/Drawer.tsx:622
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
@@ -1529,7 +1529,7 @@ msgstr "第一言語"
|
||||
msgid "Prioritize Your Follows"
|
||||
msgstr "フォローの優先順位付け"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr "プライバシー"
|
||||
|
||||
@@ -1584,7 +1584,7 @@ msgstr "推奨ユーザー"
|
||||
#: src/view/com/modals/ListAddRemoveUsers.tsx:264
|
||||
#: src/view/com/modals/SelfLabel.tsx:83
|
||||
#: src/view/com/modals/UserAddRemoveLists.tsx:193
|
||||
#: src/view/com/util/UserAvatar.tsx:278
|
||||
#: src/view/com/util/UserAvatar.tsx:282
|
||||
#: src/view/com/util/UserBanner.tsx:89
|
||||
msgid "Remove"
|
||||
msgstr "削除"
|
||||
@@ -1657,7 +1657,7 @@ msgid "Report List"
|
||||
msgstr "レポートリスト"
|
||||
|
||||
#: src/view/com/modals/report/SendReportButton.tsx:37
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:162
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:165
|
||||
msgid "Report post"
|
||||
msgstr "レポート投稿"
|
||||
|
||||
@@ -1795,7 +1795,7 @@ msgstr "アプリに表示するデフォルトのテキストのアプリ言語
|
||||
msgid "Select your preferred language for translations in your feed."
|
||||
msgstr "フィード内の翻訳に使用する言語を選択します。"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:188
|
||||
#: src/view/com/modals/VerifyEmail.tsx:196
|
||||
msgid "Send Confirmation Email"
|
||||
msgstr "確認Eメールを送信"
|
||||
|
||||
@@ -1852,7 +1852,7 @@ msgid "Sexual activity or erotic nudity."
|
||||
msgstr "性行為またはエロティックなヌード。"
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:338
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:126
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:129
|
||||
#: src/view/screens/ProfileList.tsx:407
|
||||
msgid "Share"
|
||||
msgstr "共有"
|
||||
@@ -1999,7 +1999,7 @@ msgstr "システムログ"
|
||||
msgid "Tall"
|
||||
msgstr "トール"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr "条件"
|
||||
|
||||
@@ -2064,7 +2064,7 @@ msgstr ""
|
||||
msgid "This information is not shared with other users."
|
||||
msgstr "この情報は他のユーザーと共有されません。"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:105
|
||||
#: src/view/com/modals/VerifyEmail.tsx:113
|
||||
msgid "This is important in case you ever need to change your email or reset your password."
|
||||
msgstr "これは、Eメールの変更やパスワードのリセットが必要な場合に重要です。"
|
||||
|
||||
@@ -2101,9 +2101,9 @@ msgstr "トグルドロップダウン"
|
||||
msgid "Transformations"
|
||||
msgstr "変換"
|
||||
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:704
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:706
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:98
|
||||
#: src/view/com/post-thread/PostThreadItem.tsx:708
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:101
|
||||
msgid "Translate"
|
||||
msgstr "翻訳"
|
||||
|
||||
@@ -2147,7 +2147,7 @@ msgstr "残念ながら、アカウントを作成するための要件を満た
|
||||
msgid "Unmute Account"
|
||||
msgstr "アカウントのミュート解除"
|
||||
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:144
|
||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||
msgid "Unmute thread"
|
||||
msgstr "スレッドのミュート解除"
|
||||
|
||||
@@ -2366,7 +2366,7 @@ msgstr "Eメールが保存されました!すぐにご連絡いたします
|
||||
msgid "Your email has been updated but not verified. As a next step, please verify your new email."
|
||||
msgstr "Eメールは更新されましたが、確認されていません。次のステップとして、新しいEメールを確認してください。"
|
||||
|
||||
#: src/view/com/modals/VerifyEmail.tsx:100
|
||||
#: src/view/com/modals/VerifyEmail.tsx:108
|
||||
msgid "Your email has not yet been verified. This is an important security step which we recommend."
|
||||
msgstr "Eメールはまだ確認されていません。これは、当社が推奨する重要なセキュリティステップです。"
|
||||
|
||||
@@ -2380,7 +2380,7 @@ msgid "Your hosting provider"
|
||||
msgstr "ホスティングプロバイダー"
|
||||
|
||||
#: src/view/screens/Settings.tsx:402
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/desktop/RightNav.tsx:129
|
||||
#: src/view/shell/Drawer.tsx:636
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* 3. Don't call this query's `refetch()` if you're trying to sync latest; call `checkUnread()` instead.
|
||||
*/
|
||||
|
||||
import {useEffect, useRef} from 'react'
|
||||
import {useEffect} from 'react'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
@@ -49,8 +49,6 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
||||
const threadMutes = useMutedThreads()
|
||||
const unreads = useUnreadNotificationsApi()
|
||||
const enabled = opts?.enabled !== false
|
||||
// state tracked across page fetches
|
||||
const pageState = useRef({pageNum: 0, hasMarkedRead: false})
|
||||
|
||||
const query = useInfiniteQuery<
|
||||
FeedPage,
|
||||
@@ -66,8 +64,6 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
||||
if (!pageParam) {
|
||||
// for the first page, we check the cached page held by the unread-checker first
|
||||
page = unreads.getCachedUnreadPage()
|
||||
// reset the page state
|
||||
pageState.current = {pageNum: 0, hasMarkedRead: false}
|
||||
}
|
||||
if (!page) {
|
||||
page = await fetchPage({
|
||||
@@ -80,27 +76,9 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
||||
})
|
||||
}
|
||||
|
||||
// NOTE
|
||||
// this section checks to see if we need to mark notifs read
|
||||
// we want to wait until we've seen a read notification because
|
||||
// of a timing challenge; marking read on the first page would
|
||||
// cause subsequent pages of unread notifs to incorrectly come
|
||||
// back as "read". we use page 6 as an abort condition, which means
|
||||
// after ~180 notifs we give up on tracking unread state correctly
|
||||
// -prf
|
||||
if (!pageState.current.hasMarkedRead) {
|
||||
let hasMarkedRead = false
|
||||
if (
|
||||
pageState.current.pageNum > 5 ||
|
||||
page.items.some(item => item.notification.isRead)
|
||||
) {
|
||||
unreads.markAllRead()
|
||||
hasMarkedRead = true
|
||||
}
|
||||
pageState.current = {
|
||||
pageNum: pageState.current.pageNum + 1,
|
||||
hasMarkedRead,
|
||||
}
|
||||
// if the first page has an unread, mark all read
|
||||
if (!pageParam && page.items[0] && !page.items[0].notification.isRead) {
|
||||
unreads.markAllRead()
|
||||
}
|
||||
|
||||
return page
|
||||
@@ -108,6 +86,20 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
||||
initialPageParam: undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
enabled,
|
||||
select(data: InfiniteData<FeedPage>) {
|
||||
// override 'isRead' using the first page's returned seenAt
|
||||
// we do this because the `markAllRead()` call above will
|
||||
// mark subsequent pages as read prematurely
|
||||
const seenAt = data.pages[0]?.seenAt || new Date()
|
||||
for (const page of data.pages) {
|
||||
for (const item of page.items) {
|
||||
item.notification.isRead =
|
||||
seenAt > new Date(item.notification.indexedAt)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
@@ -122,7 +114,13 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
||||
count += page.items.length
|
||||
}
|
||||
|
||||
if (!isFetching && hasNextPage && count < PAGE_SIZE && numEmpties < 3) {
|
||||
if (
|
||||
!isFetching &&
|
||||
hasNextPage &&
|
||||
count < PAGE_SIZE &&
|
||||
numEmpties < 3 &&
|
||||
(data?.pages.length || 0) < 6
|
||||
) {
|
||||
query.fetchNextPage()
|
||||
}
|
||||
}, [query])
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface FeedNotification {
|
||||
|
||||
export interface FeedPage {
|
||||
cursor: string | undefined
|
||||
seenAt: Date
|
||||
items: FeedNotification[]
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,14 @@ export async function fetchPage({
|
||||
notif => !isThreadMuted(notif, threadMutes),
|
||||
)
|
||||
|
||||
let seenAt = res.data.seenAt ? new Date(res.data.seenAt) : new Date()
|
||||
if (Number.isNaN(seenAt.getTime())) {
|
||||
seenAt = new Date()
|
||||
}
|
||||
|
||||
return {
|
||||
cursor: res.data.cursor,
|
||||
seenAt,
|
||||
items: notifsGrouped,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +291,13 @@ export function usePostFeedQuery(
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFetching && hasNextPage && count < PAGE_SIZE && numEmpties < 3) {
|
||||
if (
|
||||
!isFetching &&
|
||||
hasNextPage &&
|
||||
count < PAGE_SIZE &&
|
||||
numEmpties < 3 &&
|
||||
(data?.pages.length || 0) < 6
|
||||
) {
|
||||
query.fetchNextPage()
|
||||
}
|
||||
}, [query])
|
||||
|
||||
@@ -361,6 +361,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
|
||||
if (canReusePrevSession) {
|
||||
logger.info(`session: attempting to reuse previous session`)
|
||||
|
||||
agent.session = prevSession
|
||||
__globalAgent = agent
|
||||
queryClient.clear()
|
||||
@@ -370,6 +372,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
resumeSessionWithFreshAccount()
|
||||
.then(freshAccount => {
|
||||
if (JSON.stringify(account) !== JSON.stringify(freshAccount)) {
|
||||
logger.info(
|
||||
`session: reuse of previous session returned a fresh account, upserting`,
|
||||
)
|
||||
upsertAccount(freshAccount)
|
||||
}
|
||||
})
|
||||
@@ -385,6 +390,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
__globalAgent = PUBLIC_BSKY_AGENT
|
||||
})
|
||||
} else {
|
||||
logger.info(`session: attempting to resume using previous session`)
|
||||
|
||||
try {
|
||||
const freshAccount = await resumeSessionWithFreshAccount()
|
||||
__globalAgent = agent
|
||||
@@ -404,6 +411,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
|
||||
async function resumeSessionWithFreshAccount(): Promise<SessionAccount> {
|
||||
logger.info(`session: resumeSessionWithFreshAccount`)
|
||||
|
||||
await networkRetry(1, () => agent.resumeSession(prevSession))
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,13 +7,15 @@ import {useNavigation} from '@react-navigation/native'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
|
||||
import {MainScrollProvider} from '../util/MainScrollProvider'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {colors, s} from 'lib/styles'
|
||||
import {FlatList, View, useWindowDimensions} from 'react-native'
|
||||
import {View, useWindowDimensions} from 'react-native'
|
||||
import {ListMethods} from '../util/List'
|
||||
import {Feed} from '../posts/Feed'
|
||||
import {TextLink} from '../util/Link'
|
||||
import {FAB} from '../util/fab/FAB'
|
||||
@@ -51,10 +53,11 @@ export function FeedPage({
|
||||
const {isDesktop} = useWebMediaQueries()
|
||||
const queryClient = useQueryClient()
|
||||
const {openComposer} = useComposerControls()
|
||||
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {screen, track} = useAnalytics()
|
||||
const headerOffset = useHeaderOffset()
|
||||
const scrollElRef = React.useRef<FlatList>(null)
|
||||
const scrollElRef = React.useRef<ListMethods>(null)
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
|
||||
const scrollToTop = React.useCallback(() => {
|
||||
@@ -62,8 +65,8 @@ export function FeedPage({
|
||||
animated: isNative,
|
||||
offset: -headerOffset,
|
||||
})
|
||||
resetMainScroll()
|
||||
}, [headerOffset, resetMainScroll])
|
||||
setMinimalShellMode(false)
|
||||
}, [headerOffset, setMinimalShellMode])
|
||||
|
||||
const onSoftReset = React.useCallback(() => {
|
||||
const isScreenFocused =
|
||||
@@ -164,21 +167,22 @@ export function FeedPage({
|
||||
|
||||
return (
|
||||
<View testID={testID} style={s.h100pct}>
|
||||
<Feed
|
||||
testID={testID ? `${testID}-feed` : undefined}
|
||||
enabled={isPageFocused}
|
||||
feed={feed}
|
||||
feedParams={feedParams}
|
||||
pollInterval={POLL_FREQ}
|
||||
scrollElRef={scrollElRef}
|
||||
onScroll={onMainScroll}
|
||||
onHasNew={setHasNew}
|
||||
scrollEventThrottle={1}
|
||||
renderEmptyState={renderEmptyState}
|
||||
renderEndOfFeed={renderEndOfFeed}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
headerOffset={headerOffset}
|
||||
/>
|
||||
<MainScrollProvider>
|
||||
<Feed
|
||||
testID={testID ? `${testID}-feed` : undefined}
|
||||
enabled={isPageFocused}
|
||||
feed={feed}
|
||||
feedParams={feedParams}
|
||||
pollInterval={POLL_FREQ}
|
||||
scrollElRef={scrollElRef}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
onHasNew={setHasNew}
|
||||
renderEmptyState={renderEmptyState}
|
||||
renderEndOfFeed={renderEndOfFeed}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
headerOffset={headerOffset}
|
||||
/>
|
||||
</MainScrollProvider>
|
||||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onPressLoadLatest}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {MutableRefObject} from 'react'
|
||||
import React from 'react'
|
||||
import {
|
||||
Dimensions,
|
||||
RefreshControl,
|
||||
@@ -8,18 +8,16 @@ import {
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {FlatList} from '../util/Views'
|
||||
import {List, ListRef} from '../util/List'
|
||||
import {FeedSourceCardLoaded} from './FeedSourceCard'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useProfileFeedgensQuery, RQKEY} from '#/state/queries/profile-feedgens'
|
||||
import {OnScrollHandler} from '#/lib/hooks/useOnMainScroll'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans} from '@lingui/macro'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {hydrateFeedGenerator} from '#/state/queries/feed'
|
||||
@@ -37,9 +35,7 @@ interface SectionRef {
|
||||
|
||||
interface ProfileFeedgensProps {
|
||||
did: string
|
||||
scrollElRef: MutableRefObject<FlatList<any> | null>
|
||||
onScroll?: OnScrollHandler
|
||||
scrollEventThrottle?: number
|
||||
scrollElRef: ListRef
|
||||
headerOffset: number
|
||||
enabled?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
@@ -50,16 +46,7 @@ export const ProfileFeedgens = React.forwardRef<
|
||||
SectionRef,
|
||||
ProfileFeedgensProps
|
||||
>(function ProfileFeedgensImpl(
|
||||
{
|
||||
did,
|
||||
scrollElRef,
|
||||
onScroll,
|
||||
scrollEventThrottle,
|
||||
headerOffset,
|
||||
enabled,
|
||||
style,
|
||||
testID,
|
||||
},
|
||||
{did, scrollElRef, headerOffset, enabled, style, testID},
|
||||
ref,
|
||||
) {
|
||||
const pal = usePalette('default')
|
||||
@@ -185,10 +172,9 @@ export const ProfileFeedgens = React.forwardRef<
|
||||
[error, refetch, onPressRetryLoadMore, pal, preferences],
|
||||
)
|
||||
|
||||
const scrollHandler = useAnimatedScrollHandler(onScroll || {})
|
||||
return (
|
||||
<View testID={testID} style={style}>
|
||||
<FlatList
|
||||
<List
|
||||
testID={testID ? `${testID}-flatlist` : undefined}
|
||||
ref={scrollElRef}
|
||||
data={items}
|
||||
@@ -207,8 +193,6 @@ export const ProfileFeedgens = React.forwardRef<
|
||||
minHeight: Dimensions.get('window').height * 1.5,
|
||||
}}
|
||||
style={{paddingTop: headerOffset}}
|
||||
onScroll={onScroll != null ? scrollHandler : undefined}
|
||||
scrollEventThrottle={scrollEventThrottle}
|
||||
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
|
||||
removeClippedSubviews={true}
|
||||
contentOffset={{x: 0, y: headerOffset * -1}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {MutableRefObject} from 'react'
|
||||
import React from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Dimensions,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
|
||||
import {FlatList} from '../util/Views'
|
||||
import {List, ListRef} from '../util/List'
|
||||
import {ProfileCardFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||
@@ -18,10 +18,8 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useListMembersQuery} from '#/state/queries/list-members'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {useSession} from '#/state/session'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
|
||||
@@ -34,24 +32,22 @@ export function ListMembers({
|
||||
list,
|
||||
style,
|
||||
scrollElRef,
|
||||
onScroll,
|
||||
onScrolledDownChange,
|
||||
onPressTryAgain,
|
||||
renderHeader,
|
||||
renderEmptyState,
|
||||
testID,
|
||||
scrollEventThrottle,
|
||||
headerOffset = 0,
|
||||
desktopFixedHeightOffset,
|
||||
}: {
|
||||
list: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
||||
onScroll: OnScrollHandler
|
||||
scrollElRef?: ListRef
|
||||
onScrolledDownChange: (isScrolledDown: boolean) => void
|
||||
onPressTryAgain?: () => void
|
||||
renderHeader: () => JSX.Element
|
||||
renderEmptyState: () => JSX.Element
|
||||
testID?: string
|
||||
scrollEventThrottle?: number
|
||||
headerOffset?: number
|
||||
desktopFixedHeightOffset?: number
|
||||
}) {
|
||||
@@ -209,10 +205,9 @@ export function ListMembers({
|
||||
[isFetching],
|
||||
)
|
||||
|
||||
const scrollHandler = useAnimatedScrollHandler(onScroll)
|
||||
return (
|
||||
<View testID={testID} style={style}>
|
||||
<FlatList
|
||||
<List
|
||||
testID={testID ? `${testID}-flatlist` : undefined}
|
||||
ref={scrollElRef}
|
||||
data={items}
|
||||
@@ -233,10 +228,9 @@ export function ListMembers({
|
||||
minHeight: Dimensions.get('window').height * 1.5,
|
||||
}}
|
||||
style={{paddingTop: headerOffset}}
|
||||
onScroll={scrollHandler}
|
||||
onScrolledDownChange={onScrolledDownChange}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={0.6}
|
||||
scrollEventThrottle={scrollEventThrottle}
|
||||
removeClippedSubviews={true}
|
||||
contentOffset={{x: 0, y: headerOffset * -1}}
|
||||
// @ts-ignore our .web version only -prf
|
||||
|
||||
@@ -15,7 +15,7 @@ import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {FlatList} from '../util/Views'
|
||||
import {List} from '../util/List'
|
||||
import {s} from 'lib/styles'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans} from '@lingui/macro'
|
||||
@@ -119,7 +119,7 @@ export function MyLists({
|
||||
[error, onRefresh, renderItem, pal],
|
||||
)
|
||||
|
||||
const FlatListCom = inline ? RNFlatList : FlatList
|
||||
const FlatListCom = inline ? RNFlatList : List
|
||||
return (
|
||||
<View testID={testID} style={style}>
|
||||
{items.length > 0 && (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {MutableRefObject} from 'react'
|
||||
import React from 'react'
|
||||
import {
|
||||
Dimensions,
|
||||
RefreshControl,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {FlatList} from '../util/Views'
|
||||
import {List, ListRef} from '../util/List'
|
||||
import {ListCard} from './ListCard'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||
@@ -16,11 +16,9 @@ import {Text} from '../util/text/Text'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useProfileListsQuery, RQKEY} from '#/state/queries/profile-lists'
|
||||
import {OnScrollHandler} from '#/lib/hooks/useOnMainScroll'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans} from '@lingui/macro'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {isNative} from '#/platform/detection'
|
||||
@@ -36,9 +34,7 @@ interface SectionRef {
|
||||
|
||||
interface ProfileListsProps {
|
||||
did: string
|
||||
scrollElRef: MutableRefObject<FlatList<any> | null>
|
||||
onScroll?: OnScrollHandler
|
||||
scrollEventThrottle?: number
|
||||
scrollElRef: ListRef
|
||||
headerOffset: number
|
||||
enabled?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
@@ -47,16 +43,7 @@ interface ProfileListsProps {
|
||||
|
||||
export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
|
||||
function ProfileListsImpl(
|
||||
{
|
||||
did,
|
||||
scrollElRef,
|
||||
onScroll,
|
||||
scrollEventThrottle,
|
||||
headerOffset,
|
||||
enabled,
|
||||
style,
|
||||
testID,
|
||||
},
|
||||
{did, scrollElRef, headerOffset, enabled, style, testID},
|
||||
ref,
|
||||
) {
|
||||
const pal = usePalette('default')
|
||||
@@ -187,10 +174,9 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
|
||||
[error, refetch, onPressRetryLoadMore, pal],
|
||||
)
|
||||
|
||||
const scrollHandler = useAnimatedScrollHandler(onScroll || {})
|
||||
return (
|
||||
<View testID={testID} style={style}>
|
||||
<FlatList
|
||||
<List
|
||||
testID={testID ? `${testID}-flatlist` : undefined}
|
||||
ref={scrollElRef}
|
||||
data={items}
|
||||
@@ -209,8 +195,6 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
|
||||
minHeight: Dimensions.get('window').height * 1.5,
|
||||
}}
|
||||
style={{paddingTop: headerOffset}}
|
||||
onScroll={onScroll != null ? scrollHandler : undefined}
|
||||
scrollEventThrottle={scrollEventThrottle}
|
||||
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
|
||||
removeClippedSubviews={true}
|
||||
contentOffset={{x: 0, y: headerOffset * -1}}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useSession, useSessionApi, getAgent} from '#/state/session'
|
||||
import {logger} from '#/logger'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
@@ -45,6 +46,13 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!currentAccount) {
|
||||
logger.error(`VerifyEmail modal opened without currentAccount`)
|
||||
closeModal()
|
||||
}
|
||||
}, [currentAccount, closeModal])
|
||||
|
||||
const onSendEmail = async () => {
|
||||
setError('')
|
||||
setIsProcessing(true)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React, {MutableRefObject} from 'react'
|
||||
import {CenteredView, FlatList} from '../util/Views'
|
||||
import React from 'react'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||
import {FeedItem} from './FeedItem'
|
||||
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||
import {EmptyState} from '../util/EmptyState'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {s} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
|
||||
@@ -15,6 +13,7 @@ import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread'
|
||||
import {logger} from '#/logger'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {List, ListRef} from '../util/List'
|
||||
|
||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||
const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
|
||||
@@ -23,12 +22,12 @@ const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||
export function Feed({
|
||||
scrollElRef,
|
||||
onPressTryAgain,
|
||||
onScroll,
|
||||
onScrolledDownChange,
|
||||
ListHeaderComponent,
|
||||
}: {
|
||||
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
||||
scrollElRef?: ListRef
|
||||
onPressTryAgain?: () => void
|
||||
onScroll?: OnScrollHandler
|
||||
onScrolledDownChange: (isScrolledDown: boolean) => void
|
||||
ListHeaderComponent?: () => JSX.Element
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
@@ -135,7 +134,6 @@ export function Feed({
|
||||
[isFetchingNextPage],
|
||||
)
|
||||
|
||||
const scrollHandler = useAnimatedScrollHandler(onScroll || {})
|
||||
return (
|
||||
<View style={s.hContentRegion}>
|
||||
{error && (
|
||||
@@ -146,7 +144,7 @@ export function Feed({
|
||||
/>
|
||||
</CenteredView>
|
||||
)}
|
||||
<FlatList
|
||||
<List
|
||||
testID="notifsFeed"
|
||||
ref={scrollElRef}
|
||||
data={items}
|
||||
@@ -164,8 +162,7 @@ export function Feed({
|
||||
}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={0.6}
|
||||
onScroll={scrollHandler}
|
||||
scrollEventThrottle={1}
|
||||
onScrolledDownChange={onScrolledDownChange}
|
||||
contentContainerStyle={s.contentContainer}
|
||||
// @ts-ignore our .web version only -prf
|
||||
desktopFixedHeight
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react'
|
||||
import {
|
||||
LayoutChangeEvent,
|
||||
FlatList,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
View,
|
||||
@@ -19,17 +18,14 @@ import Animated, {
|
||||
} from 'react-native-reanimated'
|
||||
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||
import {TabBar} from './TabBar'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
|
||||
const SCROLLED_DOWN_LIMIT = 200
|
||||
import {ListMethods} from '../util/List'
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
|
||||
export interface PagerWithHeaderChildParams {
|
||||
headerHeight: number
|
||||
isFocused: boolean
|
||||
onScroll: OnScrollHandler
|
||||
isScrolledDown: boolean
|
||||
scrollElRef: React.MutableRefObject<FlatList<any> | ScrollView | null>
|
||||
scrollElRef: React.MutableRefObject<ListMethods | ScrollView | null>
|
||||
}
|
||||
|
||||
export interface PagerWithHeaderProps {
|
||||
@@ -61,7 +57,6 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
const [currentPage, setCurrentPage] = React.useState(0)
|
||||
const [tabBarHeight, setTabBarHeight] = React.useState(0)
|
||||
const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const scrollY = useSharedValue(0)
|
||||
const headerHeight = headerOnlyHeight + tabBarHeight
|
||||
|
||||
@@ -120,13 +115,16 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
)
|
||||
|
||||
const scrollRefs = useSharedValue<AnimatedRef<any>[]>([])
|
||||
const registerRef = (scrollRef: AnimatedRef<any>, index: number) => {
|
||||
scrollRefs.modify(refs => {
|
||||
'worklet'
|
||||
refs[index] = scrollRef
|
||||
return refs
|
||||
})
|
||||
}
|
||||
const registerRef = React.useCallback(
|
||||
(scrollRef: AnimatedRef<any> | null, atIndex: number) => {
|
||||
scrollRefs.modify(refs => {
|
||||
'worklet'
|
||||
refs[atIndex] = scrollRef
|
||||
return refs
|
||||
})
|
||||
},
|
||||
[scrollRefs],
|
||||
)
|
||||
|
||||
const lastForcedScrollY = useSharedValue(0)
|
||||
const adjustScrollForOtherPages = () => {
|
||||
@@ -137,8 +135,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
lastForcedScrollY.value = forcedScrollY
|
||||
const refs = scrollRefs.value
|
||||
for (let i = 0; i < refs.length; i++) {
|
||||
if (i !== currentPage) {
|
||||
// This needs to run on the UI thread.
|
||||
if (i !== currentPage && refs[i] != null) {
|
||||
scrollTo(refs[i], 0, forcedScrollY, false)
|
||||
}
|
||||
}
|
||||
@@ -152,15 +149,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
if (!throttleTimeout.current) {
|
||||
throttleTimeout.current = setTimeout(() => {
|
||||
throttleTimeout.current = null
|
||||
|
||||
runOnUI(adjustScrollForOtherPages)()
|
||||
|
||||
const nextIsScrolledDown = scrollY.value > SCROLLED_DOWN_LIMIT
|
||||
if (isScrolledDown !== nextIsScrolledDown) {
|
||||
React.startTransition(() => {
|
||||
setIsScrolledDown(nextIsScrolledDown)
|
||||
})
|
||||
}
|
||||
}, 80 /* Sync often enough you're unlikely to catch it unsynced */)
|
||||
}
|
||||
})
|
||||
@@ -205,11 +194,11 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
<View key={i} collapsable={false}>
|
||||
<PagerItem
|
||||
headerHeight={headerHeight}
|
||||
index={i}
|
||||
isReady={isReady}
|
||||
isFocused={i === currentPage}
|
||||
isScrolledDown={isScrolledDown}
|
||||
onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
|
||||
registerRef={(r: AnimatedRef<any>) => registerRef(r, i)}
|
||||
registerRef={registerRef}
|
||||
renderTab={child}
|
||||
/>
|
||||
</View>
|
||||
@@ -282,42 +271,45 @@ PagerTabBar = React.memo(PagerTabBar)
|
||||
|
||||
function PagerItem({
|
||||
headerHeight,
|
||||
index,
|
||||
isReady,
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
onScrollWorklet,
|
||||
renderTab,
|
||||
registerRef,
|
||||
}: {
|
||||
headerHeight: number
|
||||
index: number
|
||||
isFocused: boolean
|
||||
isReady: boolean
|
||||
isScrolledDown: boolean
|
||||
registerRef: (scrollRef: AnimatedRef<any>) => void
|
||||
registerRef: (scrollRef: AnimatedRef<any> | null, atIndex: number) => void
|
||||
onScrollWorklet: (e: NativeScrollEvent) => void
|
||||
renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null
|
||||
}) {
|
||||
const scrollElRef = useAnimatedRef()
|
||||
registerRef(scrollElRef)
|
||||
|
||||
const scrollHandler = React.useMemo(
|
||||
() => ({onScroll: onScrollWorklet}),
|
||||
[onScrollWorklet],
|
||||
)
|
||||
React.useEffect(() => {
|
||||
registerRef(scrollElRef, index)
|
||||
return () => {
|
||||
registerRef(null, index)
|
||||
}
|
||||
}, [scrollElRef, registerRef, index])
|
||||
|
||||
if (!isReady || renderTab == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return renderTab({
|
||||
headerHeight,
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
onScroll: scrollHandler,
|
||||
scrollElRef: scrollElRef as React.MutableRefObject<
|
||||
FlatList<any> | ScrollView | null
|
||||
>,
|
||||
})
|
||||
return (
|
||||
<ScrollProvider onScroll={onScrollWorklet}>
|
||||
{renderTab({
|
||||
headerHeight,
|
||||
isFocused,
|
||||
scrollElRef: scrollElRef as React.MutableRefObject<
|
||||
ListMethods | ScrollView | null
|
||||
>,
|
||||
})}
|
||||
</ScrollProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
import * as React from 'react'
|
||||
import {
|
||||
FlatList,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
View,
|
||||
NativeScrollEvent,
|
||||
} from 'react-native'
|
||||
import {useSharedValue, runOnJS, useAnimatedRef} from 'react-native-reanimated'
|
||||
import {FlatList, ScrollView, StyleSheet, View} from 'react-native'
|
||||
import {useAnimatedRef} from 'react-native-reanimated'
|
||||
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||
import {TabBar} from './TabBar'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
|
||||
const SCROLLED_DOWN_LIMIT = 200
|
||||
import {ListMethods} from '../util/List'
|
||||
|
||||
export interface PagerWithHeaderChildParams {
|
||||
headerHeight: number
|
||||
isFocused: boolean
|
||||
onScroll: OnScrollHandler
|
||||
isScrolledDown: boolean
|
||||
scrollElRef: React.MutableRefObject<FlatList<any> | ScrollView | null>
|
||||
}
|
||||
|
||||
@@ -50,8 +39,6 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
ref,
|
||||
) {
|
||||
const [currentPage, setCurrentPage] = React.useState(0)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const scrollY = useSharedValue(0)
|
||||
|
||||
const renderTabBar = React.useCallback(
|
||||
(props: RenderTabBarFnProps) => {
|
||||
@@ -69,34 +56,6 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
[items, renderHeader, currentPage, onCurrentPageSelected, testID],
|
||||
)
|
||||
|
||||
const throttleTimeout = React.useRef<ReturnType<typeof setTimeout> | null>(
|
||||
null,
|
||||
)
|
||||
const queueThrottledOnScroll = useNonReactiveCallback(() => {
|
||||
if (!throttleTimeout.current) {
|
||||
throttleTimeout.current = setTimeout(() => {
|
||||
throttleTimeout.current = null
|
||||
|
||||
const nextIsScrolledDown = scrollY.value > SCROLLED_DOWN_LIMIT
|
||||
if (isScrolledDown !== nextIsScrolledDown) {
|
||||
React.startTransition(() => {
|
||||
setIsScrolledDown(nextIsScrolledDown)
|
||||
})
|
||||
}
|
||||
}, 80)
|
||||
}
|
||||
})
|
||||
|
||||
const onScrollWorklet = React.useCallback(
|
||||
(e: NativeScrollEvent) => {
|
||||
'worklet'
|
||||
const nextScrollY = e.contentOffset.y
|
||||
scrollY.value = nextScrollY
|
||||
runOnJS(queueThrottledOnScroll)()
|
||||
},
|
||||
[scrollY, queueThrottledOnScroll],
|
||||
)
|
||||
|
||||
const onPageSelectedInner = React.useCallback(
|
||||
(index: number) => {
|
||||
setCurrentPage(index)
|
||||
@@ -123,12 +82,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
.map((child, i) => {
|
||||
return (
|
||||
<View key={i} collapsable={false}>
|
||||
<PagerItem
|
||||
isFocused={i === currentPage}
|
||||
isScrolledDown={isScrolledDown}
|
||||
onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
|
||||
renderTab={child}
|
||||
/>
|
||||
<PagerItem isFocused={i === currentPage} renderTab={child} />
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
@@ -182,30 +136,20 @@ PagerTabBar = React.memo(PagerTabBar)
|
||||
|
||||
function PagerItem({
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
onScrollWorklet,
|
||||
renderTab,
|
||||
}: {
|
||||
isFocused: boolean
|
||||
isScrolledDown: boolean
|
||||
onScrollWorklet: (e: NativeScrollEvent) => void
|
||||
renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null
|
||||
}) {
|
||||
const scrollElRef = useAnimatedRef()
|
||||
const scrollHandler = React.useMemo(
|
||||
() => ({onScroll: onScrollWorklet}),
|
||||
[onScrollWorklet],
|
||||
)
|
||||
if (renderTab == null) {
|
||||
return null
|
||||
}
|
||||
return renderTab({
|
||||
headerHeight: 0,
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
onScroll: scrollHandler,
|
||||
scrollElRef: scrollElRef as React.MutableRefObject<
|
||||
FlatList<any> | ScrollView | null
|
||||
ListMethods | ScrollView | null
|
||||
>,
|
||||
})
|
||||
}
|
||||
@@ -238,10 +182,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
})
|
||||
|
||||
function noop() {
|
||||
'worklet'
|
||||
}
|
||||
|
||||
function toArray<T>(v: T | T[]): T[] {
|
||||
if (Array.isArray(v)) {
|
||||
return v
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, {useCallback, useMemo, useState} from 'react'
|
||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
||||
import {CenteredView, FlatList} from '../util/Views'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {List} from '../util/List'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
@@ -84,7 +85,7 @@ export function PostLikedBy({uri}: {uri: string}) {
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<FlatList
|
||||
<List
|
||||
data={likes}
|
||||
keyExtractor={item => item.actor.did}
|
||||
refreshControl={
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, {useMemo, useCallback, useState} from 'react'
|
||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {CenteredView, FlatList} from '../util/Views'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {List} from '../util/List'
|
||||
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
@@ -85,7 +86,7 @@ export function PostRepostedBy({uri}: {uri: string}) {
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<FlatList
|
||||
<List
|
||||
data={repostedBy}
|
||||
keyExtractor={item => item.did}
|
||||
refreshControl={
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {CenteredView, FlatList} from '../util/Views'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {List, ListMethods} from '../util/List'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconStyle,
|
||||
@@ -140,7 +141,7 @@ function PostThreadLoaded({
|
||||
const {_} = useLingui()
|
||||
const pal = usePalette('default')
|
||||
const {isTablet, isDesktop} = useWebMediaQueries()
|
||||
const ref = useRef<FlatList>(null)
|
||||
const ref = useRef<ListMethods>(null)
|
||||
const highlightedPostRef = useRef<View | null>(null)
|
||||
const needsScrollAdjustment = useRef<boolean>(
|
||||
!isNative || // web always uses scroll adjustment
|
||||
@@ -335,7 +336,7 @@ function PostThreadLoaded({
|
||||
)
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
<List
|
||||
ref={ref}
|
||||
data={posts}
|
||||
initialNumToRender={!isNative ? posts.length : undefined}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {memo, MutableRefObject} from 'react'
|
||||
import React, {memo} from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
AppState,
|
||||
@@ -10,15 +10,13 @@ import {
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {FlatList} from '../util/Views'
|
||||
import {List, ListRef} from '../util/List'
|
||||
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||
import {FeedErrorMessage} from './FeedErrorMessage'
|
||||
import {FeedSlice} from './FeedSlice'
|
||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {logger} from '#/logger'
|
||||
import {
|
||||
@@ -45,9 +43,8 @@ let Feed = ({
|
||||
enabled,
|
||||
pollInterval,
|
||||
scrollElRef,
|
||||
onScroll,
|
||||
onScrolledDownChange,
|
||||
onHasNew,
|
||||
scrollEventThrottle,
|
||||
renderEmptyState,
|
||||
renderEndOfFeed,
|
||||
testID,
|
||||
@@ -62,10 +59,9 @@ let Feed = ({
|
||||
style?: StyleProp<ViewStyle>
|
||||
enabled?: boolean
|
||||
pollInterval?: number
|
||||
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
||||
scrollElRef?: ListRef
|
||||
onHasNew?: (v: boolean) => void
|
||||
onScroll?: OnScrollHandler
|
||||
scrollEventThrottle?: number
|
||||
onScrolledDownChange?: (isScrolledDown: boolean) => void
|
||||
renderEmptyState: () => JSX.Element
|
||||
renderEndOfFeed?: () => JSX.Element
|
||||
testID?: string
|
||||
@@ -167,8 +163,7 @@ let Feed = ({
|
||||
if (isFetched) {
|
||||
if (isError && isEmpty) {
|
||||
arr = arr.concat([ERROR_ITEM])
|
||||
}
|
||||
if (isEmpty) {
|
||||
} else if (isEmpty) {
|
||||
arr = arr.concat([EMPTY_FEED_ITEM])
|
||||
} else if (data) {
|
||||
for (const page of data?.pages) {
|
||||
@@ -271,10 +266,9 @@ let Feed = ({
|
||||
)
|
||||
}, [isFetchingNextPage, shouldRenderEndOfFeed, renderEndOfFeed, headerOffset])
|
||||
|
||||
const scrollHandler = useAnimatedScrollHandler(onScroll || {})
|
||||
return (
|
||||
<View testID={testID} style={style}>
|
||||
<FlatList
|
||||
<List
|
||||
testID={testID ? `${testID}-flatlist` : undefined}
|
||||
ref={scrollElRef}
|
||||
data={feedItems}
|
||||
@@ -295,8 +289,7 @@ let Feed = ({
|
||||
minHeight: Dimensions.get('window').height * 1.5,
|
||||
}}
|
||||
style={{paddingTop: headerOffset}}
|
||||
onScroll={onScroll != null ? scrollHandler : undefined}
|
||||
scrollEventThrottle={scrollEventThrottle}
|
||||
onScrolledDownChange={onScrolledDownChange}
|
||||
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={2} // number of posts left to trigger load more
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {CenteredView, FlatList} from '../util/Views'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {List} from '../util/List'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
@@ -86,7 +87,7 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<FlatList
|
||||
<List
|
||||
data={followers}
|
||||
keyExtractor={item => item.did}
|
||||
refreshControl={
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {CenteredView, FlatList} from '../util/Views'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {List} from '../util/List'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
@@ -86,7 +87,7 @@ export function ProfileFollows({name}: {name: string}) {
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<FlatList
|
||||
<List
|
||||
data={follows}
|
||||
keyExtractor={item => item.did}
|
||||
refreshControl={
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import React, {memo, startTransition} from 'react'
|
||||
import {FlatListProps} from 'react-native'
|
||||
import {FlatList_INTERNAL} from './Views'
|
||||
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||
import {runOnJS, useSharedValue} from 'react-native-reanimated'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
|
||||
export type ListMethods = FlatList_INTERNAL
|
||||
export type ListProps<ItemT> = Omit<
|
||||
FlatListProps<ItemT>,
|
||||
'onScroll' // Use ScrollContext instead.
|
||||
> & {
|
||||
onScrolledDownChange?: (isScrolledDown: boolean) => void
|
||||
}
|
||||
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
|
||||
|
||||
const SCROLLED_DOWN_LIMIT = 200
|
||||
|
||||
function ListImpl<ItemT>(
|
||||
{onScrolledDownChange, ...props}: ListProps<ItemT>,
|
||||
ref: React.Ref<ListMethods>,
|
||||
) {
|
||||
const isScrolledDown = useSharedValue(false)
|
||||
const contextScrollHandlers = useScrollHandlers()
|
||||
|
||||
function handleScrolledDownChange(didScrollDown: boolean) {
|
||||
startTransition(() => {
|
||||
onScrolledDownChange?.(didScrollDown)
|
||||
})
|
||||
}
|
||||
|
||||
const scrollHandler = useAnimatedScrollHandler({
|
||||
onBeginDrag(e, ctx) {
|
||||
contextScrollHandlers.onBeginDrag?.(e, ctx)
|
||||
},
|
||||
onEndDrag(e, ctx) {
|
||||
contextScrollHandlers.onEndDrag?.(e, ctx)
|
||||
},
|
||||
onScroll(e, ctx) {
|
||||
contextScrollHandlers.onScroll?.(e, ctx)
|
||||
|
||||
const didScrollDown = e.contentOffset.y > SCROLLED_DOWN_LIMIT
|
||||
if (isScrolledDown.value !== didScrollDown) {
|
||||
isScrolledDown.value = didScrollDown
|
||||
if (onScrolledDownChange != null) {
|
||||
runOnJS(handleScrolledDownChange)(didScrollDown)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<FlatList_INTERNAL
|
||||
{...props}
|
||||
onScroll={scrollHandler}
|
||||
scrollEventThrottle={1}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const List = memo(React.forwardRef(ListImpl)) as <ItemT>(
|
||||
props: ListProps<ItemT> & {ref?: React.Ref<ListMethods>},
|
||||
) => React.ReactElement
|
||||
@@ -1,30 +1,18 @@
|
||||
import {useState, useCallback, useMemo} from 'react'
|
||||
import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
|
||||
import React, {useCallback} from 'react'
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
import {NativeScrollEvent} from 'react-native'
|
||||
import {useSetMinimalShellMode, useMinimalShellMode} from '#/state/shell'
|
||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||
import {s} from 'lib/styles'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {
|
||||
useSharedValue,
|
||||
interpolate,
|
||||
runOnJS,
|
||||
ScrollHandlers,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSharedValue, interpolate} from 'react-native-reanimated'
|
||||
|
||||
function clamp(num: number, min: number, max: number) {
|
||||
'worklet'
|
||||
return Math.min(Math.max(num, min), max)
|
||||
}
|
||||
|
||||
export type OnScrollCb = (
|
||||
event: NativeSyntheticEvent<NativeScrollEvent>,
|
||||
) => void
|
||||
export type OnScrollHandler = ScrollHandlers<any>
|
||||
export type ResetCb = () => void
|
||||
|
||||
export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] {
|
||||
export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||
const {headerHeight} = useShellLayout()
|
||||
const [isScrolledDown, setIsScrolledDown] = useState(false)
|
||||
const mode = useMinimalShellMode()
|
||||
const setMode = useSetMinimalShellMode()
|
||||
const startDragOffset = useSharedValue<number | null>(null)
|
||||
@@ -58,13 +46,6 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] {
|
||||
const onScroll = useCallback(
|
||||
(e: NativeScrollEvent) => {
|
||||
'worklet'
|
||||
// Keep track of whether we want to show "scroll to top".
|
||||
if (!isScrolledDown && e.contentOffset.y > s.window.height) {
|
||||
runOnJS(setIsScrolledDown)(true)
|
||||
} else if (isScrolledDown && e.contentOffset.y < s.window.height) {
|
||||
runOnJS(setIsScrolledDown)(false)
|
||||
}
|
||||
|
||||
if (startDragOffset.value === null || startMode.value === null) {
|
||||
if (mode.value !== 0 && e.contentOffset.y < headerHeight.value) {
|
||||
// If we're close enough to the top, always show the shell.
|
||||
@@ -102,24 +83,15 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] {
|
||||
startMode.value = mode.value
|
||||
}
|
||||
},
|
||||
[headerHeight, mode, setMode, isScrolledDown, startDragOffset, startMode],
|
||||
[headerHeight, mode, setMode, startDragOffset, startMode],
|
||||
)
|
||||
|
||||
const scrollHandler: ScrollHandlers<any> = useMemo(
|
||||
() => ({
|
||||
onBeginDrag,
|
||||
onEndDrag,
|
||||
onScroll,
|
||||
}),
|
||||
[onBeginDrag, onEndDrag, onScroll],
|
||||
return (
|
||||
<ScrollProvider
|
||||
onBeginDrag={onBeginDrag}
|
||||
onEndDrag={onEndDrag}
|
||||
onScroll={onScroll}>
|
||||
{children}
|
||||
</ScrollProvider>
|
||||
)
|
||||
|
||||
return [
|
||||
scrollHandler,
|
||||
isScrolledDown,
|
||||
useCallback(() => {
|
||||
setIsScrolledDown(false)
|
||||
setMode(false)
|
||||
}, [setMode]),
|
||||
]
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {
|
||||
NativeSyntheticEvent,
|
||||
NativeScrollEvent,
|
||||
Pressable,
|
||||
RefreshControl,
|
||||
StyleSheet,
|
||||
View,
|
||||
ScrollView,
|
||||
} from 'react-native'
|
||||
import {FlatList} from './Views'
|
||||
import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
|
||||
import {FlatList_INTERNAL} from './Views'
|
||||
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
||||
import {Text} from './text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
@@ -38,7 +39,7 @@ export const ViewSelector = React.forwardRef<
|
||||
| null
|
||||
| undefined
|
||||
onSelectView?: (viewIndex: number) => void
|
||||
onScroll?: OnScrollCb
|
||||
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
|
||||
onRefresh?: () => void
|
||||
onEndReached?: (info: {distanceFromEnd: number}) => void
|
||||
}
|
||||
@@ -59,7 +60,7 @@ export const ViewSelector = React.forwardRef<
|
||||
) {
|
||||
const pal = usePalette('default')
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(0)
|
||||
const flatListRef = React.useRef<FlatList>(null)
|
||||
const flatListRef = React.useRef<FlatList_INTERNAL>(null)
|
||||
|
||||
// events
|
||||
// =
|
||||
@@ -110,7 +111,7 @@ export const ViewSelector = React.forwardRef<
|
||||
[items],
|
||||
)
|
||||
return (
|
||||
<FlatList
|
||||
<FlatList_INTERNAL
|
||||
ref={flatListRef}
|
||||
data={data}
|
||||
keyExtractor={keyExtractor}
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import {ViewProps} from 'react-native'
|
||||
export {FlatList, ScrollView} from 'react-native'
|
||||
export {FlatList as FlatList_INTERNAL, ScrollView} from 'react-native'
|
||||
export function CenteredView({
|
||||
style,
|
||||
sideBorders,
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import Animated from 'react-native-reanimated'
|
||||
|
||||
export const FlatList = Animated.FlatList
|
||||
export const FlatList_INTERNAL = Animated.FlatList
|
||||
export const ScrollView = Animated.ScrollView
|
||||
export function CenteredView(props) {
|
||||
return <View {...props} />
|
||||
|
||||
@@ -49,7 +49,7 @@ export function CenteredView({
|
||||
return <View style={style} {...props} />
|
||||
}
|
||||
|
||||
export const FlatList = React.forwardRef(function FlatListImpl<ItemT>(
|
||||
export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
|
||||
{
|
||||
data,
|
||||
extraData,
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
|
||||
import debounce from 'lodash.debounce'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {FlatList} from 'view/com/util/Views'
|
||||
import {List} from 'view/com/util/List'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
@@ -481,7 +481,7 @@ export function FeedsScreen(_props: Props) {
|
||||
|
||||
{preferences ? <View /> : <ActivityIndicator />}
|
||||
|
||||
<FlatList
|
||||
<List
|
||||
style={[!isTabletOrDesktop && s.flex1, styles.list]}
|
||||
data={items}
|
||||
keyExtractor={item => item.key}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import {FlatList, View} from 'react-native'
|
||||
import {View} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {
|
||||
@@ -9,8 +9,9 @@ import {
|
||||
import {ViewHeader} from '../com/util/ViewHeader'
|
||||
import {Feed} from '../com/notifications/Feed'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {ListMethods} from 'view/com/util/List'
|
||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
|
||||
import {MainScrollProvider} from '../com/util/MainScrollProvider'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {s, colors} from 'lib/styles'
|
||||
@@ -35,8 +36,8 @@ type Props = NativeStackScreenProps<
|
||||
export function NotificationsScreen({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
|
||||
const scrollElRef = React.useRef<FlatList>(null)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const scrollElRef = React.useRef<ListMethods>(null)
|
||||
const checkLatestRef = React.useRef<() => void | null>()
|
||||
const {screen} = useAnalytics()
|
||||
const pal = usePalette('default')
|
||||
@@ -50,8 +51,8 @@ export function NotificationsScreen({}: Props) {
|
||||
// =
|
||||
const scrollToTop = React.useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({animated: isNative, offset: 0})
|
||||
resetMainScroll()
|
||||
}, [scrollElRef, resetMainScroll])
|
||||
setMinimalShellMode(false)
|
||||
}, [scrollElRef, setMinimalShellMode])
|
||||
|
||||
const onPressLoadLatest = React.useCallback(() => {
|
||||
scrollToTop()
|
||||
@@ -130,11 +131,13 @@ export function NotificationsScreen({}: Props) {
|
||||
return (
|
||||
<View testID="notificationsScreen" style={s.hContentRegion}>
|
||||
<ViewHeader title={_(msg`Notifications`)} canGoBack={false} />
|
||||
<Feed
|
||||
onScroll={onMainScroll}
|
||||
scrollElRef={scrollElRef}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
/>
|
||||
<MainScrollProvider>
|
||||
<Feed
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
scrollElRef={scrollElRef}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
/>
|
||||
</MainScrollProvider>
|
||||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onPressLoadLatest}
|
||||
|
||||
@@ -5,7 +5,8 @@ import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {CenteredView, FlatList} from '../com/util/Views'
|
||||
import {CenteredView} from '../com/util/Views'
|
||||
import {ListRef} from '../com/util/List'
|
||||
import {ScreenHider} from 'view/com/util/moderation/ScreenHider'
|
||||
import {Feed} from 'view/com/posts/Feed'
|
||||
import {ProfileLists} from '../com/lists/ProfileLists'
|
||||
@@ -20,7 +21,6 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {combinedDisplayName} from 'lib/strings/display-names'
|
||||
import {OnScrollHandler} from '#/lib/hooks/useOnMainScroll'
|
||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
@@ -277,103 +277,67 @@ function ProfileScreenLoaded({
|
||||
onPageSelected={onPageSelected}
|
||||
onCurrentPageSelected={onCurrentPageSelected}
|
||||
renderHeader={renderHeader}>
|
||||
{({onScroll, headerHeight, isFocused, isScrolledDown, scrollElRef}) => (
|
||||
{({headerHeight, isFocused, scrollElRef}) => (
|
||||
<FeedSection
|
||||
ref={postsSectionRef}
|
||||
feed={`author|${profile.did}|posts_and_author_threads`}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
isScrolledDown={isScrolledDown}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
/>
|
||||
)}
|
||||
{showRepliesTab
|
||||
? ({
|
||||
onScroll,
|
||||
headerHeight,
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
scrollElRef,
|
||||
}) => (
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<FeedSection
|
||||
ref={repliesSectionRef}
|
||||
feed={`author|${profile.did}|posts_with_replies`}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
isScrolledDown={isScrolledDown}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{({onScroll, headerHeight, isFocused, isScrolledDown, scrollElRef}) => (
|
||||
{({headerHeight, isFocused, scrollElRef}) => (
|
||||
<FeedSection
|
||||
ref={mediaSectionRef}
|
||||
feed={`author|${profile.did}|posts_with_media`}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
isScrolledDown={isScrolledDown}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
/>
|
||||
)}
|
||||
{showLikesTab
|
||||
? ({
|
||||
onScroll,
|
||||
headerHeight,
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
scrollElRef,
|
||||
}) => (
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<FeedSection
|
||||
ref={likesSectionRef}
|
||||
feed={`likes|${profile.did}`}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
isScrolledDown={isScrolledDown}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showFeedsTab
|
||||
? ({onScroll, headerHeight, isFocused, scrollElRef}) => (
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedgens
|
||||
ref={feedsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showListsTab
|
||||
? ({onScroll, headerHeight, isFocused, scrollElRef}) => (
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLists
|
||||
ref={listsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
/>
|
||||
@@ -396,28 +360,19 @@ function ProfileScreenLoaded({
|
||||
|
||||
interface FeedSectionProps {
|
||||
feed: FeedDescriptor
|
||||
onScroll: OnScrollHandler
|
||||
headerHeight: number
|
||||
isFocused: boolean
|
||||
isScrolledDown: boolean
|
||||
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||
scrollElRef: ListRef
|
||||
ignoreFilterFor?: string
|
||||
}
|
||||
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
function FeedSectionImpl(
|
||||
{
|
||||
feed,
|
||||
onScroll,
|
||||
headerHeight,
|
||||
isFocused,
|
||||
isScrolledDown,
|
||||
scrollElRef,
|
||||
ignoreFilterFor,
|
||||
},
|
||||
{feed, headerHeight, isFocused, scrollElRef, ignoreFilterFor},
|
||||
ref,
|
||||
) {
|
||||
const queryClient = useQueryClient()
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
|
||||
const onScrollToTop = React.useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({
|
||||
@@ -443,8 +398,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
feed={feed}
|
||||
scrollElRef={scrollElRef}
|
||||
onHasNew={setHasNew}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
renderEmptyState={renderPostsEmpty}
|
||||
headerOffset={headerHeight}
|
||||
renderEndOfFeed={ProfileEndOfFeed}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import React, {useMemo, useCallback} from 'react'
|
||||
import {
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
View,
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
} from 'react-native'
|
||||
import {Dimensions, StyleSheet, View, ActivityIndicator} from 'react-native'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -20,6 +14,7 @@ import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
||||
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
||||
import {Feed} from 'view/com/posts/Feed'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {ListRef} from 'view/com/util/List'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {RichText} from 'view/com/util/text/RichText'
|
||||
@@ -29,12 +24,13 @@ import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown'
|
||||
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {makeCustomFeedLink} from 'lib/routes/links'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {CenteredView, ScrollView} from 'view/com/util/Views'
|
||||
@@ -46,7 +42,6 @@ import {logger} from '#/logger'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
import {
|
||||
useFeedSourceInfoQuery,
|
||||
FeedSourceFeedInfo,
|
||||
@@ -403,17 +398,13 @@ export function ProfileFeedScreenInner({
|
||||
isHeaderReady={true}
|
||||
renderHeader={renderHeader}
|
||||
onCurrentPageSelected={onCurrentPageSelected}>
|
||||
{({onScroll, headerHeight, isScrolledDown, scrollElRef, isFocused}) =>
|
||||
{({headerHeight, scrollElRef, isFocused}) =>
|
||||
isPublicResponse?.isPublic ? (
|
||||
<FeedSection
|
||||
ref={feedSectionRef}
|
||||
feed={`feedgen|${feedInfo.uri}`}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isScrolledDown={isScrolledDown}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
isFocused={isFocused}
|
||||
/>
|
||||
) : (
|
||||
@@ -422,13 +413,12 @@ export function ProfileFeedScreenInner({
|
||||
</CenteredView>
|
||||
)
|
||||
}
|
||||
{({onScroll, headerHeight, scrollElRef}) => (
|
||||
{({headerHeight, scrollElRef}) => (
|
||||
<AboutSection
|
||||
feedOwnerDid={feedInfo.creatorDid}
|
||||
feedRkey={feedInfo.route.params.rkey}
|
||||
feedInfo={feedInfo}
|
||||
headerHeight={headerHeight}
|
||||
onScroll={onScroll}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<ScrollView | null>
|
||||
}
|
||||
@@ -497,18 +487,14 @@ function NonPublicFeedMessage({rawError}: {rawError?: Error}) {
|
||||
|
||||
interface FeedSectionProps {
|
||||
feed: FeedDescriptor
|
||||
onScroll: OnScrollHandler
|
||||
headerHeight: number
|
||||
isScrolledDown: boolean
|
||||
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||
scrollElRef: ListRef
|
||||
isFocused: boolean
|
||||
}
|
||||
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
function FeedSectionImpl(
|
||||
{feed, onScroll, headerHeight, isScrolledDown, scrollElRef, isFocused},
|
||||
ref,
|
||||
) {
|
||||
function FeedSectionImpl({feed, headerHeight, scrollElRef, isFocused}, ref) {
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const onScrollToTop = useCallback(() => {
|
||||
@@ -536,8 +522,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
pollInterval={30e3}
|
||||
scrollElRef={scrollElRef}
|
||||
onHasNew={setHasNew}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={5}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
renderEmptyState={renderPostsEmpty}
|
||||
headerOffset={headerHeight}
|
||||
/>
|
||||
@@ -558,7 +543,6 @@ function AboutSection({
|
||||
feedRkey,
|
||||
feedInfo,
|
||||
headerHeight,
|
||||
onScroll,
|
||||
scrollElRef,
|
||||
isOwner,
|
||||
}: {
|
||||
@@ -566,13 +550,13 @@ function AboutSection({
|
||||
feedRkey: string
|
||||
feedInfo: FeedSourceFeedInfo
|
||||
headerHeight: number
|
||||
onScroll: OnScrollHandler
|
||||
scrollElRef: React.MutableRefObject<ScrollView | null>
|
||||
isOwner: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const scrollHandler = useAnimatedScrollHandler(onScroll)
|
||||
const scrollHandlers = useScrollHandlers()
|
||||
const onScroll = useAnimatedScrollHandler(scrollHandlers)
|
||||
const [likeUri, setLikeUri] = React.useState(feedInfo.likeUri)
|
||||
const {hasSession} = useSession()
|
||||
const {track} = useAnalytics()
|
||||
@@ -608,12 +592,12 @@ function AboutSection({
|
||||
return (
|
||||
<ScrollView
|
||||
ref={scrollElRef}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1}
|
||||
contentContainerStyle={{
|
||||
paddingTop: headerHeight,
|
||||
minHeight: Dimensions.get('window').height * 1.5,
|
||||
}}
|
||||
onScroll={scrollHandler}>
|
||||
}}>
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import React, {useCallback, useMemo} from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {ActivityIndicator, Pressable, StyleSheet, View} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
@@ -22,6 +16,7 @@ import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import {RichText} from 'view/com/util/text/RichText'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {ListRef} from 'view/com/util/List'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||
import {FAB} from 'view/com/util/fab/FAB'
|
||||
@@ -31,7 +26,6 @@ import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
@@ -165,36 +159,22 @@ function ProfileListScreenLoaded({
|
||||
isHeaderReady={true}
|
||||
renderHeader={renderHeader}
|
||||
onCurrentPageSelected={onCurrentPageSelected}>
|
||||
{({
|
||||
onScroll,
|
||||
headerHeight,
|
||||
isScrolledDown,
|
||||
scrollElRef,
|
||||
isFocused,
|
||||
}) => (
|
||||
{({headerHeight, scrollElRef, isFocused}) => (
|
||||
<FeedSection
|
||||
ref={feedSectionRef}
|
||||
feed={`list|${uri}`}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
onScroll={onScroll}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerHeight={headerHeight}
|
||||
isScrolledDown={isScrolledDown}
|
||||
isFocused={isFocused}
|
||||
/>
|
||||
)}
|
||||
{({onScroll, headerHeight, isScrolledDown, scrollElRef}) => (
|
||||
{({headerHeight, scrollElRef}) => (
|
||||
<AboutSection
|
||||
ref={aboutSectionRef}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
list={list}
|
||||
onPressAddUser={onPressAddUser}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isScrolledDown={isScrolledDown}
|
||||
/>
|
||||
)}
|
||||
</PagerWithHeader>
|
||||
@@ -221,16 +201,12 @@ function ProfileListScreenLoaded({
|
||||
items={SECTION_TITLES_MOD}
|
||||
isHeaderReady={true}
|
||||
renderHeader={renderHeader}>
|
||||
{({onScroll, headerHeight, isScrolledDown, scrollElRef}) => (
|
||||
{({headerHeight, scrollElRef}) => (
|
||||
<AboutSection
|
||||
list={list}
|
||||
scrollElRef={
|
||||
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||
}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
onPressAddUser={onPressAddUser}
|
||||
onScroll={onScroll}
|
||||
headerHeight={headerHeight}
|
||||
isScrolledDown={isScrolledDown}
|
||||
/>
|
||||
)}
|
||||
</PagerWithHeader>
|
||||
@@ -615,19 +591,15 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) {
|
||||
|
||||
interface FeedSectionProps {
|
||||
feed: FeedDescriptor
|
||||
onScroll: OnScrollHandler
|
||||
headerHeight: number
|
||||
isScrolledDown: boolean
|
||||
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||
scrollElRef: ListRef
|
||||
isFocused: boolean
|
||||
}
|
||||
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
function FeedSectionImpl(
|
||||
{feed, scrollElRef, onScroll, headerHeight, isScrolledDown, isFocused},
|
||||
ref,
|
||||
) {
|
||||
function FeedSectionImpl({feed, scrollElRef, headerHeight, isFocused}, ref) {
|
||||
const queryClient = useQueryClient()
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
|
||||
const onScrollToTop = useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({
|
||||
@@ -654,8 +626,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
pollInterval={30e3}
|
||||
scrollElRef={scrollElRef}
|
||||
onHasNew={setHasNew}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
renderEmptyState={renderPostsEmpty}
|
||||
headerOffset={headerHeight}
|
||||
/>
|
||||
@@ -674,20 +645,19 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||
interface AboutSectionProps {
|
||||
list: AppBskyGraphDefs.ListView
|
||||
onPressAddUser: () => void
|
||||
onScroll: OnScrollHandler
|
||||
headerHeight: number
|
||||
isScrolledDown: boolean
|
||||
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||
scrollElRef: ListRef
|
||||
}
|
||||
const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
||||
function AboutSectionImpl(
|
||||
{list, onPressAddUser, onScroll, headerHeight, isScrolledDown, scrollElRef},
|
||||
{list, onPressAddUser, headerHeight, scrollElRef},
|
||||
ref,
|
||||
) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {currentAccount} = useSession()
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const isCurateList = list.purpose === 'app.bsky.graph.defs#curatelist'
|
||||
const isOwner = list.creator.did === currentAccount?.did
|
||||
|
||||
@@ -817,8 +787,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
||||
renderHeader={renderHeader}
|
||||
renderEmptyState={renderEmptyState}
|
||||
headerOffset={headerHeight}
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
/>
|
||||
{isScrolledDown && (
|
||||
<LoadLatestBtn
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
Pressable,
|
||||
Platform,
|
||||
} from 'react-native'
|
||||
import {FlatList, ScrollView, CenteredView} from '#/view/com/util/Views'
|
||||
import {ScrollView, CenteredView} from '#/view/com/util/Views'
|
||||
import {List} from '#/view/com/util/List'
|
||||
import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -155,7 +156,7 @@ function SearchScreenSuggestedFollows() {
|
||||
}, [currentAccount, setSuggestions, getSuggestedFollowsByActor])
|
||||
|
||||
return suggestions.length ? (
|
||||
<FlatList
|
||||
<List
|
||||
data={suggestions}
|
||||
renderItem={({item}) => <ProfileCardWithFollowBtn profile={item} noBg />}
|
||||
keyExtractor={item => item.did}
|
||||
@@ -243,7 +244,7 @@ function SearchScreenPostResults({query}: {query: string}) {
|
||||
{isFetched ? (
|
||||
<>
|
||||
{posts.length ? (
|
||||
<FlatList
|
||||
<List
|
||||
data={items}
|
||||
renderItem={({item}) => {
|
||||
if (item.type === 'post') {
|
||||
@@ -284,7 +285,7 @@ function SearchScreenUserResults({query}: {query: string}) {
|
||||
return isFetched && results ? (
|
||||
<>
|
||||
{results.length ? (
|
||||
<FlatList
|
||||
<List
|
||||
data={results}
|
||||
renderItem={({item}) => (
|
||||
<ProfileCardWithFollowBtn profile={item} noBg />
|
||||
|
||||
@@ -15,7 +15,6 @@ import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans, msg, plural} from '@lingui/macro'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useInviteCodesQuery} from '#/state/queries/invites'
|
||||
import {ScrollView} from '#/view/com/util/Views'
|
||||
|
||||
export function DesktopRightNav() {
|
||||
const pal = usePalette('default')
|
||||
@@ -30,77 +29,75 @@ export function DesktopRightNav() {
|
||||
|
||||
return (
|
||||
<View style={[styles.rightNav, pal.view]}>
|
||||
<ScrollView contentContainerStyle={{borderWidth: 0}}>
|
||||
<View style={{paddingVertical: 20}}>
|
||||
<DesktopSearch />
|
||||
<View style={{paddingVertical: 20}}>
|
||||
<DesktopSearch />
|
||||
|
||||
{hasSession && (
|
||||
<View style={{paddingTop: 18, marginBottom: 18}}>
|
||||
<DesktopFeeds />
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View
|
||||
style={[
|
||||
styles.message,
|
||||
{
|
||||
paddingTop: hasSession ? 0 : 18,
|
||||
},
|
||||
]}>
|
||||
{isSandbox ? (
|
||||
<View style={[palError.view, styles.messageLine, s.p10]}>
|
||||
<Text type="md" style={[palError.text, s.bold]}>
|
||||
SANDBOX. Posts and accounts are not permanent.
|
||||
</Text>
|
||||
</View>
|
||||
) : undefined}
|
||||
<View style={[s.flexRow]}>
|
||||
{hasSession && (
|
||||
<>
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href={FEEDBACK_FORM_URL({
|
||||
email: currentAccount?.email,
|
||||
handle: currentAccount?.handle,
|
||||
})}
|
||||
text={_(msg`Feedback`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/privacy-policy"
|
||||
text={_(msg`Privacy`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
</Text>
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/tos"
|
||||
text={_(msg`Terms`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
</Text>
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href={HELP_DESK_URL}
|
||||
text={_(msg`Help`)}
|
||||
/>
|
||||
</View>
|
||||
{hasSession && (
|
||||
<View style={{paddingTop: 18, marginBottom: 18}}>
|
||||
<DesktopFeeds />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{hasSession && <InviteCodes />}
|
||||
<View
|
||||
style={[
|
||||
styles.message,
|
||||
{
|
||||
paddingTop: hasSession ? 0 : 18,
|
||||
},
|
||||
]}>
|
||||
{isSandbox ? (
|
||||
<View style={[palError.view, styles.messageLine, s.p10]}>
|
||||
<Text type="md" style={[palError.text, s.bold]}>
|
||||
SANDBOX. Posts and accounts are not permanent.
|
||||
</Text>
|
||||
</View>
|
||||
) : undefined}
|
||||
<View style={[s.flexRow]}>
|
||||
{hasSession && (
|
||||
<>
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href={FEEDBACK_FORM_URL({
|
||||
email: currentAccount?.email,
|
||||
handle: currentAccount?.handle,
|
||||
})}
|
||||
text={_(msg`Feedback`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/privacy-policy"
|
||||
text={_(msg`Privacy`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
</Text>
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href="https://blueskyweb.xyz/support/tos"
|
||||
text={_(msg`Terms`)}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight}>
|
||||
·
|
||||
</Text>
|
||||
<TextLink
|
||||
type="md"
|
||||
style={pal.link}
|
||||
href={HELP_DESK_URL}
|
||||
text={_(msg`Help`)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{hasSession && <InviteCodes />}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -177,7 +174,8 @@ const styles = StyleSheet.create({
|
||||
// @ts-ignore web only
|
||||
left: 'calc(50vw + 320px)',
|
||||
width: 304,
|
||||
height: '100%',
|
||||
maxHeight: '100%',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
|
||||
message: {
|
||||
|
||||
@@ -48,6 +48,20 @@
|
||||
typed-emitter "^2.1.0"
|
||||
zod "^3.21.4"
|
||||
|
||||
"@atproto/api@^0.7.3":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.7.3.tgz#3224000353619970d5e397a157c6e189e195ef47"
|
||||
integrity sha512-fKU+W+S4kKxClE6IcPBHPZAjcyBYxG28S0FW/bv3T/ZYDkNxGzDV4xuoHOyEDGtB30slltl5U83njuuRZs5xtw==
|
||||
dependencies:
|
||||
"@atproto/common-web" "^0.2.3"
|
||||
"@atproto/lexicon" "^0.3.1"
|
||||
"@atproto/syntax" "^0.1.5"
|
||||
"@atproto/xrpc" "^0.4.1"
|
||||
multiformats "^9.9.0"
|
||||
tlds "^1.234.0"
|
||||
typed-emitter "^2.1.0"
|
||||
zod "^3.21.4"
|
||||
|
||||
"@atproto/aws@^0.1.6":
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.1.6.tgz#c6ecbfd92b325f3c5433688534d47f43358b415b"
|
||||
|
||||
Reference in New Issue
Block a user