From e09cbcc0e7b59a70093fe22f915e37b981b76959 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 3 Nov 2023 22:40:25 +0100 Subject: [PATCH 001/145] Update changelog --- fastlane/metadata/android/en-US/changelogs/43.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/fastlane/metadata/android/en-US/changelogs/43.txt b/fastlane/metadata/android/en-US/changelogs/43.txt index 89d4c8c6..fbffad31 100644 --- a/fastlane/metadata/android/en-US/changelogs/43.txt +++ b/fastlane/metadata/android/en-US/changelogs/43.txt @@ -1,5 +1,6 @@ - Added setting to enable downvoting - Added theme mode setting to force light or dark theme +- Moved favicon in thread view down to provide more space for title - Changed Android versions below 12 to show glowing overscroll indicator - Fixed system navigation bar color on Android versions below 10 - Fixed themed icons rendering blank at some densities \ No newline at end of file From 00c38fc18e943290e307711c1060d9a91c27e0c6 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 4 Nov 2023 21:26:19 +0100 Subject: [PATCH 002/145] Update CocoaPods version --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3513bfcc..2af75c95 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -74,4 +74,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.13.0 +COCOAPODS: 1.14.2 From 418c4cae9b69a4029b9c1e85c3d7c0f0d2c3ebd5 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 6 Nov 2023 20:20:52 +0100 Subject: [PATCH 003/145] Preparse formatted text --- lib/common/widgets/hacker_news_text.dart | 88 ++++++++++++------------ lib/item/cubit/item_cubit.dart | 5 ++ lib/item/cubit/item_state.dart | 5 ++ lib/item/widgets/item_data_tile.dart | 66 +++++++++--------- lib/item/widgets/item_tile.dart | 1 + lib/user/cubit/user_cubit.dart | 5 ++ lib/user/cubit/user_state.dart | 5 ++ lib/user/widgets/user_data_tile.dart | 6 +- lib/user/widgets/user_tile.dart | 1 + 9 files changed, 107 insertions(+), 75 deletions(-) diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index 4e4732cb..3656d8dd 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -8,10 +8,47 @@ import 'package:glider/common/extensions/theme_data_extension.dart'; import 'package:glider/common/extensions/uri_extension.dart'; import 'package:markdown/markdown.dart' as md; -class HackerNewsText extends StatelessWidget { - const HackerNewsText(this.data, {super.key}); +typedef ParsedData = List; - final String data; +class HackerNewsText extends StatelessWidget { + HackerNewsText(String data, {super.key}) : parsedData = parse(data); + + const HackerNewsText.parsed(this.parsedData, {super.key}); + + final ParsedData parsedData; + + static final _extensionSet = md.ExtensionSet( + const [ + HackerNewsCodeBlockSyntax(), + md.FencedCodeBlockSyntax(), + md.EmptyBlockSyntax(), + md.BlockquoteSyntax(), + md.HorizontalRuleSyntax(), + md.UnorderedListSyntax(), + md.OrderedListSyntax(), + md.ParagraphSyntax(), + ], + [ + HackerNewsAsteriskEscapeSyntax(), + HackerNewsEmphasisSyntax.asterisk(), + HackerNewsAutolinkExtensionSyntax(), + md.EscapeSyntax(), + md.AutolinkSyntax(), + md.EmailAutolinkSyntax(), + md.CodeSyntax(), + ], + ); + + static ParsedData parse(String data) { + final document = md.Document( + extensionSet: _extensionSet, + encodeHtml: false, + withDefaultBlockSyntaxes: false, + withDefaultInlineSyntaxes: false, + ); + final lines = const LineSplitter().convert(data); + return document.parseLines(lines); + } @override Widget build(BuildContext context) { @@ -44,36 +81,13 @@ class HackerNewsText extends StatelessWidget { ), ); return _HackerNewsMarkdownBody( - data: data, + parsedData: parsedData, styleSheet: styleSheet, onTapLink: (text, href, title) async { if (href != null) { await Uri.tryParse(href)?.tryLaunch(title: title); } }, - extensionSet: md.ExtensionSet( - const [ - HackerNewsCodeBlockSyntax(), - md.FencedCodeBlockSyntax(), - md.EmptyBlockSyntax(), - md.BlockquoteSyntax(), - md.HorizontalRuleSyntax(), - md.UnorderedListSyntax(), - md.OrderedListSyntax(), - md.ParagraphSyntax(), - ], - [ - HackerNewsAsteriskEscapeSyntax(), - HackerNewsEmphasisSyntax.asterisk(), - HackerNewsAutolinkExtensionSyntax(), - md.EscapeSyntax(), - md.AutolinkSyntax(), - md.EmailAutolinkSyntax(), - md.CodeSyntax(), - ], - ), - withDefaultBlockSyntaxes: false, - withDefaultInlineSyntaxes: false, builders: {'pre': _PreElementBuilder(styleSheet)}, fitContent: false, ); @@ -82,18 +96,14 @@ class HackerNewsText extends StatelessWidget { class _HackerNewsMarkdownBody extends MarkdownBody { const _HackerNewsMarkdownBody({ - required super.data, + required this.parsedData, super.styleSheet, super.onTapLink, - super.extensionSet, - this.withDefaultBlockSyntaxes = true, - this.withDefaultInlineSyntaxes = true, super.builders, super.fitContent, - }); + }) : super(data: ''); - final bool withDefaultBlockSyntaxes; - final bool withDefaultInlineSyntaxes; + final ParsedData parsedData; @override State createState() => _HackerNewsMarkdownBodyState(); @@ -134,14 +144,6 @@ class _HackerNewsMarkdownBodyState extends State<_HackerNewsMarkdownBody> textScaleFactor: MediaQuery.textScalerOf(context).textScaleFactor, ); final styleSheet = fallbackStyleSheet.merge(widget.styleSheet); - final document = md.Document( - extensionSet: widget.extensionSet, - encodeHtml: false, - withDefaultBlockSyntaxes: widget.withDefaultBlockSyntaxes, - withDefaultInlineSyntaxes: widget.withDefaultInlineSyntaxes, - ); - final lines = const LineSplitter().convert(widget.data); - final astNodes = document.parseLines(lines); final builder = MarkdownBuilder( delegate: this, selectable: widget.selectable, @@ -157,7 +159,7 @@ class _HackerNewsMarkdownBodyState extends State<_HackerNewsMarkdownBody> onTapText: widget.onTapText, softLineBreak: widget.softLineBreak, ); - _children = builder.build(astNodes); + _children = builder.build(widget.parsedData); } void _disposeRecognizers() { diff --git a/lib/item/cubit/item_cubit.dart b/lib/item/cubit/item_cubit.dart index 333c17fd..b7593519 100644 --- a/lib/item/cubit/item_cubit.dart +++ b/lib/item/cubit/item_cubit.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/models/status.dart'; +import 'package:glider/common/widgets/hacker_news_text.dart'; import 'package:glider/item/models/vote_type.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; @@ -44,6 +45,10 @@ class ItemCubit extends HydratedCubit { state.copyWith( status: () => Status.success, data: () => item, + parsedText: () => switch (item.text) { + final String text => HackerNewsText.parse(text), + _ => null, + }, exception: () => null, ), ); diff --git a/lib/item/cubit/item_state.dart b/lib/item/cubit/item_state.dart index b13719b4..6a27f8a2 100644 --- a/lib/item/cubit/item_state.dart +++ b/lib/item/cubit/item_state.dart @@ -4,6 +4,7 @@ class ItemState with DataMixin, EquatableMixin { const ItemState({ this.status = Status.initial, this.data, + this.parsedText, this.visited = false, this.vote, this.favorited = false, @@ -37,6 +38,7 @@ class ItemState with DataMixin, EquatableMixin { final Status status; @override final Item? data; + final ParsedData? parsedText; final bool visited; final VoteType? vote; final bool favorited; @@ -48,6 +50,7 @@ class ItemState with DataMixin, EquatableMixin { ItemState copyWith({ Status Function()? status, Item? Function()? data, + ParsedData? Function()? parsedText, bool Function()? visited, VoteType? Function()? vote, bool Function()? favorited, @@ -59,6 +62,7 @@ class ItemState with DataMixin, EquatableMixin { ItemState( status: status != null ? status() : this.status, data: data != null ? data() : this.data, + parsedText: parsedText != null ? parsedText() : this.parsedText, visited: visited != null ? visited() : this.visited, vote: vote != null ? vote() : this.vote, favorited: favorited != null ? favorited() : this.favorited, @@ -71,6 +75,7 @@ class ItemState with DataMixin, EquatableMixin { List get props => [ status, data, + parsedText, visited, vote, favorited, diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index aab8e654..383c5006 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -26,6 +26,7 @@ class ItemDataTile extends StatelessWidget { const ItemDataTile( this.item, { super.key, + this.parsedText, this.visited = false, this.vote, this.favorited = false, @@ -45,6 +46,7 @@ class ItemDataTile extends StatelessWidget { }); final Item item; + final ParsedData? parsedText; final bool visited; final VoteType? vote; final bool favorited; @@ -72,20 +74,18 @@ class ItemDataTile extends StatelessWidget { children: [ if (item.text case final text?) Expanded( - child: HackerNewsText(text), + child: Hero( + tag: 'item_tile_text_${item.id}', + child: parsedText != null + ? HackerNewsText.parsed(parsedText!) + : HackerNewsText(text), + ), ) else const Spacer(), - MetadataWidget( - icon: vote.downvoted - ? Icons.arrow_downward_outlined - : Icons.arrow_upward_outlined, - label: item.score != null ? Text((item.score!).toString()) : null, - color: vote.downvoted - ? Theme.of(context).colorScheme.secondary - : vote.upvoted - ? Theme.of(context).colorScheme.tertiary - : null, + Hero( + tag: 'item_tile_score_${item.id}', + child: _buildVotedMetadata(context), ), ].spaced(width: AppSpacing.s), ), @@ -174,21 +174,6 @@ class ItemDataTile extends StatelessWidget { } Widget _buildMetadata(BuildContext context) { - Widget favoritedMetadata() => MetadataWidget( - icon: Icons.favorite_outline_outlined, - color: favorited ? Theme.of(context).colorScheme.tertiary : null, - ); - Widget upvotedMetadata() => MetadataWidget( - icon: vote.downvoted - ? Icons.arrow_downward_outlined - : Icons.arrow_upward_outlined, - label: item.score != null ? Text((item.score!).toString()) : null, - color: vote.downvoted - ? Theme.of(context).colorScheme.secondary - : vote.upvoted - ? Theme.of(context).colorScheme.tertiary - : null, - ); return Row( children: [ Hero( @@ -210,12 +195,12 @@ class ItemDataTile extends StatelessWidget { ? _MetadataActionButton( padding: MetadataWidget.horizontalPadding, onTap: onTapFavorite, - child: favoritedMetadata(), + child: _buildFavoritedMetadata(context), ) : AnimatedVisibility( visible: favorited, padding: MetadataWidget.horizontalPadding, - child: favoritedMetadata(), + child: _buildFavoritedMetadata(context), ), ), if (item.type != ItemType.job) @@ -225,12 +210,12 @@ class ItemDataTile extends StatelessWidget { ? _MetadataActionButton( padding: MetadataWidget.horizontalPadding, onTap: onTapUpvote, - child: upvotedMetadata(), + child: _buildVotedMetadata(context), ) : AnimatedVisibility( visible: item.score != null || vote != null, padding: MetadataWidget.horizontalPadding, - child: upvotedMetadata(), + child: _buildVotedMetadata(context), ), ), Hero( @@ -325,13 +310,32 @@ class ItemDataTile extends StatelessWidget { ); } + Widget _buildFavoritedMetadata(BuildContext context) => MetadataWidget( + icon: Icons.favorite_outline_outlined, + color: favorited ? Theme.of(context).colorScheme.tertiary : null, + ); + + Widget _buildVotedMetadata(BuildContext context) => MetadataWidget( + icon: vote.downvoted + ? Icons.arrow_downward_outlined + : Icons.arrow_upward_outlined, + label: item.score != null ? Text(item.score!.toString()) : null, + color: vote.downvoted + ? Theme.of(context).colorScheme.secondary + : vote.upvoted + ? Theme.of(context).colorScheme.tertiary + : null, + ); + Widget _buildSecondary(BuildContext context) { return Column( children: [ if (item.text case final text?) Hero( tag: 'item_tile_text_${item.id}', - child: HackerNewsText(text), + child: parsedText != null + ? HackerNewsText.parsed(parsedText!) + : HackerNewsText(text), ), if (item.url case final url?) DecoratedCard.outlined( diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 5d955695..a75ef2c5 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -127,6 +127,7 @@ class _ItemTileState extends State bloc: widget._settingsCubit, builder: (context, settingsState) => ItemDataTile( item, + parsedText: state.parsedText, visited: state.visited && widget.showVisited, vote: state.vote, favorited: state.favorited, diff --git a/lib/user/cubit/user_cubit.dart b/lib/user/cubit/user_cubit.dart index 00524508..dc585ff5 100644 --- a/lib/user/cubit/user_cubit.dart +++ b/lib/user/cubit/user_cubit.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/models/status.dart'; +import 'package:glider/common/widgets/hacker_news_text.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:share_plus/share_plus.dart'; @@ -23,6 +24,10 @@ class UserCubit extends HydratedCubit { state.copyWith( status: () => Status.success, data: () => user, + parsedAbout: () => switch (user.about) { + final String about => HackerNewsText.parse(about), + _ => null, + }, exception: () => null, ), ), diff --git a/lib/user/cubit/user_state.dart b/lib/user/cubit/user_state.dart index 26e0ef67..5768736d 100644 --- a/lib/user/cubit/user_state.dart +++ b/lib/user/cubit/user_state.dart @@ -4,6 +4,7 @@ class UserState with DataMixin, EquatableMixin { const UserState({ this.status = Status.initial, this.data, + this.parsedAbout, this.blocked = false, this.synchronizing = false, this.exception, @@ -25,6 +26,7 @@ class UserState with DataMixin, EquatableMixin { final Status status; @override final User? data; + final ParsedData? parsedAbout; final bool blocked; final bool synchronizing; @override @@ -33,6 +35,7 @@ class UserState with DataMixin, EquatableMixin { UserState copyWith({ Status Function()? status, User? Function()? data, + ParsedData? Function()? parsedAbout, bool Function()? blocked, bool Function()? synchronizing, Object? Function()? exception, @@ -40,6 +43,7 @@ class UserState with DataMixin, EquatableMixin { UserState( status: status != null ? status() : this.status, data: data != null ? data() : this.data, + parsedAbout: parsedAbout != null ? parsedAbout() : this.parsedAbout, blocked: blocked != null ? blocked() : this.blocked, synchronizing: synchronizing != null ? synchronizing() : this.synchronizing, @@ -50,6 +54,7 @@ class UserState with DataMixin, EquatableMixin { List get props => [ status, data, + parsedAbout, blocked, synchronizing, exception, diff --git a/lib/user/widgets/user_data_tile.dart b/lib/user/widgets/user_data_tile.dart index 6a1a83f8..62711dc7 100644 --- a/lib/user/widgets/user_data_tile.dart +++ b/lib/user/widgets/user_data_tile.dart @@ -14,6 +14,7 @@ class UserDataTile extends StatelessWidget { const UserDataTile( this.user, { super.key, + this.parsedAbout, this.blocked = false, this.style = UserStyle.full, this.padding = AppSpacing.defaultTilePadding, @@ -22,6 +23,7 @@ class UserDataTile extends StatelessWidget { }); final User user; + final ParsedData? parsedAbout; final bool blocked; final UserStyle style; final EdgeInsets padding; @@ -116,7 +118,9 @@ class UserDataTile extends StatelessWidget { Widget _buildSecondary(BuildContext context) { return Hero( tag: 'user_tile_about_${user.username}', - child: HackerNewsText(user.about!), + child: parsedAbout != null + ? HackerNewsText.parsed(parsedAbout!) + : HackerNewsText(user.about!), ); } } diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index b1a88a40..57db91d1 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -37,6 +37,7 @@ class UserTile extends StatelessWidget { final user = state.data!; return UserDataTile( user, + parsedAbout: state.parsedAbout, blocked: state.blocked, style: style, padding: padding, From 64946213a685dab5d0d11f01cdf5e6d8f6847a5e Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 6 Nov 2023 20:21:26 +0100 Subject: [PATCH 004/145] Simplify item tile implementation --- lib/favorites/view/favorites_shell_page.dart | 2 - lib/inbox/view/inbox_shell_page.dart | 4 - lib/item/view/item_page.dart | 2 - lib/item/widgets/item_tile.dart | 76 +++++++++---------- lib/item_tree/view/sliver_item_tree_body.dart | 1 - lib/stories/view/stories_shell_page.dart | 2 - .../view/sliver_stories_search_body.dart | 2 - .../view/sliver_story_similar_body.dart | 1 - 8 files changed, 35 insertions(+), 55 deletions(-) diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index d5b5e6d4..b8085557 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -153,8 +153,6 @@ class _SliverFavoritesBody extends StatelessWidget { key: ValueKey(id), id: id, loadingType: ItemType.story, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - useActionButtons: settingsState.useActionButtons, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), ), diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 7e3c07b6..d1bcdfa3 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -152,8 +152,6 @@ class _SliverInboxBody extends StatelessWidget { key: ValueKey(parentId), id: parentId, loadingType: ItemType.story, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - useActionButtons: settingsState.useActionButtons, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), ), @@ -167,8 +165,6 @@ class _SliverInboxBody extends StatelessWidget { key: ValueKey(id), id: id, loadingType: ItemType.comment, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - useActionButtons: settingsState.useActionButtons, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), ), diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index 5fe1b295..d0f69693 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -327,8 +327,6 @@ class _SliverItemAppBarState extends State<_SliverItemAppBar> { storyUsername: state.data?.storyUsername, loadingType: ItemType.story, showVisited: false, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - useActionButtons: settingsState.useActionButtons, // It's redundant to show a URL host in the title when view is // scrolled, because the full URL should be visible below it. style: hasOverlap ? ItemStyle.overview : ItemStyle.primary, diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index a75ef2c5..e04c00ba 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -28,9 +28,7 @@ class ItemTile extends StatefulWidget { this.collapsedCount, this.showVisited = true, this.highlight = false, - this.useLargeStoryStyle = true, this.showMetadata = true, - this.useActionButtons = false, this.showJobs = true, this.style = ItemStyle.full, this.padding = AppSpacing.defaultTilePadding, @@ -50,9 +48,7 @@ class ItemTile extends StatefulWidget { this.collapsedCount, this.showVisited = true, this.highlight = false, - this.useLargeStoryStyle = true, this.showMetadata = true, - this.useActionButtons = false, this.showJobs = true, this.style = ItemStyle.full, this.padding = AppSpacing.defaultTilePadding, @@ -70,9 +66,7 @@ class ItemTile extends StatefulWidget { final int? collapsedCount; final bool showVisited; final bool highlight; - final bool useLargeStoryStyle; final bool showMetadata; - final bool useActionButtons; final bool showJobs; final ItemStyle style; final EdgeInsets padding; @@ -97,35 +91,35 @@ class _ItemTileState extends State super.build(context); return BlocBuilder( bloc: _itemCubit, - builder: (context, state) => state.whenOrDefaultWidgets( - loading: () => ItemLoadingTile( - type: widget.loadingType, - collapsedCount: widget.collapsedCount, - showMetadata: widget.showMetadata, - useLargeStoryStyle: widget.useLargeStoryStyle, - style: widget.style, - padding: widget.padding, - ), - success: () { - final item = state.data!; + builder: (context, state) => BlocBuilder( + bloc: widget._authCubit, + builder: (context, authState) => + BlocBuilder( + bloc: widget._settingsCubit, + builder: (context, settingsState) => state.whenOrDefaultWidgets( + loading: () => ItemLoadingTile( + type: widget.loadingType, + collapsedCount: widget.collapsedCount, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showMetadata: settingsState.showStoryMetadata, + style: widget.style, + padding: widget.padding, + ), + success: () { + final item = state.data!; - if (item.type == ItemType.job && !widget.showJobs) { - return const SizedBox.shrink(); - } + if (item.type == ItemType.job && !widget.showJobs) { + return const SizedBox.shrink(); + } - return Material( - type: widget.highlight - ? MaterialType.canvas - : MaterialType.transparency, - elevation: 4, - shadowColor: Colors.transparent, - surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, - child: BlocBuilder( - bloc: widget._authCubit, - builder: (context, authState) => - BlocBuilder( - bloc: widget._settingsCubit, - builder: (context, settingsState) => ItemDataTile( + return Material( + type: widget.highlight + ? MaterialType.canvas + : MaterialType.transparency, + elevation: 4, + shadowColor: Colors.transparent, + surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, + child: ItemDataTile( item, parsedText: state.parsedText, visited: state.visited && widget.showVisited, @@ -135,7 +129,7 @@ class _ItemTileState extends State blocked: state.blocked, failed: state.status == Status.failure, collapsedCount: widget.collapsedCount, - useLargeStoryStyle: widget.useLargeStoryStyle, + useLargeStoryStyle: settingsState.useLargeStoryStyle, showMetadata: widget.showMetadata, style: widget.style, usernameStyle: authState.username == item.username @@ -155,24 +149,24 @@ class _ItemTileState extends State AppRoute.itemBottomSheet .location(parameters: {'id': item.id}), ), - onTapUpvote: widget.useActionButtons && + onTapUpvote: settingsState.useActionButtons && ItemAction.upvote .isVisible(state, authState, settingsState) ? () async => ItemAction.upvote .execute(context, _itemCubit, widget._authCubit) : null, - onTapFavorite: widget.useActionButtons && + onTapFavorite: settingsState.useActionButtons && ItemAction.favorite .isVisible(state, authState, settingsState) ? () async => ItemAction.favorite .execute(context, _itemCubit, widget._authCubit) : null, ), - ), - ), - ); - }, - onRetry: () async => _itemCubit.load(), + ); + }, + onRetry: () async => _itemCubit.load(), + ), + ), ), ); } diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index ed92364b..4c753ff7 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -118,7 +118,6 @@ class _SliverItemTreeBodyState extends State { key: ValueKey(descendant.id), storyUsername: widget.storyUsername, loadingType: ItemType.comment, - useActionButtons: settingsState.useActionButtons, collapsedCount: state.collapsedIds.contains(descendant.id) ? state.getDescendants(descendant)?.length : null, diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index a7afe3a7..bea98d7e 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -242,9 +242,7 @@ class _SliverStoriesBody extends StatelessWidget { key: ValueKey(id), id: id, loadingType: ItemType.story, - useLargeStoryStyle: settingsState.useLargeStoryStyle, showMetadata: settingsState.showStoryMetadata, - useActionButtons: settingsState.useActionButtons, showJobs: settingsState.showJobs || state.storyType == StoryType.jobStories, style: ItemStyle.overview, diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index d6154939..d5f28bbf 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -59,9 +59,7 @@ class SliverStoriesSearchBody extends StatelessWidget { key: ValueKey(id), id: id, loadingType: ItemType.story, - useLargeStoryStyle: settingsState.useLargeStoryStyle, showMetadata: settingsState.showStoryMetadata, - useActionButtons: settingsState.useActionButtons, style: ItemStyle.overview, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), diff --git a/lib/story_similar/view/sliver_story_similar_body.dart b/lib/story_similar/view/sliver_story_similar_body.dart index d2d15c89..51fab7b6 100644 --- a/lib/story_similar/view/sliver_story_similar_body.dart +++ b/lib/story_similar/view/sliver_story_similar_body.dart @@ -84,7 +84,6 @@ class SliverStorySimilarBody extends StatelessWidget { id: id, storyUsername: storyUsername, loadingType: ItemType.story, - useLargeStoryStyle: false, style: ItemStyle.primary, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), From 61d883cc0cedc8063f1c8add8361f4f66ed94308 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 20:30:00 +0100 Subject: [PATCH 005/145] Add settings for favicons and user avatars --- lib/item/widgets/item_data_tile.dart | 28 +++++++++----- lib/item/widgets/item_tile.dart | 2 + lib/item/widgets/username_widget.dart | 38 +++++++++++++++++-- lib/l10n/arb/app_en.arb | 2 + lib/settings/cubit/settings_cubit.dart | 26 +++++++++++++ lib/settings/cubit/settings_state.dart | 11 ++++++ lib/settings/view/settings_page.dart | 16 ++++++++ .../lib/src/shared_preferences_service.dart | 20 ++++++++-- .../lib/src/settings_repository.dart | 12 ++++++ 9 files changed, 139 insertions(+), 16 deletions(-) diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 383c5006..aa501440 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -35,7 +35,9 @@ class ItemDataTile extends StatelessWidget { this.failed = false, this.collapsedCount, this.useLargeStoryStyle = true, + this.showFavicons = true, this.showMetadata = true, + this.showUserAvatars = true, this.style = ItemStyle.full, this.usernameStyle = UsernameStyle.none, this.padding = AppSpacing.defaultTilePadding, @@ -55,7 +57,9 @@ class ItemDataTile extends StatelessWidget { final bool failed; final int? collapsedCount; final bool useLargeStoryStyle; + final bool showFavicons; final bool showMetadata; + final bool showUserAvatars; final ItemStyle style; final UsernameStyle usernameStyle; final EdgeInsets padding; @@ -152,7 +156,7 @@ class ItemDataTile extends StatelessWidget { ) else const Spacer(), - if (item.url != null) + if (item.url != null && showFavicons) AnimatedVisibility( visible: style == ItemStyle.overview, alignment: AlignmentDirectional.centerEnd, @@ -270,6 +274,7 @@ class ItemDataTile extends StatelessWidget { alignment: AlignmentDirectional.centerStart, child: UsernameWidget( username: username, + showAvatar: showUserAvatars, style: usernameStyle, onTap: () async => context.push( AppRoute.user.location(parameters: {'id': username}), @@ -344,16 +349,19 @@ class ItemDataTile extends StatelessWidget { onLongPress: () {}, child: Row( children: [ - Hero( - tag: 'item_tile_favicon_${item.id}', - child: Material( - type: MaterialType.transparency, - child: _ItemFavicon( - item, - isLarge: false, + if (showFavicons) + Hero( + tag: 'item_tile_favicon_${item.id}', + child: Material( + type: MaterialType.transparency, + child: _ItemFavicon( + item, + isLarge: false, + ), ), - ), - ), + ) + else + const MetadataWidget(icon: Icons.link_outlined), Expanded( child: Hero( tag: 'item_tile_url_${item.id}', diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index e04c00ba..ec814f34 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -130,7 +130,9 @@ class _ItemTileState extends State failed: state.status == Status.failure, collapsedCount: widget.collapsedCount, useLargeStoryStyle: settingsState.useLargeStoryStyle, + showFavicons: settingsState.showFavicons, showMetadata: widget.showMetadata, + showUserAvatars: settingsState.showUserAvatars, style: widget.style, usernameStyle: authState.username == item.username ? UsernameStyle.loggedInUser diff --git a/lib/item/widgets/username_widget.dart b/lib/item/widgets/username_widget.dart index 3e4f8c16..b92f75b5 100644 --- a/lib/item/widgets/username_widget.dart +++ b/lib/item/widgets/username_widget.dart @@ -6,11 +6,13 @@ class UsernameWidget extends StatelessWidget { const UsernameWidget({ super.key, required this.username, + this.showAvatar = true, this.style = UsernameStyle.none, this.onTap, }); final String username; + final bool showAvatar; final UsernameStyle style; final VoidCallback? onTap; @@ -39,7 +41,7 @@ class UsernameWidget extends StatelessWidget { ); return switch (style) { - UsernameStyle.loggedInUser => FilledButton.icon( + UsernameStyle.loggedInUser when showAvatar => FilledButton.icon( onPressed: onPressed, onLongPress: onLongPress, style: FilledButton.styleFrom( @@ -50,7 +52,17 @@ class UsernameWidget extends StatelessWidget { icon: icon, label: label, ), - UsernameStyle.storyUser => FilledButton.tonalIcon( + UsernameStyle.loggedInUser => FilledButton( + onPressed: onPressed, + onLongPress: onLongPress, + style: FilledButton.styleFrom( + padding: padding, + visualDensity: visualDensity, + tapTargetSize: tapTargetSize, + ), + child: label, + ), + UsernameStyle.storyUser when showAvatar => FilledButton.tonalIcon( onPressed: onPressed, onLongPress: onLongPress, style: FilledButton.styleFrom( @@ -61,7 +73,17 @@ class UsernameWidget extends StatelessWidget { icon: icon, label: label, ), - UsernameStyle.none => ElevatedButton.icon( + UsernameStyle.storyUser => FilledButton( + onPressed: onPressed, + onLongPress: onLongPress, + style: FilledButton.styleFrom( + padding: padding, + visualDensity: visualDensity, + tapTargetSize: tapTargetSize, + ), + child: label, + ), + UsernameStyle.none when showAvatar => ElevatedButton.icon( onPressed: onPressed, onLongPress: onLongPress, style: ElevatedButton.styleFrom( @@ -72,6 +94,16 @@ class UsernameWidget extends StatelessWidget { icon: icon, label: label, ), + UsernameStyle.none => ElevatedButton( + onPressed: onPressed, + onLongPress: onLongPress, + style: ElevatedButton.styleFrom( + padding: padding, + visualDensity: visualDensity, + tapTargetSize: tapTargetSize, + ), + child: label, + ), }; } } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 39a4ef8d..c146fc8e 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -16,10 +16,12 @@ "about": "About", "largeStoryStyle": "Large stories", "largeStoryStyleDescription": "Shows URL and larger title", + "favicons": "Story favicons", "storyMetadata": "Story metadata", "storyMetadataDescription": "Always shows on some pages", "actionButtons": "Action buttons", "actionButtonsDescription": "Makes some metadata interactive", + "userAvatars": "User avatars", "themeMode": "Theme mode", "dynamicTheme": "Dynamic theme", "dynamicThemeDescription": "Uses system color scheme", diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index 44ba4611..cabf18b0 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -28,7 +28,9 @@ class SettingsCubit extends Cubit { Future _load() async { final useLargeStoryStyle = await _settingsRepository.getUseLargeStoryStyle(); + final showFavicons = await _settingsRepository.getShowFavicons(); final showStoryMetadata = await _settingsRepository.getShowStoryMetadata(); + final showUserAvatars = await _settingsRepository.getShowUserAvatars(); final useActionButtons = await _settingsRepository.getUseActionButtons(); final useDynamicTheme = await _settingsRepository.getUseDynamicTheme(); final themeColor = await _settingsRepository.getThemeColor(); @@ -41,8 +43,10 @@ class SettingsCubit extends Cubit { state.copyWith( useLargeStoryStyle: useLargeStoryStyle != null ? () => useLargeStoryStyle : null, + showFavicons: showFavicons != null ? () => showFavicons : null, showStoryMetadata: showStoryMetadata != null ? () => showStoryMetadata : null, + showUserAvatars: showUserAvatars != null ? () => showUserAvatars : null, useActionButtons: useActionButtons != null ? () => useActionButtons : null, useDynamicTheme: useDynamicTheme != null ? () => useDynamicTheme : null, @@ -71,6 +75,17 @@ class SettingsCubit extends Cubit { } } + Future setShowFavicons(bool value) async { + await _settingsRepository.setShowFavicons(value: value); + final showFavicons = await _settingsRepository.getShowFavicons(); + + if (showFavicons != null) { + safeEmit( + state.copyWith(showFavicons: () => showFavicons), + ); + } + } + Future setShowStoryMetadata(bool value) async { await _settingsRepository.setShowStoryMetadata(value: value); final showStoryMetadata = await _settingsRepository.getShowStoryMetadata(); @@ -82,6 +97,17 @@ class SettingsCubit extends Cubit { } } + Future setShowUserAvatars(bool value) async { + await _settingsRepository.setShowUserAvatars(value: value); + final showUserAvatars = await _settingsRepository.getShowUserAvatars(); + + if (showUserAvatars != null) { + safeEmit( + state.copyWith(showUserAvatars: () => showUserAvatars), + ); + } + } + Future setUseActionButtons(bool value) async { await _settingsRepository.setUseActionButtons(value: value); final useActionButtons = await _settingsRepository.getUseActionButtons(); diff --git a/lib/settings/cubit/settings_state.dart b/lib/settings/cubit/settings_state.dart index c33d0d44..01ac86f1 100644 --- a/lib/settings/cubit/settings_state.dart +++ b/lib/settings/cubit/settings_state.dart @@ -3,7 +3,9 @@ part of 'settings_cubit.dart'; class SettingsState with EquatableMixin { const SettingsState({ this.useLargeStoryStyle = true, + this.showFavicons = true, this.showStoryMetadata = true, + this.showUserAvatars = true, this.useActionButtons = false, this.themeMode = ThemeMode.system, this.useDynamicTheme = true, @@ -17,7 +19,9 @@ class SettingsState with EquatableMixin { }); final bool useLargeStoryStyle; + final bool showFavicons; final bool showStoryMetadata; + final bool showUserAvatars; final bool useActionButtons; final ThemeMode themeMode; final bool useDynamicTheme; @@ -31,7 +35,9 @@ class SettingsState with EquatableMixin { SettingsState copyWith({ bool Function()? useLargeStoryStyle, + bool Function()? showFavicons, bool Function()? showStoryMetadata, + bool Function()? showUserAvatars, bool Function()? useActionButtons, ThemeMode Function()? themeMode, bool Function()? useDynamicTheme, @@ -47,9 +53,12 @@ class SettingsState with EquatableMixin { useLargeStoryStyle: useLargeStoryStyle != null ? useLargeStoryStyle() : this.useLargeStoryStyle, + showFavicons: showFavicons != null ? showFavicons() : this.showFavicons, showStoryMetadata: showStoryMetadata != null ? showStoryMetadata() : this.showStoryMetadata, + showUserAvatars: + showUserAvatars != null ? showUserAvatars() : this.showUserAvatars, useActionButtons: useActionButtons != null ? useActionButtons() : this.useActionButtons, @@ -74,7 +83,9 @@ class SettingsState with EquatableMixin { @override List get props => [ useLargeStoryStyle, + showFavicons, showStoryMetadata, + showUserAvatars, useActionButtons, themeMode, useDynamicTheme, diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 68c91e59..df97ecfb 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -89,6 +89,13 @@ class _SettingsBody extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), + SwitchListTile.adaptive( + value: state.showFavicons, + onChanged: _settingsCubit.setShowFavicons, + title: Text(context.l10n.favicons), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), SwitchListTile.adaptive( value: state.showStoryMetadata, onChanged: _settingsCubit.setShowStoryMetadata, @@ -97,6 +104,13 @@ class _SettingsBody extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), + SwitchListTile.adaptive( + value: state.showUserAvatars, + onChanged: _settingsCubit.setShowUserAvatars, + title: Text(context.l10n.userAvatars), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), SwitchListTile.adaptive( value: state.useActionButtons, onChanged: _settingsCubit.setUseActionButtons, @@ -178,7 +192,9 @@ class _SettingsBody extends StatelessWidget { ), vote: VoteType.upvote, useLargeStoryStyle: state.useLargeStoryStyle, + showFavicons: state.showFavicons, showMetadata: state.showStoryMetadata, + showUserAvatars: state.showUserAvatars, style: ItemStyle.overview, onTapFavorite: state.useActionButtons ? () {} : null, onTapUpvote: state.useActionButtons ? () {} : null, diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index d2c5c70e..76e1127d 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -6,8 +6,10 @@ class SharedPreferencesService { final SharedPreferences _sharedPreferences; static const String _useLargeStoryStyleKey = 'use_large_story_style'; + static const String _showFaviconsKey = 'show_favicons'; static const String _showStoryMetadataKey = 'show_story_metadata'; - static const String _getUseActionButtons = 'use_action_buttons'; + static const String _showUserAvatars = 'show_user_avatars'; + static const String _useActionButtonsKey = 'use_action_buttons'; static const String _themeModeKey = 'theme_mode'; static const String _useDynamicThemeKey = 'use_dynamic_theme'; static const String _themeColorKey = 'theme_color'; @@ -30,17 +32,29 @@ class SharedPreferencesService { Future setUseLargeStoryStyle({required bool value}) async => _sharedPreferences.setBool(_useLargeStoryStyleKey, value); + Future getShowFavicons() async => + _sharedPreferences.getBool(_showFaviconsKey); + + Future setShowFavicons({required bool value}) async => + _sharedPreferences.setBool(_showFaviconsKey, value); + Future getShowStoryMetadata() async => _sharedPreferences.getBool(_showStoryMetadataKey); Future setShowStoryMetadata({required bool value}) async => _sharedPreferences.setBool(_showStoryMetadataKey, value); + Future getShowUserAvatars() async => + _sharedPreferences.getBool(_showUserAvatars); + + Future setShowUserAvatars({required bool value}) async => + _sharedPreferences.setBool(_showUserAvatars, value); + Future getUseActionButtons() async => - _sharedPreferences.getBool(_getUseActionButtons); + _sharedPreferences.getBool(_useActionButtonsKey); Future setUseActionButtons({required bool value}) async => - _sharedPreferences.setBool(_getUseActionButtons, value); + _sharedPreferences.setBool(_useActionButtonsKey, value); Future getThemeMode() async => _sharedPreferences.getString(_themeModeKey); diff --git a/packages/glider_domain/lib/src/settings_repository.dart b/packages/glider_domain/lib/src/settings_repository.dart index 538e6b14..6205edbd 100644 --- a/packages/glider_domain/lib/src/settings_repository.dart +++ b/packages/glider_domain/lib/src/settings_repository.dart @@ -15,12 +15,24 @@ class SettingsRepository { Future setUseLargeStoryStyle({required bool value}) async => _sharedPreferencesService.setUseLargeStoryStyle(value: value); + Future getShowFavicons() async => + _sharedPreferencesService.getShowFavicons(); + + Future setShowFavicons({required bool value}) async => + _sharedPreferencesService.setShowFavicons(value: value); + Future getShowStoryMetadata() async => _sharedPreferencesService.getShowStoryMetadata(); Future setShowStoryMetadata({required bool value}) async => _sharedPreferencesService.setShowStoryMetadata(value: value); + Future getShowUserAvatars() async => + _sharedPreferencesService.getShowUserAvatars(); + + Future setShowUserAvatars({required bool value}) async => + _sharedPreferencesService.setShowUserAvatars(value: value); + Future getUseActionButtons() async => _sharedPreferencesService.getUseActionButtons(); From 305a288aed8e39738cda1e64fe5cb57a72280049 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 20:30:13 +0100 Subject: [PATCH 006/145] Optimize navigation shell scaffold a bit --- .../widgets/navigation_shell_scaffold.dart | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 0d16f1b5..671c4776 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -184,15 +184,13 @@ class _NavigationShellScaffoldState extends State { } Widget _buildBody(BuildContext context, {Widget? floatingActionButton}) { - double? calculateBottomPadding(EdgeInsets padding) => - Breakpoints.small.isActive(context) - ? max(0, padding.bottom - _currentNavigationBarHeightNotifier.value) - : null; - final directionality = Directionality.of(context); final mediaQuery = MediaQuery.of(context); final padding = mediaQuery.padding; final viewPadding = mediaQuery.viewPadding; + final isSmallBreakpointActive = Breakpoints.small.isActive(context); + final isMediumAndUpBreakpointActive = + Breakpoints.mediumAndUp.isActive(context); return NotificationListener( onNotification: (notification) { @@ -223,30 +221,37 @@ class _NavigationShellScaffoldState extends State { }, child: ValueListenableBuilder( valueListenable: _currentNavigationBarHeightNotifier, - builder: (context, currentNavigationBarHeight, child) => MediaQuery( - data: MediaQuery.of(context).copyWith( - padding: padding.copyWith( - left: Breakpoints.mediumAndUp.isActive(context) && - directionality == TextDirection.ltr - ? 0 - : null, - right: Breakpoints.mediumAndUp.isActive(context) && - directionality == TextDirection.rtl - ? 0 - : null, - bottom: calculateBottomPadding(padding), - ), - viewPadding: viewPadding.copyWith( - bottom: calculateBottomPadding(viewPadding), + builder: (context, currentNavigationBarHeight, child) { + double? calculateBottomPadding(EdgeInsets padding) => + isSmallBreakpointActive + ? max(0, padding.bottom - currentNavigationBarHeight) + : null; + + return MediaQuery( + data: mediaQuery.copyWith( + padding: padding.copyWith( + left: isMediumAndUpBreakpointActive && + directionality == TextDirection.ltr + ? 0 + : null, + right: isMediumAndUpBreakpointActive && + directionality == TextDirection.rtl + ? 0 + : null, + bottom: calculateBottomPadding(padding), + ), + viewPadding: viewPadding.copyWith( + bottom: calculateBottomPadding(viewPadding), + ), + viewInsets: EdgeInsets.zero, ), - viewInsets: EdgeInsets.zero, - ), - child: child!, - ), + child: child!, + ); + }, child: Scaffold( body: widget._navigationShell, floatingActionButton: - Breakpoints.small.isActive(context) ? floatingActionButton : null, + isSmallBreakpointActive ? floatingActionButton : null, ), ), ); @@ -256,19 +261,17 @@ class _NavigationShellScaffoldState extends State { BuildContext context, List destinations, ) { - return ClipRect( - child: ValueListenableBuilder( - valueListenable: _currentNavigationBarHeightNotifier, - builder: (context, currentNavigationBarHeight, child) => Align( - heightFactor: currentNavigationBarHeight / _paddedNavigationBarHeight, - alignment: Alignment.topCenter, - child: child, - ), - child: AdaptiveScaffold.standardBottomNavigationBar( - currentIndex: _currentIndex, - destinations: destinations, - onDestinationSelected: onDestinationSelected, - ), + return ValueListenableBuilder( + valueListenable: _currentNavigationBarHeightNotifier, + builder: (context, currentNavigationBarHeight, child) => Align( + heightFactor: currentNavigationBarHeight / _paddedNavigationBarHeight, + alignment: Alignment.topCenter, + child: child, + ), + child: AdaptiveScaffold.standardBottomNavigationBar( + currentIndex: _currentIndex, + destinations: destinations, + onDestinationSelected: onDestinationSelected, ), ); } From ff129bf6db08a4a2ca70f6b1347151d59f20b945 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 20:30:57 +0100 Subject: [PATCH 007/145] Update MacOS Podfile --- macos/Podfile.lock | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 0d33aa6a..ffbf3781 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - device_info_plus (0.0.1): + - FlutterMacOS - dynamic_color (0.0.2): - FlutterMacOS - flutter_inappwebview (0.0.1): @@ -22,6 +24,7 @@ PODS: - FlutterMacOS DEPENDENCIES: + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) - flutter_inappwebview (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) @@ -37,6 +40,8 @@ SPEC REPOS: - OrderedSet EXTERNAL SOURCES: + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos dynamic_color: :path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos flutter_inappwebview: @@ -57,6 +62,7 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f flutter_inappwebview: 62e949df616a9f6e1b0366326381f208c7fcad37 flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea @@ -70,4 +76,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.13.0 +COCOAPODS: 1.14.2 From a3ffaf03c6c91580e302bde3ba6ffe03bdc94f42 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 21:24:09 +0100 Subject: [PATCH 008/145] Apply settings styling to edit/reply/submit --- lib/app/router/app_router.dart | 3 ++ lib/edit/view/edit_page.dart | 32 +++++++++++++----- lib/reply/view/reply_page.dart | 46 ++++++++++++++++++-------- lib/submit/view/submit_page.dart | 57 ++++++++++++++++++++++---------- 4 files changed, 99 insertions(+), 39 deletions(-) diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index 20125be2..bea85291 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -159,6 +159,7 @@ class AppRouter { child: SubmitPage( appContainer.submitCubit, appContainer.authCubit, + appContainer.settingsCubit, ), ), parentNavigatorKey: _rootNavigatorKey, @@ -195,6 +196,7 @@ class AppRouter { fullscreenDialog: true, child: EditPage( appContainer.editCubitFactory, + appContainer.settingsCubit, id: int.parse(state.uri.queryParameters['id']!), ), ), @@ -207,6 +209,7 @@ class AppRouter { child: ReplyPage( appContainer.replyCubitFactory, appContainer.authCubit, + appContainer.settingsCubit, id: int.parse(state.uri.queryParameters['id']!), ), ), diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index 40fc6150..da293170 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -14,16 +14,19 @@ import 'package:glider/edit/models/title_input.dart'; import 'package:glider/item/widgets/item_data_tile.dart'; import 'package:glider/item/widgets/username_widget.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:go_router/go_router.dart'; class EditPage extends StatefulWidget { const EditPage( - this._editCubitFactory, { + this._editCubitFactory, + this._settingsCubit, { super.key, required this.id, }); final EditCubitFactory _editCubitFactory; + final SettingsCubit _settingsCubit; final int id; @override @@ -74,7 +77,10 @@ class _EditPageState extends State { builder: (context, preview) => PreviewBottomPanel( visible: preview, onChanged: _editCubit.setPreview, - child: _EditPreview(_editCubit), + child: _EditPreview( + _editCubit, + widget._settingsCubit, + ), ), ), floatingActionButton: state.isValid @@ -233,9 +239,13 @@ class _EditFormState extends State<_EditForm> { } class _EditPreview extends StatelessWidget { - const _EditPreview(this._editCubit); + const _EditPreview( + this._editCubit, + this._settingsCubit, + ); final EditCubit _editCubit; + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { @@ -251,12 +261,18 @@ class _EditPreview extends StatelessWidget { previous.title != current.title || previous.text != current.text, builder: (context, state) => state.item != null - ? ItemDataTile( - state.item!.copyWith( - title: () => state.title?.value, - text: () => state.text?.value, + ? BlocBuilder( + bloc: _settingsCubit, + builder: (context, settingsState) => ItemDataTile( + state.item!.copyWith( + title: () => state.title?.value, + text: () => state.text?.value, + ), + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showFavicons: settingsState.showFavicons, + showUserAvatars: settingsState.showUserAvatars, + usernameStyle: UsernameStyle.loggedInUser, ), - usernameStyle: UsernameStyle.loggedInUser, ) : const SizedBox.shrink(), ), diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 2a4382dc..a8625abb 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -14,19 +14,22 @@ import 'package:glider/item/widgets/username_widget.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/reply/cubit/reply_cubit.dart'; import 'package:glider/reply/models/text_input.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:go_router/go_router.dart'; class ReplyPage extends StatefulWidget { const ReplyPage( this._replyCubitFactory, - this._authCubit, { + this._authCubit, + this._settingsCubit, { super.key, required this.id, }); final ReplyCubitFactory _replyCubitFactory; final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; final int id; @override @@ -77,7 +80,11 @@ class _ReplyPageState extends State { builder: (context, preview) => PreviewBottomPanel( visible: preview, onChanged: _replyCubit.setPreview, - child: _ReplyPreview(_replyCubit, widget._authCubit), + child: _ReplyPreview( + _replyCubit, + widget._authCubit, + widget._settingsCubit, + ), ), ), floatingActionButton: state.isValid @@ -192,10 +199,15 @@ class _ReplyFormState extends State<_ReplyForm> { } class _ReplyPreview extends StatelessWidget { - const _ReplyPreview(this._replyCubit, this._authCubit); + const _ReplyPreview( + this._replyCubit, + this._authCubit, + this._settingsCubit, + ); final ReplyCubit _replyCubit; final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { @@ -211,17 +223,25 @@ class _ReplyPreview extends StatelessWidget { BlocSelector( bloc: _authCubit, selector: (state) => state.username, - builder: (context, username) => HeroMode( - enabled: false, - child: ItemDataTile( - Item( - id: 0, - username: username, - type: ItemType.comment, - text: state.text.value.isNotEmpty ? state.text.value : null, - dateTime: clock.now(), + builder: (context, username) => + BlocBuilder( + bloc: _settingsCubit, + builder: (context, settingsState) => HeroMode( + enabled: false, + child: ItemDataTile( + Item( + id: 0, + username: username, + type: ItemType.comment, + text: + state.text.value.isNotEmpty ? state.text.value : null, + dateTime: clock.now(), + ), + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showFavicons: settingsState.showFavicons, + showUserAvatars: settingsState.showUserAvatars, + usernameStyle: UsernameStyle.loggedInUser, ), - usernameStyle: UsernameStyle.loggedInUser, ), ), ), diff --git a/lib/submit/view/submit_page.dart b/lib/submit/view/submit_page.dart index 4ac6ad42..94667d8a 100644 --- a/lib/submit/view/submit_page.dart +++ b/lib/submit/view/submit_page.dart @@ -10,6 +10,7 @@ import 'package:glider/common/widgets/preview_card.dart'; import 'package:glider/item/widgets/item_data_tile.dart'; import 'package:glider/item/widgets/username_widget.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider/submit/cubit/submit_cubit.dart'; import 'package:glider/submit/models/text_input.dart'; import 'package:glider/submit/models/title_input.dart'; @@ -20,12 +21,14 @@ import 'package:go_router/go_router.dart'; class SubmitPage extends StatefulWidget { const SubmitPage( this._submitCubit, - this._authCubit, { + this._authCubit, + this._settingsCubit, { super.key, }); final SubmitCubit _submitCubit; final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; @override State createState() => _SubmitPageState(); @@ -61,7 +64,11 @@ class _SubmitPageState extends State { builder: (context, preview) => PreviewBottomPanel( visible: preview, onChanged: widget._submitCubit.setPreview, - child: _SubmitPreview(widget._submitCubit, widget._authCubit), + child: _SubmitPreview( + widget._submitCubit, + widget._authCubit, + widget._settingsCubit, + ), ), ), floatingActionButton: state.isValid @@ -234,10 +241,15 @@ class _SubmitFormState extends State<_SubmitForm> { } class _SubmitPreview extends StatelessWidget { - const _SubmitPreview(this._submitCubit, this._authCubit); + const _SubmitPreview( + this._submitCubit, + this._authCubit, + this._settingsCubit, + ); final SubmitCubit _submitCubit; final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { @@ -256,22 +268,31 @@ class _SubmitPreview extends StatelessWidget { BlocSelector( bloc: _authCubit, selector: (state) => state.username, - builder: (context, username) => HeroMode( - enabled: false, - child: ItemDataTile( - Item( - id: 0, - username: username, - type: ItemType.story, - title: - state.title.value.isNotEmpty ? state.title.value : null, - url: state.url.value.isNotEmpty - ? Uri.tryParse(state.url.value) - : null, - text: state.text.value.isNotEmpty ? state.text.value : null, - dateTime: clock.now(), + builder: (context, username) => + BlocBuilder( + bloc: _settingsCubit, + builder: (context, settingsState) => HeroMode( + enabled: false, + child: ItemDataTile( + Item( + id: 0, + username: username, + type: ItemType.story, + title: state.title.value.isNotEmpty + ? state.title.value + : null, + url: state.url.value.isNotEmpty + ? Uri.tryParse(state.url.value) + : null, + text: + state.text.value.isNotEmpty ? state.text.value : null, + dateTime: clock.now(), + ), + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showFavicons: settingsState.showFavicons, + showUserAvatars: settingsState.showUserAvatars, + usernameStyle: UsernameStyle.loggedInUser, ), - usernameStyle: UsernameStyle.loggedInUser, ), ), ), From de2ac2d10237d675329895ef177c261d0687c5c3 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 21:37:51 +0100 Subject: [PATCH 009/145] Fix edit/reply/submit text not refreshing --- lib/common/widgets/hacker_news_text.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index 3656d8dd..ad27ad02 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -11,7 +11,9 @@ import 'package:markdown/markdown.dart' as md; typedef ParsedData = List; class HackerNewsText extends StatelessWidget { - HackerNewsText(String data, {super.key}) : parsedData = parse(data); + HackerNewsText(String data) + : parsedData = parse(data), + super(key: ValueKey(data)); const HackerNewsText.parsed(this.parsedData, {super.key}); From ee99ecb1fa31302155704a349f67dc3ee387c247 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 21:46:31 +0100 Subject: [PATCH 010/145] Add padding to pages with (potentially) floating action buttons --- lib/common/constants/app_spacing.dart | 6 ++++++ lib/edit/view/edit_page.dart | 4 ++-- lib/favorites/view/favorites_shell_page.dart | 4 ++++ lib/inbox/view/inbox_shell_page.dart | 4 ++++ lib/item/view/item_page.dart | 4 ++++ lib/reply/view/reply_page.dart | 4 ++-- lib/stories/view/stories_shell_page.dart | 3 +++ lib/stories_search/view/catch_up_shell_page.dart | 4 ++++ lib/submit/view/submit_page.dart | 4 ++-- 9 files changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/common/constants/app_spacing.dart b/lib/common/constants/app_spacing.dart index 1fae9324..4ac32735 100644 --- a/lib/common/constants/app_spacing.dart +++ b/lib/common/constants/app_spacing.dart @@ -19,4 +19,10 @@ abstract final class AppSpacing { ); static const defaultShadowPadding = EdgeInsets.all(2); + + static const floatingActionButtonPageBottomPadding = + EdgeInsets.only(bottom: 88); + + static const twoSmallFloatingActionButtonsPageBottomPadding = + EdgeInsets.only(bottom: 136); } diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index da293170..32414b27 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -66,8 +66,8 @@ class _EditPageState extends State { child: _EditBody(_editCubit), ), ), - const SliverToBoxAdapter( - child: SizedBox(height: AppSpacing.xl), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, ), ], ), diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index b8085557..f343fb2e 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; 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/mixins/data_mixin.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; @@ -62,6 +63,9 @@ class _FavoritesShellPageState extends State { widget._settingsCubit, ), ), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, + ), ], ), ); diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index d1bcdfa3..0296be23 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; 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/mixins/data_mixin.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; @@ -63,6 +64,9 @@ class _InboxShellPageState extends State { widget._settingsCubit, ), ), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, + ), ], ), ); diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index d0f69693..cf65533d 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -534,6 +534,10 @@ class _SliverItemBody extends StatelessWidget { childCount: state.data?.childIds?.length, storyUsername: state.data?.storyUsername, ), + const SliverPadding( + padding: + AppSpacing.twoSmallFloatingActionButtonsPageBottomPadding, + ), ], ), onRetry: () async => _itemCubit.load(), diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index a8625abb..1048702d 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -69,8 +69,8 @@ class _ReplyPageState extends State { child: _ReplyBody(_replyCubit), ), ), - const SliverToBoxAdapter( - child: SizedBox(height: AppSpacing.xl), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, ), ], ), diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index bea98d7e..e5304252 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -77,6 +77,9 @@ class _StoriesShellPageState extends State { widget._settingsCubit, ), ), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, + ), ], ), ); diff --git a/lib/stories_search/view/catch_up_shell_page.dart b/lib/stories_search/view/catch_up_shell_page.dart index 17a73ea3..0acb7f6f 100644 --- a/lib/stories_search/view/catch_up_shell_page.dart +++ b/lib/stories_search/view/catch_up_shell_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; +import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; @@ -60,6 +61,9 @@ class _CatchUpShellPageState extends State { widget._settingsCubit, ), ), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, + ), ], ), ); diff --git a/lib/submit/view/submit_page.dart b/lib/submit/view/submit_page.dart index 94667d8a..ddd32ffb 100644 --- a/lib/submit/view/submit_page.dart +++ b/lib/submit/view/submit_page.dart @@ -53,8 +53,8 @@ class _SubmitPageState extends State { child: _SubmitBody(widget._submitCubit), ), ), - const SliverToBoxAdapter( - child: SizedBox(height: AppSpacing.xl), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, ), ], ), From e8760ef67b0062169d54c1f7d76614b51831fca6 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 7 Nov 2023 21:48:16 +0100 Subject: [PATCH 011/145] Remove redundant generic type for value keys --- lib/favorites/view/favorites_shell_page.dart | 2 +- lib/inbox/view/inbox_shell_page.dart | 4 ++-- lib/stories/view/stories_shell_page.dart | 2 +- lib/stories_search/view/sliver_stories_search_body.dart | 2 +- lib/story_item_search/view/story_item_search_view.dart | 2 +- lib/story_similar/view/sliver_story_similar_body.dart | 2 +- lib/user/view/user_page.dart | 2 +- lib/user_item_search/view/user_item_search_view.dart | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index f343fb2e..fadc6cee 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -154,7 +154,7 @@ class _SliverFavoritesBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: ItemType.story, onTap: (context, item) async => context.push( diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 0296be23..92c63292 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -153,7 +153,7 @@ class _SliverInboxBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(parentId), + key: ValueKey(parentId), id: parentId, loadingType: ItemType.story, onTap: (context, item) async => context.push( @@ -166,7 +166,7 @@ class _SliverInboxBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: ItemType.comment, onTap: (context, item) async => context.push( diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index e5304252..907d8dda 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -242,7 +242,7 @@ class _SliverStoriesBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: ItemType.story, showMetadata: settingsState.showStoryMetadata, diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index d5f28bbf..85a29c5a 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -56,7 +56,7 @@ class SliverStoriesSearchBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: ItemType.story, showMetadata: settingsState.showStoryMetadata, diff --git a/lib/story_item_search/view/story_item_search_view.dart b/lib/story_item_search/view/story_item_search_view.dart index 094e5075..bf585e88 100644 --- a/lib/story_item_search/view/story_item_search_view.dart +++ b/lib/story_item_search/view/story_item_search_view.dart @@ -71,7 +71,7 @@ class _SliverStoryItemSearchBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: _storyItemSearchBloc.itemId == id ? ItemType.story diff --git a/lib/story_similar/view/sliver_story_similar_body.dart b/lib/story_similar/view/sliver_story_similar_body.dart index 51fab7b6..26ca7db1 100644 --- a/lib/story_similar/view/sliver_story_similar_body.dart +++ b/lib/story_similar/view/sliver_story_similar_body.dart @@ -80,7 +80,7 @@ class SliverStorySimilarBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, storyUsername: storyUsername, loadingType: ItemType.story, diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index b185888f..024b729c 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -351,7 +351,7 @@ class _SliverUserBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: ItemType.story, onTap: (context, item) async => context.push( diff --git a/lib/user_item_search/view/user_item_search_view.dart b/lib/user_item_search/view/user_item_search_view.dart index 8704c8a7..aa584eac 100644 --- a/lib/user_item_search/view/user_item_search_view.dart +++ b/lib/user_item_search/view/user_item_search_view.dart @@ -71,7 +71,7 @@ class _SliverUserItemSearchBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), + key: ValueKey(id), id: id, loadingType: ItemType.story, onTap: (context, item) async => context.push( From 8076a939ad6af197b505857ec479bb43cd213d90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Nov 2023 21:02:20 +0100 Subject: [PATCH 012/145] Bump relative_time from 4.0.1 to 5.0.0 (#129) Bumps [relative_time](https://github.com/Mosc/relative_time) from 4.0.1 to 5.0.0. - [Changelog](https://github.com/Mosc/relative_time/blob/master/CHANGELOG.md) - [Commits](https://github.com/Mosc/relative_time/compare/v4.0.1...v5.0.0) --- updated-dependencies: - dependency-name: relative_time dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 100990ad..34aad8c7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -751,10 +751,10 @@ packages: dependency: "direct main" description: name: relative_time - sha256: f0475e5b922f5b125fba1d73ee03ca6d68fb85766fed27d1ad82c8b932ba4ea0 + sha256: "4e6c3b27d98ff6af5061b6dbaca178b5aa2607b7a7c2a77e6cae1d32b2759893" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "5.0.0" rxdart: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b1c42d22..b492c512 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,7 +38,7 @@ dependencies: package_info_plus: ^4.2.0 path_provider: ^2.1.1 pub_semver: ^2.1.4 - relative_time: ^4.0.1 + relative_time: ^5.0.0 rxdart: ^0.28.0-dev.0 scrollview_observer: ^1.17.0 share_plus: ^7.2.1 From fba06e0e91e6d61739c2428d528213b3a1a4c2a7 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 8 Nov 2023 21:37:42 +0100 Subject: [PATCH 013/145] Use LeanCode's custom lints --- analysis_options.yaml | 4 + packages/glider_data/analysis_options.yaml | 4 + packages/glider_data/pubspec.yaml | 3 +- packages/glider_domain/analysis_options.yaml | 4 + .../behavior_subject_extension.dart | 4 +- .../lib/src/item_interaction_repository.dart | 20 +-- .../lib/src/item_repository.dart | 8 +- .../lib/src/user_interaction_repository.dart | 4 +- .../lib/src/user_repository.dart | 4 +- packages/glider_domain/pubspec.yaml | 5 +- pubspec.lock | 124 ++++++++++++++++-- pubspec.yaml | 7 +- 12 files changed, 155 insertions(+), 36 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 2e09436c..fd51df18 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,5 +1,9 @@ include: package:leancode_lint/analysis_options.yaml +analyzer: + plugins: + custom_lint + linter: rules: always_put_control_body_on_new_line: false diff --git a/packages/glider_data/analysis_options.yaml b/packages/glider_data/analysis_options.yaml index 4af9cbc4..a49ee7ec 100644 --- a/packages/glider_data/analysis_options.yaml +++ b/packages/glider_data/analysis_options.yaml @@ -1 +1,5 @@ include: package:leancode_lint/analysis_options.yaml + +analyzer: + plugins: + custom_lint diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index 74f3f15a..474a736b 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -14,5 +14,6 @@ dependencies: shared_preferences: ^2.2.1 dev_dependencies: + custom_lint: ^0.5.6 dependency_validator: ^3.2.3 - leancode_lint: ^6.0.0 + leancode_lint: ^7.0.0+1 diff --git a/packages/glider_domain/analysis_options.yaml b/packages/glider_domain/analysis_options.yaml index 4af9cbc4..a49ee7ec 100644 --- a/packages/glider_domain/analysis_options.yaml +++ b/packages/glider_domain/analysis_options.yaml @@ -1 +1,5 @@ include: package:leancode_lint/analysis_options.yaml + +analyzer: + plugins: + custom_lint diff --git a/packages/glider_domain/lib/src/extensions/behavior_subject_extension.dart b/packages/glider_domain/lib/src/extensions/behavior_subject_extension.dart index cabac819..931f85a0 100644 --- a/packages/glider_domain/lib/src/extensions/behavior_subject_extension.dart +++ b/packages/glider_domain/lib/src/extensions/behavior_subject_extension.dart @@ -5,8 +5,8 @@ extension BehaviorSubjectExtension on BehaviorSubject { try { final value = await asyncValue(); add(value); - } on Object catch (e, s) { - addError(e, s); + } on Object catch (e, st) { + addError(e, st); } } } diff --git a/packages/glider_domain/lib/src/item_interaction_repository.dart b/packages/glider_domain/lib/src/item_interaction_repository.dart index 8e2305dc..d76afb55 100644 --- a/packages/glider_domain/lib/src/item_interaction_repository.dart +++ b/packages/glider_domain/lib/src/item_interaction_repository.dart @@ -42,8 +42,8 @@ class ItemInteractionRepository { final ids = await _sharedPreferencesService.getVisitedIds(); _visitedStreamController.add(ids); return ids; - } on Object catch (e, s) { - _visitedStreamController.addError(e, s); + } on Object catch (e, st) { + _visitedStreamController.addError(e, st); rethrow; } } @@ -53,8 +53,8 @@ class ItemInteractionRepository { final ids = await _sharedPreferencesService.getUpvotedIds(); _upvotedStreamController.add(ids); return ids; - } on Object catch (e, s) { - _upvotedStreamController.addError(e, s); + } on Object catch (e, st) { + _upvotedStreamController.addError(e, st); rethrow; } } @@ -64,8 +64,8 @@ class ItemInteractionRepository { final ids = await _sharedPreferencesService.getDownvotedIds(); _downvotedStreamController.add(ids); return ids; - } on Object catch (e, s) { - _downvotedStreamController.addError(e, s); + } on Object catch (e, st) { + _downvotedStreamController.addError(e, st); rethrow; } } @@ -75,8 +75,8 @@ class ItemInteractionRepository { final ids = await _sharedPreferencesService.getFavoritedIds(); _favoritedStreamController.add(ids); return ids; - } on Object catch (e, s) { - _favoritedStreamController.addError(e, s); + } on Object catch (e, st) { + _favoritedStreamController.addError(e, st); rethrow; } } @@ -86,8 +86,8 @@ class ItemInteractionRepository { final ids = await _sharedPreferencesService.getFlaggedIds(); _flaggedStreamController.add(ids); return ids; - } on Object catch (e, s) { - _flaggedStreamController.addError(e, s); + } on Object catch (e, st) { + _flaggedStreamController.addError(e, st); rethrow; } } diff --git a/packages/glider_domain/lib/src/item_repository.dart b/packages/glider_domain/lib/src/item_repository.dart index da673104..1ff770f8 100644 --- a/packages/glider_domain/lib/src/item_repository.dart +++ b/packages/glider_domain/lib/src/item_repository.dart @@ -133,8 +133,8 @@ class ItemRepository { final item = await compute(Item.fromDto, dto); _itemStreamControllers.getOrAdd(id).add(item); return item; - } on Object catch (e, s) { - _itemStreamControllers.getOrAdd(id).addError(e, s); + } on Object catch (e, st) { + _itemStreamControllers.getOrAdd(id).addError(e, st); rethrow; } } @@ -189,8 +189,8 @@ class ItemRepository { ); } } - } on Object catch (e, s) { - yield* Stream.error(e, s); + } on Object catch (e, st) { + yield* Stream.error(e, st); } } diff --git a/packages/glider_domain/lib/src/user_interaction_repository.dart b/packages/glider_domain/lib/src/user_interaction_repository.dart index 9dc45e43..ce34b838 100644 --- a/packages/glider_domain/lib/src/user_interaction_repository.dart +++ b/packages/glider_domain/lib/src/user_interaction_repository.dart @@ -30,8 +30,8 @@ class UserInteractionRepository { final usernames = await _sharedPreferencesService.getBlockedUsernames(); _blockedStreamController.add(usernames); return usernames; - } on Object catch (e, s) { - _blockedStreamController.addError(e, s); + } on Object catch (e, st) { + _blockedStreamController.addError(e, st); rethrow; } } diff --git a/packages/glider_domain/lib/src/user_repository.dart b/packages/glider_domain/lib/src/user_repository.dart index 7628387b..04e74630 100644 --- a/packages/glider_domain/lib/src/user_repository.dart +++ b/packages/glider_domain/lib/src/user_repository.dart @@ -23,8 +23,8 @@ class UserRepository { final user = await compute(User.fromDto, dto); _userStreamControllers.getOrAdd(username).add(user); return user; - } on Object catch (e, s) { - _userStreamControllers.getOrAdd(username).addError(e, s); + } on Object catch (e, st) { + _userStreamControllers.getOrAdd(username).addError(e, st); rethrow; } } diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index a4c33449..16e734f0 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -14,8 +14,9 @@ dependencies: material_color_utilities: any package_info_plus: ^4.2.0 pub_semver: ^2.1.4 - rxdart: ^0.28.0-dev.0 + rxdart: ^0.27.7 dev_dependencies: + custom_lint: ^0.5.6 dependency_validator: ^3.2.3 - leancode_lint: ^6.0.0 + leancode_lint: ^7.0.0+1 diff --git a/pubspec.lock b/pubspec.lock index 34aad8c7..3661d36b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,30 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + url: "https://pub.dev" + source: hosted + version: "64.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" ansi_styles: dependency: transitive description: @@ -13,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" args: dependency: transitive description: @@ -89,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" cli_launcher: dependency: transitive description: @@ -169,6 +201,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: f9a828b696930cf8307f9a3617b2b65c9b370e484dc845d69100cadb77506778 + url: "https://pub.dev" + source: hosted + version: "0.5.6" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: c6f656a4d83385fc0656ae60410ed06bb382898c45627bfb8bbaa323aea97883 + url: "https://pub.dev" + source: hosted + version: "0.5.6" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: e20a67737adcf0cf2465e734dd624af535add11f9edd1f2d444909b5b0749650 + url: "https://pub.dev" + source: hosted + version: "0.5.6" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + url: "https://pub.dev" + source: hosted + version: "2.3.3" dependency_validator: dependency: "direct dev" description: @@ -295,10 +359,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619" + sha256: "35108526a233cc0755664d445f8a6b4b61e6f8fe993b3658b80b4a26827fc196" url: "https://pub.dev" source: hosted - version: "0.6.18" + version: "0.6.18+2" flutter_secure_storage: dependency: "direct main" description: @@ -365,6 +429,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + url: "https://pub.dev" + source: hosted + version: "2.4.1" glider_data: dependency: "direct main" description: @@ -411,6 +483,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.3" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + url: "https://pub.dev" + source: hosted + version: "4.1.0" html: dependency: transitive description: @@ -487,10 +567,10 @@ packages: dependency: "direct dev" description: name: leancode_lint - sha256: da5ae45a53e9cc78c7306ff4a292d266867bc81a7629b867f5c3d01d64c8939c + sha256: "1e99cba16e084a18ce966a7df270d6da6a11ab236caac716aa1fb2359eb277eb" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "7.0.0+1" logging: dependency: transitive description: @@ -759,10 +839,10 @@ packages: dependency: "direct main" description: name: rxdart - sha256: "93bca4fe609a74c7fd162d23510dd08281e3c1b1b3cff7db99dbe57108c82cdf" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" url: "https://pub.dev" source: hosted - version: "0.28.0-dev.0" + version: "0.27.7" scrollview_observer: dependency: "direct main" description: @@ -888,6 +968,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -1004,10 +1092,10 @@ packages: dependency: transitive description: name: uuid - sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.1" vector_math: dependency: transitive description: @@ -1016,6 +1104,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b492c512..d5ec9db6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: flutter_inappwebview: ^6.0.0-beta.25 flutter_localizations: sdk: flutter - flutter_markdown: ^0.6.18 + flutter_markdown: ^0.6.18+2 flutter_secure_storage: ^9.0.0 formz: ^0.6.1 glider_data: @@ -39,7 +39,7 @@ dependencies: path_provider: ^2.1.1 pub_semver: ^2.1.4 relative_time: ^5.0.0 - rxdart: ^0.28.0-dev.0 + rxdart: ^0.27.7 scrollview_observer: ^1.17.0 share_plus: ^7.2.1 shared_preferences: ^2.2.2 @@ -47,9 +47,10 @@ dependencies: url_launcher: ^6.2.1 dev_dependencies: + custom_lint: ^0.5.6 dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 - leancode_lint: ^6.0.0 + leancode_lint: ^7.0.0+1 melos: ^3.2.0 dependency_overrides: From 9c024f47ff6e497b512c72cadeaf081a13ff9b86 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 9 Nov 2023 19:48:14 +0100 Subject: [PATCH 014/145] Fix user copy/share action --- lib/user/models/user_action.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/user/models/user_action.dart b/lib/user/models/user_action.dart index ed0bbad0..1ffd0274 100644 --- a/lib/user/models/user_action.dart +++ b/lib/user/models/user_action.dart @@ -86,7 +86,7 @@ enum UserAction, S> implements MenuItem { await context.push( AppRoute.userValueDialog .location(parameters: {'id': userCubit.username}), - extra: (userCubit.username, context.l10n.copy), + extra: context.l10n.copy, ); if (valueAction != null) { @@ -97,7 +97,7 @@ enum UserAction, S> implements MenuItem { await context.push( AppRoute.userValueDialog .location(parameters: {'id': userCubit.username}), - extra: (userCubit.username, context.l10n.share), + extra: context.l10n.share, ); if (valueAction != null) { From 0107e242b57e20233ad3a22a614906a0b778d94e Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 9 Nov 2023 19:50:03 +0100 Subject: [PATCH 015/145] Make dialogs look more similar --- lib/item/widgets/item_value_dialog.dart | 34 +++++++++++++------- lib/settings/widgets/menu_list_tile.dart | 2 +- lib/settings/widgets/theme_color_dialog.dart | 2 +- lib/user/widgets/user_value_dialog.dart | 34 +++++++++++++------- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/lib/item/widgets/item_value_dialog.dart b/lib/item/widgets/item_value_dialog.dart index 9cfb6bb9..b9449e0e 100644 --- a/lib/item/widgets/item_value_dialog.dart +++ b/lib/item/widgets/item_value_dialog.dart @@ -46,18 +46,30 @@ class _ItemValueDialogState extends State { builder: (context, authState) => BlocBuilder( bloc: widget._settingsCubit, - builder: (context, settingsState) => SimpleDialog( + builder: (context, settingsState) => AlertDialog( title: widget.title != null ? Text(widget.title!) : null, - children: [ - for (final value in ItemValue.values) - if (value.isVisible(state, authState, settingsState)) - ListTile( - leading: Icon(value.icon(state)), - title: Text(value.label(context, state)), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xxl), - onTap: () => context.pop(value), - ), + contentPadding: const EdgeInsets.all(AppSpacing.m), + content: SizedBox( + width: 0, + child: ListView( + shrinkWrap: true, + children: [ + for (final value in ItemValue.values) + if (value.isVisible(state, authState, settingsState)) + ListTile( + leading: Icon(value.icon(state)), + title: Text(value.label(context, state)), + onTap: () => context.pop(value), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: + Text(MaterialLocalizations.of(context).cancelButtonLabel), + ), ], ), ), diff --git a/lib/settings/widgets/menu_list_tile.dart b/lib/settings/widgets/menu_list_tile.dart index d09d16cc..81bde28b 100644 --- a/lib/settings/widgets/menu_list_tile.dart +++ b/lib/settings/widgets/menu_list_tile.dart @@ -16,7 +16,7 @@ class MenuListTile extends StatelessWidget { final Widget? trailing; final bool enabled; final void Function(T)? onChanged; - final List values; + final Iterable values; final bool Function(T) selected; final String Function(T) labelBuilder; diff --git a/lib/settings/widgets/theme_color_dialog.dart b/lib/settings/widgets/theme_color_dialog.dart index 0d2dacb3..25f18469 100644 --- a/lib/settings/widgets/theme_color_dialog.dart +++ b/lib/settings/widgets/theme_color_dialog.dart @@ -26,7 +26,7 @@ class ThemeColorDialog extends StatelessWidget { title: Text(context.l10n.themeColor), contentPadding: AppSpacing.defaultTilePadding, content: SizedBox( - width: double.maxFinite, + width: 0, child: GridView.extent( maxCrossAxisExtent: 64, shrinkWrap: true, diff --git a/lib/user/widgets/user_value_dialog.dart b/lib/user/widgets/user_value_dialog.dart index 6e07f5ea..6261b6da 100644 --- a/lib/user/widgets/user_value_dialog.dart +++ b/lib/user/widgets/user_value_dialog.dart @@ -46,18 +46,30 @@ class _UserValueDialogState extends State { builder: (context, authState) => BlocBuilder( bloc: widget._settingsCubit, - builder: (context, settingsState) => SimpleDialog( + builder: (context, settingsState) => AlertDialog( title: widget.title != null ? Text(widget.title!) : null, - children: [ - for (final value in UserValue.values) - if (value.isVisible(state, authState, settingsState)) - ListTile( - leading: Icon(value.icon(state)), - title: Text(value.label(context, state)), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xxl), - onTap: () => context.pop(value), - ), + contentPadding: const EdgeInsets.all(AppSpacing.m), + content: SizedBox( + width: 0, + child: ListView( + shrinkWrap: true, + children: [ + for (final value in UserValue.values) + if (value.isVisible(state, authState, settingsState)) + ListTile( + leading: Icon(value.icon(state)), + title: Text(value.label(context, state)), + onTap: () => context.pop(value), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: + Text(MaterialLocalizations.of(context).cancelButtonLabel), + ), ], ), ), From bca91287921c3f4f579634664d799d96239926d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Nov 2023 22:29:56 +0100 Subject: [PATCH 016/145] Bump go_router from 12.1.0 to 12.1.1 (#131) Bumps [go_router](https://github.com/flutter/packages/tree/main/packages) from 12.1.0 to 12.1.1. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/go_router-v12.1.1/packages) --- updated-dependencies: - dependency-name: go_router dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3661d36b..1f6f5378 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -463,10 +463,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "5098760d7478aabfe682a462bf121d61bc5dbe5df5aac8dad733564a0aee33bc" + sha256: c247a4f76071c3b97bb5ae8912968870d5565644801c5e09f3bc961b4d874895 url: "https://pub.dev" source: hosted - version: "12.1.0" + version: "12.1.1" graphs: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d5ec9db6..b68ee20a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,7 +29,7 @@ dependencies: path: packages/glider_data glider_domain: path: packages/glider_domain - go_router: ^12.1.0 + go_router: ^12.1.1 http: ^1.1.0 hydrated_bloc: ^9.1.2 intl: any From 99a41ec7b8d390feea753567dbe4344b09ea891f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 19:54:38 +0100 Subject: [PATCH 017/145] Bump bluefireteam/melos-action from 2 to 3 (#136) Bumps [bluefireteam/melos-action](https://github.com/bluefireteam/melos-action) from 2 to 3. - [Release notes](https://github.com/bluefireteam/melos-action/releases) - [Commits](https://github.com/bluefireteam/melos-action/compare/v2...v3) --- updated-dependencies: - dependency-name: bluefireteam/melos-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a0f32a8..6b571655 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: channel: any cache: true - name: Setup Melos - uses: bluefireteam/melos-action@v2 + uses: bluefireteam/melos-action@v3 - name: Run static analysis checks run: melos lint - name: Build Android APK (profile) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2cb2251e..10c80c65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: channel: any cache: true - name: Setup Melos - uses: bluefireteam/melos-action@v2 + uses: bluefireteam/melos-action@v3 - name: Build Android APK run: flutter build apk - name: Build Android App Bundle From 9e9acc3dcde8390cc70dc4b6becd46c17bb4964e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 19:59:38 +0100 Subject: [PATCH 018/145] Bump scrollview_observer from 1.17.0 to 1.18.0 (#137) Bumps [scrollview_observer](https://github.com/LinXunFeng/flutter_scrollview_observer) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/LinXunFeng/flutter_scrollview_observer/releases) - [Changelog](https://github.com/fluttercandies/flutter_scrollview_observer/blob/main/CHANGELOG.md) - [Commits](https://github.com/LinXunFeng/flutter_scrollview_observer/compare/1.17.0...1.18.0) --- updated-dependencies: - dependency-name: scrollview_observer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 1f6f5378..41b0ced0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -847,10 +847,10 @@ packages: dependency: "direct main" description: name: scrollview_observer - sha256: "3e736a2bd81a3d59f9d1f16b913d3b46f52bef301442f929389d72bbaad1cee8" + sha256: "3206607305d572196a0fd7b434720142d5295ad21cfefd9d103daca63a2489f2" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b68ee20a..cd109591 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,7 +40,7 @@ dependencies: pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 - scrollview_observer: ^1.17.0 + scrollview_observer: ^1.18.0 share_plus: ^7.2.1 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 From 0b345acef3b4b6f5e3b4c8a4503828fb82c158ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 19:59:51 +0100 Subject: [PATCH 019/145] Bump flutter_inappwebview from 6.0.0-beta.25 to 6.0.0-beta.27 (#138) Bumps [flutter_inappwebview](https://github.com/pichillilorenzo/flutter_inappwebview) from 6.0.0-beta.25 to 6.0.0-beta.27. - [Release notes](https://github.com/pichillilorenzo/flutter_inappwebview/releases) - [Changelog](https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/CHANGELOG.md) - [Commits](https://github.com/pichillilorenzo/flutter_inappwebview/compare/v6.0.0-beta.25...v6.0.0-beta.27) --- updated-dependencies: - dependency-name: flutter_inappwebview dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 41b0ced0..74419658 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -330,10 +330,10 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: "786b124c944f4914670868c9775c90600a4d4674497f617190c0d676019e337b" + sha256: "3307260fdf5819de45c7b4889ad70f9b313c7915ad828af4b8004a974de7578a" url: "https://pub.dev" source: hosted - version: "6.0.0-beta.25" + version: "6.0.0-beta.27" flutter_inappwebview_internal_annotations: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index cd109591..50d8f52c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.25 + flutter_inappwebview: ^6.0.0-beta.27 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 From 584fbdb26f82c86a8d7a794795f92194cf1fb272 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 14 Nov 2023 19:49:37 +0100 Subject: [PATCH 020/145] Make code text smaller --- lib/common/widgets/hacker_news_text.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index ad27ad02..6e986465 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -66,7 +66,7 @@ class HackerNewsText extends StatelessWidget { color: Theme.of(context).colorScheme.primary, decoration: TextDecoration.underline, ), - code: Theme.of(context).textTheme.bodyMedium?.copyWith( + code: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.secondary, fontFamily: 'NotoSansMono', ), From 874af9b2afa4fdeffe2bd53db5c1cd97dba6198a Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 14 Nov 2023 19:52:15 +0100 Subject: [PATCH 021/145] Add font setting --- lib/app/view/app.dart | 6 +- lib/l10n/arb/app_en.arb | 20 ++-- lib/settings/cubit/settings_cubit.dart | 33 ++++-- lib/settings/cubit/settings_state.dart | 61 +++++----- lib/settings/view/settings_page.dart | 110 +++++++++++------- lib/settings/widgets/menu_list_tile.dart | 6 +- .../lib/src/shared_preferences_service.dart | 76 ++++++------ .../lib/src/settings_repository.dart | 65 ++++++----- pubspec.lock | 8 ++ pubspec.yaml | 1 + 10 files changed, 229 insertions(+), 157 deletions(-) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 5ba3e5f8..e839b94d 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -8,6 +8,7 @@ import 'package:glider/app/extensions/theme_mode_extension.dart'; import 'package:glider/app/extensions/variant_extension.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:relative_time/relative_time.dart'; class App extends StatelessWidget { @@ -33,7 +34,8 @@ class App extends StatelessWidget { previous.useDynamicTheme != current.useDynamicTheme || previous.themeColor != current.themeColor || previous.themeVariant != current.themeVariant || - previous.usePureBackground != current.usePureBackground, + previous.usePureBackground != current.usePureBackground || + previous.font != current.font, builder: (context, state) => MaterialApp.router( routerConfig: _routerConfig, theme: _buildTheme(context, state, lightDynamic, Brightness.light), @@ -76,7 +78,7 @@ class App extends StatelessWidget { : Colors.white : null, ), - fontFamily: 'NotoSans', + textTheme: GoogleFonts.getTextTheme(state.font, const TextTheme()), menuTheme: const MenuThemeData( style: MenuStyle( shape: MaterialStatePropertyAll( diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index c146fc8e..8621c9db 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -11,9 +11,16 @@ "whatsNewDescription": "> “It’s important to remember that when you start from scratch there is *absolutely no reason* to believe that you are going to do a better job than you did the first time.” — *Joel Spolsky*\n\nWelcome to the new Glider for Hacker News!\n\nDefying rationality, this update is the result of a *Thing You Should Never Do* [0]: rewriting the code from scratch. Accompanying this change is a UI overhaul, based on the Material 3 design system.\n\nLogin and registration is now deferred to the Hacker News website for increased security and reliability. This unfortunately necessitates logging in again, once.\n\nOverall, most users should feel at home quickly, although not every feature survived the metamorphosis, for varying reasons. Please provide feature requests and bug reports through the issue tracker [1].\n---\n[0]: https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/\n\n[1]: https://github.com/Mosc/Glider/issues", "explore": "Explore", "settings": "Settings", + "theme": "Theme", + "themeMode": "Theme mode", + "dynamicTheme": "Dynamic theme", + "dynamicThemeDescription": "Uses system color scheme", + "themeColor": "Theme color", + "themeVariant": "Theme variant", + "pureBackground": "Pure background", + "pureBackgroundDescription": "White on light theme, black on dark theme", + "font": "Font", "appearance": "Appearance", - "behavior": "Behavior", - "about": "About", "largeStoryStyle": "Large stories", "largeStoryStyleDescription": "Shows URL and larger title", "favicons": "Story favicons", @@ -22,13 +29,7 @@ "actionButtons": "Action buttons", "actionButtonsDescription": "Makes some metadata interactive", "userAvatars": "User avatars", - "themeMode": "Theme mode", - "dynamicTheme": "Dynamic theme", - "dynamicThemeDescription": "Uses system color scheme", - "themeColor": "Theme color", - "themeVariant": "Theme variant", - "pureBackground": "Pure background", - "pureBackgroundDescription": "White on light theme, black on dark theme", + "behavior": "Behavior", "showJobs": "Show job stories", "showJobsDescription": "Always shown on jobs tab", "threadNavigation": "Thread navigation", @@ -38,6 +39,7 @@ "data": "Data", "exportFavorites": "Export favorites", "exportFavoritesDescription": "Shares favorites as IDs in JSON format", + "about": "About", "appVersion": "App version", "privacyPolicy": "Privacy policy", "privacyPolicyYc": "Privacy policy (YC)", diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index cabf18b0..a19b021d 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -26,21 +26,30 @@ class SettingsCubit extends Cubit { final ItemInteractionRepository _itemInteractionRepository; Future _load() async { + final themeMode = await _settingsRepository.getThemeMode(); + final useDynamicTheme = await _settingsRepository.getUseDynamicTheme(); + final themeColor = await _settingsRepository.getThemeColor(); + final themeVariant = await _settingsRepository.getThemeVariant(); + final usePureBackground = await _settingsRepository.getUsePureBackground(); + final font = await _settingsRepository.getFont(); final useLargeStoryStyle = await _settingsRepository.getUseLargeStoryStyle(); final showFavicons = await _settingsRepository.getShowFavicons(); final showStoryMetadata = await _settingsRepository.getShowStoryMetadata(); final showUserAvatars = await _settingsRepository.getShowUserAvatars(); final useActionButtons = await _settingsRepository.getUseActionButtons(); - final useDynamicTheme = await _settingsRepository.getUseDynamicTheme(); - final themeColor = await _settingsRepository.getThemeColor(); - final themeVariant = await _settingsRepository.getThemeVariant(); - final usePureBackground = await _settingsRepository.getUsePureBackground(); final useThreadNavigation = await _settingsRepository.getUseThreadNavigation(); final enableDownvoting = await _settingsRepository.getEnableDownvoting(); safeEmit( state.copyWith( + themeMode: themeMode != null ? () => themeMode : null, + useDynamicTheme: useDynamicTheme != null ? () => useDynamicTheme : null, + themeColor: themeColor != null ? () => themeColor : null, + themeVariant: themeVariant != null ? () => themeVariant : null, + usePureBackground: + usePureBackground != null ? () => usePureBackground : null, + font: font != null ? () => font : null, useLargeStoryStyle: useLargeStoryStyle != null ? () => useLargeStoryStyle : null, showFavicons: showFavicons != null ? () => showFavicons : null, @@ -49,11 +58,6 @@ class SettingsCubit extends Cubit { showUserAvatars: showUserAvatars != null ? () => showUserAvatars : null, useActionButtons: useActionButtons != null ? () => useActionButtons : null, - useDynamicTheme: useDynamicTheme != null ? () => useDynamicTheme : null, - themeColor: themeColor != null ? () => themeColor : null, - themeVariant: themeVariant != null ? () => themeVariant : null, - usePureBackground: - usePureBackground != null ? () => usePureBackground : null, useThreadNavigation: useThreadNavigation != null ? () => useThreadNavigation : null, enableDownvoting: @@ -174,6 +178,17 @@ class SettingsCubit extends Cubit { } } + Future setFont(String value) async { + await _settingsRepository.setFont(value: value); + final font = await _settingsRepository.getFont(); + + if (font != null) { + safeEmit( + state.copyWith(font: () => font), + ); + } + } + Future setShowJobs(bool value) async { await _settingsRepository.setShowJobs(value: value); final showJobs = await _settingsRepository.getShowJobs(); diff --git a/lib/settings/cubit/settings_state.dart b/lib/settings/cubit/settings_state.dart index 01ac86f1..444368e8 100644 --- a/lib/settings/cubit/settings_state.dart +++ b/lib/settings/cubit/settings_state.dart @@ -2,54 +2,66 @@ part of 'settings_cubit.dart'; class SettingsState with EquatableMixin { const SettingsState({ - this.useLargeStoryStyle = true, - this.showFavicons = true, - this.showStoryMetadata = true, - this.showUserAvatars = true, - this.useActionButtons = false, this.themeMode = ThemeMode.system, this.useDynamicTheme = true, this.themeColor = const Color(0xff6750a4), this.themeVariant = Variant.tonalSpot, this.usePureBackground = false, + this.font = 'Noto Sans', + this.useLargeStoryStyle = true, + this.showFavicons = true, + this.showStoryMetadata = true, + this.showUserAvatars = true, + this.useActionButtons = false, this.showJobs = true, this.useThreadNavigation = true, this.enableDownvoting = false, this.appVersion, }); - final bool useLargeStoryStyle; - final bool showFavicons; - final bool showStoryMetadata; - final bool showUserAvatars; - final bool useActionButtons; final ThemeMode themeMode; final bool useDynamicTheme; final Color themeColor; final Variant themeVariant; final bool usePureBackground; + final String font; + final bool useLargeStoryStyle; + final bool showFavicons; + final bool showStoryMetadata; + final bool showUserAvatars; + final bool useActionButtons; final bool showJobs; final bool useThreadNavigation; final bool enableDownvoting; final Version? appVersion; SettingsState copyWith({ - bool Function()? useLargeStoryStyle, - bool Function()? showFavicons, - bool Function()? showStoryMetadata, - bool Function()? showUserAvatars, - bool Function()? useActionButtons, ThemeMode Function()? themeMode, bool Function()? useDynamicTheme, Color Function()? themeColor, Variant Function()? themeVariant, bool Function()? usePureBackground, + String Function()? font, + bool Function()? useLargeStoryStyle, + bool Function()? showFavicons, + bool Function()? showStoryMetadata, + bool Function()? showUserAvatars, + bool Function()? useActionButtons, bool Function()? showJobs, bool Function()? useThreadNavigation, bool Function()? enableDownvoting, Version? Function()? appVersion, }) => SettingsState( + themeMode: themeMode != null ? themeMode() : this.themeMode, + useDynamicTheme: + useDynamicTheme != null ? useDynamicTheme() : this.useDynamicTheme, + themeColor: themeColor != null ? themeColor() : this.themeColor, + themeVariant: themeVariant != null ? themeVariant() : this.themeVariant, + usePureBackground: usePureBackground != null + ? usePureBackground() + : this.usePureBackground, + font: font != null ? font() : this.font, useLargeStoryStyle: useLargeStoryStyle != null ? useLargeStoryStyle() : this.useLargeStoryStyle, @@ -62,14 +74,6 @@ class SettingsState with EquatableMixin { useActionButtons: useActionButtons != null ? useActionButtons() : this.useActionButtons, - themeMode: themeMode != null ? themeMode() : this.themeMode, - useDynamicTheme: - useDynamicTheme != null ? useDynamicTheme() : this.useDynamicTheme, - themeColor: themeColor != null ? themeColor() : this.themeColor, - themeVariant: themeVariant != null ? themeVariant() : this.themeVariant, - usePureBackground: usePureBackground != null - ? usePureBackground() - : this.usePureBackground, showJobs: showJobs != null ? showJobs() : this.showJobs, useThreadNavigation: useThreadNavigation != null ? useThreadNavigation() @@ -82,16 +86,17 @@ class SettingsState with EquatableMixin { @override List get props => [ - useLargeStoryStyle, - showFavicons, - showStoryMetadata, - showUserAvatars, - useActionButtons, themeMode, useDynamicTheme, themeColor, themeVariant, usePureBackground, + font, + useLargeStoryStyle, + showFavicons, + showStoryMetadata, + showUserAvatars, + useActionButtons, showJobs, useThreadNavigation, enableDownvoting, diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index df97ecfb..b1cc6cbc 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -15,6 +15,7 @@ import 'package:glider/settings/extensions/variant_extension.dart'; import 'package:glider/settings/widgets/menu_list_tile.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:material_color_utilities/material_color_utilities.dart'; class SettingsPage extends StatelessWidget { @@ -54,6 +55,14 @@ class _SettingsBody extends StatelessWidget { final SettingsCubit _settingsCubit; + static const List _fonts = [ + 'Fira Sans', + 'IBM Plex Sans', + 'Inter', + 'Noto Sans', + 'Open Sans', + 'Roboto', + ]; static const String _authority = 'github.com'; static const String _basePath = 'Mosc/Glider'; static final Uri _privacyPolicyUrl = @@ -75,57 +84,19 @@ class _SettingsBody extends StatelessWidget { Padding( padding: AppSpacing.defaultTilePadding, child: Text( - context.l10n.appearance, + context.l10n.theme, style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Theme.of(context).colorScheme.secondary, ), ), ), - SwitchListTile.adaptive( - value: state.useLargeStoryStyle, - onChanged: _settingsCubit.setUseLargeStoryStyle, - title: Text(context.l10n.largeStoryStyle), - subtitle: Text(context.l10n.largeStoryStyleDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.showFavicons, - onChanged: _settingsCubit.setShowFavicons, - title: Text(context.l10n.favicons), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.showStoryMetadata, - onChanged: _settingsCubit.setShowStoryMetadata, - title: Text(context.l10n.storyMetadata), - subtitle: Text(context.l10n.storyMetadataDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.showUserAvatars, - onChanged: _settingsCubit.setShowUserAvatars, - title: Text(context.l10n.userAvatars), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.useActionButtons, - onChanged: _settingsCubit.setUseActionButtons, - title: Text(context.l10n.actionButtons), - subtitle: Text(context.l10n.actionButtonsDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), MenuListTile( title: Text(context.l10n.themeMode), trailing: Text(state.themeMode.capitalizedLabel), onChanged: (value) async => _settingsCubit.setThemeMode(value), values: ThemeMode.values, selected: (value) => state.themeMode == value, - labelBuilder: (value) => value.capitalizedLabel, + childBuilder: (value) => Text(value.capitalizedLabel), ), SwitchListTile.adaptive( value: state.useDynamicTheme, @@ -158,7 +129,7 @@ class _SettingsBody extends StatelessWidget { onChanged: (value) async => _settingsCubit.setThemeVariant(value), values: Variant.values, selected: (value) => state.themeVariant == value, - labelBuilder: (value) => value.capitalizedLabel, + childBuilder: (value) => Text(value.capitalizedLabel), ), SwitchListTile.adaptive( value: state.usePureBackground, @@ -168,6 +139,63 @@ class _SettingsBody extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), + MenuListTile( + title: Text(context.l10n.font), + trailing: Text(state.font), + onChanged: (value) async => _settingsCubit.setFont(value), + values: _fonts, + selected: (value) => state.font == value, + childBuilder: (value) => + Text(value, style: GoogleFonts.getFont(value)), + ), + const Divider(), + Padding( + padding: AppSpacing.defaultTilePadding, + child: Text( + context.l10n.appearance, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + SwitchListTile.adaptive( + value: state.useLargeStoryStyle, + onChanged: _settingsCubit.setUseLargeStoryStyle, + title: Text(context.l10n.largeStoryStyle), + subtitle: Text(context.l10n.largeStoryStyleDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.showFavicons, + onChanged: _settingsCubit.setShowFavicons, + title: Text(context.l10n.favicons), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.showStoryMetadata, + onChanged: _settingsCubit.setShowStoryMetadata, + title: Text(context.l10n.storyMetadata), + subtitle: Text(context.l10n.storyMetadataDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.showUserAvatars, + onChanged: _settingsCubit.setShowUserAvatars, + title: Text(context.l10n.userAvatars), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.useActionButtons, + onChanged: _settingsCubit.setUseActionButtons, + title: Text(context.l10n.actionButtons), + subtitle: Text(context.l10n.actionButtonsDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), BlocBuilder( bloc: _settingsCubit, builder: (context, state) => Padding( diff --git a/lib/settings/widgets/menu_list_tile.dart b/lib/settings/widgets/menu_list_tile.dart index 81bde28b..3d42b23e 100644 --- a/lib/settings/widgets/menu_list_tile.dart +++ b/lib/settings/widgets/menu_list_tile.dart @@ -9,7 +9,7 @@ class MenuListTile extends StatelessWidget { this.onChanged, required this.values, required this.selected, - required this.labelBuilder, + required this.childBuilder, }); final Widget? title; @@ -18,7 +18,7 @@ class MenuListTile extends StatelessWidget { final void Function(T)? onChanged; final Iterable values; final bool Function(T) selected; - final String Function(T) labelBuilder; + final Widget Function(T) childBuilder; @override Widget build(BuildContext context) { @@ -35,7 +35,7 @@ class MenuListTile extends StatelessWidget { visible: selected(value), child: const Icon(Icons.check_outlined), ), - child: Text(labelBuilder(value)), + child: childBuilder(value), ), ], builder: (context, controller, child) => ListTile( diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index 76e1127d..88e365f8 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -5,16 +5,17 @@ class SharedPreferencesService { final SharedPreferences _sharedPreferences; - static const String _useLargeStoryStyleKey = 'use_large_story_style'; - static const String _showFaviconsKey = 'show_favicons'; - static const String _showStoryMetadataKey = 'show_story_metadata'; - static const String _showUserAvatars = 'show_user_avatars'; - static const String _useActionButtonsKey = 'use_action_buttons'; static const String _themeModeKey = 'theme_mode'; static const String _useDynamicThemeKey = 'use_dynamic_theme'; static const String _themeColorKey = 'theme_color'; static const String _themeVariantKey = 'theme_variant'; static const String _usePureBackgroundKey = 'use_pure_background'; + static const String _fontKey = 'font'; + static const String _useLargeStoryStyleKey = 'use_large_story_style'; + static const String _showFaviconsKey = 'show_favicons'; + static const String _showStoryMetadataKey = 'show_story_metadata'; + static const String _showUserAvatars = 'show_user_avatars'; + static const String _useActionButtonsKey = 'use_action_buttons'; static const String _showJobsKey = 'show_jobs'; static const String _useThreadNavigationKey = 'use_thread_navigation'; static const String _enableDownvotingKey = 'enable_downvoting'; @@ -26,36 +27,6 @@ class SharedPreferencesService { static const String _flaggedKey = 'flagged'; static const String _blockedKey = 'blocked'; - Future getUseLargeStoryStyle() async => - _sharedPreferences.getBool(_useLargeStoryStyleKey); - - Future setUseLargeStoryStyle({required bool value}) async => - _sharedPreferences.setBool(_useLargeStoryStyleKey, value); - - Future getShowFavicons() async => - _sharedPreferences.getBool(_showFaviconsKey); - - Future setShowFavicons({required bool value}) async => - _sharedPreferences.setBool(_showFaviconsKey, value); - - Future getShowStoryMetadata() async => - _sharedPreferences.getBool(_showStoryMetadataKey); - - Future setShowStoryMetadata({required bool value}) async => - _sharedPreferences.setBool(_showStoryMetadataKey, value); - - Future getShowUserAvatars() async => - _sharedPreferences.getBool(_showUserAvatars); - - Future setShowUserAvatars({required bool value}) async => - _sharedPreferences.setBool(_showUserAvatars, value); - - Future getUseActionButtons() async => - _sharedPreferences.getBool(_useActionButtonsKey); - - Future setUseActionButtons({required bool value}) async => - _sharedPreferences.setBool(_useActionButtonsKey, value); - Future getThemeMode() async => _sharedPreferences.getString(_themeModeKey); @@ -86,6 +57,41 @@ class SharedPreferencesService { Future setUsePureBackground({required bool value}) async => _sharedPreferences.setBool(_usePureBackgroundKey, value); + Future getFont() async => _sharedPreferences.getString(_fontKey); + + Future setFont({required String value}) async => + _sharedPreferences.setString(_fontKey, value); + + Future getUseLargeStoryStyle() async => + _sharedPreferences.getBool(_useLargeStoryStyleKey); + + Future setUseLargeStoryStyle({required bool value}) async => + _sharedPreferences.setBool(_useLargeStoryStyleKey, value); + + Future getShowFavicons() async => + _sharedPreferences.getBool(_showFaviconsKey); + + Future setShowFavicons({required bool value}) async => + _sharedPreferences.setBool(_showFaviconsKey, value); + + Future getShowStoryMetadata() async => + _sharedPreferences.getBool(_showStoryMetadataKey); + + Future setShowStoryMetadata({required bool value}) async => + _sharedPreferences.setBool(_showStoryMetadataKey, value); + + Future getShowUserAvatars() async => + _sharedPreferences.getBool(_showUserAvatars); + + Future setShowUserAvatars({required bool value}) async => + _sharedPreferences.setBool(_showUserAvatars, value); + + Future getUseActionButtons() async => + _sharedPreferences.getBool(_useActionButtonsKey); + + Future setUseActionButtons({required bool value}) async => + _sharedPreferences.setBool(_useActionButtonsKey, value); + Future getShowJobs() async => _sharedPreferences.getBool(_showJobsKey); Future setShowJobs({required bool value}) async => diff --git a/packages/glider_domain/lib/src/settings_repository.dart b/packages/glider_domain/lib/src/settings_repository.dart index 6205edbd..210ac106 100644 --- a/packages/glider_domain/lib/src/settings_repository.dart +++ b/packages/glider_domain/lib/src/settings_repository.dart @@ -9,36 +9,6 @@ class SettingsRepository { final SharedPreferencesService _sharedPreferencesService; - Future getUseLargeStoryStyle() async => - _sharedPreferencesService.getUseLargeStoryStyle(); - - Future setUseLargeStoryStyle({required bool value}) async => - _sharedPreferencesService.setUseLargeStoryStyle(value: value); - - Future getShowFavicons() async => - _sharedPreferencesService.getShowFavicons(); - - Future setShowFavicons({required bool value}) async => - _sharedPreferencesService.setShowFavicons(value: value); - - Future getShowStoryMetadata() async => - _sharedPreferencesService.getShowStoryMetadata(); - - Future setShowStoryMetadata({required bool value}) async => - _sharedPreferencesService.setShowStoryMetadata(value: value); - - Future getShowUserAvatars() async => - _sharedPreferencesService.getShowUserAvatars(); - - Future setShowUserAvatars({required bool value}) async => - _sharedPreferencesService.setShowUserAvatars(value: value); - - Future getUseActionButtons() async => - _sharedPreferencesService.getUseActionButtons(); - - Future setUseActionButtons({required bool value}) async => - _sharedPreferencesService.setUseActionButtons(value: value); - Future getThemeMode() async { final value = await _sharedPreferencesService.getThemeMode(); return value != null ? ThemeMode.values.byName(value) : null; @@ -75,6 +45,41 @@ class SettingsRepository { Future setUsePureBackground({required bool value}) async => _sharedPreferencesService.setUsePureBackground(value: value); + Future getFont() async => _sharedPreferencesService.getFont(); + + Future setFont({required String value}) async => + _sharedPreferencesService.setFont(value: value); + + Future getUseLargeStoryStyle() async => + _sharedPreferencesService.getUseLargeStoryStyle(); + + Future setUseLargeStoryStyle({required bool value}) async => + _sharedPreferencesService.setUseLargeStoryStyle(value: value); + + Future getShowFavicons() async => + _sharedPreferencesService.getShowFavicons(); + + Future setShowFavicons({required bool value}) async => + _sharedPreferencesService.setShowFavicons(value: value); + + Future getShowStoryMetadata() async => + _sharedPreferencesService.getShowStoryMetadata(); + + Future setShowStoryMetadata({required bool value}) async => + _sharedPreferencesService.setShowStoryMetadata(value: value); + + Future getShowUserAvatars() async => + _sharedPreferencesService.getShowUserAvatars(); + + Future setShowUserAvatars({required bool value}) async => + _sharedPreferencesService.setShowUserAvatars(value: value); + + Future getUseActionButtons() async => + _sharedPreferencesService.getUseActionButtons(); + + Future setUseActionButtons({required bool value}) async => + _sharedPreferencesService.setUseActionButtons(value: value); + Future getShowJobs() async => _sharedPreferencesService.getShowJobs(); Future setShowJobs({required bool value}) async => diff --git a/pubspec.lock b/pubspec.lock index 74419658..dd01673d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -467,6 +467,14 @@ packages: url: "https://pub.dev" source: hosted version: "12.1.1" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8 + url: "https://pub.dev" + source: hosted + version: "6.1.0" graphs: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 50d8f52c..6dac978b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,7 @@ dependencies: glider_domain: path: packages/glider_domain go_router: ^12.1.1 + google_fonts: ^6.1.0 http: ^1.1.0 hydrated_bloc: ^9.1.2 intl: any From c68781e21b29a96f7cf39c00702d7ba64319f029 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 14 Nov 2023 20:48:31 +0100 Subject: [PATCH 022/145] Show snackbar when an interaction fails --- lib/app/models/app_route.dart | 2 - lib/app/router/app_router.dart | 59 ++----- lib/item/cubit/item_cubit.dart | 10 +- lib/item/cubit/item_cubit_event.dart | 7 + lib/item/widgets/item_bottom_sheet.dart | 28 +-- lib/item/widgets/item_tile.dart | 166 ++++++++++-------- .../cubit/navigation_shell_cubit.dart | 5 +- .../cubit/navigation_shell_cubit_event.dart | 5 + .../cubit/navigation_shell_event.dart | 3 - .../widgets/navigation_shell_scaffold.dart | 4 +- lib/user/cubit/user_cubit.dart | 19 +- lib/user/cubit/user_cubit_event.dart | 7 + lib/user/view/user_page.dart | 4 + lib/user/widgets/user_bottom_sheet.dart | 28 +-- lib/user/widgets/user_tile.dart | 68 ++++--- 15 files changed, 214 insertions(+), 201 deletions(-) create mode 100644 lib/item/cubit/item_cubit_event.dart create mode 100644 lib/navigation_shell/cubit/navigation_shell_cubit_event.dart delete mode 100644 lib/navigation_shell/cubit/navigation_shell_event.dart create mode 100644 lib/user/cubit/user_cubit_event.dart diff --git a/lib/app/models/app_route.dart b/lib/app/models/app_route.dart index 8106f0db..4c8137fd 100644 --- a/lib/app/models/app_route.dart +++ b/lib/app/models/app_route.dart @@ -9,12 +9,10 @@ enum AppRoute { themeColorDialog(parent: settings), submit, item, - itemBottomSheet(parent: item), edit(parent: item), reply(parent: item), itemValueDialog(parent: item), user, - userBottomSheet(parent: user), userValueDialog(parent: user), textSelectDialog, confirmDialog; diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index bea85291..a268172c 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:glider/app/container/app_container.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/app/models/dialog_page.dart'; -import 'package:glider/app/models/modal_bottom_sheet_page.dart'; import 'package:glider/auth/view/auth_page.dart'; import 'package:glider/common/widgets/confirm_dialog.dart'; import 'package:glider/common/widgets/text_select_dialog.dart'; @@ -11,7 +10,6 @@ import 'package:glider/edit/view/edit_page.dart'; import 'package:glider/favorites/view/favorites_shell_page.dart'; import 'package:glider/inbox/view/inbox_shell_page.dart'; import 'package:glider/item/view/item_page.dart'; -import 'package:glider/item/widgets/item_bottom_sheet.dart'; import 'package:glider/item/widgets/item_value_dialog.dart'; import 'package:glider/navigation_shell/widgets/navigation_shell_scaffold.dart'; import 'package:glider/reply/view/reply_page.dart'; @@ -21,12 +19,11 @@ import 'package:glider/stories/view/stories_shell_page.dart'; import 'package:glider/stories_search/view/catch_up_shell_page.dart'; import 'package:glider/submit/view/submit_page.dart'; import 'package:glider/user/view/user_page.dart'; -import 'package:glider/user/widgets/user_bottom_sheet.dart'; import 'package:glider/user/widgets/user_value_dialog.dart'; import 'package:glider/whats_new/view/whats_new_page.dart'; import 'package:go_router/go_router.dart'; -final _rootNavigatorKey = GlobalKey(); +final rootNavigatorKey = GlobalKey(); class AppRouter { AppRouter._(this.config); @@ -34,7 +31,7 @@ class AppRouter { factory AppRouter.create(AppContainer appContainer) { return AppRouter._( GoRouter( - navigatorKey: _rootNavigatorKey, + navigatorKey: rootNavigatorKey, initialLocation: AppRoute.stories.location(), debugLogDiagnostics: kDebugMode, routes: [ @@ -115,7 +112,7 @@ class AppRouter { fullscreenDialog: true, child: WhatsNewPage(), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), GoRoute( path: AppRoute.auth.path, @@ -129,7 +126,7 @@ class AppRouter { appContainer.userItemSearchBlocFactory, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), GoRoute( path: AppRoute.settings.path, @@ -139,7 +136,7 @@ class AppRouter { appContainer.settingsCubit, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, routes: [ GoRoute( path: AppRoute.themeColorDialog.path, @@ -148,7 +145,7 @@ class AppRouter { selectedColor: state.extra! as Color?, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), ], ), @@ -162,7 +159,7 @@ class AppRouter { appContainer.settingsCubit, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), GoRoute( path: AppRoute.item.path, @@ -175,21 +172,8 @@ class AppRouter { appContainer.settingsCubit, id: int.parse(state.uri.queryParameters['id']!), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, routes: [ - GoRoute( - path: AppRoute.itemBottomSheet.path, - pageBuilder: (context, state) => ModalBottomSheetPage( - isScrollControlled: true, - builder: (context) => ItemBottomSheet( - appContainer.itemCubitFactory, - appContainer.authCubit, - appContainer.settingsCubit, - id: int.parse(state.uri.queryParameters['id']!), - ), - ), - parentNavigatorKey: _rootNavigatorKey, - ), GoRoute( path: AppRoute.edit.path, pageBuilder: (context, state) => MaterialPage( @@ -200,7 +184,7 @@ class AppRouter { id: int.parse(state.uri.queryParameters['id']!), ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), GoRoute( path: AppRoute.reply.path, @@ -213,7 +197,7 @@ class AppRouter { id: int.parse(state.uri.queryParameters['id']!), ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), GoRoute( path: AppRoute.itemValueDialog.path, @@ -226,7 +210,7 @@ class AppRouter { title: state.extra as String?, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), ], ), @@ -240,21 +224,8 @@ class AppRouter { appContainer.settingsCubit, username: state.uri.queryParameters['id']!, ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, routes: [ - GoRoute( - path: AppRoute.userBottomSheet.path, - pageBuilder: (context, state) => ModalBottomSheetPage( - isScrollControlled: true, - builder: (context) => UserBottomSheet( - appContainer.userCubitFactory, - appContainer.authCubit, - appContainer.settingsCubit, - username: state.uri.queryParameters['id']!, - ), - ), - parentNavigatorKey: _rootNavigatorKey, - ), GoRoute( path: AppRoute.userValueDialog.path, pageBuilder: (context, state) => DialogPage( @@ -266,7 +237,7 @@ class AppRouter { title: state.extra as String?, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), ], ), @@ -277,7 +248,7 @@ class AppRouter { text: state.extra! as String, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), GoRoute( path: AppRoute.confirmDialog.path, @@ -287,7 +258,7 @@ class AppRouter { text: (state.extra as ConfirmDialogExtra?)?.text, ), ), - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, ), ], ), diff --git a/lib/item/cubit/item_cubit.dart b/lib/item/cubit/item_cubit.dart index b7593519..3b2ee4e7 100644 --- a/lib/item/cubit/item_cubit.dart +++ b/lib/item/cubit/item_cubit.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/services.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; @@ -11,9 +12,11 @@ import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:share_plus/share_plus.dart'; +part 'item_cubit_event.dart'; part 'item_state.dart'; -class ItemCubit extends HydratedCubit { +class ItemCubit extends HydratedCubit + with BlocPresentationMixin { ItemCubit( this._itemRepository, this._itemInteractionRepository, @@ -150,6 +153,7 @@ class ItemCubit extends HydratedCubit { safeEmit( state.copyWith(vote: () => vote), ); + emitPresentation(const ItemActionFailedEvent()); } else { await load(); } @@ -167,6 +171,7 @@ class ItemCubit extends HydratedCubit { safeEmit( state.copyWith(vote: () => vote), ); + emitPresentation(const ItemActionFailedEvent()); } else { await load(); } @@ -184,6 +189,7 @@ class ItemCubit extends HydratedCubit { safeEmit( state.copyWith(favorited: () => favorited), ); + emitPresentation(const ItemActionFailedEvent()); } } @@ -198,6 +204,7 @@ class ItemCubit extends HydratedCubit { safeEmit( state.copyWith(flagged: () => flagged), ); + emitPresentation(const ItemActionFailedEvent()); } else { await load(); } @@ -218,6 +225,7 @@ class ItemCubit extends HydratedCubit { data: () => state.data?.copyWith(isDeleted: () => isDeleted), ), ); + emitPresentation(const ItemActionFailedEvent()); } else { await load(); } diff --git a/lib/item/cubit/item_cubit_event.dart b/lib/item/cubit/item_cubit_event.dart new file mode 100644 index 00000000..18a0adbe --- /dev/null +++ b/lib/item/cubit/item_cubit_event.dart @@ -0,0 +1,7 @@ +part of 'item_cubit.dart'; + +sealed class ItemCubitEvent {} + +final class ItemActionFailedEvent implements ItemCubitEvent { + const ItemActionFailedEvent(); +} diff --git a/lib/item/widgets/item_bottom_sheet.dart b/lib/item/widgets/item_bottom_sheet.dart index 2c90fa0b..d37b0053 100644 --- a/lib/item/widgets/item_bottom_sheet.dart +++ b/lib/item/widgets/item_bottom_sheet.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:glider/app/container/app_container.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/widgets/notification_canceler.dart'; import 'package:glider/item/cubit/item_cubit.dart'; @@ -8,42 +7,27 @@ import 'package:glider/item/models/item_action.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:go_router/go_router.dart'; -class ItemBottomSheet extends StatefulWidget { +class ItemBottomSheet extends StatelessWidget { const ItemBottomSheet( - this._itemCubitFactory, + this._itemCubit, this._authCubit, this._settingsCubit, { super.key, - required this.id, }); - final ItemCubitFactory _itemCubitFactory; + final ItemCubit _itemCubit; final AuthCubit _authCubit; final SettingsCubit _settingsCubit; - final int id; - - @override - State createState() => _ItemBottomSheetState(); -} - -class _ItemBottomSheetState extends State { - late final ItemCubit _itemCubit; - - @override - void initState() { - _itemCubit = widget._itemCubitFactory(widget.id); - super.initState(); - } @override Widget build(BuildContext context) { return BlocBuilder( bloc: _itemCubit, builder: (context, state) => BlocBuilder( - bloc: widget._authCubit, + bloc: _authCubit, builder: (context, authState) => BlocBuilder( - bloc: widget._settingsCubit, + bloc: _settingsCubit, builder: (context, settingsState) => NotificationCanceler( child: ListView( @@ -60,7 +44,7 @@ class _ItemBottomSheetState extends State { await action.execute( context, _itemCubit, - widget._authCubit, + _authCubit, ); }, ), diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index ec814f34..3b6df14f 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -1,7 +1,8 @@ +import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; -import 'package:glider/app/models/app_route.dart'; +import 'package:glider/app/router/app_router.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; @@ -10,12 +11,13 @@ import 'package:glider/item/cubit/item_cubit.dart'; import 'package:glider/item/models/item_action.dart'; import 'package:glider/item/models/item_style.dart'; import 'package:glider/item/typedefs/item_typedefs.dart'; +import 'package:glider/item/widgets/item_bottom_sheet.dart'; import 'package:glider/item/widgets/item_data_tile.dart'; import 'package:glider/item/widgets/item_loading_tile.dart'; import 'package:glider/item/widgets/username_widget.dart'; +import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider_domain/glider_domain.dart'; -import 'package:go_router/go_router.dart'; class ItemTile extends StatefulWidget { ItemTile( @@ -89,84 +91,98 @@ class _ItemTileState extends State @override Widget build(BuildContext context) { super.build(context); - return BlocBuilder( + return BlocPresentationListener( bloc: _itemCubit, - builder: (context, state) => BlocBuilder( - bloc: widget._authCubit, - builder: (context, authState) => - BlocBuilder( - bloc: widget._settingsCubit, - builder: (context, settingsState) => state.whenOrDefaultWidgets( - loading: () => ItemLoadingTile( - type: widget.loadingType, - collapsedCount: widget.collapsedCount, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - showMetadata: settingsState.showStoryMetadata, - style: widget.style, - padding: widget.padding, + listener: (context, event) => switch (event) { + ItemActionFailedEvent() => ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.failure), ), - success: () { - final item = state.data!; + ), + }, + child: BlocBuilder( + bloc: _itemCubit, + builder: (context, state) => BlocBuilder( + bloc: widget._authCubit, + builder: (context, authState) => + BlocBuilder( + bloc: widget._settingsCubit, + builder: (context, settingsState) => state.whenOrDefaultWidgets( + loading: () => ItemLoadingTile( + type: widget.loadingType, + collapsedCount: widget.collapsedCount, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showMetadata: settingsState.showStoryMetadata, + style: widget.style, + padding: widget.padding, + ), + success: () { + final item = state.data!; - if (item.type == ItemType.job && !widget.showJobs) { - return const SizedBox.shrink(); - } + if (item.type == ItemType.job && !widget.showJobs) { + return const SizedBox.shrink(); + } - return Material( - type: widget.highlight - ? MaterialType.canvas - : MaterialType.transparency, - elevation: 4, - shadowColor: Colors.transparent, - surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, - child: ItemDataTile( - item, - parsedText: state.parsedText, - visited: state.visited && widget.showVisited, - vote: state.vote, - favorited: state.favorited, - flagged: state.flagged, - blocked: state.blocked, - failed: state.status == Status.failure, - collapsedCount: widget.collapsedCount, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - showFavicons: settingsState.showFavicons, - showMetadata: widget.showMetadata, - showUserAvatars: settingsState.showUserAvatars, - style: widget.style, - usernameStyle: authState.username == item.username - ? UsernameStyle.loggedInUser - : widget.storyUsername == item.username - ? UsernameStyle.storyUser - : UsernameStyle.none, - padding: widget.padding, - onTap: item.type == ItemType.pollopt - ? ItemAction.upvote - .isVisible(state, authState, settingsState) - ? (context, item) async => ItemAction.upvote - .execute(context, _itemCubit, widget._authCubit) - : null - : widget.onTap, - onLongPress: (context, item) async => context.push( - AppRoute.itemBottomSheet - .location(parameters: {'id': item.id}), + return Material( + type: widget.highlight + ? MaterialType.canvas + : MaterialType.transparency, + elevation: 4, + shadowColor: Colors.transparent, + surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, + child: ItemDataTile( + item, + parsedText: state.parsedText, + visited: state.visited && widget.showVisited, + vote: state.vote, + favorited: state.favorited, + flagged: state.flagged, + blocked: state.blocked, + failed: state.status == Status.failure, + collapsedCount: widget.collapsedCount, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showFavicons: settingsState.showFavicons, + showMetadata: widget.showMetadata, + showUserAvatars: settingsState.showUserAvatars, + style: widget.style, + usernameStyle: authState.username == item.username + ? UsernameStyle.loggedInUser + : widget.storyUsername == item.username + ? UsernameStyle.storyUser + : UsernameStyle.none, + padding: widget.padding, + onTap: item.type == ItemType.pollopt + ? ItemAction.upvote + .isVisible(state, authState, settingsState) + ? (context, item) async => ItemAction.upvote + .execute(context, _itemCubit, widget._authCubit) + : null + : widget.onTap, + onLongPress: (context, item) async => showModalBottomSheet( + context: rootNavigatorKey.currentContext!, + builder: (context) => ItemBottomSheet( + _itemCubit, + widget._authCubit, + widget._settingsCubit, + ), + ), + onTapUpvote: settingsState.useActionButtons && + ItemAction.upvote + .isVisible(state, authState, settingsState) + ? () async => ItemAction.upvote + .execute(context, _itemCubit, widget._authCubit) + : null, + onTapFavorite: settingsState.useActionButtons && + ItemAction.favorite + .isVisible(state, authState, settingsState) + ? () async => ItemAction.favorite + .execute(context, _itemCubit, widget._authCubit) + : null, ), - onTapUpvote: settingsState.useActionButtons && - ItemAction.upvote - .isVisible(state, authState, settingsState) - ? () async => ItemAction.upvote - .execute(context, _itemCubit, widget._authCubit) - : null, - onTapFavorite: settingsState.useActionButtons && - ItemAction.favorite - .isVisible(state, authState, settingsState) - ? () async => ItemAction.favorite - .execute(context, _itemCubit, widget._authCubit) - : null, - ), - ); - }, - onRetry: () async => _itemCubit.load(), + ); + }, + onRetry: () async => _itemCubit.load(), + ), ), ), ), diff --git a/lib/navigation_shell/cubit/navigation_shell_cubit.dart b/lib/navigation_shell/cubit/navigation_shell_cubit.dart index a75fd4eb..c47abc26 100644 --- a/lib/navigation_shell/cubit/navigation_shell_cubit.dart +++ b/lib/navigation_shell/cubit/navigation_shell_cubit.dart @@ -1,13 +1,14 @@ import 'package:bloc/bloc.dart'; import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:equatable/equatable.dart'; -import 'package:glider/navigation_shell/cubit/navigation_shell_event.dart'; import 'package:glider_domain/glider_domain.dart'; +part 'navigation_shell_cubit_event.dart'; part 'navigation_shell_state.dart'; class NavigationShellCubit extends Cubit - with BlocPresentationMixin { + with + BlocPresentationMixin { NavigationShellCubit( this._packageRepository, ) : super(const NavigationShellState()); diff --git a/lib/navigation_shell/cubit/navigation_shell_cubit_event.dart b/lib/navigation_shell/cubit/navigation_shell_cubit_event.dart new file mode 100644 index 00000000..71a4158c --- /dev/null +++ b/lib/navigation_shell/cubit/navigation_shell_cubit_event.dart @@ -0,0 +1,5 @@ +part of 'navigation_shell_cubit.dart'; + +sealed class NavigationShellCubitEvent {} + +final class ShowWhatsNewEvent implements NavigationShellCubitEvent {} diff --git a/lib/navigation_shell/cubit/navigation_shell_event.dart b/lib/navigation_shell/cubit/navigation_shell_event.dart deleted file mode 100644 index c6790b57..00000000 --- a/lib/navigation_shell/cubit/navigation_shell_event.dart +++ /dev/null @@ -1,3 +0,0 @@ -sealed class NavigationShellEvent {} - -final class ShowWhatsNewEvent implements NavigationShellEvent {} diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 671c4776..18aeb63f 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -10,7 +10,6 @@ import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/navigation_shell/cubit/navigation_shell_cubit.dart'; -import 'package:glider/navigation_shell/cubit/navigation_shell_event.dart'; import 'package:go_router/go_router.dart'; // Height based on `_NavigationBarDefaultsM3`. @@ -65,7 +64,8 @@ class _NavigationShellScaffoldState extends State { @override Widget build(BuildContext context) { - return BlocPresentationListener( + return BlocPresentationListener( bloc: widget._navigationShellCubit, listener: (context, event) => switch (event) { ShowWhatsNewEvent() => context.push(AppRoute.whatsNew.location()), diff --git a/lib/user/cubit/user_cubit.dart b/lib/user/cubit/user_cubit.dart index dc585ff5..5f8b82a5 100644 --- a/lib/user/cubit/user_cubit.dart +++ b/lib/user/cubit/user_cubit.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/services.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; @@ -10,9 +11,11 @@ import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:share_plus/share_plus.dart'; +part 'user_cubit_event.dart'; part 'user_state.dart'; -class UserCubit extends HydratedCubit { +class UserCubit extends HydratedCubit + with BlocPresentationMixin { UserCubit( this._userRepository, this._userInteractionRepository, @@ -77,14 +80,20 @@ class UserCubit extends HydratedCubit { safeEmit( state.copyWith(blocked: () => blocked), ); + emitPresentation(const UserActionFailedEvent()); } } Future synchronize() async { - await _userInteractionRepository.synchronize(); - await _itemInteractionRepository.getUpvotedIds(); - await _itemInteractionRepository.getFavoritedIds(); - await _itemInteractionRepository.getFlaggedIds(); + final success = await _userInteractionRepository.synchronize(); + + if (!success) { + emitPresentation(const UserActionFailedEvent()); + } else { + await _itemInteractionRepository.getUpvotedIds(); + await _itemInteractionRepository.getFavoritedIds(); + await _itemInteractionRepository.getFlaggedIds(); + } } Future copy(String text) async { diff --git a/lib/user/cubit/user_cubit_event.dart b/lib/user/cubit/user_cubit_event.dart new file mode 100644 index 00000000..fd200406 --- /dev/null +++ b/lib/user/cubit/user_cubit_event.dart @@ -0,0 +1,7 @@ +part of 'user_cubit.dart'; + +sealed class UserCubitEvent {} + +final class UserActionFailedEvent implements UserCubitEvent { + const UserActionFailedEvent(); +} diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 024b729c..0582233a 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -187,6 +187,8 @@ class _SliverUserAppBarState extends State<_SliverUserAppBar> { preferredSize: const Size.fromHeight(_toolbarHeight), child: UserTile( widget._userCubit, + widget._authCubit, + widget._settingsCubit, style: UserStyle.primary, onTap: (context, user) async => widget.scrollController.animateTo( 0, @@ -339,6 +341,8 @@ class _SliverUserBody extends StatelessWidget { SliverToBoxAdapter( child: UserTile( _userCubit, + _authCubit, + _settingsCubit, style: UserStyle.secondary, padding: AppSpacing.defaultTilePadding.copyWith(top: 0), ), diff --git a/lib/user/widgets/user_bottom_sheet.dart b/lib/user/widgets/user_bottom_sheet.dart index 0dec2192..e54f8c1d 100644 --- a/lib/user/widgets/user_bottom_sheet.dart +++ b/lib/user/widgets/user_bottom_sheet.dart @@ -1,48 +1,32 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:glider/app/container/app_container.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider/user/cubit/user_cubit.dart'; import 'package:glider/user/models/user_action.dart'; import 'package:go_router/go_router.dart'; -class UserBottomSheet extends StatefulWidget { +class UserBottomSheet extends StatelessWidget { const UserBottomSheet( - this._userCubitFactory, + this._userCubit, this._authCubit, this._settingsCubit, { super.key, - required this.username, }); - final UserCubitFactory _userCubitFactory; + final UserCubit _userCubit; final AuthCubit _authCubit; final SettingsCubit _settingsCubit; - final String username; - - @override - State createState() => _UserBottomSheetState(); -} - -class _UserBottomSheetState extends State { - late final UserCubit _userCubit; - - @override - void initState() { - _userCubit = widget._userCubitFactory(widget.username); - super.initState(); - } @override Widget build(BuildContext context) { return BlocBuilder( bloc: _userCubit, builder: (context, state) => BlocBuilder( - bloc: widget._authCubit, + bloc: _authCubit, builder: (context, authState) => BlocBuilder( - bloc: widget._settingsCubit, + bloc: _settingsCubit, builder: (context, settingsState) => ListView( primary: false, shrinkWrap: true, @@ -57,7 +41,7 @@ class _UserBottomSheetState extends State { await action.execute( context, _userCubit, - widget._authCubit, + _authCubit, ); }, ), diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index 57db91d1..9e14ae79 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -1,18 +1,24 @@ +import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:glider/app/models/app_route.dart'; +import 'package:glider/app/router/app_router.dart'; +import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; +import 'package:glider/l10n/extensions/app_localizations_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider/user/cubit/user_cubit.dart'; import 'package:glider/user/models/user_style.dart'; import 'package:glider/user/typedefs/user_typedefs.dart'; +import 'package:glider/user/widgets/user_bottom_sheet.dart'; import 'package:glider/user/widgets/user_data_tile.dart'; import 'package:glider/user/widgets/user_loading_tile.dart'; -import 'package:go_router/go_router.dart'; class UserTile extends StatelessWidget { const UserTile( - this._userCubit, { + this._userCubit, + this._authCubit, + this._settingsCubit, { super.key, this.style = UserStyle.full, this.padding = AppSpacing.defaultTilePadding, @@ -20,35 +26,51 @@ class UserTile extends StatelessWidget { }); final UserCubit _userCubit; + final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; final UserStyle style; final EdgeInsets padding; final UserCallback? onTap; @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocPresentationListener( bloc: _userCubit, - builder: (context, state) => state.whenOrDefaultWidgets( - loading: () => UserLoadingTile( - style: style, - padding: padding, - ), - success: () { - final user = state.data!; - return UserDataTile( - user, - parsedAbout: state.parsedAbout, - blocked: state.blocked, + listener: (context, event) => switch (event) { + UserActionFailedEvent() => ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.failure), + ), + ), + }, + child: BlocBuilder( + bloc: _userCubit, + builder: (context, state) => state.whenOrDefaultWidgets( + loading: () => UserLoadingTile( style: style, padding: padding, - onTap: onTap, - onLongPress: (context, item) async => context.push( - AppRoute.userBottomSheet - .location(parameters: {'id': user.username}), - ), - ); - }, - onRetry: () async => _userCubit.load(), + ), + success: () { + final user = state.data!; + return UserDataTile( + user, + parsedAbout: state.parsedAbout, + blocked: state.blocked, + style: style, + padding: padding, + onTap: onTap, + onLongPress: (context, item) async => showModalBottomSheet( + context: rootNavigatorKey.currentContext!, + builder: (context) => UserBottomSheet( + _userCubit, + _authCubit, + _settingsCubit, + ), + ), + ); + }, + onRetry: () async => _userCubit.load(), + ), ), ); } From a8bd1881e18d4181823539333e5b60af7a968887 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 15 Nov 2023 20:29:10 +0100 Subject: [PATCH 023/145] Fix date range picker opening more than once --- lib/stories_search/view/stories_search_range_view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/stories_search/view/stories_search_range_view.dart b/lib/stories_search/view/stories_search_range_view.dart index 4c649498..9cae473a 100644 --- a/lib/stories_search/view/stories_search_range_view.dart +++ b/lib/stories_search/view/stories_search_range_view.dart @@ -15,6 +15,9 @@ class StoriesSearchRangeView extends StatelessWidget { Widget build(BuildContext context) { return BlocConsumer( bloc: _storiesSearchBloc, + listenWhen: (previous, current) => + previous.searchRange != current.searchRange || + previous.dateRange != current.dateRange, listener: (context, state) async { if (state.searchRange == SearchRange.custom && state.dateRange == null) { From 5b93ba56286af43c2918b3d656890a6a90fadc44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 20:29:38 +0100 Subject: [PATCH 024/145] Bump flutter_inappwebview from 6.0.0-beta.27 to 6.0.0-beta.28 (#140) Bumps [flutter_inappwebview](https://github.com/pichillilorenzo/flutter_inappwebview) from 6.0.0-beta.27 to 6.0.0-beta.28. - [Release notes](https://github.com/pichillilorenzo/flutter_inappwebview/releases) - [Changelog](https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/CHANGELOG.md) - [Commits](https://github.com/pichillilorenzo/flutter_inappwebview/compare/v6.0.0-beta.27...v6.0.0-beta.28) --- updated-dependencies: - dependency-name: flutter_inappwebview dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 8 ++++---- pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index dd01673d..662e5fd3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -330,18 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: "3307260fdf5819de45c7b4889ad70f9b313c7915ad828af4b8004a974de7578a" + sha256: "838edeb770c5e6de8f178dbe6821bc31acf081fe9754210a03399b350261a491" url: "https://pub.dev" source: hosted - version: "6.0.0-beta.27" + version: "6.0.0-beta.28" flutter_inappwebview_internal_annotations: dependency: transitive description: name: flutter_inappwebview_internal_annotations - sha256: "064a8ccbc76217dcd3b0fd6c6ea6f549e69b2849a0233b5bb46af9632c3ce2ff" + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter_launcher_icons: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6dac978b..710e3c0b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.27 + flutter_inappwebview: ^6.0.0-beta.28 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 From be53f1091a8c98f292709a0eb086e7141dccb6a4 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 15 Nov 2023 20:41:47 +0100 Subject: [PATCH 025/145] Make visited items slightly more faded --- lib/item/widgets/item_data_tile.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index aa501440..9fdf00b8 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -116,7 +116,7 @@ class ItemDataTile extends StatelessWidget { child: Padding( padding: padding, child: Opacity( - opacity: visited ? 0.75 : 1, + opacity: visited ? 2 / 3 : 1, child: AnimatedSize( alignment: Alignment.topCenter, duration: AppAnimation.emphasized.duration, From 33d537fd9cc4c253c44ef7ed648b8d48a6653281 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 15 Nov 2023 21:46:35 +0100 Subject: [PATCH 026/145] Bump version --- fastlane/metadata/android/en-US/changelogs/44.txt | 8 ++++++++ pubspec.yaml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/44.txt diff --git a/fastlane/metadata/android/en-US/changelogs/44.txt b/fastlane/metadata/android/en-US/changelogs/44.txt new file mode 100644 index 00000000..1fafa91f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/44.txt @@ -0,0 +1,8 @@ +- Added font setting +- Added setting to disable favicons +- Added setting to disable user avatars +- Changed font used for code to be smaller +- Fixed gray screen appearing upon opening deeplinks +- Fixed custom date range picker opening more than once +- Added bottom padding to pages with floating action buttons +- Improved error handling slightly by showing a snackbar on errors \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 710e3c0b..0ff46399 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: glider description: An opinionated Hacker News client. -version: 2.2.0+43 +version: 2.3.0+44 publish_to: none environment: From 8fc51ff9c607d55bc0748b0a6c1f9df9e4b60610 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:06:24 +0100 Subject: [PATCH 027/145] Bump scrollview_observer from 1.18.0 to 1.18.1 (#141) Bumps [scrollview_observer](https://github.com/fluttercandies/flutter_scrollview_observer) from 1.18.0 to 1.18.1. - [Release notes](https://github.com/fluttercandies/flutter_scrollview_observer/releases) - [Changelog](https://github.com/fluttercandies/flutter_scrollview_observer/blob/main/CHANGELOG.md) - [Commits](https://github.com/fluttercandies/flutter_scrollview_observer/commits) --- updated-dependencies: - dependency-name: scrollview_observer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 662e5fd3..294ec302 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -855,10 +855,10 @@ packages: dependency: "direct main" description: name: scrollview_observer - sha256: "3206607305d572196a0fd7b434720142d5295ad21cfefd9d103daca63a2489f2" + sha256: f3b22470b4e0060566054b360eb2c04b4162b5990060cf5b2987a423a7e2f00f url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.18.1" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 0ff46399..3f9d1152 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 - scrollview_observer: ^1.18.0 + scrollview_observer: ^1.18.1 share_plus: ^7.2.1 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 From 3830ab5166b047e7ab4d66ea60c37760129191db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:03:08 +0100 Subject: [PATCH 028/145] Bump custom_lint from 0.5.6 to 0.5.7 (#142) Bumps [custom_lint](https://github.com/invertase/dart_custom_lint) from 0.5.6 to 0.5.7. - [Commits](https://github.com/invertase/dart_custom_lint/compare/custom_lint-v0.5.6...custom_lint-v0.5.7) --- updated-dependencies: - dependency-name: custom_lint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 12 ++++++------ pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 294ec302..91bdafb9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -205,26 +205,26 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: f9a828b696930cf8307f9a3617b2b65c9b370e484dc845d69100cadb77506778 + sha256: "198ec6b8e084d22f508a76556c9afcfb71706ad3f42b083fe0ee923351a96d90" url: "https://pub.dev" source: hosted - version: "0.5.6" + version: "0.5.7" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: c6f656a4d83385fc0656ae60410ed06bb382898c45627bfb8bbaa323aea97883 + sha256: dfcfa987d2bd9d0ba751ef4bdef0f6c1aa0062f2a67fe716fd5f3f8b709d6418 url: "https://pub.dev" source: hosted - version: "0.5.6" + version: "0.5.7" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: e20a67737adcf0cf2465e734dd624af535add11f9edd1f2d444909b5b0749650 + sha256: f84c3fe2f27ef3b8831953e477e59d4a29c2952623f9eac450d7b40d9cdd94cc url: "https://pub.dev" source: hosted - version: "0.5.6" + version: "0.5.7" dart_style: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3f9d1152..65907fcc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,7 +48,7 @@ dependencies: url_launcher: ^6.2.1 dev_dependencies: - custom_lint: ^0.5.6 + custom_lint: ^0.5.7 dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 leancode_lint: ^7.0.0+1 From 9d9ec6f4f318a6e6b06d16678c09a46db279fd60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:03:22 +0100 Subject: [PATCH 029/145] Bump device_info_plus from 9.1.0 to 9.1.1 (#143) Bumps [device_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus) from 9.1.0 to 9.1.1. - [Release notes](https://github.com/fluttercommunity/plus_plugins/releases) - [Commits](https://github.com/fluttercommunity/plus_plugins/commits/device_info_plus-v9.1.1/packages/device_info_plus) --- updated-dependencies: - dependency-name: device_info_plus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 91bdafb9..7ecd625f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -245,10 +245,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419" + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.1" device_info_plus_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 65907fcc..e70dff34 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: bloc_presentation: ^1.0.0 clock: ^1.1.1 collection: any - device_info_plus: ^9.1.0 + device_info_plus: ^9.1.1 dynamic_color: ^1.6.8 equatable: ^2.0.5 flutter: From eb016ed2969c9ec41028113caa03faaec756f410 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 17 Nov 2023 22:57:51 +0100 Subject: [PATCH 030/145] Upgrade Flutter version --- .fvmrc | 2 +- .vscode/settings.json | 2 +- pubspec.lock | 48 ++++++++++++++++++++++++++++++++----------- pubspec.yaml | 3 --- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/.fvmrc b/.fvmrc index 20fc8d45..05e141dd 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.16.0-0.4.pre" + "flutter": "3.17.0-0.0.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 828e2100..05c1491c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.16.0-0.4.pre" + "dart.flutterSdkPath": ".fvm/versions/3.17.0-0.0.pre" } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 7ecd625f..05e01bb5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "65.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" analyzer_plugin: dependency: transitive description: @@ -571,6 +571,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" + url: "https://pub.dev" + source: hosted + version: "9.0.13" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" leancode_lint: dependency: "direct dev" description: @@ -623,10 +639,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -751,10 +767,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointycastle: dependency: transitive description: @@ -791,10 +807,10 @@ packages: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: "direct main" description: @@ -1136,14 +1152,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.0" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e70dff34..79f132f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,9 +54,6 @@ dev_dependencies: leancode_lint: ^7.0.0+1 melos: ^3.2.0 -dependency_overrides: - material_color_utilities: ^0.8.0 - flutter: uses-material-design: true generate: true From fff6645baaea4890c147c5de94d1dcaaa7b43a2c Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 20 Nov 2023 22:05:53 +0100 Subject: [PATCH 031/145] Replace sliver lists with sliver list builders --- lib/favorites/view/favorites_shell_page.dart | 29 +++++------ lib/inbox/view/inbox_shell_page.dart | 50 ++++++++++--------- lib/item_tree/view/sliver_item_tree_body.dart | 12 +++-- lib/stories/cubit/stories_cubit.dart | 2 +- lib/stories/cubit/stories_state.dart | 9 +++- lib/stories/view/stories_shell_page.dart | 37 +++++++------- .../bloc/stories_search_bloc.dart | 2 +- .../bloc/stories_search_state.dart | 9 +++- .../view/sliver_stories_search_body.dart | 33 ++++++------ .../view/story_item_search_view.dart | 33 ++++++------ .../view/sliver_story_similar_body.dart | 41 +++++++-------- lib/user/view/user_page.dart | 29 +++++------ .../view/user_item_search_view.dart | 29 +++++------ 13 files changed, 171 insertions(+), 144 deletions(-) diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index fadc6cee..940dbed2 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -147,21 +147,22 @@ class _SliverFavoritesBody extends StatelessWidget { useLargeStoryStyle: settingsState.useLargeStoryStyle, ), ), - nonEmpty: () => SliverList.list( - children: [ - for (final id in state.data!) - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: ItemType.story, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + nonEmpty: () => SliverList.builder( + itemCount: state.data!.length, + itemBuilder: (context, index) { + final id = state.data![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: ItemType.story, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), - ], + ); + }, ), onRetry: () async => _favoritesCubit.load(), ), diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 92c63292..d2fe9aca 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -146,36 +146,40 @@ class _SliverInboxBody extends StatelessWidget { itemBuilder: (context, index) => const ItemLoadingTile(type: ItemType.comment), ), - nonEmpty: () => SliverList.list( - children: [ - for (final (parentId, id) in state.data!) ...[ - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(parentId), - id: parentId, - loadingType: ItemType.story, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), - ), - IndentedWidget( - depth: 1, - child: ItemTile.create( + nonEmpty: () => SliverList.builder( + itemCount: state.data!.length, + itemBuilder: (context, index) { + final (parentId, id) = state.data![index]; + return Column( + children: [ + ItemTile.create( _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: ItemType.comment, + key: ValueKey(parentId), + id: parentId, + loadingType: ItemType.story, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), ), ), - ), - ], - ], + IndentedWidget( + depth: 1, + child: ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: ItemType.comment, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), + ), + ), + ), + ], + ); + }, ), onRetry: () async => _inboxCubit.load(), ), diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index 4c753ff7..b7936f60 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -92,11 +92,13 @@ class _SliverItemTreeBodyState extends State { ), itemCount: widget.childCount, ), - nonEmpty: () => SliverList.list( - children: [ - for (final descendant in state.viewableData!) - _buildItemTile(descendant, state, settingsState), - ], + nonEmpty: () => SliverList.builder( + itemCount: state.viewableData!.length, + itemBuilder: (context, index) => _buildItemTile( + state.viewableData![index], + state, + settingsState, + ), ), onRetry: () async => widget._itemTreeCubit.load(), ), diff --git a/lib/stories/cubit/stories_cubit.dart b/lib/stories/cubit/stories_cubit.dart index a79b7f0e..108e56d8 100644 --- a/lib/stories/cubit/stories_cubit.dart +++ b/lib/stories/cubit/stories_cubit.dart @@ -12,7 +12,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; part 'stories_state.dart'; class StoriesCubit extends HydratedCubit { - StoriesCubit(this._itemRepository) : super(const StoriesState()); + StoriesCubit(this._itemRepository) : super(StoriesState()); final ItemRepository _itemRepository; diff --git a/lib/stories/cubit/stories_state.dart b/lib/stories/cubit/stories_state.dart index 63d00035..e3989a5b 100644 --- a/lib/stories/cubit/stories_state.dart +++ b/lib/stories/cubit/stories_state.dart @@ -2,7 +2,7 @@ part of 'stories_cubit.dart'; class StoriesState with DataMixin>, PaginatedListMixin, EquatableMixin { - const StoriesState({ + StoriesState({ this.status = Status.initial, this.data, this.page = 1, @@ -34,6 +34,13 @@ class StoriesState @override final Object? exception; + @override + late List? loadedData = super.loadedData?.toList(growable: false); + + @override + late List? currentPageData = + super.currentPageData?.toList(growable: false); + StoriesState copyWith({ Status Function()? status, List? Function()? data, diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index 907d8dda..46d9beb5 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -235,25 +235,26 @@ class _SliverStoriesBody extends StatelessWidget { ), nonEmpty: () => SliverMainAxisGroup( slivers: [ - SliverList.list( - children: [ - for (final id in state.loadedData!) - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: ItemType.story, - showMetadata: settingsState.showStoryMetadata, - showJobs: settingsState.showJobs || - state.storyType == StoryType.jobStories, - style: ItemStyle.overview, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + SliverList.builder( + itemCount: state.loadedData!.length, + itemBuilder: (context, index) { + final id = state.loadedData![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: ItemType.story, + showMetadata: settingsState.showStoryMetadata, + showJobs: settingsState.showJobs || + state.storyType == StoryType.jobStories, + style: ItemStyle.overview, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), - ], + ); + }, ), if (state.loadedData!.length < state.data!.length) SliverPadding( diff --git a/lib/stories_search/bloc/stories_search_bloc.dart b/lib/stories_search/bloc/stories_search_bloc.dart index 707f8c5a..2e5cb74a 100644 --- a/lib/stories_search/bloc/stories_search_bloc.dart +++ b/lib/stories_search/bloc/stories_search_bloc.dart @@ -23,7 +23,7 @@ EventTransformer debounce(Duration duration) => class StoriesSearchBloc extends HydratedBloc { StoriesSearchBloc(this._itemRepository, {this.searchType = SearchType.search}) - : super(const StoriesSearchState()) { + : super(StoriesSearchState()) { on( (event, emit) async => _load(), transformer: debounce(_debounceDuration), diff --git a/lib/stories_search/bloc/stories_search_state.dart b/lib/stories_search/bloc/stories_search_state.dart index 2eb398d2..1388ada1 100644 --- a/lib/stories_search/bloc/stories_search_state.dart +++ b/lib/stories_search/bloc/stories_search_state.dart @@ -2,7 +2,7 @@ part of 'stories_search_bloc.dart'; class StoriesSearchState with DataMixin>, PaginatedListMixin, EquatableMixin { - const StoriesSearchState({ + StoriesSearchState({ this.status = Status.initial, this.data, this.page = 1, @@ -57,6 +57,13 @@ class StoriesSearchState @override final Object? exception; + @override + late List? loadedData = super.loadedData?.toList(growable: false); + + @override + late List? currentPageData = + super.currentPageData?.toList(growable: false); + StoriesSearchState copyWith({ Status Function()? status, List? Function()? data, diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index 85a29c5a..24e627e3 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -49,23 +49,24 @@ class SliverStoriesSearchBody extends StatelessWidget { ), nonEmpty: () => SliverMainAxisGroup( slivers: [ - SliverList.list( - children: [ - for (final id in state.loadedData!) - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: ItemType.story, - showMetadata: settingsState.showStoryMetadata, - style: ItemStyle.overview, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + SliverList.builder( + itemCount: state.loadedData!.length, + itemBuilder: (context, index) { + final id = state.loadedData![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: ItemType.story, + showMetadata: settingsState.showStoryMetadata, + style: ItemStyle.overview, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), - ], + ); + }, ), if (state.loadedData!.length < state.data!.length) SliverPadding( diff --git a/lib/story_item_search/view/story_item_search_view.dart b/lib/story_item_search/view/story_item_search_view.dart index bf585e88..9e93c14c 100644 --- a/lib/story_item_search/view/story_item_search_view.dart +++ b/lib/story_item_search/view/story_item_search_view.dart @@ -64,23 +64,24 @@ class _SliverStoryItemSearchBody extends StatelessWidget { return BlocBuilder( bloc: _storyItemSearchBloc, builder: (context, state) => state.whenOrDefaultSlivers( - nonEmpty: () => SliverList.list( - children: [ - for (final id in state.data!) - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: _storyItemSearchBloc.itemId == id - ? ItemType.story - : ItemType.comment, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + nonEmpty: () => SliverList.builder( + itemCount: state.data!.length, + itemBuilder: (context, index) { + final id = state.data![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: _storyItemSearchBloc.itemId == id + ? ItemType.story + : ItemType.comment, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), - ], + ); + }, ), onRetry: () async => _storyItemSearchBloc.add(const LoadStoryItemSearchEvent()), diff --git a/lib/story_similar/view/sliver_story_similar_body.dart b/lib/story_similar/view/sliver_story_similar_body.dart index 26ca7db1..2620a726 100644 --- a/lib/story_similar/view/sliver_story_similar_body.dart +++ b/lib/story_similar/view/sliver_story_similar_body.dart @@ -69,29 +69,30 @@ class SliverStorySimilarBody extends StatelessWidget { ), ), ), - SliverList.list( - children: [ - for (final id in state.data!) - Padding( - padding: AppSpacing.defaultShadowPadding, - child: Material( - type: MaterialType.transparency, - child: ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - storyUsername: storyUsername, - loadingType: ItemType.story, - style: ItemStyle.primary, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + SliverList.builder( + itemCount: state.data!.length, + itemBuilder: (context, index) { + final id = state.data![index]; + return Padding( + padding: AppSpacing.defaultShadowPadding, + child: Material( + type: MaterialType.transparency, + child: ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + storyUsername: storyUsername, + loadingType: ItemType.story, + style: ItemStyle.primary, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), ), ), - ], + ); + }, ), ], ), diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 0582233a..1f73f945 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -348,21 +348,22 @@ class _SliverUserBody extends StatelessWidget { ), ), if (state.data?.submittedIds case final submittedIds?) - SliverList.list( - children: [ - for (final id in submittedIds) - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: ItemType.story, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + SliverList.builder( + itemCount: submittedIds.length, + itemBuilder: (context, index) { + final id = submittedIds[index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: ItemType.story, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), - ], + ); + }, ), ], ), diff --git a/lib/user_item_search/view/user_item_search_view.dart b/lib/user_item_search/view/user_item_search_view.dart index aa584eac..a8f67594 100644 --- a/lib/user_item_search/view/user_item_search_view.dart +++ b/lib/user_item_search/view/user_item_search_view.dart @@ -64,21 +64,22 @@ class _SliverUserItemSearchBody extends StatelessWidget { return BlocBuilder( bloc: _userItemSearchBloc, builder: (context, state) => state.whenOrDefaultSlivers( - nonEmpty: () => SliverList.list( - children: [ - for (final id in state.data!) - ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - key: ValueKey(id), - id: id, - loadingType: ItemType.story, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), + nonEmpty: () => SliverList.builder( + itemCount: state.data!.length, + itemBuilder: (context, index) { + final id = state.data![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + key: ValueKey(id), + id: id, + loadingType: ItemType.story, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), ), - ], + ); + }, ), onRetry: () async => _userItemSearchBloc.add(const LoadUserItemSearchEvent()), From 5b46c3bb356256483af47d59afee53359d175c8e Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 12:04:24 +0100 Subject: [PATCH 032/145] Optimize animated visibility --- lib/common/widgets/animated_visibility.dart | 47 ++++++++++++--------- lib/item/widgets/item_data_tile.dart | 1 - 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/lib/common/widgets/animated_visibility.dart b/lib/common/widgets/animated_visibility.dart index 113ac682..4462e1c8 100644 --- a/lib/common/widgets/animated_visibility.dart +++ b/lib/common/widgets/animated_visibility.dart @@ -6,41 +6,50 @@ class AnimatedVisibility extends StatelessWidget { super.key, required this.visible, this.padding = EdgeInsets.zero, - this.alignment = AlignmentDirectional.centerStart, + this.alignment = -1, required this.child, - }) : _replacement = const SizedBox.shrink(); + }) : _axis = Axis.horizontal, + _replacement = const SizedBox.shrink(); const AnimatedVisibility.vertical({ super.key, required this.visible, this.padding = EdgeInsets.zero, - this.alignment = Alignment.topCenter, + this.alignment = -1, required this.child, - }) : _replacement = const SizedBox(width: double.infinity); + }) : _axis = Axis.vertical, + _replacement = const SizedBox(width: double.infinity); final bool visible; final EdgeInsetsGeometry padding; - final AlignmentGeometry alignment; + final double alignment; final Widget child; + final Axis _axis; final Widget _replacement; @override Widget build(BuildContext context) { - return AnimatedCrossFade( - crossFadeState: - visible ? CrossFadeState.showFirst : CrossFadeState.showSecond, - firstChild: AnimatedPadding( - padding: padding, - duration: AppAnimation.standard.duration, - curve: AppAnimation.standard.easing, - child: child, - ), - secondChild: _replacement, - alignment: alignment, + return AnimatedSwitcher( duration: AppAnimation.standard.duration, - firstCurve: AppAnimation.standard.easing, - secondCurve: AppAnimation.standard.easing, - sizeCurve: AppAnimation.standard.easing, + switchInCurve: AppAnimation.standard.easing, + switchOutCurve: AppAnimation.standard.easing, + transitionBuilder: (child, animation) => SizeTransition( + axis: _axis, + axisAlignment: alignment, + sizeFactor: animation, + child: FadeTransition( + opacity: animation, + child: child, + ), + ), + child: visible + ? AnimatedPadding( + padding: padding, + duration: AppAnimation.standard.duration, + curve: AppAnimation.standard.easing, + child: child, + ) + : _replacement, ); } } diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 9fdf00b8..8a78d9d0 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -159,7 +159,6 @@ class ItemDataTile extends StatelessWidget { if (item.url != null && showFavicons) AnimatedVisibility( visible: style == ItemStyle.overview, - alignment: AlignmentDirectional.centerEnd, child: InkWell( onTap: () async => item.url!.tryLaunch(), // Explicitly override parent widget's long press. From 78e959cd8bf58cfb20df037510d6a4638b087445 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 12:04:39 +0100 Subject: [PATCH 033/145] Simplify item tree --- lib/item_tree/view/sliver_item_tree_body.dart | 61 +++++-------------- 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index b7936f60..ce630a8d 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -1,12 +1,9 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/mixins/data_mixin.dart'; -import 'package:glider/item/cubit/item_cubit.dart'; import 'package:glider/item/widgets/indented_widget.dart'; import 'package:glider/item/widgets/item_loading_tile.dart'; import 'package:glider/item/widgets/item_tile.dart'; @@ -15,7 +12,7 @@ import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider_domain/glider_domain.dart'; -class SliverItemTreeBody extends StatefulWidget { +class SliverItemTreeBody extends StatelessWidget { const SliverItemTreeBody( this._itemTreeCubit, this._itemCubitFactory, @@ -33,44 +30,13 @@ class SliverItemTreeBody extends StatefulWidget { final int? childCount; final String? storyUsername; - @override - State createState() => _SliverItemTreeBodyState(); -} - -class _SliverItemTreeBodyState extends State { - final Map _itemCubits = {}; - - @override - void initState() { - _updateItemCubits(widget._itemTreeCubit.state); - super.initState(); - } - - @override - void dispose() { - for (final itemCubit in _itemCubits.values) { - unawaited(itemCubit.close()); - } - - super.dispose(); - } - - void _updateItemCubits(ItemTreeState state) { - if (state.data case final descendants?) { - for (final descendant in descendants) { - _itemCubits[descendant.id] ??= widget._itemCubitFactory(descendant.id); - } - } - } - @override Widget build(BuildContext context) { return BlocConsumer( - bloc: widget._itemTreeCubit, - listenWhen: (previous, current) => previous.data != current.data, + bloc: _itemTreeCubit, + listenWhen: (previous, current) => + previous.newDescendantsCount != current.newDescendantsCount, listener: (context, state) { - _updateItemCubits(state); - if (state.newDescendantsCount > 0) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -81,7 +47,7 @@ class _SliverItemTreeBodyState extends State { } }, builder: (context, state) => BlocBuilder( - bloc: widget._settingsCubit, + bloc: _settingsCubit, buildWhen: (previous, current) => previous.useActionButtons != current.useActionButtons, builder: (context, settingsState) => state.whenOrDefaultSlivers( @@ -90,7 +56,7 @@ class _SliverItemTreeBodyState extends State { depth: 1, child: ItemLoadingTile(type: ItemType.comment), ), - itemCount: widget.childCount, + itemCount: childCount, ), nonEmpty: () => SliverList.builder( itemCount: state.viewableData!.length, @@ -100,7 +66,7 @@ class _SliverItemTreeBodyState extends State { settingsState, ), ), - onRetry: () async => widget._itemTreeCubit.load(), + onRetry: () async => _itemTreeCubit.load(), ), ), ); @@ -113,12 +79,13 @@ class _SliverItemTreeBodyState extends State { ) { return IndentedWidget( depth: descendant.isPart ? 0 : descendant.depth, - child: ItemTile( - _itemCubits[descendant.id]!, - widget._authCubit, - widget._settingsCubit, + child: ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, key: ValueKey(descendant.id), - storyUsername: widget.storyUsername, + id: descendant.id, + storyUsername: storyUsername, loadingType: ItemType.comment, collapsedCount: state.collapsedIds.contains(descendant.id) ? state.getDescendants(descendant)?.length @@ -129,7 +96,7 @@ class _SliverItemTreeBodyState extends State { true), onTap: (context, item) async { if (!item.isDeleted) { - widget._itemTreeCubit.toggleCollapsed(item.id); + _itemTreeCubit.toggleCollapsed(item.id); } await Scrollable.ensureVisible( From 9ffb61018f76691ce2670575fc3935221222e357 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 20:36:51 +0100 Subject: [PATCH 034/145] Add actions and setting to manage read statuses --- lib/item/models/item_action.dart | 9 + lib/l10n/arb/app_en.arb | 3 + lib/settings/cubit/settings_cubit.dart | 20 +- lib/settings/cubit/settings_cubit_event.dart | 7 + lib/settings/view/settings_page.dart | 486 +++++++++--------- .../lib/src/shared_preferences_service.dart | 5 + .../lib/src/item_interaction_repository.dart | 10 + 7 files changed, 303 insertions(+), 237 deletions(-) create mode 100644 lib/settings/cubit/settings_cubit_event.dart diff --git a/lib/item/models/item_action.dart b/lib/item/models/item_action.dart index cfc5b7b8..01dc3824 100644 --- a/lib/item/models/item_action.dart +++ b/lib/item/models/item_action.dart @@ -12,6 +12,7 @@ import 'package:glider_domain/glider_domain.dart'; import 'package:go_router/go_router.dart'; enum ItemAction, S> implements MenuItem { + visit, upvote, downvote, favorite, @@ -36,6 +37,7 @@ enum ItemAction, S> implements MenuItem { final item = state.data; if (item == null) return false; return switch (this) { + ItemAction.visit => true, ItemAction.upvote => !item.isDeleted && item.type != ItemType.job && authState.isLoggedIn && @@ -72,6 +74,8 @@ enum ItemAction, S> implements MenuItem { @override String label(BuildContext context, ItemState state) { return switch (this) { + ItemAction.visit => + state.visited ? context.l10n.unvisit : context.l10n.visit, ItemAction.upvote => state.vote.upvoted ? context.l10n.unvote : context.l10n.upvote, ItemAction.downvote => @@ -101,6 +105,9 @@ enum ItemAction, S> implements MenuItem { ? Icons.heart_broken_outlined : Icons.favorite_outline_outlined, ItemAction.flag => state.flagged ? Icons.flag : Icons.flag_outlined, + ItemAction.visit => state.visited + ? Icons.visibility_off_outlined + : Icons.visibility_outlined, ItemAction.edit => Icons.edit_outlined, ItemAction.delete => Icons.delete_outline_outlined, ItemAction.reply => Icons.reply_outlined, @@ -118,6 +125,8 @@ enum ItemAction, S> implements MenuItem { }) async { final id = itemCubit.id; switch (this) { + case ItemAction.visit: + await itemCubit.visit(!itemCubit.state.visited); case ItemAction.upvote: await itemCubit.upvote(itemCubit.state.vote != VoteType.upvote); case ItemAction.downvote: diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8621c9db..12f24f80 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -39,6 +39,7 @@ "data": "Data", "exportFavorites": "Export favorites", "exportFavoritesDescription": "Shares favorites as IDs in JSON format", + "clearVisited": "Clear read statuses", "about": "About", "appVersion": "App version", "privacyPolicy": "Privacy policy", @@ -67,6 +68,8 @@ "unvote": "Unvote", "favorite": "Favorite", "unfavorite": "Unfavorite", + "visit": "Mark read", + "unvisit": "Mark unread", "flag": "Flag", "unflag": "Unflag", "edit": "Edit", diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index a19b021d..98318f08 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:ui'; import 'package:bloc/bloc.dart'; +import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:equatable/equatable.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider_domain/glider_domain.dart'; @@ -10,9 +11,11 @@ import 'package:material_color_utilities/scheme/variant.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:share_plus/share_plus.dart'; +part 'settings_cubit_event.dart'; part 'settings_state.dart'; -class SettingsCubit extends Cubit { +class SettingsCubit extends Cubit + with BlocPresentationMixin { SettingsCubit( this._settingsRepository, this._packageRepository, @@ -225,6 +228,19 @@ class SettingsCubit extends Cubit { Future exportFavorites() async { final favorites = await _itemInteractionRepository.favoritedStream.first; - await Share.share(jsonEncode(favorites)); + + try { + await Share.share(jsonEncode(favorites)); + } on Object { + emitPresentation(const SettingsActionFailedEvent()); + } + } + + Future clearVisited() async { + final success = await _itemInteractionRepository.clearVisited(); + + if (!success) { + emitPresentation(const SettingsActionFailedEvent()); + } } } diff --git a/lib/settings/cubit/settings_cubit_event.dart b/lib/settings/cubit/settings_cubit_event.dart new file mode 100644 index 00000000..b736e3cb --- /dev/null +++ b/lib/settings/cubit/settings_cubit_event.dart @@ -0,0 +1,7 @@ +part of 'settings_cubit.dart'; + +sealed class SettingsCubitEvent {} + +final class SettingsActionFailedEvent implements SettingsCubitEvent { + const SettingsActionFailedEvent(); +} diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index b1cc6cbc..50c7c645 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -1,3 +1,4 @@ +import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:clock/clock.dart'; import 'package:flutter/material.dart' hide ThemeMode; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -76,260 +77,275 @@ class _SettingsBody extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocPresentationListener( bloc: _settingsCubit, - builder: (context, state) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: AppSpacing.defaultTilePadding, - child: Text( - context.l10n.theme, - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), + listener: (context, event) => switch (event) { + SettingsActionFailedEvent() => + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.failure), ), ), - MenuListTile( - title: Text(context.l10n.themeMode), - trailing: Text(state.themeMode.capitalizedLabel), - onChanged: (value) async => _settingsCubit.setThemeMode(value), - values: ThemeMode.values, - selected: (value) => state.themeMode == value, - childBuilder: (value) => Text(value.capitalizedLabel), - ), - SwitchListTile.adaptive( - value: state.useDynamicTheme, - onChanged: _settingsCubit.setUseDynamicTheme, - title: Text(context.l10n.dynamicTheme), - subtitle: Text(context.l10n.dynamicThemeDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - ListTile( - title: Text(context.l10n.themeColor), - trailing: Icon( - Icons.circle, - color: state.themeColor, - size: 40, + }, + child: BlocBuilder( + bloc: _settingsCubit, + builder: (context, state) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: AppSpacing.defaultTilePadding, + child: Text( + context.l10n.theme, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), ), - enabled: !state.useDynamicTheme, - onTap: () async { - final value = await context.push( - AppRoute.themeColorDialog.location(), - extra: state.themeColor, - ); - if (value != null) await _settingsCubit.setThemeColor(value); - }, - ), - MenuListTile( - title: Text(context.l10n.themeVariant), - trailing: Text(state.themeVariant.capitalizedLabel), - enabled: !state.useDynamicTheme, - onChanged: (value) async => _settingsCubit.setThemeVariant(value), - values: Variant.values, - selected: (value) => state.themeVariant == value, - childBuilder: (value) => Text(value.capitalizedLabel), - ), - SwitchListTile.adaptive( - value: state.usePureBackground, - onChanged: _settingsCubit.setUsePureBackground, - title: Text(context.l10n.pureBackground), - subtitle: Text(context.l10n.pureBackgroundDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - MenuListTile( - title: Text(context.l10n.font), - trailing: Text(state.font), - onChanged: (value) async => _settingsCubit.setFont(value), - values: _fonts, - selected: (value) => state.font == value, - childBuilder: (value) => - Text(value, style: GoogleFonts.getFont(value)), - ), - const Divider(), - Padding( - padding: AppSpacing.defaultTilePadding, - child: Text( - context.l10n.appearance, - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), + MenuListTile( + title: Text(context.l10n.themeMode), + trailing: Text(state.themeMode.capitalizedLabel), + onChanged: (value) async => _settingsCubit.setThemeMode(value), + values: ThemeMode.values, + selected: (value) => state.themeMode == value, + childBuilder: (value) => Text(value.capitalizedLabel), ), - ), - SwitchListTile.adaptive( - value: state.useLargeStoryStyle, - onChanged: _settingsCubit.setUseLargeStoryStyle, - title: Text(context.l10n.largeStoryStyle), - subtitle: Text(context.l10n.largeStoryStyleDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.showFavicons, - onChanged: _settingsCubit.setShowFavicons, - title: Text(context.l10n.favicons), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.showStoryMetadata, - onChanged: _settingsCubit.setShowStoryMetadata, - title: Text(context.l10n.storyMetadata), - subtitle: Text(context.l10n.storyMetadataDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.showUserAvatars, - onChanged: _settingsCubit.setShowUserAvatars, - title: Text(context.l10n.userAvatars), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.useActionButtons, - onChanged: _settingsCubit.setUseActionButtons, - title: Text(context.l10n.actionButtons), - subtitle: Text(context.l10n.actionButtonsDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - BlocBuilder( - bloc: _settingsCubit, - builder: (context, state) => Padding( + SwitchListTile.adaptive( + value: state.useDynamicTheme, + onChanged: _settingsCubit.setUseDynamicTheme, + title: Text(context.l10n.dynamicTheme), + subtitle: Text(context.l10n.dynamicThemeDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + ListTile( + title: Text(context.l10n.themeColor), + trailing: Icon( + Icons.circle, + color: state.themeColor, + size: 40, + ), + enabled: !state.useDynamicTheme, + onTap: () async { + final value = await context.push( + AppRoute.themeColorDialog.location(), + extra: state.themeColor, + ); + if (value != null) await _settingsCubit.setThemeColor(value); + }, + ), + MenuListTile( + title: Text(context.l10n.themeVariant), + trailing: Text(state.themeVariant.capitalizedLabel), + enabled: !state.useDynamicTheme, + onChanged: (value) async => _settingsCubit.setThemeVariant(value), + values: Variant.values, + selected: (value) => state.themeVariant == value, + childBuilder: (value) => Text(value.capitalizedLabel), + ), + SwitchListTile.adaptive( + value: state.usePureBackground, + onChanged: _settingsCubit.setUsePureBackground, + title: Text(context.l10n.pureBackground), + subtitle: Text(context.l10n.pureBackgroundDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + MenuListTile( + title: Text(context.l10n.font), + trailing: Text(state.font), + onChanged: (value) async => _settingsCubit.setFont(value), + values: _fonts, + selected: (value) => state.font == value, + childBuilder: (value) => + Text(value, style: GoogleFonts.getFont(value)), + ), + const Divider(), + Padding( padding: AppSpacing.defaultTilePadding, - child: PreviewCard( - child: HeroMode( - enabled: false, - child: ItemDataTile( - Item( - id: -1, - username: 'cats4ever', - dateTime: clock.now(), - title: - 'Show HN: A fat cat on a treadmill under water [video]', - url: Uri.https( - 'www.youtube.com', - 'watch', - {'v': '1A37RTaoEuM'}, + child: Text( + context.l10n.appearance, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + SwitchListTile.adaptive( + value: state.useLargeStoryStyle, + onChanged: _settingsCubit.setUseLargeStoryStyle, + title: Text(context.l10n.largeStoryStyle), + subtitle: Text(context.l10n.largeStoryStyleDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.showFavicons, + onChanged: _settingsCubit.setShowFavicons, + title: Text(context.l10n.favicons), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.showStoryMetadata, + onChanged: _settingsCubit.setShowStoryMetadata, + title: Text(context.l10n.storyMetadata), + subtitle: Text(context.l10n.storyMetadataDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.showUserAvatars, + onChanged: _settingsCubit.setShowUserAvatars, + title: Text(context.l10n.userAvatars), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.useActionButtons, + onChanged: _settingsCubit.setUseActionButtons, + title: Text(context.l10n.actionButtons), + subtitle: Text(context.l10n.actionButtonsDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + BlocBuilder( + bloc: _settingsCubit, + builder: (context, state) => Padding( + padding: AppSpacing.defaultTilePadding, + child: PreviewCard( + child: HeroMode( + enabled: false, + child: ItemDataTile( + Item( + id: -1, + username: 'cats4ever', + dateTime: clock.now(), + title: + 'Show HN: A fat cat on a treadmill under water [video]', + url: Uri.https( + 'www.youtube.com', + 'watch', + {'v': '1A37RTaoEuM'}, + ), + score: 42, + descendantCount: 7, ), - score: 42, - descendantCount: 7, + vote: VoteType.upvote, + useLargeStoryStyle: state.useLargeStoryStyle, + showFavicons: state.showFavicons, + showMetadata: state.showStoryMetadata, + showUserAvatars: state.showUserAvatars, + style: ItemStyle.overview, + onTapFavorite: state.useActionButtons ? () {} : null, + onTapUpvote: state.useActionButtons ? () {} : null, ), - vote: VoteType.upvote, - useLargeStoryStyle: state.useLargeStoryStyle, - showFavicons: state.showFavicons, - showMetadata: state.showStoryMetadata, - showUserAvatars: state.showUserAvatars, - style: ItemStyle.overview, - onTapFavorite: state.useActionButtons ? () {} : null, - onTapUpvote: state.useActionButtons ? () {} : null, ), ), ), ), - ), - const Divider(), - Padding( - padding: AppSpacing.defaultTilePadding, - child: Text( - context.l10n.behavior, - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), + const Divider(), + Padding( + padding: AppSpacing.defaultTilePadding, + child: Text( + context.l10n.behavior, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), ), - ), - SwitchListTile.adaptive( - value: state.showJobs, - onChanged: _settingsCubit.setShowJobs, - title: Text(context.l10n.showJobs), - subtitle: Text(context.l10n.showJobsDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.useThreadNavigation, - onChanged: _settingsCubit.setUseThreadNavigation, - title: Text(context.l10n.threadNavigation), - subtitle: Text(context.l10n.threadNavigationDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - SwitchListTile.adaptive( - value: state.enableDownvoting, - onChanged: _settingsCubit.setEnableDownvoting, - title: Text(context.l10n.downvoting), - subtitle: Text(context.l10n.downvotingDescription), - contentPadding: - const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - ), - const Divider(), - Padding( - padding: AppSpacing.defaultTilePadding, - child: Text( - context.l10n.data, - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), + SwitchListTile.adaptive( + value: state.showJobs, + onChanged: _settingsCubit.setShowJobs, + title: Text(context.l10n.showJobs), + subtitle: Text(context.l10n.showJobsDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), - ), - ListTile( - title: Text(context.l10n.exportFavorites), - subtitle: Text(context.l10n.exportFavoritesDescription), - onTap: _settingsCubit.exportFavorites, - ), - const Divider(), - Padding( - padding: AppSpacing.defaultTilePadding, - child: Text( - context.l10n.about, - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), + SwitchListTile.adaptive( + value: state.useThreadNavigation, + onChanged: _settingsCubit.setUseThreadNavigation, + title: Text(context.l10n.threadNavigation), + subtitle: Text(context.l10n.threadNavigationDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + SwitchListTile.adaptive( + value: state.enableDownvoting, + onChanged: _settingsCubit.setEnableDownvoting, + title: Text(context.l10n.downvoting), + subtitle: Text(context.l10n.downvotingDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), + const Divider(), + Padding( + padding: AppSpacing.defaultTilePadding, + child: Text( + context.l10n.data, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), ), - ), - if (state.appVersion case final appVersion?) ListTile( - title: Text(context.l10n.appVersion), - subtitle: Text(appVersion.canonicalizedVersion), - enabled: false, + title: Text(context.l10n.exportFavorites), + subtitle: Text(context.l10n.exportFavoritesDescription), + onTap: _settingsCubit.exportFavorites, ), - ListTile( - title: Text(context.l10n.privacyPolicy), - trailing: const Icon(Icons.open_in_new_outlined), - onTap: _privacyPolicyUrl.tryLaunch, - ), - ListTile( - title: Text(context.l10n.license), - subtitle: const Text(_license), - trailing: const Icon(Icons.open_in_new_outlined), - onTap: _licenseUrl.tryLaunch, - ), - ListTile( - title: Text(context.l10n.sourceCode), - subtitle: Text(_sourceCodeUrl.toString()), - trailing: const Icon(Icons.open_in_new_outlined), - onTap: _sourceCodeUrl.tryLaunch, - ), - ListTile( - title: Text(context.l10n.issueTracker), - subtitle: Text(_issueTrackerUrl.toString()), - trailing: const Icon(Icons.open_in_new_outlined), - onTap: _issueTrackerUrl.tryLaunch, - ), - ListTile( - title: Text( - MaterialLocalizations.of(context).licensesPageTitle, + ListTile( + title: Text(context.l10n.clearVisited), + onTap: _settingsCubit.clearVisited, ), - onTap: () => showLicensePage( - context: context, - applicationName: context.l10n.appName, - applicationVersion: state.appVersion?.canonicalizedVersion, + const Divider(), + Padding( + padding: AppSpacing.defaultTilePadding, + child: Text( + context.l10n.about, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), ), - ), - ], + if (state.appVersion case final appVersion?) + ListTile( + title: Text(context.l10n.appVersion), + subtitle: Text(appVersion.canonicalizedVersion), + enabled: false, + ), + ListTile( + title: Text(context.l10n.privacyPolicy), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: _privacyPolicyUrl.tryLaunch, + ), + ListTile( + title: Text(context.l10n.license), + subtitle: const Text(_license), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: _licenseUrl.tryLaunch, + ), + ListTile( + title: Text(context.l10n.sourceCode), + subtitle: Text(_sourceCodeUrl.toString()), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: _sourceCodeUrl.tryLaunch, + ), + ListTile( + title: Text(context.l10n.issueTracker), + subtitle: Text(_issueTrackerUrl.toString()), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: _issueTrackerUrl.tryLaunch, + ), + ListTile( + title: Text( + MaterialLocalizations.of(context).licensesPageTitle, + ), + onTap: () => showLicensePage( + context: context, + applicationName: context.l10n.appName, + applicationVersion: state.appVersion?.canonicalizedVersion, + ), + ), + ], + ), ), ); } diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index 88e365f8..d77b33b3 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -129,6 +129,11 @@ class SharedPreferencesService { Future> getVisitedIds() async => [...?_sharedPreferences.getStringList(_visitedKey)?.map(int.parse)]; + Future setVisitedIds({required Iterable ids}) async { + return _sharedPreferences + .setStringList(_visitedKey, [...ids.map((id) => id.toString())]); + } + Future getUpvoted({required int id}) async => _sharedPreferences.containsElement(_upvotedKey, id.toString()); diff --git a/packages/glider_domain/lib/src/item_interaction_repository.dart b/packages/glider_domain/lib/src/item_interaction_repository.dart index d76afb55..a601b8dd 100644 --- a/packages/glider_domain/lib/src/item_interaction_repository.dart +++ b/packages/glider_domain/lib/src/item_interaction_repository.dart @@ -229,4 +229,14 @@ class ItemInteractionRepository { return false; } } + + Future clearVisited() async { + try { + await _sharedPreferencesService.setVisitedIds(ids: []); + await getVisitedIds(); + return true; + } on Object { + return false; + } + } } From ff105071a5f26a0fb4ee724e6017a2a33655a173 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 21:11:08 +0100 Subject: [PATCH 035/145] Replace in app webview with in app browser for login --- lib/auth/view/auth_page.dart | 161 +++++++++++++++++++++-------------- lib/l10n/arb/app_en.arb | 3 +- 2 files changed, 100 insertions(+), 64 deletions(-) diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index 4ec5e04b..598ec165 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; @@ -8,7 +9,7 @@ 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/extensions/uri_extension.dart'; -import 'package:glider/common/widgets/decorated_card.dart'; +import 'package:glider/common/extensions/widget_list_extension.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider/user/view/user_page.dart'; @@ -35,8 +36,25 @@ class AuthPage extends StatefulWidget { } class _AuthPageState extends State { + late final InAppBrowser _browser; + @override void initState() { + _browser = _AuthInAppBrowser(widget._authCubit); + + if (!kIsWeb && defaultTargetPlatform != TargetPlatform.iOS) { + WidgetsBinding.instance.addPostFrameCallback( + (timeStamp) => _browser.addMenuItem( + InAppBrowserMenuItem( + id: 0, + title: MaterialLocalizations.of(context).closeButtonLabel, + showAsAction: true, + onClick: () async => _browser.close(), + ), + ), + ); + } + unawaited(widget._authCubit.init()); super.initState(); } @@ -46,6 +64,7 @@ class _AuthPageState extends State { return BlocConsumer( listenWhen: (previous, current) => current.isLoggedIn, listener: (context, state) async { + await _browser.close(); final confirm = await context.push( AppRoute.confirmDialog.location(), extra: ( @@ -68,15 +87,40 @@ class _AuthPageState extends State { username: state.username!, ) : Scaffold( - body: CustomScrollView( + body: const CustomScrollView( slivers: [ - const _SliverAuthAppBar(), + _SliverAuthAppBar(), SliverSafeArea( top: false, - sliver: _SliverAuthBody(widget._authCubit), + sliver: SliverFillRemaining( + hasScrollBody: false, + child: _AuthBody(), + ), ), ], ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () async => _browser.openUrlRequest( + settings: InAppBrowserClassSettings( + browserSettings: InAppBrowserSettings( + hideUrlBar: true, + hideDefaultMenuItems: true, + hideToolbarBottom: true, + ), + webViewSettings: InAppWebViewSettings( + isInspectable: kDebugMode, + incognito: true, + ), + ), + urlRequest: URLRequest( + url: WebUri( + Uri.https('news.ycombinator.com', 'login').toString(), + ), + ), + ), + icon: const Icon(Icons.login_outlined), + label: Text(context.l10n.login), + ), ), ); } @@ -91,72 +135,63 @@ class _SliverAuthAppBar extends StatelessWidget { } } -class _SliverAuthBody extends StatelessWidget { - const _SliverAuthBody(this._authCubit); - - final AuthCubit _authCubit; +class _AuthBody extends StatelessWidget { + const _AuthBody(); @override Widget build(BuildContext context) { - return SliverMainAxisGroup( - slivers: [ - SliverPadding( - padding: AppSpacing.defaultTilePadding, - sliver: SliverToBoxAdapter( - child: DecoratedCard.outlined( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text(context.l10n.authDescription), - TextButtonTheme( - data: TextButtonThemeData( - style: TextButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - ), - child: Wrap( - alignment: WrapAlignment.end, - children: [ - TextButton( - onPressed: () async => Uri.https( - 'github.com', - 'Mosc/Glider/blob/master/PRIVACY.md', - ).tryLaunch(), - child: Text(context.l10n.privacyPolicy), - ), - TextButton( - onPressed: () async => Uri.https( - 'www.ycombinator.com', - 'legal', - ).replace(fragment: 'privacy').tryLaunch(), - child: Text(context.l10n.privacyPolicyYc), - ), - TextButton( - onPressed: () async => Uri.https( - 'www.ycombinator.com', - 'legal', - ).replace(fragment: 'tou').tryLaunch(), - child: Text(context.l10n.termsOfUseYc), - ), - ], - ), - ), - ], + return Padding( + padding: AppSpacing.defaultTilePadding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text(context.l10n.authDescription), + TextButtonTheme( + data: TextButtonThemeData( + style: TextButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), - ), - ), - SliverFillRemaining( - child: InAppWebView( - initialUrlRequest: URLRequest( - url: WebUri( - Uri.https('news.ycombinator.com', 'login').toString(), - ), + child: ButtonBar( + children: [ + TextButton( + onPressed: () async => Uri.https( + 'github.com', + 'Mosc/Glider/blob/master/PRIVACY.md', + ).tryLaunch(), + child: Text(context.l10n.privacyPolicy), + ), + TextButton( + onPressed: () async => Uri.https( + 'www.ycombinator.com', + 'legal', + ).replace(fragment: 'privacy').tryLaunch(), + child: Text(context.l10n.privacyPolicyYc), + ), + TextButton( + onPressed: () async => Uri.https( + 'www.ycombinator.com', + 'legal', + ).replace(fragment: 'tou').tryLaunch(), + child: Text(context.l10n.termsOfUseYc), + ), + ], ), - onPageCommitVisible: (controller, url) async => _authCubit.login(), ), - ), - ], + ].spaced(height: AppSpacing.m), + ), ); } } + +class _AuthInAppBrowser extends InAppBrowser { + _AuthInAppBrowser(this._authCubit); + + final AuthCubit _authCubit; + + @override + void onPageCommitVisible(WebUri? url) { + _authCubit.login(); + super.onPageCommitVisible(url); + } +} diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 12f24f80..372ebc1b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -48,7 +48,8 @@ "sourceCode": "Source code", "issueTracker": "Issue tracker", "account": "Account", - "authDescription": "Log in or register on Hacker News below. The session cookie will be securely saved and used for requests that require authentication. This app will not have access to your password.", + "login": "Log in", + "authDescription": "Log in on Hacker News below. The session cookie will be securely saved and used for requests that require authentication. This app will not have access to your password.", "termsOfUseYc": "Terms of use (YC)", "logout": "Log out", "showMore": "Show more", From fd439f8e7dc637a406a52b76a3a3f96bfdb50484 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 21:15:00 +0100 Subject: [PATCH 036/145] Upgrade dependencies --- ios/Podfile.lock | 2 +- pubspec.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2af75c95..2e296480 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -70,7 +70,7 @@ SPEC CHECKSUMS: path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 - url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86 + url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/pubspec.lock b/pubspec.lock index 05e01bb5..1923ee81 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" dependency_validator: dependency: "direct dev" description: @@ -935,10 +935,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: @@ -1068,10 +1068,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.1" url_launcher_linux: dependency: transitive description: @@ -1209,5 +1209,5 @@ packages: source: hosted version: "2.1.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" From 771467706cc8ed892d1feaf7600743890a7eda85 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 21:15:26 +0100 Subject: [PATCH 037/145] Bump version --- fastlane/metadata/android/en-US/changelogs/45.txt | 4 ++++ pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/45.txt diff --git a/fastlane/metadata/android/en-US/changelogs/45.txt b/fastlane/metadata/android/en-US/changelogs/45.txt new file mode 100644 index 00000000..a7bbac5c --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/45.txt @@ -0,0 +1,4 @@ +- Added action to mark items as read or unread +- Added setting to clear all read statuses +- Fixed login autofill issues by replacing login flow +- Improved (scrolling) performance \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 79f132f2..b9f5e75e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: glider description: An opinionated Hacker News client. -version: 2.3.0+44 +version: 2.4.0+45 publish_to: none environment: From 5ea0de5908a7242c00a37c59db4d57e682749e3a Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 21 Nov 2023 22:05:53 +0100 Subject: [PATCH 038/145] Use cache clearing over incognito to fix iOS login --- lib/auth/view/auth_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index 598ec165..d663a5fe 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -109,7 +109,7 @@ class _AuthPageState extends State { ), webViewSettings: InAppWebViewSettings( isInspectable: kDebugMode, - incognito: true, + clearCache: true, ), ), urlRequest: URLRequest( From 6cf8380093b8d7d0d92a9b381e8fd3b23d319b8b Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 23 Nov 2023 08:20:51 +0100 Subject: [PATCH 039/145] Speed up Algolia calls --- packages/glider_data/lib/src/algolia_api_service.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/glider_data/lib/src/algolia_api_service.dart b/packages/glider_data/lib/src/algolia_api_service.dart index 3622729b..6087a33d 100644 --- a/packages/glider_data/lib/src/algolia_api_service.dart +++ b/packages/glider_data/lib/src/algolia_api_service.dart @@ -15,7 +15,8 @@ class AlgoliaApiService { Map _getCommonQueryParameters({int hits = 1000}) => { 'hitsPerPage': hits.toString(), - 'attributesToHighlight': '', + // Specify non-existent attribute to avoid returning highlight results. + 'attributesToHighlight': '_', 'typoTolerance': false.toString(), 'analytics': false.toString(), }; From 809d59237d8d37562850c04bf0e3dccccb166230 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 23 Nov 2023 19:42:00 +0100 Subject: [PATCH 040/145] Handle item tree load error when not empty --- lib/item_tree/cubit/item_tree_cubit.dart | 27 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/item_tree/cubit/item_tree_cubit.dart b/lib/item_tree/cubit/item_tree_cubit.dart index 84a9a753..341d3a50 100644 --- a/lib/item_tree/cubit/item_tree_cubit.dart +++ b/lib/item_tree/cubit/item_tree_cubit.dart @@ -56,15 +56,24 @@ class ItemTreeCubit extends HydratedCubit { cancelOnError: true, ); } else { - final descendants = await descendantsStream.last; - safeEmit( - state.copyWith( - status: () => Status.success, - data: () => descendants, - previousData: () => state.data, - exception: () => null, - ), - ); + try { + final descendants = await descendantsStream.last; + safeEmit( + state.copyWith( + status: () => Status.success, + data: () => descendants, + previousData: () => state.data, + exception: () => null, + ), + ); + } on Object catch (exception) { + safeEmit( + state.copyWith( + status: () => Status.failure, + exception: () => exception, + ), + ); + } } } From 3ab5c792c9797ca22454789388f87d47469a3394 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 23 Nov 2023 19:54:33 +0100 Subject: [PATCH 041/145] Add custom tabs setting --- lib/app/router/app_router.dart | 6 ++- lib/auth/view/auth_page.dart | 22 ++++++--- lib/common/extensions/uri_extension.dart | 32 +++++++++---- lib/common/widgets/hacker_news_text.dart | 15 ++++-- lib/edit/view/edit_page.dart | 1 + lib/item/widgets/item_data_tile.dart | 23 +++++---- lib/item/widgets/item_tile.dart | 1 + lib/l10n/arb/app_en.arb | 2 + lib/reply/view/reply_page.dart | 1 + lib/settings/cubit/settings_cubit.dart | 11 +++++ lib/settings/cubit/settings_state.dart | 6 +++ lib/settings/view/settings_page.dart | 25 ++++++++-- lib/submit/view/submit_page.dart | 1 + lib/user/widgets/user_data_tile.dart | 10 ++-- lib/user/widgets/user_tile.dart | 48 ++++++++++--------- lib/whats_new/view/whats_new_page.dart | 34 ++++++++----- .../lib/src/shared_preferences_service.dart | 7 +++ .../lib/src/settings_repository.dart | 6 +++ 18 files changed, 178 insertions(+), 73 deletions(-) diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index a268172c..8b913720 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -108,9 +108,11 @@ class AppRouter { ), GoRoute( path: AppRoute.whatsNew.path, - pageBuilder: (context, state) => const MaterialPage( + pageBuilder: (context, state) => MaterialPage( fullscreenDialog: true, - child: WhatsNewPage(), + child: WhatsNewPage( + appContainer.settingsCubit, + ), ), parentNavigatorKey: rootNavigatorKey, ), diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index d663a5fe..43c9f7d8 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -87,14 +87,14 @@ class _AuthPageState extends State { username: state.username!, ) : Scaffold( - body: const CustomScrollView( + body: CustomScrollView( slivers: [ - _SliverAuthAppBar(), + const _SliverAuthAppBar(), SliverSafeArea( top: false, sliver: SliverFillRemaining( hasScrollBody: false, - child: _AuthBody(), + child: _AuthBody(widget._settingsCubit), ), ), ], @@ -136,7 +136,9 @@ class _SliverAuthAppBar extends StatelessWidget { } class _AuthBody extends StatelessWidget { - const _AuthBody(); + const _AuthBody(this._settingsCubit); + + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { @@ -158,21 +160,27 @@ class _AuthBody extends StatelessWidget { onPressed: () async => Uri.https( 'github.com', 'Mosc/Glider/blob/master/PRIVACY.md', - ).tryLaunch(), + ).tryLaunch( + useInAppBrowser: _settingsCubit.state.useInAppBrowser, + ), child: Text(context.l10n.privacyPolicy), ), TextButton( onPressed: () async => Uri.https( 'www.ycombinator.com', 'legal', - ).replace(fragment: 'privacy').tryLaunch(), + ).replace(fragment: 'privacy').tryLaunch( + useInAppBrowser: _settingsCubit.state.useInAppBrowser, + ), child: Text(context.l10n.privacyPolicyYc), ), TextButton( onPressed: () async => Uri.https( 'www.ycombinator.com', 'legal', - ).replace(fragment: 'tou').tryLaunch(), + ).replace(fragment: 'tou').tryLaunch( + useInAppBrowser: _settingsCubit.state.useInAppBrowser, + ), child: Text(context.l10n.termsOfUseYc), ), ], diff --git a/lib/common/extensions/uri_extension.dart b/lib/common/extensions/uri_extension.dart index a7fa66f1..edcea2db 100644 --- a/lib/common/extensions/uri_extension.dart +++ b/lib/common/extensions/uri_extension.dart @@ -1,23 +1,35 @@ import 'package:url_launcher/url_launcher.dart'; extension UriExtension on Uri { - Future tryLaunch({String? title}) async { + Future tryLaunch({String? title, required bool useInAppBrowser}) async { if (await canLaunchUrl(this)) { - final success = await launchUrl( - this, - mode: LaunchMode.externalNonBrowserApplication, - webOnlyWindowName: title, - ); + if (useInAppBrowser && + await supportsLaunchMode(LaunchMode.inAppBrowserView)) { + final success = await launchUrl( + this, + mode: LaunchMode.inAppBrowserView, + webOnlyWindowName: title, + ); + if (success) return true; + } - if (!success) { - await launchUrl( + if (await supportsLaunchMode(LaunchMode.externalNonBrowserApplication)) { + final success = await launchUrl( this, - mode: LaunchMode.inAppWebView, + mode: LaunchMode.externalNonBrowserApplication, webOnlyWindowName: title, ); + if (success) return true; } - return true; + if (await supportsLaunchMode(LaunchMode.externalApplication)) { + final success = await launchUrl( + this, + mode: LaunchMode.externalApplication, + webOnlyWindowName: title, + ); + if (success) return true; + } } return false; diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index 6e986465..caeaf7e2 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -11,13 +11,15 @@ import 'package:markdown/markdown.dart' as md; typedef ParsedData = List; class HackerNewsText extends StatelessWidget { - HackerNewsText(String data) - : parsedData = parse(data), + HackerNewsText( + String data, { + ParsedData? parsedData, + required this.useInAppBrowser, + }) : parsedData = parsedData ?? parse(data), super(key: ValueKey(data)); - const HackerNewsText.parsed(this.parsedData, {super.key}); - final ParsedData parsedData; + final bool useInAppBrowser; static final _extensionSet = md.ExtensionSet( const [ @@ -87,7 +89,10 @@ class HackerNewsText extends StatelessWidget { styleSheet: styleSheet, onTapLink: (text, href, title) async { if (href != null) { - await Uri.tryParse(href)?.tryLaunch(title: title); + await Uri.tryParse(href)?.tryLaunch( + title: title, + useInAppBrowser: useInAppBrowser, + ); } }, builders: {'pre': _PreElementBuilder(styleSheet)}, diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index 32414b27..c8b2a7f4 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -272,6 +272,7 @@ class _EditPreview extends StatelessWidget { showFavicons: settingsState.showFavicons, showUserAvatars: settingsState.showUserAvatars, usernameStyle: UsernameStyle.loggedInUser, + useInAppBrowser: settingsState.useInAppBrowser, ), ) : const SizedBox.shrink(), diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 8a78d9d0..2ae5b11a 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -38,6 +38,7 @@ class ItemDataTile extends StatelessWidget { this.showFavicons = true, this.showMetadata = true, this.showUserAvatars = true, + this.useInAppBrowser = false, this.style = ItemStyle.full, this.usernameStyle = UsernameStyle.none, this.padding = AppSpacing.defaultTilePadding, @@ -60,6 +61,7 @@ class ItemDataTile extends StatelessWidget { final bool showFavicons; final bool showMetadata; final bool showUserAvatars; + final bool useInAppBrowser; final ItemStyle style; final UsernameStyle usernameStyle; final EdgeInsets padding; @@ -80,9 +82,11 @@ class ItemDataTile extends StatelessWidget { Expanded( child: Hero( tag: 'item_tile_text_${item.id}', - child: parsedText != null - ? HackerNewsText.parsed(parsedText!) - : HackerNewsText(text), + child: HackerNewsText( + text, + parsedData: parsedText, + useInAppBrowser: useInAppBrowser, + ), ), ) else @@ -160,7 +164,8 @@ class ItemDataTile extends StatelessWidget { AnimatedVisibility( visible: style == ItemStyle.overview, child: InkWell( - onTap: () async => item.url!.tryLaunch(), + onTap: () async => + item.url!.tryLaunch(useInAppBrowser: useInAppBrowser), // Explicitly override parent widget's long press. onLongPress: () {}, child: _ItemFavicon( @@ -337,13 +342,15 @@ class ItemDataTile extends StatelessWidget { if (item.text case final text?) Hero( tag: 'item_tile_text_${item.id}', - child: parsedText != null - ? HackerNewsText.parsed(parsedText!) - : HackerNewsText(text), + child: HackerNewsText( + text, + parsedData: parsedText, + useInAppBrowser: useInAppBrowser, + ), ), if (item.url case final url?) DecoratedCard.outlined( - onTap: () async => url.tryLaunch(), + onTap: () async => url.tryLaunch(useInAppBrowser: useInAppBrowser), // Explicitly override parent widget's long press. onLongPress: () {}, child: Row( diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 3b6df14f..860ad650 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -151,6 +151,7 @@ class _ItemTileState extends State ? UsernameStyle.storyUser : UsernameStyle.none, padding: widget.padding, + useInAppBrowser: settingsState.useInAppBrowser, onTap: item.type == ItemType.pollopt ? ItemAction.upvote .isVisible(state, authState, settingsState) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 372ebc1b..4e18459d 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -36,6 +36,8 @@ "threadNavigationDescription": "Allows jumps between top-level comments", "downvoting": "Enable downvoting", "downvotingDescription": "Requires an account with 501+ karma", + "inAppBrowser": "Custom tabs", + "inAppBrowserDescription": "Opens links in in-app browser if possible", "data": "Data", "exportFavorites": "Export favorites", "exportFavoritesDescription": "Shares favorites as IDs in JSON format", diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 1048702d..547f2705 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -241,6 +241,7 @@ class _ReplyPreview extends StatelessWidget { showFavicons: settingsState.showFavicons, showUserAvatars: settingsState.showUserAvatars, usernameStyle: UsernameStyle.loggedInUser, + useInAppBrowser: settingsState.useInAppBrowser, ), ), ), diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index 98318f08..c17c37b0 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -226,6 +226,17 @@ class SettingsCubit extends Cubit } } + Future setUseInAppBrowser(bool value) async { + await _settingsRepository.setUseInAppBrowser(value: value); + final useInAppBrowser = await _settingsRepository.getUseInAppBrowser(); + + if (useInAppBrowser != null) { + safeEmit( + state.copyWith(useInAppBrowser: () => useInAppBrowser), + ); + } + } + Future exportFavorites() async { final favorites = await _itemInteractionRepository.favoritedStream.first; diff --git a/lib/settings/cubit/settings_state.dart b/lib/settings/cubit/settings_state.dart index 444368e8..4be6c275 100644 --- a/lib/settings/cubit/settings_state.dart +++ b/lib/settings/cubit/settings_state.dart @@ -16,6 +16,7 @@ class SettingsState with EquatableMixin { this.showJobs = true, this.useThreadNavigation = true, this.enableDownvoting = false, + this.useInAppBrowser = false, this.appVersion, }); @@ -33,6 +34,7 @@ class SettingsState with EquatableMixin { final bool showJobs; final bool useThreadNavigation; final bool enableDownvoting; + final bool useInAppBrowser; final Version? appVersion; SettingsState copyWith({ @@ -50,6 +52,7 @@ class SettingsState with EquatableMixin { bool Function()? showJobs, bool Function()? useThreadNavigation, bool Function()? enableDownvoting, + bool Function()? useInAppBrowser, Version? Function()? appVersion, }) => SettingsState( @@ -81,6 +84,8 @@ class SettingsState with EquatableMixin { enableDownvoting: enableDownvoting != null ? enableDownvoting() : this.enableDownvoting, + useInAppBrowser: + useInAppBrowser != null ? useInAppBrowser() : this.useInAppBrowser, appVersion: appVersion != null ? appVersion() : this.appVersion, ); @@ -100,6 +105,7 @@ class SettingsState with EquatableMixin { showJobs, useThreadNavigation, enableDownvoting, + useInAppBrowser, appVersion, ]; } diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 50c7c645..427e7871 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -235,6 +235,7 @@ class _SettingsBody extends StatelessWidget { showMetadata: state.showStoryMetadata, showUserAvatars: state.showUserAvatars, style: ItemStyle.overview, + useInAppBrowser: state.useInAppBrowser, onTapFavorite: state.useActionButtons ? () {} : null, onTapUpvote: state.useActionButtons ? () {} : null, ), @@ -276,6 +277,14 @@ class _SettingsBody extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), + SwitchListTile.adaptive( + value: state.useInAppBrowser, + onChanged: _settingsCubit.setUseInAppBrowser, + title: Text(context.l10n.inAppBrowser), + subtitle: Text(context.l10n.inAppBrowserDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), const Divider(), Padding( padding: AppSpacing.defaultTilePadding, @@ -314,25 +323,33 @@ class _SettingsBody extends StatelessWidget { ListTile( title: Text(context.l10n.privacyPolicy), trailing: const Icon(Icons.open_in_new_outlined), - onTap: _privacyPolicyUrl.tryLaunch, + onTap: () => _privacyPolicyUrl.tryLaunch( + useInAppBrowser: state.useInAppBrowser, + ), ), ListTile( title: Text(context.l10n.license), subtitle: const Text(_license), trailing: const Icon(Icons.open_in_new_outlined), - onTap: _licenseUrl.tryLaunch, + onTap: () => _licenseUrl.tryLaunch( + useInAppBrowser: state.useInAppBrowser, + ), ), ListTile( title: Text(context.l10n.sourceCode), subtitle: Text(_sourceCodeUrl.toString()), trailing: const Icon(Icons.open_in_new_outlined), - onTap: _sourceCodeUrl.tryLaunch, + onTap: () => _sourceCodeUrl.tryLaunch( + useInAppBrowser: state.useInAppBrowser, + ), ), ListTile( title: Text(context.l10n.issueTracker), subtitle: Text(_issueTrackerUrl.toString()), trailing: const Icon(Icons.open_in_new_outlined), - onTap: _issueTrackerUrl.tryLaunch, + onTap: () => _issueTrackerUrl.tryLaunch( + useInAppBrowser: state.useInAppBrowser, + ), ), ListTile( title: Text( diff --git a/lib/submit/view/submit_page.dart b/lib/submit/view/submit_page.dart index ddd32ffb..bc29a667 100644 --- a/lib/submit/view/submit_page.dart +++ b/lib/submit/view/submit_page.dart @@ -292,6 +292,7 @@ class _SubmitPreview extends StatelessWidget { showFavicons: settingsState.showFavicons, showUserAvatars: settingsState.showUserAvatars, usernameStyle: UsernameStyle.loggedInUser, + useInAppBrowser: settingsState.useInAppBrowser, ), ), ), diff --git a/lib/user/widgets/user_data_tile.dart b/lib/user/widgets/user_data_tile.dart index 62711dc7..bb89f8fc 100644 --- a/lib/user/widgets/user_data_tile.dart +++ b/lib/user/widgets/user_data_tile.dart @@ -18,6 +18,7 @@ class UserDataTile extends StatelessWidget { this.blocked = false, this.style = UserStyle.full, this.padding = AppSpacing.defaultTilePadding, + required this.useInAppBrowser, this.onTap, this.onLongPress, }); @@ -27,6 +28,7 @@ class UserDataTile extends StatelessWidget { final bool blocked; final UserStyle style; final EdgeInsets padding; + final bool useInAppBrowser; final UserCallback? onTap; final UserCallback? onLongPress; @@ -118,9 +120,11 @@ class UserDataTile extends StatelessWidget { Widget _buildSecondary(BuildContext context) { return Hero( tag: 'user_tile_about_${user.username}', - child: parsedAbout != null - ? HackerNewsText.parsed(parsedAbout!) - : HackerNewsText(user.about!), + child: HackerNewsText( + user.about!, + parsedData: parsedAbout, + useInAppBrowser: useInAppBrowser, + ), ); } } diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index 9e14ae79..be7add9f 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -45,31 +45,35 @@ class UserTile extends StatelessWidget { }, child: BlocBuilder( bloc: _userCubit, - builder: (context, state) => state.whenOrDefaultWidgets( - loading: () => UserLoadingTile( - style: style, - padding: padding, - ), - success: () { - final user = state.data!; - return UserDataTile( - user, - parsedAbout: state.parsedAbout, - blocked: state.blocked, + builder: (context, state) => BlocBuilder( + bloc: _settingsCubit, + builder: (context, settingsState) => state.whenOrDefaultWidgets( + loading: () => UserLoadingTile( style: style, padding: padding, - onTap: onTap, - onLongPress: (context, item) async => showModalBottomSheet( - context: rootNavigatorKey.currentContext!, - builder: (context) => UserBottomSheet( - _userCubit, - _authCubit, - _settingsCubit, + ), + success: () { + final user = state.data!; + return UserDataTile( + user, + parsedAbout: state.parsedAbout, + blocked: state.blocked, + style: style, + padding: padding, + useInAppBrowser: settingsState.useInAppBrowser, + onTap: onTap, + onLongPress: (context, item) async => showModalBottomSheet( + context: rootNavigatorKey.currentContext!, + builder: (context) => UserBottomSheet( + _userCubit, + _authCubit, + _settingsCubit, + ), ), - ), - ); - }, - onRetry: () async => _userCubit.load(), + ); + }, + onRetry: () async => _userCubit.load(), + ), ), ), ); diff --git a/lib/whats_new/view/whats_new_page.dart b/lib/whats_new/view/whats_new_page.dart index e9d86522..87db4d7a 100644 --- a/lib/whats_new/view/whats_new_page.dart +++ b/lib/whats_new/view/whats_new_page.dart @@ -1,31 +1,33 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/widgets/hacker_news_text.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:go_router/go_router.dart'; class WhatsNewPage extends StatelessWidget { - const WhatsNewPage({ - this.selectedColor, + const WhatsNewPage( + this._settingsCubit, { super.key, }); - final Color? selectedColor; + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { return Scaffold( - body: const CustomScrollView( + body: CustomScrollView( slivers: [ - _SliverWhatsNewAppBar(), + const _SliverWhatsNewAppBar(), SliverSafeArea( top: false, sliver: SliverToBoxAdapter( - child: _WhatsNewBody(), + child: _WhatsNewBody(_settingsCubit), ), ), - SliverToBoxAdapter( - child: SizedBox(height: AppSpacing.xxl * 4), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, ), ], ), @@ -48,13 +50,21 @@ class _SliverWhatsNewAppBar extends StatelessWidget { } class _WhatsNewBody extends StatelessWidget { - const _WhatsNewBody(); + const _WhatsNewBody(this._settingsCubit); + + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - child: HackerNewsText(context.l10n.whatsNewDescription), + return BlocBuilder( + bloc: _settingsCubit, + builder: (context, settingsState) => Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + child: HackerNewsText( + context.l10n.whatsNewDescription, + useInAppBrowser: settingsState.useInAppBrowser, + ), + ), ); } } diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index d77b33b3..7076bf95 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -19,6 +19,7 @@ class SharedPreferencesService { static const String _showJobsKey = 'show_jobs'; static const String _useThreadNavigationKey = 'use_thread_navigation'; static const String _enableDownvotingKey = 'enable_downvoting'; + static const String _useInAppBrowserKey = 'use_in_app_browser'; static const String _lastVersionKey = 'last_version'; static const String _visitedKey = 'visited'; static const String _upvotedKey = 'upvoted'; @@ -109,6 +110,12 @@ class SharedPreferencesService { Future setEnableDownvoting({required bool value}) async => _sharedPreferences.setBool(_enableDownvotingKey, value); + Future getUseInAppBrowser() async => + _sharedPreferences.getBool(_useInAppBrowserKey); + + Future setUseInAppBrowser({required bool value}) async => + _sharedPreferences.setBool(_useInAppBrowserKey, value); + Future getLastVersion() async => _sharedPreferences.getString(_lastVersionKey); diff --git a/packages/glider_domain/lib/src/settings_repository.dart b/packages/glider_domain/lib/src/settings_repository.dart index 210ac106..0171f227 100644 --- a/packages/glider_domain/lib/src/settings_repository.dart +++ b/packages/glider_domain/lib/src/settings_repository.dart @@ -96,4 +96,10 @@ class SettingsRepository { Future setEnableDownvoting({required bool value}) async => _sharedPreferencesService.setEnableDownvoting(value: value); + + Future getUseInAppBrowser() async => + _sharedPreferencesService.getUseInAppBrowser(); + + Future setUseInAppBrowser({required bool value}) async => + _sharedPreferencesService.setUseInAppBrowser(value: value); } From b1deffebd3bedfa1ec9afe0e2b60301a9fd03784 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 23 Nov 2023 20:00:27 +0100 Subject: [PATCH 042/145] Eliminate an unnecessary unwrap --- lib/item/widgets/item_data_tile.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 2ae5b11a..f7003275 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -310,7 +310,7 @@ class ItemDataTile extends StatelessWidget { child: MetadataWidget( label: Tooltip( message: dateTime.toString(), - child: Text(item.dateTime!.relativeTime(context)), + child: Text(dateTime.relativeTime(context)), ), ), ), From ad7e62ac0deed5cd09ea95dad1d722d3c3d2bf9e Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 23 Nov 2023 23:28:56 +0100 Subject: [PATCH 043/145] Tweak string concatenation --- lib/app/models/app_route.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/app/models/app_route.dart b/lib/app/models/app_route.dart index 4c8137fd..e22ae9d2 100644 --- a/lib/app/models/app_route.dart +++ b/lib/app/models/app_route.dart @@ -21,10 +21,10 @@ enum AppRoute { final AppRoute? parent; - String get path => parent != null ? name : '/$name'; + String get path => [if (parent == null) '/', name].join(); String location({Map? parameters}) => Uri( - path: parent != null ? '${parent!.path}/$path' : path, + path: [if (parent case final parent?) '${parent.path}/', path].join(), queryParameters: parameters != null ? { for (final parameter in parameters.entries) From 970b3adc74010cfb1fc0885e30833f0f9a0cb02d Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 23 Nov 2023 23:29:53 +0100 Subject: [PATCH 044/145] Make widgets responsible for setting appropriate key --- lib/edit/view/edit_page.dart | 5 ++--- lib/favorites/view/favorites_shell_page.dart | 1 - lib/inbox/view/inbox_shell_page.dart | 2 -- lib/item/view/item_page.dart | 5 ++--- lib/item/widgets/avatar_widget.dart | 5 +---- lib/item/widgets/item_tile.dart | 10 +++++----- lib/item/widgets/item_value_dialog.dart | 5 ++--- lib/item/widgets/username_widget.dart | 5 ++--- lib/item_tree/view/sliver_item_tree_body.dart | 1 - lib/reply/view/reply_page.dart | 5 ++--- lib/stories/view/stories_shell_page.dart | 1 - .../view/sliver_stories_search_body.dart | 1 - .../view/story_item_search_view.dart | 1 - .../view/sliver_story_similar_body.dart | 1 - lib/user/view/user_page.dart | 14 ++++++-------- lib/user/widgets/user_tile.dart | 5 ++--- lib/user/widgets/user_value_dialog.dart | 5 ++--- .../view/user_item_search_view.dart | 1 - 18 files changed, 26 insertions(+), 47 deletions(-) diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index c8b2a7f4..9692f4e8 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -18,12 +18,11 @@ import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:go_router/go_router.dart'; class EditPage extends StatefulWidget { - const EditPage( + EditPage( this._editCubitFactory, this._settingsCubit, { - super.key, required this.id, - }); + }) : super(key: ValueKey(id)); final EditCubitFactory _editCubitFactory; final SettingsCubit _settingsCubit; diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index 940dbed2..f8fe4fdd 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -155,7 +155,6 @@ class _SliverFavoritesBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: ItemType.story, onTap: (context, item) async => context.push( diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index d2fe9aca..d0edf182 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -156,7 +156,6 @@ class _SliverInboxBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(parentId), id: parentId, loadingType: ItemType.story, onTap: (context, item) async => context.push( @@ -169,7 +168,6 @@ class _SliverInboxBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: ItemType.comment, onTap: (context, item) async => context.push( diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index cf65533d..772e0066 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -33,7 +33,7 @@ import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:sliver_tools/sliver_tools.dart'; class ItemPage extends StatefulWidget { - const ItemPage( + ItemPage( this._itemCubitFactory, this._itemTreeCubitFactory, this._storySimilarCubitFactory, @@ -41,8 +41,7 @@ class ItemPage extends StatefulWidget { this._authCubit, this._settingsCubit, { required this.id, - super.key, - }); + }) : super(key: ValueKey(id)); final ItemCubitFactory _itemCubitFactory; final ItemTreeCubitFactory _itemTreeCubitFactory; diff --git a/lib/item/widgets/avatar_widget.dart b/lib/item/widgets/avatar_widget.dart index e7a0c293..c97bb9cc 100644 --- a/lib/item/widgets/avatar_widget.dart +++ b/lib/item/widgets/avatar_widget.dart @@ -4,10 +4,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; class AvatarWidget extends StatelessWidget { - const AvatarWidget({ - super.key, - required this.username, - }); + AvatarWidget({required this.username}) : super(key: ValueKey(username)); final String username; diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 860ad650..418372a5 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -24,7 +24,6 @@ class ItemTile extends StatefulWidget { ItemCubit itemCubit, this._authCubit, this._settingsCubit, { - super.key, this.storyUsername, required this.loadingType, this.collapsedCount, @@ -37,13 +36,13 @@ class ItemTile extends StatefulWidget { this.onTap, }) : _itemCubit = itemCubit, _itemCubitFactory = null, - id = itemCubit.itemId; + id = itemCubit.itemId, + super(key: ValueKey(itemCubit.itemId)); - const ItemTile.create( + ItemTile.create( ItemCubitFactory itemCubitFactory, this._authCubit, this._settingsCubit, { - super.key, required this.id, this.storyUsername, required this.loadingType, @@ -56,7 +55,8 @@ class ItemTile extends StatefulWidget { this.padding = AppSpacing.defaultTilePadding, this.onTap, }) : _itemCubit = null, - _itemCubitFactory = itemCubitFactory; + _itemCubitFactory = itemCubitFactory, + super(key: ValueKey(id)); final ItemCubit? _itemCubit; final ItemCubitFactory? _itemCubitFactory; diff --git a/lib/item/widgets/item_value_dialog.dart b/lib/item/widgets/item_value_dialog.dart index b9449e0e..163168fb 100644 --- a/lib/item/widgets/item_value_dialog.dart +++ b/lib/item/widgets/item_value_dialog.dart @@ -9,14 +9,13 @@ import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:go_router/go_router.dart'; class ItemValueDialog extends StatefulWidget { - const ItemValueDialog( + ItemValueDialog( this._itemCubitFactory, this._authCubit, this._settingsCubit, { required this.id, this.title, - super.key, - }); + }) : super(key: ValueKey(id)); final ItemCubitFactory _itemCubitFactory; final AuthCubit _authCubit; diff --git a/lib/item/widgets/username_widget.dart b/lib/item/widgets/username_widget.dart index b92f75b5..c817f93a 100644 --- a/lib/item/widgets/username_widget.dart +++ b/lib/item/widgets/username_widget.dart @@ -3,13 +3,12 @@ import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/item/widgets/avatar_widget.dart'; class UsernameWidget extends StatelessWidget { - const UsernameWidget({ - super.key, + UsernameWidget({ required this.username, this.showAvatar = true, this.style = UsernameStyle.none, this.onTap, - }); + }) : super(key: ValueKey(username)); final String username; final bool showAvatar; diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index ce630a8d..b06b4572 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -83,7 +83,6 @@ class SliverItemTreeBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(descendant.id), id: descendant.id, storyUsername: storyUsername, loadingType: ItemType.comment, diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 547f2705..da6f4faa 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -19,13 +19,12 @@ import 'package:glider_domain/glider_domain.dart'; import 'package:go_router/go_router.dart'; class ReplyPage extends StatefulWidget { - const ReplyPage( + ReplyPage( this._replyCubitFactory, this._authCubit, this._settingsCubit, { - super.key, required this.id, - }); + }) : super(key: ValueKey(id)); final ReplyCubitFactory _replyCubitFactory; final AuthCubit _authCubit; diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index 46d9beb5..8203c245 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -243,7 +243,6 @@ class _SliverStoriesBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: ItemType.story, showMetadata: settingsState.showStoryMetadata, diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index 24e627e3..1d15b075 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -57,7 +57,6 @@ class SliverStoriesSearchBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: ItemType.story, showMetadata: settingsState.showStoryMetadata, diff --git a/lib/story_item_search/view/story_item_search_view.dart b/lib/story_item_search/view/story_item_search_view.dart index 9e93c14c..c7124b7a 100644 --- a/lib/story_item_search/view/story_item_search_view.dart +++ b/lib/story_item_search/view/story_item_search_view.dart @@ -72,7 +72,6 @@ class _SliverStoryItemSearchBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: _storyItemSearchBloc.itemId == id ? ItemType.story diff --git a/lib/story_similar/view/sliver_story_similar_body.dart b/lib/story_similar/view/sliver_story_similar_body.dart index 2620a726..8ea8b3ee 100644 --- a/lib/story_similar/view/sliver_story_similar_body.dart +++ b/lib/story_similar/view/sliver_story_similar_body.dart @@ -81,7 +81,6 @@ class SliverStorySimilarBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, storyUsername: storyUsername, loadingType: ItemType.story, diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 1f73f945..57b63288 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -27,15 +27,14 @@ import 'package:go_router/go_router.dart'; const _toolbarHeight = 32.0; class UserPage extends StatefulWidget { - const UserPage( + UserPage( this._userCubitFactory, this._itemCubitFactory, this._userItemSearchBlocFactory, this._authCubit, this._settingsCubit, { - super.key, required this.username, - }); + }) : super(key: ValueKey(username)); final UserCubitFactory _userCubitFactory; final ItemCubitFactory _itemCubitFactory; @@ -88,7 +87,7 @@ class _UserPageState extends State { _userItemSearchBloc, widget._authCubit, widget._settingsCubit, - id: widget.username, + username: widget.username, scrollController: _scrollController, ), SliverSafeArea( @@ -142,7 +141,7 @@ class _SliverUserAppBar extends StatefulWidget { this._userItemSearchBloc, this._authCubit, this._settingsCubit, { - required this.id, + required this.username, required this.scrollController, }); @@ -151,7 +150,7 @@ class _SliverUserAppBar extends StatefulWidget { final UserItemSearchBloc _userItemSearchBloc; final AuthCubit _authCubit; final SettingsCubit _settingsCubit; - final String id; + final String username; final ScrollController scrollController; @override @@ -181,7 +180,7 @@ class _SliverUserAppBarState extends State<_SliverUserAppBar> { @override Widget build(BuildContext context) { return SliverAppBar( - title: Text(widget.id), + title: Text(widget.username), flexibleSpace: AppBarProgressIndicator(widget._userCubit), bottom: PreferredSize( preferredSize: const Size.fromHeight(_toolbarHeight), @@ -356,7 +355,6 @@ class _SliverUserBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: ItemType.story, onTap: (context, item) async => context.push( diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index be7add9f..c54f3811 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -15,15 +15,14 @@ import 'package:glider/user/widgets/user_data_tile.dart'; import 'package:glider/user/widgets/user_loading_tile.dart'; class UserTile extends StatelessWidget { - const UserTile( + UserTile( this._userCubit, this._authCubit, this._settingsCubit, { - super.key, this.style = UserStyle.full, this.padding = AppSpacing.defaultTilePadding, this.onTap, - }); + }) : super(key: ValueKey(_userCubit.username)); final UserCubit _userCubit; final AuthCubit _authCubit; diff --git a/lib/user/widgets/user_value_dialog.dart b/lib/user/widgets/user_value_dialog.dart index 6261b6da..70e00b25 100644 --- a/lib/user/widgets/user_value_dialog.dart +++ b/lib/user/widgets/user_value_dialog.dart @@ -9,14 +9,13 @@ import 'package:glider/user/models/user_value.dart'; import 'package:go_router/go_router.dart'; class UserValueDialog extends StatefulWidget { - const UserValueDialog( + UserValueDialog( this._userCubitFactory, this._authCubit, this._settingsCubit, { required this.username, this.title, - super.key, - }); + }) : super(key: ValueKey(username)); final UserCubitFactory _userCubitFactory; final AuthCubit _authCubit; diff --git a/lib/user_item_search/view/user_item_search_view.dart b/lib/user_item_search/view/user_item_search_view.dart index a8f67594..36f19fd6 100644 --- a/lib/user_item_search/view/user_item_search_view.dart +++ b/lib/user_item_search/view/user_item_search_view.dart @@ -72,7 +72,6 @@ class _SliverUserItemSearchBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, - key: ValueKey(id), id: id, loadingType: ItemType.story, onTap: (context, item) async => context.push( From a5208a008729e282f3af022e83f4f2916dda2b0c Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 13:04:29 +0100 Subject: [PATCH 045/145] Do prefer external non-browser apps over custom tabs --- lib/common/extensions/uri_extension.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/common/extensions/uri_extension.dart b/lib/common/extensions/uri_extension.dart index edcea2db..882f3698 100644 --- a/lib/common/extensions/uri_extension.dart +++ b/lib/common/extensions/uri_extension.dart @@ -3,20 +3,20 @@ import 'package:url_launcher/url_launcher.dart'; extension UriExtension on Uri { Future tryLaunch({String? title, required bool useInAppBrowser}) async { if (await canLaunchUrl(this)) { - if (useInAppBrowser && - await supportsLaunchMode(LaunchMode.inAppBrowserView)) { + if (await supportsLaunchMode(LaunchMode.externalNonBrowserApplication)) { final success = await launchUrl( this, - mode: LaunchMode.inAppBrowserView, + mode: LaunchMode.externalNonBrowserApplication, webOnlyWindowName: title, ); if (success) return true; } - if (await supportsLaunchMode(LaunchMode.externalNonBrowserApplication)) { + if (useInAppBrowser && + await supportsLaunchMode(LaunchMode.inAppBrowserView)) { final success = await launchUrl( this, - mode: LaunchMode.externalNonBrowserApplication, + mode: LaunchMode.inAppBrowserView, webOnlyWindowName: title, ); if (success) return true; From 2ae019cabb5c25b0ed9fca99e8c5128432e36bec Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 14:19:33 +0100 Subject: [PATCH 046/145] Confirm before clearing read statuses --- lib/settings/view/settings_page.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 427e7871..219a01d7 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -302,7 +302,12 @@ class _SettingsBody extends StatelessWidget { ), ListTile( title: Text(context.l10n.clearVisited), - onTap: _settingsCubit.clearVisited, + onTap: () async { + final confirm = await context.push( + AppRoute.confirmDialog.location(), + ); + if (confirm ?? false) await _settingsCubit.clearVisited(); + }, ), const Divider(), Padding( From e87c1ef1792dab30d48a70c6e966845f06f5602f Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 16:59:38 +0100 Subject: [PATCH 047/145] Fix inbox parent item tap opening child comment --- lib/inbox/view/inbox_shell_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index d0edf182..428ad248 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -159,7 +159,7 @@ class _SliverInboxBody extends StatelessWidget { id: parentId, loadingType: ItemType.story, onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), + AppRoute.item.location(parameters: {'id': parentId}), ), ), IndentedWidget( From 79cb3124cc2837a39b826475d7e072f91f824f7e Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 19:30:48 +0100 Subject: [PATCH 048/145] Enforce title height more consistently --- lib/item/widgets/item_data_tile.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index f7003275..d42eeadb 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -478,10 +478,9 @@ class _ItemTitle extends StatelessWidget { text: ')', style: Theme.of(context).textTheme.titleSmall, ), - // Attach zero-width space of title style to - // enforce height. - const TextSpan(text: '\u200b'), ], + // Attach zero-width space of title style to enforce height. + const TextSpan(text: '\u200b'), if (useLargeStoryStyle) const TextSpan(text: '\n'), ], ), From 0e8dc9366c704a0f8d13dd3981421039641d8fa2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 20:09:06 +0100 Subject: [PATCH 049/145] Delegate more settings checking to item tile class --- lib/item/widgets/item_tile.dart | 21 +++-- lib/stories/view/stories_shell_page.dart | 90 +++++++++---------- .../view/sliver_stories_search_body.dart | 88 +++++++++--------- 3 files changed, 100 insertions(+), 99 deletions(-) diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 418372a5..9d0ec159 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -29,8 +29,8 @@ class ItemTile extends StatefulWidget { this.collapsedCount, this.showVisited = true, this.highlight = false, - this.showMetadata = true, - this.showJobs = true, + this.forceShowMetadata = true, + this.forceShowJobs = false, this.style = ItemStyle.full, this.padding = AppSpacing.defaultTilePadding, this.onTap, @@ -49,8 +49,8 @@ class ItemTile extends StatefulWidget { this.collapsedCount, this.showVisited = true, this.highlight = false, - this.showMetadata = true, - this.showJobs = true, + this.forceShowMetadata = true, + this.forceShowJobs = false, this.style = ItemStyle.full, this.padding = AppSpacing.defaultTilePadding, this.onTap, @@ -68,8 +68,8 @@ class ItemTile extends StatefulWidget { final int? collapsedCount; final bool showVisited; final bool highlight; - final bool showMetadata; - final bool showJobs; + final bool forceShowMetadata; + final bool forceShowJobs; final ItemStyle style; final EdgeInsets padding; final ItemCallback? onTap; @@ -112,14 +112,16 @@ class _ItemTileState extends State type: widget.loadingType, collapsedCount: widget.collapsedCount, useLargeStoryStyle: settingsState.useLargeStoryStyle, - showMetadata: settingsState.showStoryMetadata, + showMetadata: + settingsState.showStoryMetadata || widget.forceShowMetadata, style: widget.style, padding: widget.padding, ), success: () { final item = state.data!; - if (item.type == ItemType.job && !widget.showJobs) { + if (item.type == ItemType.job && + !(settingsState.showJobs || widget.forceShowJobs)) { return const SizedBox.shrink(); } @@ -142,7 +144,8 @@ class _ItemTileState extends State collapsedCount: widget.collapsedCount, useLargeStoryStyle: settingsState.useLargeStoryStyle, showFavicons: settingsState.showFavicons, - showMetadata: widget.showMetadata, + showMetadata: settingsState.showStoryMetadata || + widget.forceShowMetadata, showUserAvatars: settingsState.showUserAvatars, style: widget.style, usernameStyle: authState.username == item.username diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index 8203c245..7f741215 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -217,62 +217,60 @@ class _SliverStoriesBody extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( bloc: _storiesCubit, - builder: (context, state) => BlocBuilder( - bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.showStoryMetadata != current.showStoryMetadata || - previous.useActionButtons != current.useActionButtons || - previous.showJobs != current.showJobs, - builder: (context, settingsState) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( - itemBuilder: (context, index) => ItemLoadingTile( + builder: (context, state) => state.whenOrDefaultSlivers( + loading: () => SliverList.builder( + itemBuilder: (context, index) => + BlocBuilder( + bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.useLargeStoryStyle != current.useLargeStoryStyle || + previous.showStoryMetadata != current.showStoryMetadata, + builder: (context, settingsState) => ItemLoadingTile( type: ItemType.story, showMetadata: settingsState.showStoryMetadata, useLargeStoryStyle: settingsState.useLargeStoryStyle, style: ItemStyle.overview, ), ), - nonEmpty: () => SliverMainAxisGroup( - slivers: [ - SliverList.builder( - itemCount: state.loadedData!.length, - itemBuilder: (context, index) { - final id = state.loadedData![index]; - return ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - id: id, - loadingType: ItemType.story, - showMetadata: settingsState.showStoryMetadata, - showJobs: settingsState.showJobs || - state.storyType == StoryType.jobStories, - style: ItemStyle.overview, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), - ); - }, - ), - if (state.loadedData!.length < state.data!.length) - SliverPadding( - padding: AppSpacing.defaultTilePadding, - sliver: SliverToBoxAdapter( - child: OutlinedButton.icon( - onPressed: _storiesCubit.showMore, - style: OutlinedButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - icon: const Icon(Icons.expand_more_outlined), - label: Text(context.l10n.showMore), + ), + nonEmpty: () => SliverMainAxisGroup( + slivers: [ + SliverList.builder( + itemCount: state.loadedData!.length, + itemBuilder: (context, index) { + final id = state.loadedData![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + id: id, + loadingType: ItemType.story, + forceShowMetadata: false, + forceShowJobs: state.storyType == StoryType.jobStories, + style: ItemStyle.overview, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), + ), + ); + }, + ), + if (state.loadedData!.length < state.data!.length) + SliverPadding( + padding: AppSpacing.defaultTilePadding, + sliver: SliverToBoxAdapter( + child: OutlinedButton.icon( + onPressed: _storiesCubit.showMore, + style: OutlinedButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), + icon: const Icon(Icons.expand_more_outlined), + label: Text(context.l10n.showMore), ), ), - ], - ), - onRetry: () async => _storiesCubit.load(), + ), + ], ), + onRetry: () async => _storiesCubit.load(), ), ); } diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index 1d15b075..6631bb53 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -32,60 +32,60 @@ class SliverStoriesSearchBody extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( bloc: _storiesSearchBloc, - builder: (context, state) => BlocBuilder( - bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.showStoryMetadata != current.showStoryMetadata || - previous.useActionButtons != current.useActionButtons, - builder: (context, settingsState) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( - itemBuilder: (context, index) => ItemLoadingTile( + builder: (context, state) => state.whenOrDefaultSlivers( + loading: () => SliverList.builder( + itemBuilder: (context, index) => + BlocBuilder( + bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.useLargeStoryStyle != current.useLargeStoryStyle || + previous.showStoryMetadata != current.showStoryMetadata, + builder: (context, settingsState) => ItemLoadingTile( type: ItemType.story, useLargeStoryStyle: settingsState.useLargeStoryStyle, showMetadata: settingsState.showStoryMetadata, style: ItemStyle.overview, ), ), - nonEmpty: () => SliverMainAxisGroup( - slivers: [ - SliverList.builder( - itemCount: state.loadedData!.length, - itemBuilder: (context, index) { - final id = state.loadedData![index]; - return ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - id: id, - loadingType: ItemType.story, - showMetadata: settingsState.showStoryMetadata, - style: ItemStyle.overview, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), - ); - }, - ), - if (state.loadedData!.length < state.data!.length) - SliverPadding( - padding: AppSpacing.defaultTilePadding, - sliver: SliverToBoxAdapter( - child: OutlinedButton.icon( - onPressed: _storiesSearchBloc.showMore, - style: OutlinedButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - icon: const Icon(Icons.expand_more_outlined), - label: Text(context.l10n.showMore), + ), + nonEmpty: () => SliverMainAxisGroup( + slivers: [ + SliverList.builder( + itemCount: state.loadedData!.length, + itemBuilder: (context, index) { + final id = state.loadedData![index]; + return ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + id: id, + loadingType: ItemType.story, + forceShowMetadata: false, + style: ItemStyle.overview, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': id}), + ), + ); + }, + ), + if (state.loadedData!.length < state.data!.length) + SliverPadding( + padding: AppSpacing.defaultTilePadding, + sliver: SliverToBoxAdapter( + child: OutlinedButton.icon( + onPressed: _storiesSearchBloc.showMore, + style: OutlinedButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), + icon: const Icon(Icons.expand_more_outlined), + label: Text(context.l10n.showMore), ), ), - ], - ), - onRetry: () async => - _storiesSearchBloc.add(const LoadStoriesSearchEvent()), + ), + ], ), + onRetry: () async => + _storiesSearchBloc.add(const LoadStoriesSearchEvent()), ), ); } From 9a6d57d80911560a4b11b660e13875ff2d8237fe Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 22:28:38 +0100 Subject: [PATCH 050/145] Remove unused code --- lib/app/models/modal_bottom_sheet_page.dart | 74 --------------------- 1 file changed, 74 deletions(-) delete mode 100644 lib/app/models/modal_bottom_sheet_page.dart diff --git a/lib/app/models/modal_bottom_sheet_page.dart b/lib/app/models/modal_bottom_sheet_page.dart deleted file mode 100644 index 2383f68c..00000000 --- a/lib/app/models/modal_bottom_sheet_page.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; - -const double _defaultScrollControlDisabledMaxHeightRatio = 9.0 / 16.0; - -class ModalBottomSheetPage extends Page { - const ModalBottomSheetPage({ - super.key, - super.name, - super.arguments, - super.restorationId, - required this.builder, - this.capturedThemes, - this.barrierLabel, - this.barrierOnTapHint, - this.backgroundColor, - this.elevation, - this.shape, - this.clipBehavior, - this.constraints, - this.modalBarrierColor, - this.isDismissible = true, - this.enableDrag = true, - this.showDragHandle, - required this.isScrollControlled, - this.scrollControlDisabledMaxHeightRatio = - _defaultScrollControlDisabledMaxHeightRatio, - this.transitionAnimationController, - this.anchorPoint, - this.useSafeArea = false, - }); - - final WidgetBuilder builder; - final CapturedThemes? capturedThemes; - final String? barrierLabel; - final String? barrierOnTapHint; - final Color? backgroundColor; - final double? elevation; - final ShapeBorder? shape; - final Clip? clipBehavior; - final BoxConstraints? constraints; - final Color? modalBarrierColor; - final bool isDismissible; - final bool enableDrag; - final bool? showDragHandle; - final bool isScrollControlled; - final double scrollControlDisabledMaxHeightRatio; - final AnimationController? transitionAnimationController; - final Offset? anchorPoint; - final bool useSafeArea; - - @override - Route createRoute(BuildContext context) => ModalBottomSheetRoute( - builder: builder, - capturedThemes: capturedThemes, - barrierLabel: barrierLabel, - barrierOnTapHint: barrierOnTapHint, - backgroundColor: backgroundColor, - elevation: elevation, - shape: shape, - clipBehavior: clipBehavior, - constraints: constraints, - modalBarrierColor: modalBarrierColor, - isDismissible: isDismissible, - enableDrag: enableDrag, - showDragHandle: showDragHandle, - isScrollControlled: isScrollControlled, - scrollControlDisabledMaxHeightRatio: - scrollControlDisabledMaxHeightRatio, - settings: this, - transitionAnimationController: transitionAnimationController, - anchorPoint: anchorPoint, - useSafeArea: useSafeArea, - ); -} From 246ebc65776a9bdf292f3d14626d233a49a55f9f Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 23:09:49 +0100 Subject: [PATCH 051/145] Specify page builder for every route --- lib/app/router/app_router.dart | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index 8b913720..c9273dfd 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -165,14 +165,16 @@ class AppRouter { ), GoRoute( path: AppRoute.item.path, - builder: (context, state) => ItemPage( - appContainer.itemCubitFactory, - appContainer.itemTreeCubitFactory, - appContainer.storySimilarCubitFactory, - appContainer.storyItemSearchCubitFactory, - appContainer.authCubit, - appContainer.settingsCubit, - id: int.parse(state.uri.queryParameters['id']!), + pageBuilder: (context, state) => MaterialPage( + child: ItemPage( + appContainer.itemCubitFactory, + appContainer.itemTreeCubitFactory, + appContainer.storySimilarCubitFactory, + appContainer.storyItemSearchCubitFactory, + appContainer.authCubit, + appContainer.settingsCubit, + id: int.parse(state.uri.queryParameters['id']!), + ), ), parentNavigatorKey: rootNavigatorKey, routes: [ @@ -218,13 +220,15 @@ class AppRouter { ), GoRoute( path: AppRoute.user.path, - builder: (context, state) => UserPage( - appContainer.userCubitFactory, - appContainer.itemCubitFactory, - appContainer.userItemSearchBlocFactory, - appContainer.authCubit, - appContainer.settingsCubit, - username: state.uri.queryParameters['id']!, + pageBuilder: (context, state) => MaterialPage( + child: UserPage( + appContainer.userCubitFactory, + appContainer.itemCubitFactory, + appContainer.userItemSearchBlocFactory, + appContainer.authCubit, + appContainer.settingsCubit, + username: state.uri.queryParameters['id']!, + ), ), parentNavigatorKey: rootNavigatorKey, routes: [ From 989be94557529dfbe00db5b5ebc90dfe683a9b86 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 24 Nov 2023 23:12:03 +0100 Subject: [PATCH 052/145] Respect disable animations accessibility setting --- lib/common/constants/app_animation.dart | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/common/constants/app_animation.dart b/lib/common/constants/app_animation.dart index be6d8a09..44adf21f 100644 --- a/lib/common/constants/app_animation.dart +++ b/lib/common/constants/app_animation.dart @@ -3,14 +3,17 @@ import 'package:flutter/material.dart'; typedef Animation = ({Duration duration, Curve easing}); abstract final class AppAnimation { - static const Animation emphasized = ( - duration: Durations.long2, - // Emphasized easing is not yet defined in `Easing`. - easing: Curves.easeInOutCubicEmphasized, - ); + static Animation get emphasized => ( + duration: disableAnimations ? Duration.zero : Durations.long2, + // Emphasized easing is not yet defined in `Easing`. + easing: Curves.easeInOutCubicEmphasized, + ); - static const Animation standard = ( - duration: Durations.medium2, - easing: Easing.standard, - ); + static Animation get standard => ( + duration: disableAnimations ? Duration.zero : Durations.medium2, + easing: Easing.standard, + ); + + static bool get disableAnimations => + WidgetsBinding.instance.disableAnimations; } From c5e469787ec4699a43860b1986c093244119d360 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 26 Nov 2023 21:43:02 +0100 Subject: [PATCH 053/145] Extract search anchors into widgets --- lib/item/view/item_page.dart | 137 +++++++++++++-------- lib/stories/view/stories_shell_page.dart | 145 +++++++++++++--------- lib/user/view/user_page.dart | 149 +++++++++++++---------- 3 files changed, 261 insertions(+), 170 deletions(-) diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index 772e0066..be6ffe79 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -221,18 +221,11 @@ class _SliverItemAppBar extends StatefulWidget { } class _SliverItemAppBarState extends State<_SliverItemAppBar> { - late final SearchController _searchController; late final ValueNotifier _hasOverlapNotifier; RenderSliver? bodyRenderSliver; @override void initState() { - _searchController = SearchController() - ..text = widget._storyItemSearchBloc.state.searchText ?? '' - ..addListener( - () async => widget._storyItemSearchBloc - .add(SetTextStoryItemSearchEvent(_searchController.text)), - ); WidgetsBinding.instance .addPostFrameCallback((timeStamp) => _updateBodyRenderSliver()); widget.scrollController?.addListener(_scrollListener); @@ -275,7 +268,6 @@ class _SliverItemAppBarState extends State<_SliverItemAppBar> { @override void dispose() { widget.scrollController?.removeListener(_scrollListener); - _searchController.dispose(); _hasOverlapNotifier.dispose(); super.dispose(); } @@ -343,47 +335,11 @@ class _SliverItemAppBarState extends State<_SliverItemAppBar> { ), actions: [ if (state.data?.parentId == null) - SearchAnchor( - searchController: _searchController, - builder: (context, controller) => IconButton( - onPressed: () async { - controller.openView(); - widget._storyItemSearchBloc - .add(const LoadStoryItemSearchEvent()); - }, - tooltip: context.l10n.search, - icon: const Icon(Icons.search_outlined), - ), - viewLeading: IconButton( - onPressed: context.pop, - style: IconButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - icon: const BackButtonIcon(), - ), - viewTrailing: [ - BlocSelector( - bloc: widget._storyItemSearchBloc, - selector: (state) => state.status, - builder: (context, searchStatus) => AnimatedOpacity( - opacity: searchStatus == Status.loading ? 1 : 0, - duration: AppAnimation.standard.duration, - curve: AppAnimation.standard.easing, - child: const CircularProgressIndicator.adaptive(), - ), - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: _searchController.clear, - ), - ], - viewBuilder: (suggestions) => StoryItemSearchView( - widget._storyItemSearchBloc, - widget._itemCubitFactory, - widget._authCubit, - widget._settingsCubit, - ), - suggestionsBuilder: (context, controller) => [], + _ItemSearchAnchor( + widget._storyItemSearchBloc, + widget._itemCubitFactory, + widget._authCubit, + widget._settingsCubit, ), _ItemOverflowMenu( widget._itemCubit, @@ -398,6 +354,89 @@ class _SliverItemAppBarState extends State<_SliverItemAppBar> { } } +class _ItemSearchAnchor extends StatefulWidget { + const _ItemSearchAnchor( + this._storyItemSearchBloc, + this._itemCubitFactory, + this._authCubit, + this._settingsCubit, + ); + + final StoryItemSearchBloc _storyItemSearchBloc; + final ItemCubitFactory _itemCubitFactory; + final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; + + @override + State<_ItemSearchAnchor> createState() => _ItemSearchAnchorState(); +} + +class _ItemSearchAnchorState extends State<_ItemSearchAnchor> { + late final SearchController _searchController; + + @override + void initState() { + _searchController = SearchController() + ..text = widget._storyItemSearchBloc.state.searchText ?? '' + ..addListener( + () async => widget._storyItemSearchBloc + .add(SetTextStoryItemSearchEvent(_searchController.text)), + ); + super.initState(); + } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SearchAnchor( + searchController: _searchController, + builder: (context, controller) => IconButton( + onPressed: () async { + controller.openView(); + widget._storyItemSearchBloc.add(const LoadStoryItemSearchEvent()); + }, + tooltip: context.l10n.search, + icon: const Icon(Icons.search_outlined), + ), + viewLeading: IconButton( + onPressed: context.pop, + style: IconButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + icon: const BackButtonIcon(), + ), + viewTrailing: [ + BlocSelector( + bloc: widget._storyItemSearchBloc, + selector: (state) => state.status, + builder: (context, searchStatus) => AnimatedOpacity( + opacity: searchStatus == Status.loading ? 1 : 0, + duration: AppAnimation.standard.duration, + curve: AppAnimation.standard.easing, + child: const CircularProgressIndicator.adaptive(), + ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: _searchController.clear, + ), + ], + viewBuilder: (suggestions) => StoryItemSearchView( + widget._storyItemSearchBloc, + widget._itemCubitFactory, + widget._authCubit, + widget._settingsCubit, + ), + suggestionsBuilder: (context, controller) => [], + ); + } +} + class _ItemOverflowMenu extends StatelessWidget { const _ItemOverflowMenu( this._itemCubit, diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index 7f741215..e6fab8c9 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -106,70 +106,17 @@ class _SliverStoriesAppBar extends StatefulWidget { } class _SliverStoriesAppBarState extends State<_SliverStoriesAppBar> { - late final SearchController _searchController; - - @override - void initState() { - _searchController = SearchController() - ..text = widget._storiesSearchBloc.state.searchText ?? '' - ..addListener( - () async => widget._storiesSearchBloc - .add(SetTextStoriesSearchEvent(_searchController.text)), - ); - super.initState(); - } - - @override - void dispose() { - _searchController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return SliverAppBar( title: Text(context.l10n.stories), flexibleSpace: AppBarProgressIndicator(widget._storiesCubit), actions: [ - SearchAnchor( - searchController: _searchController, - builder: (context, controller) => IconButton( - onPressed: () { - controller.openView(); - widget._storiesSearchBloc.add(const LoadStoriesSearchEvent()); - }, - tooltip: context.l10n.search, - icon: const Icon(Icons.search_outlined), - ), - viewLeading: IconButton( - onPressed: context.pop, - style: IconButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - icon: const BackButtonIcon(), - ), - viewTrailing: [ - BlocBuilder( - bloc: widget._storiesSearchBloc, - builder: (context, state) => AnimatedOpacity( - opacity: state.status == Status.loading ? 1 : 0, - duration: AppAnimation.standard.duration, - curve: AppAnimation.standard.easing, - child: const CircularProgressIndicator.adaptive(), - ), - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: _searchController.clear, - ), - ], - viewBuilder: (suggestions) => StoriesSearchView( - widget._storiesSearchBloc, - widget._itemCubitFactory, - widget._authCubit, - widget._settingsCubit, - ), - suggestionsBuilder: (context, controller) => [], + _StoriesSearchAnchor( + widget._storiesSearchBloc, + widget._itemCubitFactory, + widget._authCubit, + widget._settingsCubit, ), BlocBuilder( bloc: widget._authCubit, @@ -200,6 +147,88 @@ class _SliverStoriesAppBarState extends State<_SliverStoriesAppBar> { } } +class _StoriesSearchAnchor extends StatefulWidget { + const _StoriesSearchAnchor( + this._storiesSearchBloc, + this._itemCubitFactory, + this._authCubit, + this._settingsCubit, + ); + + final StoriesSearchBloc _storiesSearchBloc; + final ItemCubitFactory _itemCubitFactory; + final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; + + @override + State<_StoriesSearchAnchor> createState() => _StoriesSearchAnchorState(); +} + +class _StoriesSearchAnchorState extends State<_StoriesSearchAnchor> { + late final SearchController _searchController; + + @override + void initState() { + _searchController = SearchController() + ..text = widget._storiesSearchBloc.state.searchText ?? '' + ..addListener( + () async => widget._storiesSearchBloc + .add(SetTextStoriesSearchEvent(_searchController.text)), + ); + super.initState(); + } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SearchAnchor( + searchController: _searchController, + builder: (context, controller) => IconButton( + onPressed: () async { + controller.openView(); + widget._storiesSearchBloc.add(const LoadStoriesSearchEvent()); + }, + tooltip: context.l10n.search, + icon: const Icon(Icons.search_outlined), + ), + viewLeading: IconButton( + onPressed: context.pop, + style: IconButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + icon: const BackButtonIcon(), + ), + viewTrailing: [ + BlocBuilder( + bloc: widget._storiesSearchBloc, + builder: (context, state) => AnimatedOpacity( + opacity: state.status == Status.loading ? 1 : 0, + duration: AppAnimation.standard.duration, + curve: AppAnimation.standard.easing, + child: const CircularProgressIndicator.adaptive(), + ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: _searchController.clear, + ), + ], + viewBuilder: (suggestions) => StoriesSearchView( + widget._storiesSearchBloc, + widget._itemCubitFactory, + widget._authCubit, + widget._settingsCubit, + ), + suggestionsBuilder: (context, controller) => [], + ); + } +} + class _SliverStoriesBody extends StatelessWidget { const _SliverStoriesBody( this._storiesCubit, diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 57b63288..0a170b19 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -134,7 +134,7 @@ class _UserPageState extends State { } } -class _SliverUserAppBar extends StatefulWidget { +class _SliverUserAppBar extends StatelessWidget { const _SliverUserAppBar( this._userCubit, this._itemCubitFactory, @@ -154,10 +154,60 @@ class _SliverUserAppBar extends StatefulWidget { final ScrollController scrollController; @override - State<_SliverUserAppBar> createState() => _SliverUserAppBarState(); + Widget build(BuildContext context) { + return SliverAppBar( + title: Text(username), + flexibleSpace: AppBarProgressIndicator(_userCubit), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(_toolbarHeight), + child: UserTile( + _userCubit, + _authCubit, + _settingsCubit, + style: UserStyle.primary, + onTap: (context, user) async => scrollController.animateTo( + 0, + duration: AppAnimation.emphasized.duration, + curve: AppAnimation.emphasized.easing, + ), + ), + ), + actions: [ + _UserSearchAnchor( + _userItemSearchBloc, + _itemCubitFactory, + _authCubit, + _settingsCubit, + ), + _UserOverflowMenu( + _userCubit, + _authCubit, + _settingsCubit, + ), + ], + floating: true, + ); + } } -class _SliverUserAppBarState extends State<_SliverUserAppBar> { +class _UserSearchAnchor extends StatefulWidget { + const _UserSearchAnchor( + this._userItemSearchBloc, + this._itemCubitFactory, + this._authCubit, + this._settingsCubit, + ); + + final UserItemSearchBloc _userItemSearchBloc; + final ItemCubitFactory _itemCubitFactory; + final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; + + @override + State<_UserSearchAnchor> createState() => _UserSearchAnchorState(); +} + +class _UserSearchAnchorState extends State<_UserSearchAnchor> { late final SearchController _searchController; @override @@ -179,72 +229,45 @@ class _SliverUserAppBarState extends State<_SliverUserAppBar> { @override Widget build(BuildContext context) { - return SliverAppBar( - title: Text(widget.username), - flexibleSpace: AppBarProgressIndicator(widget._userCubit), - bottom: PreferredSize( - preferredSize: const Size.fromHeight(_toolbarHeight), - child: UserTile( - widget._userCubit, - widget._authCubit, - widget._settingsCubit, - style: UserStyle.primary, - onTap: (context, user) async => widget.scrollController.animateTo( - 0, - duration: AppAnimation.emphasized.duration, - curve: AppAnimation.emphasized.easing, - ), + return SearchAnchor( + searchController: _searchController, + builder: (context, controller) => IconButton( + onPressed: () async { + controller.openView(); + widget._userItemSearchBloc.add(const LoadUserItemSearchEvent()); + }, + tooltip: context.l10n.search, + icon: const Icon(Icons.search_outlined), + ), + viewLeading: IconButton( + onPressed: context.pop, + style: IconButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), + icon: const BackButtonIcon(), ), - actions: [ - SearchAnchor( - searchController: _searchController, - builder: (context, controller) => IconButton( - onPressed: () async { - controller.openView(); - widget._userItemSearchBloc.add(const LoadUserItemSearchEvent()); - }, - tooltip: context.l10n.search, - icon: const Icon(Icons.search_outlined), + viewTrailing: [ + BlocBuilder( + bloc: widget._userItemSearchBloc, + builder: (context, state) => AnimatedOpacity( + opacity: state.status == Status.loading ? 1 : 0, + duration: AppAnimation.standard.duration, + curve: AppAnimation.standard.easing, + child: const CircularProgressIndicator.adaptive(), ), - viewLeading: IconButton( - onPressed: context.pop, - style: IconButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - icon: const BackButtonIcon(), - ), - viewTrailing: [ - BlocSelector( - bloc: widget._userItemSearchBloc, - selector: (state) => state.status, - builder: (context, searchStatus) => AnimatedOpacity( - opacity: searchStatus == Status.loading ? 1 : 0, - duration: AppAnimation.standard.duration, - curve: AppAnimation.standard.easing, - child: const CircularProgressIndicator.adaptive(), - ), - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: _searchController.clear, - ), - ], - viewBuilder: (suggestions) => UserItemSearchView( - widget._userItemSearchBloc, - widget._itemCubitFactory, - widget._authCubit, - widget._settingsCubit, - ), - suggestionsBuilder: (context, controller) => [], ), - _UserOverflowMenu( - widget._userCubit, - widget._authCubit, - widget._settingsCubit, + IconButton( + icon: const Icon(Icons.close), + onPressed: _searchController.clear, ), ], - floating: true, + viewBuilder: (suggestions) => UserItemSearchView( + widget._userItemSearchBloc, + widget._itemCubitFactory, + widget._authCubit, + widget._settingsCubit, + ), + suggestionsBuilder: (context, controller) => [], ); } } From 66a3a13edda1ea8446393dbcb0ed00407c95c70b Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 27 Nov 2023 20:21:39 +0100 Subject: [PATCH 054/145] Eliminate duplicate string --- lib/l10n/arb/app_en.arb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 4e18459d..d98a7820 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -91,7 +91,6 @@ "block": "Block", "unblock": "Unblock", "username": "Username", - "about": "About", "userLink": "User link", "autofillTitle": "Autofill title", "emptyError": "This field cannot be empty.", From bde8aaddc40a93d02d42aa8ebdc90e21cf1b5b59 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 27 Nov 2023 22:46:11 +0100 Subject: [PATCH 055/145] Upgrade dependencies --- ios/Podfile.lock | 14 ++-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 +- macos/Podfile.lock | 10 +-- packages/glider_data/pubspec.yaml | 2 +- packages/glider_domain/pubspec.yaml | 2 +- pubspec.lock | 64 +++++++++++++++---- pubspec.yaml | 8 +-- 7 files changed, 71 insertions(+), 31 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2e296480..6c3a4662 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,11 +2,11 @@ PODS: - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) - - flutter_inappwebview (0.0.1): + - flutter_inappwebview_ios (0.0.1): - Flutter - - flutter_inappwebview/Core (= 0.0.1) + - flutter_inappwebview_ios/Core (= 0.0.1) - OrderedSet (~> 5.0) - - flutter_inappwebview/Core (0.0.1): + - flutter_inappwebview_ios/Core (0.0.1): - Flutter - OrderedSet (~> 5.0) - flutter_secure_storage (6.0.0): @@ -28,7 +28,7 @@ PODS: DEPENDENCIES: - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) + - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) @@ -45,8 +45,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter - flutter_inappwebview: - :path: ".symlinks/plugins/flutter_inappwebview/ios" + flutter_inappwebview_ios: + :path: ".symlinks/plugins/flutter_inappwebview_ios/ios" flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" package_info_plus: @@ -63,7 +63,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - flutter_inappwebview: 166ed136c90e43c69b451f6d825da379be66c847 + flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 6a7d85b6..ffe7b05f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,7 +7,7 @@ import Foundation import device_info_plus import dynamic_color -import flutter_inappwebview +import flutter_inappwebview_macos import flutter_secure_storage_macos import package_info_plus import path_provider_foundation diff --git a/macos/Podfile.lock b/macos/Podfile.lock index ffbf3781..c0517ab9 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,7 +3,7 @@ PODS: - FlutterMacOS - dynamic_color (0.0.2): - FlutterMacOS - - flutter_inappwebview (0.0.1): + - flutter_inappwebview_macos (0.0.1): - FlutterMacOS - OrderedSet (~> 5.0) - flutter_secure_storage_macos (6.1.1): @@ -26,7 +26,7 @@ PODS: DEPENDENCIES: - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) - - flutter_inappwebview (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview/macos`) + - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) @@ -44,8 +44,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos dynamic_color: :path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos - flutter_inappwebview: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview/macos + flutter_inappwebview_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos flutter_secure_storage_macos: :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: @@ -64,7 +64,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f - flutter_inappwebview: 62e949df616a9f6e1b0366326381f208c7fcad37 + flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index 474a736b..1ee7f158 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: compute: ^1.0.2 flutter_secure_storage: ^9.0.0 html: ^0.15.4 - http: ^1.1.0 + http: ^1.1.2 shared_preferences: ^2.2.1 dev_dependencies: diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index 16e734f0..be964f53 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: path: ../glider_data html: ^0.15.4 material_color_utilities: any - package_info_plus: ^4.2.0 + package_info_plus: ^5.0.1 pub_semver: ^2.1.4 rxdart: ^0.27.7 diff --git a/pubspec.lock b/pubspec.lock index 1923ee81..eb1f52dd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c" + sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" url: "https://pub.dev" source: hosted - version: "0.3.3+6" + version: "0.3.3+7" crypto: dependency: transitive description: @@ -330,10 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: "838edeb770c5e6de8f178dbe6821bc31acf081fe9754210a03399b350261a491" + sha256: "2349c52ea4c425dcd2cf80d82464d14fe6589a6f2d92c7f865df4b3f74e36525" url: "https://pub.dev" source: hosted - version: "6.0.0-beta.28" + version: "6.0.0-beta.29" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "11919ca2f2e2b438a6b6d8ef365cf67050bac0e87712fa937c570b18d72ad855" + url: "https://pub.dev" + source: hosted + version: "1.0.1" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -342,6 +350,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "3a7fe9e6a41879ef3538cd1a834d7e5f23673af6cb8eea5e28829fb1a5154085" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: "4b074e2efbcdccd202f2928c4afde5c4ad242f3112b9655735b80c4e735bc180" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cd789c378c8270e7b5ee52bcc9364001bdaebd843571e1952635ebcf4e65bb9f + url: "https://pub.dev" + source: hosted + version: "1.0.1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: b1757cfafc110e1a874f6a4193b41a0c90ec630ee0a5d288a82d46290c507d71 + url: "https://pub.dev" + source: hosted + version: "1.0.0" flutter_launcher_icons: dependency: "direct dev" description: @@ -511,10 +551,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -679,10 +719,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -871,10 +911,10 @@ packages: dependency: "direct main" description: name: scrollview_observer - sha256: f3b22470b4e0060566054b360eb2c04b4162b5990060cf5b2987a423a7e2f00f + sha256: "72b806ea64d446efffac1000c0e0c0b03b06ce019b0fcc7d24d3b1f86e93fb0d" url: "https://pub.dev" source: hosted - version: "1.18.1" + version: "1.18.2" share_plus: dependency: "direct main" description: @@ -1100,10 +1140,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" url_launcher_windows: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b9f5e75e..c39432ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.28 + flutter_inappwebview: ^6.0.0-beta.29 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 @@ -31,17 +31,17 @@ dependencies: path: packages/glider_domain go_router: ^12.1.1 google_fonts: ^6.1.0 - http: ^1.1.0 + http: ^1.1.2 hydrated_bloc: ^9.1.2 intl: any markdown: ^7.1.1 material_color_utilities: any - package_info_plus: ^4.2.0 + package_info_plus: ^5.0.1 path_provider: ^2.1.1 pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 - scrollview_observer: ^1.18.1 + scrollview_observer: ^1.18.2 share_plus: ^7.2.1 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 From 0a906a6bb667c2bcac908296b91f745a862483c5 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 27 Nov 2023 23:13:41 +0100 Subject: [PATCH 056/145] Fix edit/delete calls using the wrong hmac value --- .../lib/src/hacker_news_website_service.dart | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/glider_data/lib/src/hacker_news_website_service.dart b/packages/glider_data/lib/src/hacker_news_website_service.dart index fd217e90..d177bbb8 100644 --- a/packages/glider_data/lib/src/hacker_news_website_service.dart +++ b/packages/glider_data/lib/src/hacker_news_website_service.dart @@ -144,7 +144,7 @@ class HackerNewsWebsiteService { required bool upvote, required String userCookie, }) async { - final auth = await _getItemAuthValue(id: id, userCookie: userCookie); + final auth = await _getAuthValue(id: id, userCookie: userCookie); final endpoint = Uri.https(authority, 'vote'); final body = { 'id': id.toString(), @@ -159,7 +159,7 @@ class HackerNewsWebsiteService { required bool downvote, required String userCookie, }) async { - final auth = await _getItemAuthValue(id: id, userCookie: userCookie); + final auth = await _getAuthValue(id: id, userCookie: userCookie); final endpoint = Uri.https(authority, 'vote'); final body = { 'id': id.toString(), @@ -174,7 +174,7 @@ class HackerNewsWebsiteService { required bool favorite, required String userCookie, }) async { - final auth = await _getItemAuthValue(id: id, userCookie: userCookie); + final auth = await _getAuthValue(id: id, userCookie: userCookie); final endpoint = Uri.https(authority, 'fave'); final body = { 'id': id.toString(), @@ -189,7 +189,7 @@ class HackerNewsWebsiteService { required bool flag, required String userCookie, }) async { - final auth = await _getItemAuthValue(id: id, userCookie: userCookie); + final auth = await _getAuthValue(id: id, userCookie: userCookie); final endpoint = Uri.https(authority, 'flag'); final body = { 'id': id.toString(), @@ -206,7 +206,11 @@ class HackerNewsWebsiteService { // ignore: always_put_required_named_parameters_first required String userCookie, }) async { - final hmac = await _getItemHmacValue(id: id, userCookie: userCookie); + final hmac = await _getHmacValue( + path: 'edit', + id: id, + userCookie: userCookie, + ); final endpoint = Uri.https(authority, 'xedit'); final body = { 'id': id.toString(), @@ -221,7 +225,11 @@ class HackerNewsWebsiteService { required int id, required String userCookie, }) async { - final hmac = await _getItemHmacValue(id: id, userCookie: userCookie); + final hmac = await _getHmacValue( + path: 'delete-confirm', + id: id, + userCookie: userCookie, + ); final endpoint = Uri.https(authority, 'xdelete'); final body = { 'id': id.toString(), @@ -236,7 +244,7 @@ class HackerNewsWebsiteService { required String text, required String userCookie, }) async { - final hmac = await _getItemHmacValue(id: parentId, userCookie: userCookie); + final hmac = await _getHmacValue(id: parentId, userCookie: userCookie); final endpoint = Uri.https(authority, 'comment'); final body = { 'parent': parentId.toString(), @@ -266,11 +274,16 @@ class HackerNewsWebsiteService { await _performPost(endpoint, body: body, userCookie: userCookie); } - Future _getItemAuthValue({ + Future _getAuthValue({ + String path = 'item', required int id, required String userCookie, }) async { - final endpoint = _getItemUrl(id); + final endpoint = Uri.https( + authority, + path, + {'id': id.toString()}, + ); final response = await _performGet(endpoint, userCookie: userCookie); final voteHref = await compute( (body) => @@ -286,11 +299,16 @@ class HackerNewsWebsiteService { return voteUrl.queryParameters['auth']; } - Future _getItemHmacValue({ + Future _getHmacValue({ + String path = 'item', required int id, required String userCookie, }) async { - final endpoint = _getItemUrl(id); + final endpoint = Uri.https( + authority, + path, + {'id': id.toString()}, + ); final response = await _performGet(endpoint, userCookie: userCookie); return compute( (body) => html_parser @@ -318,9 +336,6 @@ class HackerNewsWebsiteService { ); } - Uri _getItemUrl(int id) => - Uri.https(authority, 'item', {'id': id.toString()}); - Future _performGet( Uri endpoint, { String? userCookie, From 1cc208463dad2b207d50479c1cbb65a477b808fa Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 27 Nov 2023 23:25:08 +0100 Subject: [PATCH 057/145] Bump version --- fastlane/metadata/android/en-US/changelogs/46.txt | 5 +++++ pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/46.txt diff --git a/fastlane/metadata/android/en-US/changelogs/46.txt b/fastlane/metadata/android/en-US/changelogs/46.txt new file mode 100644 index 00000000..ce1dd558 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/46.txt @@ -0,0 +1,5 @@ +- Added setting to enable custom tabs +- Fixed only first opened deeplink loading correctly +- Fixed edit/delete actions not properly authenticating and thus failing +- Fixed comments page sometimes staying in loading state when error occurs +- Improved search and catch up page load speed \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index c39432ed..8abd8753 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: glider description: An opinionated Hacker News client. -version: 2.4.0+45 +version: 2.5.0+46 publish_to: none environment: From 871782c965d0d43591b9f228b0dcf4e2303ec312 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:45:01 +0100 Subject: [PATCH 058/145] Bump flutter_inappwebview from 6.0.0-beta.29 to 6.0.0-beta.30 (#146) Bumps [flutter_inappwebview](https://github.com/pichillilorenzo/flutter_inappwebview) from 6.0.0-beta.29 to 6.0.0-beta.30. - [Release notes](https://github.com/pichillilorenzo/flutter_inappwebview/releases) - [Commits](https://github.com/pichillilorenzo/flutter_inappwebview/compare/v6.0.0-beta.29...v6.0.0-beta.30) --- updated-dependencies: - dependency-name: flutter_inappwebview dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 64 ++++++++++++++++------------------------------------ pubspec.yaml | 2 +- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index eb1f52dd..16c1459b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "65.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.2.0" analyzer_plugin: dependency: transitive description: @@ -330,18 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: "2349c52ea4c425dcd2cf80d82464d14fe6589a6f2d92c7f865df4b3f74e36525" + sha256: bd62be6aef0662a4bb55a10fd4dc0a5f0070ad92e2c4879f903a355468ca7643 url: "https://pub.dev" source: hosted - version: "6.0.0-beta.29" + version: "6.0.0-beta.30" flutter_inappwebview_android: dependency: transitive description: name: flutter_inappwebview_android - sha256: "11919ca2f2e2b438a6b6d8ef365cf67050bac0e87712fa937c570b18d72ad855" + sha256: c5dbd2ad4b53d13e18085118eef389b4916937c38beded1cfd314675334600a9 url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -354,34 +354,34 @@ packages: dependency: transitive description: name: flutter_inappwebview_ios - sha256: "3a7fe9e6a41879ef3538cd1a834d7e5f23673af6cb8eea5e28829fb1a5154085" + sha256: "78bbe8e773e0ad0f3ba11f29dd4102451934fc22ec2d70726a810072bfab59ce" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.2" flutter_inappwebview_macos: dependency: transitive description: name: flutter_inappwebview_macos - sha256: "4b074e2efbcdccd202f2928c4afde5c4ad242f3112b9655735b80c4e735bc180" + sha256: "22c851a4f4647fc51906566feae8019252c3d198179bf9f1190db6ea158fe98b" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" flutter_inappwebview_platform_interface: dependency: transitive description: name: flutter_inappwebview_platform_interface - sha256: cd789c378c8270e7b5ee52bcc9364001bdaebd843571e1952635ebcf4e65bb9f + sha256: "14e7a7fb25fde02e753ed8fd5276f3b8a49f6e7ee2867c20d02ceef9e3091542" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" flutter_inappwebview_web: dependency: transitive description: name: flutter_inappwebview_web - sha256: b1757cfafc110e1a874f6a4193b41a0c90ec630ee0a5d288a82d46290c507d71 + sha256: e276e5417c286d5ba66b833151edb3ec6706fa46b0d1cee04cdfa046595ea797 url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" flutter_launcher_icons: dependency: "direct dev" description: @@ -611,22 +611,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" - url: "https://pub.dev" - source: hosted - version: "9.0.13" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff - url: "https://pub.dev" - source: hosted - version: "1.0.5" leancode_lint: dependency: "direct dev" description: @@ -663,10 +647,10 @@ packages: dependency: "direct main" description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" melos: dependency: "direct dev" description: @@ -679,10 +663,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" mime: dependency: transitive description: @@ -1192,14 +1176,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8abd8753..b5e4d6b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.29 + flutter_inappwebview: ^6.0.0-beta.30 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 From be2f70efed73b1d334a3aa93e9fbea92715e2d03 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 28 Nov 2023 19:26:31 +0100 Subject: [PATCH 059/145] Animate tiles between loading and success state --- lib/common/widgets/preview_card.dart | 8 +- lib/item/widgets/item_data_tile.dart | 22 ++-- lib/item/widgets/item_tile.dart | 161 ++++++++++++++------------- lib/user/widgets/user_data_tile.dart | 19 ++-- lib/user/widgets/user_tile.dart | 52 +++++---- 5 files changed, 137 insertions(+), 125 deletions(-) diff --git a/lib/common/widgets/preview_card.dart b/lib/common/widgets/preview_card.dart index f62aaaa1..4e048ef6 100644 --- a/lib/common/widgets/preview_card.dart +++ b/lib/common/widgets/preview_card.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; import 'package:glider/common/widgets/decorated_card.dart'; @@ -32,7 +33,12 @@ class PreviewCard extends StatelessWidget { ].spaced(width: AppSpacing.l), ), ), - child, + AnimatedSize( + alignment: Alignment.topCenter, + duration: AppAnimation.emphasized.duration, + curve: AppAnimation.emphasized.easing, + child: child, + ), ], ), ); diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index d42eeadb..081bd3e5 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:glider/app/models/app_route.dart'; -import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/extensions/uri_extension.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; @@ -121,19 +120,14 @@ class ItemDataTile extends StatelessWidget { padding: padding, child: Opacity( opacity: visited ? 2 / 3 : 1, - child: AnimatedSize( - alignment: Alignment.topCenter, - duration: AppAnimation.emphasized.duration, - curve: AppAnimation.emphasized.easing, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (hasPrimary) _buildPrimary(context), - if (hasSecondary && collapsedCount == null) - _buildSecondary(context), - ].spaced(height: AppSpacing.m), - ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (hasPrimary) _buildPrimary(context), + if (hasSecondary && collapsedCount == null) + _buildSecondary(context), + ].spaced(height: AppSpacing.m), ), ), ), diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 9d0ec159..1883eea1 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; import 'package:glider/app/router/app_router.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; +import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/models/status.dart'; @@ -107,85 +108,95 @@ class _ItemTileState extends State builder: (context, authState) => BlocBuilder( bloc: widget._settingsCubit, - builder: (context, settingsState) => state.whenOrDefaultWidgets( - loading: () => ItemLoadingTile( - type: widget.loadingType, - collapsedCount: widget.collapsedCount, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - showMetadata: - settingsState.showStoryMetadata || widget.forceShowMetadata, - style: widget.style, - padding: widget.padding, - ), - success: () { - final item = state.data!; + builder: (context, settingsState) => AnimatedSize( + alignment: Alignment.topCenter, + duration: AppAnimation.emphasized.duration, + curve: AppAnimation.emphasized.easing, + child: state.whenOrDefaultWidgets( + loading: () => ItemLoadingTile( + type: widget.loadingType, + collapsedCount: widget.collapsedCount, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showMetadata: settingsState.showStoryMetadata || + widget.forceShowMetadata, + style: widget.style, + padding: widget.padding, + ), + success: () { + final item = state.data!; - if (item.type == ItemType.job && - !(settingsState.showJobs || widget.forceShowJobs)) { - return const SizedBox.shrink(); - } + if (item.type == ItemType.job && + !(settingsState.showJobs || widget.forceShowJobs)) { + return const SizedBox.shrink(); + } - return Material( - type: widget.highlight - ? MaterialType.canvas - : MaterialType.transparency, - elevation: 4, - shadowColor: Colors.transparent, - surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, - child: ItemDataTile( - item, - parsedText: state.parsedText, - visited: state.visited && widget.showVisited, - vote: state.vote, - favorited: state.favorited, - flagged: state.flagged, - blocked: state.blocked, - failed: state.status == Status.failure, - collapsedCount: widget.collapsedCount, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - showFavicons: settingsState.showFavicons, - showMetadata: settingsState.showStoryMetadata || - widget.forceShowMetadata, - showUserAvatars: settingsState.showUserAvatars, - style: widget.style, - usernameStyle: authState.username == item.username - ? UsernameStyle.loggedInUser - : widget.storyUsername == item.username - ? UsernameStyle.storyUser - : UsernameStyle.none, - padding: widget.padding, - useInAppBrowser: settingsState.useInAppBrowser, - onTap: item.type == ItemType.pollopt - ? ItemAction.upvote - .isVisible(state, authState, settingsState) - ? (context, item) async => ItemAction.upvote - .execute(context, _itemCubit, widget._authCubit) - : null - : widget.onTap, - onLongPress: (context, item) async => showModalBottomSheet( - context: rootNavigatorKey.currentContext!, - builder: (context) => ItemBottomSheet( - _itemCubit, - widget._authCubit, - widget._settingsCubit, + return Material( + type: widget.highlight + ? MaterialType.canvas + : MaterialType.transparency, + elevation: 4, + shadowColor: Colors.transparent, + surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, + child: ItemDataTile( + item, + parsedText: state.parsedText, + visited: state.visited && widget.showVisited, + vote: state.vote, + favorited: state.favorited, + flagged: state.flagged, + blocked: state.blocked, + failed: state.status == Status.failure, + collapsedCount: widget.collapsedCount, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + showFavicons: settingsState.showFavicons, + showMetadata: settingsState.showStoryMetadata || + widget.forceShowMetadata, + showUserAvatars: settingsState.showUserAvatars, + style: widget.style, + usernameStyle: authState.username == item.username + ? UsernameStyle.loggedInUser + : widget.storyUsername == item.username + ? UsernameStyle.storyUser + : UsernameStyle.none, + padding: widget.padding, + useInAppBrowser: settingsState.useInAppBrowser, + onTap: item.type == ItemType.pollopt + ? ItemAction.upvote + .isVisible(state, authState, settingsState) + ? (context, item) async => + ItemAction.upvote.execute( + context, + _itemCubit, + widget._authCubit, + ) + : null + : widget.onTap, + onLongPress: (context, item) async => + showModalBottomSheet( + context: rootNavigatorKey.currentContext!, + builder: (context) => ItemBottomSheet( + _itemCubit, + widget._authCubit, + widget._settingsCubit, + ), ), + onTapUpvote: settingsState.useActionButtons && + ItemAction.upvote + .isVisible(state, authState, settingsState) + ? () async => ItemAction.upvote + .execute(context, _itemCubit, widget._authCubit) + : null, + onTapFavorite: settingsState.useActionButtons && + ItemAction.favorite + .isVisible(state, authState, settingsState) + ? () async => ItemAction.favorite + .execute(context, _itemCubit, widget._authCubit) + : null, ), - onTapUpvote: settingsState.useActionButtons && - ItemAction.upvote - .isVisible(state, authState, settingsState) - ? () async => ItemAction.upvote - .execute(context, _itemCubit, widget._authCubit) - : null, - onTapFavorite: settingsState.useActionButtons && - ItemAction.favorite - .isVisible(state, authState, settingsState) - ? () async => ItemAction.favorite - .execute(context, _itemCubit, widget._authCubit) - : null, - ), - ); - }, - onRetry: () async => _itemCubit.load(), + ); + }, + onRetry: () async => _itemCubit.load(), + ), ), ), ), diff --git a/lib/user/widgets/user_data_tile.dart b/lib/user/widgets/user_data_tile.dart index bb89f8fc..67a68322 100644 --- a/lib/user/widgets/user_data_tile.dart +++ b/lib/user/widgets/user_data_tile.dart @@ -47,18 +47,13 @@ class UserDataTile extends StatelessWidget { onLongPress != null ? () => onLongPress!(context, user) : null, child: Padding( padding: padding, - child: AnimatedSize( - alignment: Alignment.topCenter, - duration: AppAnimation.emphasized.duration, - curve: AppAnimation.emphasized.easing, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (hasPrimary) _buildPrimary(context), - if (hasSecondary) _buildSecondary(context), - ].spaced(height: AppSpacing.m), - ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (hasPrimary) _buildPrimary(context), + if (hasSecondary) _buildSecondary(context), + ].spaced(height: AppSpacing.m), ), ), ); diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index c54f3811..e2ad23d6 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/router/app_router.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; +import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; @@ -46,32 +47,37 @@ class UserTile extends StatelessWidget { bloc: _userCubit, builder: (context, state) => BlocBuilder( bloc: _settingsCubit, - builder: (context, settingsState) => state.whenOrDefaultWidgets( - loading: () => UserLoadingTile( - style: style, - padding: padding, - ), - success: () { - final user = state.data!; - return UserDataTile( - user, - parsedAbout: state.parsedAbout, - blocked: state.blocked, + builder: (context, settingsState) => AnimatedSize( + alignment: Alignment.topCenter, + duration: AppAnimation.emphasized.duration, + curve: AppAnimation.emphasized.easing, + child: state.whenOrDefaultWidgets( + loading: () => UserLoadingTile( style: style, padding: padding, - useInAppBrowser: settingsState.useInAppBrowser, - onTap: onTap, - onLongPress: (context, item) async => showModalBottomSheet( - context: rootNavigatorKey.currentContext!, - builder: (context) => UserBottomSheet( - _userCubit, - _authCubit, - _settingsCubit, + ), + success: () { + final user = state.data!; + return UserDataTile( + user, + parsedAbout: state.parsedAbout, + blocked: state.blocked, + style: style, + padding: padding, + useInAppBrowser: settingsState.useInAppBrowser, + onTap: onTap, + onLongPress: (context, item) async => showModalBottomSheet( + context: rootNavigatorKey.currentContext!, + builder: (context) => UserBottomSheet( + _userCubit, + _authCubit, + _settingsCubit, + ), ), - ), - ); - }, - onRetry: () async => _userCubit.load(), + ); + }, + onRetry: () async => _userCubit.load(), + ), ), ), ), From 664b976217f8830885a7ec6562780f71da422393 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 28 Nov 2023 20:52:41 +0100 Subject: [PATCH 060/145] Upgrade dependencies again --- pubspec.lock | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 16c1459b..112fe906 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "65.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" analyzer_plugin: dependency: transitive description: @@ -611,6 +611,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" + url: "https://pub.dev" + source: hosted + version: "9.0.13" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" leancode_lint: dependency: "direct dev" description: @@ -647,10 +663,10 @@ packages: dependency: "direct main" description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" melos: dependency: "direct dev" description: @@ -663,10 +679,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -1176,6 +1192,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" win32: dependency: transitive description: From cb89793a444527a4b544a3ce8678c7214f72839c Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 28 Nov 2023 20:53:03 +0100 Subject: [PATCH 061/145] Lower README screenshot width to fit width --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 802c197a..59d2868f 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ Glider is an opinionated Hacker News client. Ad-free, open-source, no-nonsense. - Sensible defaults

- - - - - + + + + +

[play store]: https://play.google.com/store/apps/details?id=nl.viter.glider From 90d0a2c61ab7b6f186c9a4975f564ad935e8d940 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 28 Nov 2023 21:29:47 +0100 Subject: [PATCH 062/145] Prevent unwrap --- lib/app/router/app_router.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index c9273dfd..ffc64617 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -144,7 +144,7 @@ class AppRouter { path: AppRoute.themeColorDialog.path, pageBuilder: (context, state) => DialogPage( builder: (context) => ThemeColorDialog( - selectedColor: state.extra! as Color?, + selectedColor: state.extra as Color?, ), ), parentNavigatorKey: rootNavigatorKey, From eeb7fdfb7ac7428aa26badfdd43a7d7edbaa21b2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 29 Nov 2023 21:50:42 +0100 Subject: [PATCH 063/145] Simplify item tree logic somewhat --- lib/item_tree/cubit/item_tree_cubit.dart | 13 +++---------- lib/item_tree/cubit/item_tree_state.dart | 7 +------ packages/glider_domain/lib/src/item_repository.dart | 6 +++--- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/lib/item_tree/cubit/item_tree_cubit.dart b/lib/item_tree/cubit/item_tree_cubit.dart index 341d3a50..8fd70477 100644 --- a/lib/item_tree/cubit/item_tree_cubit.dart +++ b/lib/item_tree/cubit/item_tree_cubit.dart @@ -24,13 +24,9 @@ class ItemTreeCubit extends HydratedCubit { Future load() async { safeEmit( - state.copyWith( - status: () => Status.loading, - visited: () => state.status != Status.initial, - ), + state.copyWith(status: () => Status.loading), ); - final descendantsStream = - _itemRepository.getFlatItemDescendantsStream(itemId); + final descendantsStream = _itemRepository.getItemDescendantsStream(itemId); if (state.data == null || state.data!.isEmpty) { descendantsStream.listen( @@ -49,11 +45,8 @@ class ItemTreeCubit extends HydratedCubit { ), ), onDone: () => safeEmit( - state.copyWith( - status: () => Status.success, - ), + state.copyWith(status: () => Status.success), ), - cancelOnError: true, ); } else { try { diff --git a/lib/item_tree/cubit/item_tree_state.dart b/lib/item_tree/cubit/item_tree_state.dart index 51f64360..088aff9a 100644 --- a/lib/item_tree/cubit/item_tree_state.dart +++ b/lib/item_tree/cubit/item_tree_state.dart @@ -6,7 +6,6 @@ class ItemTreeState with DataMixin>, EquatableMixin { this.data, this.previousData, this.collapsedIds = const {}, - this.visited = false, this.exception, }); @@ -37,7 +36,6 @@ class ItemTreeState with DataMixin>, EquatableMixin { final List? data; final List? previousData; final Set collapsedIds; - final bool visited; @override final Object? exception; @@ -46,7 +44,7 @@ class ItemTreeState with DataMixin>, EquatableMixin { .toList(growable: false); late int newDescendantsCount = data != null && previousData != null - ? data!.toSet().difference(previousData!.toSet()).length + ? {...?data}.difference({...?previousData}).length : 0; ItemTreeState copyWith({ @@ -54,7 +52,6 @@ class ItemTreeState with DataMixin>, EquatableMixin { List? Function()? data, List? Function()? previousData, Set Function()? collapsedIds, - bool Function()? visited, Object? Function()? exception, }) => ItemTreeState( @@ -62,7 +59,6 @@ class ItemTreeState with DataMixin>, EquatableMixin { data: data != null ? data() : this.data, previousData: previousData != null ? previousData() : this.previousData, collapsedIds: collapsedIds != null ? collapsedIds() : this.collapsedIds, - visited: visited != null ? visited() : this.visited, exception: exception != null ? exception() : this.exception, ); @@ -72,7 +68,6 @@ class ItemTreeState with DataMixin>, EquatableMixin { data, previousData, collapsedIds, - visited, exception, ]; } diff --git a/packages/glider_domain/lib/src/item_repository.dart b/packages/glider_domain/lib/src/item_repository.dart index 1ff770f8..0e6dd87e 100644 --- a/packages/glider_domain/lib/src/item_repository.dart +++ b/packages/glider_domain/lib/src/item_repository.dart @@ -139,10 +139,10 @@ class ItemRepository { } } - Stream> getFlatItemDescendantsStream(int id) async* { - var descendants = []; - + Stream> getItemDescendantsStream(int id) async* { try { + var descendants = []; + await for (final item in _getItemTreeStream(id)) { if (item.partIds != null && item.partIds!.isNotEmpty || item.childIds != null && item.childIds!.isNotEmpty) { From 4efc867e1b41534693a663c2f85a41436dac758c Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 30 Nov 2023 22:55:26 +0100 Subject: [PATCH 064/145] Initialize custom tabs setting --- lib/settings/cubit/settings_cubit.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index c17c37b0..e872718d 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -44,6 +44,7 @@ class SettingsCubit extends Cubit final useThreadNavigation = await _settingsRepository.getUseThreadNavigation(); final enableDownvoting = await _settingsRepository.getEnableDownvoting(); + final useInAppBrowser = await _settingsRepository.getUseInAppBrowser(); safeEmit( state.copyWith( themeMode: themeMode != null ? () => themeMode : null, @@ -65,6 +66,7 @@ class SettingsCubit extends Cubit useThreadNavigation != null ? () => useThreadNavigation : null, enableDownvoting: enableDownvoting != null ? () => enableDownvoting : null, + useInAppBrowser: useInAppBrowser != null ? () => useInAppBrowser : null, appVersion: _packageRepository.getVersion, ), ); From 69640138c54a291d2ba2e233e76c1a9bfdbf12b3 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 30 Nov 2023 22:56:01 +0100 Subject: [PATCH 065/145] Move padding up in interaction pages --- lib/reply/view/reply_page.dart | 25 ++++++++++--------------- lib/submit/view/submit_page.dart | 23 ++++++++++------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index da6f4faa..1b9b3a31 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -116,25 +116,20 @@ class _ReplyBody extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( bloc: _replyCubit, - builder: (context, state) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - child: _ReplyForm(_replyCubit), - ), - if (state.parentItem?.text != null) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: AppSpacing.xl, - ), - child: ElevatedButton.icon( + builder: (context, state) => Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _ReplyForm(_replyCubit), + if (state.parentItem?.text != null) + ElevatedButton.icon( onPressed: _replyCubit.quoteParent, icon: const Icon(Icons.format_quote), label: Text(context.l10n.quoteParent), ), - ), - ].spaced(height: AppSpacing.xl), + ].spaced(height: AppSpacing.xl), + ), ), ); } diff --git a/lib/submit/view/submit_page.dart b/lib/submit/view/submit_page.dart index bc29a667..aa960c15 100644 --- a/lib/submit/view/submit_page.dart +++ b/lib/submit/view/submit_page.dart @@ -101,23 +101,20 @@ class _SubmitBody extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( bloc: _submitCubit, - builder: (context, state) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - child: _SubmitForm(_submitCubit), - ), - if (state.url.hasHost) - Padding( - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), - child: ElevatedButton.icon( + builder: (context, state) => Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _SubmitForm(_submitCubit), + if (state.url.hasHost) + ElevatedButton.icon( onPressed: _submitCubit.autofillTitle, icon: const Icon(Icons.title_outlined), label: Text(context.l10n.autofillTitle), ), - ), - ].spaced(height: AppSpacing.xl), + ].spaced(height: AppSpacing.xl), + ), ), ); } From be5868534ebb498680edce9ef8453a51265f2d3c Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 30 Nov 2023 23:53:33 +0100 Subject: [PATCH 066/145] Specify filled input decoration in theme --- lib/app/view/app.dart | 1 + lib/edit/view/edit_page.dart | 3 --- lib/reply/view/reply_page.dart | 1 - lib/submit/view/submit_page.dart | 3 --- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index e839b94d..4e8bca3e 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -106,6 +106,7 @@ class App extends StatelessWidget { ), ), ), + inputDecorationTheme: const InputDecorationTheme(filled: true), ); } } diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index 9692f4e8..b278cffe 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -192,7 +192,6 @@ class _EditFormState extends State<_EditForm> { decoration: InputDecoration( labelText: context.l10n.title, errorText: state.title?.displayError?.label(context), - filled: true, ), textCapitalization: TextCapitalization.words, maxLength: TitleInput.maxLength, @@ -209,7 +208,6 @@ class _EditFormState extends State<_EditForm> { controller: _urlController, decoration: InputDecoration( labelText: context.l10n.link, - filled: true, ), enabled: false, ), @@ -223,7 +221,6 @@ class _EditFormState extends State<_EditForm> { decoration: InputDecoration( labelText: context.l10n.text, errorText: state.text?.displayError?.label(context), - filled: true, ), keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 1b9b3a31..3d2ac78c 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -180,7 +180,6 @@ class _ReplyFormState extends State<_ReplyForm> { decoration: InputDecoration( labelText: context.l10n.text, errorText: state.text.displayError?.label(context), - filled: true, ), keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, diff --git a/lib/submit/view/submit_page.dart b/lib/submit/view/submit_page.dart index aa960c15..a124f628 100644 --- a/lib/submit/view/submit_page.dart +++ b/lib/submit/view/submit_page.dart @@ -189,7 +189,6 @@ class _SubmitFormState extends State<_SubmitForm> { decoration: InputDecoration( labelText: context.l10n.title, errorText: state.title.displayError?.label(context), - filled: true, ), textCapitalization: TextCapitalization.words, maxLength: TitleInput.maxLength, @@ -207,7 +206,6 @@ class _SubmitFormState extends State<_SubmitForm> { labelText: context.l10n.link, errorText: state.url.displayError ?.label(context, otherField: context.l10n.text), - filled: true, ), keyboardType: TextInputType.url, onChanged: widget._submitCubit.setUrl, @@ -223,7 +221,6 @@ class _SubmitFormState extends State<_SubmitForm> { labelText: context.l10n.text, errorText: state.text.displayError ?.label(context, otherField: context.l10n.link), - filled: true, ), keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, From 25382f54832a1b4f91249852d036bfd04ff05321 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 16:16:48 +0100 Subject: [PATCH 067/145] Add filters setting --- lib/app/extensions/string_extension.dart | 4 + lib/app/models/app_route.dart | 1 + lib/app/router/app_router.dart | 10 + lib/item/widgets/item_data_tile.dart | 21 ++- lib/item/widgets/item_tile.dart | 11 ++ lib/l10n/arb/app_en.arb | 6 + lib/settings/cubit/settings_cubit.dart | 26 +++ lib/settings/cubit/settings_state.dart | 11 ++ lib/settings/view/filters_dialog.dart | 175 ++++++++++++++++++ lib/settings/view/settings_page.dart | 7 + .../lib/src/shared_preferences_service.dart | 30 +++ .../lib/src/settings_repository.dart | 18 ++ 12 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 lib/app/extensions/string_extension.dart create mode 100644 lib/settings/view/filters_dialog.dart diff --git a/lib/app/extensions/string_extension.dart b/lib/app/extensions/string_extension.dart new file mode 100644 index 00000000..2b252106 --- /dev/null +++ b/lib/app/extensions/string_extension.dart @@ -0,0 +1,4 @@ +extension StringExtension on String { + bool caseInsensitiveContains(String other) => + toLowerCase().contains(other.toLowerCase()); +} diff --git a/lib/app/models/app_route.dart b/lib/app/models/app_route.dart index e22ae9d2..cbba5bf5 100644 --- a/lib/app/models/app_route.dart +++ b/lib/app/models/app_route.dart @@ -7,6 +7,7 @@ enum AppRoute { auth, settings, themeColorDialog(parent: settings), + filtersDialog(parent: settings), submit, item, edit(parent: item), diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index ffc64617..8de42a3e 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -13,6 +13,7 @@ import 'package:glider/item/view/item_page.dart'; import 'package:glider/item/widgets/item_value_dialog.dart'; import 'package:glider/navigation_shell/widgets/navigation_shell_scaffold.dart'; import 'package:glider/reply/view/reply_page.dart'; +import 'package:glider/settings/view/filters_dialog.dart'; import 'package:glider/settings/view/settings_page.dart'; import 'package:glider/settings/widgets/theme_color_dialog.dart'; import 'package:glider/stories/view/stories_shell_page.dart'; @@ -149,6 +150,15 @@ class AppRouter { ), parentNavigatorKey: rootNavigatorKey, ), + GoRoute( + path: AppRoute.filtersDialog.path, + pageBuilder: (context, state) => DialogPage( + builder: (context) => FiltersDialog( + appContainer.settingsCubit, + ), + ), + parentNavigatorKey: rootNavigatorKey, + ), ], ), GoRoute( diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 081bd3e5..9308bf7a 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -31,6 +31,7 @@ class ItemDataTile extends StatelessWidget { this.favorited = false, this.flagged = false, this.blocked = false, + this.filtered = false, this.failed = false, this.collapsedCount, this.useLargeStoryStyle = true, @@ -54,6 +55,7 @@ class ItemDataTile extends StatelessWidget { final bool favorited; final bool flagged; final bool blocked; + final bool filtered; final bool failed; final int? collapsedCount; final bool useLargeStoryStyle; @@ -103,10 +105,12 @@ class ItemDataTile extends StatelessWidget { final hasPrimary = style.showPrimary && item.dateTime != null && - (showMetadata || (item.title != null || item.url != null) && !blocked); + (showMetadata || + (item.title != null || item.url != null) && !blocked && !filtered); final hasSecondary = style.showSecondary && (item.text != null || item.url != null) && - !blocked; + !blocked && + !filtered; if (!hasPrimary && !hasSecondary) { return const SizedBox.shrink(); @@ -137,7 +141,7 @@ class ItemDataTile extends StatelessWidget { Widget _buildPrimary(BuildContext context) { return Column( children: [ - if ((item.title != null || item.url != null) && !blocked) + if ((item.title != null || item.url != null) && !blocked && !filtered) Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -255,6 +259,17 @@ class ItemDataTile extends StatelessWidget { ), ), ), + Hero( + tag: 'item_tile_filtered_${item.id}', + child: AnimatedVisibility( + visible: filtered, + padding: MetadataWidget.horizontalPadding, + child: const MetadataWidget( + icon: Icons.filter_alt_outlined, + label: Text('[filtered]'), + ), + ), + ), ...[ if (item.isDeleted) Hero( diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 1883eea1..f25cd0d2 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -2,6 +2,7 @@ import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/string_extension.dart'; import 'package:glider/app/router/app_router.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_animation.dart'; @@ -145,6 +146,16 @@ class _ItemTileState extends State favorited: state.favorited, flagged: state.flagged, blocked: state.blocked, + filtered: item.title != null && + settingsState.wordFilters.any( + (word) => + item.title!.caseInsensitiveContains(word), + ) || + item.url != null && + settingsState.domainFilters.any( + (domain) => item.url!.host + .caseInsensitiveContains(domain), + ), failed: state.status == Status.failure, collapsedCount: widget.collapsedCount, useLargeStoryStyle: settingsState.useLargeStoryStyle, diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index d98a7820..e39d63aa 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -38,6 +38,12 @@ "downvotingDescription": "Requires an account with 501+ karma", "inAppBrowser": "Custom tabs", "inAppBrowserDescription": "Opens links in in-app browser if possible", + "filters": "Filters", + "filtersDescription": "Hides stories based on words or domains", + "words": "Words", + "wordsHint": "Foobar", + "domains": "Domains", + "domainsHelp": "example.com", "data": "Data", "exportFavorites": "Export favorites", "exportFavoritesDescription": "Shares favorites as IDs in JSON format", diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index e872718d..f1845516 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -44,6 +44,8 @@ class SettingsCubit extends Cubit final useThreadNavigation = await _settingsRepository.getUseThreadNavigation(); final enableDownvoting = await _settingsRepository.getEnableDownvoting(); + final wordFilters = await _settingsRepository.getWordFilters(); + final domainFilters = await _settingsRepository.getDomainFilters(); final useInAppBrowser = await _settingsRepository.getUseInAppBrowser(); safeEmit( state.copyWith( @@ -67,6 +69,8 @@ class SettingsCubit extends Cubit enableDownvoting: enableDownvoting != null ? () => enableDownvoting : null, useInAppBrowser: useInAppBrowser != null ? () => useInAppBrowser : null, + wordFilters: wordFilters != null ? () => wordFilters : null, + domainFilters: domainFilters != null ? () => domainFilters : null, appVersion: _packageRepository.getVersion, ), ); @@ -239,6 +243,28 @@ class SettingsCubit extends Cubit } } + Future setWordFilter(String value, {required bool filter}) async { + await _settingsRepository.setWordFilter(value: value, filter: filter); + final wordFilters = await _settingsRepository.getWordFilters(); + + if (wordFilters != null) { + safeEmit( + state.copyWith(wordFilters: () => wordFilters), + ); + } + } + + Future setDomainFilter(String value, {required bool filter}) async { + await _settingsRepository.setDomainFilter(value: value, filter: filter); + final domainFilters = await _settingsRepository.getDomainFilters(); + + if (domainFilters != null) { + safeEmit( + state.copyWith(domainFilters: () => domainFilters), + ); + } + } + Future exportFavorites() async { final favorites = await _itemInteractionRepository.favoritedStream.first; diff --git a/lib/settings/cubit/settings_state.dart b/lib/settings/cubit/settings_state.dart index 4be6c275..aeb812da 100644 --- a/lib/settings/cubit/settings_state.dart +++ b/lib/settings/cubit/settings_state.dart @@ -17,6 +17,8 @@ class SettingsState with EquatableMixin { this.useThreadNavigation = true, this.enableDownvoting = false, this.useInAppBrowser = false, + this.wordFilters = const {}, + this.domainFilters = const {}, this.appVersion, }); @@ -35,6 +37,8 @@ class SettingsState with EquatableMixin { final bool useThreadNavigation; final bool enableDownvoting; final bool useInAppBrowser; + final Set wordFilters; + final Set domainFilters; final Version? appVersion; SettingsState copyWith({ @@ -53,6 +57,8 @@ class SettingsState with EquatableMixin { bool Function()? useThreadNavigation, bool Function()? enableDownvoting, bool Function()? useInAppBrowser, + Set Function()? wordFilters, + Set Function()? domainFilters, Version? Function()? appVersion, }) => SettingsState( @@ -86,6 +92,9 @@ class SettingsState with EquatableMixin { : this.enableDownvoting, useInAppBrowser: useInAppBrowser != null ? useInAppBrowser() : this.useInAppBrowser, + wordFilters: wordFilters != null ? wordFilters() : this.wordFilters, + domainFilters: + domainFilters != null ? domainFilters() : this.domainFilters, appVersion: appVersion != null ? appVersion() : this.appVersion, ); @@ -106,6 +115,8 @@ class SettingsState with EquatableMixin { useThreadNavigation, enableDownvoting, useInAppBrowser, + wordFilters, + domainFilters, appVersion, ]; } diff --git a/lib/settings/view/filters_dialog.dart b/lib/settings/view/filters_dialog.dart new file mode 100644 index 00000000..f3b83721 --- /dev/null +++ b/lib/settings/view/filters_dialog.dart @@ -0,0 +1,175 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:glider/common/constants/app_animation.dart'; +import 'package:glider/common/constants/app_spacing.dart'; +import 'package:glider/common/extensions/widget_list_extension.dart'; +import 'package:glider/l10n/extensions/app_localizations_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; +import 'package:go_router/go_router.dart'; + +class FiltersDialog extends StatelessWidget { + const FiltersDialog(this._settingsCubit, {super.key}); + + final SettingsCubit _settingsCubit; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(context.l10n.filters), + content: SingleChildScrollView( + child: _FiltersBody(_settingsCubit), + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text(MaterialLocalizations.of(context).okButtonLabel), + ), + ], + ); + } +} + +class _FiltersBody extends StatefulWidget { + const _FiltersBody(this._settingsCubit); + + final SettingsCubit _settingsCubit; + + @override + State<_FiltersBody> createState() => _FiltersBodyState(); +} + +class _FiltersBodyState extends State<_FiltersBody> { + late final TextEditingController _wordsController; + late final TextEditingController _domainsController; + late final FocusNode _wordsFocusNode; + late final FocusNode _domainsFocusNode; + + @override + void initState() { + _wordsController = TextEditingController(); + _domainsController = TextEditingController(); + _wordsFocusNode = FocusNode(); + _domainsFocusNode = FocusNode(); + super.initState(); + } + + @override + void dispose() { + _wordsController.dispose(); + _domainsController.dispose(); + _wordsFocusNode.dispose(); + _domainsFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + bloc: widget._settingsCubit, + builder: (context, state) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.words, + style: Theme.of(context).textTheme.titleMedium, + ), + Row( + children: [ + Expanded( + child: TextFormField( + controller: _wordsController, + focusNode: _wordsFocusNode, + decoration: InputDecoration(hintText: context.l10n.wordsHint), + keyboardType: TextInputType.text, + onFieldSubmitted: (text) async => _addWordFilter(), + ), + ), + IconButton.filledTonal( + icon: const Icon(Icons.add), + onPressed: _addWordFilter, + ), + ].spaced(width: AppSpacing.m), + ), + AnimatedSize( + alignment: AlignmentDirectional.topStart, + duration: AppAnimation.emphasized.duration, + curve: AppAnimation.emphasized.easing, + child: SizedBox( + width: double.infinity, + child: Wrap( + spacing: AppSpacing.m, + children: [ + for (final word in state.wordFilters) + InputChip( + label: Text(word), + onDeleted: () => widget._settingsCubit + .setWordFilter(word, filter: false), + ), + ], + ), + ), + ), + Text( + context.l10n.domains, + style: Theme.of(context).textTheme.titleMedium, + ), + Row( + children: [ + Expanded( + child: TextFormField( + controller: _domainsController, + focusNode: _domainsFocusNode, + decoration: + InputDecoration(hintText: context.l10n.domainsHelp), + keyboardType: TextInputType.url, + onFieldSubmitted: (text) async => _addDomainFilter(), + ), + ), + IconButton.filledTonal( + icon: const Icon(Icons.add), + onPressed: _addDomainFilter, + ), + ].spaced(width: AppSpacing.m), + ), + AnimatedSize( + alignment: AlignmentDirectional.topStart, + duration: AppAnimation.emphasized.duration, + curve: AppAnimation.emphasized.easing, + child: SizedBox( + width: double.infinity, + child: Wrap( + spacing: AppSpacing.m, + children: [ + for (final domain in state.domainFilters) + InputChip( + label: Text(domain), + onDeleted: () => widget._settingsCubit + .setDomainFilter(domain, filter: false), + ), + ], + ), + ), + ), + ].spaced(height: AppSpacing.l), + ), + ); + } + + Future _addWordFilter() async { + if (_wordsController.text.isNotEmpty) { + await widget._settingsCubit + .setWordFilter(_wordsController.text, filter: true); + _wordsController.clear(); + _wordsFocusNode.requestFocus(); + } + } + + Future _addDomainFilter() async { + if (_domainsController.text.isNotEmpty) { + await widget._settingsCubit + .setDomainFilter(_domainsController.text, filter: true); + _domainsController.clear(); + _domainsFocusNode.requestFocus(); + } + } +} diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 219a01d7..c01fb832 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -285,6 +285,13 @@ class _SettingsBody extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), + ListTile( + title: Text(context.l10n.filters), + subtitle: Text(context.l10n.filtersDescription), + onTap: () async => context.push( + AppRoute.filtersDialog.location(), + ), + ), const Divider(), Padding( padding: AppSpacing.defaultTilePadding, diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index 7076bf95..169e5a28 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -20,6 +20,8 @@ class SharedPreferencesService { static const String _useThreadNavigationKey = 'use_thread_navigation'; static const String _enableDownvotingKey = 'enable_downvoting'; static const String _useInAppBrowserKey = 'use_in_app_browser'; + static const String _wordFiltersKey = 'word_filters'; + static const String _domainFiltersKey = 'domain_filters'; static const String _lastVersionKey = 'last_version'; static const String _visitedKey = 'visited'; static const String _upvotedKey = 'upvoted'; @@ -116,6 +118,34 @@ class SharedPreferencesService { Future setUseInAppBrowser({required bool value}) async => _sharedPreferences.setBool(_useInAppBrowserKey, value); + Future?> getWordFilters() async => + _sharedPreferences.getStringList(_wordFiltersKey); + + Future setWordFilter({ + required String value, + required bool filter, + }) async { + if (filter) { + return _sharedPreferences.addElement(_wordFiltersKey, value); + } else { + return _sharedPreferences.removeElement(_wordFiltersKey, value); + } + } + + Future?> getDomainFilters() async => + _sharedPreferences.getStringList(_domainFiltersKey); + + Future setDomainFilter({ + required String value, + required bool filter, + }) async { + if (filter) { + return _sharedPreferences.addElement(_domainFiltersKey, value); + } else { + return _sharedPreferences.removeElement(_domainFiltersKey, value); + } + } + Future getLastVersion() async => _sharedPreferences.getString(_lastVersionKey); diff --git a/packages/glider_domain/lib/src/settings_repository.dart b/packages/glider_domain/lib/src/settings_repository.dart index 0171f227..8949da6a 100644 --- a/packages/glider_domain/lib/src/settings_repository.dart +++ b/packages/glider_domain/lib/src/settings_repository.dart @@ -102,4 +102,22 @@ class SettingsRepository { Future setUseInAppBrowser({required bool value}) async => _sharedPreferencesService.setUseInAppBrowser(value: value); + + Future?> getWordFilters() async => + (await _sharedPreferencesService.getWordFilters())?.toSet(); + + Future setWordFilter({ + required String value, + required bool filter, + }) async => + _sharedPreferencesService.setWordFilter(value: value, filter: filter); + + Future?> getDomainFilters() async => + (await _sharedPreferencesService.getDomainFilters())?.toSet(); + + Future setDomainFilter({ + required String value, + required bool filter, + }) async => + _sharedPreferencesService.setDomainFilter(value: value, filter: filter); } From 902d04ba0a51af2e0e70f28fc33e10c2e5d07a3a Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 16:17:00 +0100 Subject: [PATCH 068/145] Make theme color changes instant --- lib/app/router/app_router.dart | 6 +- lib/settings/view/settings_page.dart | 10 +-- lib/settings/view/theme_color_dialog.dart | 74 ++++++++++++++++++++ lib/settings/widgets/theme_color_dialog.dart | 61 ---------------- 4 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 lib/settings/view/theme_color_dialog.dart delete mode 100644 lib/settings/widgets/theme_color_dialog.dart diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index 8de42a3e..889e6e20 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -15,7 +15,7 @@ import 'package:glider/navigation_shell/widgets/navigation_shell_scaffold.dart'; import 'package:glider/reply/view/reply_page.dart'; import 'package:glider/settings/view/filters_dialog.dart'; import 'package:glider/settings/view/settings_page.dart'; -import 'package:glider/settings/widgets/theme_color_dialog.dart'; +import 'package:glider/settings/view/theme_color_dialog.dart'; import 'package:glider/stories/view/stories_shell_page.dart'; import 'package:glider/stories_search/view/catch_up_shell_page.dart'; import 'package:glider/submit/view/submit_page.dart'; @@ -143,9 +143,9 @@ class AppRouter { routes: [ GoRoute( path: AppRoute.themeColorDialog.path, - pageBuilder: (context, state) => DialogPage( + pageBuilder: (context, state) => DialogPage( builder: (context) => ThemeColorDialog( - selectedColor: state.extra as Color?, + appContainer.settingsCubit, ), ), parentNavigatorKey: rootNavigatorKey, diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index c01fb832..19001fda 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -125,13 +125,9 @@ class _SettingsBody extends StatelessWidget { size: 40, ), enabled: !state.useDynamicTheme, - onTap: () async { - final value = await context.push( - AppRoute.themeColorDialog.location(), - extra: state.themeColor, - ); - if (value != null) await _settingsCubit.setThemeColor(value); - }, + onTap: () async => context.push( + AppRoute.themeColorDialog.location(), + ), ), MenuListTile( title: Text(context.l10n.themeVariant), diff --git a/lib/settings/view/theme_color_dialog.dart b/lib/settings/view/theme_color_dialog.dart new file mode 100644 index 00000000..22214c27 --- /dev/null +++ b/lib/settings/view/theme_color_dialog.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:glider/common/constants/app_spacing.dart'; +import 'package:glider/l10n/extensions/app_localizations_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; +import 'package:go_router/go_router.dart'; + +const _totalColors = 20; + +final _colors = [ + for (var i = 0; i < _totalColors; i++) + HSVColor.fromAHSV(1, 360 / _totalColors * i, 0.5, 1).toColor(), +]; + +const _iconSize = 40.0; + +class ThemeColorDialog extends StatelessWidget { + const ThemeColorDialog(this._settingsCubit, {super.key}); + + final SettingsCubit _settingsCubit; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(context.l10n.themeColor), + contentPadding: AppSpacing.defaultTilePadding, + content: SizedBox( + width: 0, + child: _ThemeColorBody(_settingsCubit), + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text(MaterialLocalizations.of(context).okButtonLabel), + ), + ], + ); + } +} + +class _ThemeColorBody extends StatelessWidget { + const _ThemeColorBody(this._settingsCubit); + + final SettingsCubit _settingsCubit; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + bloc: _settingsCubit, + builder: (context, state) => GridView.extent( + maxCrossAxisExtent: 64, + shrinkWrap: true, + children: [ + for (final color in _colors) + IconButton( + icon: Icon( + Icons.circle_outlined, + color: color, + size: _iconSize, + ), + selectedIcon: Icon( + Icons.circle, + color: color, + size: _iconSize, + ), + isSelected: color == state.themeColor, + padding: const EdgeInsets.all(AppSpacing.m), + onPressed: () => _settingsCubit.setThemeColor(color), + ), + ], + ), + ); + } +} diff --git a/lib/settings/widgets/theme_color_dialog.dart b/lib/settings/widgets/theme_color_dialog.dart deleted file mode 100644 index 25f18469..00000000 --- a/lib/settings/widgets/theme_color_dialog.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:glider/common/constants/app_spacing.dart'; -import 'package:glider/l10n/extensions/app_localizations_extension.dart'; -import 'package:go_router/go_router.dart'; - -const _totalColors = 20; - -final _colors = [ - for (var i = 0; i < _totalColors; i++) - HSVColor.fromAHSV(1, 360 / _totalColors * i, 0.5, 1).toColor(), -]; - -const _iconSize = 40.0; - -class ThemeColorDialog extends StatelessWidget { - const ThemeColorDialog({ - this.selectedColor, - super.key, - }); - - final Color? selectedColor; - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(context.l10n.themeColor), - contentPadding: AppSpacing.defaultTilePadding, - content: SizedBox( - width: 0, - child: GridView.extent( - maxCrossAxisExtent: 64, - shrinkWrap: true, - children: [ - for (final color in _colors) - IconButton( - icon: Icon( - Icons.circle_outlined, - color: color, - size: _iconSize, - ), - selectedIcon: Icon( - Icons.circle, - color: color, - size: _iconSize, - ), - isSelected: color == selectedColor, - padding: const EdgeInsets.all(AppSpacing.m), - onPressed: () => context.pop(color), - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () => context.pop(), - child: Text(MaterialLocalizations.of(context).cancelButtonLabel), - ), - ], - ); - } -} From 60c88bd63c73687031b76c8d36606a50033f6b45 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 16:38:52 +0100 Subject: [PATCH 069/145] Add filters in lowercase --- lib/l10n/arb/app_en.arb | 2 +- lib/settings/view/filters_dialog.dart | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index e39d63aa..88122ea1 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -41,7 +41,7 @@ "filters": "Filters", "filtersDescription": "Hides stories based on words or domains", "words": "Words", - "wordsHint": "Foobar", + "wordsHint": "foobar", "domains": "Domains", "domainsHelp": "example.com", "data": "Data", diff --git a/lib/settings/view/filters_dialog.dart b/lib/settings/view/filters_dialog.dart index f3b83721..c844e098 100644 --- a/lib/settings/view/filters_dialog.dart +++ b/lib/settings/view/filters_dialog.dart @@ -157,8 +157,10 @@ class _FiltersBodyState extends State<_FiltersBody> { Future _addWordFilter() async { if (_wordsController.text.isNotEmpty) { - await widget._settingsCubit - .setWordFilter(_wordsController.text, filter: true); + await widget._settingsCubit.setWordFilter( + _wordsController.text.toLowerCase(), + filter: true, + ); _wordsController.clear(); _wordsFocusNode.requestFocus(); } @@ -166,8 +168,10 @@ class _FiltersBodyState extends State<_FiltersBody> { Future _addDomainFilter() async { if (_domainsController.text.isNotEmpty) { - await widget._settingsCubit - .setDomainFilter(_domainsController.text, filter: true); + await widget._settingsCubit.setDomainFilter( + _domainsController.text.toLowerCase(), + filter: true, + ); _domainsController.clear(); _domainsFocusNode.requestFocus(); } From 0b6523f4b82f69acfe8e2f913ba7631bfd1b47a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:20:47 +0100 Subject: [PATCH 070/145] Bump actions/setup-java from 3 to 4 (#147) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b571655..9f7d7379 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: with: fetch-depth: 0 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 10c80c65..dae44eec 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: with: fetch-depth: 0 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 From 46940d95ac9241fc4423720e7d954da5eb0d3328 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 21:39:44 +0100 Subject: [PATCH 071/145] Trim filters before adding --- lib/settings/view/filters_dialog.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/settings/view/filters_dialog.dart b/lib/settings/view/filters_dialog.dart index c844e098..908bf870 100644 --- a/lib/settings/view/filters_dialog.dart +++ b/lib/settings/view/filters_dialog.dart @@ -158,7 +158,7 @@ class _FiltersBodyState extends State<_FiltersBody> { Future _addWordFilter() async { if (_wordsController.text.isNotEmpty) { await widget._settingsCubit.setWordFilter( - _wordsController.text.toLowerCase(), + _wordsController.text.trim().toLowerCase(), filter: true, ); _wordsController.clear(); @@ -169,7 +169,7 @@ class _FiltersBodyState extends State<_FiltersBody> { Future _addDomainFilter() async { if (_domainsController.text.isNotEmpty) { await widget._settingsCubit.setDomainFilter( - _domainsController.text.toLowerCase(), + _domainsController.text.trim().toLowerCase(), filter: true, ); _domainsController.clear(); From f70efa76061737c5275cff19bdc4dad40aec73da Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 21:53:48 +0100 Subject: [PATCH 072/145] Fix auth page platform logic and eliminate post-frame callback --- lib/auth/view/auth_page.dart | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index 43c9f7d8..f5a2ea79 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -41,22 +41,26 @@ class _AuthPageState extends State { @override void initState() { _browser = _AuthInAppBrowser(widget._authCubit); + unawaited(widget._authCubit.init()); + super.initState(); + } - if (!kIsWeb && defaultTargetPlatform != TargetPlatform.iOS) { - WidgetsBinding.instance.addPostFrameCallback( - (timeStamp) => _browser.addMenuItem( - InAppBrowserMenuItem( - id: 0, - title: MaterialLocalizations.of(context).closeButtonLabel, - showAsAction: true, - onClick: () async => _browser.close(), - ), + @override + void didChangeDependencies() { + _browser.removeAllMenuItem(); + + if (kIsWeb || defaultTargetPlatform != TargetPlatform.iOS) { + _browser.addMenuItem( + InAppBrowserMenuItem( + id: 0, + title: MaterialLocalizations.of(context).closeButtonLabel, + showAsAction: true, + onClick: () async => _browser.close(), ), ); } - unawaited(widget._authCubit.init()); - super.initState(); + super.didChangeDependencies(); } @override From faf0df1073759a120763d099d80f76187e7cfc4a Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 22:25:39 +0100 Subject: [PATCH 073/145] Hide jobs tab if show jobs setting is disabled --- lib/item/widgets/item_tile.dart | 6 +- lib/l10n/arb/app_en.arb | 2 +- lib/stories/view/stories_shell_page.dart | 7 ++- lib/stories/view/stories_type_view.dart | 74 ++++++++++++++++-------- 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index f25cd0d2..d2260855 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -32,7 +32,6 @@ class ItemTile extends StatefulWidget { this.showVisited = true, this.highlight = false, this.forceShowMetadata = true, - this.forceShowJobs = false, this.style = ItemStyle.full, this.padding = AppSpacing.defaultTilePadding, this.onTap, @@ -52,7 +51,6 @@ class ItemTile extends StatefulWidget { this.showVisited = true, this.highlight = false, this.forceShowMetadata = true, - this.forceShowJobs = false, this.style = ItemStyle.full, this.padding = AppSpacing.defaultTilePadding, this.onTap, @@ -71,7 +69,6 @@ class ItemTile extends StatefulWidget { final bool showVisited; final bool highlight; final bool forceShowMetadata; - final bool forceShowJobs; final ItemStyle style; final EdgeInsets padding; final ItemCallback? onTap; @@ -126,8 +123,7 @@ class _ItemTileState extends State success: () { final item = state.data!; - if (item.type == ItemType.job && - !(settingsState.showJobs || widget.forceShowJobs)) { + if (item.type == ItemType.job && !settingsState.showJobs) { return const SizedBox.shrink(); } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 88122ea1..8541d742 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -31,7 +31,7 @@ "userAvatars": "User avatars", "behavior": "Behavior", "showJobs": "Show job stories", - "showJobsDescription": "Always shown on jobs tab", + "showJobsDescription": "Also shows jobs tab", "threadNavigation": "Thread navigation", "threadNavigationDescription": "Allows jumps between top-level comments", "downvoting": "Enable downvoting", diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index e6fab8c9..80486010 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -18,7 +18,6 @@ import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/navigation_shell/models/navigation_shell_action.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider/stories/cubit/stories_cubit.dart'; -import 'package:glider/stories/models/story_type.dart'; import 'package:glider/stories/view/stories_type_view.dart'; import 'package:glider/stories_search/bloc/stories_search_bloc.dart'; import 'package:glider/stories_search/view/stories_search_view.dart'; @@ -66,7 +65,10 @@ class _StoriesShellPageState extends State { widget._settingsCubit, ), SliverToBoxAdapter( - child: StoriesTypeView(widget._storiesCubit), + child: StoriesTypeView( + widget._storiesCubit, + widget._settingsCubit, + ), ), SliverSafeArea( top: false, @@ -275,7 +277,6 @@ class _SliverStoriesBody extends StatelessWidget { id: id, loadingType: ItemType.story, forceShowMetadata: false, - forceShowJobs: state.storyType == StoryType.jobStories, style: ItemStyle.overview, onTap: (context, item) async => context.push( AppRoute.item.location(parameters: {'id': id}), diff --git a/lib/stories/view/stories_type_view.dart b/lib/stories/view/stories_type_view.dart index 6fc626dc..2fb356a9 100644 --- a/lib/stories/view/stories_type_view.dart +++ b/lib/stories/view/stories_type_view.dart @@ -2,38 +2,65 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider/stories/cubit/stories_cubit.dart'; import 'package:glider/stories/models/story_type.dart'; class StoriesTypeView extends StatelessWidget { - const StoriesTypeView(this._storiesCubit, {super.key}); + const StoriesTypeView( + this._storiesCubit, + this._settingsCubit, { + super.key, + }); final StoriesCubit _storiesCubit; + final SettingsCubit _settingsCubit; + + @override + Widget build(BuildContext context) { + final directionality = Directionality.of(context); + final padding = MediaQuery.paddingOf(context); + + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + padding: EdgeInsetsDirectional.only( + top: AppSpacing.s, + bottom: AppSpacing.s, + start: AppSpacing.xl, + end: AppSpacing.xl + + switch (directionality) { + TextDirection.ltr => padding.right, + TextDirection.rtl => padding.left, + }, + ), + child: _StoriesTypeBody( + _storiesCubit, + _settingsCubit, + ), + ); + } +} + +class _StoriesTypeBody extends StatelessWidget { + const _StoriesTypeBody( + this._storiesCubit, + this._settingsCubit, + ); + + final StoriesCubit _storiesCubit; + final SettingsCubit _settingsCubit; @override Widget build(BuildContext context) { return BlocBuilder( bloc: _storiesCubit, buildWhen: (previous, current) => previous.storyType != current.storyType, - builder: (context, state) { - final directionality = Directionality.of(context); - final padding = MediaQuery.paddingOf(context); - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - padding: EdgeInsetsDirectional.only( - top: AppSpacing.s, - bottom: AppSpacing.s, - start: AppSpacing.xl, - end: AppSpacing.xl + - switch (directionality) { - TextDirection.ltr => padding.right, - TextDirection.rtl => padding.left, - }, - ), - child: Row( - children: [ - for (final storyType in StoryType.values) + builder: (context, state) => BlocBuilder( + bloc: _settingsCubit, + builder: (context, settingsState) => Row( + children: [ + for (final storyType in StoryType.values) + if (storyType != StoryType.jobStories || settingsState.showJobs) ChoiceChip( showCheckmark: false, avatar: Icon( @@ -47,10 +74,9 @@ class StoriesTypeView extends StatelessWidget { onSelected: (selected) => _storiesCubit.setStoryType(storyType), ), - ].spaced(width: AppSpacing.m), - ), - ); - }, + ].spaced(width: AppSpacing.m), + ), + ), ); } } From 12b654286e4fc58bb54c1eb98a17759b65539577 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 1 Dec 2023 22:55:52 +0100 Subject: [PATCH 074/145] Stop showing what's new page on fresh installs --- lib/navigation_shell/cubit/navigation_shell_cubit.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/navigation_shell/cubit/navigation_shell_cubit.dart b/lib/navigation_shell/cubit/navigation_shell_cubit.dart index c47abc26..a56b3f1c 100644 --- a/lib/navigation_shell/cubit/navigation_shell_cubit.dart +++ b/lib/navigation_shell/cubit/navigation_shell_cubit.dart @@ -22,7 +22,7 @@ class NavigationShellCubit extends Cubit if (version != lastVersion) { await _packageRepository.setLastVersion(value: version); - if (lastVersion == null || version >= lastVersion.nextMajor) { + if (lastVersion != null && version >= lastVersion.nextMajor) { emitPresentation(ShowWhatsNewEvent()); } } From 36ac47d4c3b0e842b4b54faa00f59bec8e61675b Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 2 Dec 2023 00:03:39 +0100 Subject: [PATCH 075/145] Reformat item tree body a bit --- lib/item_tree/view/sliver_item_tree_body.dart | 79 +++++++++---------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index b06b4572..0077ebbb 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -52,60 +52,53 @@ class SliverItemTreeBody extends StatelessWidget { previous.useActionButtons != current.useActionButtons, builder: (context, settingsState) => state.whenOrDefaultSlivers( loading: () => SliverList.builder( + itemCount: childCount, itemBuilder: (context, index) => const IndentedWidget( depth: 1, child: ItemLoadingTile(type: ItemType.comment), ), - itemCount: childCount, ), nonEmpty: () => SliverList.builder( itemCount: state.viewableData!.length, - itemBuilder: (context, index) => _buildItemTile( - state.viewableData![index], - state, - settingsState, - ), + itemBuilder: (context, index) { + final descendant = state.viewableData![index]; + return IndentedWidget( + depth: descendant.isPart ? 0 : descendant.depth, + child: ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + id: descendant.id, + storyUsername: storyUsername, + loadingType: ItemType.comment, + collapsedCount: state.collapsedIds.contains(descendant.id) + ? state.getDescendants(descendant)?.length + : null, + showVisited: false, + highlight: !(state.previousData + ?.map((e) => e.id) + .contains(descendant.id) ?? + true), + onTap: (context, item) async { + if (!item.isDeleted) { + _itemTreeCubit.toggleCollapsed(item.id); + } + + await Scrollable.ensureVisible( + context, + duration: AppAnimation.standard.duration, + curve: AppAnimation.standard.easing, + alignmentPolicy: + ScrollPositionAlignmentPolicy.keepVisibleAtStart, + ); + }, + ), + ); + }, ), onRetry: () async => _itemTreeCubit.load(), ), ), ); } - - Widget _buildItemTile( - ItemDescendant descendant, - ItemTreeState state, - SettingsState settingsState, - ) { - return IndentedWidget( - depth: descendant.isPart ? 0 : descendant.depth, - child: ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - id: descendant.id, - storyUsername: storyUsername, - loadingType: ItemType.comment, - collapsedCount: state.collapsedIds.contains(descendant.id) - ? state.getDescendants(descendant)?.length - : null, - showVisited: false, - highlight: - !(state.previousData?.map((e) => e.id).contains(descendant.id) ?? - true), - onTap: (context, item) async { - if (!item.isDeleted) { - _itemTreeCubit.toggleCollapsed(item.id); - } - - await Scrollable.ensureVisible( - context, - duration: AppAnimation.standard.duration, - curve: AppAnimation.standard.easing, - alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart, - ); - }, - ), - ); - } } From 07a763eede6548bb5e5ff23fef793e71450c2158 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 2 Dec 2023 20:28:55 +0100 Subject: [PATCH 076/145] Show chevron for bottom sheet action if follow-up options exist --- lib/item/widgets/item_bottom_sheet.dart | 3 +++ lib/user/widgets/user_bottom_sheet.dart | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/item/widgets/item_bottom_sheet.dart b/lib/item/widgets/item_bottom_sheet.dart index d37b0053..375ac4b8 100644 --- a/lib/item/widgets/item_bottom_sheet.dart +++ b/lib/item/widgets/item_bottom_sheet.dart @@ -39,6 +39,9 @@ class ItemBottomSheet extends StatelessWidget { ListTile( leading: Icon(action.icon(state)), title: Text(action.label(context, state)), + trailing: action.options != null + ? const Icon(Icons.chevron_right) + : null, onTap: () async { context.pop(); await action.execute( diff --git a/lib/user/widgets/user_bottom_sheet.dart b/lib/user/widgets/user_bottom_sheet.dart index e54f8c1d..9adf7db9 100644 --- a/lib/user/widgets/user_bottom_sheet.dart +++ b/lib/user/widgets/user_bottom_sheet.dart @@ -36,6 +36,9 @@ class UserBottomSheet extends StatelessWidget { ListTile( leading: Icon(action.icon(state)), title: Text(action.label(context, state)), + trailing: action.options != null + ? const Icon(Icons.chevron_right) + : null, onTap: () async { context.pop(); await action.execute( From b5082e9044b26d8deed63bec7264a08e03584df5 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 3 Dec 2023 19:16:10 +0100 Subject: [PATCH 077/145] Use non-extended navigation rail for large breakpoint --- .../widgets/navigation_shell_scaffold.dart | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 18aeb63f..dd075c3b 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -108,22 +108,22 @@ class _NavigationShellScaffoldState extends State { child: AdaptiveLayout( primaryNavigation: SlotLayout( config: { - Breakpoints.medium: SlotLayout.from( - key: const Key('primaryNavigationMedium'), + Breakpoints.mediumAndUp: SlotLayout.from( + key: const Key('primaryNavigationMediumAndUp'), builder: (context) => _buildPrimaryNavigation( context, destinations, leading: floatingActionButton, ), ), - Breakpoints.large: SlotLayout.from( - key: const Key('primaryNavigationLarge'), - builder: (context) => _buildPrimaryNavigation( - context, - destinations, - leading: floatingActionButton, - extended: true, - ), + }, + ), + bottomNavigation: SlotLayout( + config: { + Breakpoints.small: SlotLayout.from( + key: const Key('bottomNavigationStandard'), + builder: (context) => + _buildBottomNavigation(context, destinations), ), }, ), @@ -138,15 +138,6 @@ class _NavigationShellScaffoldState extends State { ), }, ), - bottomNavigation: SlotLayout( - config: { - Breakpoints.small: SlotLayout.from( - key: const Key('bottomNavigationSmall'), - builder: (context) => - _buildBottomNavigation(context, destinations), - ), - }, - ), ), ); }, @@ -189,8 +180,6 @@ class _NavigationShellScaffoldState extends State { final padding = mediaQuery.padding; final viewPadding = mediaQuery.viewPadding; final isSmallBreakpointActive = Breakpoints.small.isActive(context); - final isMediumAndUpBreakpointActive = - Breakpoints.mediumAndUp.isActive(context); return NotificationListener( onNotification: (notification) { @@ -230,11 +219,11 @@ class _NavigationShellScaffoldState extends State { return MediaQuery( data: mediaQuery.copyWith( padding: padding.copyWith( - left: isMediumAndUpBreakpointActive && + left: !isSmallBreakpointActive && directionality == TextDirection.ltr ? 0 : null, - right: isMediumAndUpBreakpointActive && + right: !isSmallBreakpointActive && directionality == TextDirection.rtl ? 0 : null, From 675c4e01c4fcc86e0286f586e142c99b02a6c624 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 3 Dec 2023 19:16:16 +0100 Subject: [PATCH 078/145] Don't persist search text --- .../bloc/stories_search_state.dart | 6 ------ .../bloc/story_item_search_bloc.dart | 13 +------------ .../bloc/story_item_search_event.dart | 8 ++++---- .../bloc/story_item_search_state.dart | 16 ---------------- .../bloc/user_item_search_bloc.dart | 13 +------------ .../bloc/user_item_search_state.dart | 16 ---------------- 6 files changed, 6 insertions(+), 66 deletions(-) diff --git a/lib/stories_search/bloc/stories_search_state.dart b/lib/stories_search/bloc/stories_search_state.dart index 1388ada1..9b3f8040 100644 --- a/lib/stories_search/bloc/stories_search_state.dart +++ b/lib/stories_search/bloc/stories_search_state.dart @@ -14,12 +14,6 @@ class StoriesSearchState factory StoriesSearchState.fromJson(Map json) => StoriesSearchState( - status: Status.values.byName(json['status'] as String), - data: (json['data'] as List?) - ?.map((e) => e as int) - .toList(growable: false) ?? - const [], - searchText: json['searchText'] as String?, searchRange: json['searchRange'] != null ? SearchRange.values.byName(json['searchRange'] as String) : null, diff --git a/lib/story_item_search/bloc/story_item_search_bloc.dart b/lib/story_item_search/bloc/story_item_search_bloc.dart index 2fceb529..a8f2237e 100644 --- a/lib/story_item_search/bloc/story_item_search_bloc.dart +++ b/lib/story_item_search/bloc/story_item_search_bloc.dart @@ -17,7 +17,7 @@ EventTransformer debounce(Duration duration) => (events, mapper) => events.debounceTime(duration).switchMap(mapper); class StoryItemSearchBloc - extends HydratedBloc { + extends Bloc { StoryItemSearchBloc(this._itemRepository, {required int id}) : itemId = id, super(const StoryItemSearchState()) { @@ -33,9 +33,6 @@ class StoryItemSearchBloc final ItemRepository _itemRepository; final int itemId; - @override - String get id => itemId.toString(); - Future _load() async { safeEmit( state.copyWith(status: () => Status.loading), @@ -69,12 +66,4 @@ class StoryItemSearchBloc ); add(const LoadStoryItemSearchEvent()); } - - @override - StoryItemSearchState? fromJson(Map json) => - StoryItemSearchState.fromJson(json); - - @override - Map? toJson(StoryItemSearchState state) => - state.status == Status.success ? state.toJson() : null; } diff --git a/lib/story_item_search/bloc/story_item_search_event.dart b/lib/story_item_search/bloc/story_item_search_event.dart index 14f9bd68..548732c7 100644 --- a/lib/story_item_search/bloc/story_item_search_event.dart +++ b/lib/story_item_search/bloc/story_item_search_event.dart @@ -1,17 +1,17 @@ part of 'story_item_search_bloc.dart'; -sealed class ItemSearchEvent with EquatableMixin { - const ItemSearchEvent(); +sealed class StoryItemSearchEvent with EquatableMixin { + const StoryItemSearchEvent(); } -final class LoadStoryItemSearchEvent extends ItemSearchEvent { +final class LoadStoryItemSearchEvent extends StoryItemSearchEvent { const LoadStoryItemSearchEvent(); @override List get props => []; } -final class SetTextStoryItemSearchEvent extends ItemSearchEvent { +final class SetTextStoryItemSearchEvent extends StoryItemSearchEvent { const SetTextStoryItemSearchEvent(this.text); final String? text; diff --git a/lib/story_item_search/bloc/story_item_search_state.dart b/lib/story_item_search/bloc/story_item_search_state.dart index eb903264..a31fe7e5 100644 --- a/lib/story_item_search/bloc/story_item_search_state.dart +++ b/lib/story_item_search/bloc/story_item_search_state.dart @@ -8,22 +8,6 @@ class StoryItemSearchState with DataMixin>, EquatableMixin { this.exception, }); - factory StoryItemSearchState.fromJson(Map json) => - StoryItemSearchState( - status: Status.values.byName(json['status'] as String), - data: (json['data'] as List?) - ?.map((e) => e as int) - .toList(growable: false) ?? - const [], - searchText: json['searchText'] as String?, - ); - - Map toJson() => { - 'status': status.name, - 'data': data, - 'searchText': searchText, - }; - @override final Status status; @override diff --git a/lib/user_item_search/bloc/user_item_search_bloc.dart b/lib/user_item_search/bloc/user_item_search_bloc.dart index 3eaf3e98..d4595535 100644 --- a/lib/user_item_search/bloc/user_item_search_bloc.dart +++ b/lib/user_item_search/bloc/user_item_search_bloc.dart @@ -17,7 +17,7 @@ EventTransformer debounce(Duration duration) => (events, mapper) => events.debounceTime(duration).switchMap(mapper); class UserItemSearchBloc - extends HydratedBloc { + extends Bloc { UserItemSearchBloc(this._itemRepository, {required this.username}) : super(const UserItemSearchState()) { on( @@ -32,9 +32,6 @@ class UserItemSearchBloc final ItemRepository _itemRepository; final String username; - @override - String get id => username; - Future _load() async { safeEmit( state.copyWith(status: () => Status.loading), @@ -68,12 +65,4 @@ class UserItemSearchBloc ); add(const LoadUserItemSearchEvent()); } - - @override - UserItemSearchState? fromJson(Map json) => - UserItemSearchState.fromJson(json); - - @override - Map? toJson(UserItemSearchState state) => - state.status == Status.success ? state.toJson() : null; } diff --git a/lib/user_item_search/bloc/user_item_search_state.dart b/lib/user_item_search/bloc/user_item_search_state.dart index cbf07ad4..42cd6a59 100644 --- a/lib/user_item_search/bloc/user_item_search_state.dart +++ b/lib/user_item_search/bloc/user_item_search_state.dart @@ -8,22 +8,6 @@ class UserItemSearchState with DataMixin>, EquatableMixin { this.exception, }); - factory UserItemSearchState.fromJson(Map json) => - UserItemSearchState( - status: Status.values.byName(json['status'] as String), - data: (json['data'] as List?) - ?.map((e) => e as int) - .toList(growable: false) ?? - const [], - searchText: json['searchText'] as String?, - ); - - Map toJson() => { - 'status': status.name, - 'data': data, - 'searchText': searchText, - }; - @override final Status status; @override From 3c91afdbe6689715b520ec286c5a402898f63920 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 3 Dec 2023 20:50:36 +0100 Subject: [PATCH 079/145] Default to past 3 days on catch up page --- lib/stories_search/bloc/stories_search_bloc.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/stories_search/bloc/stories_search_bloc.dart b/lib/stories_search/bloc/stories_search_bloc.dart index 2e5cb74a..d6902887 100644 --- a/lib/stories_search/bloc/stories_search_bloc.dart +++ b/lib/stories_search/bloc/stories_search_bloc.dart @@ -23,7 +23,12 @@ EventTransformer debounce(Duration duration) => class StoriesSearchBloc extends HydratedBloc { StoriesSearchBloc(this._itemRepository, {this.searchType = SearchType.search}) - : super(StoriesSearchState()) { + : super( + StoriesSearchState( + searchRange: + searchType == SearchType.catchUp ? SearchRange.past3Days : null, + ), + ) { on( (event, emit) async => _load(), transformer: debounce(_debounceDuration), From 6dd1a83d7bb3ab5d032157e375fc45d2619fa6da Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 3 Dec 2023 22:16:14 +0100 Subject: [PATCH 080/145] Upgrade dependencies --- packages/glider_data/pubspec.yaml | 4 ++-- packages/glider_domain/pubspec.yaml | 4 ++-- pubspec.lock | 36 ++++++++++++++--------------- pubspec.yaml | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index 1ee7f158..fea308ba 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.2.0-0 <4.0.0" dependencies: collection: any @@ -14,6 +14,6 @@ dependencies: shared_preferences: ^2.2.1 dev_dependencies: - custom_lint: ^0.5.6 + custom_lint: ^0.5.7 dependency_validator: ^3.2.3 leancode_lint: ^7.0.0+1 diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index be964f53..931a268f 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.2.0-0 <4.0.0" dependencies: compute: ^1.0.2 @@ -17,6 +17,6 @@ dependencies: rxdart: ^0.27.7 dev_dependencies: - custom_lint: ^0.5.6 + custom_lint: ^0.5.7 dependency_validator: ^3.2.3 leancode_lint: ^7.0.0+1 diff --git a/pubspec.lock b/pubspec.lock index 112fe906..eba2f43b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e url: "https://pub.dev" source: hosted - version: "0.3.3+7" + version: "0.3.3+8" crypto: dependency: transitive description: @@ -338,10 +338,10 @@ packages: dependency: transitive description: name: flutter_inappwebview_android - sha256: c5dbd2ad4b53d13e18085118eef389b4916937c38beded1cfd314675334600a9 + sha256: e74d15bf6a62ae399427f00c75e4bb44b349a1ad39df6690ec5268f65fb19918 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -354,26 +354,26 @@ packages: dependency: transitive description: name: flutter_inappwebview_ios - sha256: "78bbe8e773e0ad0f3ba11f29dd4102451934fc22ec2d70726a810072bfab59ce" + sha256: "23228676302a6931ab5b149f5d356cddba72eccfdb4b1f0555bd7d63fe3b0037" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" flutter_inappwebview_macos: dependency: transitive description: name: flutter_inappwebview_macos - sha256: "22c851a4f4647fc51906566feae8019252c3d198179bf9f1190db6ea158fe98b" + sha256: "0d0b96e51d172b298e5242794778665ff86b56a5d606ee86aab14fed56245f86" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" flutter_inappwebview_platform_interface: dependency: transitive description: name: flutter_inappwebview_platform_interface - sha256: "14e7a7fb25fde02e753ed8fd5276f3b8a49f6e7ee2867c20d02ceef9e3091542" + sha256: d614f7d4cfee774094e136b9a7c3b3596c971fcaee99706fa6c8e133082a49c7 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" flutter_inappwebview_web: dependency: transitive description: @@ -671,10 +671,10 @@ packages: dependency: "direct dev" description: name: melos - sha256: a45e54b72cc2444b46be9d32a590119b9ba8c4e87117f2743a73ec049542f2d3 + sha256: "28136bf9fcd26ca2ffb104a02eff876efb776e7fff9303e2c8c978f0bd7c5800" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0" meta: dependency: transitive description: @@ -791,10 +791,10 @@ packages: dependency: transitive description: name: petitparser - sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.0.2" platform: dependency: transitive description: @@ -1204,10 +1204,10 @@ packages: dependency: transitive description: name: win32 - sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.1.1" win32_registry: dependency: transitive description: @@ -1228,10 +1228,10 @@ packages: dependency: transitive description: name: xml - sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.4.2" + version: "6.5.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b5e4d6b3..5a97a669 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,7 +52,7 @@ dev_dependencies: dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 leancode_lint: ^7.0.0+1 - melos: ^3.2.0 + melos: ^3.3.0 flutter: uses-material-design: true From b668481a43ad016e6db065aaec621ba5d4e1c0b6 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 3 Dec 2023 22:38:36 +0100 Subject: [PATCH 081/145] Bump version --- fastlane/metadata/android/en-US/changelogs/47.txt | 7 +++++++ pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/47.txt diff --git a/fastlane/metadata/android/en-US/changelogs/47.txt b/fastlane/metadata/android/en-US/changelogs/47.txt new file mode 100644 index 00000000..35f11473 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/47.txt @@ -0,0 +1,7 @@ +- Added setting to configure filters for words and domains +- Added animation to list tiles changing size, most noticably when comments load in +- Changed what's new page to not show on new installs +- Changed show job stories setting to also hide jobs tab when disabled +- Changed navigation rail on large devices or orientations to be more compact +- Removed persistence of search queries across sessions +- Fixed custom tabs setting not persisting \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 5a97a669..831b9aa8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: glider description: An opinionated Hacker News client. -version: 2.5.0+46 +version: 2.6.0+47 publish_to: none environment: From e2f361b5fbb9a85e5c78f0f8772dbac20ff5db3b Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 20:36:47 +0100 Subject: [PATCH 082/145] Clean up unused code --- .../widgets/navigation_shell_scaffold.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index dd075c3b..96804b1f 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -149,20 +149,17 @@ class _NavigationShellScaffoldState extends State { BuildContext context, List destinations, { Widget? leading, - bool extended = false, }) { final padding = MediaQuery.paddingOf(context); final directionality = Directionality.of(context); return AdaptiveScaffold.standardNavigationRail( - extended: extended, - width: (extended ? 160 : 80) + + width: 80 + switch (directionality) { TextDirection.ltr => padding.left, TextDirection.rtl => padding.right, }, - labelType: - extended ? NavigationRailLabelType.none : NavigationRailLabelType.all, + labelType: NavigationRailLabelType.all, groupAlignment: 0, leading: leading, selectedIndex: _currentIndex, From e9eecfb672362b2a462e7b978035b95db56a9430 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 20:37:26 +0100 Subject: [PATCH 083/145] Fix `[a-z]` in `[a-z].com` being recognized as a suffix --- lib/item/extensions/item_extension.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/item/extensions/item_extension.dart b/lib/item/extensions/item_extension.dart index ac58d04a..31d45f27 100644 --- a/lib/item/extensions/item_extension.dart +++ b/lib/item/extensions/item_extension.dart @@ -5,7 +5,7 @@ import 'package:glider_domain/glider_domain.dart'; // These regular expressions are partially based on analysis from // https://vsupalov.com/frequent-hn-title-suffixes-prefixes/. final RegExp _prefixRegExp = RegExp(r'^(\S+\s+(?:HN|YC|PG)|Poll):\s+'); -final RegExp _suffixRegExp = RegExp(r'\s+\[(\S+)\]'); +final RegExp _suffixRegExp = RegExp(r'\s+\[(\S+)\](?=(?:\s|$))'); final RegExp _originalDateRegExp = RegExp(r'\s+\(((?:\S+\s+)?\d{4})\)$'); final RegExp _ycBatchRegExp = RegExp(r'\s+\((YC\s*[SW]\d{2})\)'); From 2f817f65cec1472085bc78a7a328e2e12984ffbe Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 20:41:56 +0100 Subject: [PATCH 084/145] Upgrade Flutter and dependencies --- .fvmrc | 2 +- .vscode/settings.json | 2 +- packages/glider_data/pubspec.yaml | 2 +- packages/glider_domain/pubspec.yaml | 2 +- pubspec.lock | 60 +++++++++++++---------------- pubspec.yaml | 10 ++--- 6 files changed, 35 insertions(+), 43 deletions(-) diff --git a/.fvmrc b/.fvmrc index 05e141dd..6c11074f 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.17.0-0.0.pre" + "flutter": "3.18.0-0.1.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 05c1491c..b80b9406 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.17.0-0.0.pre" + "dart.flutterSdkPath": ".fvm/versions/3.18.0-0.1.pre" } \ No newline at end of file diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index fea308ba..fb70f8e2 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -16,4 +16,4 @@ dependencies: dev_dependencies: custom_lint: ^0.5.7 dependency_validator: ^3.2.3 - leancode_lint: ^7.0.0+1 + leancode_lint: ^8.0.0 diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index 931a268f..90612bc5 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -19,4 +19,4 @@ dependencies: dev_dependencies: custom_lint: ^0.5.7 dependency_validator: ^3.2.3 - leancode_lint: ^7.0.0+1 + leancode_lint: ^8.0.0 diff --git a/pubspec.lock b/pubspec.lock index eba2f43b..bad484fb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -330,18 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: bd62be6aef0662a4bb55a10fd4dc0a5f0070ad92e2c4879f903a355468ca7643 + sha256: b34fb3b0a8232a5adae1501e4beb6997717e4a17664eab528d4a0b8fd596d417 url: "https://pub.dev" source: hosted - version: "6.0.0-beta.30" + version: "6.0.0-beta.31" flutter_inappwebview_android: dependency: transitive description: name: flutter_inappwebview_android - sha256: e74d15bf6a62ae399427f00c75e4bb44b349a1ad39df6690ec5268f65fb19918 + sha256: "297ef285b47529332dda074f3bca6dbe103f9e83534b7aca5f081be45da80610" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.7" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -354,34 +354,34 @@ packages: dependency: transitive description: name: flutter_inappwebview_ios - sha256: "23228676302a6931ab5b149f5d356cddba72eccfdb4b1f0555bd7d63fe3b0037" + sha256: "6b1e3b4f46596700a2393aeb7023379e8a4cd609d2f4d05d358697bcffdcf010" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.8" flutter_inappwebview_macos: dependency: transitive description: name: flutter_inappwebview_macos - sha256: "0d0b96e51d172b298e5242794778665ff86b56a5d606ee86aab14fed56245f86" + sha256: f341d16cf471c072e5424eed748aac90b48a7ff1b4897a254ada189bb551145f url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.6" flutter_inappwebview_platform_interface: dependency: transitive description: name: flutter_inappwebview_platform_interface - sha256: d614f7d4cfee774094e136b9a7c3b3596c971fcaee99706fa6c8e133082a49c7 + sha256: efc08458cde40f1b2ea213334d02892910c0f5bf2c8a8cd418d0f0522de7e016 url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" flutter_inappwebview_web: dependency: transitive description: name: flutter_inappwebview_web - sha256: e276e5417c286d5ba66b833151edb3ec6706fa46b0d1cee04cdfa046595ea797 + sha256: "77ac7a82fec6e7a84ad4ea9d219cc84c8e542fb5791f208614e82620a4c3d7f4" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.3" flutter_launcher_icons: dependency: "direct dev" description: @@ -503,10 +503,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: c247a4f76071c3b97bb5ae8912968870d5565644801c5e09f3bc961b4d874895 + sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15 url: "https://pub.dev" source: hosted - version: "12.1.1" + version: "12.1.3" google_fonts: dependency: "direct main" description: @@ -615,10 +615,10 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7e108028e3d258667d079986da8c0bc32da4cb57431c2af03b1dc1038621a9dc" + sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" url: "https://pub.dev" source: hosted - version: "9.0.13" + version: "9.0.16" leak_tracker_testing: dependency: transitive description: @@ -631,10 +631,10 @@ packages: dependency: "direct dev" description: name: leancode_lint - sha256: "1e99cba16e084a18ce966a7df270d6da6a11ab236caac716aa1fb2359eb277eb" + sha256: bf50886eba9c3f7ad163656867670c114bd9ba3b8e5ca22821cb5edcc72fc2f1 url: "https://pub.dev" source: hosted - version: "7.0.0+1" + version: "8.0.0" logging: dependency: transitive description: @@ -671,10 +671,10 @@ packages: dependency: "direct dev" description: name: melos - sha256: "28136bf9fcd26ca2ffb104a02eff876efb776e7fff9303e2c8c978f0bd7c5800" + sha256: "96e64bbade5712c3f010137e195bca9f1b351fac34ab1f322af492ae34032067" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.4.0" meta: dependency: transitive description: @@ -1092,10 +1092,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba + sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.2" url_launcher_android: dependency: transitive description: @@ -1140,10 +1140,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" + sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" url_launcher_windows: dependency: transitive description: @@ -1188,18 +1188,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa url: "https://pub.dev" source: hosted - version: "0.3.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" + version: "0.4.0" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 831b9aa8..5d373733 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.30 + flutter_inappwebview: ^6.0.0-beta.31 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 @@ -29,7 +29,7 @@ dependencies: path: packages/glider_data glider_domain: path: packages/glider_domain - go_router: ^12.1.1 + go_router: ^12.1.3 google_fonts: ^6.1.0 http: ^1.1.2 hydrated_bloc: ^9.1.2 @@ -45,14 +45,14 @@ dependencies: share_plus: ^7.2.1 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 - url_launcher: ^6.2.1 + url_launcher: ^6.2.2 dev_dependencies: custom_lint: ^0.5.7 dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 - leancode_lint: ^7.0.0+1 - melos: ^3.3.0 + leancode_lint: ^8.0.0 + melos: ^3.4.0 flutter: uses-material-design: true From 52331862617a172274ecabef6427cd15e0590544 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 20:52:49 +0100 Subject: [PATCH 085/145] Don't use context across async gap --- lib/auth/view/auth_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index f5a2ea79..4f659a62 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -68,7 +68,7 @@ class _AuthPageState extends State { return BlocConsumer( listenWhen: (previous, current) => current.isLoggedIn, listener: (context, state) async { - await _browser.close(); + unawaited(_browser.close()); final confirm = await context.push( AppRoute.confirmDialog.location(), extra: ( From 99c99e89ed98f5dcb136d2ed494f544bd0e37760 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 21:04:06 +0100 Subject: [PATCH 086/145] Ignore included file warning --- analysis_options.yaml | 2 ++ packages/glider_data/analysis_options.yaml | 2 ++ packages/glider_domain/analysis_options.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/analysis_options.yaml b/analysis_options.yaml index fd51df18..8d33785a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,6 +1,8 @@ include: package:leancode_lint/analysis_options.yaml analyzer: + errors: + included_file_warning: ignore plugins: custom_lint diff --git a/packages/glider_data/analysis_options.yaml b/packages/glider_data/analysis_options.yaml index a49ee7ec..1859d09b 100644 --- a/packages/glider_data/analysis_options.yaml +++ b/packages/glider_data/analysis_options.yaml @@ -1,5 +1,7 @@ include: package:leancode_lint/analysis_options.yaml analyzer: + errors: + included_file_warning: ignore plugins: custom_lint diff --git a/packages/glider_domain/analysis_options.yaml b/packages/glider_domain/analysis_options.yaml index a49ee7ec..1859d09b 100644 --- a/packages/glider_domain/analysis_options.yaml +++ b/packages/glider_domain/analysis_options.yaml @@ -1,5 +1,7 @@ include: package:leancode_lint/analysis_options.yaml analyzer: + errors: + included_file_warning: ignore plugins: custom_lint From 53ef45cd04eccb771f7ebfd2b287bdffd176ffa0 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 21:11:08 +0100 Subject: [PATCH 087/145] Also color app bar pure for pure background setting --- lib/app/view/app.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 4e8bca3e..003d5908 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -64,6 +64,11 @@ class App extends StatelessWidget { ) { // ignore: deprecated_member_use final textScaleFactor = MediaQuery.textScalerOf(context).textScaleFactor; + final backgroundColor = state.usePureBackground + ? brightness == Brightness.dark + ? Colors.black + : Colors.white + : null; return ThemeData( useMaterial3: true, visualDensity: VisualDensity.comfortable, @@ -72,11 +77,7 @@ class App extends StatelessWidget { ? dynamicColorScheme : state.themeVariant.toColorScheme(state.themeColor, brightness)) .copyWith( - background: state.usePureBackground - ? brightness == Brightness.dark - ? Colors.black - : Colors.white - : null, + background: backgroundColor, ), textTheme: GoogleFonts.getTextTheme(state.font, const TextTheme()), menuTheme: const MenuThemeData( @@ -88,7 +89,10 @@ class App extends StatelessWidget { ), ), ), - appBarTheme: const AppBarTheme(centerTitle: false), + appBarTheme: AppBarTheme( + color: backgroundColor, + centerTitle: false, + ), // Badges do not handle text scaling by default. badgeTheme: BadgeThemeData( smallSize: 6 * textScaleFactor, From 383120d4254b6c2bd8384ba25aae40f58ac18035 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 21:19:03 +0100 Subject: [PATCH 088/145] Improve filter matching to respect word boundaries --- lib/app/extensions/string_extension.dart | 4 ++-- lib/item/widgets/item_tile.dart | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/app/extensions/string_extension.dart b/lib/app/extensions/string_extension.dart index 2b252106..b365146f 100644 --- a/lib/app/extensions/string_extension.dart +++ b/lib/app/extensions/string_extension.dart @@ -1,4 +1,4 @@ extension StringExtension on String { - bool caseInsensitiveContains(String other) => - toLowerCase().contains(other.toLowerCase()); + bool containsWord(String other, {bool caseSensitive = true}) => + RegExp('\\b$other\\b', caseSensitive: caseSensitive).hasMatch(this); } diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index d2260855..054369c0 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -144,13 +144,17 @@ class _ItemTileState extends State blocked: state.blocked, filtered: item.title != null && settingsState.wordFilters.any( - (word) => - item.title!.caseInsensitiveContains(word), + (word) => item.title!.containsWord( + word, + caseSensitive: false, + ), ) || item.url != null && settingsState.domainFilters.any( - (domain) => item.url!.host - .caseInsensitiveContains(domain), + (domain) => item.url!.host.containsWord( + domain, + caseSensitive: false, + ), ), failed: state.status == Status.failure, collapsedCount: widget.collapsedCount, From 71dc80f46bbc07ffbc2696810272db93e414b3de Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 8 Dec 2023 21:52:24 +0100 Subject: [PATCH 089/145] DRY debounce transformer --- lib/common/transformers/debounce.dart | 5 +++++ lib/stories_search/bloc/stories_search_bloc.dart | 9 ++------- lib/story_item_search/bloc/story_item_search_bloc.dart | 9 ++------- lib/user_item_search/bloc/user_item_search_bloc.dart | 9 ++------- 4 files changed, 11 insertions(+), 21 deletions(-) create mode 100644 lib/common/transformers/debounce.dart diff --git a/lib/common/transformers/debounce.dart b/lib/common/transformers/debounce.dart new file mode 100644 index 00000000..d69970f9 --- /dev/null +++ b/lib/common/transformers/debounce.dart @@ -0,0 +1,5 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:rxdart/transformers.dart'; + +EventTransformer debounce(Duration duration) => + (events, mapper) => events.debounceTime(duration).switchMap(mapper); diff --git a/lib/stories_search/bloc/stories_search_bloc.dart b/lib/stories_search/bloc/stories_search_bloc.dart index d6902887..e05f06a4 100644 --- a/lib/stories_search/bloc/stories_search_bloc.dart +++ b/lib/stories_search/bloc/stories_search_bloc.dart @@ -6,20 +6,15 @@ import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/mixins/paginated_list_mixin.dart'; import 'package:glider/common/models/status.dart'; +import 'package:glider/common/transformers/debounce.dart'; import 'package:glider/stories_search/models/search_range.dart'; import 'package:glider/stories_search/models/search_type.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:rxdart/transformers.dart'; part 'stories_search_event.dart'; part 'stories_search_state.dart'; -const _debounceDuration = Duration(milliseconds: 300); - -EventTransformer debounce(Duration duration) => - (events, mapper) => events.debounceTime(duration).switchMap(mapper); - class StoriesSearchBloc extends HydratedBloc { StoriesSearchBloc(this._itemRepository, {this.searchType = SearchType.search}) @@ -31,7 +26,7 @@ class StoriesSearchBloc ) { on( (event, emit) async => _load(), - transformer: debounce(_debounceDuration), + transformer: debounce(const Duration(milliseconds: 300)), ); on( (event, emit) async => _setText(event), diff --git a/lib/story_item_search/bloc/story_item_search_bloc.dart b/lib/story_item_search/bloc/story_item_search_bloc.dart index a8f2237e..db0ec362 100644 --- a/lib/story_item_search/bloc/story_item_search_bloc.dart +++ b/lib/story_item_search/bloc/story_item_search_bloc.dart @@ -4,18 +4,13 @@ import 'package:equatable/equatable.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/models/status.dart'; +import 'package:glider/common/transformers/debounce.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:rxdart/transformers.dart'; part 'story_item_search_event.dart'; part 'story_item_search_state.dart'; -const _debounceDuration = Duration(milliseconds: 300); - -EventTransformer debounce(Duration duration) => - (events, mapper) => events.debounceTime(duration).switchMap(mapper); - class StoryItemSearchBloc extends Bloc { StoryItemSearchBloc(this._itemRepository, {required int id}) @@ -23,7 +18,7 @@ class StoryItemSearchBloc super(const StoryItemSearchState()) { on( (event, emit) async => _load(), - transformer: debounce(_debounceDuration), + transformer: debounce(const Duration(milliseconds: 300)), ); on( (event, emit) async => _setText(event), diff --git a/lib/user_item_search/bloc/user_item_search_bloc.dart b/lib/user_item_search/bloc/user_item_search_bloc.dart index d4595535..e75d7e21 100644 --- a/lib/user_item_search/bloc/user_item_search_bloc.dart +++ b/lib/user_item_search/bloc/user_item_search_bloc.dart @@ -4,25 +4,20 @@ import 'package:equatable/equatable.dart'; import 'package:glider/common/extensions/bloc_base_extension.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/models/status.dart'; +import 'package:glider/common/transformers/debounce.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:rxdart/transformers.dart'; part 'user_item_search_event.dart'; part 'user_item_search_state.dart'; -const _debounceDuration = Duration(milliseconds: 300); - -EventTransformer debounce(Duration duration) => - (events, mapper) => events.debounceTime(duration).switchMap(mapper); - class UserItemSearchBloc extends Bloc { UserItemSearchBloc(this._itemRepository, {required this.username}) : super(const UserItemSearchState()) { on( (event, emit) async => _load(), - transformer: debounce(_debounceDuration), + transformer: debounce(const Duration(milliseconds: 300)), ); on( (event, emit) async => _setText(event), From 408490945a46879b0d0afb65c39ff8597194a51e Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 10 Dec 2023 21:12:47 +0100 Subject: [PATCH 090/145] Also color navigation bar pure for pure background setting --- lib/app/view/app.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 003d5908..03ed4a95 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -98,6 +98,9 @@ class App extends StatelessWidget { smallSize: 6 * textScaleFactor, largeSize: 16 * textScaleFactor, ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: backgroundColor, + ), bottomSheetTheme: const BottomSheetThemeData( showDragHandle: true, // Material 3 dictates a maximum width for bottom sheets. From af17222b30730ba99f5687335a5ae3fbd55b7bc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:21:57 +0100 Subject: [PATCH 091/145] Bump scrollview_observer from 1.18.2 to 1.19.0 (#156) Bumps [scrollview_observer](https://github.com/fluttercandies/flutter_scrollview_observer) from 1.18.2 to 1.19.0. - [Release notes](https://github.com/fluttercandies/flutter_scrollview_observer/releases) - [Changelog](https://github.com/fluttercandies/flutter_scrollview_observer/blob/main/CHANGELOG.md) - [Commits](https://github.com/fluttercandies/flutter_scrollview_observer/compare/1.18.2...1.19.0) --- updated-dependencies: - dependency-name: scrollview_observer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 40 ++++++++++++---------------------------- pubspec.yaml | 2 +- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index bad484fb..dec39064 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "65.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.2.0" analyzer_plugin: dependency: transitive description: @@ -611,22 +611,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" - url: "https://pub.dev" - source: hosted - version: "9.0.16" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff - url: "https://pub.dev" - source: hosted - version: "1.0.5" leancode_lint: dependency: "direct dev" description: @@ -663,10 +647,10 @@ packages: dependency: "direct main" description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" melos: dependency: "direct dev" description: @@ -679,10 +663,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" mime: dependency: transitive description: @@ -911,10 +895,10 @@ packages: dependency: "direct main" description: name: scrollview_observer - sha256: "72b806ea64d446efffac1000c0e0c0b03b06ce019b0fcc7d24d3b1f86e93fb0d" + sha256: "69de9e6e4900a2d21dd9f593c597a1418e9f463cceeac6677ba83b007bf1f3e9" url: "https://pub.dev" source: hosted - version: "1.18.2" + version: "1.19.0" share_plus: dependency: "direct main" description: @@ -1188,10 +1172,10 @@ packages: dependency: transitive description: name: web - sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.3.0" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5d373733..3b20cc98 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 - scrollview_observer: ^1.18.2 + scrollview_observer: ^1.19.0 share_plus: ^7.2.1 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 From 7bd301cbdae65e6ead6ec1b72c483631632c1b93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:22:05 +0100 Subject: [PATCH 092/145] Bump hydrated_bloc from 9.1.2 to 9.1.3 (#157) Bumps [hydrated_bloc](https://github.com/felangel/bloc/tree/master/packages) from 9.1.2 to 9.1.3. - [Release notes](https://github.com/felangel/bloc/releases) - [Commits](https://github.com/felangel/bloc/commits/hydrated_bloc-v9.1.3/packages) --- updated-dependencies: - dependency-name: hydrated_bloc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index dec39064..a18c3380 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -567,10 +567,10 @@ packages: dependency: "direct main" description: name: hydrated_bloc - sha256: "24994e61f64904d911683cce1a31dc4ef611619da5253f1de2b7b8fc6f79a118" + sha256: c925e49704c052a8f249226ae7603f86bfa776b910816390763b956c71d2cbaf url: "https://pub.dev" source: hosted - version: "9.1.2" + version: "9.1.3" image: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3b20cc98..2dc2a0e3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,7 +32,7 @@ dependencies: go_router: ^12.1.3 google_fonts: ^6.1.0 http: ^1.1.2 - hydrated_bloc: ^9.1.2 + hydrated_bloc: ^9.1.3 intl: any markdown: ^7.1.1 material_color_utilities: any From 8dc4156166f66fd24e8dd18ec08358d64cb85c74 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 15:10:25 +0100 Subject: [PATCH 093/145] Update Pubspec lock file --- pubspec.lock | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index a18c3380..bd06ccfa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -611,6 +611,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" + url: "https://pub.dev" + source: hosted + version: "9.0.16" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" leancode_lint: dependency: "direct dev" description: @@ -647,10 +663,10 @@ packages: dependency: "direct main" description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" melos: dependency: "direct dev" description: @@ -663,10 +679,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -1172,10 +1188,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.4.0" win32: dependency: transitive description: From e32693749369b1dcb6a73941dd1a295f1b39a6f5 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 13:11:02 +0100 Subject: [PATCH 094/145] Use FVM in Melos scripts --- .github/actions/bootstrap/action.yml | 30 ++++++++++++++++++++++++++++ .github/workflows/ci.yml | 21 +++---------------- .github/workflows/release.yml | 25 +++++------------------ melos.yaml | 15 ++++++++------ 4 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 .github/actions/bootstrap/action.yml diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml new file mode 100644 index 00000000..a8569025 --- /dev/null +++ b/.github/actions/bootstrap/action.yml @@ -0,0 +1,30 @@ +name: Bootstrap +description: Bootstrap workspace +runs: + using: composite + steps: + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + cache: gradle + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@master + - name: Set up FVM + run: | + brew tap leoafarias/fvm + brew install fvm@3.0.0-beta.5 + shell: bash + - name: Set up Flutter + run: | + fvm install + fvm flutter config --disable-analytics + echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH + shell: bash + - name: Set up Melos + run: fvm dart pub global activate melos + shell: bash + - name: Bootstrap workspace + run: fvm flutter pub global run melos bootstrap + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f7d7379..f1ba0671 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,27 +15,12 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - cache: gradle - - name: Parse FVM config - id: fvm-config-action - uses: kuhnroyal/flutter-fvm-config-action@v2.0 - - name: Setup Flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ steps.fvm-config-action.outputs.FLUTTER_VERSION }} - channel: any - cache: true - - name: Setup Melos - uses: bluefireteam/melos-action@v3 + - name: Bootstrap workspace + uses: ./.github/actions/bootstrap - name: Run static analysis checks run: melos lint - name: Build Android APK (profile) - run: flutter build apk --profile + run: fvm flutter build apk --profile - name: Upload APK (profile) artifact uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dae44eec..43d9ac35 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,12 +13,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - cache: gradle + - name: Bootstrap workspace + uses: ./.github/actions/bootstrap - name: Get Android keystore id: android-keystore uses: timheuer/base64-to-file@v1 @@ -39,21 +35,10 @@ jobs: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ${{ secrets.ANDROID_KEY_PASSWORD }} ${{ secrets.ANDROID_KEY_ALIAS }} - - name: Parse FVM config - id: fvm-config-action - uses: kuhnroyal/flutter-fvm-config-action@v2.0 - - name: Setup Flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ steps.fvm-config-action.outputs.FLUTTER_VERSION }} - channel: any - cache: true - - name: Setup Melos - uses: bluefireteam/melos-action@v3 - name: Build Android APK - run: flutter build apk + run: fvm flutter build apk - name: Build Android App Bundle - run: flutter build appbundle + run: fvm flutter build appbundle - name: Upload APK artifact uses: actions/upload-artifact@v3 with: @@ -98,7 +83,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup Ruby + - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: 3.2 diff --git a/melos.yaml b/melos.yaml index c24b041b..836442ec 100644 --- a/melos.yaml +++ b/melos.yaml @@ -10,25 +10,28 @@ packages: scripts: lint: - run: melos run analyze && melos run format-check && melos run dependency-validate + run: | + melos run analyze + melos run format-check + melos run dependency-validate description: Run all static analysis checks. analyze: - exec: dart analyze . --fatal-infos + exec: fvm dart analyze . --fatal-infos description: Run `dart analyze` in all packages. format: - exec: dart format . --fix + exec: fvm dart format . --fix description: Run `dart format` for all packages. format-check: - exec: dart format . --set-exit-if-changed + exec: fvm dart format . --set-exit-if-changed description: Run `dart format` checks for all packages. dependency-validate: - exec: flutter pub run dependency_validator + exec: fvm flutter pub run dependency_validator description: Run `flutter pub run dependency_validator` for all packages. outdated: - exec: flutter pub outdated + exec: fvm flutter pub outdated description: Run `flutter pub outdated` for all packages. From 108215af073f3963d3eb7afd5c615edf5e4e545e Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 13:15:55 +0100 Subject: [PATCH 095/145] Call `initState`'s inherited method at start of implementation, per recommended usage --- lib/auth/view/auth_page.dart | 2 +- lib/edit/view/edit_page.dart | 4 ++-- lib/favorites/view/favorites_shell_page.dart | 2 +- lib/inbox/view/inbox_shell_page.dart | 2 +- lib/item/view/item_page.dart | 6 +++--- lib/item/widgets/item_tile.dart | 1 + lib/item/widgets/item_value_dialog.dart | 2 +- lib/navigation_shell/widgets/navigation_shell_scaffold.dart | 2 +- lib/reply/view/reply_page.dart | 4 ++-- lib/settings/view/filters_dialog.dart | 2 +- lib/stories/view/stories_shell_page.dart | 4 ++-- lib/stories_search/view/catch_up_shell_page.dart | 2 +- lib/submit/view/submit_page.dart | 2 +- lib/user/view/user_page.dart | 4 ++-- lib/user/widgets/user_value_dialog.dart | 2 +- 15 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/auth/view/auth_page.dart b/lib/auth/view/auth_page.dart index 4f659a62..107a0305 100644 --- a/lib/auth/view/auth_page.dart +++ b/lib/auth/view/auth_page.dart @@ -40,9 +40,9 @@ class _AuthPageState extends State { @override void initState() { + super.initState(); _browser = _AuthInAppBrowser(widget._authCubit); unawaited(widget._authCubit.init()); - super.initState(); } @override diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index b278cffe..521d3d85 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -37,8 +37,8 @@ class _EditPageState extends State { @override void initState() { - _editCubit = widget._editCubitFactory(widget.id); super.initState(); + _editCubit = widget._editCubitFactory(widget.id); } @override @@ -133,11 +133,11 @@ class _EditFormState extends State<_EditForm> { @override void initState() { + super.initState(); final state = widget._editCubit.state; _titleController = TextEditingController(text: state.title?.value); _urlController = TextEditingController(text: state.item?.url?.toString()); _textController = TextEditingController(text: state.text?.value); - super.initState(); } @override diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index f8fe4fdd..66030baf 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -39,8 +39,8 @@ class FavoritesShellPage extends StatefulWidget { class _FavoritesShellPageState extends State { @override void initState() { - unawaited(widget._favoritesCubit.load()); super.initState(); + unawaited(widget._favoritesCubit.load()); } @override diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 428ad248..68aba11f 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -40,8 +40,8 @@ class InboxShellPage extends StatefulWidget { class _InboxShellPageState extends State { @override void initState() { - unawaited(widget._inboxCubit.load()); super.initState(); + unawaited(widget._inboxCubit.load()); } @override diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index be6ffe79..3855d964 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -68,6 +68,7 @@ class _ItemPageState extends State { @override void initState() { + super.initState(); _itemCubit = widget._itemCubitFactory(widget.id); unawaited(_itemCubit.visit(true)); _itemTreeCubit = widget._itemTreeCubitFactory(widget.id); @@ -77,7 +78,6 @@ class _ItemPageState extends State { _scrollController = ScrollController(); _sliverObserverController = SliverObserverController(controller: _scrollController); - super.initState(); } @override @@ -226,11 +226,11 @@ class _SliverItemAppBarState extends State<_SliverItemAppBar> { @override void initState() { + super.initState(); WidgetsBinding.instance .addPostFrameCallback((timeStamp) => _updateBodyRenderSliver()); widget.scrollController?.addListener(_scrollListener); _hasOverlapNotifier = ValueNotifier(false); - super.initState(); } @override @@ -376,13 +376,13 @@ class _ItemSearchAnchorState extends State<_ItemSearchAnchor> { @override void initState() { + super.initState(); _searchController = SearchController() ..text = widget._storyItemSearchBloc.state.searchText ?? '' ..addListener( () async => widget._storyItemSearchBloc .add(SetTextStoryItemSearchEvent(_searchController.text)), ); - super.initState(); } @override diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 054369c0..d4ff3a11 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -83,6 +83,7 @@ class _ItemTileState extends State @override void initState() { + // Define before `super.initState()` for safe access in `wantKeepAlive`. _itemCubit = widget._itemCubit ?? widget._itemCubitFactory!(widget.id); super.initState(); } diff --git a/lib/item/widgets/item_value_dialog.dart b/lib/item/widgets/item_value_dialog.dart index 163168fb..29a3563f 100644 --- a/lib/item/widgets/item_value_dialog.dart +++ b/lib/item/widgets/item_value_dialog.dart @@ -32,8 +32,8 @@ class _ItemValueDialogState extends State { @override void initState() { - _itemCubit = widget._itemCubitFactory(widget.id); super.initState(); + _itemCubit = widget._itemCubitFactory(widget.id); } @override diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 96804b1f..8139bc67 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -45,9 +45,9 @@ class _NavigationShellScaffoldState extends State { @override void initState() { + super.initState(); unawaited(widget._navigationShellCubit.init()); _currentNavigationBarHeightNotifier = ValueNotifier(0); - super.initState(); } @override diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 3d2ac78c..537a4f8c 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -40,8 +40,8 @@ class _ReplyPageState extends State { @override void initState() { - _replyCubit = widget._replyCubitFactory(widget.id); super.initState(); + _replyCubit = widget._replyCubitFactory(widget.id); } @override @@ -149,9 +149,9 @@ class _ReplyFormState extends State<_ReplyForm> { @override void initState() { + super.initState(); final state = widget._replyCubit.state; _textController = TextEditingController(text: state.text.value); - super.initState(); } @override diff --git a/lib/settings/view/filters_dialog.dart b/lib/settings/view/filters_dialog.dart index 908bf870..7e0c428f 100644 --- a/lib/settings/view/filters_dialog.dart +++ b/lib/settings/view/filters_dialog.dart @@ -46,11 +46,11 @@ class _FiltersBodyState extends State<_FiltersBody> { @override void initState() { + super.initState(); _wordsController = TextEditingController(); _domainsController = TextEditingController(); _wordsFocusNode = FocusNode(); _domainsFocusNode = FocusNode(); - super.initState(); } @override diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index 80486010..fab2a7ad 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -47,8 +47,8 @@ class StoriesShellPage extends StatefulWidget { class _StoriesShellPageState extends State { @override void initState() { - unawaited(widget._storiesCubit.load()); super.initState(); + unawaited(widget._storiesCubit.load()); } @override @@ -171,13 +171,13 @@ class _StoriesSearchAnchorState extends State<_StoriesSearchAnchor> { @override void initState() { + super.initState(); _searchController = SearchController() ..text = widget._storiesSearchBloc.state.searchText ?? '' ..addListener( () async => widget._storiesSearchBloc .add(SetTextStoriesSearchEvent(_searchController.text)), ); - super.initState(); } @override diff --git a/lib/stories_search/view/catch_up_shell_page.dart b/lib/stories_search/view/catch_up_shell_page.dart index 0acb7f6f..089f5759 100644 --- a/lib/stories_search/view/catch_up_shell_page.dart +++ b/lib/stories_search/view/catch_up_shell_page.dart @@ -33,8 +33,8 @@ class CatchUpShellPage extends StatefulWidget { class _CatchUpShellPageState extends State { @override void initState() { - widget._storiesSearchBloc.add(const LoadStoriesSearchEvent()); super.initState(); + widget._storiesSearchBloc.add(const LoadStoriesSearchEvent()); } @override diff --git a/lib/submit/view/submit_page.dart b/lib/submit/view/submit_page.dart index a124f628..f8f8ccfc 100644 --- a/lib/submit/view/submit_page.dart +++ b/lib/submit/view/submit_page.dart @@ -136,11 +136,11 @@ class _SubmitFormState extends State<_SubmitForm> { @override void initState() { + super.initState(); final state = widget._submitCubit.state; _titleController = TextEditingController(text: state.title.value); _urlController = TextEditingController(text: state.url.value); _textController = TextEditingController(text: state.text.value); - super.initState(); } @override diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 0a170b19..620daa0c 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -54,11 +54,11 @@ class _UserPageState extends State { @override void initState() { + super.initState(); _userCubit = widget._userCubitFactory(widget.username); unawaited(_userCubit.load()); _userItemSearchBloc = widget._userItemSearchBlocFactory(widget.username); _scrollController = ScrollController(); - super.initState(); } @override @@ -212,13 +212,13 @@ class _UserSearchAnchorState extends State<_UserSearchAnchor> { @override void initState() { + super.initState(); _searchController = SearchController() ..text = widget._userItemSearchBloc.state.searchText ?? '' ..addListener( () async => widget._userItemSearchBloc .add(SetTextUserItemSearchEvent(_searchController.text)), ); - super.initState(); } @override diff --git a/lib/user/widgets/user_value_dialog.dart b/lib/user/widgets/user_value_dialog.dart index 70e00b25..1a223a0f 100644 --- a/lib/user/widgets/user_value_dialog.dart +++ b/lib/user/widgets/user_value_dialog.dart @@ -32,8 +32,8 @@ class _UserValueDialogState extends State { @override void initState() { - _userCubit = widget._userCubitFactory(widget.username); super.initState(); + _userCubit = widget._userCubitFactory(widget.username); } @override From 7d784748d38de0e8aa6003561d371749e075134f Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 15:05:37 +0100 Subject: [PATCH 096/145] 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(), From 6bc36c1c9b3041aec31c564812bdefd262f582d6 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 20:35:14 +0100 Subject: [PATCH 097/145] Fix show jobs setting persistence --- lib/settings/cubit/settings_cubit.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index f1845516..6a51dccc 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -41,6 +41,7 @@ class SettingsCubit extends Cubit final showStoryMetadata = await _settingsRepository.getShowStoryMetadata(); final showUserAvatars = await _settingsRepository.getShowUserAvatars(); final useActionButtons = await _settingsRepository.getUseActionButtons(); + final showJobs = await _settingsRepository.getShowJobs(); final useThreadNavigation = await _settingsRepository.getUseThreadNavigation(); final enableDownvoting = await _settingsRepository.getEnableDownvoting(); @@ -64,6 +65,7 @@ class SettingsCubit extends Cubit showUserAvatars: showUserAvatars != null ? () => showUserAvatars : null, useActionButtons: useActionButtons != null ? () => useActionButtons : null, + showJobs: showJobs != null ? () => showJobs : null, useThreadNavigation: useThreadNavigation != null ? () => useThreadNavigation : null, enableDownvoting: From 8c18ed11efcfad87d47444247b0b0f85d767d0b2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 20:56:09 +0100 Subject: [PATCH 098/145] Show a more friendly message on logged in user's page --- lib/common/widgets/failure_widget.dart | 4 +++- lib/l10n/arb/app_en.arb | 1 + lib/user/view/user_page.dart | 30 +++++++++++++++++++------- lib/user/widgets/user_tile.dart | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/common/widgets/failure_widget.dart b/lib/common/widgets/failure_widget.dart index 2be8f0fa..8fa597f0 100644 --- a/lib/common/widgets/failure_widget.dart +++ b/lib/common/widgets/failure_widget.dart @@ -5,11 +5,13 @@ import 'package:glider/l10n/extensions/app_localizations_extension.dart'; class FailureWidget extends StatelessWidget { const FailureWidget({ super.key, + this.title, this.exception, this.onRetry, this.compact = false, }); + final String? title; final Object? exception; final VoidCallback? onRetry; final bool compact; @@ -21,7 +23,7 @@ class FailureWidget extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - title: Text(context.l10n.failure), + title: Text(title ?? context.l10n.failure), subtitle: exception != null ? Text(exception.runtimeType.toString()) : null, diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8541d742..407a9c56 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -98,6 +98,7 @@ "unblock": "Unblock", "username": "Username", "userLink": "User link", + "userUnavailable": "User information is not available yet", "autofillTitle": "Autofill title", "emptyError": "This field cannot be empty.", "bothEmptyError": "This field and {otherField} cannot both be empty.", diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 620daa0c..57f441fd 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -10,6 +10,7 @@ import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; import 'package:glider/common/models/status.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; +import 'package:glider/common/widgets/failure_widget.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; import 'package:glider/item/widgets/item_loading_tile.dart'; import 'package:glider/item/widgets/item_tile.dart'; @@ -70,12 +71,12 @@ class _UserPageState extends State { @override Widget build(BuildContext context) { - return BlocSelector( - bloc: widget._authCubit, - selector: (state) => state.username == widget.username, - builder: (context, isLoggedInUser) => BlocBuilder( - bloc: _userCubit, - builder: (context, state) => Scaffold( + return BlocBuilder( + bloc: _userCubit, + builder: (context, state) => BlocSelector( + bloc: widget._authCubit, + selector: (state) => state.username == widget.username, + builder: (context, isLoggedInUser) => Scaffold( body: RefreshableScrollView( scrollController: _scrollController, onRefresh: () async => unawaited(_userCubit.load()), @@ -97,6 +98,7 @@ class _UserPageState extends State { widget._itemCubitFactory, widget._authCubit, widget._settingsCubit, + isLoggedInUser: isLoggedInUser, ), ), ], @@ -341,13 +343,15 @@ class _SliverUserBody extends StatelessWidget { this._userCubit, this._itemCubitFactory, this._authCubit, - this._settingsCubit, - ); + this._settingsCubit, { + this.isLoggedInUser = false, + }); final UserCubit _userCubit; final ItemCubitFactory _itemCubitFactory; final AuthCubit _authCubit; final SettingsCubit _settingsCubit; + final bool isLoggedInUser; @override Widget build(BuildContext context) { @@ -388,6 +392,16 @@ class _SliverUserBody extends StatelessWidget { ), ], ), + // Data may not be available yet for newly registered users. Show a more + // friendly message when we suspect this is the case. + failure: isLoggedInUser + ? () => SliverFillRemaining( + child: FailureWidget( + title: context.l10n.userUnavailable, + onRetry: () async => _userCubit.load(), + ), + ) + : null, onRetry: () async => _userCubit.load(), ), ); diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index e2ad23d6..4f05093b 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -76,7 +76,7 @@ class UserTile extends StatelessWidget { ), ); }, - onRetry: () async => _userCubit.load(), + failure: SizedBox.shrink, ), ), ), From 73a9a09a9f9eb37da2b65f962eb27201b5a1769c Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 22:12:53 +0100 Subject: [PATCH 099/145] Replace fastlane action with one-line command --- .github/workflows/release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43d9ac35..a9c617bd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,10 +94,7 @@ jobs: with: name: app-release.aab - name: Release to Google Play - uses: maierj/fastlane-action@v3.0.0 - with: - lane: supply - options: '{ "aab": "app-release.aab" }' + run: bundle exec fastlane supply --aab app-release.aab env: SUPPLY_PACKAGE_NAME: ${{ secrets.ANDROID_PACKAGE_NAME }} SUPPLY_JSON_KEY_DATA: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }} From 06784335c28dda81e6948ca1cc0fe87abe5e8996 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 11 Dec 2023 22:22:09 +0100 Subject: [PATCH 100/145] Bump version --- fastlane/metadata/android/en-US/changelogs/48.txt | 6 ++++++ pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/48.txt diff --git a/fastlane/metadata/android/en-US/changelogs/48.txt b/fastlane/metadata/android/en-US/changelogs/48.txt new file mode 100644 index 00000000..a030dad0 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/48.txt @@ -0,0 +1,6 @@ +- Improved filter setting to match on word boundaries +- Improved pure background setting to also darken the app bar and navigation bar +- Fixed Hacker News links opened within the app clearing navigation stack +- Fixed show jobs setting not persisting +- Fixed user page showing a nondescript error shortly after registering new account +- Fixed incorrect story title suffix detection \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 2dc2a0e3..e0bff4c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: glider description: An opinionated Hacker News client. -version: 2.6.0+47 +version: 2.7.0+48 publish_to: none environment: From d11f1757eb6d6cf501c55469daecef21afca22cd Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 12 Dec 2023 19:56:23 +0100 Subject: [PATCH 101/145] Upgradle --- android/app/build.gradle | 12 +++++------- android/build.gradle | 4 ++-- android/gradle/wrapper/gradle-wrapper.properties | 4 +++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2fbd5d78..40028db6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) { def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") + throw new GradleException('Flutter SDK not found. Define location with flutter.sdk in the local.properties file.') } def flutterVersionCode = localProperties.getProperty('flutter.versionCode') @@ -33,12 +33,12 @@ if (keystorePropertiesFile.exists()) { android { // Conditional for compatibility with AGP <4.2. - if (project.android.hasProperty("namespace")) { + if (project.android.hasProperty('namespace')) { namespace 'nl.viter.glider' } - compileSdkVersion Math.max(flutter.compileSdkVersion, 34) - ndkVersion flutter.ndkVersion + compileSdkVersion flutter.compileSdkVersion + ndkVersion '26.1.10909125' kotlinOptions { jvmTarget = '1.8' @@ -50,8 +50,6 @@ android { defaultConfig { applicationId 'nl.viter.glider' - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 21 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() @@ -81,5 +79,5 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "com.google.android.material:material:1.9.0" + implementation 'com.google.android.material:material:1.10.0' } diff --git a/android/build.gradle b/android/build.gradle index 070b4ffb..639493e8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.9.10' + ext.kotlin_version = '1.9.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.0' + classpath 'com.android.tools.build:gradle:8.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 7bb2df6b..1af9e093 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip From 3aec11d06c36797106d4d4fc37f3b01a9f750f1b Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 12 Dec 2023 20:00:30 +0100 Subject: [PATCH 102/145] Apply pure background to more UI elements --- lib/app/view/app.dart | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 03ed4a95..68515b00 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -70,7 +70,6 @@ class App extends StatelessWidget { : Colors.white : null; return ThemeData( - useMaterial3: true, visualDensity: VisualDensity.comfortable, brightness: brightness, colorScheme: (state.useDynamicTheme && dynamicColorScheme != null @@ -80,15 +79,6 @@ class App extends StatelessWidget { background: backgroundColor, ), textTheme: GoogleFonts.getTextTheme(state.font, const TextTheme()), - menuTheme: const MenuThemeData( - style: MenuStyle( - shape: MaterialStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(AppSpacing.m)), - ), - ), - ), - ), appBarTheme: AppBarTheme( color: backgroundColor, centerTitle: false, @@ -98,9 +88,6 @@ class App extends StatelessWidget { smallSize: 6 * textScaleFactor, largeSize: 16 * textScaleFactor, ), - navigationBarTheme: NavigationBarThemeData( - backgroundColor: backgroundColor, - ), bottomSheetTheme: const BottomSheetThemeData( showDragHandle: true, // Material 3 dictates a maximum width for bottom sheets. @@ -114,6 +101,24 @@ class App extends StatelessWidget { ), ), inputDecorationTheme: const InputDecorationTheme(filled: true), + menuTheme: const MenuThemeData( + style: MenuStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(AppSpacing.m)), + ), + ), + ), + ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: backgroundColor, + ), + navigationRailTheme: NavigationRailThemeData( + backgroundColor: backgroundColor, + ), + searchViewTheme: SearchViewThemeData( + backgroundColor: backgroundColor, + ), ); } } From 6f5ab2337792d96f69178b7653e2b89446b7b953 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 12 Dec 2023 21:50:40 +0100 Subject: [PATCH 103/145] Revert "Clean up unused code" This reverts commit e2f361b5fbb9a85e5c78f0f8772dbac20ff5db3b. --- .../widgets/navigation_shell_scaffold.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 8139bc67..05c9729f 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -149,17 +149,20 @@ class _NavigationShellScaffoldState extends State { BuildContext context, List destinations, { Widget? leading, + bool extended = false, }) { final padding = MediaQuery.paddingOf(context); final directionality = Directionality.of(context); return AdaptiveScaffold.standardNavigationRail( - width: 80 + + extended: extended, + width: (extended ? 160 : 80) + switch (directionality) { TextDirection.ltr => padding.left, TextDirection.rtl => padding.right, }, - labelType: NavigationRailLabelType.all, + labelType: + extended ? NavigationRailLabelType.none : NavigationRailLabelType.all, groupAlignment: 0, leading: leading, selectedIndex: _currentIndex, From 42bb88a3106a0ab319f8e7182d5fa690cb6fdb56 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 13 Dec 2023 21:19:26 +0100 Subject: [PATCH 104/145] Add navigation drawer setting --- lib/app/router/app_router.dart | 1 + lib/l10n/arb/app_en.arb | 2 + .../widgets/navigation_shell_scaffold.dart | 241 ++++++++++-------- lib/settings/cubit/settings_cubit.dart | 12 + lib/settings/cubit/settings_state.dart | 7 + lib/settings/view/settings_page.dart | 8 + lib/stories/view/stories_type_view.dart | 6 +- .../view/stories_search_range_view.dart | 6 +- .../lib/src/shared_preferences_service.dart | 7 + .../lib/src/settings_repository.dart | 6 + 10 files changed, 192 insertions(+), 104 deletions(-) diff --git a/lib/app/router/app_router.dart b/lib/app/router/app_router.dart index 889e6e20..1b601509 100644 --- a/lib/app/router/app_router.dart +++ b/lib/app/router/app_router.dart @@ -41,6 +41,7 @@ class AppRouter { NavigationShellScaffold( appContainer.navigationShellCubit, appContainer.authCubit, + appContainer.settingsCubit, navigationShell, ), branches: [ diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 407a9c56..482dc5a0 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -38,6 +38,8 @@ "downvotingDescription": "Requires an account with 501+ karma", "inAppBrowser": "Custom tabs", "inAppBrowserDescription": "Opens links in in-app browser if possible", + "navigationDrawer": "Navigation drawer", + "navigationDrawerDescription": "Replaces navigation bar or rail", "filters": "Filters", "filtersDescription": "Hides stories based on words or domains", "words": "Words", diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 05c9729f..213065eb 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -10,6 +10,7 @@ import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/navigation_shell/cubit/navigation_shell_cubit.dart'; +import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:go_router/go_router.dart'; // Height based on `_NavigationBarDefaultsM3`. @@ -19,12 +20,14 @@ class NavigationShellScaffold extends StatefulWidget { const NavigationShellScaffold( this._navigationShellCubit, this._authCubit, + this._settingsCubit, this._navigationShell, { super.key, }); final NavigationShellCubit _navigationShellCubit; final AuthCubit _authCubit; + final SettingsCubit _settingsCubit; final StatefulNavigationShell _navigationShell; @override @@ -70,77 +73,90 @@ class _NavigationShellScaffoldState extends State { listener: (context, event) => switch (event) { ShowWhatsNewEvent() => context.push(AppRoute.whatsNew.location()), }, - child: BlocBuilder( + child: BlocSelector( bloc: widget._authCubit, - builder: (context, authState) { - final destinations = [ - NavigationDestination( - icon: const Icon(Icons.whatshot_outlined), - selectedIcon: const Icon(Icons.whatshot), - label: context.l10n.stories, - ), - NavigationDestination( - icon: const Icon(Icons.fast_rewind_outlined), - selectedIcon: const Icon(Icons.fast_rewind), - label: context.l10n.catchUp, - ), - NavigationDestination( - icon: const Icon(Icons.favorite_outline_outlined), - selectedIcon: const Icon(Icons.favorite), - label: context.l10n.favorites, - ), - if (authState.isLoggedIn) + selector: (state) => state.isLoggedIn, + builder: (context, isLoggedIn) => + BlocBuilder( + bloc: widget._settingsCubit, + buildWhen: (previous, current) => + previous.useNavigationDrawer != current.useNavigationDrawer, + builder: (context, settingsState) { + final destinations = [ NavigationDestination( - icon: const Icon(Icons.inbox_outlined), - selectedIcon: const Icon(Icons.inbox), - label: context.l10n.inbox, + icon: const Icon(Icons.whatshot_outlined), + selectedIcon: const Icon(Icons.whatshot), + label: context.l10n.stories, ), - ]; - final floatingActionButton = authState.isLoggedIn - ? FloatingActionButton( - onPressed: () => context.push(AppRoute.submit.location()), - tooltip: context.l10n.submit, - child: const Icon(Icons.add_outlined), - ) - : null; - - return Material( - child: AdaptiveLayout( - primaryNavigation: SlotLayout( - config: { - Breakpoints.mediumAndUp: SlotLayout.from( - key: const Key('primaryNavigationMediumAndUp'), - builder: (context) => _buildPrimaryNavigation( - context, - destinations, - leading: floatingActionButton, - ), - ), - }, + NavigationDestination( + icon: const Icon(Icons.fast_rewind_outlined), + selectedIcon: const Icon(Icons.fast_rewind), + label: context.l10n.catchUp, ), - bottomNavigation: SlotLayout( - config: { - Breakpoints.small: SlotLayout.from( - key: const Key('bottomNavigationStandard'), - builder: (context) => - _buildBottomNavigation(context, destinations), - ), - }, + NavigationDestination( + icon: const Icon(Icons.favorite_outline_outlined), + selectedIcon: const Icon(Icons.favorite), + label: context.l10n.favorites, ), + if (isLoggedIn) + NavigationDestination( + icon: const Icon(Icons.inbox_outlined), + selectedIcon: const Icon(Icons.inbox), + label: context.l10n.inbox, + ), + ]; + final floatingActionButton = isLoggedIn + ? FloatingActionButton( + onPressed: () => context.push(AppRoute.submit.location()), + tooltip: context.l10n.submit, + child: const Icon(Icons.add_outlined), + ) + : null; + + return AdaptiveLayout( + primaryNavigation: settingsState.useNavigationDrawer + ? null + : SlotLayout( + config: { + Breakpoints.mediumAndUp: SlotLayout.from( + key: const Key('primaryNavigationMediumAndUp'), + builder: (context) => _buildPrimaryNavigation( + context, + destinations, + leading: floatingActionButton, + ), + ), + }, + ), + bottomNavigation: settingsState.useNavigationDrawer + ? null + : SlotLayout( + config: { + Breakpoints.small: SlotLayout.from( + key: const Key('bottomNavigationStandard'), + builder: (context) => _buildBottomNavigation( + context, + destinations, + ), + ), + }, + ), body: SlotLayout( config: { Breakpoints.standard: SlotLayout.from( key: const Key('bodyStandard'), builder: (context) => _buildBody( context, + destinations, floatingActionButton: floatingActionButton, + useNavigationDrawer: settingsState.useNavigationDrawer, ), ), }, ), - ), - ); - }, + ); + }, + ), ), ); } @@ -154,32 +170,67 @@ class _NavigationShellScaffoldState extends State { final padding = MediaQuery.paddingOf(context); final directionality = Directionality.of(context); - return AdaptiveScaffold.standardNavigationRail( - extended: extended, - width: (extended ? 160 : 80) + - switch (directionality) { - TextDirection.ltr => padding.left, - TextDirection.rtl => padding.right, - }, - labelType: - extended ? NavigationRailLabelType.none : NavigationRailLabelType.all, - groupAlignment: 0, - leading: leading, - selectedIndex: _currentIndex, - destinations: [ - for (final destination in destinations) - AdaptiveScaffold.toRailDestination(destination), - ], - onDestinationSelected: onDestinationSelected, + return Material( + child: AdaptiveScaffold.standardNavigationRail( + extended: extended, + width: (extended ? 192 : 80) + + switch (directionality) { + TextDirection.ltr => padding.left, + TextDirection.rtl => padding.right, + }, + labelType: extended + ? NavigationRailLabelType.none + : NavigationRailLabelType.all, + groupAlignment: extended ? -1 : 0, + leading: leading, + selectedIndex: _currentIndex, + destinations: [ + for (final destination in destinations) + AdaptiveScaffold.toRailDestination(destination), + ], + onDestinationSelected: (index) { + if (extended) { + Navigator.of(context).pop(); + } + + onDestinationSelected(index); + }, + ), ); } - Widget _buildBody(BuildContext context, {Widget? floatingActionButton}) { + Widget _buildBottomNavigation( + BuildContext context, + List destinations, + ) { + return ValueListenableBuilder( + valueListenable: _currentNavigationBarHeightNotifier, + builder: (context, currentNavigationBarHeight, child) => Align( + heightFactor: currentNavigationBarHeight / _paddedNavigationBarHeight, + alignment: Alignment.topCenter, + child: child, + ), + child: AdaptiveScaffold.standardBottomNavigationBar( + currentIndex: _currentIndex, + destinations: destinations, + onDestinationSelected: onDestinationSelected, + ), + ); + } + + Widget _buildBody( + BuildContext context, + List destinations, { + Widget? floatingActionButton, + bool useNavigationDrawer = false, + }) { final directionality = Directionality.of(context); final mediaQuery = MediaQuery.of(context); final padding = mediaQuery.padding; final viewPadding = mediaQuery.viewPadding; final isSmallBreakpointActive = Breakpoints.small.isActive(context); + final hasNavigationBar = !useNavigationDrawer && isSmallBreakpointActive; + final hasNavigationRail = !useNavigationDrawer && !isSmallBreakpointActive; return NotificationListener( onNotification: (notification) { @@ -211,20 +262,17 @@ class _NavigationShellScaffoldState extends State { child: ValueListenableBuilder( valueListenable: _currentNavigationBarHeightNotifier, builder: (context, currentNavigationBarHeight, child) { - double? calculateBottomPadding(EdgeInsets padding) => - isSmallBreakpointActive - ? max(0, padding.bottom - currentNavigationBarHeight) - : null; + double? calculateBottomPadding(EdgeInsets padding) => hasNavigationBar + ? max(0, padding.bottom - currentNavigationBarHeight) + : null; return MediaQuery( data: mediaQuery.copyWith( padding: padding.copyWith( - left: !isSmallBreakpointActive && - directionality == TextDirection.ltr + left: hasNavigationRail && directionality == TextDirection.ltr ? 0 : null, - right: !isSmallBreakpointActive && - directionality == TextDirection.rtl + right: hasNavigationRail && directionality == TextDirection.rtl ? 0 : null, bottom: calculateBottomPadding(padding), @@ -238,30 +286,19 @@ class _NavigationShellScaffoldState extends State { ); }, child: Scaffold( + drawer: useNavigationDrawer + ? Drawer( + child: _buildPrimaryNavigation( + context, + destinations, + extended: true, + ), + ) + : null, body: widget._navigationShell, - floatingActionButton: - isSmallBreakpointActive ? floatingActionButton : null, + floatingActionButton: hasNavigationRail ? null : floatingActionButton, ), ), ); } - - Widget _buildBottomNavigation( - BuildContext context, - List destinations, - ) { - return ValueListenableBuilder( - valueListenable: _currentNavigationBarHeightNotifier, - builder: (context, currentNavigationBarHeight, child) => Align( - heightFactor: currentNavigationBarHeight / _paddedNavigationBarHeight, - alignment: Alignment.topCenter, - child: child, - ), - child: AdaptiveScaffold.standardBottomNavigationBar( - currentIndex: _currentIndex, - destinations: destinations, - onDestinationSelected: onDestinationSelected, - ), - ); - } } diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index 6a51dccc..5276be33 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -245,6 +245,18 @@ class SettingsCubit extends Cubit } } + Future setUseNavigationDrawer(bool value) async { + await _settingsRepository.setUseNavigationDrawer(value: value); + final useNavigationDrawer = + await _settingsRepository.getUseNavigationDrawer(); + + if (useNavigationDrawer != null) { + safeEmit( + state.copyWith(useNavigationDrawer: () => useNavigationDrawer), + ); + } + } + Future setWordFilter(String value, {required bool filter}) async { await _settingsRepository.setWordFilter(value: value, filter: filter); final wordFilters = await _settingsRepository.getWordFilters(); diff --git a/lib/settings/cubit/settings_state.dart b/lib/settings/cubit/settings_state.dart index aeb812da..ffcd77f4 100644 --- a/lib/settings/cubit/settings_state.dart +++ b/lib/settings/cubit/settings_state.dart @@ -17,6 +17,7 @@ class SettingsState with EquatableMixin { this.useThreadNavigation = true, this.enableDownvoting = false, this.useInAppBrowser = false, + this.useNavigationDrawer = false, this.wordFilters = const {}, this.domainFilters = const {}, this.appVersion, @@ -37,6 +38,7 @@ class SettingsState with EquatableMixin { final bool useThreadNavigation; final bool enableDownvoting; final bool useInAppBrowser; + final bool useNavigationDrawer; final Set wordFilters; final Set domainFilters; final Version? appVersion; @@ -57,6 +59,7 @@ class SettingsState with EquatableMixin { bool Function()? useThreadNavigation, bool Function()? enableDownvoting, bool Function()? useInAppBrowser, + bool Function()? useNavigationDrawer, Set Function()? wordFilters, Set Function()? domainFilters, Version? Function()? appVersion, @@ -92,6 +95,9 @@ class SettingsState with EquatableMixin { : this.enableDownvoting, useInAppBrowser: useInAppBrowser != null ? useInAppBrowser() : this.useInAppBrowser, + useNavigationDrawer: useNavigationDrawer != null + ? useNavigationDrawer() + : this.useNavigationDrawer, wordFilters: wordFilters != null ? wordFilters() : this.wordFilters, domainFilters: domainFilters != null ? domainFilters() : this.domainFilters, @@ -115,6 +121,7 @@ class SettingsState with EquatableMixin { useThreadNavigation, enableDownvoting, useInAppBrowser, + useNavigationDrawer, wordFilters, domainFilters, appVersion, diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 7cd5713d..f025edab 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -280,6 +280,14 @@ class _SettingsBody extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl), ), + SwitchListTile.adaptive( + value: state.useNavigationDrawer, + onChanged: _settingsCubit.setUseNavigationDrawer, + title: Text(context.l10n.navigationDrawer), + subtitle: Text(context.l10n.navigationDrawerDescription), + contentPadding: + const EdgeInsets.symmetric(horizontal: AppSpacing.xl), + ), ListTile( title: Text(context.l10n.filters), subtitle: Text(context.l10n.filtersDescription), diff --git a/lib/stories/view/stories_type_view.dart b/lib/stories/view/stories_type_view.dart index 2fb356a9..25532fd7 100644 --- a/lib/stories/view/stories_type_view.dart +++ b/lib/stories/view/stories_type_view.dart @@ -26,7 +26,11 @@ class StoriesTypeView extends StatelessWidget { padding: EdgeInsetsDirectional.only( top: AppSpacing.s, bottom: AppSpacing.s, - start: AppSpacing.xl, + start: AppSpacing.xl + + switch (directionality) { + TextDirection.ltr => padding.left, + TextDirection.rtl => padding.right, + }, end: AppSpacing.xl + switch (directionality) { TextDirection.ltr => padding.right, diff --git a/lib/stories_search/view/stories_search_range_view.dart b/lib/stories_search/view/stories_search_range_view.dart index 9cae473a..3765b0dd 100644 --- a/lib/stories_search/view/stories_search_range_view.dart +++ b/lib/stories_search/view/stories_search_range_view.dart @@ -45,7 +45,11 @@ class StoriesSearchRangeView extends StatelessWidget { padding: EdgeInsetsDirectional.only( top: AppSpacing.s, bottom: AppSpacing.s, - start: AppSpacing.xl, + start: AppSpacing.xl + + switch (directionality) { + TextDirection.ltr => padding.left, + TextDirection.rtl => padding.right, + }, end: AppSpacing.xl + switch (directionality) { TextDirection.ltr => padding.right, diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index 169e5a28..bbc1e1f4 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -20,6 +20,7 @@ class SharedPreferencesService { static const String _useThreadNavigationKey = 'use_thread_navigation'; static const String _enableDownvotingKey = 'enable_downvoting'; static const String _useInAppBrowserKey = 'use_in_app_browser'; + static const String _useNavigationDrawerKey = 'use_navigation_drawer'; static const String _wordFiltersKey = 'word_filters'; static const String _domainFiltersKey = 'domain_filters'; static const String _lastVersionKey = 'last_version'; @@ -118,6 +119,12 @@ class SharedPreferencesService { Future setUseInAppBrowser({required bool value}) async => _sharedPreferences.setBool(_useInAppBrowserKey, value); + Future getUseNavigationDrawer() async => + _sharedPreferences.getBool(_useNavigationDrawerKey); + + Future setUseNavigationDrawer({required bool value}) async => + _sharedPreferences.setBool(_useNavigationDrawerKey, value); + Future?> getWordFilters() async => _sharedPreferences.getStringList(_wordFiltersKey); diff --git a/packages/glider_domain/lib/src/settings_repository.dart b/packages/glider_domain/lib/src/settings_repository.dart index 8949da6a..9d6740f4 100644 --- a/packages/glider_domain/lib/src/settings_repository.dart +++ b/packages/glider_domain/lib/src/settings_repository.dart @@ -103,6 +103,12 @@ class SettingsRepository { Future setUseInAppBrowser({required bool value}) async => _sharedPreferencesService.setUseInAppBrowser(value: value); + Future getUseNavigationDrawer() async => + _sharedPreferencesService.getUseNavigationDrawer(); + + Future setUseNavigationDrawer({required bool value}) async => + _sharedPreferencesService.setUseNavigationDrawer(value: value); + Future?> getWordFilters() async => (await _sharedPreferencesService.getWordFilters())?.toSet(); From 9d643de22bf82ce2a3fbea90ea6498c5da87fbb2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 13 Dec 2023 21:21:09 +0100 Subject: [PATCH 105/145] Optimize settings state rebuilding --- lib/edit/view/edit_page.dart | 6 ++ lib/item/view/item_page.dart | 3 + lib/item/widgets/item_tile.dart | 6 ++ lib/item_tree/view/sliver_item_tree_body.dart | 93 +++++++++---------- lib/reply/view/reply_page.dart | 5 + lib/settings/view/filters_dialog.dart | 3 + lib/settings/view/settings_page.dart | 7 ++ lib/settings/view/theme_color_dialog.dart | 2 + lib/stories/view/stories_type_view.dart | 1 + lib/user/widgets/user_tile.dart | 2 + 10 files changed, 79 insertions(+), 49 deletions(-) diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index 521d3d85..661b0654 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -259,6 +259,12 @@ class _EditPreview extends StatelessWidget { builder: (context, state) => state.item != null ? BlocBuilder( bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.useLargeStoryStyle != + current.useLargeStoryStyle || + previous.showFavicons != current.showFavicons || + previous.showUserAvatars != current.showUserAvatars || + previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => ItemDataTile( state.item!.copyWith( title: () => state.title?.value, diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index 3855d964..e6373954 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -97,6 +97,9 @@ class _ItemPageState extends State { Widget build(BuildContext context) { return BlocBuilder( bloc: widget._settingsCubit, + buildWhen: (previous, current) => + previous.useLargeStoryStyle != current.useLargeStoryStyle || + previous.useThreadNavigation != current.useThreadNavigation, builder: (context, settingsState) => Scaffold( body: SliverViewObserver( controller: _sliverObserverController, diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index d4ff3a11..a49b11b5 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -107,6 +107,12 @@ class _ItemTileState extends State builder: (context, authState) => BlocBuilder( bloc: widget._settingsCubit, + buildWhen: (previous, current) => + previous.showStoryMetadata != current.showStoryMetadata || + previous.useLargeStoryStyle != current.useLargeStoryStyle || + previous.showFavicons != current.showFavicons || + previous.showUserAvatars != current.showUserAvatars || + previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => AnimatedSize( alignment: Alignment.topCenter, duration: AppAnimation.emphasized.duration, diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index 0077ebbb..f9ad44ab 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -46,58 +46,53 @@ class SliverItemTreeBody extends StatelessWidget { ); } }, - builder: (context, state) => BlocBuilder( - bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useActionButtons != current.useActionButtons, - builder: (context, settingsState) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( - itemCount: childCount, - itemBuilder: (context, index) => const IndentedWidget( - depth: 1, - child: ItemLoadingTile(type: ItemType.comment), - ), + builder: (context, state) => state.whenOrDefaultSlivers( + loading: () => SliverList.builder( + itemCount: childCount, + itemBuilder: (context, index) => const IndentedWidget( + depth: 1, + child: ItemLoadingTile(type: ItemType.comment), ), - nonEmpty: () => SliverList.builder( - itemCount: state.viewableData!.length, - itemBuilder: (context, index) { - final descendant = state.viewableData![index]; - return IndentedWidget( - depth: descendant.isPart ? 0 : descendant.depth, - child: ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - id: descendant.id, - storyUsername: storyUsername, - loadingType: ItemType.comment, - collapsedCount: state.collapsedIds.contains(descendant.id) - ? state.getDescendants(descendant)?.length - : null, - showVisited: false, - highlight: !(state.previousData - ?.map((e) => e.id) - .contains(descendant.id) ?? - true), - onTap: (context, item) async { - if (!item.isDeleted) { - _itemTreeCubit.toggleCollapsed(item.id); - } + ), + nonEmpty: () => SliverList.builder( + itemCount: state.viewableData!.length, + itemBuilder: (context, index) { + final descendant = state.viewableData![index]; + return IndentedWidget( + depth: descendant.isPart ? 0 : descendant.depth, + child: ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + id: descendant.id, + storyUsername: storyUsername, + loadingType: ItemType.comment, + collapsedCount: state.collapsedIds.contains(descendant.id) + ? state.getDescendants(descendant)?.length + : null, + showVisited: false, + highlight: !(state.previousData + ?.map((e) => e.id) + .contains(descendant.id) ?? + true), + onTap: (context, item) async { + if (!item.isDeleted) { + _itemTreeCubit.toggleCollapsed(item.id); + } - await Scrollable.ensureVisible( - context, - duration: AppAnimation.standard.duration, - curve: AppAnimation.standard.easing, - alignmentPolicy: - ScrollPositionAlignmentPolicy.keepVisibleAtStart, - ); - }, - ), - ); - }, - ), - onRetry: () async => _itemTreeCubit.load(), + await Scrollable.ensureVisible( + context, + duration: AppAnimation.standard.duration, + curve: AppAnimation.standard.easing, + alignmentPolicy: + ScrollPositionAlignmentPolicy.keepVisibleAtStart, + ); + }, + ), + ); + }, ), + onRetry: () async => _itemTreeCubit.load(), ), ); } diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 537a4f8c..8d92007b 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -219,6 +219,11 @@ class _ReplyPreview extends StatelessWidget { builder: (context, username) => BlocBuilder( bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.useLargeStoryStyle != current.useLargeStoryStyle || + previous.showFavicons != current.showFavicons || + previous.showUserAvatars != current.showUserAvatars || + previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => HeroMode( enabled: false, child: ItemDataTile( diff --git a/lib/settings/view/filters_dialog.dart b/lib/settings/view/filters_dialog.dart index 7e0c428f..50a14063 100644 --- a/lib/settings/view/filters_dialog.dart +++ b/lib/settings/view/filters_dialog.dart @@ -66,6 +66,9 @@ class _FiltersBodyState extends State<_FiltersBody> { Widget build(BuildContext context) { return BlocBuilder( bloc: widget._settingsCubit, + buildWhen: (previous, current) => + previous.wordFilters != current.wordFilters || + previous.domainFilters != current.domainFilters, builder: (context, state) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index f025edab..506b2319 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -204,6 +204,13 @@ class _SettingsBody extends StatelessWidget { ), BlocBuilder( bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.useLargeStoryStyle != current.useLargeStoryStyle || + previous.showFavicons != current.showFavicons || + previous.showStoryMetadata != current.showStoryMetadata || + previous.showUserAvatars != current.showUserAvatars || + previous.useActionButtons != current.useActionButtons || + previous.useInAppBrowser != current.useInAppBrowser, builder: (context, state) => Padding( padding: AppSpacing.defaultTilePadding, child: PreviewCard( diff --git a/lib/settings/view/theme_color_dialog.dart b/lib/settings/view/theme_color_dialog.dart index 22214c27..2e61bd5d 100644 --- a/lib/settings/view/theme_color_dialog.dart +++ b/lib/settings/view/theme_color_dialog.dart @@ -47,6 +47,8 @@ class _ThemeColorBody extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.themeColor != current.themeColor, builder: (context, state) => GridView.extent( maxCrossAxisExtent: 64, shrinkWrap: true, diff --git a/lib/stories/view/stories_type_view.dart b/lib/stories/view/stories_type_view.dart index 25532fd7..364090d3 100644 --- a/lib/stories/view/stories_type_view.dart +++ b/lib/stories/view/stories_type_view.dart @@ -61,6 +61,7 @@ class _StoriesTypeBody extends StatelessWidget { buildWhen: (previous, current) => previous.storyType != current.storyType, builder: (context, state) => BlocBuilder( bloc: _settingsCubit, + buildWhen: (previous, current) => previous.showJobs != current.showJobs, builder: (context, settingsState) => Row( children: [ for (final storyType in StoryType.values) diff --git a/lib/user/widgets/user_tile.dart b/lib/user/widgets/user_tile.dart index 4f05093b..ea1ab666 100644 --- a/lib/user/widgets/user_tile.dart +++ b/lib/user/widgets/user_tile.dart @@ -47,6 +47,8 @@ class UserTile extends StatelessWidget { bloc: _userCubit, builder: (context, state) => BlocBuilder( bloc: _settingsCubit, + buildWhen: (previous, current) => + previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => AnimatedSize( alignment: Alignment.topCenter, duration: AppAnimation.emphasized.duration, From 3a395424ad8dc426a87f0913dc0c808acd744efd Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 13 Dec 2023 22:35:45 +0100 Subject: [PATCH 106/145] Fix settings initialization --- lib/settings/cubit/settings_cubit.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index 5276be33..936d9bcb 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -45,9 +45,11 @@ class SettingsCubit extends Cubit final useThreadNavigation = await _settingsRepository.getUseThreadNavigation(); final enableDownvoting = await _settingsRepository.getEnableDownvoting(); + final useInAppBrowser = await _settingsRepository.getUseInAppBrowser(); + final useNavigationDrawer = + await _settingsRepository.getUseNavigationDrawer(); final wordFilters = await _settingsRepository.getWordFilters(); final domainFilters = await _settingsRepository.getDomainFilters(); - final useInAppBrowser = await _settingsRepository.getUseInAppBrowser(); safeEmit( state.copyWith( themeMode: themeMode != null ? () => themeMode : null, @@ -71,6 +73,8 @@ class SettingsCubit extends Cubit enableDownvoting: enableDownvoting != null ? () => enableDownvoting : null, useInAppBrowser: useInAppBrowser != null ? () => useInAppBrowser : null, + useNavigationDrawer: + useNavigationDrawer != null ? () => useNavigationDrawer : null, wordFilters: wordFilters != null ? () => wordFilters : null, domainFilters: domainFilters != null ? () => domainFilters : null, appVersion: _packageRepository.getVersion, From 8bd7abf8eeee6819009c7e638720e387a89948a1 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 13 Dec 2023 22:40:27 +0100 Subject: [PATCH 107/145] Regenerate iOS project files --- .metadata | 20 ++++--- ios/Runner.xcodeproj/project.pbxproj | 84 ++++++++++++++-------------- ios/Runner/Info.plist | 2 - 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/.metadata b/.metadata index abd5e4f6..31f8a5f1 100644 --- a/.metadata +++ b/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: 4b12645012342076800eb701bcdfe18f87da21cf - channel: stable + revision: "593a031efb1e54aef4d083fc810482c2877ba1d2" + channel: "beta" project_type: app @@ -13,11 +13,17 @@ project_type: app migration: platforms: - platform: root - create_revision: 4b12645012342076800eb701bcdfe18f87da21cf - base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + - platform: android + create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + - platform: ios + create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 - platform: macos - create_revision: 4b12645012342076800eb701bcdfe18f87da21cf - base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 # User provided section diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c8626266..9ba3cf1b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,12 +8,12 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2F2F97A798DDADD6C430D61E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2888D36307280CE7527F8980 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + FFE78A087DCDC29FA3E57801 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C909B87A58928FAF6B6408A8 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -32,14 +32,12 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2888D36307280CE7527F8980 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 312732DAE4CCDF0A2DCFCD17 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 37C95D52045BB8C48BDB8200 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5C3323A6209CF07D7877ED13 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7B63812C6BC52D2BA522C6BA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 8147FCBA10197544F72AFA0E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -47,6 +45,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C909B87A58928FAF6B6408A8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D00926024DE5B3DC5078CA4B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,28 +54,17 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2F2F97A798DDADD6C430D61E /* Pods_Runner.framework in Frameworks */, + FFE78A087DCDC29FA3E57801 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 72CC7D77BB11259EECCA021F /* Pods */ = { + 4B1226DD43465C0AF3875744 /* Frameworks */ = { isa = PBXGroup; children = ( - 37C95D52045BB8C48BDB8200 /* Pods-Runner.debug.xcconfig */, - 7B63812C6BC52D2BA522C6BA /* Pods-Runner.release.xcconfig */, - 312732DAE4CCDF0A2DCFCD17 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - 89D4A003E74E6E16EDB49DD6 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2888D36307280CE7527F8980 /* Pods_Runner.framework */, + C909B87A58928FAF6B6408A8 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -97,8 +86,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 72CC7D77BB11259EECCA021F /* Pods */, - 89D4A003E74E6E16EDB49DD6 /* Frameworks */, + ED06CD5BACA7842DB8682C9E /* Pods */, + 4B1226DD43465C0AF3875744 /* Frameworks */, ); sourceTree = ""; }; @@ -125,6 +114,17 @@ path = Runner; sourceTree = ""; }; + ED06CD5BACA7842DB8682C9E /* Pods */ = { + isa = PBXGroup; + children = ( + 8147FCBA10197544F72AFA0E /* Pods-Runner.debug.xcconfig */, + D00926024DE5B3DC5078CA4B /* Pods-Runner.release.xcconfig */, + 5C3323A6209CF07D7877ED13 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -132,14 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 3C69BA2FB8957AAA622D1F3F /* [CP] Check Pods Manifest.lock */, + 1440E43EF629F4A6B0C60252 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - BB4E20410EA8F6A89C70EF6C /* [CP] Embed Pods Frameworks */, + FA749DAED25F4EA850B3D05D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -156,6 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -198,43 +199,43 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 1440E43EF629F4A6B0C60252 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Thin Binary"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - 3C69BA2FB8957AAA622D1F3F /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); + name = "Thin Binary"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -251,7 +252,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - BB4E20410EA8F6A89C70EF6C /* [CP] Embed Pods Frameworks */ = { + FA749DAED25F4EA850B3D05D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -359,7 +360,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = MDRNL8TR6J; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -488,7 +488,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = MDRNL8TR6J; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -511,7 +510,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = MDRNL8TR6J; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index fad1dbf6..2891b277 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -41,8 +41,6 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone UIApplicationSupportsIndirectInputEvents From 5c2d980bb4fce6145b595c2ef466ee148310d327 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 16 Dec 2023 21:03:43 +0100 Subject: [PATCH 108/145] Modernize drawer design --- .../widgets/navigation_shell_scaffold.dart | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index 213065eb..fc6ca489 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -165,36 +165,30 @@ class _NavigationShellScaffoldState extends State { BuildContext context, List destinations, { Widget? leading, - bool extended = false, }) { final padding = MediaQuery.paddingOf(context); final directionality = Directionality.of(context); return Material( child: AdaptiveScaffold.standardNavigationRail( - extended: extended, - width: (extended ? 192 : 80) + + width: 80 + switch (directionality) { TextDirection.ltr => padding.left, TextDirection.rtl => padding.right, }, - labelType: extended - ? NavigationRailLabelType.none - : NavigationRailLabelType.all, - groupAlignment: extended ? -1 : 0, + labelType: NavigationRailLabelType.all, + groupAlignment: 0, leading: leading, - selectedIndex: _currentIndex, destinations: [ for (final destination in destinations) - AdaptiveScaffold.toRailDestination(destination), + NavigationRailDestination( + icon: destination.icon, + selectedIcon: destination.selectedIcon, + label: Text(destination.label), + ), ], - onDestinationSelected: (index) { - if (extended) { - Navigator.of(context).pop(); - } - - onDestinationSelected(index); - }, + selectedIndex: _currentIndex, + onDestinationSelected: onDestinationSelected, ), ); } @@ -211,8 +205,8 @@ class _NavigationShellScaffoldState extends State { child: child, ), child: AdaptiveScaffold.standardBottomNavigationBar( - currentIndex: _currentIndex, destinations: destinations, + currentIndex: _currentIndex, onDestinationSelected: onDestinationSelected, ), ); @@ -287,12 +281,20 @@ class _NavigationShellScaffoldState extends State { }, child: Scaffold( drawer: useNavigationDrawer - ? Drawer( - child: _buildPrimaryNavigation( - context, - destinations, - extended: true, - ), + ? NavigationDrawer( + selectedIndex: _currentIndex, + onDestinationSelected: (index) { + onDestinationSelected(index); + Navigator.pop(context); + }, + children: [ + for (final destination in destinations) + NavigationDrawerDestination( + icon: destination.icon, + selectedIcon: destination.selectedIcon, + label: Text(destination.label), + ), + ], ) : null, body: widget._navigationShell, From 242764ddd0386362fbeb863ac081b311f5529b01 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 16 Dec 2023 21:06:17 +0100 Subject: [PATCH 109/145] Upgrade dependencies --- .fvmrc | 2 +- .vscode/settings.json | 2 +- ios/Podfile.lock | 2 +- pubspec.lock | 48 +++++++++++++++++++++---------------------- pubspec.yaml | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.fvmrc b/.fvmrc index 6c11074f..38875c11 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.18.0-0.1.pre" + "flutter": "3.18.0-0.2.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b80b9406..65544029 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.18.0-0.1.pre" + "dart.flutterSdkPath": ".fvm/versions/3.18.0-0.2.pre" } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6c3a4662..ef35817f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -74,4 +74,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.14.2 +COCOAPODS: 1.14.3 diff --git a/pubspec.lock b/pubspec.lock index bd06ccfa..66f1a018 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "65.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" analyzer_plugin: dependency: transitive description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: "direct main" description: @@ -330,18 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: b34fb3b0a8232a5adae1501e4beb6997717e4a17664eab528d4a0b8fd596d417 + sha256: "585348d7682f3a59fe6c63f895cb67d28f078815794f16d1f3943fd5b4718e20" url: "https://pub.dev" source: hosted - version: "6.0.0-beta.31" + version: "6.0.0-beta.32" flutter_inappwebview_android: dependency: transitive description: name: flutter_inappwebview_android - sha256: "297ef285b47529332dda074f3bca6dbe103f9e83534b7aca5f081be45da80610" + sha256: "29d493ec6fbb11788d1d79394287f13c7c362e6862726d1c922cb1f0b7ec92c4" url: "https://pub.dev" source: hosted - version: "1.0.7" + version: "1.0.8" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -354,34 +354,34 @@ packages: dependency: transitive description: name: flutter_inappwebview_ios - sha256: "6b1e3b4f46596700a2393aeb7023379e8a4cd609d2f4d05d358697bcffdcf010" + sha256: "2ed0639516a6a13209871fcc9bc21db03cb9dd8f93a6125cc6c9deb14db38ce6" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" flutter_inappwebview_macos: dependency: transitive description: name: flutter_inappwebview_macos - sha256: f341d16cf471c072e5424eed748aac90b48a7ff1b4897a254ada189bb551145f + sha256: "23aea85f9e16ce6671696c95b3eae6b53912264e18c07934ad16457c2f152ff0" url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.7" flutter_inappwebview_platform_interface: dependency: transitive description: name: flutter_inappwebview_platform_interface - sha256: efc08458cde40f1b2ea213334d02892910c0f5bf2c8a8cd418d0f0522de7e016 + sha256: b9941cb6c358f50ab4a890a6301e50a6cfca20e70408627ec34bc69fddd85ca7 url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" flutter_inappwebview_web: dependency: transitive description: name: flutter_inappwebview_web - sha256: "77ac7a82fec6e7a84ad4ea9d219cc84c8e542fb5791f208614e82620a4c3d7f4" + sha256: "6a2b5c0496f8f8260e3564d4323deccb7635368477576710e4f03879a1c4a2b7" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" flutter_launcher_icons: dependency: "direct dev" description: @@ -1116,10 +1116,10 @@ packages: dependency: transitive description: name: url_launcher_linux - sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" url_launcher_macos: dependency: transitive description: @@ -1148,18 +1148,18 @@ packages: dependency: transitive description: name: url_launcher_windows - sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" uuid: dependency: transitive description: name: uuid - sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 + sha256: bb55f38968b9427ce5dcdb8aaaa41049282195e0cfa4cf48593572fa3d1f36bc url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "4.3.1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e0bff4c9..b0198f29 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.31 + flutter_inappwebview: ^6.0.0-beta.32 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 From 259c5f38d9943f2e68da4f4c10bede121b674f6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:08:36 +0100 Subject: [PATCH 110/145] Bump actions/download-artifact from 3 to 4 (#162) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a9c617bd..ecc4505b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: uses: altive/pubspec-metadata@v1 - name: Get artifact id: download-artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: app-release.apk - name: Release @@ -90,7 +90,7 @@ jobs: bundler-cache: true - name: Get artifact id: download-artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: app-release.aab - name: Release to Google Play From 65a62506708efc5432932d94243442ebfa4e7fc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:08:46 +0100 Subject: [PATCH 111/145] Bump actions/upload-artifact from 3 to 4 (#163) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1ba0671..d5b5633f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: - name: Build Android APK (profile) run: fvm flutter build apk --profile - name: Upload APK (profile) artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: app-profile.apk path: build/app/outputs/apk/profile/app-profile.apk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ecc4505b..96e15a8c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,12 +40,12 @@ jobs: - name: Build Android App Bundle run: fvm flutter build appbundle - name: Upload APK artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: app-release.apk path: build/app/outputs/apk/release/app-release.apk - name: Upload App Bundle artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: app-release.aab path: build/app/outputs/bundle/release/app-release.aab From 80da21f9cf121ddd00c49c92cce0b77935de7767 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 18 Dec 2023 21:52:55 +0100 Subject: [PATCH 112/145] Fix sizes in loading tile --- lib/item/widgets/item_loading_tile.dart | 138 ++++++++++++------------ 1 file changed, 71 insertions(+), 67 deletions(-) diff --git a/lib/item/widgets/item_loading_tile.dart b/lib/item/widgets/item_loading_tile.dart index e2118c79..3d5fa4ed 100644 --- a/lib/item/widgets/item_loading_tile.dart +++ b/lib/item/widgets/item_loading_tile.dart @@ -25,7 +25,7 @@ class ItemLoadingTile extends StatelessWidget { final ItemStyle style; final EdgeInsetsGeometry padding; - int get _faviconSize => useLargeStoryStyle ? 20 : 2 * 24; + int get _faviconSize => useLargeStoryStyle ? 2 * 24 : 20; @override Widget build(BuildContext context) { @@ -47,8 +47,6 @@ class ItemLoadingTile extends StatelessWidget { Widget _buildPrimary(BuildContext context) { final textTheme = Theme.of(context).textTheme; - final color = - Theme.of(context).colorScheme.outline.withOpacity(LoadingBlock.opacity); return Column( children: [ if (type != ItemType.comment) @@ -77,74 +75,80 @@ class ItemLoadingTile extends StatelessWidget { ), ].spaced(width: AppSpacing.xl), ), - if (showMetadata) - Row( - children: [ - if (type != ItemType.comment) ...[ - MetadataWidget( - icon: Icons.arrow_upward_outlined, - label: LoadingTextBlock( - width: 14, - style: textTheme.bodySmall, - hasLeading: false, - ), - color: color, - ), - MetadataWidget( - icon: Icons.chat_bubble_outline_outlined, - label: LoadingTextBlock( - width: 14, - style: textTheme.bodySmall, - hasLeading: false, - ), - color: color, - ), - ], - ElevatedButton.icon( - onPressed: null, - style: ElevatedButton.styleFrom( - padding: ButtonStyleButton.scaledPadding( - const EdgeInsets.symmetric(horizontal: AppSpacing.l), - const EdgeInsets.symmetric(horizontal: AppSpacing.m), - const EdgeInsets.symmetric(horizontal: AppSpacing.s), - // ignore: deprecated_member_use - MediaQuery.textScalerOf(context).textScaleFactor, - ), - visualDensity: const VisualDensity( - horizontal: VisualDensity.minimumDensity, - vertical: VisualDensity.minimumDensity, - ), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - icon: const LoadingBlock( - width: 14, - height: 14, - ), - label: LoadingTextBlock( - width: 63, - style: textTheme.bodySmall, - hasLeading: false, - ), - ), - if (collapsedCount != null) - MetadataWidget( - icon: Icons.add_circle_outline_outlined, - color: color, - ), - const Spacer(), - MetadataWidget( - label: LoadingTextBlock( - width: 70, - style: textTheme.bodySmall, - hasLeading: false, - ), - ), - ].spaced(width: AppSpacing.m), - ), + if (showMetadata) _buildMetadata(context), ].spaced(height: AppSpacing.m), ); } + Widget _buildMetadata(BuildContext context) { + final textTheme = Theme.of(context).textTheme; + final color = + Theme.of(context).colorScheme.outline.withOpacity(LoadingBlock.opacity); + return Row( + children: [ + if (type != ItemType.comment) ...[ + MetadataWidget( + icon: Icons.arrow_upward_outlined, + label: LoadingTextBlock( + width: 14, + style: textTheme.bodySmall, + hasLeading: false, + ), + color: color, + ), + MetadataWidget( + icon: Icons.chat_bubble_outline_outlined, + label: LoadingTextBlock( + width: 14, + style: textTheme.bodySmall, + hasLeading: false, + ), + color: color, + ), + ], + ElevatedButton.icon( + onPressed: null, + style: ElevatedButton.styleFrom( + padding: ButtonStyleButton.scaledPadding( + const EdgeInsets.symmetric(horizontal: AppSpacing.l), + const EdgeInsets.symmetric(horizontal: AppSpacing.m), + const EdgeInsets.symmetric(horizontal: AppSpacing.s), + // ignore: deprecated_member_use + MediaQuery.textScalerOf(context).textScaleFactor, + ), + visualDensity: const VisualDensity( + horizontal: VisualDensity.minimumDensity, + vertical: VisualDensity.minimumDensity, + ), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + icon: const LoadingBlock( + width: 14, + height: 14, + ), + label: LoadingTextBlock( + width: 63, + style: textTheme.bodySmall, + hasLeading: false, + ), + ), + if (collapsedCount != null) + MetadataWidget( + icon: Icons.add_circle_outline_outlined, + color: color, + ), + const Spacer(), + MetadataWidget( + label: LoadingTextBlock( + width: 70, + style: textTheme.bodySmall, + hasLeading: false, + ), + ), + ].spaced(width: AppSpacing.m), + ); + } + Widget _buildSecondary(BuildContext context) { final textTheme = Theme.of(context).textTheme; return Column( From fb37f06498ab0eff7198cb0dae20d7ab68c588a3 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 19 Dec 2023 14:19:09 +0100 Subject: [PATCH 113/145] Tighten space between title and metadata --- lib/common/widgets/refreshable_scroll_view.dart | 4 ++-- lib/item/view/item_page.dart | 2 +- lib/item/widgets/item_data_tile.dart | 2 +- lib/item/widgets/item_loading_tile.dart | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/common/widgets/refreshable_scroll_view.dart b/lib/common/widgets/refreshable_scroll_view.dart index c878c5b1..78bec1f1 100644 --- a/lib/common/widgets/refreshable_scroll_view.dart +++ b/lib/common/widgets/refreshable_scroll_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; // This value happens to fit a page worth of items (30) with the standard height -// of an item in the stories overview (96). It does not appear to have a +// of an item in the stories overview (92). It does not appear to have a // significant negative impact on initial load performance, while making // scrolling noticably smoother on most affected pages compared to the default. -const _cacheExtent = 2880.0; +const _cacheExtent = 2760.0; class RefreshableScrollView extends StatelessWidget { const RefreshableScrollView({ diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index e6373954..9dd00710 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -91,7 +91,7 @@ class _ItemPageState extends State { } static double _getToolbarHeight({required bool useLargeStoryStyle}) => - useLargeStoryStyle ? 96 : 88; + useLargeStoryStyle ? 92 : 84; @override Widget build(BuildContext context) { diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 22b2e8ca..ffa27278 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -177,7 +177,7 @@ class ItemDataTile extends StatelessWidget { ].spaced(width: AppSpacing.xl), ), if (showMetadata) _buildMetadata(context), - ].spaced(height: AppSpacing.m), + ].spaced(height: AppSpacing.s), ); } diff --git a/lib/item/widgets/item_loading_tile.dart b/lib/item/widgets/item_loading_tile.dart index 3d5fa4ed..d07dda46 100644 --- a/lib/item/widgets/item_loading_tile.dart +++ b/lib/item/widgets/item_loading_tile.dart @@ -76,7 +76,7 @@ class ItemLoadingTile extends StatelessWidget { ].spaced(width: AppSpacing.xl), ), if (showMetadata) _buildMetadata(context), - ].spaced(height: AppSpacing.m), + ].spaced(height: AppSpacing.s), ); } From 9dfaeb89ffac494fce7c39edfefda7ff7602fbeb Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 23 Dec 2023 20:52:50 +0100 Subject: [PATCH 114/145] Eliminate some deprecated member usages --- lib/app/extensions/text_scaler_extension.dart | 11 ++++ lib/app/view/app.dart | 10 ++-- lib/item/widgets/item_loading_tile.dart | 7 ++- lib/item/widgets/username_widget.dart | 57 ++++++++----------- 4 files changed, 45 insertions(+), 40 deletions(-) create mode 100644 lib/app/extensions/text_scaler_extension.dart diff --git a/lib/app/extensions/text_scaler_extension.dart b/lib/app/extensions/text_scaler_extension.dart new file mode 100644 index 00000000..cbf20e89 --- /dev/null +++ b/lib/app/extensions/text_scaler_extension.dart @@ -0,0 +1,11 @@ +import 'package:flutter/rendering.dart'; + +extension TextScalerExtension on TextScaler { + double getFontSizeMultiplier({ + required double? fontSize, + required double fallbackFontSize, + }) { + final defaultFontSize = fontSize ?? fallbackFontSize; + return scale(defaultFontSize) / fallbackFontSize; + } +} diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 68515b00..c5d819c7 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -62,8 +62,6 @@ class App extends StatelessWidget { ColorScheme? dynamicColorScheme, Brightness brightness, ) { - // ignore: deprecated_member_use - final textScaleFactor = MediaQuery.textScalerOf(context).textScaleFactor; final backgroundColor = state.usePureBackground ? brightness == Brightness.dark ? Colors.black @@ -83,16 +81,17 @@ class App extends StatelessWidget { color: backgroundColor, centerTitle: false, ), - // Badges do not handle text scaling by default. badgeTheme: BadgeThemeData( - smallSize: 6 * textScaleFactor, - largeSize: 16 * textScaleFactor, + // Badges do not handle text scaling by default. + smallSize: MediaQuery.textScalerOf(context).scale(6), + largeSize: MediaQuery.textScalerOf(context).scale(16), ), bottomSheetTheme: const BottomSheetThemeData( showDragHandle: true, // Material 3 dictates a maximum width for bottom sheets. constraints: BoxConstraints(maxWidth: 640), ), + inputDecorationTheme: const InputDecorationTheme(filled: true), menuButtonTheme: const MenuButtonThemeData( style: ButtonStyle( padding: MaterialStatePropertyAll( @@ -100,7 +99,6 @@ class App extends StatelessWidget { ), ), ), - inputDecorationTheme: const InputDecorationTheme(filled: true), menuTheme: const MenuThemeData( style: MenuStyle( shape: MaterialStatePropertyAll( diff --git a/lib/item/widgets/item_loading_tile.dart b/lib/item/widgets/item_loading_tile.dart index d07dda46..517526b7 100644 --- a/lib/item/widgets/item_loading_tile.dart +++ b/lib/item/widgets/item_loading_tile.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:glider/app/extensions/text_scaler_extension.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; import 'package:glider/common/widgets/loading_block.dart'; @@ -113,8 +114,10 @@ class ItemLoadingTile extends StatelessWidget { const EdgeInsets.symmetric(horizontal: AppSpacing.l), const EdgeInsets.symmetric(horizontal: AppSpacing.m), const EdgeInsets.symmetric(horizontal: AppSpacing.s), - // ignore: deprecated_member_use - MediaQuery.textScalerOf(context).textScaleFactor, + MediaQuery.textScalerOf(context).getFontSizeMultiplier( + fontSize: Theme.of(context).textTheme.labelLarge?.fontSize, + fallbackFontSize: 14, + ), ), visualDensity: const VisualDensity( horizontal: VisualDensity.minimumDensity, diff --git a/lib/item/widgets/username_widget.dart b/lib/item/widgets/username_widget.dart index c817f93a..d754e043 100644 --- a/lib/item/widgets/username_widget.dart +++ b/lib/item/widgets/username_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:glider/app/extensions/text_scaler_extension.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/item/widgets/avatar_widget.dart'; @@ -24,14 +25,30 @@ class UsernameWidget extends StatelessWidget { const EdgeInsets.symmetric(horizontal: AppSpacing.l), const EdgeInsets.symmetric(horizontal: AppSpacing.m), const EdgeInsets.symmetric(horizontal: AppSpacing.s), - // ignore: deprecated_member_use - MediaQuery.textScalerOf(context).textScaleFactor, + MediaQuery.textScalerOf(context).getFontSizeMultiplier( + fontSize: Theme.of(context).textTheme.labelLarge?.fontSize, + fallbackFontSize: 14, + ), ); const visualDensity = VisualDensity( horizontal: VisualDensity.minimumDensity, vertical: VisualDensity.minimumDensity, ); const tapTargetSize = MaterialTapTargetSize.shrinkWrap; + final buttonStyle = switch (style) { + UsernameStyle.loggedInUser || + UsernameStyle.storyUser => + FilledButton.styleFrom( + padding: padding, + visualDensity: visualDensity, + tapTargetSize: tapTargetSize, + ), + UsernameStyle.none => ElevatedButton.styleFrom( + padding: padding, + visualDensity: visualDensity, + tapTargetSize: tapTargetSize, + ), + }; final icon = AvatarWidget(username: username); final label = Text( username, @@ -43,64 +60,40 @@ class UsernameWidget extends StatelessWidget { UsernameStyle.loggedInUser when showAvatar => FilledButton.icon( onPressed: onPressed, onLongPress: onLongPress, - style: FilledButton.styleFrom( - padding: padding, - visualDensity: visualDensity, - tapTargetSize: tapTargetSize, - ), + style: buttonStyle, icon: icon, label: label, ), UsernameStyle.loggedInUser => FilledButton( onPressed: onPressed, onLongPress: onLongPress, - style: FilledButton.styleFrom( - padding: padding, - visualDensity: visualDensity, - tapTargetSize: tapTargetSize, - ), + style: buttonStyle, child: label, ), UsernameStyle.storyUser when showAvatar => FilledButton.tonalIcon( onPressed: onPressed, onLongPress: onLongPress, - style: FilledButton.styleFrom( - padding: padding, - visualDensity: visualDensity, - tapTargetSize: tapTargetSize, - ), + style: buttonStyle, icon: icon, label: label, ), UsernameStyle.storyUser => FilledButton( onPressed: onPressed, onLongPress: onLongPress, - style: FilledButton.styleFrom( - padding: padding, - visualDensity: visualDensity, - tapTargetSize: tapTargetSize, - ), + style: buttonStyle, child: label, ), UsernameStyle.none when showAvatar => ElevatedButton.icon( onPressed: onPressed, onLongPress: onLongPress, - style: ElevatedButton.styleFrom( - padding: padding, - visualDensity: visualDensity, - tapTargetSize: tapTargetSize, - ), + style: buttonStyle, icon: icon, label: label, ), UsernameStyle.none => ElevatedButton( onPressed: onPressed, onLongPress: onLongPress, - style: ElevatedButton.styleFrom( - padding: padding, - visualDensity: visualDensity, - tapTargetSize: tapTargetSize, - ), + style: buttonStyle, child: label, ), }; From f9a0d9fbed83b61d705225273587faafa29f1dc5 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 23 Dec 2023 21:14:19 +0100 Subject: [PATCH 115/145] Fix action buttons setting not taking effect immediately --- lib/inbox/view/inbox_shell_page.dart | 70 +++++++++++++--------------- lib/item/widgets/item_tile.dart | 1 + 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 68aba11f..340e4121 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -136,51 +136,45 @@ class _SliverInboxBody extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( bloc: _inboxCubit, - builder: (context, state) => BlocBuilder( - bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.useActionButtons != current.useActionButtons, - builder: (context, settingsState) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( - itemBuilder: (context, index) => - const ItemLoadingTile(type: ItemType.comment), - ), - nonEmpty: () => SliverList.builder( - itemCount: state.data!.length, - itemBuilder: (context, index) { - final (parentId, id) = state.data![index]; - return Column( - children: [ - ItemTile.create( + builder: (context, state) => state.whenOrDefaultSlivers( + loading: () => SliverList.builder( + itemBuilder: (context, index) => + const ItemLoadingTile(type: ItemType.comment), + ), + nonEmpty: () => SliverList.builder( + itemCount: state.data!.length, + itemBuilder: (context, index) { + final (parentId, id) = state.data![index]; + return Column( + children: [ + ItemTile.create( + _itemCubitFactory, + _authCubit, + _settingsCubit, + id: parentId, + loadingType: ItemType.story, + onTap: (context, item) async => context.push( + AppRoute.item.location(parameters: {'id': parentId}), + ), + ), + IndentedWidget( + depth: 1, + child: ItemTile.create( _itemCubitFactory, _authCubit, _settingsCubit, - id: parentId, - loadingType: ItemType.story, + id: id, + loadingType: ItemType.comment, onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': parentId}), + AppRoute.item.location(parameters: {'id': id}), ), ), - IndentedWidget( - depth: 1, - child: ItemTile.create( - _itemCubitFactory, - _authCubit, - _settingsCubit, - id: id, - loadingType: ItemType.comment, - onTap: (context, item) async => context.push( - AppRoute.item.location(parameters: {'id': id}), - ), - ), - ), - ], - ); - }, - ), - onRetry: () async => _inboxCubit.load(), + ), + ], + ); + }, ), + onRetry: () async => _inboxCubit.load(), ), ); } diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index a49b11b5..39d7c462 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -112,6 +112,7 @@ class _ItemTileState extends State previous.useLargeStoryStyle != current.useLargeStoryStyle || previous.showFavicons != current.showFavicons || previous.showUserAvatars != current.showUserAvatars || + previous.useActionButtons != current.useActionButtons || previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => AnimatedSize( alignment: Alignment.topCenter, From aa3b9a928d929c8161e7ef750894f815f57fc407 Mon Sep 17 00:00:00 2001 From: Mosc Date: Sat, 23 Dec 2023 21:24:39 +0100 Subject: [PATCH 116/145] Upgrade dependencies --- pubspec.lock | 36 ++++++++++++++++++------------------ pubspec.yaml | 6 +++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 66f1a018..9ad3706b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -261,10 +261,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f" + sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b url: "https://pub.dev" source: hosted - version: "1.6.8" + version: "1.6.9" equatable: dependency: "direct main" description: @@ -330,18 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: "585348d7682f3a59fe6c63f895cb67d28f078815794f16d1f3943fd5b4718e20" + sha256: "69cd9b5fa2c5dba20d896df0aa7d6300c2fca0459fda685266c0ad3cf1486d1d" url: "https://pub.dev" source: hosted - version: "6.0.0-beta.32" + version: "6.0.0-rc.3" flutter_inappwebview_android: dependency: transitive description: name: flutter_inappwebview_android - sha256: "29d493ec6fbb11788d1d79394287f13c7c362e6862726d1c922cb1f0b7ec92c4" + sha256: "3cb7c49b51faab8b0938910b707fc10e8c9538e5cd9fc1da03b6b47dfa4340cc" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.11" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -354,34 +354,34 @@ packages: dependency: transitive description: name: flutter_inappwebview_ios - sha256: "2ed0639516a6a13209871fcc9bc21db03cb9dd8f93a6125cc6c9deb14db38ce6" + sha256: "548f393fa06385e6d8621778a3012eb5e66fa11d7bea0a7f4ae9c37bb1a97a84" url: "https://pub.dev" source: hosted - version: "1.0.9" + version: "1.0.12" flutter_inappwebview_macos: dependency: transitive description: name: flutter_inappwebview_macos - sha256: "23aea85f9e16ce6671696c95b3eae6b53912264e18c07934ad16457c2f152ff0" + sha256: c2ed011d45d7c536cd4ce4201160658e68912b764cb2894f73183c3cd796b5ee url: "https://pub.dev" source: hosted - version: "1.0.7" + version: "1.0.10" flutter_inappwebview_platform_interface: dependency: transitive description: name: flutter_inappwebview_platform_interface - sha256: b9941cb6c358f50ab4a890a6301e50a6cfca20e70408627ec34bc69fddd85ca7 + sha256: "9b0bce22162f05af36dc7480dbe43650414704591df263810d773a7ba4139447" url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.9" flutter_inappwebview_web: dependency: transitive description: name: flutter_inappwebview_web - sha256: "6a2b5c0496f8f8260e3564d4323deccb7635368477576710e4f03879a1c4a2b7" + sha256: "1d8c6574a17747f472b039393f365e39d594945f795b3b178abd8ce22c4c836c" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.7" flutter_launcher_icons: dependency: "direct dev" description: @@ -503,10 +503,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15 + sha256: ca7e4a2249f96773152f1853fa25933ac752495cdd7fdf5dafb9691bd05830fd url: "https://pub.dev" source: hosted - version: "12.1.3" + version: "13.0.0" google_fonts: dependency: "direct main" description: @@ -1052,10 +1052,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b0198f29..3082155b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,14 +12,14 @@ dependencies: clock: ^1.1.1 collection: any device_info_plus: ^9.1.1 - dynamic_color: ^1.6.8 + dynamic_color: ^1.6.9 equatable: ^2.0.5 flutter: sdk: flutter flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-beta.32 + flutter_inappwebview: ^6.0.0-rc.3 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 @@ -29,7 +29,7 @@ dependencies: path: packages/glider_data glider_domain: path: packages/glider_domain - go_router: ^12.1.3 + go_router: ^13.0.0 google_fonts: ^6.1.0 http: ^1.1.2 hydrated_bloc: ^9.1.3 From 5cab7e9051bde4f93d349589e28cea58d18103cf Mon Sep 17 00:00:00 2001 From: Mosc Date: Sun, 31 Dec 2023 22:03:57 +0100 Subject: [PATCH 117/145] Shorten setting name --- lib/l10n/arb/app_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 482dc5a0..c2c0f940 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -34,7 +34,7 @@ "showJobsDescription": "Also shows jobs tab", "threadNavigation": "Thread navigation", "threadNavigationDescription": "Allows jumps between top-level comments", - "downvoting": "Enable downvoting", + "downvoting": "Downvoting", "downvotingDescription": "Requires an account with 501+ karma", "inAppBrowser": "Custom tabs", "inAppBrowserDescription": "Opens links in in-app browser if possible", From 1b79c531f72e73686aecf6ba3ded16dd61317a58 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Jan 2024 07:42:03 +0100 Subject: [PATCH 118/145] Speed up workflows with caching --- .github/actions/bootstrap/action.yml | 50 ++++++++++++++++++++++------ .github/workflows/ci.yml | 6 ++++ .github/workflows/release.yml | 8 ++++- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index a8569025..621f0141 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -1,30 +1,58 @@ name: Bootstrap description: Bootstrap workspace +inputs: + fvm-version: + description: FVM version + required: false + default: 3.0.0-beta.5 runs: using: composite steps: - - name: Set up Java - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - cache: gradle - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@master + - name: Disable Homebrew analytics + shell: bash + run: brew analytics off + - name: Get Homebrew directory + shell: bash + run: echo "HOMEBREW_CELLAR=$(brew --cellar)" >> $GITHUB_ENV + - name: Cache Homebrew + uses: actions/cache@v3 + with: + path: | + ${{ env.HOMEBREW_CELLAR }}/dart + ${{ env.HOMEBREW_CELLAR }}/fvm@${{ inputs.fvm-version }} + key: ${{ runner.os }}-homebrew-fvm-${{ inputs.fvm-version }} + restore-keys: ${{ runner.os }}-homebrew-fvm- + - name: Cache FVM + uses: actions/cache@v3 + with: + path: ~/fvm + key: ${{ runner.os }}-fvm-${{ hashFiles('.fvmrc') }} + restore-keys: ${{ runner.os }}-fvm- + - name: Cache pub + uses: actions/cache@v3 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} + restore-keys: ${{ runner.os }}-pub- - name: Set up FVM + shell: bash run: | brew tap leoafarias/fvm - brew install fvm@3.0.0-beta.5 - shell: bash + brew link dart fvm@${{ inputs.fvm-version }} || brew install fvm@${{ inputs.fvm-version }} + env: + HOMEBREW_NO_ENV_HINTS: '1' - name: Set up Flutter + shell: bash run: | fvm install + fvm dart --disable-analytics fvm flutter config --disable-analytics echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH - shell: bash - name: Set up Melos - run: fvm dart pub global activate melos shell: bash + run: fvm dart pub global activate melos - name: Bootstrap workspace - run: fvm flutter pub global run melos bootstrap shell: bash + run: fvm flutter pub global run melos bootstrap diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5b5633f..ed0a820a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,12 @@ jobs: fetch-depth: 0 - name: Bootstrap workspace uses: ./.github/actions/bootstrap + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + cache: gradle - name: Run static analysis checks run: melos lint - name: Build Android APK (profile) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96e15a8c..2ca2f8da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,12 @@ jobs: fetch-depth: 0 - name: Bootstrap workspace uses: ./.github/actions/bootstrap + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + cache: gradle - name: Get Android keystore id: android-keystore uses: timheuer/base64-to-file@v1 @@ -86,7 +92,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.2 + ruby-version: 3.3 bundler-cache: true - name: Get artifact id: download-artifact From 2413fb25f7fe9fe01405da8f4805ee040496244c Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Jan 2024 07:44:37 +0100 Subject: [PATCH 119/145] Upgrade dependencies --- pubspec.lock | 28 ++++++++++++++-------------- pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 9ad3706b..65029a5c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -330,18 +330,18 @@ packages: dependency: "direct main" description: name: flutter_inappwebview - sha256: "69cd9b5fa2c5dba20d896df0aa7d6300c2fca0459fda685266c0ad3cf1486d1d" + sha256: "3e9a443a18ecef966fb930c3a76ca5ab6a7aafc0c7b5e14a4a850cf107b09959" url: "https://pub.dev" source: hosted - version: "6.0.0-rc.3" + version: "6.0.0" flutter_inappwebview_android: dependency: transitive description: name: flutter_inappwebview_android - sha256: "3cb7c49b51faab8b0938910b707fc10e8c9538e5cd9fc1da03b6b47dfa4340cc" + sha256: fd4db51e46f49b140d83a3206851432c54ea920b381137c0ba82d0cf59be1dee url: "https://pub.dev" source: hosted - version: "1.0.11" + version: "1.0.12" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -354,34 +354,34 @@ packages: dependency: transitive description: name: flutter_inappwebview_ios - sha256: "548f393fa06385e6d8621778a3012eb5e66fa11d7bea0a7f4ae9c37bb1a97a84" + sha256: f363577208b97b10b319cd0c428555cd8493e88b468019a8c5635a0e4312bd0f url: "https://pub.dev" source: hosted - version: "1.0.12" + version: "1.0.13" flutter_inappwebview_macos: dependency: transitive description: name: flutter_inappwebview_macos - sha256: c2ed011d45d7c536cd4ce4201160658e68912b764cb2894f73183c3cd796b5ee + sha256: b55b9e506c549ce88e26580351d2c71d54f4825901666bd6cfa4be9415bb2636 url: "https://pub.dev" source: hosted - version: "1.0.10" + version: "1.0.11" flutter_inappwebview_platform_interface: dependency: transitive description: name: flutter_inappwebview_platform_interface - sha256: "9b0bce22162f05af36dc7480dbe43650414704591df263810d773a7ba4139447" + sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187" url: "https://pub.dev" source: hosted - version: "1.0.9" + version: "1.0.10" flutter_inappwebview_web: dependency: transitive description: name: flutter_inappwebview_web - sha256: "1d8c6574a17747f472b039393f365e39d594945f795b3b178abd8ce22c4c836c" + sha256: d8c680abfb6fec71609a700199635d38a744df0febd5544c5a020bd73de8ee07 url: "https://pub.dev" source: hosted - version: "1.0.7" + version: "1.0.8" flutter_launcher_icons: dependency: "direct dev" description: @@ -751,10 +751,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3082155b..f6103769 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_adaptive_scaffold: ^0.1.7+1 flutter_bloc: ^8.1.3 flutter_displaymode: ^0.6.0 - flutter_inappwebview: ^6.0.0-rc.3 + flutter_inappwebview: ^6.0.0 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.18+2 From 27902b733269787883c64b29f0b86fd38303a14a Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Jan 2024 08:00:39 +0100 Subject: [PATCH 120/145] Bump version --- fastlane/metadata/android/en-US/changelogs/49.txt | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/49.txt diff --git a/fastlane/metadata/android/en-US/changelogs/49.txt b/fastlane/metadata/android/en-US/changelogs/49.txt new file mode 100644 index 00000000..064f8db6 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/49.txt @@ -0,0 +1,3 @@ +- Added setting to change navigation bar or rail into navigation drawer +- Improved pure background setting to apply to more UI elemeents +- Changed padding between story title and metadata to be a bit tighter \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index f6103769..6b368d25 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: glider description: An opinionated Hacker News client. -version: 2.7.0+48 +version: 2.8.0+49 publish_to: none environment: From fb3000e381407fa6d63b6d416feb5108ee948048 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 4 Jan 2024 22:28:09 +0100 Subject: [PATCH 121/145] Rename `fromJson`/`toJson` to `fromMap`/`toMap` --- lib/edit/cubit/edit_cubit.dart | 4 ++-- lib/edit/cubit/edit_state.dart | 6 +++--- lib/favorites/cubit/favorites_cubit.dart | 4 ++-- lib/favorites/cubit/favorites_state.dart | 4 ++-- lib/inbox/cubit/inbox_cubit.dart | 4 ++-- lib/inbox/cubit/inbox_state.dart | 4 ++-- lib/item/cubit/item_cubit.dart | 4 ++-- lib/item/cubit/item_state.dart | 8 ++++---- lib/item_tree/cubit/item_tree_cubit.dart | 4 ++-- lib/item_tree/cubit/item_tree_state.dart | 12 ++++++------ lib/reply/cubit/reply_cubit.dart | 4 ++-- lib/reply/cubit/reply_state.dart | 6 +++--- lib/settings/view/settings_page.dart | 4 +--- lib/stories/cubit/stories_cubit.dart | 4 ++-- lib/stories/cubit/stories_state.dart | 4 ++-- lib/stories_search/bloc/stories_search_bloc.dart | 4 ++-- lib/stories_search/bloc/stories_search_state.dart | 4 ++-- lib/story_similar/cubit/story_similar_cubit.dart | 4 ++-- lib/story_similar/cubit/story_similar_state.dart | 6 +++--- lib/submit/cubit/submit_cubit.dart | 5 ++--- lib/submit/cubit/submit_state.dart | 4 ++-- lib/user/cubit/user_cubit.dart | 4 ++-- lib/user/cubit/user_state.dart | 8 ++++---- .../glider_data/lib/src/algolia_api_service.dart | 10 +++++----- .../glider_data/lib/src/dtos/algolia_search_dto.dart | 6 +++--- packages/glider_data/lib/src/dtos/item_dto.dart | 2 +- packages/glider_data/lib/src/dtos/user_dto.dart | 2 +- .../glider_data/lib/src/hacker_news_api_service.dart | 4 ++-- packages/glider_domain/lib/src/entities/item.dart | 4 ++-- .../lib/src/entities/item_descendant.dart | 4 ++-- packages/glider_domain/lib/src/entities/user.dart | 4 ++-- 31 files changed, 74 insertions(+), 77 deletions(-) diff --git a/lib/edit/cubit/edit_cubit.dart b/lib/edit/cubit/edit_cubit.dart index f75b26bf..9feb03c1 100644 --- a/lib/edit/cubit/edit_cubit.dart +++ b/lib/edit/cubit/edit_cubit.dart @@ -85,10 +85,10 @@ class EditCubit extends HydratedCubit { } @override - EditState? fromJson(Map json) => EditState.fromJson(json); + EditState? fromJson(Map json) => EditState.fromMap(json); @override - Map? toJson(EditState state) => state.toJson(); + Map? toJson(EditState state) => state.toMap(); @override Future close() async { diff --git a/lib/edit/cubit/edit_state.dart b/lib/edit/cubit/edit_state.dart index 7d7c6314..deabf909 100644 --- a/lib/edit/cubit/edit_state.dart +++ b/lib/edit/cubit/edit_state.dart @@ -10,9 +10,9 @@ class EditState with EquatableMixin { this.success = false, }); - factory EditState.fromJson(Map json) => EditState( + factory EditState.fromMap(Map json) => EditState( item: json['item'] != null - ? Item.fromJson(json['item'] as Map) + ? Item.fromMap(json['item'] as Map) : null, title: json['title'] != null ? TitleInput.pure(json['title'] as String) @@ -23,7 +23,7 @@ class EditState with EquatableMixin { isValid: json['isValid'] as bool? ?? false, ); - Map toJson() => { + Map toMap() => { 'item': item, 'title': title?.value, 'text': text?.value, diff --git a/lib/favorites/cubit/favorites_cubit.dart b/lib/favorites/cubit/favorites_cubit.dart index 598020a8..ec22eb16 100644 --- a/lib/favorites/cubit/favorites_cubit.dart +++ b/lib/favorites/cubit/favorites_cubit.dart @@ -44,11 +44,11 @@ class FavoritesCubit extends HydratedCubit { @override FavoritesState? fromJson(Map json) => - FavoritesState.fromJson(json); + FavoritesState.fromMap(json); @override Map? toJson(FavoritesState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; @override Future close() async { diff --git a/lib/favorites/cubit/favorites_state.dart b/lib/favorites/cubit/favorites_state.dart index 4a66d9ab..2a108fa0 100644 --- a/lib/favorites/cubit/favorites_state.dart +++ b/lib/favorites/cubit/favorites_state.dart @@ -7,14 +7,14 @@ class FavoritesState with DataMixin>, EquatableMixin { this.exception, }); - factory FavoritesState.fromJson(Map json) => FavoritesState( + factory FavoritesState.fromMap(Map json) => FavoritesState( status: Status.values.byName(json['status'] as String), data: (json['data'] as List?) ?.map((e) => e as int) .toList(growable: false), ); - Map toJson() => { + Map toMap() => { 'status': status.name, 'data': data, }; diff --git a/lib/inbox/cubit/inbox_cubit.dart b/lib/inbox/cubit/inbox_cubit.dart index d1f12be2..7c44f03f 100644 --- a/lib/inbox/cubit/inbox_cubit.dart +++ b/lib/inbox/cubit/inbox_cubit.dart @@ -43,9 +43,9 @@ class InboxCubit extends HydratedCubit { } @override - InboxState? fromJson(Map json) => InboxState.fromJson(json); + InboxState? fromJson(Map json) => InboxState.fromMap(json); @override Map? toJson(InboxState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; } diff --git a/lib/inbox/cubit/inbox_state.dart b/lib/inbox/cubit/inbox_state.dart index 63878c62..1daa6f7b 100644 --- a/lib/inbox/cubit/inbox_state.dart +++ b/lib/inbox/cubit/inbox_state.dart @@ -9,7 +9,7 @@ class InboxState with DataMixin>, EquatableMixin { this.exception, }); - factory InboxState.fromJson(Map json) => InboxState( + factory InboxState.fromMap(Map json) => InboxState( status: Status.values.byName(json['status'] as String), data: (json['data'] as List?) ?.map((e) => e as Map) @@ -17,7 +17,7 @@ class InboxState with DataMixin>, EquatableMixin { .toList(growable: false), ); - Map toJson() => { + Map toMap() => { 'status': status.name, 'data': data ?.map((e) => {'parentId': e.$1, 'id': e.$2}) diff --git a/lib/item/cubit/item_cubit.dart b/lib/item/cubit/item_cubit.dart index 3b2ee4e7..4a26957e 100644 --- a/lib/item/cubit/item_cubit.dart +++ b/lib/item/cubit/item_cubit.dart @@ -240,11 +240,11 @@ class ItemCubit extends HydratedCubit } @override - ItemState? fromJson(Map json) => ItemState.fromJson(json); + ItemState? fromJson(Map json) => ItemState.fromMap(json); @override Map? toJson(ItemState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; @override Future close() async { diff --git a/lib/item/cubit/item_state.dart b/lib/item/cubit/item_state.dart index 6a27f8a2..e5864264 100644 --- a/lib/item/cubit/item_state.dart +++ b/lib/item/cubit/item_state.dart @@ -13,9 +13,9 @@ class ItemState with DataMixin, EquatableMixin { this.exception, }); - factory ItemState.fromJson(Map json) => ItemState( + factory ItemState.fromMap(Map json) => ItemState( status: Status.values.byName(json['status'] as String), - data: Item.fromJson(json['data'] as Map), + data: Item.fromMap(json['data'] as Map), visited: json['visited'] as bool? ?? false, vote: json['voted'] != null ? VoteType.values.byName(json['voted'] as String) @@ -25,9 +25,9 @@ class ItemState with DataMixin, EquatableMixin { blocked: json['blocked'] as bool? ?? false, ); - Map toJson() => { + Map toMap() => { 'status': status.name, - 'data': data?.toJson(), + 'data': data?.toMap(), 'voted': vote?.name, 'favorited': favorited, 'flagged': flagged, diff --git a/lib/item_tree/cubit/item_tree_cubit.dart b/lib/item_tree/cubit/item_tree_cubit.dart index 8fd70477..a8487f35 100644 --- a/lib/item_tree/cubit/item_tree_cubit.dart +++ b/lib/item_tree/cubit/item_tree_cubit.dart @@ -82,9 +82,9 @@ class ItemTreeCubit extends HydratedCubit { @override ItemTreeState? fromJson(Map json) => - ItemTreeState.fromJson(json); + ItemTreeState.fromMap(json); @override Map? toJson(ItemTreeState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; } diff --git a/lib/item_tree/cubit/item_tree_state.dart b/lib/item_tree/cubit/item_tree_state.dart index 088aff9a..a94f482a 100644 --- a/lib/item_tree/cubit/item_tree_state.dart +++ b/lib/item_tree/cubit/item_tree_state.dart @@ -9,24 +9,24 @@ class ItemTreeState with DataMixin>, EquatableMixin { this.exception, }); - factory ItemTreeState.fromJson(Map json) => ItemTreeState( + factory ItemTreeState.fromMap(Map json) => ItemTreeState( status: Status.values.byName(json['status'] as String), data: (json['data'] as List?) - ?.map((e) => ItemDescendant.fromJson(e as Map)) + ?.map((e) => ItemDescendant.fromMap(e as Map)) .toList(growable: false), previousData: (json['previousData'] as List?) - ?.map((e) => ItemDescendant.fromJson(e as Map)) + ?.map((e) => ItemDescendant.fromMap(e as Map)) .toList(growable: false), collapsedIds: (json['collapsedIds'] as List) .map((e) => e as int) .toSet(), ); - Map toJson() => { + Map toMap() => { 'status': status.name, - 'data': data?.map((e) => e.toJson()).toList(growable: false), + 'data': data?.map((e) => e.toMap()).toList(growable: false), 'previousData': - previousData?.map((e) => e.toJson()).toList(growable: false), + previousData?.map((e) => e.toMap()).toList(growable: false), 'collapsedIds': collapsedIds.toList(growable: false), }; diff --git a/lib/reply/cubit/reply_cubit.dart b/lib/reply/cubit/reply_cubit.dart index 261e2eae..594c917f 100644 --- a/lib/reply/cubit/reply_cubit.dart +++ b/lib/reply/cubit/reply_cubit.dart @@ -71,10 +71,10 @@ class ReplyCubit extends HydratedCubit { } @override - ReplyState? fromJson(Map json) => ReplyState.fromJson(json); + ReplyState? fromJson(Map json) => ReplyState.fromMap(json); @override - Map? toJson(ReplyState state) => state.toJson(); + Map? toJson(ReplyState state) => state.toMap(); @override Future close() async { diff --git a/lib/reply/cubit/reply_state.dart b/lib/reply/cubit/reply_state.dart index a5e3ec01..a51c6f22 100644 --- a/lib/reply/cubit/reply_state.dart +++ b/lib/reply/cubit/reply_state.dart @@ -9,15 +9,15 @@ class ReplyState with EquatableMixin { this.success = false, }); - factory ReplyState.fromJson(Map json) => ReplyState( + factory ReplyState.fromMap(Map json) => ReplyState( parentItem: json['parentItem'] != null - ? Item.fromJson(json['parentItem'] as Map) + ? Item.fromMap(json['parentItem'] as Map) : null, text: TextInput.pure(json['text'] as String? ?? ''), isValid: json['isValid'] as bool? ?? false, ); - Map toJson() => { + Map toMap() => { 'parentItem': parentItem, 'text': text.value, 'isValid': isValid, diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 506b2319..2894ce76 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -17,7 +17,6 @@ import 'package:glider/settings/extensions/variant_extension.dart'; import 'package:glider/settings/widgets/menu_list_tile.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:go_router/go_router.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:material_color_utilities/material_color_utilities.dart'; class SettingsPage extends StatelessWidget { @@ -151,8 +150,7 @@ class _SettingsBody extends StatelessWidget { onChanged: (value) async => _settingsCubit.setFont(value), values: _fonts, selected: (value) => state.font == value, - childBuilder: (value) => - Text(value, style: GoogleFonts.getFont(value)), + childBuilder: Text.new, ), const Divider(), Padding( diff --git a/lib/stories/cubit/stories_cubit.dart b/lib/stories/cubit/stories_cubit.dart index 108e56d8..3e0df0fb 100644 --- a/lib/stories/cubit/stories_cubit.dart +++ b/lib/stories/cubit/stories_cubit.dart @@ -97,9 +97,9 @@ class StoriesCubit extends HydratedCubit { @override StoriesState? fromJson(Map json) => - StoriesState.fromJson(json); + StoriesState.fromMap(json); @override Map? toJson(StoriesState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; } diff --git a/lib/stories/cubit/stories_state.dart b/lib/stories/cubit/stories_state.dart index e3989a5b..388754fd 100644 --- a/lib/stories/cubit/stories_state.dart +++ b/lib/stories/cubit/stories_state.dart @@ -10,7 +10,7 @@ class StoriesState this.exception, }); - factory StoriesState.fromJson(Map json) => StoriesState( + factory StoriesState.fromMap(Map json) => StoriesState( status: Status.values.byName(json['status'] as String), data: (json['data'] as List?) ?.map((e) => e as int) @@ -18,7 +18,7 @@ class StoriesState storyType: StoryType.values.byName(json['storyType'] as String), ); - Map toJson() => { + Map toMap() => { 'status': status.name, 'data': data, 'storyType': storyType.name, diff --git a/lib/stories_search/bloc/stories_search_bloc.dart b/lib/stories_search/bloc/stories_search_bloc.dart index e05f06a4..2570093a 100644 --- a/lib/stories_search/bloc/stories_search_bloc.dart +++ b/lib/stories_search/bloc/stories_search_bloc.dart @@ -119,9 +119,9 @@ class StoriesSearchBloc @override StoriesSearchState? fromJson(Map json) => - StoriesSearchState.fromJson(json); + StoriesSearchState.fromMap(json); @override Map? toJson(StoriesSearchState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; } diff --git a/lib/stories_search/bloc/stories_search_state.dart b/lib/stories_search/bloc/stories_search_state.dart index 9b3f8040..bcb6478c 100644 --- a/lib/stories_search/bloc/stories_search_state.dart +++ b/lib/stories_search/bloc/stories_search_state.dart @@ -12,7 +12,7 @@ class StoriesSearchState this.exception, }); - factory StoriesSearchState.fromJson(Map json) => + factory StoriesSearchState.fromMap(Map json) => StoriesSearchState( searchRange: json['searchRange'] != null ? SearchRange.values.byName(json['searchRange'] as String) @@ -30,7 +30,7 @@ class StoriesSearchState : null, ); - Map toJson() => { + Map toMap() => { 'status': status.name, 'data': data, 'searchText': searchText, diff --git a/lib/story_similar/cubit/story_similar_cubit.dart b/lib/story_similar/cubit/story_similar_cubit.dart index 12a03c6a..315ac963 100644 --- a/lib/story_similar/cubit/story_similar_cubit.dart +++ b/lib/story_similar/cubit/story_similar_cubit.dart @@ -72,11 +72,11 @@ class StorySimilarCubit extends HydratedCubit { @override StorySimilarState? fromJson(Map json) => - StorySimilarState.fromJson(json); + StorySimilarState.fromMap(json); @override Map? toJson(StorySimilarState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; @override Future close() { diff --git a/lib/story_similar/cubit/story_similar_state.dart b/lib/story_similar/cubit/story_similar_state.dart index 7b66f30b..8f87548f 100644 --- a/lib/story_similar/cubit/story_similar_state.dart +++ b/lib/story_similar/cubit/story_similar_state.dart @@ -8,16 +8,16 @@ class StorySimilarState with DataMixin>, EquatableMixin { this.exception, }); - factory StorySimilarState.fromJson(Map json) => + factory StorySimilarState.fromMap(Map json) => StorySimilarState( status: Status.values.byName(json['status'] as String), - item: Item.fromJson(json['item'] as Map), + item: Item.fromMap(json['item'] as Map), data: (json['data'] as List?) ?.map((e) => e as int) .toList(growable: false), ); - Map toJson() => { + Map toMap() => { 'status': status.name, 'item': item, 'data': data, diff --git a/lib/submit/cubit/submit_cubit.dart b/lib/submit/cubit/submit_cubit.dart index 4d6d7bc6..959c2940 100644 --- a/lib/submit/cubit/submit_cubit.dart +++ b/lib/submit/cubit/submit_cubit.dart @@ -90,9 +90,8 @@ class SubmitCubit extends HydratedCubit { } @override - SubmitState? fromJson(Map json) => - SubmitState.fromJson(json); + SubmitState? fromJson(Map json) => SubmitState.fromMap(json); @override - Map? toJson(SubmitState state) => state.toJson(); + Map? toJson(SubmitState state) => state.toMap(); } diff --git a/lib/submit/cubit/submit_state.dart b/lib/submit/cubit/submit_state.dart index d49df2dd..f4d664e4 100644 --- a/lib/submit/cubit/submit_state.dart +++ b/lib/submit/cubit/submit_state.dart @@ -10,7 +10,7 @@ class SubmitState with EquatableMixin { this.success = false, }); - factory SubmitState.fromJson(Map json) => SubmitState( + factory SubmitState.fromMap(Map json) => SubmitState( title: TitleInput.pure(json['title'] as String? ?? ''), url: UrlInput.pure( json['url'] as String? ?? '', @@ -23,7 +23,7 @@ class SubmitState with EquatableMixin { isValid: json['isValid'] as bool? ?? false, ); - Map toJson() => { + Map toMap() => { 'title': title.value, 'url': url.value, 'text': text.value, diff --git a/lib/user/cubit/user_cubit.dart b/lib/user/cubit/user_cubit.dart index 5f8b82a5..b8233a97 100644 --- a/lib/user/cubit/user_cubit.dart +++ b/lib/user/cubit/user_cubit.dart @@ -105,11 +105,11 @@ class UserCubit extends HydratedCubit } @override - UserState? fromJson(Map json) => UserState.fromJson(json); + UserState? fromJson(Map json) => UserState.fromMap(json); @override Map? toJson(UserState state) => - state.status == Status.success ? state.toJson() : null; + state.status == Status.success ? state.toMap() : null; @override Future close() async { diff --git a/lib/user/cubit/user_state.dart b/lib/user/cubit/user_state.dart index 5768736d..703805de 100644 --- a/lib/user/cubit/user_state.dart +++ b/lib/user/cubit/user_state.dart @@ -10,15 +10,15 @@ class UserState with DataMixin, EquatableMixin { this.exception, }); - factory UserState.fromJson(Map json) => UserState( + factory UserState.fromMap(Map json) => UserState( status: Status.values.byName(json['status'] as String), - data: User.fromJson(json['data'] as Map), + data: User.fromMap(json['data'] as Map), blocked: json['blocked'] as bool? ?? false, ); - Map toJson() => { + Map toMap() => { 'status': status.name, - 'data': data?.toJson(), + 'data': data?.toMap(), 'blocked': blocked, }; diff --git a/packages/glider_data/lib/src/algolia_api_service.dart b/packages/glider_data/lib/src/algolia_api_service.dart index 6087a33d..d64728f0 100644 --- a/packages/glider_data/lib/src/algolia_api_service.dart +++ b/packages/glider_data/lib/src/algolia_api_service.dart @@ -46,7 +46,7 @@ class AlgoliaApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return AlgoliaSearchDto.fromJson(map); + return AlgoliaSearchDto.fromMap(map); }, response.body, ); @@ -67,7 +67,7 @@ class AlgoliaApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return AlgoliaSearchDto.fromJson(map); + return AlgoliaSearchDto.fromMap(map); }, response.body, ); @@ -94,7 +94,7 @@ class AlgoliaApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return AlgoliaSearchDto.fromJson(map); + return AlgoliaSearchDto.fromMap(map); }, response.body, ); @@ -118,7 +118,7 @@ class AlgoliaApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return AlgoliaSearchDto.fromJson(map); + return AlgoliaSearchDto.fromMap(map); }, response.body, ); @@ -138,7 +138,7 @@ class AlgoliaApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return AlgoliaSearchDto.fromJson(map); + return AlgoliaSearchDto.fromMap(map); }, response.body, ); diff --git a/packages/glider_data/lib/src/dtos/algolia_search_dto.dart b/packages/glider_data/lib/src/dtos/algolia_search_dto.dart index cc8f5f20..3f8d94bb 100644 --- a/packages/glider_data/lib/src/dtos/algolia_search_dto.dart +++ b/packages/glider_data/lib/src/dtos/algolia_search_dto.dart @@ -1,11 +1,11 @@ class AlgoliaSearchDto { const AlgoliaSearchDto({required this.hits}); - factory AlgoliaSearchDto.fromJson(Map json) => + factory AlgoliaSearchDto.fromMap(Map json) => AlgoliaSearchDto( hits: (json['hits'] as List) .map( - (e) => AlgoliaSearchHitDto.fromJson(e as Map), + (e) => AlgoliaSearchHitDto.fromMap(e as Map), ) .toList(growable: false), ); @@ -27,7 +27,7 @@ class AlgoliaSearchHitDto { this.createdAtI, }); - factory AlgoliaSearchHitDto.fromJson(Map json) => + factory AlgoliaSearchHitDto.fromMap(Map json) => AlgoliaSearchHitDto( objectId: json['objectID'] as String, title: json['title'] as String?, diff --git a/packages/glider_data/lib/src/dtos/item_dto.dart b/packages/glider_data/lib/src/dtos/item_dto.dart index d3901f9a..a9eb8ecf 100644 --- a/packages/glider_data/lib/src/dtos/item_dto.dart +++ b/packages/glider_data/lib/src/dtos/item_dto.dart @@ -17,7 +17,7 @@ class ItemDto { this.descendants, }); - factory ItemDto.fromJson(Map json) => ItemDto( + factory ItemDto.fromMap(Map json) => ItemDto( id: json['id'] as int, deleted: json['deleted'] as bool?, type: json['type'] as String?, diff --git a/packages/glider_data/lib/src/dtos/user_dto.dart b/packages/glider_data/lib/src/dtos/user_dto.dart index fc2d13df..6e4f6e4d 100644 --- a/packages/glider_data/lib/src/dtos/user_dto.dart +++ b/packages/glider_data/lib/src/dtos/user_dto.dart @@ -7,7 +7,7 @@ class UserDto { this.submitted, }); - factory UserDto.fromJson(Map json) => UserDto( + factory UserDto.fromMap(Map json) => UserDto( id: json['id'] as String, created: json['created'] as int, karma: json['karma'] as int, diff --git a/packages/glider_data/lib/src/hacker_news_api_service.dart b/packages/glider_data/lib/src/hacker_news_api_service.dart index 0b5b57d1..6ef18335 100644 --- a/packages/glider_data/lib/src/hacker_news_api_service.dart +++ b/packages/glider_data/lib/src/hacker_news_api_service.dart @@ -41,7 +41,7 @@ class HackerNewsApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return ItemDto.fromJson(map); + return ItemDto.fromMap(map); }, response.body, ); @@ -53,7 +53,7 @@ class HackerNewsApiService { return compute( (body) { final map = jsonDecode(body) as Map; - return UserDto.fromJson(map); + return UserDto.fromMap(map); }, response.body, ); diff --git a/packages/glider_domain/lib/src/entities/item.dart b/packages/glider_domain/lib/src/entities/item.dart index 96f0f495..4837c647 100644 --- a/packages/glider_domain/lib/src/entities/item.dart +++ b/packages/glider_domain/lib/src/entities/item.dart @@ -59,7 +59,7 @@ class Item with EquatableMixin { descendantCount: dto.numComments, ); - factory Item.fromJson(Map json) => Item( + factory Item.fromMap(Map json) => Item( id: json['id'] as int, isDeleted: json['isDeleted'] as bool? ?? false, type: json['type'] != null @@ -87,7 +87,7 @@ class Item with EquatableMixin { descendantCount: json['descendantCount'] as int?, ); - Map toJson() => { + Map toMap() => { 'id': id, 'isDeleted': isDeleted, 'type': type?.name, diff --git a/packages/glider_domain/lib/src/entities/item_descendant.dart b/packages/glider_domain/lib/src/entities/item_descendant.dart index 54dd3885..92ecd66d 100644 --- a/packages/glider_domain/lib/src/entities/item_descendant.dart +++ b/packages/glider_domain/lib/src/entities/item_descendant.dart @@ -7,7 +7,7 @@ class ItemDescendant with EquatableMixin { this.isPart = false, }); - factory ItemDescendant.fromJson(Map json) => ItemDescendant( + factory ItemDescendant.fromMap(Map json) => ItemDescendant( id: json['id'] as int, ancestorIds: (json['ancestorIds'] as List?) ?.map((e) => e as int) @@ -16,7 +16,7 @@ class ItemDescendant with EquatableMixin { isPart: json['isPart'] as bool? ?? false, ); - Map toJson() => { + Map toMap() => { 'id': id, 'ancestorIds': ancestorIds, 'isPart': isPart, diff --git a/packages/glider_domain/lib/src/entities/user.dart b/packages/glider_domain/lib/src/entities/user.dart index 94f7f58f..2d60e42b 100644 --- a/packages/glider_domain/lib/src/entities/user.dart +++ b/packages/glider_domain/lib/src/entities/user.dart @@ -20,7 +20,7 @@ class User with EquatableMixin { submittedIds: dto.submitted ?? const [], ); - factory User.fromJson(Map json) => User( + factory User.fromMap(Map json) => User( username: json['username'] as String, createdDateTime: DateTime.fromMillisecondsSinceEpoch(json['createdDateTime'] as int), @@ -31,7 +31,7 @@ class User with EquatableMixin { .toList(growable: false), ); - Map toJson() => { + Map toMap() => { 'username': username, 'createdDateTime': createdDateTime.millisecondsSinceEpoch, 'karma': karma, From 962bdd31f15555bb873e6c653f4bd0d48c30b188 Mon Sep 17 00:00:00 2001 From: Mosc Date: Mon, 8 Jan 2024 19:49:36 +0100 Subject: [PATCH 122/145] Add header to navigation drawer --- .../widgets/navigation_shell_scaffold.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart index fc6ca489..35c5fb4e 100644 --- a/lib/navigation_shell/widgets/navigation_shell_scaffold.dart +++ b/lib/navigation_shell/widgets/navigation_shell_scaffold.dart @@ -288,6 +288,16 @@ class _NavigationShellScaffoldState extends State { Navigator.pop(context); }, children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 32, + vertical: 16, + ), + child: Text( + context.l10n.appName, + style: Theme.of(context).textTheme.titleSmall, + ), + ), for (final destination in destinations) NavigationDrawerDestination( icon: destination.icon, From 3efafe7040310e07c57f9a0b83aea19e03a65197 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 9 Jan 2024 19:54:36 +0100 Subject: [PATCH 123/145] Add story lines setting --- lib/edit/view/edit_page.dart | 7 +- lib/favorites/view/favorites_shell_page.dart | 4 +- lib/item/view/item_page.dart | 12 ++- lib/item/widgets/item_data_tile.dart | 33 ++++-- lib/item/widgets/item_loading_tile.dart | 19 ++-- lib/item/widgets/item_tile.dart | 3 + lib/l10n/arb/app_en.arb | 4 +- lib/reply/view/reply_page.dart | 6 +- lib/settings/cubit/settings_cubit.dart | 101 ++++++++++-------- lib/settings/cubit/settings_state.dart | 5 + lib/settings/view/settings_page.dart | 27 +++-- lib/stories/view/stories_shell_page.dart | 6 +- .../view/sliver_stories_search_body.dart | 4 +- .../lib/src/shared_preferences_service.dart | 7 ++ .../lib/src/settings_repository.dart | 6 ++ 15 files changed, 152 insertions(+), 92 deletions(-) diff --git a/lib/edit/view/edit_page.dart b/lib/edit/view/edit_page.dart index 661b0654..c346cdde 100644 --- a/lib/edit/view/edit_page.dart +++ b/lib/edit/view/edit_page.dart @@ -259,17 +259,12 @@ class _EditPreview extends StatelessWidget { builder: (context, state) => state.item != null ? BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != - current.useLargeStoryStyle || - previous.showFavicons != current.showFavicons || - previous.showUserAvatars != current.showUserAvatars || - previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => ItemDataTile( state.item!.copyWith( title: () => state.title?.value, text: () => state.text?.value, ), + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, showFavicons: settingsState.showFavicons, showUserAvatars: settingsState.showUserAvatars, diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index 66030baf..48a3cf34 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -137,13 +137,11 @@ class _SliverFavoritesBody extends StatelessWidget { bloc: _favoritesCubit, builder: (context, state) => BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.useActionButtons != current.useActionButtons, builder: (context, settingsState) => state.whenOrDefaultSlivers( loading: () => SliverList.builder( itemBuilder: (context, index) => ItemLoadingTile( type: ItemType.story, + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, ), ), diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index 9dd00710..15b6207d 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -90,14 +90,20 @@ class _ItemPageState extends State { super.dispose(); } - static double _getToolbarHeight({required bool useLargeStoryStyle}) => - useLargeStoryStyle ? 92 : 84; + static double _getToolbarHeight({ + required int storyLines, + required bool useLargeStoryStyle, + }) => + (storyLines >= 0 ? storyLines : (useLargeStoryStyle ? 3 : 2)) * + (useLargeStoryStyle ? 24 : 20) + + 44; @override Widget build(BuildContext context) { return BlocBuilder( bloc: widget._settingsCubit, buildWhen: (previous, current) => + previous.storyLines != current.storyLines || previous.useLargeStoryStyle != current.useLargeStoryStyle || previous.useThreadNavigation != current.useThreadNavigation, builder: (context, settingsState) => Scaffold( @@ -112,6 +118,7 @@ class _ItemPageState extends State { scrollController: _scrollController, onRefresh: () async => unawaited(_itemTreeCubit.load()), toolbarHeight: _getToolbarHeight( + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, ), slivers: [ @@ -125,6 +132,7 @@ class _ItemPageState extends State { bodyKey: _bodyKey, scrollController: _scrollController, toolbarHeight: _getToolbarHeight( + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, ), ), diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index ffa27278..8f59e879 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -34,6 +34,7 @@ class ItemDataTile extends StatelessWidget { this.filtered = false, this.failed = false, this.collapsedCount, + this.storyLines = 2, this.useLargeStoryStyle = true, this.showFavicons = true, this.showMetadata = true, @@ -58,6 +59,7 @@ class ItemDataTile extends StatelessWidget { final bool filtered; final bool failed; final int? collapsedCount; + final int storyLines; final bool useLargeStoryStyle; final bool showFavicons; final bool showMetadata; @@ -151,6 +153,7 @@ class ItemDataTile extends StatelessWidget { tag: 'item_tile_title_${item.id}', child: _ItemTitle( item, + storyLines: storyLines, useLargeStoryStyle: useLargeStoryStyle, style: style, ), @@ -170,7 +173,8 @@ class ItemDataTile extends StatelessWidget { onLongPress: () {}, child: _ItemFavicon( item, - isLarge: useLargeStoryStyle, + storyLines: storyLines, + useLargeStoryStyle: useLargeStoryStyle, ), ), ), @@ -376,7 +380,8 @@ class ItemDataTile extends StatelessWidget { type: MaterialType.transparency, child: _ItemFavicon( item, - isLarge: false, + storyLines: 1, + useLargeStoryStyle: false, ), ), ) @@ -407,14 +412,19 @@ class ItemDataTile extends StatelessWidget { class _ItemTitle extends StatelessWidget { const _ItemTitle( this.item, { + required this.storyLines, required this.useLargeStoryStyle, required this.style, }); final Item item; + final int storyLines; final bool useLargeStoryStyle; final ItemStyle style; + int get maxLines => + storyLines >= 0 ? storyLines : (useLargeStoryStyle ? 3 : 2); + @override Widget build(BuildContext context) { return Text.rich( @@ -493,12 +503,14 @@ class _ItemTitle extends StatelessWidget { style: Theme.of(context).textTheme.titleSmall, ), ], - // Attach zero-width space of title style to enforce height. + // Append zero-width space of title style to enforce height. const TextSpan(text: '\u200b'), - if (useLargeStoryStyle) const TextSpan(text: '\n'), + if (storyLines >= 0) + // `minLines` does not exist, so append newlines as a workaround. + TextSpan(text: '\n' * (maxLines - 1)), ], ), - maxLines: 2, + maxLines: maxLines, overflow: TextOverflow.ellipsis, ); } @@ -507,13 +519,18 @@ class _ItemTitle extends StatelessWidget { class _ItemFavicon extends StatelessWidget { const _ItemFavicon( this.item, { - required this.isLarge, + required this.storyLines, + required this.useLargeStoryStyle, }); final Item item; - final bool isLarge; + final int storyLines; + final bool useLargeStoryStyle; - int get _faviconSize => min(isLarge ? 2 * 24 : 20, _faviconRequestSize); + int get _faviconSize => min( + useLargeStoryStyle ? (storyLines >= 0 ? storyLines : 2) * 24 : 20, + _faviconRequestSize, + ); @override Widget build(BuildContext context) { diff --git a/lib/item/widgets/item_loading_tile.dart b/lib/item/widgets/item_loading_tile.dart index 517526b7..6110fe47 100644 --- a/lib/item/widgets/item_loading_tile.dart +++ b/lib/item/widgets/item_loading_tile.dart @@ -13,6 +13,7 @@ class ItemLoadingTile extends StatelessWidget { super.key, required this.type, this.collapsedCount, + this.storyLines = 2, this.useLargeStoryStyle = true, this.showMetadata = true, this.style = ItemStyle.full, @@ -21,12 +22,14 @@ class ItemLoadingTile extends StatelessWidget { final ItemType type; final int? collapsedCount; + final int storyLines; final bool useLargeStoryStyle; final bool showMetadata; final ItemStyle style; final EdgeInsetsGeometry padding; - int get _faviconSize => useLargeStoryStyle ? 2 * 24 : 20; + int get _faviconSize => + useLargeStoryStyle ? (storyLines >= 0 ? storyLines : 2) * 24 : 20; @override Widget build(BuildContext context) { @@ -57,16 +60,20 @@ class ItemLoadingTile extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + for (var i = 0; + i < (storyLines >= 0 ? storyLines : 2) - 1; + i++) + LoadingTextBlock( + style: useLargeStoryStyle + ? textTheme.titleMedium + : textTheme.titleSmall, + ), LoadingTextBlock( + width: 200, style: useLargeStoryStyle ? textTheme.titleMedium : textTheme.titleSmall, ), - if (useLargeStoryStyle) - LoadingTextBlock( - width: 200, - style: textTheme.titleMedium, - ), ], ), ), diff --git a/lib/item/widgets/item_tile.dart b/lib/item/widgets/item_tile.dart index 39d7c462..d229315d 100644 --- a/lib/item/widgets/item_tile.dart +++ b/lib/item/widgets/item_tile.dart @@ -109,6 +109,7 @@ class _ItemTileState extends State bloc: widget._settingsCubit, buildWhen: (previous, current) => previous.showStoryMetadata != current.showStoryMetadata || + previous.storyLines != current.storyLines || previous.useLargeStoryStyle != current.useLargeStoryStyle || previous.showFavicons != current.showFavicons || previous.showUserAvatars != current.showUserAvatars || @@ -122,6 +123,7 @@ class _ItemTileState extends State loading: () => ItemLoadingTile( type: widget.loadingType, collapsedCount: widget.collapsedCount, + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, showMetadata: settingsState.showStoryMetadata || widget.forceShowMetadata, @@ -166,6 +168,7 @@ class _ItemTileState extends State ), failed: state.status == Status.failure, collapsedCount: widget.collapsedCount, + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, showFavicons: settingsState.showFavicons, showMetadata: settingsState.showStoryMetadata || diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index c2c0f940..3a5bd5ae 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -21,8 +21,10 @@ "pureBackgroundDescription": "White on light theme, black on dark theme", "font": "Font", "appearance": "Appearance", + "storyLines": "Story lines", + "variable": "Variable", "largeStoryStyle": "Large stories", - "largeStoryStyleDescription": "Shows URL and larger title", + "largeStoryStyleDescription": "Shows URL, larger title, larger favicon", "favicons": "Story favicons", "storyMetadata": "Story metadata", "storyMetadataDescription": "Always shows on some pages", diff --git a/lib/reply/view/reply_page.dart b/lib/reply/view/reply_page.dart index 8d92007b..d190a287 100644 --- a/lib/reply/view/reply_page.dart +++ b/lib/reply/view/reply_page.dart @@ -219,11 +219,6 @@ class _ReplyPreview extends StatelessWidget { builder: (context, username) => BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.showFavicons != current.showFavicons || - previous.showUserAvatars != current.showUserAvatars || - previous.useInAppBrowser != current.useInAppBrowser, builder: (context, settingsState) => HeroMode( enabled: false, child: ItemDataTile( @@ -235,6 +230,7 @@ class _ReplyPreview extends StatelessWidget { state.text.value.isNotEmpty ? state.text.value : null, dateTime: clock.now(), ), + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, showFavicons: settingsState.showFavicons, showUserAvatars: settingsState.showUserAvatars, diff --git a/lib/settings/cubit/settings_cubit.dart b/lib/settings/cubit/settings_cubit.dart index 936d9bcb..ece0d281 100644 --- a/lib/settings/cubit/settings_cubit.dart +++ b/lib/settings/cubit/settings_cubit.dart @@ -35,6 +35,7 @@ class SettingsCubit extends Cubit final themeVariant = await _settingsRepository.getThemeVariant(); final usePureBackground = await _settingsRepository.getUsePureBackground(); final font = await _settingsRepository.getFont(); + final storyLines = await _settingsRepository.getStoryLines(); final useLargeStoryStyle = await _settingsRepository.getUseLargeStoryStyle(); final showFavicons = await _settingsRepository.getShowFavicons(); @@ -59,6 +60,7 @@ class SettingsCubit extends Cubit usePureBackground: usePureBackground != null ? () => usePureBackground : null, font: font != null ? () => font : null, + storyLines: storyLines != null ? () => storyLines : null, useLargeStoryStyle: useLargeStoryStyle != null ? () => useLargeStoryStyle : null, showFavicons: showFavicons != null ? () => showFavicons : null, @@ -94,50 +96,6 @@ class SettingsCubit extends Cubit } } - Future setShowFavicons(bool value) async { - await _settingsRepository.setShowFavicons(value: value); - final showFavicons = await _settingsRepository.getShowFavicons(); - - if (showFavicons != null) { - safeEmit( - state.copyWith(showFavicons: () => showFavicons), - ); - } - } - - Future setShowStoryMetadata(bool value) async { - await _settingsRepository.setShowStoryMetadata(value: value); - final showStoryMetadata = await _settingsRepository.getShowStoryMetadata(); - - if (showStoryMetadata != null) { - safeEmit( - state.copyWith(showStoryMetadata: () => showStoryMetadata), - ); - } - } - - Future setShowUserAvatars(bool value) async { - await _settingsRepository.setShowUserAvatars(value: value); - final showUserAvatars = await _settingsRepository.getShowUserAvatars(); - - if (showUserAvatars != null) { - safeEmit( - state.copyWith(showUserAvatars: () => showUserAvatars), - ); - } - } - - Future setUseActionButtons(bool value) async { - await _settingsRepository.setUseActionButtons(value: value); - final useActionButtons = await _settingsRepository.getUseActionButtons(); - - if (useActionButtons != null) { - safeEmit( - state.copyWith(useActionButtons: () => useActionButtons), - ); - } - } - Future setThemeMode(ThemeMode value) async { await _settingsRepository.setThemeMode(value: value); final themeMode = await _settingsRepository.getThemeMode(); @@ -193,6 +151,17 @@ class SettingsCubit extends Cubit } } + Future setStoryLines(int value) async { + await _settingsRepository.setStoryLines(value: value); + final storyLines = await _settingsRepository.getStoryLines(); + + if (storyLines != null) { + safeEmit( + state.copyWith(storyLines: () => storyLines), + ); + } + } + Future setFont(String value) async { await _settingsRepository.setFont(value: value); final font = await _settingsRepository.getFont(); @@ -204,6 +173,50 @@ class SettingsCubit extends Cubit } } + Future setShowFavicons(bool value) async { + await _settingsRepository.setShowFavicons(value: value); + final showFavicons = await _settingsRepository.getShowFavicons(); + + if (showFavicons != null) { + safeEmit( + state.copyWith(showFavicons: () => showFavicons), + ); + } + } + + Future setShowStoryMetadata(bool value) async { + await _settingsRepository.setShowStoryMetadata(value: value); + final showStoryMetadata = await _settingsRepository.getShowStoryMetadata(); + + if (showStoryMetadata != null) { + safeEmit( + state.copyWith(showStoryMetadata: () => showStoryMetadata), + ); + } + } + + Future setShowUserAvatars(bool value) async { + await _settingsRepository.setShowUserAvatars(value: value); + final showUserAvatars = await _settingsRepository.getShowUserAvatars(); + + if (showUserAvatars != null) { + safeEmit( + state.copyWith(showUserAvatars: () => showUserAvatars), + ); + } + } + + Future setUseActionButtons(bool value) async { + await _settingsRepository.setUseActionButtons(value: value); + final useActionButtons = await _settingsRepository.getUseActionButtons(); + + if (useActionButtons != null) { + safeEmit( + state.copyWith(useActionButtons: () => useActionButtons), + ); + } + } + Future setShowJobs(bool value) async { await _settingsRepository.setShowJobs(value: value); final showJobs = await _settingsRepository.getShowJobs(); diff --git a/lib/settings/cubit/settings_state.dart b/lib/settings/cubit/settings_state.dart index ffcd77f4..6c6f170e 100644 --- a/lib/settings/cubit/settings_state.dart +++ b/lib/settings/cubit/settings_state.dart @@ -8,6 +8,7 @@ class SettingsState with EquatableMixin { this.themeVariant = Variant.tonalSpot, this.usePureBackground = false, this.font = 'Noto Sans', + this.storyLines = 2, this.useLargeStoryStyle = true, this.showFavicons = true, this.showStoryMetadata = true, @@ -29,6 +30,7 @@ class SettingsState with EquatableMixin { final Variant themeVariant; final bool usePureBackground; final String font; + final int storyLines; final bool useLargeStoryStyle; final bool showFavicons; final bool showStoryMetadata; @@ -50,6 +52,7 @@ class SettingsState with EquatableMixin { Variant Function()? themeVariant, bool Function()? usePureBackground, String Function()? font, + int Function()? storyLines, bool Function()? useLargeStoryStyle, bool Function()? showFavicons, bool Function()? showStoryMetadata, @@ -74,6 +77,7 @@ class SettingsState with EquatableMixin { ? usePureBackground() : this.usePureBackground, font: font != null ? font() : this.font, + storyLines: storyLines != null ? storyLines() : this.storyLines, useLargeStoryStyle: useLargeStoryStyle != null ? useLargeStoryStyle() : this.useLargeStoryStyle, @@ -112,6 +116,7 @@ class SettingsState with EquatableMixin { themeVariant, usePureBackground, font, + storyLines, useLargeStoryStyle, showFavicons, showStoryMetadata, diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 2894ce76..1a727f73 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -102,7 +102,7 @@ class _SettingsBody extends StatelessWidget { MenuListTile( title: Text(context.l10n.themeMode), trailing: Text(state.themeMode.capitalizedLabel), - onChanged: (value) async => _settingsCubit.setThemeMode(value), + onChanged: _settingsCubit.setThemeMode, values: ThemeMode.values, selected: (value) => state.themeMode == value, childBuilder: (value) => Text(value.capitalizedLabel), @@ -131,7 +131,7 @@ class _SettingsBody extends StatelessWidget { title: Text(context.l10n.themeVariant), trailing: Text(state.themeVariant.capitalizedLabel), enabled: !state.useDynamicTheme, - onChanged: (value) async => _settingsCubit.setThemeVariant(value), + onChanged: _settingsCubit.setThemeVariant, values: Variant.values, selected: (value) => state.themeVariant == value, childBuilder: (value) => Text(value.capitalizedLabel), @@ -147,7 +147,7 @@ class _SettingsBody extends StatelessWidget { MenuListTile( title: Text(context.l10n.font), trailing: Text(state.font), - onChanged: (value) async => _settingsCubit.setFont(value), + onChanged: _settingsCubit.setFont, values: _fonts, selected: (value) => state.font == value, childBuilder: Text.new, @@ -162,6 +162,19 @@ class _SettingsBody extends StatelessWidget { ), ), ), + MenuListTile( + title: Text(context.l10n.storyLines), + trailing: Text( + state.storyLines >= 0 + ? '${state.storyLines}' + : context.l10n.variable, + ), + onChanged: _settingsCubit.setStoryLines, + values: const [1, 2, -1], + selected: (value) => state.storyLines == value, + childBuilder: (value) => + Text(value >= 0 ? '$value' : context.l10n.variable), + ), SwitchListTile.adaptive( value: state.useLargeStoryStyle, onChanged: _settingsCubit.setUseLargeStoryStyle, @@ -202,13 +215,6 @@ class _SettingsBody extends StatelessWidget { ), BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.showFavicons != current.showFavicons || - previous.showStoryMetadata != current.showStoryMetadata || - previous.showUserAvatars != current.showUserAvatars || - previous.useActionButtons != current.useActionButtons || - previous.useInAppBrowser != current.useInAppBrowser, builder: (context, state) => Padding( padding: AppSpacing.defaultTilePadding, child: PreviewCard( @@ -230,6 +236,7 @@ class _SettingsBody extends StatelessWidget { descendantCount: 7, ), vote: VoteType.upvote, + storyLines: state.storyLines, useLargeStoryStyle: state.useLargeStoryStyle, showFavicons: state.showFavicons, showMetadata: state.showStoryMetadata, diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index fab2a7ad..df7a36db 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -253,13 +253,11 @@ class _SliverStoriesBody extends StatelessWidget { itemBuilder: (context, index) => BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.showStoryMetadata != current.showStoryMetadata, builder: (context, settingsState) => ItemLoadingTile( type: ItemType.story, - showMetadata: settingsState.showStoryMetadata, + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, + showMetadata: settingsState.showStoryMetadata, style: ItemStyle.overview, ), ), diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index 6631bb53..0412e9be 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -37,11 +37,9 @@ class SliverStoriesSearchBody extends StatelessWidget { itemBuilder: (context, index) => BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => - previous.useLargeStoryStyle != current.useLargeStoryStyle || - previous.showStoryMetadata != current.showStoryMetadata, builder: (context, settingsState) => ItemLoadingTile( type: ItemType.story, + storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, showMetadata: settingsState.showStoryMetadata, style: ItemStyle.overview, diff --git a/packages/glider_data/lib/src/shared_preferences_service.dart b/packages/glider_data/lib/src/shared_preferences_service.dart index bbc1e1f4..6211982d 100644 --- a/packages/glider_data/lib/src/shared_preferences_service.dart +++ b/packages/glider_data/lib/src/shared_preferences_service.dart @@ -11,6 +11,7 @@ class SharedPreferencesService { static const String _themeVariantKey = 'theme_variant'; static const String _usePureBackgroundKey = 'use_pure_background'; static const String _fontKey = 'font'; + static const String _storyLinesKey = 'story_lines'; static const String _useLargeStoryStyleKey = 'use_large_story_style'; static const String _showFaviconsKey = 'show_favicons'; static const String _showStoryMetadataKey = 'show_story_metadata'; @@ -66,6 +67,12 @@ class SharedPreferencesService { Future setFont({required String value}) async => _sharedPreferences.setString(_fontKey, value); + Future getStoryLines() async => + _sharedPreferences.getInt(_storyLinesKey); + + Future setStoryLines({required int value}) async => + _sharedPreferences.setInt(_storyLinesKey, value); + Future getUseLargeStoryStyle() async => _sharedPreferences.getBool(_useLargeStoryStyleKey); diff --git a/packages/glider_domain/lib/src/settings_repository.dart b/packages/glider_domain/lib/src/settings_repository.dart index 9d6740f4..c905db08 100644 --- a/packages/glider_domain/lib/src/settings_repository.dart +++ b/packages/glider_domain/lib/src/settings_repository.dart @@ -50,6 +50,12 @@ class SettingsRepository { Future setFont({required String value}) async => _sharedPreferencesService.setFont(value: value); + Future getStoryLines() async => + _sharedPreferencesService.getStoryLines(); + + Future setStoryLines({required int value}) async => + _sharedPreferencesService.setStoryLines(value: value); + Future getUseLargeStoryStyle() async => _sharedPreferencesService.getUseLargeStoryStyle(); From 8dbb5c994a5f5f9df39cf5004ae2f66d7daf5beb Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 9 Jan 2024 19:57:19 +0100 Subject: [PATCH 124/145] Change story type selection to app bar filter chip --- lib/l10n/arb/app_en.arb | 12 ++-- lib/stories/view/stories_shell_page.dart | 11 ++-- lib/stories/view/stories_type_view.dart | 75 +++++++----------------- 3 files changed, 30 insertions(+), 68 deletions(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 3a5bd5ae..9f087662 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -33,7 +33,7 @@ "userAvatars": "User avatars", "behavior": "Behavior", "showJobs": "Show job stories", - "showJobsDescription": "Also shows jobs tab", + "showJobsDescription": "Also shows jobs in dropdown menu", "threadNavigation": "Thread navigation", "threadNavigationDescription": "Allows jumps between top-level comments", "downvoting": "Downvoting", @@ -66,11 +66,11 @@ "logout": "Log out", "showMore": "Show more", "stories": "Frontpage", - "storyTypeTop": "Top", - "storyTypeNew": "New", - "storyTypeBest": "Best", - "storyTypeAsk": "Ask", - "storyTypeShow": "Show", + "storyTypeTop": "Top stories", + "storyTypeNew": "New stories", + "storyTypeBest": "Best stories", + "storyTypeAsk": "Ask HN", + "storyTypeShow": "Show HN", "storyTypeJob": "Jobs", "submit": "Submit", "catchUp": "Catch up", diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index df7a36db..d6420b90 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -64,12 +64,6 @@ class _StoriesShellPageState extends State { widget._authCubit, widget._settingsCubit, ), - SliverToBoxAdapter( - child: StoriesTypeView( - widget._storiesCubit, - widget._settingsCubit, - ), - ), SliverSafeArea( top: false, sliver: _SliverStoriesBody( @@ -111,7 +105,10 @@ class _SliverStoriesAppBarState extends State<_SliverStoriesAppBar> { @override Widget build(BuildContext context) { return SliverAppBar( - title: Text(context.l10n.stories), + title: StoriesTypeView( + widget._storiesCubit, + widget._settingsCubit, + ), flexibleSpace: AppBarProgressIndicator(widget._storiesCubit), actions: [ _StoriesSearchAnchor( diff --git a/lib/stories/view/stories_type_view.dart b/lib/stories/view/stories_type_view.dart index 364090d3..ded2cff9 100644 --- a/lib/stories/view/stories_type_view.dart +++ b/lib/stories/view/stories_type_view.dart @@ -16,70 +16,35 @@ class StoriesTypeView extends StatelessWidget { final StoriesCubit _storiesCubit; final SettingsCubit _settingsCubit; - @override - Widget build(BuildContext context) { - final directionality = Directionality.of(context); - final padding = MediaQuery.paddingOf(context); - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - padding: EdgeInsetsDirectional.only( - top: AppSpacing.s, - bottom: AppSpacing.s, - start: AppSpacing.xl + - switch (directionality) { - TextDirection.ltr => padding.left, - TextDirection.rtl => padding.right, - }, - end: AppSpacing.xl + - switch (directionality) { - TextDirection.ltr => padding.right, - TextDirection.rtl => padding.left, - }, - ), - child: _StoriesTypeBody( - _storiesCubit, - _settingsCubit, - ), - ); - } -} - -class _StoriesTypeBody extends StatelessWidget { - const _StoriesTypeBody( - this._storiesCubit, - this._settingsCubit, - ); - - final StoriesCubit _storiesCubit; - final SettingsCubit _settingsCubit; - @override Widget build(BuildContext context) { return BlocBuilder( bloc: _storiesCubit, - buildWhen: (previous, current) => previous.storyType != current.storyType, builder: (context, state) => BlocBuilder( bloc: _settingsCubit, - buildWhen: (previous, current) => previous.showJobs != current.showJobs, - builder: (context, settingsState) => Row( - children: [ + builder: (context, settingsState) => MenuAnchor( + menuChildren: [ for (final storyType in StoryType.values) if (storyType != StoryType.jobStories || settingsState.showJobs) - ChoiceChip( - showCheckmark: false, - avatar: Icon( - state.storyType == storyType - ? storyType.selectedIcon - : storyType.icon, - color: Theme.of(context).colorScheme.onBackground, - ), - label: Text(storyType.label(context)), - selected: state.storyType == storyType, - onSelected: (selected) => - _storiesCubit.setStoryType(storyType), + MenuItemButton( + onPressed: () async => _storiesCubit.setStoryType(storyType), + child: Text(storyType.label(context)), + ), + ], + builder: (context, controller, child) => FilterChip.elevated( + avatar: Icon(state.storyType.icon), + label: Row( + children: [ + Expanded( + child: Text(state.storyType.label(context)), ), - ].spaced(width: AppSpacing.m), + const Icon(Icons.arrow_drop_down), + ].spaced(width: AppSpacing.m), + ), + labelPadding: const EdgeInsetsDirectional.only(start: AppSpacing.m), + onSelected: (storyType) => + controller.isOpen ? controller.close() : controller.open(), + ), ), ), ); From 431255e90f1e7d63703d2b498abea8cf67bf7774 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 12 Jan 2024 20:34:31 +0100 Subject: [PATCH 125/145] Add padding for potential FAB to stories search view --- lib/stories_search/view/stories_search_view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/stories_search/view/stories_search_view.dart b/lib/stories_search/view/stories_search_view.dart index d74f6fc0..561e97fd 100644 --- a/lib/stories_search/view/stories_search_view.dart +++ b/lib/stories_search/view/stories_search_view.dart @@ -46,6 +46,9 @@ class StoriesSearchView extends StatelessWidget { _settingsCubit, ), ), + const SliverPadding( + padding: AppSpacing.floatingActionButtonPageBottomPadding, + ), ], ), ); From e7c36af3bbff647231fd8827135473e7dcbdae82 Mon Sep 17 00:00:00 2001 From: Mosc Date: Fri, 12 Jan 2024 20:42:48 +0100 Subject: [PATCH 126/145] Upgrade dependencies --- .fvmrc | 2 +- .vscode/settings.json | 2 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile | 2 +- ios/Podfile.lock | 10 +- ios/Runner.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- packages/glider_data/pubspec.yaml | 2 +- packages/glider_domain/pubspec.yaml | 2 +- pubspec.lock | 140 ++++++++++-------- pubspec.yaml | 14 +- 11 files changed, 101 insertions(+), 85 deletions(-) diff --git a/.fvmrc b/.fvmrc index 38875c11..f7989351 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.18.0-0.2.pre" + "flutter": "3.19.0-0.1.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 65544029..7af06559 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.18.0-0.2.pre" + "dart.flutterSdkPath": ".fvm/versions/3.19.0-0.1.pre" } \ No newline at end of file diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Podfile b/ios/Podfile index 88359b22..279576f3 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ef35817f..e63a3ec9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -62,16 +62,16 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 - url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 -PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 +PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 COCOAPODS: 1.14.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 9ba3cf1b..56448f9c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -157,7 +157,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -344,7 +344,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -421,7 +421,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -470,7 +470,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826db..5e31d3d3 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Thu, 7 Mar 2024 16:23:43 +0100 Subject: [PATCH 127/145] Upgrade Flutter version and packages --- .fvmrc | 2 +- .vscode/settings.json | 2 +- ios/Podfile.lock | 4 +- lib/common/widgets/hacker_news_text.dart | 7 +- lib/item/widgets/avatar_widget.dart | 13 +- packages/glider_data/pubspec.yaml | 8 +- packages/glider_domain/pubspec.yaml | 6 +- pubspec.lock | 156 +++++++++++------------ pubspec.yaml | 36 +++--- 9 files changed, 114 insertions(+), 120 deletions(-) diff --git a/.fvmrc b/.fvmrc index f7989351..124e8756 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.19.0-0.1.pre" + "flutter": "3.20.0-1.2.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 7af06559..8c1d337a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.19.0-0.1.pre" + "dart.flutterSdkPath": ".fvm/versions/3.20.0-1.2.pre" } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e63a3ec9..02d2502c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -70,8 +70,8 @@ SPEC CHECKSUMS: path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 + url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index 0751ae4f..77727b12 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -146,11 +146,8 @@ class _HackerNewsMarkdownBodyState extends State<_HackerNewsMarkdownBody> void _parseMarkdown() { _disposeRecognizers(); - final fallbackStyleSheet = - MarkdownStyleSheet.fromTheme(Theme.of(context)).copyWith( - // ignore: deprecated_member_use - textScaleFactor: MediaQuery.textScalerOf(context).textScaleFactor, - ); + final fallbackStyleSheet = MarkdownStyleSheet.fromTheme(Theme.of(context)) + .copyWith(textScaler: MediaQuery.textScalerOf(context)); final styleSheet = fallbackStyleSheet.merge(widget.styleSheet); final builder = MarkdownBuilder( delegate: this, diff --git a/lib/item/widgets/avatar_widget.dart b/lib/item/widgets/avatar_widget.dart index c97bb9cc..5f2774c3 100644 --- a/lib/item/widgets/avatar_widget.dart +++ b/lib/item/widgets/avatar_widget.dart @@ -10,16 +10,14 @@ class AvatarWidget extends StatelessWidget { @override Widget build(BuildContext context) { - // ignore: deprecated_member_use - final scaleFactor = MediaQuery.textScalerOf(context).textScaleFactor; - final pixelSize = scaleFactor * 2; + final pixelSize = MediaQuery.textScalerOf(context).scale(2); final avatarSize = pixelSize * 7; return CustomPaint( painter: _AvatarPainter( username: username, pixelSize: pixelSize, - scaleFactor: scaleFactor, + offset: Offset(pixelSize / 2, pixelSize / 2), ), size: Size.square(avatarSize), ); @@ -31,17 +29,16 @@ class _AvatarPainter extends CustomPainter with EquatableMixin { const _AvatarPainter({ required this.username, required this.pixelSize, - this.scaleFactor = 1, + required this.offset, }); final String username; final double pixelSize; - final double scaleFactor; + final Offset offset; @override void paint(Canvas canvas, Size size) { const seedSteps = 28; - final offset = Offset(scaleFactor, scaleFactor); final points = []; final paint = Paint()..strokeWidth = pixelSize; var seed = 1; @@ -77,7 +74,7 @@ class _AvatarPainter extends CustomPainter with EquatableMixin { List get props => [ username, pixelSize, - scaleFactor, + offset, ]; static int _xorShift32(int number) { diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index c1c99447..47dba747 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -3,17 +3,17 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.2.0-0 <4.0.0" + sdk: ">=3.3.0-0 <4.0.0" dependencies: collection: any compute: ^1.0.2 flutter_secure_storage: ^9.0.0 html: ^0.15.4 - http: ^1.1.2 + http: ^1.2.0 shared_preferences: ^2.2.1 dev_dependencies: - custom_lint: ^0.5.8 + custom_lint: ^0.6.2 dependency_validator: ^3.2.3 - leancode_lint: ^8.0.0 + leancode_lint: ^11.0.0 diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index fee7c0f8..7191861d 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.2.0-0 <4.0.0" + sdk: ">=3.3.0-0 <4.0.0" dependencies: compute: ^1.0.2 @@ -17,6 +17,6 @@ dependencies: rxdart: ^0.27.7 dev_dependencies: - custom_lint: ^0.5.8 + custom_lint: ^0.6.2 dependency_validator: ^3.2.3 - leancode_lint: ^8.0.0 + leancode_lint: ^11.0.0 diff --git a/pubspec.lock b/pubspec.lock index 9c4531ac..1d864574 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "65.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.4.1" analyzer_plugin: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: bloc - sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e url: "https://pub.dev" source: hosted - version: "8.1.2" + version: "8.1.3" bloc_presentation: dependency: "direct main" description: @@ -205,34 +205,34 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: dfb893ff17c83cf08676c6b64df11d3e53d80590978d7c1fb242afff3ba6dedb + sha256: "445242371d91d2e24bd7b82e3583a2c05610094ba2d0575262484ad889c8f981" url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.6.2" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "8df6634b38a36a6c6cb74a9c0eb02e9ba0b0ab89b29e38e6daa86e8ed2c6288d" + sha256: "4c0aed2a3491096e91cf1281923ba1b6814993f16dde0fd60f697925225bbbd6" url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.6.2" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "2b235be098d157e244f18ea905a15a18c16a205e30553888fac6544bbf52f03f" + sha256: ce5d6215f4e143f7780ce53f73dfa6fc503f39d2d30bef76c48be9ac1a09d9a6 url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.6.2" dart_style: dependency: transitive description: name: dart_style - sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.6" dependency_validator: dependency: "direct dev" description: @@ -245,10 +245,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" url: "https://pub.dev" source: hosted - version: "9.1.1" + version: "9.1.2" device_info_plus_platform_interface: dependency: transitive description: @@ -261,10 +261,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b + sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d url: "https://pub.dev" source: hosted - version: "1.6.9" + version: "1.7.0" equatable: dependency: "direct main" description: @@ -285,10 +285,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" file: dependency: transitive description: @@ -314,18 +314,18 @@ packages: dependency: "direct main" description: name: flutter_adaptive_scaffold - sha256: b3b7f8537b0af3e9982a0d1199d4d0e73b225672c867cecfb731c7f2bfc2f0f8 + sha256: "4257142551ec97761d44f4258b8ad53ac76593dd0992197b876769df19f8a018" url: "https://pub.dev" source: hosted - version: "0.1.7+2" + version: "0.1.8" flutter_bloc: dependency: "direct main" description: name: flutter_bloc - sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1" url: "https://pub.dev" source: hosted - version: "8.1.3" + version: "8.1.4" flutter_displaymode: dependency: "direct main" description: @@ -346,10 +346,10 @@ packages: dependency: transitive description: name: flutter_inappwebview_android - sha256: fd4db51e46f49b140d83a3206851432c54ea920b381137c0ba82d0cf59be1dee + sha256: d247f6ed417f1f8c364612fa05a2ecba7f775c8d0c044c1d3b9ee33a6515c421 url: "https://pub.dev" source: hosted - version: "1.0.12" + version: "1.0.13" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -407,10 +407,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "30088ce826b5b9cfbf9e8bece34c716c8a59fa54461dcae1e4ac01a94639e762" + sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db url: "https://pub.dev" source: hosted - version: "0.6.18+3" + version: "0.6.21" flutter_secure_storage: dependency: "direct main" description: @@ -473,10 +473,10 @@ packages: dependency: "direct main" description: name: formz - sha256: df8301299601139de7e653e68a07c332fd2db7cec65745eca1a1ea73fb711e06 + sha256: a58eb48d84685b7ffafac1d143bf47d585bf54c7db89fe81c175dfd6e53201c7 url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" freezed_annotation: dependency: transitive description: @@ -511,18 +511,18 @@ packages: dependency: "direct main" description: name: go_router - sha256: "3b40e751eaaa855179b416974d59d29669e750d2e50fcdb2b37f1cb0ca8c803a" + sha256: "170c46e237d6eb0e6e9f0e8b3f56101e14fb64f787016e42edd74c39cf8b176a" url: "https://pub.dev" source: hosted - version: "13.0.1" + version: "13.2.0" google_fonts: dependency: "direct main" description: name: google_fonts - sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8 + sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.1" graphs: dependency: transitive description: @@ -543,10 +543,10 @@ packages: dependency: transitive description: name: hotreloader - sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.0" html: dependency: transitive description: @@ -559,10 +559,10 @@ packages: dependency: "direct main" description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" http_parser: dependency: transitive description: @@ -575,18 +575,18 @@ packages: dependency: "direct main" description: name: hydrated_bloc - sha256: c925e49704c052a8f249226ae7603f86bfa776b910816390763b956c71d2cbaf + sha256: "00a2099680162e74b5a836b8a7f446e478520a9cae9f6032e028ad8129f4432d" url: "https://pub.dev" source: hosted - version: "9.1.3" + version: "9.1.4" image: dependency: transitive description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.7" intl: dependency: "direct main" description: @@ -623,34 +623,34 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: cdd14e3836065a1f6302a236ec8b5f700695c803c57ae11a1c84df31e6bcf831 url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.3" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "9b2ef90589911d665277464e0482b209d39882dffaaf4ef69a3561a3354b2ebc" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.2" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: fd3cd66cb2bcd7b50dcd3b413af49d78051f809c8b3f6e047962765c15a0d23d url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" leancode_lint: dependency: "direct dev" description: name: leancode_lint - sha256: bf50886eba9c3f7ad163656867670c114bd9ba3b8e5ca22821cb5edcc72fc2f1 + sha256: a0130e0dbe5d5ea2f03f7ba7fa097f0fd44be9dc43d3d7badd6d09cc50015384 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "11.0.0" logging: dependency: transitive description: @@ -663,10 +663,10 @@ packages: dependency: "direct main" description: name: markdown - sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd + sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.2.2" matcher: dependency: transitive description: @@ -687,10 +687,10 @@ packages: dependency: "direct dev" description: name: melos - sha256: ddd8e0871f02ba1eaa3fdc007e306d05801a5ef595d1637083d6654b272ade68 + sha256: "7266e9fc9fee5f4a0c075e5cec375c00736dfc944358f533b740b93b3d8d681e" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.1.0" meta: dependency: transitive description: @@ -703,10 +703,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mustache_template: dependency: transitive description: @@ -831,10 +831,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" pool: dependency: transitive description: @@ -863,10 +863,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.2" pub_semver: dependency: "direct main" description: @@ -935,10 +935,10 @@ packages: dependency: "direct main" description: name: share_plus - sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd + sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" url: "https://pub.dev" source: hosted - version: "7.2.1" + version: "7.2.2" share_plus_platform_interface: dependency: transitive description: @@ -1084,10 +1084,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -1108,26 +1108,26 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: d25bb0ca00432a5e1ee40e69c36c85863addf7cc45e433769d61bed3fe81fd96 + sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" url: "https://pub.dev" source: hosted - version: "6.2.3" + version: "6.2.5" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" + sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" url: "https://pub.dev" source: hosted - version: "6.2.4" + version: "6.2.5" url_launcher_linux: dependency: transitive description: @@ -1148,10 +1148,10 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" url_launcher_web: dependency: transitive description: @@ -1188,10 +1188,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.0.0" watcher: dependency: transitive description: @@ -1252,10 +1252,10 @@ packages: dependency: transitive description: name: yaml_edit - sha256: "1579d4a0340a83cf9e4d580ea51a16329c916973bffd5bd4b45e911b25d46bfd" + sha256: c566f4f804215d84a7a2c377667f546c6033d5b34b4f9e60dfb09d17c4e97826 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.0" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.2" diff --git a/pubspec.yaml b/pubspec.yaml index e47d4a3f..cff451b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,37 +4,37 @@ version: 2.8.0+49 publish_to: none environment: - sdk: ">=3.2.0-0 <4.0.0" + sdk: ">=3.3.0-0 <4.0.0" dependencies: - bloc: ^8.1.2 + bloc: ^8.1.3 bloc_presentation: ^1.0.0 clock: ^1.1.1 collection: any - device_info_plus: ^9.1.1 - dynamic_color: ^1.6.9 + device_info_plus: ^9.1.2 + dynamic_color: ^1.7.0 equatable: ^2.0.5 flutter: sdk: flutter - flutter_adaptive_scaffold: ^0.1.7+2 - flutter_bloc: ^8.1.3 + flutter_adaptive_scaffold: ^0.1.8 + flutter_bloc: ^8.1.4 flutter_displaymode: ^0.6.0 flutter_inappwebview: ^6.0.0 flutter_localizations: sdk: flutter - flutter_markdown: ^0.6.18+3 + flutter_markdown: ^0.6.21 flutter_secure_storage: ^9.0.0 - formz: ^0.6.1 + formz: ^0.7.0 glider_data: path: packages/glider_data glider_domain: path: packages/glider_domain - go_router: ^13.0.1 - google_fonts: ^6.1.0 - http: ^1.1.2 - hydrated_bloc: ^9.1.3 + go_router: ^13.2.0 + google_fonts: ^6.2.1 + http: ^1.2.0 + hydrated_bloc: ^9.1.4 intl: any - markdown: ^7.1.1 + markdown: ^7.2.2 material_color_utilities: any package_info_plus: ^5.0.1 path_provider: ^2.1.2 @@ -42,17 +42,17 @@ dependencies: relative_time: ^5.0.0 rxdart: ^0.27.7 scrollview_observer: ^1.19.0 - share_plus: ^7.2.1 + share_plus: ^7.2.2 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 - url_launcher: ^6.2.3 + url_launcher: ^6.2.5 dev_dependencies: - custom_lint: ^0.5.8 + custom_lint: ^0.6.2 dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 - leancode_lint: ^8.0.0 - melos: ^4.0.0 + leancode_lint: ^11.0.0 + melos: ^4.1.0 flutter: uses-material-design: true From b9e38574a0e7abeb1f58cb7257ac6583cf5012c8 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 7 Mar 2024 21:01:40 +0100 Subject: [PATCH 128/145] Migrate Gradle imperative applies --- .metadata | 18 +++++++++--------- android/app/build.gradle | 35 ++++++++++++++++------------------- android/build.gradle | 13 ------------- android/settings.gradle | 30 ++++++++++++++++++++++-------- 4 files changed, 47 insertions(+), 49 deletions(-) diff --git a/.metadata b/.metadata index 31f8a5f1..f5350d4d 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "593a031efb1e54aef4d083fc810482c2877ba1d2" + revision: "1751123cde4ffad08ae27bdee4f8ddebd033fe76" channel: "beta" project_type: app @@ -13,17 +13,17 @@ project_type: app migration: platforms: - platform: root - create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 - base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + create_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 + base_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 - platform: android - create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 - base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + create_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 + base_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 - platform: ios - create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 - base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + create_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 + base_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 - platform: macos - create_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 - base_revision: 593a031efb1e54aef4d083fc810482c2877ba1d2 + create_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 + base_revision: 1751123cde4ffad08ae27bdee4f8ddebd033fe76 # User provided section diff --git a/android/app/build.gradle b/android/app/build.gradle index 40028db6..9dbeb486 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException('Flutter SDK not found. Define location with flutter.sdk in the local.properties file.') -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { @@ -32,13 +29,14 @@ if (keystorePropertiesFile.exists()) { } android { - // Conditional for compatibility with AGP <4.2. - if (project.android.hasProperty('namespace')) { - namespace 'nl.viter.glider' - } + namespace 'nl.viter.glider' + compileSdk flutter.compileSdkVersion + ndkVersion '26.2.11394342' - compileSdkVersion flutter.compileSdkVersion - ndkVersion '26.1.10909125' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } kotlinOptions { jvmTarget = '1.8' @@ -50,8 +48,8 @@ android { defaultConfig { applicationId 'nl.viter.glider' - minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion + minSdk 21 + targetSdk flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } @@ -78,6 +76,5 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.google.android.material:material:1.10.0' + implementation 'com.google.android.material:material:1.11.0' } diff --git a/android/build.gradle b/android/build.gradle index 639493e8..bc157bd1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.9.21' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/android/settings.gradle b/android/settings.gradle index 44e62bcf..9e87bc8d 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.23" apply false +} + +include ":app" From ba14879023039506e914916174d0ea057343c8a4 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 19:34:09 +0200 Subject: [PATCH 129/145] Use `super_sliver_list` for sliver lists --- .../super_sliver_list_extension.dart | 24 ++++++++++++++ lib/common/mixins/paginated_list_mixin.dart | 8 ++--- lib/favorites/view/favorites_shell_page.dart | 7 ++-- lib/inbox/view/inbox_shell_page.dart | 7 ++-- lib/item/view/item_page.dart | 20 ++++-------- lib/item_tree/view/sliver_item_tree_body.dart | 11 +++++-- lib/stories/view/stories_shell_page.dart | 7 ++-- .../view/sliver_stories_search_body.dart | 7 ++-- .../view/story_item_search_view.dart | 3 +- .../view/sliver_story_similar_body.dart | 3 +- lib/user/view/user_page.dart | 7 ++-- .../view/user_item_search_view.dart | 3 +- pubspec.lock | 32 ++++++++++++------- pubspec.yaml | 1 + 14 files changed, 94 insertions(+), 46 deletions(-) create mode 100644 lib/app/extensions/super_sliver_list_extension.dart diff --git a/lib/app/extensions/super_sliver_list_extension.dart b/lib/app/extensions/super_sliver_list_extension.dart new file mode 100644 index 00000000..65ea1b1e --- /dev/null +++ b/lib/app/extensions/super_sliver_list_extension.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:super_sliver_list/super_sliver_list.dart'; + +extension SuperSliverListExtension on SuperSliverList { + static SuperSliverList builder({ + ListController? listController, + required NullableIndexedWidgetBuilder itemBuilder, + required int itemCount, + }) => + SuperSliverList( + listController: listController, + delegate: SliverChildBuilderDelegate( + itemBuilder, + childCount: itemCount, + ), + extentPrecalculationPolicy: AlwaysPrecalculateExtentPolicy(), + layoutKeptAliveChildren: true, + ); +} + +class AlwaysPrecalculateExtentPolicy extends ExtentPrecalculationPolicy { + @override + bool shouldPrecalculateExtents(ExtentPrecalculationContext context) => true; +} diff --git a/lib/common/mixins/paginated_list_mixin.dart b/lib/common/mixins/paginated_list_mixin.dart index b60a2c6e..e5c16f6a 100644 --- a/lib/common/mixins/paginated_list_mixin.dart +++ b/lib/common/mixins/paginated_list_mixin.dart @@ -1,11 +1,11 @@ import 'package:glider/common/mixins/data_mixin.dart'; -const _pageSize = 30; - mixin PaginatedListMixin on DataMixin> { + static const pageSize = 30; + int get page; - Iterable? get loadedData => data?.take(page * _pageSize); + Iterable? get loadedData => data?.take(page * pageSize); - Iterable? get currentPageData => loadedData?.skip((page - 1) * _pageSize); + Iterable? get currentPageData => loadedData?.skip((page - 1) * pageSize); } diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index 48a3cf34..5f0a41b3 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -3,10 +3,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.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/mixins/data_mixin.dart'; +import 'package:glider/common/mixins/paginated_list_mixin.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; import 'package:glider/favorites/cubit/favorites_cubit.dart'; @@ -138,14 +140,15 @@ class _SliverFavoritesBody extends StatelessWidget { builder: (context, state) => BlocBuilder( bloc: _settingsCubit, builder: (context, settingsState) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( + loading: () => SuperSliverListExtension.builder( + itemCount: PaginatedListMixin.pageSize, itemBuilder: (context, index) => ItemLoadingTile( type: ItemType.story, storyLines: settingsState.storyLines, useLargeStoryStyle: settingsState.useLargeStoryStyle, ), ), - nonEmpty: () => SliverList.builder( + nonEmpty: () => SuperSliverListExtension.builder( itemCount: state.data!.length, itemBuilder: (context, index) { final id = state.data![index]; diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 340e4121..4e0fd999 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -3,10 +3,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.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/mixins/data_mixin.dart'; +import 'package:glider/common/mixins/paginated_list_mixin.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; import 'package:glider/inbox/cubit/inbox_cubit.dart'; @@ -137,11 +139,12 @@ class _SliverInboxBody extends StatelessWidget { return BlocBuilder( bloc: _inboxCubit, builder: (context, state) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( + loading: () => SuperSliverListExtension.builder( + itemCount: PaginatedListMixin.pageSize, itemBuilder: (context, index) => const ItemLoadingTile(type: ItemType.comment), ), - nonEmpty: () => SliverList.builder( + nonEmpty: () => SuperSliverListExtension.builder( itemCount: state.data!.length, itemBuilder: (context, index) { final (parentId, id) = state.data![index]; diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index 15b6207d..53e0af30 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -535,20 +535,12 @@ class _SliverItemBody extends StatelessWidget { return BlocBuilder( bloc: _itemCubit, builder: (context, state) => state.whenOrDefaultSlivers( - loading: () => SliverMainAxisGroup( - slivers: [ - SliverToBoxAdapter( - child: ItemLoadingTile( - type: ItemType.story, - style: ItemStyle.secondary, - padding: AppSpacing.defaultTilePadding.copyWith(top: 0), - ), - ), - SliverList.builder( - itemBuilder: (context, index) => - const ItemLoadingTile(type: ItemType.comment), - ), - ], + loading: () => SliverToBoxAdapter( + child: ItemLoadingTile( + type: ItemType.story, + style: ItemStyle.secondary, + padding: AppSpacing.defaultTilePadding.copyWith(top: 0), + ), ), success: () => SliverMainAxisGroup( slivers: [ diff --git a/lib/item_tree/view/sliver_item_tree_body.dart b/lib/item_tree/view/sliver_item_tree_body.dart index f9ad44ab..c8b01155 100644 --- a/lib/item_tree/view/sliver_item_tree_body.dart +++ b/lib/item_tree/view/sliver_item_tree_body.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/mixins/data_mixin.dart'; @@ -11,6 +12,7 @@ import 'package:glider/item_tree/cubit/item_tree_cubit.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; import 'package:glider/settings/cubit/settings_cubit.dart'; import 'package:glider_domain/glider_domain.dart'; +import 'package:super_sliver_list/super_sliver_list.dart'; class SliverItemTreeBody extends StatelessWidget { const SliverItemTreeBody( @@ -19,6 +21,7 @@ class SliverItemTreeBody extends StatelessWidget { this._authCubit, this._settingsCubit, { super.key, + this.listController, this.childCount, this.storyUsername, }); @@ -27,6 +30,7 @@ class SliverItemTreeBody extends StatelessWidget { final ItemCubitFactory _itemCubitFactory; final AuthCubit _authCubit; final SettingsCubit _settingsCubit; + final ListController? listController; final int? childCount; final String? storyUsername; @@ -47,14 +51,15 @@ class SliverItemTreeBody extends StatelessWidget { } }, builder: (context, state) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( - itemCount: childCount, + loading: () => SuperSliverListExtension.builder( + itemCount: childCount ?? 0, itemBuilder: (context, index) => const IndentedWidget( depth: 1, child: ItemLoadingTile(type: ItemType.comment), ), ), - nonEmpty: () => SliverList.builder( + nonEmpty: () => SuperSliverListExtension.builder( + listController: listController, itemCount: state.viewableData!.length, itemBuilder: (context, index) { final descendant = state.viewableData![index]; diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index d6420b90..25284902 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -3,11 +3,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; +import 'package:glider/common/mixins/paginated_list_mixin.dart'; import 'package:glider/common/models/status.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/refreshable_scroll_view.dart'; @@ -246,7 +248,8 @@ class _SliverStoriesBody extends StatelessWidget { return BlocBuilder( bloc: _storiesCubit, builder: (context, state) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( + loading: () => SuperSliverListExtension.builder( + itemCount: PaginatedListMixin.pageSize, itemBuilder: (context, index) => BlocBuilder( bloc: _settingsCubit, @@ -261,7 +264,7 @@ class _SliverStoriesBody extends StatelessWidget { ), nonEmpty: () => SliverMainAxisGroup( slivers: [ - SliverList.builder( + SuperSliverListExtension.builder( itemCount: state.loadedData!.length, itemBuilder: (context, index) { final id = state.loadedData![index]; diff --git a/lib/stories_search/view/sliver_stories_search_body.dart b/lib/stories_search/view/sliver_stories_search_body.dart index 0412e9be..0e0490ed 100644 --- a/lib/stories_search/view/sliver_stories_search_body.dart +++ b/lib/stories_search/view/sliver_stories_search_body.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.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/mixins/data_mixin.dart'; +import 'package:glider/common/mixins/paginated_list_mixin.dart'; import 'package:glider/item/models/item_style.dart'; import 'package:glider/item/widgets/item_loading_tile.dart'; import 'package:glider/item/widgets/item_tile.dart'; @@ -33,7 +35,8 @@ class SliverStoriesSearchBody extends StatelessWidget { return BlocBuilder( bloc: _storiesSearchBloc, builder: (context, state) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( + loading: () => SuperSliverListExtension.builder( + itemCount: PaginatedListMixin.pageSize, itemBuilder: (context, index) => BlocBuilder( bloc: _settingsCubit, @@ -48,7 +51,7 @@ class SliverStoriesSearchBody extends StatelessWidget { ), nonEmpty: () => SliverMainAxisGroup( slivers: [ - SliverList.builder( + SuperSliverListExtension.builder( itemCount: state.loadedData!.length, itemBuilder: (context, index) { final id = state.loadedData![index]; diff --git a/lib/story_item_search/view/story_item_search_view.dart b/lib/story_item_search/view/story_item_search_view.dart index c7124b7a..974be43c 100644 --- a/lib/story_item_search/view/story_item_search_view.dart +++ b/lib/story_item_search/view/story_item_search_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/mixins/data_mixin.dart'; @@ -64,7 +65,7 @@ class _SliverStoryItemSearchBody extends StatelessWidget { return BlocBuilder( bloc: _storyItemSearchBloc, builder: (context, state) => state.whenOrDefaultSlivers( - nonEmpty: () => SliverList.builder( + nonEmpty: () => SuperSliverListExtension.builder( itemCount: state.data!.length, itemBuilder: (context, index) { final id = state.data![index]; diff --git a/lib/story_similar/view/sliver_story_similar_body.dart b/lib/story_similar/view/sliver_story_similar_body.dart index 8ea8b3ee..4e6f4b9c 100644 --- a/lib/story_similar/view/sliver_story_similar_body.dart +++ b/lib/story_similar/view/sliver_story_similar_body.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.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'; @@ -69,7 +70,7 @@ class SliverStorySimilarBody extends StatelessWidget { ), ), ), - SliverList.builder( + SuperSliverListExtension.builder( itemCount: state.data!.length, itemBuilder: (context, index) { final id = state.data![index]; diff --git a/lib/user/view/user_page.dart b/lib/user/view/user_page.dart index 57f441fd..fc763be7 100644 --- a/lib/user/view/user_page.dart +++ b/lib/user/view/user_page.dart @@ -3,11 +3,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/mixins/data_mixin.dart'; +import 'package:glider/common/mixins/paginated_list_mixin.dart'; import 'package:glider/common/models/status.dart'; import 'package:glider/common/widgets/app_bar_progress_indicator.dart'; import 'package:glider/common/widgets/failure_widget.dart'; @@ -358,7 +360,8 @@ class _SliverUserBody extends StatelessWidget { return BlocBuilder( bloc: _userCubit, builder: (context, state) => state.whenOrDefaultSlivers( - loading: () => SliverList.builder( + loading: () => SuperSliverListExtension.builder( + itemCount: PaginatedListMixin.pageSize, itemBuilder: (context, index) => const ItemLoadingTile(type: ItemType.story), ), @@ -374,7 +377,7 @@ class _SliverUserBody extends StatelessWidget { ), ), if (state.data?.submittedIds case final submittedIds?) - SliverList.builder( + SuperSliverListExtension.builder( itemCount: submittedIds.length, itemBuilder: (context, index) { final id = submittedIds[index]; diff --git a/lib/user_item_search/view/user_item_search_view.dart b/lib/user_item_search/view/user_item_search_view.dart index 36f19fd6..e47dd772 100644 --- a/lib/user_item_search/view/user_item_search_view.dart +++ b/lib/user_item_search/view/user_item_search_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:glider/app/container/app_container.dart'; +import 'package:glider/app/extensions/super_sliver_list_extension.dart'; import 'package:glider/app/models/app_route.dart'; import 'package:glider/auth/cubit/auth_cubit.dart'; import 'package:glider/common/mixins/data_mixin.dart'; @@ -64,7 +65,7 @@ class _SliverUserItemSearchBody extends StatelessWidget { return BlocBuilder( bloc: _userItemSearchBloc, builder: (context, state) => state.whenOrDefaultSlivers( - nonEmpty: () => SliverList.builder( + nonEmpty: () => SuperSliverListExtension.builder( itemCount: state.data!.length, itemBuilder: (context, index) { final id = state.data![index]; diff --git a/pubspec.lock b/pubspec.lock index 1d864574..69625f88 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -623,26 +623,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: cdd14e3836065a1f6302a236ec8b5f700695c803c57ae11a1c84df31e6bcf831 + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.dev" source: hosted - version: "10.0.3" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9b2ef90589911d665277464e0482b209d39882dffaaf4ef69a3561a3354b2ebc" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: fd3cd66cb2bcd7b50dcd3b413af49d78051f809c8b3f6e047962765c15a0d23d + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "2.0.1" leancode_lint: dependency: "direct dev" description: @@ -927,10 +927,10 @@ packages: dependency: "direct main" description: name: scrollview_observer - sha256: "69de9e6e4900a2d21dd9f593c597a1418e9f463cceeac6677ba83b007bf1f3e9" + sha256: "9fde897d25f05f8f8295807eae96ac337aae70d6860714ca8702a430d735ac4a" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" share_plus: dependency: "direct main" description: @@ -1064,6 +1064,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + super_sliver_list: + dependency: "direct main" + description: + name: super_sliver_list + sha256: b1e1e64d08ce40e459b9bb5d9f8e361617c26b8c9f3bb967760b0f436b6e3f56 + url: "https://pub.dev" + source: hosted + version: "0.4.1" synchronized: dependency: transitive description: @@ -1084,10 +1092,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -1188,10 +1196,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "13.0.0" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index cff451b7..dfbb9e14 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: share_plus: ^7.2.2 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 + super_sliver_list: ^0.4.1 url_launcher: ^6.2.5 dev_dependencies: From ae287fdec8f2b6aa490c4f32fed4dad81f09caff Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 19:34:33 +0200 Subject: [PATCH 130/145] Upgrade Flutter and dependencies --- .fvmrc | 2 +- .vscode/settings.json | 2 +- ios/Podfile.lock | 6 +- packages/glider_data/pubspec.yaml | 6 +- packages/glider_domain/pubspec.yaml | 4 +- pubspec.lock | 104 ++++++++++++++-------------- pubspec.yaml | 24 +++---- 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/.fvmrc b/.fvmrc index 124e8756..a14d81b9 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.20.0-1.2.pre" + "flutter": "3.21.0-0.0.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 8c1d337a..aa9d5ce9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.20.0-1.2.pre" + "dart.flutterSdkPath": ".fvm/versions/3.21.0-0.0.pre" } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 02d2502c..bc26795d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -61,14 +61,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: - device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 + device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c - package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 + package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 + share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index 47dba747..f721e9fc 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -10,10 +10,10 @@ dependencies: compute: ^1.0.2 flutter_secure_storage: ^9.0.0 html: ^0.15.4 - http: ^1.2.0 - shared_preferences: ^2.2.1 + http: ^1.2.1 + shared_preferences: ^2.2.2 dev_dependencies: - custom_lint: ^0.6.2 + custom_lint: ^0.6.4 dependency_validator: ^3.2.3 leancode_lint: ^11.0.0 diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index 7191861d..7ca0a1a4 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -12,11 +12,11 @@ dependencies: path: ../glider_data html: ^0.15.4 material_color_utilities: any - package_info_plus: ^5.0.1 + package_info_plus: ^6.0.0 pub_semver: ^2.1.4 rxdart: ^0.27.7 dev_dependencies: - custom_lint: ^0.6.2 + custom_lint: ^0.6.4 dependency_validator: ^3.2.3 leancode_lint: ^11.0.0 diff --git a/pubspec.lock b/pubspec.lock index 69625f88..b0a54ba4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: bloc - sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.dev" source: hosted - version: "8.1.3" + version: "8.1.4" bloc_presentation: dependency: "direct main" description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: cross_file - sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.dev" source: hosted - version: "0.3.3+8" + version: "0.3.4+1" crypto: dependency: transitive description: @@ -205,26 +205,26 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "445242371d91d2e24bd7b82e3583a2c05610094ba2d0575262484ad889c8f981" + sha256: "7c0aec12df22f9082146c354692056677f1e70bc43471644d1fdb36c6fdda799" url: "https://pub.dev" source: hosted - version: "0.6.2" + version: "0.6.4" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "4c0aed2a3491096e91cf1281923ba1b6814993f16dde0fd60f697925225bbbd6" + sha256: d7dc41e709dde223806660268678be7993559e523eb3164e2a1425fd6f7615a9 url: "https://pub.dev" source: hosted - version: "0.6.2" + version: "0.6.4" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: ce5d6215f4e143f7780ce53f73dfa6fc503f39d2d30bef76c48be9ac1a09d9a6 + sha256: a85e8f78f4c52f6c63cdaf8c872eb573db0231dcdf3c3a5906d493c1f8bc20e6 url: "https://pub.dev" source: hosted - version: "0.6.2" + version: "0.6.3" dart_style: dependency: transitive description: @@ -245,10 +245,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84" url: "https://pub.dev" source: hosted - version: "9.1.2" + version: "10.0.1" device_info_plus_platform_interface: dependency: transitive description: @@ -314,18 +314,18 @@ packages: dependency: "direct main" description: name: flutter_adaptive_scaffold - sha256: "4257142551ec97761d44f4258b8ad53ac76593dd0992197b876769df19f8a018" + sha256: "600bbe237530a249f957f7d0f36273c20bd38d137e28e098c5231c30cadbe927" url: "https://pub.dev" source: hosted - version: "0.1.8" + version: "0.1.10+1" flutter_bloc: dependency: "direct main" description: name: flutter_bloc - sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1" + sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 url: "https://pub.dev" source: hosted - version: "8.1.4" + version: "8.1.5" flutter_displaymode: dependency: "direct main" description: @@ -407,10 +407,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db + sha256: "87e11b9df25a42e2db315b8b7a51fae8e66f57a4b2f50ec4b822d0fa155e6b52" url: "https://pub.dev" source: hosted - version: "0.6.21" + version: "0.6.22" flutter_secure_storage: dependency: "direct main" description: @@ -511,10 +511,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "170c46e237d6eb0e6e9f0e8b3f56101e14fb64f787016e42edd74c39cf8b176a" + sha256: "5ed2687bc961f33a752017ccaa7edead3e5601b28b6376a5901bf24728556b85" url: "https://pub.dev" source: hosted - version: "13.2.0" + version: "13.2.2" google_fonts: dependency: "direct main" description: @@ -559,10 +559,10 @@ packages: dependency: "direct main" description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" http_parser: dependency: transitive description: @@ -575,10 +575,10 @@ packages: dependency: "direct main" description: name: hydrated_bloc - sha256: "00a2099680162e74b5a836b8a7f446e478520a9cae9f6032e028ad8129f4432d" + sha256: af35b357739fe41728df10bec03aad422cdc725a1e702e03af9d2a41ea05160c url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "9.1.5" image: dependency: transitive description: @@ -623,26 +623,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" leancode_lint: dependency: "direct dev" description: @@ -687,18 +687,18 @@ packages: dependency: "direct dev" description: name: melos - sha256: "7266e9fc9fee5f4a0c075e5cec375c00736dfc944358f533b740b93b3d8d681e" + sha256: a0cb264096a315e4acdb66ae75ee594a76c97fe15ce9ae469f6c58c6c4b2be87 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "5.3.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -735,10 +735,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" + sha256: cb44f49b6e690fa766f023d5b22cac6b9affe741dd792b6ac7ad4fabe0d7b097 url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "6.0.0" package_info_plus_platform_interface: dependency: transitive description: @@ -935,18 +935,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" + sha256: "05ec043470319bfbabe0adbc90d3a84cbff0426b9d9f3a6e2ad3e131fa5fa629" url: "https://pub.dev" source: hosted - version: "7.2.2" + version: "8.0.2" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 + sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496" url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.4.0" shared_preferences: dependency: "direct main" description: @@ -991,10 +991,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.0" shared_preferences_windows: dependency: transitive description: @@ -1092,10 +1092,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -1164,10 +1164,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b + sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.0" url_launcher_windows: dependency: transitive description: @@ -1196,10 +1196,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.0.0" watcher: dependency: transitive description: @@ -1212,18 +1212,18 @@ packages: dependency: transitive description: name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "0.5.1" win32: dependency: transitive description: name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.4.0" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index dfbb9e14..ed5e5cb2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,53 +7,53 @@ environment: sdk: ">=3.3.0-0 <4.0.0" dependencies: - bloc: ^8.1.3 + bloc: ^8.1.4 bloc_presentation: ^1.0.0 clock: ^1.1.1 collection: any - device_info_plus: ^9.1.2 + device_info_plus: ^10.0.1 dynamic_color: ^1.7.0 equatable: ^2.0.5 flutter: sdk: flutter - flutter_adaptive_scaffold: ^0.1.8 - flutter_bloc: ^8.1.4 + flutter_adaptive_scaffold: ^0.1.10+1 + flutter_bloc: ^8.1.5 flutter_displaymode: ^0.6.0 flutter_inappwebview: ^6.0.0 flutter_localizations: sdk: flutter - flutter_markdown: ^0.6.21 + flutter_markdown: ^0.6.22 flutter_secure_storage: ^9.0.0 formz: ^0.7.0 glider_data: path: packages/glider_data glider_domain: path: packages/glider_domain - go_router: ^13.2.0 + go_router: ^13.2.2 google_fonts: ^6.2.1 - http: ^1.2.0 - hydrated_bloc: ^9.1.4 + http: ^1.2.1 + hydrated_bloc: ^9.1.5 intl: any markdown: ^7.2.2 material_color_utilities: any - package_info_plus: ^5.0.1 + package_info_plus: ^6.0.0 path_provider: ^2.1.2 pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 scrollview_observer: ^1.19.0 - share_plus: ^7.2.2 + share_plus: ^8.0.2 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 super_sliver_list: ^0.4.1 url_launcher: ^6.2.5 dev_dependencies: - custom_lint: ^0.6.2 + custom_lint: ^0.6.4 dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 leancode_lint: ^11.0.0 - melos: ^4.1.0 + melos: ^5.3.0 flutter: uses-material-design: true From 75b64e0c599fa98471abcb24d5d2e380b8b15803 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 19:35:24 +0200 Subject: [PATCH 131/145] Fix theming issues --- lib/app/view/app.dart | 45 ++++++++++++------- .../widgets/app_bar_progress_indicator.dart | 5 ++- lib/common/widgets/decorated_card.dart | 3 +- lib/favorites/view/favorites_shell_page.dart | 1 + lib/inbox/view/inbox_shell_page.dart | 1 + lib/item/widgets/item_data_tile.dart | 2 +- lib/stories/view/stories_shell_page.dart | 1 + .../view/catch_up_shell_page.dart | 1 + 8 files changed, 41 insertions(+), 18 deletions(-) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index c5d819c7..6a83ca5e 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -62,23 +62,33 @@ class App extends StatelessWidget { ColorScheme? dynamicColorScheme, Brightness brightness, ) { - final backgroundColor = state.usePureBackground - ? brightness == Brightness.dark - ? Colors.black - : Colors.white - : null; + final colorScheme = switch (state) { + SettingsState(useDynamicTheme: true) when dynamicColorScheme != null => + dynamicColorScheme, + SettingsState(useDynamicTheme: true) => ColorScheme.fromSeed( + seedColor: state.themeColor, + brightness: brightness, + ), + final state => state.themeVariant.toColorScheme( + state.themeColor, + brightness, + ), + } + .copyWith( + surface: state.usePureBackground + ? brightness == Brightness.dark + ? Colors.black + : Colors.white + : null, + ); return ThemeData( visualDensity: VisualDensity.comfortable, brightness: brightness, - colorScheme: (state.useDynamicTheme && dynamicColorScheme != null - ? dynamicColorScheme - : state.themeVariant.toColorScheme(state.themeColor, brightness)) - .copyWith( - background: backgroundColor, - ), + colorScheme: colorScheme, + scaffoldBackgroundColor: colorScheme.surface, textTheme: GoogleFonts.getTextTheme(state.font, const TextTheme()), appBarTheme: AppBarTheme( - color: backgroundColor, + color: colorScheme.surface, centerTitle: false, ), badgeTheme: BadgeThemeData( @@ -91,6 +101,11 @@ class App extends StatelessWidget { // Material 3 dictates a maximum width for bottom sheets. constraints: BoxConstraints(maxWidth: 640), ), + chipTheme: ChipThemeData( + iconTheme: IconThemeData( + color: colorScheme.onSurface, + ), + ), inputDecorationTheme: const InputDecorationTheme(filled: true), menuButtonTheme: const MenuButtonThemeData( style: ButtonStyle( @@ -109,13 +124,13 @@ class App extends StatelessWidget { ), ), navigationBarTheme: NavigationBarThemeData( - backgroundColor: backgroundColor, + backgroundColor: colorScheme.surface, ), navigationRailTheme: NavigationRailThemeData( - backgroundColor: backgroundColor, + backgroundColor: colorScheme.surface, ), searchViewTheme: SearchViewThemeData( - backgroundColor: backgroundColor, + backgroundColor: colorScheme.surface, ), ); } diff --git a/lib/common/widgets/app_bar_progress_indicator.dart b/lib/common/widgets/app_bar_progress_indicator.dart index 10b684e5..b8a39ee0 100644 --- a/lib/common/widgets/app_bar_progress_indicator.dart +++ b/lib/common/widgets/app_bar_progress_indicator.dart @@ -27,7 +27,10 @@ class AppBarProgressIndicator, ), child: TickerMode( enabled: isLoading, - child: const LinearProgressIndicator(minHeight: _height), + child: const LinearProgressIndicator( + minHeight: _height, + backgroundColor: Colors.transparent, + ), ), ), ), diff --git a/lib/common/widgets/decorated_card.dart b/lib/common/widgets/decorated_card.dart index 45d3fbcd..4f4ba9af 100644 --- a/lib/common/widgets/decorated_card.dart +++ b/lib/common/widgets/decorated_card.dart @@ -64,7 +64,8 @@ enum _CardType { final double? elevation; Color? color(BuildContext context) => switch (this) { - _CardType.filled => Theme.of(context).colorScheme.surfaceVariant, + _CardType.filled => + Theme.of(context).colorScheme.surfaceContainerHighest, _ => null, }; diff --git a/lib/favorites/view/favorites_shell_page.dart b/lib/favorites/view/favorites_shell_page.dart index 5f0a41b3..6fe3308f 100644 --- a/lib/favorites/view/favorites_shell_page.dart +++ b/lib/favorites/view/favorites_shell_page.dart @@ -48,6 +48,7 @@ class _FavoritesShellPageState extends State { @override Widget build(BuildContext context) { return Material( + type: MaterialType.transparency, child: RefreshableScrollView( onRefresh: () async => unawaited(widget._favoritesCubit.load()), slivers: [ diff --git a/lib/inbox/view/inbox_shell_page.dart b/lib/inbox/view/inbox_shell_page.dart index 4e0fd999..766cbda1 100644 --- a/lib/inbox/view/inbox_shell_page.dart +++ b/lib/inbox/view/inbox_shell_page.dart @@ -49,6 +49,7 @@ class _InboxShellPageState extends State { @override Widget build(BuildContext context) { return Material( + type: MaterialType.transparency, child: RefreshableScrollView( onRefresh: () async => unawaited(widget._inboxCubit.load()), slivers: [ diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 8f59e879..10400f31 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -567,7 +567,7 @@ class _MetadataActionButton extends StatelessWidget { child: ElevatedButton( onPressed: onTap, style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.onBackground, + foregroundColor: Theme.of(context).colorScheme.onSurface, padding: const EdgeInsets.symmetric(horizontal: AppSpacing.m), minimumSize: const Size.square(40), visualDensity: const VisualDensity( diff --git a/lib/stories/view/stories_shell_page.dart b/lib/stories/view/stories_shell_page.dart index 25284902..aaad2fe6 100644 --- a/lib/stories/view/stories_shell_page.dart +++ b/lib/stories/view/stories_shell_page.dart @@ -56,6 +56,7 @@ class _StoriesShellPageState extends State { @override Widget build(BuildContext context) { return Material( + type: MaterialType.transparency, child: RefreshableScrollView( onRefresh: () async => unawaited(widget._storiesCubit.load()), slivers: [ diff --git a/lib/stories_search/view/catch_up_shell_page.dart b/lib/stories_search/view/catch_up_shell_page.dart index 089f5759..0acdbc8c 100644 --- a/lib/stories_search/view/catch_up_shell_page.dart +++ b/lib/stories_search/view/catch_up_shell_page.dart @@ -40,6 +40,7 @@ class _CatchUpShellPageState extends State { @override Widget build(BuildContext context) { return Material( + type: MaterialType.transparency, child: RefreshableScrollView( onRefresh: () async => widget._storiesSearchBloc.add(const LoadStoriesSearchEvent()), From 898fbf3c2b4e499bb53fe7f1a9fe91aae743a48b Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 19:37:28 +0200 Subject: [PATCH 132/145] Remove redundant custom autolink extension syntax --- lib/common/widgets/hacker_news_text.dart | 39 +----------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index 77727b12..82728fd8 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -35,7 +35,7 @@ class HackerNewsText extends StatelessWidget { [ HackerNewsAsteriskEscapeSyntax(), HackerNewsEmphasisSyntax.asterisk(), - HackerNewsAutolinkExtensionSyntax(), + md.AutolinkExtensionSyntax(), md.EscapeSyntax(), md.AutolinkSyntax(), md.EmailAutolinkSyntax(), @@ -205,43 +205,6 @@ class HackerNewsCodeBlockSyntax extends md.CodeBlockSyntax { RegExp get pattern => RegExp(r'^(?: | ?\t)(.*)$'); } -// Auto-linking should not be different from default Markdown, but a bug in its -// implementation causes it to not handle links directly after newlines. -class HackerNewsAutolinkExtensionSyntax extends md.AutolinkExtensionSyntax { - @override - bool tryMatch(md.InlineParser parser, [int? startMatchPos]) { - startMatchPos ??= parser.pos; - final startMatch = pattern.matchAsPrefix(parser.source, startMatchPos); - if (startMatch == null) { - return false; - } - - // When it is a link and it is not preceded by `*`, `_`, `~`, `(`, or `>`, - // it is invalid. See - // https://github.github.com/gfm/#extended-autolink-path-validation. - if (startMatch[1] != null && parser.pos > 0) { - final precededBy = String.fromCharCode(parser.charAt(parser.pos - 1)); - const validPrecedingChars = {'\n', ' ', '*', '_', '~', '(', '>'}; - if (validPrecedingChars.contains(precededBy) == false) { - return false; - } - } - - // When it is an email link and followed by `_` or `-`, it is invalid. See - // https://github.github.com/gfm/#example-633 - if (startMatch[2] != null && parser.source.length > startMatch.end) { - final followedBy = String.fromCharCode(parser.charAt(startMatch.end)); - const invalidFollowingChars = {'_', '-'}; - if (invalidFollowingChars.contains(followedBy)) { - return false; - } - } - - parser.writeText(); - return onMatch(parser, startMatch); - } -} - // Unlike default Markdown, double asterisks do not result in a strong tag. class HackerNewsEmphasisSyntax extends md.DelimiterSyntax { HackerNewsEmphasisSyntax.asterisk() From 25940490bdfc90da6ce59e8f9474460dd08f9346 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 19:38:08 +0200 Subject: [PATCH 133/145] Decode percent-encoded URLs --- lib/common/widgets/hacker_news_text.dart | 1 + .../glider_domain/lib/src/extensions/string_extension.dart | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/common/widgets/hacker_news_text.dart b/lib/common/widgets/hacker_news_text.dart index 82728fd8..930f8c1a 100644 --- a/lib/common/widgets/hacker_news_text.dart +++ b/lib/common/widgets/hacker_news_text.dart @@ -37,6 +37,7 @@ class HackerNewsText extends StatelessWidget { HackerNewsEmphasisSyntax.asterisk(), md.AutolinkExtensionSyntax(), md.EscapeSyntax(), + md.LinkSyntax(), md.AutolinkSyntax(), md.EmailAutolinkSyntax(), md.CodeSyntax(), diff --git a/packages/glider_domain/lib/src/extensions/string_extension.dart b/packages/glider_domain/lib/src/extensions/string_extension.dart index f7c62aab..0d1deeaf 100644 --- a/packages/glider_domain/lib/src/extensions/string_extension.dart +++ b/packages/glider_domain/lib/src/extensions/string_extension.dart @@ -7,10 +7,13 @@ extension StringExtension on String { } extension on html_dom.Node { + String get _url => attributes['href'] ?? text!; + String convert() => switch (this) { // "Urls become links, except in the text field of a submission." // We cheat by not handling submissions any differently. - html_dom.Element(localName: 'a') => attributes['href'] ?? text!, + // Unlike the website, we prefer showing the full URL. + html_dom.Element(localName: 'a') => '[$_url](${Uri.decodeFull(_url)})', // "Text surrounded by asterisks is italicized." html_dom.Element(localName: 'i') => '*${convertNodes()}*', // "Blank lines separate paragraphs." From 0bc52133d0c0e5563fe562bcd9c30e22e04b8827 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 20:13:29 +0200 Subject: [PATCH 134/145] Fix card color --- lib/common/widgets/decorated_card.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/common/widgets/decorated_card.dart b/lib/common/widgets/decorated_card.dart index 4f4ba9af..33e9206f 100644 --- a/lib/common/widgets/decorated_card.dart +++ b/lib/common/widgets/decorated_card.dart @@ -65,6 +65,8 @@ enum _CardType { Color? color(BuildContext context) => switch (this) { _CardType.filled => + // ignore: deprecated_member_use, dependent on `dynamic_color`'s + // `toColorScheme` supporting `surfaceContainerHighest`. Theme.of(context).colorScheme.surfaceContainerHighest, _ => null, }; From b23757737498e2cfcbd18387f1b3325653afa1b2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 20:36:29 +0200 Subject: [PATCH 135/145] Add more (mostly serif) fonts --- lib/settings/view/settings_page.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 1a727f73..9cc7642f 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -57,12 +57,18 @@ class _SettingsBody extends StatelessWidget { final SettingsCubit _settingsCubit; static const List _fonts = [ + 'EB Garamond', 'Fira Sans', 'IBM Plex Sans', + 'IBM Plex Serif', 'Inter', 'Noto Sans', + 'Noto Serif', 'Open Sans', 'Roboto', + 'Roboto Serif', + 'Source Sans 3', + 'Source Serif 4', ]; static final Uri _privacyPolicyUrl = AppUris.projectUri .replace(path: '${AppUris.projectUri.path}/blob/master/PRIVACY.md'); From e87e9a411cbec92b154b993f03efbcf9c8fdddb2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 20:37:39 +0200 Subject: [PATCH 136/145] Constrain preview panel to smaller screen portion --- lib/common/widgets/preview_bottom_panel.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common/widgets/preview_bottom_panel.dart b/lib/common/widgets/preview_bottom_panel.dart index 7dca9161..5ee4b454 100644 --- a/lib/common/widgets/preview_bottom_panel.dart +++ b/lib/common/widgets/preview_bottom_panel.dart @@ -22,12 +22,12 @@ class PreviewBottomPanel extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Flexible( - // Use half of the available height for the preview (if visible). + // Use a third of the available height for the preview (if visible). // The layout builder helps exclude any keyboard's view insets. child: LayoutBuilder( builder: (context, constraints) => ConstrainedBox( constraints: BoxConstraints( - maxHeight: constraints.maxHeight / 2, + maxHeight: constraints.maxHeight / 3, ), child: AnimatedVisibility.vertical( visible: visible, From 44d842376e5701bb9dd5d7dc4001afdf553b772f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:34:32 +0200 Subject: [PATCH 137/145] Bump softprops/action-gh-release from 1 to 2 (#193) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ca2f8da..4b298d85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,7 +76,7 @@ jobs: with: name: app-release.apk - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: body_path: fastlane/metadata/android/en-US/changelogs/${{ steps.pubspec.outputs.build-number }}.txt files: app-release.apk From 6e1613ac695413634e8268c6c52ececa1bc2e690 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 22:38:39 +0200 Subject: [PATCH 138/145] Actually fix card color --- lib/common/widgets/decorated_card.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/common/widgets/decorated_card.dart b/lib/common/widgets/decorated_card.dart index 33e9206f..f2185e6a 100644 --- a/lib/common/widgets/decorated_card.dart +++ b/lib/common/widgets/decorated_card.dart @@ -65,9 +65,9 @@ enum _CardType { Color? color(BuildContext context) => switch (this) { _CardType.filled => - // ignore: deprecated_member_use, dependent on `dynamic_color`'s - // `toColorScheme` supporting `surfaceContainerHighest`. - Theme.of(context).colorScheme.surfaceContainerHighest, + // Dependent on `dynamic_color` supporting `surfaceContainerHighest`. + // ignore: deprecated_member_use + Theme.of(context).colorScheme.surfaceVariant, _ => null, }; From 164dfae4c64dfef154b1e96fb340fcc80c349d73 Mon Sep 17 00:00:00 2001 From: Mosc Date: Tue, 2 Apr 2024 23:09:26 +0200 Subject: [PATCH 139/145] Fix previous/next buttons and eliminate dependency --- lib/item/view/item_page.dart | 116 ++++++++++++++++++----------------- pubspec.lock | 8 --- pubspec.yaml | 1 - 3 files changed, 60 insertions(+), 65 deletions(-) diff --git a/lib/item/view/item_page.dart b/lib/item/view/item_page.dart index 53e0af30..85be563e 100644 --- a/lib/item/view/item_page.dart +++ b/lib/item/view/item_page.dart @@ -29,8 +29,8 @@ import 'package:glider/story_similar/cubit/story_similar_cubit.dart'; import 'package:glider/story_similar/view/sliver_story_similar_body.dart'; import 'package:glider_domain/glider_domain.dart'; import 'package:go_router/go_router.dart'; -import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:sliver_tools/sliver_tools.dart'; +import 'package:super_sliver_list/super_sliver_list.dart'; class ItemPage extends StatefulWidget { ItemPage( @@ -61,11 +61,9 @@ class _ItemPageState extends State { late final StorySimilarCubit _storySimilarCubit; late final StoryItemSearchBloc _storyItemSearchBloc; late final ScrollController _scrollController; - late final SliverObserverController _sliverObserverController; + late final ListController _listController; final GlobalKey _bodyKey = GlobalKey(); - int index = 0; - @override void initState() { super.initState(); @@ -76,8 +74,7 @@ class _ItemPageState extends State { _storySimilarCubit = widget._storySimilarCubitFactory(widget.id); _storyItemSearchBloc = widget._storyItemSearchBlocFactory(widget.id); _scrollController = ScrollController(); - _sliverObserverController = - SliverObserverController(controller: _scrollController); + _listController = ListController(); } @override @@ -107,49 +104,42 @@ class _ItemPageState extends State { previous.useLargeStoryStyle != current.useLargeStoryStyle || previous.useThreadNavigation != current.useThreadNavigation, builder: (context, settingsState) => Scaffold( - body: SliverViewObserver( - controller: _sliverObserverController, - leadingOffset: MediaQuery.paddingOf(context).top, - onObserve: (observeModel) { - final index = observeModel.displayingChildIndexList.firstOrNull; - if (index != null) this.index = index; - }, - child: RefreshableScrollView( - scrollController: _scrollController, - onRefresh: () async => unawaited(_itemTreeCubit.load()), - toolbarHeight: _getToolbarHeight( - storyLines: settingsState.storyLines, - useLargeStoryStyle: settingsState.useLargeStoryStyle, + body: RefreshableScrollView( + scrollController: _scrollController, + onRefresh: () async => unawaited(_itemTreeCubit.load()), + toolbarHeight: _getToolbarHeight( + storyLines: settingsState.storyLines, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + ), + slivers: [ + _SliverItemAppBar( + _itemCubit, + _itemTreeCubit, + widget._itemCubitFactory, + _storyItemSearchBloc, + widget._authCubit, + widget._settingsCubit, + bodyKey: _bodyKey, + scrollController: _scrollController, + toolbarHeight: _getToolbarHeight( + storyLines: settingsState.storyLines, + useLargeStoryStyle: settingsState.useLargeStoryStyle, + ), ), - slivers: [ - _SliverItemAppBar( + SliverSafeArea( + top: false, + sliver: _SliverItemBody( _itemCubit, _itemTreeCubit, + _storySimilarCubit, widget._itemCubitFactory, - _storyItemSearchBloc, widget._authCubit, widget._settingsCubit, - bodyKey: _bodyKey, - scrollController: _scrollController, - toolbarHeight: _getToolbarHeight( - storyLines: settingsState.storyLines, - useLargeStoryStyle: settingsState.useLargeStoryStyle, - ), - ), - SliverSafeArea( - top: false, - sliver: _SliverItemBody( - _itemCubit, - _itemTreeCubit, - _storySimilarCubit, - widget._itemCubitFactory, - widget._authCubit, - widget._settingsCubit, - key: _bodyKey, - ), + key: _bodyKey, + listController: _listController, ), - ], - ), + ), + ], ), floatingActionButton: settingsState.useThreadNavigation ? Theme( @@ -168,21 +158,13 @@ class _ItemPageState extends State { children: [ FloatingActionButton.small( heroTag: null, - onPressed: () { - final index = _itemTreeCubit.state - .getPreviousRootChildIndex(index: this.index); - if (index != null) _animateTo(index: index); - }, + onPressed: _onPreviousPressed, tooltip: context.l10n.previousRootChild, child: const Icon(Icons.keyboard_arrow_up_outlined), ), FloatingActionButton.small( heroTag: null, - onPressed: () { - final index = _itemTreeCubit.state - .getNextRootChildIndex(index: this.index); - if (index != null) _animateTo(index: index); - }, + onPressed: _onNextPressed, tooltip: context.l10n.nextRootChild, child: const Icon(Icons.keyboard_arrow_down_outlined), ), @@ -195,12 +177,31 @@ class _ItemPageState extends State { ); } + Future _onPreviousPressed() async { + if (_listController.unobstructedVisibleRange case final visibleRange?) { + if (_itemTreeCubit.state.getPreviousRootChildIndex(index: visibleRange.$1) + case final index?) { + await _animateTo(index: index); + } + } + } + + Future _onNextPressed() async { + if (_listController.unobstructedVisibleRange case final visibleRange?) { + if (_itemTreeCubit.state.getNextRootChildIndex(index: visibleRange.$1) + case final index?) { + await _animateTo(index: index); + } + } + } + Future _animateTo({required int index}) async => - _sliverObserverController.animateTo( + _listController.animateToItem( index: index, - duration: AppAnimation.emphasized.duration, - curve: AppAnimation.emphasized.easing, - offset: (targetOffset) => MediaQuery.paddingOf(context).top, + scrollController: _scrollController, + duration: (estimatedDistance) => AppAnimation.emphasized.duration, + curve: (estimatedDistance) => AppAnimation.emphasized.easing, + alignment: 0, ); } @@ -521,6 +522,7 @@ class _SliverItemBody extends StatelessWidget { this._authCubit, this._settingsCubit, { super.key, + this.listController, }); final ItemCubit _itemCubit; @@ -529,6 +531,7 @@ class _SliverItemBody extends StatelessWidget { final ItemCubitFactory _itemCubitFactory; final AuthCubit _authCubit; final SettingsCubit _settingsCubit; + final ListController? listController; @override Widget build(BuildContext context) { @@ -572,6 +575,7 @@ class _SliverItemBody extends StatelessWidget { _itemCubitFactory, _authCubit, _settingsCubit, + listController: listController, childCount: state.data?.childIds?.length, storyUsername: state.data?.storyUsername, ), diff --git a/pubspec.lock b/pubspec.lock index b0a54ba4..204ab0a9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -923,14 +923,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" - scrollview_observer: - dependency: "direct main" - description: - name: scrollview_observer - sha256: "9fde897d25f05f8f8295807eae96ac337aae70d6860714ca8702a430d735ac4a" - url: "https://pub.dev" - source: hosted - version: "1.19.1" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ed5e5cb2..c26bd5c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,6 @@ dependencies: pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 - scrollview_observer: ^1.19.0 share_plus: ^8.0.2 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 From 1b4c5b2a948b7b54338c06b32a138923480f088e Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 3 Apr 2024 08:41:25 +0200 Subject: [PATCH 140/145] Add one more serif font --- lib/settings/view/settings_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart index 9cc7642f..cc7044bf 100644 --- a/lib/settings/view/settings_page.dart +++ b/lib/settings/view/settings_page.dart @@ -57,6 +57,7 @@ class _SettingsBody extends StatelessWidget { final SettingsCubit _settingsCubit; static const List _fonts = [ + 'Crimson Pro', 'EB Garamond', 'Fira Sans', 'IBM Plex Sans', From e0bc828cde6f906749fdcdd3ebf803ffb4aa5805 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 10 Apr 2024 17:21:12 +0200 Subject: [PATCH 141/145] Upgrade Flutter and dependencies --- .fvmrc | 2 +- .vscode/settings.json | 2 +- packages/glider_data/pubspec.yaml | 2 +- packages/glider_domain/pubspec.yaml | 4 +-- pubspec.lock | 48 ++++++++++++++--------------- pubspec.yaml | 12 ++++---- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.fvmrc b/.fvmrc index a14d81b9..cededd1e 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.21.0-0.0.pre" + "flutter": "3.22.0-0.1.pre" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index aa9d5ce9..b0186fc1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.21.0-0.0.pre" + "dart.flutterSdkPath": ".fvm/versions/3.22.0-0.1.pre" } \ No newline at end of file diff --git a/packages/glider_data/pubspec.yaml b/packages/glider_data/pubspec.yaml index f721e9fc..a21350cb 100644 --- a/packages/glider_data/pubspec.yaml +++ b/packages/glider_data/pubspec.yaml @@ -16,4 +16,4 @@ dependencies: dev_dependencies: custom_lint: ^0.6.4 dependency_validator: ^3.2.3 - leancode_lint: ^11.0.0 + leancode_lint: ^12.0.0 diff --git a/packages/glider_domain/pubspec.yaml b/packages/glider_domain/pubspec.yaml index 7ca0a1a4..c34a7fec 100644 --- a/packages/glider_domain/pubspec.yaml +++ b/packages/glider_domain/pubspec.yaml @@ -12,11 +12,11 @@ dependencies: path: ../glider_data html: ^0.15.4 material_color_utilities: any - package_info_plus: ^6.0.0 + package_info_plus: ^7.0.0 pub_semver: ^2.1.4 rxdart: ^0.27.7 dev_dependencies: custom_lint: ^0.6.4 dependency_validator: ^3.2.3 - leancode_lint: ^11.0.0 + leancode_lint: ^12.0.0 diff --git a/pubspec.lock b/pubspec.lock index 204ab0a9..fda37581 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -245,10 +245,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84" + sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 url: "https://pub.dev" source: hosted - version: "10.0.1" + version: "10.1.0" device_info_plus_platform_interface: dependency: transitive description: @@ -407,10 +407,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "87e11b9df25a42e2db315b8b7a51fae8e66f57a4b2f50ec4b822d0fa155e6b52" + sha256: "31c12de79262b5431c5492e9c89948aa789158435f707d3519a7fdef6af28af7" url: "https://pub.dev" source: hosted - version: "0.6.22" + version: "0.6.22+1" flutter_secure_storage: dependency: "direct main" description: @@ -511,10 +511,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "5ed2687bc961f33a752017ccaa7edead3e5601b28b6376a5901bf24728556b85" + sha256: f6ba8eed5fa831e461122de577d4a26674a1d836e2956abe6c0f6c4d952e6673 url: "https://pub.dev" source: hosted - version: "13.2.2" + version: "13.2.3" google_fonts: dependency: "direct main" description: @@ -591,10 +591,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -647,10 +647,10 @@ packages: dependency: "direct dev" description: name: leancode_lint - sha256: a0130e0dbe5d5ea2f03f7ba7fa097f0fd44be9dc43d3d7badd6d09cc50015384 + sha256: c8f7d0e445d0080c76f28ede88e6c94929fd8e06fffa7f6cb4fdacfd2b6bb32c url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "12.0.0" logging: dependency: transitive description: @@ -735,18 +735,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: cb44f49b6e690fa766f023d5b22cac6b9affe741dd792b6ac7ad4fabe0d7b097 + sha256: "2c582551839386fa7ddbc7770658be7c0f87f388a4bff72066478f597c34d17f" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "7.0.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" path: dependency: transitive description: @@ -767,10 +767,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" path_provider_foundation: dependency: transitive description: @@ -831,10 +831,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.8.0" pool: dependency: transitive description: @@ -927,10 +927,10 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "05ec043470319bfbabe0adbc90d3a84cbff0426b9d9f3a6e2ad3e131fa5fa629" + sha256: fb5319f3aab4c5dda5ebb92dca978179ba21f8c783ee4380910ef4c1c6824f51 url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.0.3" share_plus_platform_interface: dependency: transitive description: @@ -1172,10 +1172,10 @@ packages: dependency: transitive description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.4.0" vector_math: dependency: transitive description: @@ -1188,10 +1188,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: a75f83f14ad81d5fe4b3319710b90dec37da0e22612326b696c9e1b8f34bbf48 url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.2.0" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c26bd5c4..2e14fa4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: bloc_presentation: ^1.0.0 clock: ^1.1.1 collection: any - device_info_plus: ^10.0.1 + device_info_plus: ^10.1.0 dynamic_color: ^1.7.0 equatable: ^2.0.5 flutter: @@ -22,26 +22,26 @@ dependencies: flutter_inappwebview: ^6.0.0 flutter_localizations: sdk: flutter - flutter_markdown: ^0.6.22 + flutter_markdown: ^0.6.22+1 flutter_secure_storage: ^9.0.0 formz: ^0.7.0 glider_data: path: packages/glider_data glider_domain: path: packages/glider_domain - go_router: ^13.2.2 + go_router: ^13.2.3 google_fonts: ^6.2.1 http: ^1.2.1 hydrated_bloc: ^9.1.5 intl: any markdown: ^7.2.2 material_color_utilities: any - package_info_plus: ^6.0.0 + package_info_plus: ^7.0.0 path_provider: ^2.1.2 pub_semver: ^2.1.4 relative_time: ^5.0.0 rxdart: ^0.27.7 - share_plus: ^8.0.2 + share_plus: ^8.0.3 shared_preferences: ^2.2.2 sliver_tools: ^0.2.12 super_sliver_list: ^0.4.1 @@ -51,7 +51,7 @@ dev_dependencies: custom_lint: ^0.6.4 dependency_validator: ^3.2.3 flutter_launcher_icons: ^0.13.1 - leancode_lint: ^11.0.0 + leancode_lint: ^12.0.0 melos: ^5.3.0 flutter: From a2cf4617498844727761dd91ba154ff2cb38dfa2 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 10 Apr 2024 20:49:17 +0200 Subject: [PATCH 142/145] Eliminate `DecoratedCard` --- lib/app/view/app.dart | 7 ++- lib/common/mixins/data_mixin.dart | 6 +- lib/common/widgets/decorated_card.dart | 81 -------------------------- lib/common/widgets/preview_card.dart | 4 +- lib/item/widgets/item_data_tile.dart | 80 +++++++++++++------------ 5 files changed, 52 insertions(+), 126 deletions(-) delete mode 100644 lib/common/widgets/decorated_card.dart diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 6a83ca5e..7f6dcd51 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -101,6 +101,9 @@ class App extends StatelessWidget { // Material 3 dictates a maximum width for bottom sheets. constraints: BoxConstraints(maxWidth: 640), ), + cardTheme: const CardTheme( + margin: EdgeInsets.zero, + ), chipTheme: ChipThemeData( iconTheme: IconThemeData( color: colorScheme.onSurface, @@ -109,14 +112,14 @@ class App extends StatelessWidget { inputDecorationTheme: const InputDecorationTheme(filled: true), menuButtonTheme: const MenuButtonThemeData( style: ButtonStyle( - padding: MaterialStatePropertyAll( + padding: WidgetStatePropertyAll( AppSpacing.defaultTilePadding, ), ), ), menuTheme: const MenuThemeData( style: MenuStyle( - shape: MaterialStatePropertyAll( + shape: WidgetStatePropertyAll( RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(AppSpacing.m)), ), diff --git a/lib/common/mixins/data_mixin.dart b/lib/common/mixins/data_mixin.dart index 982a4b28..3f780246 100644 --- a/lib/common/mixins/data_mixin.dart +++ b/lib/common/mixins/data_mixin.dart @@ -1,7 +1,6 @@ -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/models/status.dart'; -import 'package:glider/common/widgets/decorated_card.dart'; import 'package:glider/common/widgets/empty_widget.dart'; import 'package:glider/common/widgets/failure_widget.dart'; import 'package:glider/common/widgets/loading_widget.dart'; @@ -60,8 +59,7 @@ extension DataMixinExtension on DataMixin { SliverPadding( padding: AppSpacing.defaultTilePadding, sliver: SliverToBoxAdapter( - child: DecoratedCard.elevated( - padding: EdgeInsets.zero, + child: Card( child: FailureWidget( exception: exception, onRetry: onRetry, diff --git a/lib/common/widgets/decorated_card.dart b/lib/common/widgets/decorated_card.dart deleted file mode 100644 index f2185e6a..00000000 --- a/lib/common/widgets/decorated_card.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:glider/common/constants/app_spacing.dart'; - -const _borderRadius = BorderRadius.all(Radius.circular(12)); - -class DecoratedCard extends StatelessWidget { - const DecoratedCard.elevated({ - super.key, - this.padding, - this.onTap, - this.onLongPress, - this.child, - }) : _type = _CardType.elevated; - - const DecoratedCard.filled({ - super.key, - this.padding, - this.onTap, - this.onLongPress, - this.child, - }) : _type = _CardType.filled; - - const DecoratedCard.outlined({ - super.key, - this.padding, - this.onTap, - this.onLongPress, - this.child, - }) : _type = _CardType.outlined; - - final _CardType _type; - final EdgeInsetsGeometry? padding; - final VoidCallback? onTap; - final VoidCallback? onLongPress; - final Widget? child; - - @override - Widget build(BuildContext context) { - return Card( - color: _type.color(context), - elevation: _type.elevation, - shape: _type.shape(context), - margin: EdgeInsets.zero, - child: InkWell( - borderRadius: _borderRadius, - onTap: onTap, - onLongPress: onLongPress, - child: Padding( - padding: padding ?? AppSpacing.defaultTilePadding, - child: child, - ), - ), - ); - } -} - -enum _CardType { - elevated, - filled(elevation: 0), - outlined(elevation: 0); - - const _CardType({this.elevation}); - - final double? elevation; - - Color? color(BuildContext context) => switch (this) { - _CardType.filled => - // Dependent on `dynamic_color` supporting `surfaceContainerHighest`. - // ignore: deprecated_member_use - Theme.of(context).colorScheme.surfaceVariant, - _ => null, - }; - - ShapeBorder? shape(BuildContext context) => switch (this) { - _CardType.outlined => RoundedRectangleBorder( - side: BorderSide(color: Theme.of(context).colorScheme.outline), - borderRadius: _borderRadius, - ), - _ => null, - }; -} diff --git a/lib/common/widgets/preview_card.dart b/lib/common/widgets/preview_card.dart index 4e048ef6..4412dec1 100644 --- a/lib/common/widgets/preview_card.dart +++ b/lib/common/widgets/preview_card.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:glider/common/constants/app_animation.dart'; import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; -import 'package:glider/common/widgets/decorated_card.dart'; import 'package:glider/common/widgets/metadata_widget.dart'; import 'package:glider/l10n/extensions/app_localizations_extension.dart'; @@ -16,8 +15,7 @@ class PreviewCard extends StatelessWidget { @override Widget build(BuildContext context) { - return DecoratedCard.filled( - padding: EdgeInsets.zero, + return Card.outlined( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/item/widgets/item_data_tile.dart b/lib/item/widgets/item_data_tile.dart index 10400f31..1f8db20c 100644 --- a/lib/item/widgets/item_data_tile.dart +++ b/lib/item/widgets/item_data_tile.dart @@ -6,7 +6,6 @@ import 'package:glider/common/constants/app_spacing.dart'; import 'package:glider/common/extensions/uri_extension.dart'; import 'package:glider/common/extensions/widget_list_extension.dart'; import 'package:glider/common/widgets/animated_visibility.dart'; -import 'package:glider/common/widgets/decorated_card.dart'; import 'package:glider/common/widgets/hacker_news_text.dart'; import 'package:glider/common/widgets/metadata_widget.dart'; import 'package:glider/item/extensions/item_extension.dart'; @@ -364,44 +363,53 @@ class ItemDataTile extends StatelessWidget { ), ), if (item.url case final url?) - DecoratedCard.outlined( - onTap: () async => url.tryLaunch( - context, - useInAppBrowser: useInAppBrowser, - ), - // Explicitly override parent widget's long press. - onLongPress: () {}, - child: Row( - children: [ - if (showFavicons) - Hero( - tag: 'item_tile_favicon_${item.id}', - child: Material( - type: MaterialType.transparency, - child: _ItemFavicon( - item, - storyLines: 1, - useLargeStoryStyle: false, - ), - ), - ) - else - const MetadataWidget(icon: Icons.link_outlined), - Expanded( - child: Hero( - tag: 'item_tile_url_${item.id}', - child: Text( - item.url!.toString(), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.primary, - decoration: TextDecoration.underline, + Card.outlined( + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(12)), + onTap: () async => url.tryLaunch( + context, + useInAppBrowser: useInAppBrowser, + ), + // Explicitly override parent widget's long press. + onLongPress: () {}, + child: Padding( + padding: AppSpacing.defaultTilePadding, + child: Row( + children: [ + if (showFavicons) + Hero( + tag: 'item_tile_favicon_${item.id}', + child: Material( + type: MaterialType.transparency, + child: _ItemFavicon( + item, + storyLines: 1, + useLargeStoryStyle: false, ), - maxLines: 3, - overflow: TextOverflow.ellipsis, + ), + ) + else + const MetadataWidget(icon: Icons.link_outlined), + Expanded( + child: Hero( + tag: 'item_tile_url_${item.id}', + child: Text( + item.url!.toString(), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + ), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), ), - ), + ].spaced(width: AppSpacing.l), ), - ].spaced(width: AppSpacing.l), + ), ), ), ].spaced(height: AppSpacing.m), From 80dd9790b4173ac5c5def1ea9eb5346a75b4b392 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 10 Apr 2024 21:14:17 +0200 Subject: [PATCH 143/145] Enable predictive back transitions --- lib/app/view/app.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 7f6dcd51..6d6e74e3 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -132,6 +132,11 @@ class App extends StatelessWidget { navigationRailTheme: NavigationRailThemeData( backgroundColor: colorScheme.surface, ), + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: PredictiveBackPageTransitionsBuilder(), + }, + ), searchViewTheme: SearchViewThemeData( backgroundColor: colorScheme.surface, ), From 9e5fdaa178c2ea1a45a645a156cc222851cda766 Mon Sep 17 00:00:00 2001 From: Mosc Date: Wed, 10 Apr 2024 21:33:33 +0200 Subject: [PATCH 144/145] Fix state serialization issues --- lib/edit/cubit/edit_state.dart | 2 +- lib/item/cubit/item_state.dart | 4 +++- lib/story_similar/cubit/story_similar_state.dart | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/edit/cubit/edit_state.dart b/lib/edit/cubit/edit_state.dart index deabf909..7cec2c31 100644 --- a/lib/edit/cubit/edit_state.dart +++ b/lib/edit/cubit/edit_state.dart @@ -24,7 +24,7 @@ class EditState with EquatableMixin { ); Map toMap() => { - 'item': item, + 'item': item?.toMap(), 'title': title?.value, 'text': text?.value, 'isValid': isValid, diff --git a/lib/item/cubit/item_state.dart b/lib/item/cubit/item_state.dart index e5864264..f14b1145 100644 --- a/lib/item/cubit/item_state.dart +++ b/lib/item/cubit/item_state.dart @@ -15,7 +15,9 @@ class ItemState with DataMixin, EquatableMixin { factory ItemState.fromMap(Map json) => ItemState( status: Status.values.byName(json['status'] as String), - data: Item.fromMap(json['data'] as Map), + data: json['data'] != null + ? Item.fromMap(json['data'] as Map) + : null, visited: json['visited'] as bool? ?? false, vote: json['voted'] != null ? VoteType.values.byName(json['voted'] as String) diff --git a/lib/story_similar/cubit/story_similar_state.dart b/lib/story_similar/cubit/story_similar_state.dart index 8f87548f..f91c35de 100644 --- a/lib/story_similar/cubit/story_similar_state.dart +++ b/lib/story_similar/cubit/story_similar_state.dart @@ -11,7 +11,9 @@ class StorySimilarState with DataMixin>, EquatableMixin { factory StorySimilarState.fromMap(Map json) => StorySimilarState( status: Status.values.byName(json['status'] as String), - item: Item.fromMap(json['item'] as Map), + item: json['item'] != null + ? Item.fromMap(json['item'] as Map) + : null, data: (json['data'] as List?) ?.map((e) => e as int) .toList(growable: false), @@ -19,7 +21,7 @@ class StorySimilarState with DataMixin>, EquatableMixin { Map toMap() => { 'status': status.name, - 'item': item, + 'item': item?.toMap(), 'data': data, }; From 035589e20c6a45a2f1c55095d115e24c4e552218 Mon Sep 17 00:00:00 2001 From: Mosc Date: Thu, 11 Apr 2024 21:57:39 +0200 Subject: [PATCH 145/145] Update FVM version --- .github/actions/bootstrap/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index 621f0141..a81bc313 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -4,7 +4,7 @@ inputs: fvm-version: description: FVM version required: false - default: 3.0.0-beta.5 + default: 3.1.3 runs: using: composite steps: