-
Notifications
You must be signed in to change notification settings - Fork 584
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: Android nav #4362
feat: Android nav #4362
Conversation
@@ -84,7 +84,9 @@ | |||
"@react-native-community/masked-view": "^0.1.10", | |||
"@react-native-community/netinfo": "4.6.1", | |||
"@react-native-community/picker": "1.3.0", | |||
"@react-native-community/viewpager": "^4.2.2", |
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 was a transitive dependency of react-native-scrollable-tab-view
that we had to make explicit, otherwise the android autolinking doesn't work.
<OpaqueImageView | ||
aspectRatio={artwork.image?.aspectRatio ?? 1} | ||
imageURL={artwork.image?.url} | ||
style={styles.artworkImage} | ||
> | ||
<View> | ||
<OpaqueImageView aspectRatio={artwork.image?.aspectRatio ?? 1} imageURL={artwork.image?.url} /> | ||
{Boolean(!hideUrgencyTags && urgencyTag && artwork?.sale?.isAuction && !artwork?.sale?.isClosed) && ( | ||
<Flex backgroundColor="white" px="5px" py="3px" borderRadius={2} alignSelf="flex-start"> | ||
<Flex | ||
position="absolute" | ||
bottom="5px" | ||
left="5px" | ||
backgroundColor="white" | ||
px="5px" | ||
py="3px" | ||
borderRadius={2} | ||
alignSelf="flex-start" | ||
> | ||
<Sans size="2" color="black100" numberOfLines={1}> | ||
{urgencyTag} | ||
</Sans> | ||
</Flex> | ||
)} | ||
</OpaqueImageView> | ||
</View> |
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.
Lots of screens were causing the app to crash because a few commonly-used components were putting children inside an OpaqueImageView
. On android OpaqueImageView
currently uses the normal react-native Image
component under the hood, which doesn't support children like our iOS native image component, so we decided to just refactor the few places where this was being done.
<View | ||
key={index} | ||
pointerEvents={index === activeIndex ? undefined : "none"} | ||
style={{ position: "absolute", width: "100%", height: "100%" }} | ||
> | ||
<Animated.View style={{ opacity: opacities[index], flex: 1, backgroundColor: "white" }}> | ||
{!!(hasLoaded[index] || index === activeIndex) && v} | ||
</Animated.View> | ||
</View> |
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.
We refactored this component because there seemed to be an Animated
bug on android during fade transitions. When animating the next view from opacity 0 to 1 the first frame would have opacity 1 already for some reason. e.g. if there were 10 frames in the animation, the opacity at each frame should be like [0, 0.1, 0.2, ..., 1]
but we were seeing [1, 0.1, 0.2, ..., 1]
.
Most confusing.
Anyway we made the problem disappear by moving things around.
<TouchableOpacity onPress={onPress}> | ||
<ChevronIcon direction="left" /> | ||
</TouchableOpacity> |
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.
💡 At first this was using our custom Touchable
but when debugging on android the onPress event was not being fired, no clue why. Most bizarre.
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.
Hm, we could take a look at a KS.
public static void didLaunch(SharedPreferences prefs) { | ||
launchCount = prefs.getInt(LAUNCH_COUNT, 0) + 1; | ||
prefs.edit().putInt(LAUNCH_COUNT, launchCount).commit(); | ||
System.out.println("LaunchCount:" + launchCount); | ||
} |
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.
We need the launchCount
at dev time for maintaining navigation state across reloads.
We want the behaviour to be:
- If you relaunch the app, the navigation state should reset
- If you reload the js bundle, the navigation state should kept
We couldn't find a built-in way to tell the difference between relaunch and reload, so we added this launchCount
property that is incremented only when the app launches. We already had it available on iOS.
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.
Smort! Nice one.
<NavigationContainer> | ||
<NavigationContainer independent> |
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.
We now have a NavigationContainer
wrapping the main part of the app (after onboarding). So any places where we use react-navigation in modals, e.g. here, we need to set this independent
prop to true
, which just silences a console.error
that is intended for people who are using react-navigation in the normal way.
onStartShouldSetResponderCapture={() => { | ||
const now = Date.now() | ||
const state = gestureState.current | ||
|
||
if (now - state.lastTapTimestamp < 500) { | ||
state.numTaps += 1 | ||
} else { | ||
state.numTaps = 1 | ||
} | ||
|
||
state.lastTapTimestamp = now | ||
|
||
if (state.numTaps >= 5) { | ||
state.numTaps = 0 | ||
setIsShowingAdminMenu(true) | ||
} | ||
return false | ||
}} |
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 added a placeholder admin menu already, to allow us to clear AsyncStorage
easily during development. For now I just made it trigger on five taps anywhere, with less than 0.5 seconds between each tap. We can iterate on this.
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.
It might be good to have like a three finger hold or something, but yea, that's for the admin jira ticket 👍.
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.
only problem with a three finger hold is, how to do that on a simulator?
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.
Hm.. true.. ok we will figure it out in the other ticket. I only don't like the spam tapping because it's easy to keep tapping and then tap something on the admin menu 😬.
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.t>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
Co-authored-by: Mounir Dhahri <mounir.dhahri@ensi-uma.tn>
hmm QAing this and it seems to have affected touch handling on the |
OK @pvinis this is good to go |
return null | ||
} | ||
return ( | ||
<Animated.View |
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.
Do you need this to be an Animated.View
@@ -1,67 +0,0 @@ | |||
import type { ViewDescriptor } from "lib/navigation/navigate" |
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.
👋
@@ -180,6 +180,8 @@ export const FilterModalNavigator: React.FC<FilterModalProps> = (props) => { | |||
<FancyModal visible={props.isFilterArtworksModalVisible} onBackgroundPressed={handleClosingModal} maxHeight={550}> | |||
<View style={{ flex: 1 }}> | |||
<Stack.Navigator | |||
// force it to not use react-native-screens, which is broken inside a react-native Modal for some reason | |||
detachInactiveScreens={false} |
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 is an interesting prop!
🔥🔥🔥 |
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.
public static void didLaunch(SharedPreferences prefs) { | ||
launchCount = prefs.getInt(LAUNCH_COUNT, 0) + 1; | ||
prefs.edit().putInt(LAUNCH_COUNT, launchCount).commit(); | ||
System.out.println("LaunchCount:" + launchCount); | ||
} |
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.
Smort! Nice one.
import { NativeModules } from "react-native" | ||
|
||
/** | ||
* Here we maintain references to all the navigators in the main app navigation hierarchy, which are: |
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.
👌
<TouchableOpacity onPress={onPress}> | ||
<ChevronIcon direction="left" /> | ||
</TouchableOpacity> |
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.
Hm, we could take a look at a KS.
/** | ||
* ModalStack is the root navigation stack in the app. The root screen in this stack is | ||
* the main app (with bottom tabs, etc), and then whenever we present a modal it gets | ||
* pushed on the top of this stack. We use react-native-screens to get native modal presetation | ||
* transitions etc. | ||
*/ |
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.
These comments are very nice and helpful! I'm sure they will help make everything clear to every dev that comes in here.
export const AdminMenuWrapper: React.FC = ({ children }) => { | ||
const [isShowingAdminMenu, setIsShowingAdminMenu] = useState(false) | ||
const gestureState = useRef({ lastTapTimestamp: 0, numTaps: 0 }) | ||
// TODO: if public release, just return <>{children}</> |
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 would say we keep it in release too, still useful as it is now.
onStartShouldSetResponderCapture={() => { | ||
const now = Date.now() | ||
const state = gestureState.current | ||
|
||
if (now - state.lastTapTimestamp < 500) { | ||
state.numTaps += 1 | ||
} else { | ||
state.numTaps = 1 | ||
} | ||
|
||
state.lastTapTimestamp = now | ||
|
||
if (state.numTaps >= 5) { | ||
state.numTaps = 0 | ||
setIsShowingAdminMenu(true) | ||
} | ||
return false | ||
}} |
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.
It might be good to have like a three finger hold or something, but yea, that's for the admin jira ticket 👍.
# Conflicts: # Podfile.lock # package.json
This PR resolves CX-873 CX-872 CX-874
Description
This PR introduces navigation stacks for Android (and eventually iOS) using
react-navigation
+react-native-screens
. This means we are using platform native navigation primitives under the hood, for speed 🚗This PR also puts Artsy's iOS-only native modules (e.g.
ARTemporaryAPIModule
) behind aLegacyNativeModules
object to emphasise that they are old, and many of them are ripe for removal as we get Android up and running.nav-stacks.mp4
admin-menu.mp4
Follow up work
PR Checklist (tick all before merging)