From 7d784748d38de0e8aa6003561d371749e075134f Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 15:05:37 +0100 Subject: [PATCH] Handle Hacker News links routing in-app --- lib/auth/cubit/auth_cubit.dart | 3 ++- lib/auth/view/auth_page.dart | 6 +++++- lib/common/constants/app_uris.dart | 5 +++++ lib/common/extensions/uri_extension.dart | 16 +++++++++++++++- lib/common/widgets/hacker_news_text.dart | 1 + lib/item/models/item_value.dart | 8 ++++---- lib/item/widgets/item_data_tile.dart | 11 ++++++++--- lib/settings/view/settings_page.dart | 19 +++++++++++-------- lib/user/models/user_value.dart | 8 ++++---- 9 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 lib/common/constants/app_uris.dart diff --git a/lib/auth/cubit/auth_cubit.dart b/lib/auth/cubit/auth_cubit.dart index a1428f2f..1c3e8bdd 100644 --- a/lib/auth/cubit/auth_cubit.dart +++ b/lib/auth/cubit/auth_cubit.dart @@ -1,6 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:glider/common/constants/app_uris.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider_domain/glider_domain.dart'; @@ -22,7 +23,7 @@ class AuthCubit extends Cubit { } Future login() async { - final userCookieUrl = WebUri('https://news.ycombinator.com'); + final userCookieUrl = WebUri.uri(AppUris.hackerNewsUri); const userCookieName = 'user'; final userCookie = await _cookieManager.getCookie( url: userCookieUrl, diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index 107a0305..dccb49e2 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -8,6 +8,7 @@ import 'package:glider/app/container/app_container.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_spacing.dart'; +import 'package:glider/common/constants/app_uris.dart'; import 'package:glider/common/extensions/uri_extension.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; @@ -118,7 +119,7 @@ class _AuthPageState extends State { ), urlRequest: URLRequest( url: WebUri( - Uri.https('news.ycombinator.com', 'login').toString(), + AppUris.hackerNewsUri.replace(path: 'login').toString(), ), ), ), @@ -165,6 +166,7 @@ class _AuthBody extends StatelessWidget { 'github.com', 'Mosc/Glider/blob/master/PRIVACY.md', ).tryLaunch( + context, useInAppBrowser: _settingsCubit.state.useInAppBrowser, ), child: Text(context.l10n.privacyPolicy), @@ -174,6 +176,7 @@ class _AuthBody extends StatelessWidget { 'www.ycombinator.com', 'legal', ).replace(fragment: 'privacy').tryLaunch( + context, useInAppBrowser: _settingsCubit.state.useInAppBrowser, ), child: Text(context.l10n.privacyPolicyYc), @@ -183,6 +186,7 @@ class _AuthBody extends StatelessWidget { 'www.ycombinator.com', 'legal', ).replace(fragment: 'tou').tryLaunch( + context, useInAppBrowser: _settingsCubit.state.useInAppBrowser, ), child: Text(context.l10n.termsOfUseYc), diff --git a/lib/common/constants/app_uris.dart b/lib/common/constants/app_uris.dart new file mode 100644 index 00000000..ec513efd --- /dev/null +++ b/lib/common/constants/app_uris.dart @@ -0,0 +1,5 @@ +abstract final class AppUris { + static final hackerNewsUri = Uri.https('news.ycombinator.com'); + + static final projectUri = Uri.https('github.com', 'Mosc/Glider'); +} diff --git a/lib/common/extensions/uri_extension.dart b/lib/common/extensions/uri_extension.dart index 882f3698..16368d13 100644 --- a/lib/common/extensions/uri_extension.dart +++ b/lib/common/extensions/uri_extension.dart @@ -1,7 +1,21 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:glider/common/constants/app_uris.dart'; +import 'package:go_router/go_router.dart'; import 'package:url_launcher/url_launcher.dart'; extension UriExtension on Uri { - Future tryLaunch({String? title, required bool useInAppBrowser}) async { + Future tryLaunch( + BuildContext context, { + String? title, + required bool useInAppBrowser, + }) async { + if (authority == AppUris.hackerNewsUri.authority) { + unawaited(context.push(toString())); + return true; + } + if (await canLaunchUrl(this)) { if (await supportsLaunchMode(LaunchMode.externalNonBrowserApplication)) { final success = await launchUrl( diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index caeaf7e2..0751ae4f 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -90,6 +90,7 @@ class HackerNewsText extends StatelessWidget { onTapLink: (text, href, title) async { if (href != null) { await Uri.tryParse(href)?.tryLaunch( + context, title: title, useInAppBrowser: useInAppBrowser, ); diff --git a/lib/item/models/item_value.dart b/lib/item/models/item_value.dart index b8b09400..74acbccf 100644 --- a/lib/item/models/item_value.dart +++ b/lib/item/models/item_value.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; +import 'package:glider/common/constants/app_uris.dart'; import 'package:glider/common/interfaces/menu_item.dart'; import 'package:glider/item/cubit/item_cubit.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; @@ -53,10 +54,9 @@ enum ItemValue implements MenuItem { ItemValue.title => item?.title, ItemValue.link => item?.url.toString(), ItemValue.text => item?.text, - ItemValue.itemLink => Uri.https( - 'news.ycombinator.com', - 'item', - { + ItemValue.itemLink => AppUris.hackerNewsUri.replace( + path: 'item', + queryParameters: { 'id': itemCubit.itemId.toString(), }, ).toString(), diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 9308bf7a..22b2e8ca 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -162,8 +162,10 @@ class ItemDataTile extends StatelessWidget { AnimatedVisibility( visible: style == ItemStyle.overview, child: InkWell( - onTap: () async => - item.url!.tryLaunch(useInAppBrowser: useInAppBrowser), + onTap: () async => item.url!.tryLaunch( + context, + useInAppBrowser: useInAppBrowser, + ), // Explicitly override parent widget's long press. onLongPress: () {}, child: _ItemFavicon( @@ -359,7 +361,10 @@ class ItemDataTile extends StatelessWidget { ), if (item.url case final url?) DecoratedCard.outlined( - onTap: () async => url.tryLaunch(useInAppBrowser: useInAppBrowser), + onTap: () async => url.tryLaunch( + context, + useInAppBrowser: useInAppBrowser, + ), // Explicitly override parent widget's long press. onLongPress: () {}, child: Row( diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 19001fda..7cd5713d 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart' hide ThemeMode; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/common/constants/app_spacing.dart'; +import 'package:glider/common/constants/app_uris.dart'; import 'package:glider/common/extensions/uri_extension.dart'; import 'package:glider/common/widgets/preview_card.dart'; import 'package:glider/item/models/item_style.dart'; @@ -64,16 +65,14 @@ class _SettingsBody extends StatelessWidget { 'Open Sans', 'Roboto', ]; - static const String _authority = 'github.com'; - static const String _basePath = 'Mosc/Glider'; - static final Uri _privacyPolicyUrl = - Uri.https(_authority, '$_basePath/blob/master/PRIVACY.md'); + static final Uri _privacyPolicyUrl = AppUris.projectUri + .replace(path: '${AppUris.projectUri.path}/blob/master/PRIVACY.md'); static const String _license = 'MIT'; - static final Uri _licenseUrl = - Uri.https(_authority, '$_basePath/blob/master/LICENSE'); - static final Uri _sourceCodeUrl = Uri.https('github.com', _basePath); + static final Uri _licenseUrl = AppUris.projectUri + .replace(path: '${AppUris.projectUri.path}/blob/master/LICENSE'); + static final Uri _sourceCodeUrl = AppUris.projectUri; static final Uri _issueTrackerUrl = - Uri.https(_authority, '$_basePath/issues'); + AppUris.projectUri.replace(path: '${AppUris.projectUri.path}/issues'); @override Widget build(BuildContext context) { @@ -332,6 +331,7 @@ class _SettingsBody extends StatelessWidget { title: Text(context.l10n.privacyPolicy), trailing: const Icon(Icons.open_in_new_outlined), onTap: () => _privacyPolicyUrl.tryLaunch( + context, useInAppBrowser: state.useInAppBrowser, ), ), @@ -340,6 +340,7 @@ class _SettingsBody extends StatelessWidget { subtitle: const Text(_license), trailing: const Icon(Icons.open_in_new_outlined), onTap: () => _licenseUrl.tryLaunch( + context, useInAppBrowser: state.useInAppBrowser, ), ), @@ -348,6 +349,7 @@ class _SettingsBody extends StatelessWidget { subtitle: Text(_sourceCodeUrl.toString()), trailing: const Icon(Icons.open_in_new_outlined), onTap: () => _sourceCodeUrl.tryLaunch( + context, useInAppBrowser: state.useInAppBrowser, ), ), @@ -356,6 +358,7 @@ class _SettingsBody extends StatelessWidget { subtitle: Text(_issueTrackerUrl.toString()), trailing: const Icon(Icons.open_in_new_outlined), onTap: () => _issueTrackerUrl.tryLaunch( + context, useInAppBrowser: state.useInAppBrowser, ), ), diff --git a/lib/user/models/user_value.dart b/lib/user/models/user_value.dart index b54ab206..8b65fdcf 100644 --- a/lib/user/models/user_value.dart +++ b/lib/user/models/user_value.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; +import 'package:glider/common/constants/app_uris.dart'; import 'package:glider/common/interfaces/menu_item.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; @@ -48,10 +49,9 @@ enum UserValue implements MenuItem { return switch (this) { UserValue.username => userCubit.username, UserValue.about => user?.about, - UserValue.userLink => Uri.https( - 'news.ycombinator.com', - 'user', - { + UserValue.userLink => AppUris.hackerNewsUri.replace( + path: 'user', + queryParameters: { 'id': userCubit.username, }, ).toString(),