Skip to content
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: refactor navigation infra #10977

Merged
merged 85 commits into from
Nov 8, 2024
Merged

Conversation

MounirDhahri
Copy link
Member

@MounirDhahri MounirDhahri commented Oct 18, 2024

Description

This PR started as a Future Friday effort to spike on a potential hybrid approach of adopting React-Navigation in the App.

After a few commits, I realized this was worth pursuing and kept working on it to get us to a mergeable presentable state.

What does this PR bring?

  • iOS and Android navigation is now aligned. using react-navigation
  • Noticeable performance improvements: install the app and feel it by yourself!
  • Many many fixes to unaligned screen headers
    • This was achieved mostly thanks to using default navigation headers from react-navigation (and styling them like we do)

What does this PR not do

  • No changes to the deep linking logic
  • No changes to route names
  • No changes to the bottom tabs
    • I tried to, and it worked but I figured out it's better to keep that separate in order not to bring any visual changes.

What does this PR allow us to

  • Align our headers across the app with minimal effort
  • Remove the old ModalStack NavStack and a bunch of native features about navigation.
  • Once we upgrade to React-navigation 7, we will be able to take advantage of exciting new features like preload link
  • Introduce a better pattern for adding new screens and defining their layout. Just like we do in Force
  • Less old native code means less tech debt means less obstacles when it comes to trying out some new exciting features.

Notes for the reviewers

  • This PR can be reviewed commit by commit. If you want a journey in my mind and a journey to how I introduced things. I encourage you to to review it in order of commits. I added a guided tour that you can start by clicking here 👉👉🔗 First commit: Add Feature Flag
  • Are there any resources I can watch in order to understand better what we currently have and where we want to be? Join the practice mobile to learn more about it! I am presenting today

What comes next

  • Introduce Unauthorized flow logic
  • Align on a new format for defining modules and screens. We need a thread around that to ensure everyone is aligned.
  • Simplification to our deep linking logic by relying more on react-navigation
  • Remove old code
  • Upgrade react-navigation 7

How can I test things out

  • Enable "Enable new navigation infra (requires app restart feature flag)"
  • Install ios-8.55.0-2024.10.29.13 or android-8.55.0-1674645910 betas

PR Checklist

  • I have tested my changes on iOS and Android.
  • I hid my changes behind a feature flag, or they don't need one.
  • I have included screenshots or videos, or I have not changed the UI.
  • I have added tests, or my changes don't require any.
  • I added an app state migration, or my changes do not require one.
  • I have documented any follow-up work that this PR will require, or it does not require any.
  • I have added a changelog entry below, or my changes do not require one.

To the reviewers 👀

  • I would like at least one of the reviewers to run this PR on the simulator or device.
Changelog updates

Changelog updates

Cross-platform user-facing changes

iOS user-facing changes

Android user-facing changes

Dev changes

  • feat: refactor navigation infra - mounir

Need help with something? Have a look at our docs, or get in touch with us.

@MounirDhahri MounirDhahri self-assigned this Oct 18, 2024
Copy link
Contributor

@araujobarret araujobarret left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewise looks good 👍
To clarify this PR is only about reorg of nav stuff, right?

export const HomeTab = () => {
return (
<TabStackNavigator.Navigator initialRouteName="Home" screenOptions={{ headerShown: false }}>
{SharedRoutes()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we just <SharedRoutes /> instead? I noticed the typings there need to be React.FC too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why this wasn't possible, was because StackNavigator.Navigator expects its children to be of type Screen, Group or Fragment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(follow-up PR): We can make our component with this type, right? Usually, people use these extra guard-rails to avoid using wrong children, but it's just a type check in their code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies, I didn't get what you mean here. Do you mind if you explain it to me in code so I get better what would you like this API to look like.

src/app/Navigation/AuthenticatedRoutes/SharedRoutes.tsx Outdated Show resolved Hide resolved
src/app/Navigation/AuthenticatedRoutes/Tabs.tsx Outdated Show resolved Hide resolved
src/app/Navigation/Navigation.tsx Outdated Show resolved Hide resolved
@MounirDhahri MounirDhahri marked this pull request as draft October 22, 2024 15:46
@gkartalis gkartalis force-pushed the feat/update-navigation-infra branch from d61825c to 9e156cb Compare October 23, 2024 12:29
@gkartalis
Copy link
Member

8.55.0 - 2024.10.23.12
android-8.55.0-1674645897

@MounirDhahri MounirDhahri force-pushed the feat/update-navigation-infra branch 8 times, most recently from 5d9716a to da45c6a Compare October 28, 2024 12:04
<Box mb={2}>
<OpaqueImageView imageURL={url} height={145} />
<Box mb={2} justifyContent="center" overflow="hidden">
<Image src={url} height={145} />
Copy link
Member Author

@MounirDhahri MounirDhahri Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally fixed this issue which was there since years

@MounirDhahri
Copy link
Member Author

Hey @damassi - sorry for the late reply, I was sick and got back today. The PR is already reviewable actually and I can address any comments.
My goal is to get this merged by end of week and schedule a QA session for beginning of next week.

@MounirDhahri
Copy link
Member Author

Requests are welcome - I won't push more to this and we can have the unauthorized flow in a separate PR.

damassi
damassi previously approved these changes Nov 7, 2024
Copy link
Member

@damassi damassi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big PR and I don't have many comments other than its nice to get this all into one system, and its a very good improvement.

Couple things:

  • Once this is merged, we should prioritize getting in the other unauthenticated routes so that we're not in a split situation
  • We should also prioritize, sooner rather than later, removing the FF split between old and new, so that we're on one system. Once that's done i think we can do a cleanup/polish pass and this will be 👌

@brainbicycle, @gkartalis - do y'all have any thoughts?

araujobarret
araujobarret previously approved these changes Nov 8, 2024
Copy link
Contributor

@araujobarret araujobarret left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks for the hard work here 💪
Left a small comment for a follow-up PR

export const HomeTab = () => {
return (
<TabStackNavigator.Navigator initialRouteName="Home" screenOptions={{ headerShown: false }}>
{SharedRoutes()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(follow-up PR): We can make our component with this type, right? Usually, people use these extra guard-rails to avoid using wrong children, but it's just a type check in their code.

olerichter00
olerichter00 previously approved these changes Nov 8, 2024
Copy link
Contributor

@olerichter00 olerichter00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the great work! 🌟

I didn't take the time to check all the code changes in detail, but overall, it looks pretty good 👍

gkartalis
gkartalis previously approved these changes Nov 8, 2024
Copy link
Member

@gkartalis gkartalis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work! I did leave some questions but none are blocking.

Another generic question that I have is around the headers across the app, lets say that design/product doesn't agree with the header changes is there a way to change the headers across the app with one custom component easily now with the react navigation?

Comment on lines +41 to +45
if (Platform.OS === "ios") {
modules.map(({ moduleName, module }) => {
register(moduleName, module)
})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

readonly hidesBottomTabs?: boolean
}

export const ScreenWrapper: React.FC<ScreenWrapperProps> = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question(non-blocking): how is the screen wrapper different than our <Screen /> component from palette? Would it make sense to use the Screen here instead of this custom one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted actually to use Screen from palette-mobile initially here but that meant that I would have to go through all screens and fix their padding and in some cases, remove Screen from the screens that are using it. And that felt to me something too early to do and it would require a separate PR for it. So I just copied the existing sreen wrapper that we currently have in AppRegistry and simplified it a bit. A future step would be to have no wrapper logic here but it's probably too early for it now

Comment on lines -22 to -27
{/*
NOTE: we don't need safeAreaEdges but passing undefined or empty array didn't work,
so we're passing "left" that doesn't actually add anything to the webview to avoid
having double paddings from Screen and ArtsyWebView
*/}
<ArtsyWebView url={data.href} safeAreaEdges={["left"]} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question(non-blocking): why did we get rid of safeAreaEdges here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, the reason behind that here was that after my changes, this wasn't needed anymore and there were no more paddings. But good note on this because It means I need to bring them back in case the feature flag is disabled

const space = useSpace()
const enableNewNavigation = useFeatureFlag("AREnableNewNavigation")

useAndroidGoBack()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be curious to understand if we do need this still after this PR stabilizes though and we do remove the feature flag

(for context: this was added in some places of the app because without it it was navigating us to the MyCollection screen when pressing the android back button instead of going back on the navigation stack probably because of our hybrid nav setup)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good note. Let me test it out actually

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! it's working without it now. I will remove it

Screenshot 2024-11-08 at 11 24 15
Screen.Recording.2024-11-08.at.11.23.49.mov

@@ -18,6 +18,7 @@ export const BottomTabs: React.FC<BottomTabBarProps> = (props) => {
const focusedRoute = findFocusedRoute(props.state)
const params = focusedRoute?.params as any
const module = modules[params?.moduleName as AppModule]
// const unreadConversationsCount = 3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️ should we get rid of this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes definitely

Comment on lines +61 to +65
useLayoutEffect(() => {
navigation.setOptions({
headerTitle: `${auctionResult.title}, ${auctionResult.dateText}`,
})
}, [navigation])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does the headerTitle show up if the title and date text is too long to be displayed? If we do use our <Text> component in order to render this text we are okay since this is handled

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default, react-navigation shows ellipsis dots if it's too long. We can in the future use our custom ellipsis mode there but since we are not doing that currently I didn't update it

Simulator Screenshot - iPhone 16 Pro - 2024-11-08 at 11 27 13

Comment on lines +34 to +46
useEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<Touchable onPress={handleSave} disabled={!isEmailValid}>
<Text variant="xs" color={isEmailValid ? "black100" : "black60"}>
Save
</Text>
</Touchable>
)
},
})
}, [navigation, email])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is a static component (if I understand this correctly) why do we have it set in a use effect? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I am doing that to take advantage of react-navigation defined headers. (title and headerLeft and rightHeader). We could define this in component withing the render tree but then we would need maintain the header in this component separately.
News is that react-navigation might be migrating to a different API where we would be defining that header withing the render method. 🤞

Email: <Text variant="xs">{userEmail}</Text>
</Text>
useEffect(() => {
queueMicrotask(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we use queue microtask + hide the setOptions here with a feature flag versus the other places where we do set the options for the header right item without queueMicrotask + featureflag?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sharp, this is not neeeded. I will remove it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context, initially, I was showing modal screens just with a different presentation way and hide their bottom tabs. That led to too much stuff happening on the UI and this useEffect was breaking the UI so I queued it. However later in the PR, I realized that it makes more sense to actually have the modals be modals and not deal with any issues like this

@MounirDhahri
Copy link
Member Author

Did a first round of the existing infra QA to make sure I didn't break anything. I will merge this PR in an hour then send an invitation for QA for next week. Thanks everyone for reviewing.

@MounirDhahri
Copy link
Member Author

Only thing left to do now is hooking up the unauthorized flow. Everything else should be working fine

Merging this now.

🚀 🚀 🚀 🚀 🚀

@MounirDhahri MounirDhahri merged commit 5fcfa7d into main Nov 8, 2024
7 checks passed
@MounirDhahri MounirDhahri deleted the feat/update-navigation-infra branch November 8, 2024 16:04
@damassi
Copy link
Member

damassi commented Nov 8, 2024

Nice work @MounirDhahri 💥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants