New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: iOS custom detents & Android form sheets #2045
base: main
Are you sure you want to change the base?
Conversation
… fragment underneath is not removed
It is now implemented as extension method on `View` class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great changes! Allow me to review this PR in parts 😄 (Part 1/3)
// useFocusEffect( | ||
// React.useCallback(() => { | ||
// const handle = setInterval(() => { | ||
// console.log('Hellow from setInterval'); | ||
// setBgColor(value => { | ||
// return value === 'firebrick' ? 'green' : 'firebrick'; | ||
// }); | ||
// }, 750); | ||
// | ||
// return () => { | ||
// clearInterval(handle); | ||
// }; | ||
// }, []), | ||
// ); | ||
|
||
// React.useEffect(() => { | ||
// const handle = setInterval(() => { | ||
// console.log('Hellow from setInterval'); | ||
// setBgColor(value => { | ||
// return value === 'firebrick' ? 'green' : 'firebrick'; | ||
// }); | ||
// }, 750); | ||
// | ||
// return () => { | ||
// clearInterval(handle); | ||
// }; | ||
// }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this commenting in the test intended? Do we want to leave this logic uncommented? 😄
onSheetDetentChanged: (e: NativeSyntheticEvent<{ index: number }>) => { | ||
console.log(`onSheetDetentChanged in App with index ${e.nativeEvent.index}`) | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this event defined like this? Don't we want to use listeners
object in this case and define this event there?
enableFreeze(true); | ||
// enableFreeze(true); | ||
enableFreeze(false); | ||
|
||
export default function App() { | ||
return <Test42 />; | ||
return <Test1649 />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be Test1649 and enableFreeze should be still true
|
||
implementation("com.google.android.material:material:1.9.0") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be defined inside the build.gradle of screens? Or moved to the separate PR? 😄
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar"> | ||
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
override fun runGuarded() { | ||
// If the status bar is translucent hook into the window insets calculations | ||
// and consume all the top insets so no padding will be added under the status bar. | ||
val decorView = activity.window.decorView | ||
// val decorView = activity.window.decorView |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be removed
if (translucent) { | ||
Log.w("ScreenWindowTraits", "setting onApplyWindowInsetsListener.Callback on $decorView") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be removed :D
ViewCompat.setOnApplyWindowInsetsListener(decorView) { v, insets -> | ||
Log.w("ScreenWindowTraits", "onApplyWindowInsetsListener.Callback on $v") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be removed
// ViewCompat.requestApplyInsets(decorView) | ||
} else { | ||
Log.w("ScreenWindowTraits", "Removing listener from $decorView") | ||
ViewCompat.setOnApplyWindowInsetsListener(decorView, null) | ||
// ViewCompat.setOnApplyWindowInsetsListener(decorView) { view, insets -> | ||
// val isImeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) | ||
// val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) | ||
// if (isImeVisible) { | ||
// WindowInsetsCompat | ||
// .Builder(insets) | ||
// .setInsets( | ||
// WindowInsetsCompat.Type.navigationBars(), | ||
// Insets.of( | ||
// prevInsets.left, | ||
// prevInsets.top, | ||
// prevInsets.right, | ||
// 0 | ||
// ) | ||
// ).build() | ||
// } else { | ||
// insets | ||
// } | ||
// } | ||
// ViewCompat.setOnApplyWindowInsetsListener(decorView, decorView.getTag(androidx.core.R.id.tag_on_apply_window_listener) as? OnApplyWindowInsetsListener) | ||
hasAttachedWindowInsetsCallback = false | ||
// ViewCompat.requestApplyInsets(decorView) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove comments
private var jsPointerDispatcher: JSPointerDispatcher? = null | ||
|
||
init { | ||
// Can we safely use ReactFeatureFlags? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's for now remove this comment and assume we can 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^Nevermind, lemme check source code of RN to find out if we can 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was lying, that my review will take 3 parts 😄
Part 2/2
override fun handleException(throwable: Throwable?) { | ||
// TODO: I need ThemedReactContext here. | ||
// TODO: Determine where it is initially created & verify its lifecycle | ||
// reactContext?.reactApplicationContext?.handleException(RuntimeException(throwable)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we still keep this?
package com.swmansion.rnscreens.bottomsheet | ||
|
||
import android.content.Context | ||
import android.util.Log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize imports 😄
// to run, as this will lead to inconsistencies in ScreenStack state. Instead we intercept | ||
// dismiss intention and run our logic. | ||
override fun cancel() { | ||
Log.d(TAG, "cancel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove Log call
// dismiss intention and run our logic. | ||
override fun cancel() { | ||
Log.d(TAG, "cancel") | ||
fragmentRef.get()!!.dismissFromContainer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requireNotNull()? 🥺
import android.graphics.Color | ||
import android.os.Build | ||
import android.os.Bundle | ||
import android.util.Log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize imports
// return ( | ||
// <ScreenContentWrapper style={{ display: "flex", flexDirection: "column", justifyContent: "space-between" }}> | ||
// <View collapsable={false} /> | ||
// <ScreenFooter | ||
// collapsable={false}> | ||
// {children} | ||
// </ScreenFooter> | ||
// </ScreenContentWrapper> | ||
// ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove?
// stackPresentation === 'formSheet' && Platform.OS === 'ios' ? styles.absoluteFillNoBottom : styles.container, | ||
// styles.container, | ||
stackPresentation === 'formSheet' | ||
? Platform.OS === 'ios' | ||
? styles.absoluteFillNoBottom | ||
: null | ||
: styles.container, | ||
// stackPresentation === 'formSheet' && Platform.OS === 'ios' ? styles.absoluteFillNoBottom : styles.container, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove comments
@@ -319,31 +349,36 @@ const RouteView = ({ | |||
}); | |||
}} | |||
onWillAppear={() => { | |||
console.log(`onWillAppear/transitionStart route: ${route.key}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove console.logs
@@ -256,20 +262,30 @@ export interface ScreenProps extends ViewProps { | |||
* - "landscape_right" – landscape-right orientation is permitted | |||
*/ | |||
screenOrientation?: ScreenOrientationTypes; | |||
screenStyle?: StyleProp<ViewStyle>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this prop do?
/** | ||
* Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet. | ||
* | ||
* Not dynamic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does not dynamic
mean?
Also, should we create an example of how does new sheets behave with new detent types on iOS/Android? Example of using ScreenFooter would be also helpful! |
Description
This PR introduces series of features & changes:
Screen
component (namely types of values accepted),fitToContents
formSheet
presentation styleformSheet
, however the sheet is mounted under separate window.a. controlling style of the
Screen
component (necessary workaround for issue with flickering on iOS,b. controlling whether the screen fragment of particular screen should be unmounted or not on Android when the screen is on JS stack but not visible (necessary to achieve "staying form sheet" when navigating back to a screen with presented form sheet),
c. listening for
sheetDetentChange
events, in case of Android stable & dragging states are reported, in case of iOS only stable statesd. todo: describe rest
Changes
Known issues
ScrollView
interactions with Android'sCoordinatorLayout
facebook/react-native#44099, no other workaround found. There is one approach suggested by grahammendick, however yet untested.Test code and steps to reproduce
I've used & extended
Test1649
to present all capabilities of new API.Checklist