Bluesky app fork with some witchin' additions 💫

Merge branch 'main' into d/profile-screen

DS Boyce 4356ff00 54598571

+299 -324
+6 -1
.github/workflows/build-submit-ios.yml
··· 118 fi 119 120 - name: 🚀 Deploy 121 - run: eas submit -p ios --non-interactive --path ios-build/ios/build/Bluesky.ipa 122 123 - name: 🪲 Upload dSYM to Sentry 124 run: >
··· 118 fi 119 120 - name: 🚀 Deploy 121 + env: 122 + PROFILE: ${{ inputs.profile || 'testflight' }} 123 + run: > 124 + eas submit -p ios --non-interactive 125 + --path ios-build/ios/build/Bluesky.ipa 126 + --what-to-test "$([[ "$PROFILE" == "production" ]] && echo "Production build" || echo "TestFlight build")" 127 128 - name: 🪲 Upload dSYM to Sentry 129 run: >
+1 -1
.github/workflows/bundle-deploy-eas-update.yml
··· 242 --local --output build.ipa --non-interactive 243 244 - name: 🚀 Deploy 245 - run: eas submit -p ios --non-interactive --path build.ipa 246 247 - name: ⬇️ Restore Cache 248 id: get-base-commit
··· 242 --local --output build.ipa --non-interactive 243 244 - name: 🚀 Deploy 245 + run: eas submit -p ios --non-interactive --path build.ipa --what-to-test "TestFlight build" 246 247 - name: ⬇️ Restore Cache 248 id: get-base-commit
+1 -1
modules/bottom-sheet/android/build.gradle
··· 44 45 dependencies { 46 implementation project(':expo-modules-core') 47 - implementation 'com.google.android.material:material:1.12.0' 48 implementation "com.facebook.react:react-native:+" 49 }
··· 44 45 dependencies { 46 implementation project(':expo-modules-core') 47 + implementation 'com.google.android.material:material:1.13.0' 48 implementation "com.facebook.react:react-native:+" 49 }
+115 -85
modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
··· 5 import android.view.View 6 import android.view.ViewGroup 7 import android.view.ViewStructure 8 import android.view.accessibility.AccessibilityEvent 9 import android.widget.FrameLayout 10 import androidx.core.view.allViews 11 import com.facebook.react.bridge.LifecycleEventListener 12 import com.facebook.react.bridge.ReactContext ··· 15 import com.facebook.react.uimanager.events.EventDispatcher 16 import com.google.android.material.bottomsheet.BottomSheetBehavior 17 import com.google.android.material.bottomsheet.BottomSheetDialog 18 import expo.modules.kotlin.AppContext 19 import expo.modules.kotlin.viewevent.EventDispatcher 20 import expo.modules.kotlin.views.ExpoView ··· 29 30 private lateinit var dialogRootViewGroup: DialogRootViewGroup 31 private var eventDispatcher: EventDispatcher? = null 32 33 - private val rawScreenHeight = 34 context.resources.displayMetrics.heightPixels 35 .toFloat() 36 - private val safeScreenHeight = (rawScreenHeight - getNavigationBarHeight()).toFloat() 37 38 private fun getNavigationBarHeight(): Int { 39 val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android") 40 return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 41 } 42 43 private val onAttemptDismiss by EventDispatcher() 44 private val onSnapPointChange by EventDispatcher() 45 private val onStateChange by EventDispatcher() 46 47 - // Props 48 var disableDrag = false 49 set(value) { 50 field = value ··· 56 field = value 57 this.dialog?.setCancelable(!value) 58 } 59 var preventExpansion = false 60 61 var minHeight = 0f 62 set(value) { 63 - field = 64 - if (value < 0) { 65 - 0f 66 - } else { 67 - dpToPx(value) 68 - } 69 } 70 71 - var maxHeight = this.safeScreenHeight 72 set(value) { 73 val px = dpToPx(value) 74 - field = 75 - if (px > this.safeScreenHeight) { 76 - this.safeScreenHeight 77 - } else { 78 - px 79 - } 80 } 81 82 private var isOpen: Boolean = false 83 set(value) { 84 field = value 85 - onStateChange( 86 - mapOf( 87 - "state" to if (value) "open" else "closed", 88 - ), 89 - ) 90 } 91 92 private var isOpening: Boolean = false 93 set(value) { 94 field = value 95 if (value) { 96 - onStateChange( 97 - mapOf( 98 - "state" to "opening", 99 - ), 100 - ) 101 } 102 } 103 ··· 105 set(value) { 106 field = value 107 if (value) { 108 - onStateChange( 109 - mapOf( 110 - "state" to "closing", 111 - ), 112 - ) 113 } 114 } 115 116 private var selectedSnapPoint = 0 117 set(value) { 118 if (field == value) return 119 - 120 field = value 121 - onSnapPointChange( 122 - mapOf( 123 - "snapPoint" to value, 124 - ), 125 - ) 126 } 127 - 128 - // Lifecycle 129 130 init { 131 (appContext.reactContext as? ReactContext)?.let { 132 it.addLifecycleEventListener(this) 133 this.eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(it, this.id) 134 - 135 this.dialogRootViewGroup = DialogRootViewGroup(context) 136 this.dialogRootViewGroup.eventDispatcher = this.eventDispatcher 137 } ··· 161 private fun getHalfExpandedRatio(contentHeight: Float): Float = 162 when { 163 // Full height sheets 164 - contentHeight >= safeScreenHeight -> 0.99f 165 - // Medium height sheets (>50% but <100%) 166 - contentHeight >= safeScreenHeight / 2 -> 167 - this.clampRatio(this.getTargetHeight() / safeScreenHeight) 168 - // Small height sheets (<50%) 169 - else -> 170 - this.clampRatio(this.getTargetHeight() / rawScreenHeight) 171 } 172 173 private fun present() { 174 if (this.isOpen || this.isOpening || this.isClosing) return 175 176 val contentHeight = this.getContentHeight() 177 - val dialog = BottomSheetDialog(context) 178 dialog.setContentView(dialogRootViewGroup) 179 dialog.setCancelable(!preventDismiss) 180 dialog.setOnDismissListener { 181 this.isClosing = true 182 this.destroy() 183 } 184 185 val bottomSheet = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet) 186 bottomSheet?.let { 187 it.setBackgroundColor(0) ··· 194 behavior.isDraggable = true 195 behavior.isHideable = true 196 197 - if (contentHeight >= this.safeScreenHeight || this.minHeight >= this.safeScreenHeight) { 198 behavior.state = BottomSheetBehavior.STATE_EXPANDED 199 this.selectedSnapPoint = 2 200 } else { ··· 209 newState: Int, 210 ) { 211 when (newState) { 212 - BottomSheetBehavior.STATE_EXPANDED -> { 213 - selectedSnapPoint = 2 214 - } 215 - BottomSheetBehavior.STATE_COLLAPSED -> { 216 - selectedSnapPoint = 1 217 - } 218 - BottomSheetBehavior.STATE_HALF_EXPANDED -> { 219 - selectedSnapPoint = 1 220 - } 221 - BottomSheetBehavior.STATE_HIDDEN -> { 222 - selectedSnapPoint = 0 223 - } 224 } 225 } 226 ··· 231 }, 232 ) 233 } 234 this.isOpening = true 235 dialog.show() 236 this.dialog = dialog 237 } 238 239 fun updateLayout() { ··· 246 val currentState = behavior.state 247 248 val oldRatio = behavior.halfExpandedRatio 249 - var newRatio = getHalfExpandedRatio(contentHeight) 250 behavior.halfExpandedRatio = newRatio 251 252 - if (contentHeight > this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) { 253 behavior.state = BottomSheetBehavior.STATE_EXPANDED 254 - } else if (contentHeight < this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) { 255 behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED 256 } else if (currentState == BottomSheetBehavior.STATE_HALF_EXPANDED && oldRatio != newRatio) { 257 behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED ··· 279 280 private fun getTargetHeight(): Float { 281 val contentHeight = this.getContentHeight() 282 - val height = 283 - if (contentHeight > maxHeight) { 284 - maxHeight 285 - } else if (contentHeight < minHeight) { 286 - minHeight 287 - } else { 288 - contentHeight 289 - } 290 - return height 291 } 292 293 - private fun clampRatio(ratio: Float): Float { 294 - if (ratio < 0.01) { 295 - return 0.01f 296 - } else if (ratio > 0.99) { 297 - return 0.99f 298 } 299 - return ratio 300 - } 301 302 private fun setDraggable(draggable: Boolean) { 303 val dialog = this.dialog ?: return ··· 322 // View overrides to pass to DialogRootViewGroup instead 323 324 override fun dispatchProvideStructure(structure: ViewStructure?) { 325 - if (structure == null) { 326 - return 327 - } 328 dialogRootViewGroup.dispatchProvideStructure(structure) 329 } 330 ··· 363 // https://stackoverflow.com/questions/11862391/getheight-px-or-dpi 364 fun dpToPx(dp: Float): Float { 365 val displayMetrics = context.resources.displayMetrics 366 - val px = dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT) 367 - return px 368 } 369 }
··· 5 import android.view.View 6 import android.view.ViewGroup 7 import android.view.ViewStructure 8 + import android.view.Window 9 import android.view.accessibility.AccessibilityEvent 10 import android.widget.FrameLayout 11 + import androidx.core.view.ViewCompat 12 + import androidx.core.view.WindowInsetsCompat 13 + import androidx.core.view.WindowInsetsControllerCompat 14 import androidx.core.view.allViews 15 import com.facebook.react.bridge.LifecycleEventListener 16 import com.facebook.react.bridge.ReactContext ··· 19 import com.facebook.react.uimanager.events.EventDispatcher 20 import com.google.android.material.bottomsheet.BottomSheetBehavior 21 import com.google.android.material.bottomsheet.BottomSheetDialog 22 + import com.google.android.material.internal.EdgeToEdgeUtils 23 import expo.modules.kotlin.AppContext 24 import expo.modules.kotlin.viewevent.EventDispatcher 25 import expo.modules.kotlin.views.ExpoView ··· 34 35 private lateinit var dialogRootViewGroup: DialogRootViewGroup 36 private var eventDispatcher: EventDispatcher? = null 37 + private var isKeyboardVisible: Boolean = false 38 39 + private val screenHeight = 40 context.resources.displayMetrics.heightPixels 41 .toFloat() 42 43 private fun getNavigationBarHeight(): Int { 44 val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android") 45 return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 46 } 47 48 + private fun getStatusBarHeight(): Int { 49 + val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") 50 + return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 51 + } 52 + 53 private val onAttemptDismiss by EventDispatcher() 54 private val onSnapPointChange by EventDispatcher() 55 private val onStateChange by EventDispatcher() 56 57 var disableDrag = false 58 set(value) { 59 field = value ··· 65 field = value 66 this.dialog?.setCancelable(!value) 67 } 68 + 69 var preventExpansion = false 70 71 var minHeight = 0f 72 set(value) { 73 + field = if (value < 0) 0f else dpToPx(value) 74 } 75 76 + var maxHeight = this.screenHeight 77 set(value) { 78 val px = dpToPx(value) 79 + field = if (px > this.screenHeight) this.screenHeight else px 80 } 81 82 private var isOpen: Boolean = false 83 set(value) { 84 field = value 85 + onStateChange(mapOf("state" to if (value) "open" else "closed")) 86 } 87 88 private var isOpening: Boolean = false 89 set(value) { 90 field = value 91 if (value) { 92 + onStateChange(mapOf("state" to "opening")) 93 } 94 } 95 ··· 97 set(value) { 98 field = value 99 if (value) { 100 + onStateChange(mapOf("state" to "closing")) 101 } 102 } 103 104 private var selectedSnapPoint = 0 105 set(value) { 106 if (field == value) return 107 field = value 108 + onSnapPointChange(mapOf("snapPoint" to value)) 109 } 110 111 init { 112 (appContext.reactContext as? ReactContext)?.let { 113 it.addLifecycleEventListener(this) 114 this.eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(it, this.id) 115 this.dialogRootViewGroup = DialogRootViewGroup(context) 116 this.dialogRootViewGroup.eventDispatcher = this.eventDispatcher 117 } ··· 141 private fun getHalfExpandedRatio(contentHeight: Float): Float = 142 when { 143 // Full height sheets 144 + contentHeight >= screenHeight -> 0.99f 145 + else -> this.clampRatio(this.getTargetHeight() / screenHeight) 146 } 147 148 private fun present() { 149 if (this.isOpen || this.isOpening || this.isClosing) return 150 151 val contentHeight = this.getContentHeight() 152 + 153 + var activityWindow: Window? = null 154 + var currentContext = context 155 + while (currentContext != null) { 156 + if (currentContext is android.app.Activity) { 157 + activityWindow = currentContext.window 158 + break 159 + } 160 + currentContext = (currentContext as? android.content.ContextWrapper)?.baseContext 161 + } 162 + 163 + val originalStatusBarAppearance = 164 + activityWindow?.let { window -> 165 + WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightStatusBars 166 + } 167 + val originalNavBarAppearance = 168 + activityWindow?.let { window -> 169 + WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars 170 + } 171 + 172 + val dialog = BottomSheetDialog(context, R.style.EdgeToEdgeBottomSheetDialogTheme) 173 dialog.setContentView(dialogRootViewGroup) 174 dialog.setCancelable(!preventDismiss) 175 + dialog.setDismissWithAnimation(true) 176 dialog.setOnDismissListener { 177 this.isClosing = true 178 this.destroy() 179 } 180 181 + dialog.setOnShowListener { 182 + dialog.window?.let { window -> 183 + val insetsController = WindowInsetsControllerCompat(window, window.decorView) 184 + if (originalNavBarAppearance != null) { 185 + insetsController.isAppearanceLightNavigationBars = originalNavBarAppearance 186 + } 187 + if (originalStatusBarAppearance != null) { 188 + EdgeToEdgeUtils.setLightStatusBar(window, originalStatusBarAppearance) 189 + } 190 + } 191 + } 192 + 193 val bottomSheet = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet) 194 bottomSheet?.let { 195 it.setBackgroundColor(0) ··· 202 behavior.isDraggable = true 203 behavior.isHideable = true 204 205 + if (preventExpansion) { 206 + behavior.maxHeight = (behavior.halfExpandedRatio * screenHeight).toInt() 207 + } else { 208 + behavior.maxHeight = (screenHeight - getStatusBarHeight()).toInt() 209 + } 210 + 211 + val targetHeight = this.getTargetHeight() 212 + val availableHeight = screenHeight - getStatusBarHeight() - getNavigationBarHeight() 213 + val shouldBeExpanded = targetHeight >= availableHeight 214 + 215 + if (shouldBeExpanded) { 216 behavior.state = BottomSheetBehavior.STATE_EXPANDED 217 this.selectedSnapPoint = 2 218 } else { ··· 227 newState: Int, 228 ) { 229 when (newState) { 230 + BottomSheetBehavior.STATE_EXPANDED -> selectedSnapPoint = 2 231 + BottomSheetBehavior.STATE_COLLAPSED -> selectedSnapPoint = 1 232 + BottomSheetBehavior.STATE_HALF_EXPANDED -> selectedSnapPoint = 1 233 + BottomSheetBehavior.STATE_HIDDEN -> selectedSnapPoint = 0 234 } 235 } 236 ··· 241 }, 242 ) 243 } 244 + 245 this.isOpening = true 246 dialog.show() 247 this.dialog = dialog 248 + 249 + ViewCompat.setOnApplyWindowInsetsListener(dialogRootViewGroup) { view, insets -> 250 + val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) 251 + val bottomSheet = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet) 252 + val behavior = bottomSheet?.let { BottomSheetBehavior.from(it) } 253 + 254 + val wasKeyboardVisible = isKeyboardVisible 255 + isKeyboardVisible = imeVisible 256 + 257 + if (imeVisible && behavior?.state == BottomSheetBehavior.STATE_HALF_EXPANDED) { 258 + behavior.state = BottomSheetBehavior.STATE_EXPANDED 259 + } else if (!imeVisible && wasKeyboardVisible) { 260 + updateLayout() 261 + } 262 + insets 263 + } 264 } 265 266 fun updateLayout() { ··· 273 val currentState = behavior.state 274 275 val oldRatio = behavior.halfExpandedRatio 276 + val newRatio = getHalfExpandedRatio(contentHeight) 277 behavior.halfExpandedRatio = newRatio 278 279 + if (preventExpansion) { 280 + behavior.maxHeight = (behavior.halfExpandedRatio * screenHeight).toInt() 281 + } 282 + 283 + val targetHeight = this.getTargetHeight() 284 + val availableHeight = screenHeight - getStatusBarHeight() - getNavigationBarHeight() 285 + val shouldBeExpanded = targetHeight >= availableHeight 286 + 287 + if (isKeyboardVisible) { 288 + if (behavior.state != BottomSheetBehavior.STATE_EXPANDED) { 289 + behavior.state = BottomSheetBehavior.STATE_EXPANDED 290 + } 291 + } else if (shouldBeExpanded && behavior.state != BottomSheetBehavior.STATE_EXPANDED && !preventExpansion) { 292 behavior.state = BottomSheetBehavior.STATE_EXPANDED 293 + } else if (!shouldBeExpanded && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) { 294 behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED 295 } else if (currentState == BottomSheetBehavior.STATE_HALF_EXPANDED && oldRatio != newRatio) { 296 behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED ··· 318 319 private fun getTargetHeight(): Float { 320 val contentHeight = this.getContentHeight() 321 + return when { 322 + contentHeight > maxHeight -> maxHeight 323 + contentHeight < minHeight -> minHeight 324 + else -> contentHeight 325 + } 326 } 327 328 + private fun clampRatio(ratio: Float): Float = 329 + when { 330 + ratio < 0.01 -> 0.01f 331 + ratio > 0.99 -> 0.99f 332 + else -> ratio 333 } 334 335 private fun setDraggable(draggable: Boolean) { 336 val dialog = this.dialog ?: return ··· 355 // View overrides to pass to DialogRootViewGroup instead 356 357 override fun dispatchProvideStructure(structure: ViewStructure?) { 358 + if (structure == null) return 359 dialogRootViewGroup.dispatchProvideStructure(structure) 360 } 361 ··· 394 // https://stackoverflow.com/questions/11862391/getheight-px-or-dpi 395 fun dpToPx(dp: Float): Float { 396 val displayMetrics = context.resources.displayMetrics 397 + return dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT) 398 } 399 }
+2
modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/DialogRootViewGroup.kt
··· 52 if (ReactFeatureFlags.dispatchPointerEvents) { 53 jSPointerDispatcher = JSPointerDispatcher(this) 54 } 55 } 56 57 override fun onSizeChanged(
··· 52 if (ReactFeatureFlags.dispatchPointerEvents) { 53 jSPointerDispatcher = JSPointerDispatcher(this) 54 } 55 + 56 + fitsSystemWindows = false 57 } 58 59 override fun onSizeChanged(
+20
modules/bottom-sheet/android/src/main/res/values/styles.xml
···
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <resources> 3 + <style name="EdgeToEdgeBottomSheetDialogTheme" parent="Theme.Material3.DayNight.BottomSheetDialog"> 4 + <!-- Enable edge-to-edge --> 5 + <item name="android:navigationBarColor">@android:color/transparent</item> 6 + <item name="android:statusBarColor">@android:color/transparent</item> 7 + <item name="android:windowIsFloating">false</item> 8 + <item name="enableEdgeToEdge">true</item> 9 + 10 + <!-- Configure bottom sheet to respect system window insets --> 11 + <item name="bottomSheetStyle">@style/EdgeToEdgeBottomSheet</item> 12 + </style> 13 + 14 + <style name="EdgeToEdgeBottomSheet" parent="Widget.Material3.BottomSheet"> 15 + <item name="paddingBottomSystemWindowInsets">false</item> 16 + <item name="paddingLeftSystemWindowInsets">true</item> 17 + <item name="paddingRightSystemWindowInsets">true</item> 18 + <item name="paddingTopSystemWindowInsets">false</item> 19 + </style> 20 + </resources>
+1
modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
··· 175 Platform.OS === 'android' && { 176 borderTopLeftRadius: cornerRadius, 177 borderTopRightRadius: cornerRadius, 178 }, 179 extraStyles, 180 ]}>
··· 175 Platform.OS === 'android' && { 176 borderTopLeftRadius: cornerRadius, 177 borderTopRightRadius: cornerRadius, 178 + overflow: 'hidden', 179 }, 180 extraStyles, 181 ]}>
+3
modules/expo-background-notification-handler/android/src/main/java/expo/modules/backgroundnotificationhandler/NotificationPrefs.kt
··· 34 is Boolean -> { 35 putBoolean(key, value) 36 } 37 is String -> { 38 putString(key, value) 39 } 40 is Array<*> -> { 41 putStringSet(key, value.map { it.toString() }.toSet()) 42 } 43 is Map<*, *> -> { 44 putStringSet(key, value.map { it.toString() }.toSet()) 45 }
··· 34 is Boolean -> { 35 putBoolean(key, value) 36 } 37 + 38 is String -> { 39 putString(key, value) 40 } 41 + 42 is Array<*> -> { 43 putStringSet(key, value.map { it.toString() }.toSet()) 44 } 45 + 46 is Map<*, *> -> { 47 putStringSet(key, value.map { it.toString() }.toSet()) 48 }
+2 -2
modules/expo-receive-android-intents/android/src/main/java/xyz/blueskyweb/app/exporeceiveandroidintents/ExpoReceiveAndroidIntentsModule.kt
··· 117 118 private fun handleImageIntents( 119 uris: List<Uri>, 120 - text: String? 121 ) { 122 var allParams = "" 123 ··· 145 146 private fun handleVideoIntents( 147 uris: List<Uri>, 148 - text: String? 149 ) { 150 val uri = uris[0] 151 // If there is no extension for the file, substringAfterLast returns the original string - not
··· 117 118 private fun handleImageIntents( 119 uris: List<Uri>, 120 + text: String?, 121 ) { 122 var allParams = "" 123 ··· 145 146 private fun handleVideoIntents( 147 uris: List<Uri>, 148 + text: String?, 149 ) { 150 val uri = uris[0] 151 // If there is no extension for the file, substringAfterLast returns the original string - not
+1 -1
package.json
··· 202 "react-native-edge-to-edge": "^1.6.0", 203 "react-native-gesture-handler": "~2.28.0", 204 "react-native-get-random-values": "~1.11.0", 205 - "react-native-keyboard-controller": "1.18.5", 206 "react-native-pager-view": "6.8.0", 207 "react-native-progress": "bluesky-social/react-native-progress", 208 "react-native-qrcode-styled": "^0.3.3",
··· 202 "react-native-edge-to-edge": "^1.6.0", 203 "react-native-gesture-handler": "~2.28.0", 204 "react-native-get-random-values": "~1.11.0", 205 + "react-native-keyboard-controller": "^1.20.7", 206 "react-native-pager-view": "6.8.0", 207 "react-native-progress": "bluesky-social/react-native-progress", 208 "react-native-qrcode-styled": "^0.3.3",
+1 -1
src/App.native.tsx
··· 3 4 import React, {useEffect, useState} from 'react' 5 import {GestureHandlerRootView} from 'react-native-gesture-handler' 6 import { 7 initialWindowMetrics, 8 SafeAreaProvider, ··· 14 import {useLingui} from '@lingui/react' 15 import * as Sentry from '@sentry/react-native' 16 17 - import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController' 18 import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder' 19 import {QueryProvider} from '#/lib/react-query' 20 import {s} from '#/lib/styles'
··· 3 4 import React, {useEffect, useState} from 'react' 5 import {GestureHandlerRootView} from 'react-native-gesture-handler' 6 + import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller' 7 import { 8 initialWindowMetrics, 9 SafeAreaProvider, ··· 15 import {useLingui} from '@lingui/react' 16 import * as Sentry from '@sentry/react-native' 17 18 import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder' 19 import {QueryProvider} from '#/lib/react-query' 20 import {s} from '#/lib/styles'
+3 -7
src/components/Dialog/index.tsx
··· 11 } from 'react-native' 12 import { 13 KeyboardAwareScrollView, 14 useKeyboardHandler, 15 useReanimatedKeyboardAnimation, 16 } from 'react-native-keyboard-controller' ··· 23 import {msg} from '@lingui/macro' 24 import {useLingui} from '@lingui/react' 25 26 - import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController' 27 import {ScrollProvider} from '#/lib/ScrollContext' 28 import {logger} from '#/logger' 29 import {useA11y} from '#/state/a11y' ··· 209 const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() 210 const insets = useSafeAreaInsets() 211 212 - useEnableKeyboardController(IS_IOS) 213 - 214 const [keyboardHeight, setKeyboardHeight] = React.useState(0) 215 216 useKeyboardHandler( 217 { 218 onEnd: e => { ··· 231 } 232 paddingBottom = Math.max(paddingBottom, tokens.space._2xl) 233 } else { 234 - paddingBottom += keyboardHeight 235 if (nativeSnapPoint === BottomSheetSnapPoint.Full) { 236 paddingBottom += insets.top 237 } ··· 259 {paddingBottom}, 260 contentContainerStyle, 261 ]} 262 - ref={ref} 263 showsVerticalScrollIndicator={IS_ANDROID ? false : undefined} 264 {...props} 265 bounces={nativeSnapPoint === BottomSheetSnapPoint.Full} ··· 288 >(function InnerFlatList({footer, style, ...props}, ref) { 289 const insets = useSafeAreaInsets() 290 const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() 291 - 292 - useEnableKeyboardController(IS_IOS) 293 294 const onScroll = (e: ScrollEvent) => { 295 'worklet'
··· 11 } from 'react-native' 12 import { 13 KeyboardAwareScrollView, 14 + type KeyboardAwareScrollViewRef, 15 useKeyboardHandler, 16 useReanimatedKeyboardAnimation, 17 } from 'react-native-keyboard-controller' ··· 24 import {msg} from '@lingui/macro' 25 import {useLingui} from '@lingui/react' 26 27 import {ScrollProvider} from '#/lib/ScrollContext' 28 import {logger} from '#/logger' 29 import {useA11y} from '#/state/a11y' ··· 209 const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() 210 const insets = useSafeAreaInsets() 211 212 const [keyboardHeight, setKeyboardHeight] = React.useState(0) 213 214 + // note: iOS-only. keyboard-controller doesn't seem to work inside the sheets on Android 215 useKeyboardHandler( 216 { 217 onEnd: e => { ··· 230 } 231 paddingBottom = Math.max(paddingBottom, tokens.space._2xl) 232 } else { 233 if (nativeSnapPoint === BottomSheetSnapPoint.Full) { 234 paddingBottom += insets.top 235 } ··· 257 {paddingBottom}, 258 contentContainerStyle, 259 ]} 260 + ref={ref as React.Ref<KeyboardAwareScrollViewRef>} 261 showsVerticalScrollIndicator={IS_ANDROID ? false : undefined} 262 {...props} 263 bounces={nativeSnapPoint === BottomSheetSnapPoint.Full} ··· 286 >(function InnerFlatList({footer, style, ...props}, ref) { 287 const insets = useSafeAreaInsets() 288 const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() 289 290 const onScroll = (e: ScrollEvent) => { 291 'worklet'
+4 -2
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx
··· 58 <BlueskyVideoView 59 url={embed.playlist} 60 autoplay={!autoplayDisabled && !isWithinMessage} 61 - beginMuted={isGif || autoplayDisabled ? false : muted} 62 style={[a.rounded_sm]} 63 onActiveChange={e => { 64 setIsActive(e.nativeEvent.isActive) ··· 67 setIsLoading(e.nativeEvent.isLoading) 68 }} 69 onMutedChange={e => { 70 - setMuted(e.nativeEvent.isMuted) 71 }} 72 onStatusChange={e => { 73 setStatus(e.nativeEvent.status)
··· 58 <BlueskyVideoView 59 url={embed.playlist} 60 autoplay={!autoplayDisabled && !isWithinMessage} 61 + beginMuted={isGif || (autoplayDisabled ? false : muted)} 62 style={[a.rounded_sm]} 63 onActiveChange={e => { 64 setIsActive(e.nativeEvent.isActive) ··· 67 setIsLoading(e.nativeEvent.isLoading) 68 }} 69 onMutedChange={e => { 70 + if (!isGif) { 71 + setMuted(e.nativeEvent.isMuted) 72 + } 73 }} 74 onStatusChange={e => { 75 setStatus(e.nativeEvent.status)
+1 -3
src/components/verification/VerifierDialog.tsx
··· 28 verificationState: FullVerificationState 29 }) { 30 return ( 31 - <Dialog.Outer control={control}> 32 <Dialog.Handle /> 33 <Inner 34 control={control} ··· 123 }), 124 )} 125 size="small" 126 - variant="solid" 127 color="primary" 128 style={[a.justify_center]} 129 onPress={() => { ··· 138 <Button 139 label={_(msg`Close dialog`)} 140 size="small" 141 - variant="solid" 142 color="secondary" 143 onPress={() => { 144 control.close()
··· 28 verificationState: FullVerificationState 29 }) { 30 return ( 31 + <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 32 <Dialog.Handle /> 33 <Inner 34 control={control} ··· 123 }), 124 )} 125 size="small" 126 color="primary" 127 style={[a.justify_center]} 128 onPress={() => { ··· 137 <Button 138 label={_(msg`Close dialog`)} 139 size="small" 140 color="secondary" 141 onPress={() => { 142 control.close()
-107
src/lib/hooks/useEnableKeyboardController.tsx
··· 1 - import { 2 - createContext, 3 - useCallback, 4 - useContext, 5 - useEffect, 6 - useMemo, 7 - useRef, 8 - } from 'react' 9 - import { 10 - KeyboardProvider, 11 - useKeyboardController, 12 - } from 'react-native-keyboard-controller' 13 - import {useFocusEffect} from '@react-navigation/native' 14 - 15 - const KeyboardControllerRefCountContext = createContext<{ 16 - incrementRefCount: () => void 17 - decrementRefCount: () => void 18 - }>({ 19 - incrementRefCount: () => {}, 20 - decrementRefCount: () => {}, 21 - }) 22 - KeyboardControllerRefCountContext.displayName = 23 - 'KeyboardControllerRefCountContext' 24 - 25 - export function KeyboardControllerProvider({ 26 - children, 27 - }: { 28 - children: React.ReactNode 29 - }) { 30 - return ( 31 - <KeyboardProvider enabled={false} preload={false}> 32 - <KeyboardControllerProviderInner> 33 - {children} 34 - </KeyboardControllerProviderInner> 35 - </KeyboardProvider> 36 - ) 37 - } 38 - 39 - function KeyboardControllerProviderInner({ 40 - children, 41 - }: { 42 - children: React.ReactNode 43 - }) { 44 - const {setEnabled} = useKeyboardController() 45 - const refCount = useRef(0) 46 - 47 - const value = useMemo( 48 - () => ({ 49 - incrementRefCount: () => { 50 - refCount.current++ 51 - setEnabled(refCount.current > 0) 52 - }, 53 - decrementRefCount: () => { 54 - refCount.current-- 55 - setEnabled(refCount.current > 0) 56 - 57 - if (__DEV__ && refCount.current < 0) { 58 - console.error('KeyboardController ref count < 0') 59 - } 60 - }, 61 - }), 62 - [setEnabled], 63 - ) 64 - 65 - return ( 66 - <KeyboardControllerRefCountContext.Provider value={value}> 67 - {children} 68 - </KeyboardControllerRefCountContext.Provider> 69 - ) 70 - } 71 - 72 - export function useEnableKeyboardController(shouldEnable: boolean) { 73 - const {incrementRefCount, decrementRefCount} = useContext( 74 - KeyboardControllerRefCountContext, 75 - ) 76 - 77 - useEffect(() => { 78 - if (!shouldEnable) { 79 - return 80 - } 81 - incrementRefCount() 82 - return () => { 83 - decrementRefCount() 84 - } 85 - }, [shouldEnable, incrementRefCount, decrementRefCount]) 86 - } 87 - 88 - /** 89 - * Like `useEnableKeyboardController`, but using `useFocusEffect` 90 - */ 91 - export function useEnableKeyboardControllerScreen(shouldEnable: boolean) { 92 - const {incrementRefCount, decrementRefCount} = useContext( 93 - KeyboardControllerRefCountContext, 94 - ) 95 - 96 - useFocusEffect( 97 - useCallback(() => { 98 - if (!shouldEnable) { 99 - return 100 - } 101 - incrementRefCount() 102 - return () => { 103 - decrementRefCount() 104 - } 105 - }, [shouldEnable, incrementRefCount, decrementRefCount]), 106 - ) 107 - }
···
+64 -64
src/locale/locales/en/messages.po
··· 129 130 #. Number of users (always at least 25) who have joined Bluesky using a specific starter pack 131 #: src/screens/StarterPack/StarterPackScreen.tsx:499 132 - msgid "{0, plural, other {# people have}} used this starter pack!" 133 msgstr "" 134 135 #: src/components/dialogs/StarterPackDialog.tsx:361 ··· 242 msgstr "" 243 244 #: src/lib/generate-starterpack.ts:104 245 - #: src/screens/StarterPack/Wizard/index.tsx:202 246 msgid "{displayName}'s Starter Pack" 247 msgstr "" 248 ··· 475 msgid "+{computedTotal}" 476 msgstr "" 477 478 - #: src/screens/StarterPack/Wizard/index.tsx:525 479 msgctxt "profiles" 480 msgid "<0>{0}, </0><1>{1}, </1>and {2, plural, one {# other} other {# others}} are included in your starter pack" 481 msgstr "" 482 483 - #: src/screens/StarterPack/Wizard/index.tsx:578 484 msgctxt "feeds" 485 msgid "<0>{0}, </0><1>{1}, </1>and {2, plural, one {# other} other {# others}} are included in your starter pack" 486 msgstr "" ··· 493 msgid "<0>{0}</0> {1, plural, one {following} other {following}}" 494 msgstr "" 495 496 - #: src/screens/StarterPack/Wizard/index.tsx:512 497 - #: src/screens/StarterPack/Wizard/index.tsx:566 498 msgid "<0>{0}</0> and<1> </1><2>{1} </2>are included in your starter pack" 499 msgstr "" 500 501 - #: src/screens/StarterPack/Wizard/index.tsx:559 502 msgid "<0>{0}</0> is included in your starter pack" 503 msgstr "" 504 ··· 515 msgid "<0>Sign in</0><1> or </1><2>create an account</2><3> </3><4>to search for news, sports, politics, and everything else happening on Bluesky.</4>" 516 msgstr "" 517 518 - #: src/screens/StarterPack/Wizard/index.tsx:503 519 msgid "<0>You</0> and<1> </1><2>{0} </2>are included in your starter pack" 520 msgstr "" 521 ··· 581 582 #. Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English. 583 #: src/components/dialogs/nuxs/DraftsAnnouncement.tsx:97 584 - msgid "A screenshot of a the post composer with a new button next to the post button that says \"Drafts\", with a rainbow firework effect. Below, the text in the composer reads \"Hey, did you hear the news? Bluesky has drafts now???\"." 585 msgstr "" 586 587 #: src/Navigation.tsx:535 ··· 710 msgid "Add" 711 msgstr "" 712 713 - #: src/screens/StarterPack/Wizard/index.tsx:614 714 msgid "Add {0} more to continue" 715 msgstr "" 716 ··· 742 #: src/view/com/composer/GifAltText.tsx:211 743 #: src/view/com/composer/photos/Gallery.tsx:170 744 #: src/view/com/composer/photos/Gallery.tsx:217 745 - #: src/view/com/composer/photos/ImageAltTextDialog.tsx:88 746 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:93 747 msgid "Add alt text" 748 msgstr "" 749 ··· 819 msgid "Add recommended feeds" 820 msgstr "" 821 822 - #: src/screens/StarterPack/Wizard/index.tsx:547 823 msgid "Add some feeds to your starter pack!" 824 msgstr "" 825 ··· 1029 1030 #: src/screens/Settings/AccessibilitySettings.tsx:54 1031 #: src/view/com/composer/GifAltText.tsx:154 1032 - #: src/view/com/composer/photos/ImageAltTextDialog.tsx:117 1033 #: src/view/com/composer/videos/SubtitleDialog.tsx:40 1034 #: src/view/com/composer/videos/SubtitleDialog.tsx:58 1035 #: src/view/com/composer/videos/SubtitleDialog.tsx:109 ··· 1050 msgstr "" 1051 1052 #: src/view/com/composer/GifAltText.tsx:179 1053 - #: src/view/com/composer/photos/ImageAltTextDialog.tsx:138 1054 msgid "Alt text will be truncated. {MAX_ALT_TEXT, plural, other {Limit: {0} characters.}}" 1055 msgstr "" 1056 ··· 1277 msgid "Appeal submitted" 1278 msgstr "" 1279 1280 - #: src/screens/Takendown.tsx:114 1281 - #: src/screens/Takendown.tsx:142 1282 msgid "Appeal suspension" 1283 msgstr "" 1284 1285 - #: src/screens/Takendown.tsx:117 1286 msgid "Appeal Suspension" 1287 msgstr "" 1288 ··· 1408 #: src/screens/Settings/components/ChangePasswordDialog.tsx:272 1409 #: src/screens/Settings/components/ChangePasswordDialog.tsx:281 1410 #: src/screens/Signup/BackNextButtons.tsx:41 1411 - #: src/screens/StarterPack/Wizard/index.tsx:324 1412 #: src/view/com/composer/drafts/DraftsListDialog.tsx:80 1413 #: src/view/com/composer/drafts/DraftsListDialog.tsx:86 1414 msgid "Back" ··· 1453 #: src/components/dms/dialogs/NewChatDialog.tsx:55 1454 #: src/components/dms/MessageProfileButton.tsx:59 1455 #: src/screens/Messages/ChatList.tsx:371 1456 - #: src/screens/Messages/Conversation.tsx:228 1457 msgid "Before you can message another user, you must first verify your email." 1458 msgstr "" 1459 ··· 1617 msgid "Bluesky is more fun with friends. Do you want to invite some of yours? <0/>" 1618 msgstr "" 1619 1620 - #: src/screens/Takendown.tsx:215 1621 msgid "Bluesky Social Terms of Service" 1622 msgstr "" 1623 ··· 1788 #: src/screens/Settings/components/ChangePasswordDialog.tsx:247 1789 #: src/screens/Settings/components/ChangePasswordDialog.tsx:253 1790 #: src/screens/Settings/Settings.tsx:305 1791 - #: src/screens/Takendown.tsx:102 1792 - #: src/screens/Takendown.tsx:105 1793 #: src/view/com/composer/Composer.tsx:1449 1794 #: src/view/com/composer/Composer.tsx:1461 1795 #: src/view/com/composer/photos/EditImageDialog.web.tsx:43 ··· 1987 msgid "Choose domain verification method" 1988 msgstr "" 1989 1990 - #: src/screens/StarterPack/Wizard/index.tsx:218 1991 msgid "Choose Feeds" 1992 msgstr "" 1993 ··· 1995 msgid "Choose for me" 1996 msgstr "" 1997 1998 - #: src/screens/StarterPack/Wizard/index.tsx:214 1999 msgid "Choose People" 2000 msgstr "" 2001 ··· 2119 #: src/components/StarterPack/Wizard/WizardEditListDialog.tsx:118 2120 #: src/components/StarterPack/Wizard/WizardEditListDialog.tsx:124 2121 #: src/components/verification/VerificationsDialog.tsx:145 2122 - #: src/components/verification/VerifierDialog.tsx:147 2123 #: src/components/WhoCanReply.tsx:235 2124 #: src/components/WhoCanReply.tsx:242 2125 #: src/screens/Settings/components/ChangePasswordDialog.tsx:287 ··· 2147 #: src/components/ageAssurance/AgeAssuranceInitDialog.tsx:228 2148 #: src/components/dialogs/GifSelect.tsx:263 2149 #: src/components/verification/VerificationsDialog.tsx:137 2150 - #: src/components/verification/VerifierDialog.tsx:139 2151 #: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:204 2152 #: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:298 2153 #: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:330 ··· 2433 msgid "Continue to next step" 2434 msgstr "" 2435 2436 - #: src/screens/Messages/Conversation.tsx:57 2437 msgid "Conversation" 2438 msgstr "" 2439 ··· 2926 msgstr "" 2927 2928 #: src/view/com/composer/GifAltText.tsx:150 2929 - #: src/view/com/composer/photos/ImageAltTextDialog.tsx:113 2930 msgid "Descriptive alt text" 2931 msgstr "" 2932 ··· 3040 msgid "Discover New Feeds" 3041 msgstr "" 3042 3043 - #: src/components/Dialog/index.tsx:379 3044 msgid "Dismiss" 3045 msgstr "" 3046 ··· 3159 msgid "Double tap or long press the message to add a reaction" 3160 msgstr "" 3161 3162 - #: src/components/Dialog/index.tsx:380 3163 msgid "Double tap to close the dialog" 3164 msgstr "" 3165 ··· 3238 #: src/screens/Settings/AccountSettings.tsx:145 3239 #: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:252 3240 #: src/screens/StarterPack/StarterPackScreen.tsx:598 3241 - #: src/screens/StarterPack/Wizard/index.tsx:340 3242 - #: src/screens/StarterPack/Wizard/index.tsx:345 3243 msgid "Edit" 3244 msgstr "" 3245 ··· 3726 msgid "Failed to create conversation" 3727 msgstr "" 3728 3729 - #: src/screens/StarterPack/Wizard/index.tsx:263 3730 - #: src/screens/StarterPack/Wizard/index.tsx:271 3731 msgid "Failed to create starter pack" 3732 msgstr "" 3733 ··· 4098 msgid "Finding friends..." 4099 msgstr "" 4100 4101 - #: src/screens/StarterPack/Wizard/index.tsx:219 4102 msgid "Finish" 4103 msgstr "" 4104 ··· 5118 msgid "It's correct" 5119 msgstr "" 5120 5121 - #: src/screens/StarterPack/Wizard/index.tsx:492 5122 msgid "It's just <0>{0} </0>right now! Add more people to your starter pack by searching above." 5123 msgstr "" 5124 5125 - #: src/screens/StarterPack/Wizard/index.tsx:487 5126 msgid "It's just you right now! Add more people to your starter pack by searching above." 5127 msgstr "" 5128 ··· 5219 msgstr "" 5220 5221 #: src/components/verification/VerificationsDialog.tsx:167 5222 - #: src/components/verification/VerifierDialog.tsx:135 5223 #: src/screens/Moderation/VerificationSettings.tsx:49 5224 #: src/screens/Profile/Header/EditProfileDialog.tsx:349 5225 #: src/screens/Settings/components/ChangeHandleDialog.tsx:213 ··· 6140 #: src/screens/Settings/components/AddAppPasswordDialog.tsx:157 6141 #: src/screens/Settings/components/AddAppPasswordDialog.tsx:165 6142 #: src/screens/Signup/BackNextButtons.tsx:67 6143 - #: src/screens/StarterPack/Wizard/index.tsx:211 6144 - #: src/screens/StarterPack/Wizard/index.tsx:215 6145 - #: src/screens/StarterPack/Wizard/index.tsx:393 6146 - #: src/screens/StarterPack/Wizard/index.tsx:400 6147 msgid "Next" 6148 msgstr "" 6149 ··· 6355 msgid "None" 6356 msgstr "" 6357 6358 - #: src/screens/FindContactsFlowScreen.tsx:70 6359 #: src/screens/Settings/FindContactsSettings.tsx:103 6360 msgid "Not available on this platform." 6361 msgstr "" ··· 7138 msgid "Please use the native app to import your contacts." 7139 msgstr "" 7140 7141 - #: src/screens/FindContactsFlowScreen.tsx:71 7142 msgid "Please use the native app to sync your contacts." 7143 msgstr "" 7144 ··· 7556 msgid "Real people." 7557 msgstr "" 7558 7559 - #: src/screens/Takendown.tsx:160 7560 - #: src/screens/Takendown.tsx:168 7561 msgid "Reason for appeal" 7562 msgstr "" 7563 ··· 8132 msgid "Returns to previous page" 8133 msgstr "" 8134 8135 - #: src/screens/StarterPack/Wizard/index.tsx:325 8136 msgid "Returns to the previous step" 8137 msgstr "" 8138 ··· 8152 #: src/view/com/composer/GifAltText.tsx:202 8153 #: src/view/com/composer/photos/EditImageDialog.web.tsx:62 8154 #: src/view/com/composer/photos/EditImageDialog.web.tsx:75 8155 - #: src/view/com/composer/photos/ImageAltTextDialog.tsx:152 8156 - #: src/view/com/composer/photos/ImageAltTextDialog.tsx:162 8157 msgid "Save" 8158 msgstr "" 8159 ··· 8305 msgid "Search for \"{searchText}\"" 8306 msgstr "" 8307 8308 - #: src/screens/StarterPack/Wizard/index.tsx:550 8309 msgid "Search for feeds that you want to suggest to others." 8310 msgstr "" 8311 ··· 9001 #: src/screens/Settings/Settings.tsx:304 9002 #: src/screens/SignupQueued.tsx:93 9003 #: src/screens/SignupQueued.tsx:96 9004 - #: src/screens/Takendown.tsx:88 9005 #: src/view/shell/desktop/LeftNav.tsx:212 9006 #: src/view/shell/desktop/LeftNav.tsx:269 9007 #: src/view/shell/desktop/LeftNav.tsx:272 9008 msgid "Sign out" 9009 msgstr "" 9010 9011 - #: src/screens/Takendown.tsx:91 9012 msgid "Sign Out" 9013 msgstr "" 9014 ··· 9035 #: src/screens/Onboarding/StepFinished/index.tsx:316 9036 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:261 9037 #: src/screens/Onboarding/StepSuggestedStarterpacks/index.tsx:104 9038 - #: src/screens/StarterPack/Wizard/index.tsx:219 9039 msgid "Skip" 9040 msgstr "" 9041 ··· 9098 msgid "Something wasn't quite right with the data you're trying to report. Please contact support." 9099 msgstr "" 9100 9101 - #: src/screens/Messages/Conversation.tsx:144 9102 msgid "Something went wrong" 9103 msgstr "" 9104 ··· 9194 9195 #: src/Navigation.tsx:590 9196 #: src/Navigation.tsx:595 9197 - #: src/screens/StarterPack/Wizard/index.tsx:210 9198 msgid "Starter Pack" 9199 msgstr "" 9200 ··· 9270 msgid "Submit" 9271 msgstr "" 9272 9273 - #: src/screens/Takendown.tsx:76 9274 msgid "Submit appeal" 9275 msgstr "" 9276 9277 - #: src/screens/Takendown.tsx:80 9278 msgid "Submit Appeal" 9279 msgstr "" 9280 ··· 9505 #: src/screens/StarterPack/StarterPackScreen.tsx:113 9506 #: src/screens/StarterPack/StarterPackScreen.tsx:157 9507 #: src/screens/StarterPack/StarterPackScreen.tsx:158 9508 - #: src/screens/StarterPack/Wizard/index.tsx:117 9509 - #: src/screens/StarterPack/Wizard/index.tsx:127 9510 msgid "That starter pack could not be found." 9511 msgstr "" 9512 ··· 10970 msgid "We couldn't find any results for that topic." 10971 msgstr "" 10972 10973 - #: src/screens/Messages/Conversation.tsx:145 10974 msgid "We couldn't load this conversation" 10975 msgstr "" 10976 ··· 11191 msgid "Whoops! Trending videos failed to load." 11192 msgstr "" 11193 11194 - #: src/screens/Takendown.tsx:171 11195 msgid "Why are you appealing?" 11196 msgstr "" 11197 ··· 11751 msgid "Your account has been deleted" 11752 msgstr "" 11753 11754 - #: src/screens/Takendown.tsx:144 11755 msgid "Your account has been suspended" 11756 msgstr "" 11757 ··· 11763 msgid "Your account repository, containing all public data records, can be downloaded as a \"CAR\" file. This file does not include media embeds, such as images, or your private data, which must be fetched separately." 11764 msgstr "" 11765 11766 - #: src/screens/Takendown.tsx:212 11767 msgid "Your account was found to be in violation of the <0>Bluesky Social Terms of Service</0>. You have been sent an email outlining the specific violation and suspension period, if applicable. You can appeal this decision if you believe it was made in error." 11768 msgstr "" 11769 11770 - #: src/screens/Takendown.tsx:152 11771 msgid "Your appeal has been submitted. If your appeal succeeds, you will receive an email." 11772 msgstr "" 11773
··· 129 130 #. Number of users (always at least 25) who have joined Bluesky using a specific starter pack 131 #: src/screens/StarterPack/StarterPackScreen.tsx:499 132 + msgid "{0, plural, other {# people have}} joined Bluesky via this starter pack!" 133 msgstr "" 134 135 #: src/components/dialogs/StarterPackDialog.tsx:361 ··· 242 msgstr "" 243 244 #: src/lib/generate-starterpack.ts:104 245 + #: src/screens/StarterPack/Wizard/index.tsx:199 246 msgid "{displayName}'s Starter Pack" 247 msgstr "" 248 ··· 475 msgid "+{computedTotal}" 476 msgstr "" 477 478 + #: src/screens/StarterPack/Wizard/index.tsx:522 479 msgctxt "profiles" 480 msgid "<0>{0}, </0><1>{1}, </1>and {2, plural, one {# other} other {# others}} are included in your starter pack" 481 msgstr "" 482 483 + #: src/screens/StarterPack/Wizard/index.tsx:575 484 msgctxt "feeds" 485 msgid "<0>{0}, </0><1>{1}, </1>and {2, plural, one {# other} other {# others}} are included in your starter pack" 486 msgstr "" ··· 493 msgid "<0>{0}</0> {1, plural, one {following} other {following}}" 494 msgstr "" 495 496 + #: src/screens/StarterPack/Wizard/index.tsx:509 497 + #: src/screens/StarterPack/Wizard/index.tsx:563 498 msgid "<0>{0}</0> and<1> </1><2>{1} </2>are included in your starter pack" 499 msgstr "" 500 501 + #: src/screens/StarterPack/Wizard/index.tsx:556 502 msgid "<0>{0}</0> is included in your starter pack" 503 msgstr "" 504 ··· 515 msgid "<0>Sign in</0><1> or </1><2>create an account</2><3> </3><4>to search for news, sports, politics, and everything else happening on Bluesky.</4>" 516 msgstr "" 517 518 + #: src/screens/StarterPack/Wizard/index.tsx:500 519 msgid "<0>You</0> and<1> </1><2>{0} </2>are included in your starter pack" 520 msgstr "" 521 ··· 581 582 #. Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English. 583 #: src/components/dialogs/nuxs/DraftsAnnouncement.tsx:97 584 + msgid "A screenshot of the post composer with a new button next to the post button that says \"Drafts\", with a rainbow firework effect. Below, the text in the composer reads \"Hey, did you hear the news? Bluesky has drafts now!!!\"." 585 msgstr "" 586 587 #: src/Navigation.tsx:535 ··· 710 msgid "Add" 711 msgstr "" 712 713 + #: src/screens/StarterPack/Wizard/index.tsx:611 714 msgid "Add {0} more to continue" 715 msgstr "" 716 ··· 742 #: src/view/com/composer/GifAltText.tsx:211 743 #: src/view/com/composer/photos/Gallery.tsx:170 744 #: src/view/com/composer/photos/Gallery.tsx:217 745 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:93 746 + #: src/view/com/composer/photos/ImageAltTextDialog.tsx:98 747 msgid "Add alt text" 748 msgstr "" 749 ··· 819 msgid "Add recommended feeds" 820 msgstr "" 821 822 + #: src/screens/StarterPack/Wizard/index.tsx:544 823 msgid "Add some feeds to your starter pack!" 824 msgstr "" 825 ··· 1029 1030 #: src/screens/Settings/AccessibilitySettings.tsx:54 1031 #: src/view/com/composer/GifAltText.tsx:154 1032 + #: src/view/com/composer/photos/ImageAltTextDialog.tsx:122 1033 #: src/view/com/composer/videos/SubtitleDialog.tsx:40 1034 #: src/view/com/composer/videos/SubtitleDialog.tsx:58 1035 #: src/view/com/composer/videos/SubtitleDialog.tsx:109 ··· 1050 msgstr "" 1051 1052 #: src/view/com/composer/GifAltText.tsx:179 1053 + #: src/view/com/composer/photos/ImageAltTextDialog.tsx:143 1054 msgid "Alt text will be truncated. {MAX_ALT_TEXT, plural, other {Limit: {0} characters.}}" 1055 msgstr "" 1056 ··· 1277 msgid "Appeal submitted" 1278 msgstr "" 1279 1280 + #: src/screens/Takendown.tsx:113 1281 + #: src/screens/Takendown.tsx:139 1282 msgid "Appeal suspension" 1283 msgstr "" 1284 1285 + #: src/screens/Takendown.tsx:116 1286 msgid "Appeal Suspension" 1287 msgstr "" 1288 ··· 1408 #: src/screens/Settings/components/ChangePasswordDialog.tsx:272 1409 #: src/screens/Settings/components/ChangePasswordDialog.tsx:281 1410 #: src/screens/Signup/BackNextButtons.tsx:41 1411 + #: src/screens/StarterPack/Wizard/index.tsx:321 1412 #: src/view/com/composer/drafts/DraftsListDialog.tsx:80 1413 #: src/view/com/composer/drafts/DraftsListDialog.tsx:86 1414 msgid "Back" ··· 1453 #: src/components/dms/dialogs/NewChatDialog.tsx:55 1454 #: src/components/dms/MessageProfileButton.tsx:59 1455 #: src/screens/Messages/ChatList.tsx:371 1456 + #: src/screens/Messages/Conversation.tsx:225 1457 msgid "Before you can message another user, you must first verify your email." 1458 msgstr "" 1459 ··· 1617 msgid "Bluesky is more fun with friends. Do you want to invite some of yours? <0/>" 1618 msgstr "" 1619 1620 + #: src/screens/Takendown.tsx:212 1621 msgid "Bluesky Social Terms of Service" 1622 msgstr "" 1623 ··· 1788 #: src/screens/Settings/components/ChangePasswordDialog.tsx:247 1789 #: src/screens/Settings/components/ChangePasswordDialog.tsx:253 1790 #: src/screens/Settings/Settings.tsx:305 1791 + #: src/screens/Takendown.tsx:101 1792 + #: src/screens/Takendown.tsx:104 1793 #: src/view/com/composer/Composer.tsx:1449 1794 #: src/view/com/composer/Composer.tsx:1461 1795 #: src/view/com/composer/photos/EditImageDialog.web.tsx:43 ··· 1987 msgid "Choose domain verification method" 1988 msgstr "" 1989 1990 + #: src/screens/StarterPack/Wizard/index.tsx:215 1991 msgid "Choose Feeds" 1992 msgstr "" 1993 ··· 1995 msgid "Choose for me" 1996 msgstr "" 1997 1998 + #: src/screens/StarterPack/Wizard/index.tsx:211 1999 msgid "Choose People" 2000 msgstr "" 2001 ··· 2119 #: src/components/StarterPack/Wizard/WizardEditListDialog.tsx:118 2120 #: src/components/StarterPack/Wizard/WizardEditListDialog.tsx:124 2121 #: src/components/verification/VerificationsDialog.tsx:145 2122 + #: src/components/verification/VerifierDialog.tsx:145 2123 #: src/components/WhoCanReply.tsx:235 2124 #: src/components/WhoCanReply.tsx:242 2125 #: src/screens/Settings/components/ChangePasswordDialog.tsx:287 ··· 2147 #: src/components/ageAssurance/AgeAssuranceInitDialog.tsx:228 2148 #: src/components/dialogs/GifSelect.tsx:263 2149 #: src/components/verification/VerificationsDialog.tsx:137 2150 + #: src/components/verification/VerifierDialog.tsx:138 2151 #: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:204 2152 #: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:298 2153 #: src/view/com/composer/select-language/PostLanguageSelectDialog.tsx:330 ··· 2433 msgid "Continue to next step" 2434 msgstr "" 2435 2436 + #: src/screens/Messages/Conversation.tsx:56 2437 msgid "Conversation" 2438 msgstr "" 2439 ··· 2926 msgstr "" 2927 2928 #: src/view/com/composer/GifAltText.tsx:150 2929 + #: src/view/com/composer/photos/ImageAltTextDialog.tsx:118 2930 msgid "Descriptive alt text" 2931 msgstr "" 2932 ··· 3040 msgid "Discover New Feeds" 3041 msgstr "" 3042 3043 + #: src/components/Dialog/index.tsx:375 3044 msgid "Dismiss" 3045 msgstr "" 3046 ··· 3159 msgid "Double tap or long press the message to add a reaction" 3160 msgstr "" 3161 3162 + #: src/components/Dialog/index.tsx:376 3163 msgid "Double tap to close the dialog" 3164 msgstr "" 3165 ··· 3238 #: src/screens/Settings/AccountSettings.tsx:145 3239 #: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:252 3240 #: src/screens/StarterPack/StarterPackScreen.tsx:598 3241 + #: src/screens/StarterPack/Wizard/index.tsx:337 3242 + #: src/screens/StarterPack/Wizard/index.tsx:342 3243 msgid "Edit" 3244 msgstr "" 3245 ··· 3726 msgid "Failed to create conversation" 3727 msgstr "" 3728 3729 + #: src/screens/StarterPack/Wizard/index.tsx:260 3730 + #: src/screens/StarterPack/Wizard/index.tsx:268 3731 msgid "Failed to create starter pack" 3732 msgstr "" 3733 ··· 4098 msgid "Finding friends..." 4099 msgstr "" 4100 4101 + #: src/screens/StarterPack/Wizard/index.tsx:216 4102 msgid "Finish" 4103 msgstr "" 4104 ··· 5118 msgid "It's correct" 5119 msgstr "" 5120 5121 + #: src/screens/StarterPack/Wizard/index.tsx:489 5122 msgid "It's just <0>{0} </0>right now! Add more people to your starter pack by searching above." 5123 msgstr "" 5124 5125 + #: src/screens/StarterPack/Wizard/index.tsx:484 5126 msgid "It's just you right now! Add more people to your starter pack by searching above." 5127 msgstr "" 5128 ··· 5219 msgstr "" 5220 5221 #: src/components/verification/VerificationsDialog.tsx:167 5222 + #: src/components/verification/VerifierDialog.tsx:134 5223 #: src/screens/Moderation/VerificationSettings.tsx:49 5224 #: src/screens/Profile/Header/EditProfileDialog.tsx:349 5225 #: src/screens/Settings/components/ChangeHandleDialog.tsx:213 ··· 6140 #: src/screens/Settings/components/AddAppPasswordDialog.tsx:157 6141 #: src/screens/Settings/components/AddAppPasswordDialog.tsx:165 6142 #: src/screens/Signup/BackNextButtons.tsx:67 6143 + #: src/screens/StarterPack/Wizard/index.tsx:208 6144 + #: src/screens/StarterPack/Wizard/index.tsx:212 6145 + #: src/screens/StarterPack/Wizard/index.tsx:390 6146 + #: src/screens/StarterPack/Wizard/index.tsx:397 6147 msgid "Next" 6148 msgstr "" 6149 ··· 6355 msgid "None" 6356 msgstr "" 6357 6358 + #: src/screens/FindContactsFlowScreen.tsx:67 6359 #: src/screens/Settings/FindContactsSettings.tsx:103 6360 msgid "Not available on this platform." 6361 msgstr "" ··· 7138 msgid "Please use the native app to import your contacts." 7139 msgstr "" 7140 7141 + #: src/screens/FindContactsFlowScreen.tsx:68 7142 msgid "Please use the native app to sync your contacts." 7143 msgstr "" 7144 ··· 7556 msgid "Real people." 7557 msgstr "" 7558 7559 + #: src/screens/Takendown.tsx:157 7560 + #: src/screens/Takendown.tsx:165 7561 msgid "Reason for appeal" 7562 msgstr "" 7563 ··· 8132 msgid "Returns to previous page" 8133 msgstr "" 8134 8135 + #: src/screens/StarterPack/Wizard/index.tsx:322 8136 msgid "Returns to the previous step" 8137 msgstr "" 8138 ··· 8152 #: src/view/com/composer/GifAltText.tsx:202 8153 #: src/view/com/composer/photos/EditImageDialog.web.tsx:62 8154 #: src/view/com/composer/photos/EditImageDialog.web.tsx:75 8155 + #: src/view/com/composer/photos/ImageAltTextDialog.tsx:157 8156 + #: src/view/com/composer/photos/ImageAltTextDialog.tsx:167 8157 msgid "Save" 8158 msgstr "" 8159 ··· 8305 msgid "Search for \"{searchText}\"" 8306 msgstr "" 8307 8308 + #: src/screens/StarterPack/Wizard/index.tsx:547 8309 msgid "Search for feeds that you want to suggest to others." 8310 msgstr "" 8311 ··· 9001 #: src/screens/Settings/Settings.tsx:304 9002 #: src/screens/SignupQueued.tsx:93 9003 #: src/screens/SignupQueued.tsx:96 9004 + #: src/screens/Takendown.tsx:87 9005 #: src/view/shell/desktop/LeftNav.tsx:212 9006 #: src/view/shell/desktop/LeftNav.tsx:269 9007 #: src/view/shell/desktop/LeftNav.tsx:272 9008 msgid "Sign out" 9009 msgstr "" 9010 9011 + #: src/screens/Takendown.tsx:90 9012 msgid "Sign Out" 9013 msgstr "" 9014 ··· 9035 #: src/screens/Onboarding/StepFinished/index.tsx:316 9036 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:261 9037 #: src/screens/Onboarding/StepSuggestedStarterpacks/index.tsx:104 9038 + #: src/screens/StarterPack/Wizard/index.tsx:216 9039 msgid "Skip" 9040 msgstr "" 9041 ··· 9098 msgid "Something wasn't quite right with the data you're trying to report. Please contact support." 9099 msgstr "" 9100 9101 + #: src/screens/Messages/Conversation.tsx:141 9102 msgid "Something went wrong" 9103 msgstr "" 9104 ··· 9194 9195 #: src/Navigation.tsx:590 9196 #: src/Navigation.tsx:595 9197 + #: src/screens/StarterPack/Wizard/index.tsx:207 9198 msgid "Starter Pack" 9199 msgstr "" 9200 ··· 9270 msgid "Submit" 9271 msgstr "" 9272 9273 + #: src/screens/Takendown.tsx:75 9274 msgid "Submit appeal" 9275 msgstr "" 9276 9277 + #: src/screens/Takendown.tsx:79 9278 msgid "Submit Appeal" 9279 msgstr "" 9280 ··· 9505 #: src/screens/StarterPack/StarterPackScreen.tsx:113 9506 #: src/screens/StarterPack/StarterPackScreen.tsx:157 9507 #: src/screens/StarterPack/StarterPackScreen.tsx:158 9508 + #: src/screens/StarterPack/Wizard/index.tsx:116 9509 + #: src/screens/StarterPack/Wizard/index.tsx:126 9510 msgid "That starter pack could not be found." 9511 msgstr "" 9512 ··· 10970 msgid "We couldn't find any results for that topic." 10971 msgstr "" 10972 10973 + #: src/screens/Messages/Conversation.tsx:142 10974 msgid "We couldn't load this conversation" 10975 msgstr "" 10976 ··· 11191 msgid "Whoops! Trending videos failed to load." 11192 msgstr "" 11193 11194 + #: src/screens/Takendown.tsx:168 11195 msgid "Why are you appealing?" 11196 msgstr "" 11197 ··· 11751 msgid "Your account has been deleted" 11752 msgstr "" 11753 11754 + #: src/screens/Takendown.tsx:141 11755 msgid "Your account has been suspended" 11756 msgstr "" 11757 ··· 11763 msgid "Your account repository, containing all public data records, can be downloaded as a \"CAR\" file. This file does not include media embeds, such as images, or your private data, which must be fetched separately." 11764 msgstr "" 11765 11766 + #: src/screens/Takendown.tsx:209 11767 msgid "Your account was found to be in violation of the <0>Bluesky Social Terms of Service</0>. You have been sent an email outlining the specific violation and suspension period, if applicable. You can appeal this decision if you believe it was made in error." 11768 msgstr "" 11769 11770 + #: src/screens/Takendown.tsx:149 11771 msgid "Your appeal has been submitted. If your appeal succeeds, you will receive an email." 11772 msgstr "" 11773
-3
src/screens/FindContactsFlowScreen.tsx
··· 4 import {useLingui} from '@lingui/react' 5 import {usePreventRemove} from '@react-navigation/native' 6 7 - import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' 8 import { 9 type AllNavigatorParams, 10 type NativeStackScreenProps, ··· 36 setTransitionDirection('Forward') 37 }) 38 }) 39 - 40 - useEnableKeyboardControllerScreen(true) 41 42 const setMinimalShellMode = useSetMinimalShellMode() 43 const effect = useCallback(() => {
··· 4 import {useLingui} from '@lingui/react' 5 import {usePreventRemove} from '@react-navigation/native' 6 7 import { 8 type AllNavigatorParams, 9 type NativeStackScreenProps, ··· 35 setTransitionDirection('Forward') 36 }) 37 }) 38 39 const setMinimalShellMode = useSetMinimalShellMode() 40 const effect = useCallback(() => {
-3
src/screens/Messages/Conversation.tsx
··· 15 } from '@react-navigation/native' 16 import {type NativeStackScreenProps} from '@react-navigation/native-stack' 17 18 - import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' 19 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 20 import { 21 type CommonNavigatorParams, ··· 67 68 const convoId = route.params.conversation 69 const {setCurrentConvoId} = useCurrentConvoId() 70 - 71 - useEnableKeyboardControllerScreen(true) 72 73 useFocusEffect( 74 useCallback(() => {
··· 15 } from '@react-navigation/native' 16 import {type NativeStackScreenProps} from '@react-navigation/native-stack' 17 18 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 19 import { 20 type CommonNavigatorParams, ··· 66 67 const convoId = route.params.conversation 68 const {setCurrentConvoId} = useCurrentConvoId() 69 70 useFocusEffect( 71 useCallback(() => {
-3
src/screens/Onboarding/index.tsx
··· 2 import {View} from 'react-native' 3 import * as bcp47Match from 'bcp-47-match' 4 5 - import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' 6 import {useLanguagePrefs} from '#/state/preferences' 7 import { 8 Layout, ··· 59 createInitialOnboardingState, 60 ) 61 const [contactsFlowState, contactsFlowDispatch] = useFindContactsFlowState() 62 - 63 - useEnableKeyboardControllerScreen(true) 64 65 return ( 66 <Portal>
··· 2 import {View} from 'react-native' 3 import * as bcp47Match from 'bcp-47-match' 4 5 import {useLanguagePrefs} from '#/state/preferences' 6 import { 7 Layout, ··· 58 createInitialOnboardingState, 59 ) 60 const [contactsFlowState, contactsFlowDispatch] = useFindContactsFlowState() 61 62 return ( 63 <Portal>
+24 -9
src/screens/ProfileList/index.tsx
··· 1 - import {useCallback, useMemo, useRef} from 'react' 2 import {View} from 'react-native' 3 import {useAnimatedRef} from 'react-native-reanimated' 4 import { ··· 35 import {FAB} from '#/view/com/util/fab/FAB' 36 import {type ListRef} from '#/view/com/util/List' 37 import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen' 38 - import {atoms as a, platform} from '#/alf' 39 import {useDialogControl} from '#/components/Dialog' 40 import {ListAddRemoveUsersDialog} from '#/components/dialogs/lists/ListAddRemoveUsersDialog' 41 import * as Layout from '#/components/Layout' 42 import {Loader} from '#/components/Loader' 43 import * as Hider from '#/components/moderation/Hider' 44 import {AboutSection} from './AboutSection' 45 import {ErrorScreen} from './components/ErrorScreen' 46 import {Header} from './components/Header' ··· 149 moderationOpts: ModerationOpts 150 preferences: UsePreferencesQueryResponse 151 }) { 152 const {_} = useLingui() 153 const queryClient = useQueryClient() 154 const {openComposer} = useOpenComposer() ··· 164 const scrollElRef = useAnimatedRef() 165 const addUserDialogControl = useDialogControl() 166 const sectionTitlesCurate = [_(msg`Posts`), _(msg`People`)] 167 168 const moderation = useMemo(() => { 169 return moderateUserList(list, moderationOpts) ··· 263 </Hider.Mask> 264 <Hider.Content> 265 <View style={[a.util_screen_outer]}> 266 - <Layout.Center>{renderHeader()}</Layout.Center> 267 - <AboutSection 268 - list={list} 269 - scrollElRef={scrollElRef as ListRef} 270 - onPressAddUser={addUserDialogControl.open} 271 - headerHeight={0} 272 - /> 273 <FAB 274 testID="composeFAB" 275 onPress={() => openComposer({logContext: 'Fab'})}
··· 1 + import {useCallback, useMemo, useRef, useState} from 'react' 2 import {View} from 'react-native' 3 import {useAnimatedRef} from 'react-native-reanimated' 4 import { ··· 35 import {FAB} from '#/view/com/util/fab/FAB' 36 import {type ListRef} from '#/view/com/util/List' 37 import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen' 38 + import {atoms as a, native, platform, useTheme} from '#/alf' 39 import {useDialogControl} from '#/components/Dialog' 40 import {ListAddRemoveUsersDialog} from '#/components/dialogs/lists/ListAddRemoveUsersDialog' 41 import * as Layout from '#/components/Layout' 42 import {Loader} from '#/components/Loader' 43 import * as Hider from '#/components/moderation/Hider' 44 + import {IS_WEB} from '#/env' 45 import {AboutSection} from './AboutSection' 46 import {ErrorScreen} from './components/ErrorScreen' 47 import {Header} from './components/Header' ··· 150 moderationOpts: ModerationOpts 151 preferences: UsePreferencesQueryResponse 152 }) { 153 + const t = useTheme() 154 const {_} = useLingui() 155 const queryClient = useQueryClient() 156 const {openComposer} = useOpenComposer() ··· 166 const scrollElRef = useAnimatedRef() 167 const addUserDialogControl = useDialogControl() 168 const sectionTitlesCurate = [_(msg`Posts`), _(msg`People`)] 169 + // modlist only 170 + const [headerHeight, setHeaderHeight] = useState<number | null>(null) 171 172 const moderation = useMemo(() => { 173 return moderateUserList(list, moderationOpts) ··· 267 </Hider.Mask> 268 <Hider.Content> 269 <View style={[a.util_screen_outer]}> 270 + <Layout.Center 271 + onLayout={evt => setHeaderHeight(evt.nativeEvent.layout.height)} 272 + style={[ 273 + native([a.absolute, a.z_10, t.atoms.bg]), 274 + 275 + a.border_b, 276 + t.atoms.border_contrast_low, 277 + ]}> 278 + {renderHeader()} 279 + </Layout.Center> 280 + {headerHeight !== null && ( 281 + <AboutSection 282 + list={list} 283 + scrollElRef={scrollElRef as ListRef} 284 + onPressAddUser={addUserDialogControl.open} 285 + headerHeight={IS_WEB ? 0 : headerHeight} 286 + /> 287 + )} 288 <FAB 289 testID="composeFAB" 290 onPress={() => openComposer({logContext: 'Fab'})}
-3
src/screens/StarterPack/Wizard/index.tsx
··· 16 import {type NativeStackScreenProps} from '@react-navigation/native-stack' 17 18 import {STARTER_PACK_MAX_SIZE} from '#/lib/constants' 19 - import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' 20 import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' 21 import { 22 type CommonNavigatorParams, ··· 184 gestureEnabled: false, 185 }) 186 }, [navigation]) 187 - 188 - useEnableKeyboardControllerScreen(true) 189 190 useFocusEffect( 191 React.useCallback(() => {
··· 16 import {type NativeStackScreenProps} from '@react-navigation/native-stack' 17 18 import {STARTER_PACK_MAX_SIZE} from '#/lib/constants' 19 import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' 20 import { 21 type CommonNavigatorParams, ··· 183 gestureEnabled: false, 184 }) 185 }, [navigation]) 186 187 useFocusEffect( 188 React.useCallback(() => {
-3
src/screens/Takendown.tsx
··· 12 BLUESKY_MOD_SERVICE_HEADERS, 13 MAX_REPORT_REASON_GRAPHEME_LENGTH, 14 } from '#/lib/constants' 15 - import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController' 16 import {cleanError} from '#/lib/strings/errors' 17 import {useAgent, useSession, useSessionApi} from '#/state/session' 18 import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' ··· 120 ) 121 122 const webLayout = IS_WEB && gtMobile 123 - 124 - useEnableKeyboardController(true) 125 126 return ( 127 <View style={[a.util_screen_outer, a.flex_1]}>
··· 12 BLUESKY_MOD_SERVICE_HEADERS, 13 MAX_REPORT_REASON_GRAPHEME_LENGTH, 14 } from '#/lib/constants' 15 import {cleanError} from '#/lib/strings/errors' 16 import {useAgent, useSession, useSessionApi} from '#/state/session' 17 import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' ··· 119 ) 120 121 const webLayout = IS_WEB && gtMobile 122 123 return ( 124 <View style={[a.util_screen_outer, a.flex_1]}>
+9 -1
src/state/queries/preferences/index.ts
··· 200 }) 201 } 202 203 - export function useSetThreadViewPreferencesMutation() { 204 const queryClient = useQueryClient() 205 const agent = useAgent() 206 ··· 212 queryKey: preferencesQueryKey, 213 }) 214 }, 215 }) 216 } 217
··· 200 }) 201 } 202 203 + export function useSetThreadViewPreferencesMutation({ 204 + onSuccess, 205 + onError, 206 + }: { 207 + onSuccess?: (data: void, variables: Partial<ThreadViewPreferences>) => void 208 + onError?: (error: unknown) => void 209 + }) { 210 const queryClient = useQueryClient() 211 const agent = useAgent() 212 ··· 218 queryKey: preferencesQueryKey, 219 }) 220 }, 221 + onSuccess, 222 + onError, 223 }) 224 } 225
+30 -18
src/state/queries/preferences/useThreadPreferences.ts
··· 1 import {useCallback, useMemo, useRef, useState} from 'react' 2 import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api' 3 import debounce from 'lodash.debounce' 4 5 import {useCallOnce} from '#/lib/once' ··· 70 } 71 72 const userUpdatedPrefs = useRef(false) 73 - const [isSaving, setIsSaving] = useState(false) 74 - const {mutateAsync} = useSetThreadViewPreferencesMutation() 75 const savePrefs = useMemo(() => { 76 - return debounce(async (prefs: ThreadViewPreferences) => { 77 - try { 78 - setIsSaving(true) 79 - await mutateAsync(prefs) 80 - ax.metric('thread:preferences:update', { 81 - sort: prefs.sort, 82 - view: prefs.lab_treeViewEnabled ? 'tree' : 'linear', 83 - }) 84 - } catch (e) { 85 - ax.logger.error('useThreadPreferences failed to save', { 86 - safeMessage: e, 87 - }) 88 - } finally { 89 - setIsSaving(false) 90 } 91 - }, 4e3) 92 - }, [mutateAsync]) 93 94 if (save && userUpdatedPrefs.current) { 95 savePrefs({
··· 1 import {useCallback, useMemo, useRef, useState} from 'react' 2 import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api' 3 + import {useFocusEffect} from '@react-navigation/native' 4 import debounce from 'lodash.debounce' 5 6 import {useCallOnce} from '#/lib/once' ··· 71 } 72 73 const userUpdatedPrefs = useRef(false) 74 + const {mutate, isPending: isSaving} = useSetThreadViewPreferencesMutation({ 75 + onSuccess: (_data, prefs) => { 76 + ax.metric('thread:preferences:update', { 77 + sort: prefs.sort, 78 + view: prefs.lab_treeViewEnabled ? 'tree' : 'linear', 79 + }) 80 + }, 81 + onError: err => { 82 + ax.logger.error('useThreadPreferences failed to save', { 83 + safeMessage: err, 84 + }) 85 + }, 86 + }) 87 const savePrefs = useMemo(() => { 88 + return debounce( 89 + (prefs: ThreadViewPreferences) => { 90 + mutate(prefs) 91 + }, 92 + 2e3, 93 + {leading: true, trailing: true}, 94 + ) 95 + }, [mutate]) 96 + 97 + // flush on leave screen 98 + useFocusEffect( 99 + useCallback(() => { 100 + return () => { 101 + void savePrefs.flush() 102 } 103 + }, [savePrefs]), 104 + ) 105 106 if (save && userUpdatedPrefs.current) { 107 savePrefs({
+7 -2
src/view/com/composer/photos/ImageAltTextDialog.tsx
··· 5 import {useLingui} from '@lingui/react' 6 7 import {MAX_ALT_TEXT} from '#/lib/constants' 8 import {enforceLen} from '#/lib/strings/helpers' 9 import {type ComposerImage} from '#/state/gallery' 10 import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' ··· 28 image, 29 onChange, 30 }: Props): React.ReactNode => { 31 const [altText, setAltText] = React.useState(image.alt) 32 33 return ( ··· 38 ...image, 39 alt: enforceLen(altText, MAX_ALT_TEXT, true), 40 }) 41 - }}> 42 <Dialog.Handle /> 43 <ImageAltTextInner 44 control={control} ··· 64 const {_, i18n} = useLingui() 65 const t = useTheme() 66 const windim = useWindowDimensions() 67 68 const imageStyle = React.useMemo<ImageStyle>(() => { 69 const maxWidth = IS_WEB ? 450 : windim.width ··· 165 </AltTextCounterWrapper> 166 </View> 167 {/* Maybe fix this later -h */} 168 - {IS_ANDROID ? <View style={{height: 300}} /> : null} 169 </Dialog.ScrollableInner> 170 ) 171 }
··· 5 import {useLingui} from '@lingui/react' 6 7 import {MAX_ALT_TEXT} from '#/lib/constants' 8 + import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' 9 import {enforceLen} from '#/lib/strings/helpers' 10 import {type ComposerImage} from '#/state/gallery' 11 import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' ··· 29 image, 30 onChange, 31 }: Props): React.ReactNode => { 32 + const {height: minHeight} = useWindowDimensions() 33 const [altText, setAltText] = React.useState(image.alt) 34 35 return ( ··· 40 ...image, 41 alt: enforceLen(altText, MAX_ALT_TEXT, true), 42 }) 43 + }} 44 + nativeOptions={{minHeight}}> 45 <Dialog.Handle /> 46 <ImageAltTextInner 47 control={control} ··· 67 const {_, i18n} = useLingui() 68 const t = useTheme() 69 const windim = useWindowDimensions() 70 + 71 + const [isKeyboardVisible] = useIsKeyboardVisible() 72 73 const imageStyle = React.useMemo<ImageStyle>(() => { 74 const maxWidth = IS_WEB ? 450 : windim.width ··· 170 </AltTextCounterWrapper> 171 </View> 172 {/* Maybe fix this later -h */} 173 + {IS_ANDROID && isKeyboardVisible ? <View style={{height: 300}} /> : null} 174 </Dialog.ScrollableInner> 175 ) 176 }
+4 -4
yarn.lock
··· 17163 resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" 17164 integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== 17165 17166 - react-native-keyboard-controller@1.18.5: 17167 - version "1.18.5" 17168 - resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.18.5.tgz#ae12131f2019c574178479d2c55784f55e08bb68" 17169 - integrity sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ== 17170 dependencies: 17171 react-native-is-edge-to-edge "^1.2.1" 17172
··· 17163 resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" 17164 integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== 17165 17166 + react-native-keyboard-controller@^1.20.7: 17167 + version "1.20.7" 17168 + resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.20.7.tgz#e1be1c15a5eb10b96a40a0812d8472e6e4bd8f29" 17169 + integrity sha512-G8S5jz1FufPrcL1vPtReATx+jJhT/j+sTqxMIb30b1z7cYEfMlkIzOCyaHgf6IMB2KA9uBmnA5M6ve2A9Ou4kw== 17170 dependencies: 17171 react-native-is-edge-to-edge "^1.2.1" 17172