-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
clean up the deep link code #17452
clean up the deep link code #17452
Conversation
@arosiclair Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
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 looks 💯 times better! Thank you
if (prevProps.session.shortLivedAuthToken || !this.props.session.shortLivedAuthToken) { | ||
return; | ||
} | ||
this.openRouteInDesktopApp(); |
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.
Could you also please add a code comment here explaining something like "Now that there is a shortLivedAuthToken, the route to the desktop app can be opened"?
@@ -101,7 +95,6 @@ class DeeplinkWrapper extends PureComponent { | |||
if (!iframe.parentNode) { | |||
return; | |||
} | |||
|
|||
iframe.parentNode.removeChild(iframe); | |||
}, 100); |
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.
Were you ever able to figure out with this setTimeout
is necessary? I could see it maybe needing the code to run on the next loop (ie. setTimeout(0)
), but 100
seems arbitrary.
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 didn't pay attention to this setTimeout
before. I refresh page dozens of times, and then print its timestamp difference, even if the actual time difference is only a few milliseconds, setTimeout(0)
seems to work well.
What's interesting is that it doesn't seem to just make the code run on the next loop, I also tested with setImmediate/MessageChannel
and it made a difference of a few hundred milliseconds, but the popup did not appear.
src/libs/actions/Session/index.js
Outdated
} | ||
Navigation.navigate(); | ||
}); | ||
API.read('SignInWithShortLivedAuthToken', {authToken, oldPartnerUserID, shouldRetry: false}, {optimisticData, successData, failureData}); |
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 this be a write()
? I think it was originally a write?
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.
src/libs/actions/Session/index.js
Outdated
@@ -642,6 +621,23 @@ function authenticatePusher(socketID, channelName, callback) { | |||
}); | |||
} | |||
|
|||
function getShortLivedAuthToken() { | |||
// TODO: If we have any way to read the shortLivedAuthToken and save it, we can also no longer call makeRequestWithSideEffects. |
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'm not sure what "and save it" implies. We are saving it in Onyx, but that isn't enough? Also, TODOs are not allowed in our code. Instead, you should open a GH issue and point to this line of code and then detail what needs to be done.
src/libs/actions/Session/index.js
Outdated
'OpenOldDotLink', {shouldRetry: false}, {}, | ||
).then((response) => { | ||
const {shortLivedAuthToken} = response; | ||
Onyx.merge(ONYXKEYS.SESSION, {shortLivedAuthToken}); |
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 the OpenOldDotLink
command be sending Onyx instructions back in the response? That's the way all of our other API commands work.
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.
original context: https://github.com/Expensify/Expensify/issues/218742
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.
Yeah it looks like this should just be an API.read
now. We're just listening for the short lived token to change in Onyx instead of blocking on the request.
return; | ||
} | ||
this.openRouteInDesktopApp(); | ||
Session.removeShortLivedAuthToken(); |
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 it being removed here? (please also add the answer as a code comment)
@@ -62,7 +74,12 @@ class LogInWithShortLivedAuthTokenPage extends Component { | |||
} | |||
|
|||
render() { | |||
if (this.props.isTokenValid) { | |||
// TODO: Not sure if we should redirect the user to the login page or just show the expiration screen or offline message |
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 comment as above about TODOs (unless you are meaning to clean up this TODO in this PR)
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.
Yeah, these two todos will be handled in this PR, I think if if there is a sudden disconnection, we can redirect the user to the login page, do you think that's ok?
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.
@ntdiary I am working on a clean up for this in another PR... it would have been nice to have a heads up that you are going to tackle improving this code so that we are not duplicating any effort and more generally agree on a plan for how to do it.
@@ -96,7 +101,7 @@ class DeeplinkWrapper extends PureComponent { | |||
return; | |||
} | |||
iframe.parentNode.removeChild(iframe); | |||
}, 100); | |||
}, 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.
Why do we actually need this setTimeout()
? The explanation above is not very satisfying...
we need to give this iframe some time for it to do what it needs to do
What does it mean?
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 don't know the original purpose either, but according to my tests, without this delay, the pop-up window can't appear.
@@ -48,6 +48,10 @@ class DeeplinkWrapper extends PureComponent { | |||
if (!this.isMacOSWeb() || CONFIG.ENVIRONMENT === CONST.ENVIRONMENT.DEV) { | |||
return; | |||
} | |||
|
|||
// We need to clear the old short-lived auth token if it exists. |
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 do we need to do 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.
We need to ensure that the short-lived auth token doesn't exist, then get the new short-lived auto token from the server, and then the pop-up window can be opened.
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.
Please update the code comment to explain the "why" and not just "what" the code is doing. Why do we need to clear out the previous one before fetching a new one?
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.
@tgolen , sorry, my english is not good. I'm not sure how to describe it. Since we open the popup window based on whether there is an short-lived auth token. If we don't clear it first, the popup won't open?
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 think that's a perfect explanation 👍 Here is how I would write it:
Since there is no way to know if a previous short-lived authToken is still valid, any previous short-lived authToken must be cleared out and a new one must be fetched so that the popup window will only open when we know the short-lived authToken is valid.
@@ -74,7 +74,7 @@ class LogInWithShortLivedAuthTokenPage extends Component { | |||
} | |||
|
|||
render() { | |||
// TODO: Not sure if we should redirect the user to the login page or just show the expiration screen or offline message | |||
// redirect the user to the login page if there is a sudden disconnection | |||
if (this.props.network.isOffline) { | |||
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.
Should we be passing something more explicit here? Calling navigate()
with no arguments seems strange to me.
@@ -74,7 +74,7 @@ class LogInWithShortLivedAuthTokenPage extends Component { | |||
} | |||
|
|||
render() { | |||
// TODO: Not sure if we should redirect the user to the login page or just show the expiration screen or offline message | |||
// redirect the user to the login page if there is a sudden disconnection |
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'm unsure what the exact edge case we're trying to handle is based on this comment. The user hits this page and in between downloading the JS bundle and executing it they somehow become offline? Can you expand on what exactly we are preventing and why we are doing 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.
Eh, does the desktop app also need to download the js bundle?
After the user clicks Allow
button in the popup window to open the desktop app, if the network is disconnected at this time, I'm trying to redirect the user to the login page, prevent them being stuck on the transition page.
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.
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'm sorry I don't think I fully understood the previous comments. I don't think the desktop app needs to download the js bundle because it's an electron app and should be bundled with the app (IIRC).
if the network is disconnected at this time
If they are disconnected then how can they access the page at all 🤔
Without this condition, the session expired ui will be displayed, if we think this page is also ok, then we can remove this condition.
I think it's fine but the message about the session being expired seems wrong?
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 thought that event if the user is disconnected the desktop app would still display the page, only the request SignInWithShortLivedAuthToken
would fail.
Or maybe we can just remove the condition as this seems like a very edge case?
I think it's fine but the message about the session being expired seems wrong?
Or we can display an appropriate message for this case.
@@ -59,8 +63,9 @@ class DeeplinkWrapper extends PureComponent { | |||
if (prevProps.session.shortLivedAuthToken || !this.props.session.shortLivedAuthToken) { |
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.
Are we not handling the errors we were handling before anymore?
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.
Yeah, the redirection UI has been removed in PR #16383, there would be no point in updating the appInstallationCheckStatus
variable in the error handler.
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.
Can you remind me what errors we were handling and why they are not necessary anymore? I looked at the linked PR but I'm not seeing anything about errors or error handling (maybe I missed it).
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.
Yeah, the redirection UI has been removed in PR #16383, there would be no point in updating the
appInstallationCheckStatus
variable in the error handler.
Eh, Error handling was newly added by me in PR #16043. At that time, I just thought that due to the complexity of the network transmission, we could not guarantee that a request would always succeed. And If the request failed (e.g. due to timeouts), we needed to update appInstallationCheckStatus
from CHECKING
to NOT_INSTALLED
to allow sub components to render properly.
In fact, after #16383, we will always render the sub components, so updating appInstallationCheckStatus
no longer makes sense, which also means that error handling is no longer needed.
@@ -107,6 +124,7 @@ LogInWithShortLivedAuthTokenPage.defaultProps = defaultProps; | |||
export default compose( | |||
withLocalize, | |||
withOnyx({ | |||
isTokenValid: {key: ONYXKEYS.IS_TOKEN_VALID}, |
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.
Just curious, but what was it doing before and why are we removing it now?
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.
When the user clicks Allow
to open the desktop app one minute after the pop-up window appears, the token will become invalid, and we will prompt the user that the session has expired.
Based on this #16043 (comment) , we can just use ONYXKEYS.ACCOUNT.isLoading
Hi, I think the logic of deep link redirection is relatively clear now, could we merge this PR first? If there are any other questions or anything I can do, please feel free to let me know. cc @tgolen, @marcaaron |
Yeah, I'd like to get this merged. Can you please complete the PR description and checklist? It'd also be nice to have a C+ review/test this. |
@tgolen of course, please feel free to let me know if there are any mistakes. : ) |
bump @arosiclair for review and checklist #17452 (comment) |
@arosiclair sorry about the bump, I'm still confused on if we are wanting to merge this or not. I'm going to place a HOLD on it until we decide. |
What's the status of this PR? I'm thinking maybe we can create an issue for this to go over the general the plan then treat it like a normal "Bug" issue so that it doesn't fall off our radar? Does that sound OK to you @tgolen ? |
I thought the plan was to update this PR with all the necessary fixes. There was some discussion about it over here: #17391 (comment) That's a good reminder that this can be taken off HOLD (but it still needs updated before it can be reviewed). |
@0xmiroslav, the conflict has been resolved. : ) |
@ntdiary sorry, conflicts again |
@0xmiroslav , fixed. : ) |
@0xmiroslav Friendly bump to review the last changes asap! |
will do in a few hrs |
Reviewer Checklist
Screenshots/VideosWeb
web-desktop.mov
web-logged.out.mov
desktop.other.account.movMobile Web - ChromeMobile Web - SafariDesktopiOSAndroid |
function openRouteInDesktopApp(shortLivedAuthToken = '', email = '') { | ||
const params = new URLSearchParams(); | ||
params.set('exitTo', `${window.location.pathname}${window.location.search}${window.location.hash}`); | ||
if (email && shortLivedAuthToken) { |
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.
So this means that either both of them are passed or none of them 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.
Yeah, These two are the required parameters for signing in. If these two parameters are not present, such as when the login page is opened in a browser, the use can also open the login page in the desktop app via deep-link. This is consistent with our production environment. : )
@marcaaron @arosiclair In general, it looks good to me but I am not great with the transitioning logic so it would be great if you guys can give it a review as well! |
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.
LGTM as well. All yours @marcaaron
Merge conflicts! |
Sorry, I gotta pass on the review for this one as I am scrambling to make up other work and really don't want this to be blocked on my review. I tried to unassign myself a while back, but looks like it's not possible after you've left a comment. 🙃 Anyways, I trust you guys got this. |
🚀 Deployed to staging by https://github.com/arosiclair in version: 1.3.42-15 🚀
|
Tests well! Gonna check this off :) |
🚀 Deployed to production by https://github.com/jasperhuangg in version: 1.3.42-26 🚀
|
🚀 Deployed to staging by https://github.com/arosiclair in version: 1.3.44-0 🚀
|
🚀 Deployed to production by https://github.com/marcaaron in version: 1.3.44-2 🚀
|
Details
This is a followup PR for #16043.
After we remove the redirection ui in the deep link component, We no longer need to update the
appInstallationCheckStatus
variable.We can also remove the call to
Network.post()
.We need to implement the transition process: oldDot --> newDot --> Desktop app
Fixed Issues
$ #17391
Tests
Note: If we can't start OldDot locally, we can open the developer tools and add a breakpoint in the source panel to get the
transition
path, just like the image above.console.dir(t.replace('https://staging.new.expensify.com', ''))
in console to get the/transition...
path part, create a new tab and typelocalhost:8080
and append the transition path part and then open this url (tab 3).Offline tests
N/A
QA Steps
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
files and using the translation methodWaiting for Copy
label for a copy review on the original GH to get the correct copy.STYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)/** comment above it */
this
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Web
test.mov
Mobile Web - Chrome
N/AMobile Web - Safari
N/ADesktop
N/AiOS
N/AAndroid
N/A