Compare commits

...

3 Commits

Author SHA1 Message Date
1f4e6cf41c fix pagination button. (#298) 2023-11-02 21:50:09 -07:00
be6ed35888 update version. (#297) 2023-11-02 21:09:55 -07:00
b2ea50cea6 add pagination. (#296) 2023-11-02 20:22:51 -07:00
8 changed files with 111 additions and 37 deletions

View File

@ -0,0 +1,5 @@
- Ability to use pagination on home screen.
- Ability to use Material 3 (experimental).
- Ability to search in thread.
- Ability to customize text scale factor.
- Ability to customize app's accent color.

View File

@ -72,6 +72,8 @@ class PreferenceState extends Equatable {
bool get material3Enabled => _isOn<Material3Preference>();
bool get paginationEnabled => _isOn<PaginationPreference>();
double get textScaleFactor =>
preferences.singleWhereType<TextScaleFactorPreference>().val;

View File

@ -334,6 +334,17 @@ class HackiApp extends StatelessWidget {
state.appColor.shade200.withOpacity(0.5),
)
: null,
outlinedButtonTheme: state.material3Enabled
? OutlinedButtonThemeData(
style: ButtonStyle(
side: MaterialStateBorderSide.resolveWith(
(_) => const BorderSide(
color: Palette.grey,
),
),
),
)
: null,
),
routerConfig: router,
),

View File

@ -37,13 +37,14 @@ abstract class Preference<T> extends Equatable with SettingsDisplayable {
const MarkReadStoriesModePreference(),
// Divider.
const NotificationModePreference(),
const SwipeGesturePreference(),
const AutoScrollModePreference(),
const CollapseModePreference(),
const ReaderModePreference(),
const CustomTabPreference(),
const EyeCandyModePreference(),
const Material3Preference(),
const PaginationPreference(),
const SwipeGesturePreference(),
],
);
@ -75,6 +76,7 @@ const bool _collapseModeDefaultValue = true;
const bool _autoScrollModeDefaultValue = false;
const bool _customTabModeDefaultValue = false;
const bool _material3ModeDefaultValue = false;
const bool _paginationModeDefaultValue = false;
const double _textScaleFactorDefaultValue = 1;
final int _fetchModeDefaultValue = FetchMode.eager.index;
final int _commentsOrderDefaultValue = CommentsOrder.natural.index;
@ -287,6 +289,25 @@ class EyeCandyModePreference extends BooleanPreference {
String get subtitle => 'some sort of magic.';
}
class PaginationPreference extends BooleanPreference {
const PaginationPreference({bool? val})
: super(val: val ?? _paginationModeDefaultValue);
@override
PaginationPreference copyWith({required bool? val}) {
return PaginationPreference(val: val);
}
@override
String get key => 'paginationMode';
@override
String get title => 'Enable Pagination';
@override
String get subtitle => '''so you can get stuff done.''';
}
class Material3Preference extends BooleanPreference {
const Material3Preference({bool? val})
: super(val: val ?? _material3ModeDefaultValue);

View File

@ -11,34 +11,31 @@ class InThreadSearchIconButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider<CommentsCubit>.value(
value: context.read<CommentsCubit>(),
child: OpenContainer(
closedColor: Palette.transparent,
openColor: Theme.of(context).canvasColor,
closedShape: const CircleBorder(),
closedElevation: 0,
openElevation: 0,
transitionType: ContainerTransitionType.fadeThrough,
closedBuilder: (BuildContext context, void Function() action) {
return CustomDescribedFeatureOverlay(
tapTarget: const Icon(
Icons.search,
color: Palette.white,
),
feature: DiscoverableFeature.searchInThread,
child: IconButton(
tooltip: 'Search in thread',
icon: const Icon(Icons.search),
onPressed: action,
),
);
},
openBuilder: (_, void Function({Object? returnValue}) action) =>
_InThreadSearchView(
commentsCubit: context.read<CommentsCubit>(),
action: action,
),
return OpenContainer(
closedColor: Palette.transparent,
openColor: Theme.of(context).canvasColor,
closedShape: const CircleBorder(),
closedElevation: 0,
openElevation: 0,
transitionType: ContainerTransitionType.fadeThrough,
closedBuilder: (BuildContext context, void Function() action) {
return CustomDescribedFeatureOverlay(
tapTarget: const Icon(
Icons.search,
color: Palette.white,
),
feature: DiscoverableFeature.searchInThread,
child: IconButton(
tooltip: 'Search in thread',
icon: const Icon(Icons.search),
onPressed: action,
),
);
},
openBuilder: (_, void Function({Object? returnValue}) action) =>
_InThreadSearchView(
commentsCubit: context.read<CommentsCubit>(),
action: action,
),
);
}

View File

@ -25,10 +25,12 @@ class ItemsListView<T extends Item> extends StatelessWidget {
this.enablePullDown = true,
this.markReadStories = false,
this.showOfflineBanner = false,
this.loadStyle = LoadStyle.ShowWhenLoading,
this.onRefresh,
this.onLoadMore,
this.onPinned,
this.header,
this.footer,
this.onMoreTapped,
this.scrollController,
this.itemBuilder,
@ -43,8 +45,10 @@ class ItemsListView<T extends Item> extends StatelessWidget {
final bool markReadStories;
final bool showOfflineBanner;
final LoadStyle loadStyle;
final List<T> items;
final Widget? header;
final Widget? footer;
final RefreshController refreshController;
final ScrollController? scrollController;
final VoidCallback? onRefresh;
@ -224,6 +228,7 @@ class ItemsListView<T extends Item> extends StatelessWidget {
? Column(children: e)
: itemBuilder!(Column(children: e), items.elementAt(index)),
),
if (footer != null) footer!,
const SizedBox(
height: Dimens.pt40,
),
@ -237,7 +242,7 @@ class ItemsListView<T extends Item> extends StatelessWidget {
backgroundColor: Theme.of(context).primaryColor,
),
footer: CustomFooter(
loadStyle: LoadStyle.ShowWhenLoading,
loadStyle: loadStyle,
builder: (BuildContext context, LoadStatus? mode) {
const double height = 55;
late final Widget body;

View File

@ -7,6 +7,7 @@ import 'package:hacki/cubits/cubits.dart';
import 'package:hacki/extensions/extensions.dart';
import 'package:hacki/models/models.dart';
import 'package:hacki/screens/widgets/widgets.dart';
import 'package:hacki/styles/styles.dart';
import 'package:hacki/utils/utils.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:visibility_detector/visibility_detector.dart';
@ -48,7 +49,8 @@ class _StoriesListViewState extends State<StoriesListView>
return BlocBuilder<PreferenceCubit, PreferenceState>(
buildWhen: (PreferenceState previous, PreferenceState current) =>
previous.complexStoryTileEnabled != current.complexStoryTileEnabled ||
previous.metadataEnabled != current.metadataEnabled,
previous.metadataEnabled != current.metadataEnabled ||
previous.paginationEnabled != current.paginationEnabled,
builder: (BuildContext context, PreferenceState preferenceState) {
return BlocConsumer<StoriesBloc, StoriesState>(
listenWhen: (StoriesState previous, StoriesState current) =>
@ -70,8 +72,7 @@ class _StoriesListViewState extends State<StoriesListView>
builder: (BuildContext context, StoriesState state) {
return ItemsListView<Story>(
showOfflineBanner: true,
markReadStories:
context.read<PreferenceCubit>().state.markReadStoriesEnabled,
markReadStories: preferenceState.markReadStoriesEnabled,
showWebPreviewOnStoryTile:
preferenceState.complexStoryTileEnabled,
showMetadataOnStoryTile: preferenceState.metadataEnabled,
@ -87,13 +88,42 @@ class _StoriesListViewState extends State<StoriesListView>
context.read<PinCubit>().refresh();
},
onLoadMore: () {
context
.read<StoriesBloc>()
.add(StoriesLoadMore(type: storyType));
if (preferenceState.paginationEnabled) {
refreshController
..refreshCompleted(resetFooterState: true)
..loadComplete();
} else {
loadMoreStories();
}
},
onTap: onStoryTapped,
onPinned: context.read<PinCubit>().pinStory,
header: state.isOfflineReading ? null : header,
loadStyle: LoadStyle.HideAlways,
footer: preferenceState.paginationEnabled &&
state.statusByType[widget.storyType] == Status.success &&
(state.storiesByType[widget.storyType]?.length ?? 0) <
(state.storyIdsByType[widget.storyType]?.length ?? 0)
? Padding(
padding: const EdgeInsets.only(
left: Dimens.pt48,
right: Dimens.pt48,
top: Dimens.pt36,
bottom: Dimens.pt12,
),
child: OutlinedButton(
onPressed: loadMoreStories,
style: ButtonStyle(
foregroundColor: MaterialStateColor.resolveWith(
(_) => Theme.of(context).colorScheme.onSurface,
),
),
child: Text(
'''Load Page ${(state.currentPageByType[widget.storyType] ?? 0) + 2}''',
),
),
)
: null,
onMoreTapped: onMoreTapped,
itemBuilder: (Widget child, Story story) {
return Slidable(
@ -162,4 +192,7 @@ class _StoriesListViewState extends State<StoriesListView>
},
);
}
void loadMoreStories() =>
context.read<StoriesBloc>().add(StoriesLoadMore(type: widget.storyType));
}

View File

@ -1,6 +1,6 @@
name: hacki
description: A Hacker News reader.
version: 2.1.0+127
version: 2.2.0+128
publish_to: none
environment: