mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
1f4e6cf41c | |||
be6ed35888 | |||
b2ea50cea6 |
5
fastlane/metadata/android/en-US/changelogs/128.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/128.txt
Normal 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.
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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:
|
||||
|
Reference in New Issue
Block a user