-
-
Notifications
You must be signed in to change notification settings - Fork 287
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: Support for deep links #3995
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #3995 +/- ##
===========================================
- Coverage 10.96% 10.77% -0.20%
===========================================
Files 265 270 +5
Lines 13090 13322 +232
===========================================
Hits 1435 1435
- Misses 11655 11887 +232
... and 4 files with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. 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.
Hi @g123k!
For the record I have no previous experience with deep-links.
Please have a look at my comments.
The most important thing is that you probably confused two pages: "add new product" page and "display product" page. I assume that "add new product" page shouldn't be dealt with here.
packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart
Show resolved
Hide resolved
AppNavigator.of(context).push( | ||
AppRoutes.PRODUCT_CREATOR(barcode), | ||
); | ||
if (callback != null) { | ||
await callback!(); | ||
} | ||
|
||
// TODO(g123k): Find another way | ||
// if (callback != null) { | ||
// await callback!(); | ||
// } |
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 you're making a mistake here (which explains your TODO
): here we open the page where you edit a new product. We don't open the ("new") product page.
Therefore I think you should remove your changes on that file.
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.
PRODUCT_CREATOR
actually opens AddNewProductPage
.
The only problem we have now is that the push
method doesn't return any result.
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.
PRODUCT_CREATOR
actually opensAddNewProductPage
. The only problem we have now is that thepush
method doesn't return any result.
Then perhaps the concept of PRODUCT_CREATOR
is flawed, or at least we should put it on the shelf.
To me the use-case is a user browsing and clicking on a link that automagically opens the app. A link that opens a known product, I can understand. A link that opens the search, I can understand. A link that opens an edit page for an unknown barcode, given that we scan on the app and we search on the app, I cannot quite picture that.
That said, perhaps the callback
(that we actually set only once in that case) is redundant with whatever we do at the end of the AddNewProductPage
, to be investigated.
But in general, I think that we assumed a long time ago that the deep-links were rather "read/display" pages than "write/edit" pages, specifically because they don't return values. Maybe it's not relevant, after all.
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 understand your point, but the routes in GoRouter, doesn't necessary mean they have to be opened via a deep link. In that case, PRODUCT_CREATOR
is just a named route, as we can find in the Navigator v1.
I have handled the case where a product doesn't exist, as it may happen.
Let's say you receive a link by email and the sender has made a typo => we have to do something for that.
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 understand your point, but the routes in GoRouter, doesn't necessary mean they have to be opened via a deep link. In that case,
PRODUCT_CREATOR
is just a named route, as we can find in the Navigator v1.
Ok, but in that case if we want to handle the callback that you commented/TODO
ed, perhaps we would be better off making this page a full screen dialog instead of a page.
I have handled the case where a product doesn't exist, as it may happen. Let's say you receive a link by email and the sender has made a typo => we have to do something for that.
In that case I would rather land on a "The product doesn't exist" page (with a "do you want to create it?" button).
If not we somehow ask users to create new products with wrong barcodes!
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.
For the first point, OK I will revert what(s done here
Mmm for the second point, I let @teolemon what's the best implementation to use.
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.
@monsieutanuki @g123k Mmh.. I can see some potential abuse potential but also the ability for third party apps to delegate some of their duties to Open Food Facts when a product is missing (some are currently using a web link)
I'd be in favor of giving the product addition approach a try, and kill it if we get abuse
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.
Ok, I let the feature as is.
Are we able as of now to detect from where the Product Creation page was opened?
Or should I add an Analytics event?
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 believe we are. We can open it from scan, search, and now deep links, right ?
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.
Yes, exactly. Is-it useful to add it, or should I leave it as is?
), | ||
), | ||
? () => AppNavigator.of(context).push( | ||
AppRoutes.PREFERENCES(PreferencePageType.FOOD), |
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 checking: is it possible for the user to say, from the browser, "Ok you can send me to the app but this time I prefer to stay on the website"?
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.
Unfortunately, no (both on Android and 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.
That means we'd better check if we have all the major features on the app, compared to the website on smartphone.
In order to avoid user frustration.
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're at feature parity, but some (hard to implement vs ROI) stuff is missing (like revision history).
Simple solution: on the product page, we could add an optional button to open the classic webpage in a webview.
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're at feature parity, but some (hard to implement vs ROI) stuff is missing (like revision history). Simple solution: on the product page, we could add an optional button to open the classic webpage in a webview.
I guess a button at the end of the product page would do the trick. Assuming we can exit the app using a URL the app is supposed to catch ;)
// TODO(g123k): Improve this with sealed classes (Dart 3 is required) | ||
// ignore_for_file: non_constant_identifier_names |
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.
Looks like in dart
the constants should rather be written in camelCase. Is that the same with sealed classes?
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.
Sealed classes are actually like regular classes.
This is just a temporary way to distinguish a simple getter from this.
I know this isn't perfect
AppRoutes._(); | ||
|
||
// Home page (or walkthrough during the onboarding) | ||
static const String HOME = _SmoothGoRouter.HOME_PAGE; | ||
|
||
// Product details (a [Product] is mandatory in the extra) | ||
static String PRODUCT(String barcode) => | ||
'/${_SmoothGoRouter.PRODUCT_DETAILS_PAGE}/$barcode'; | ||
|
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.
AppRoutes._(); | |
// Home page (or walkthrough during the onboarding) | |
static const String HOME = _SmoothGoRouter.HOME_PAGE; | |
// Product details (a [Product] is mandatory in the extra) | |
static String PRODUCT(String barcode) => | |
'/${_SmoothGoRouter.PRODUCT_DETAILS_PAGE}/$barcode'; | |
final String path; | |
// Home page (or walkthrough during the onboarding) | |
AppRoutes.home() : path = _SmoothGoRouter.HOME_PAGE; | |
// Product details (a [Product] is mandatory in the extra) | |
AppRoutes.product(String barcode) : path = | |
'/${_SmoothGoRouter.PRODUCT_DETAILS_PAGE}/$barcode'; |
Would look better that way, I think.
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.
That would be better, but it means that each entity contains an attribute for nothing.
And what should we do if you we want no one, but two elements?
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.
That would be better, but it means that each entity contains an attribute for nothing.
The attribute being the String
that you return, right? I don't see it as a problem, I see it as OOP.
And what should we do if you we want no one, but two elements?
Then with my OOP solution we can do it, like AppRoutes.product(String barcode, Color color)
.
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.
In your second example, you would still have only the path and merge the two data in the construct you mean?
Co-authored-by: monsieurtanuki <fabrice_fontaine@hotmail.com>
|
Co-authored-by: Pierre Slamich <pierre.slamich@gmail.com>
@teolemon If you mean for a prod release, I guess we should list the other issues we would like to be fixed beforehand. |
ok, @monsieurtanuki @g123k let's merge as is ? |
Hi everyone,
This PR allows the app to support deep links (e.g.: clicking on a link in a search result and being redirected to the appropriate screen). We have decided to only support two routes: a product and the homepage.
A product will be opened if:
Some examples:
product/123456789
orproduit/3017620422003/nutella-ferrero
Implementation
We now use the Navigator 2 (or router) in the app, with the help of go_router.
If this is the first time the user opens the app, the link will be ignored and, in general, if the onboarding is not successfully achieved, all links are ignored.
The
redirect
method of GoRouter is called both for internal routes and external ones (= url), that's why internal routes are prefixed with an underscore.Product loader
The
ProductDetails
screen requires it to be in the database. That's why aProductLoader
will load all the required stuff on the server and then show the details. Obviously, products not found, and network errors are handled.External links
A deep link can be called with an unsupported path (e.g.:
/contact
). In that case, we will redirect to the browser, through theExternalPage
.Migration from the Navigator
Instead of calling
Navigator.of(context)
, we now have to useAppNavigator.of(context)
which embeds our GoRouter implementation.Notes
I have not migrated how the onboarding, tabs and preferences handle their navigation, as it's not mandatory.
How to test it?
On Android, you can use this ADB command: