add option to disable auto-scroll. (#225)

This commit is contained in:
Jiaqi Feng
2023-06-05 18:23:29 -07:00
committed by GitHub
parent e33ff417fb
commit 8d238744c7
14 changed files with 159 additions and 153 deletions

BIN
assets/hacki-github.xcf Normal file

Binary file not shown.

BIN
assets/hacki.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

View File

@ -68,6 +68,8 @@ class PreferenceState extends Equatable {
bool get swipeGestureEnabled => _isOn<SwipeGesturePreference>(); bool get swipeGestureEnabled => _isOn<SwipeGesturePreference>();
bool get autoScrollEnabled => _isOn<AutoScrollModePreference>();
List<StoryType> get tabs { List<StoryType> get tabs {
final String result = final String result =
preferences.singleWhereType<TabOrderPreference>().val.toString(); preferences.singleWhereType<TabOrderPreference>().val.toString();

View File

@ -48,3 +48,11 @@ class SearchState extends Equatable {
params, params,
]; ];
} }
extension SearchStateExtension on SearchState {
bool get showDateRangeShortcutChips {
return hasDateFilter &&
dateFilter?.startTime != null &&
dateFilter?.endTime != null;
}
}

View File

@ -30,6 +30,7 @@ abstract class Preference<T> extends Equatable with SettingsDisplayable {
const StoryUrlModePreference(), const StoryUrlModePreference(),
const NotificationModePreference(), const NotificationModePreference(),
const SwipeGesturePreference(), const SwipeGesturePreference(),
const AutoScrollModePreference(),
const CollapseModePreference(), const CollapseModePreference(),
const ReaderModePreference(), const ReaderModePreference(),
const MarkReadStoriesModePreference(), const MarkReadStoriesModePreference(),
@ -54,12 +55,13 @@ const bool _notificationModeDefaultValue = true;
const bool _swipeGestureModeDefaultValue = false; const bool _swipeGestureModeDefaultValue = false;
const bool _displayModeDefaultValue = true; const bool _displayModeDefaultValue = true;
const bool _eyeCandyModeDefaultValue = false; const bool _eyeCandyModeDefaultValue = false;
const bool _trueDarkModeDefaultValue = false; const bool _trueDarkModeDefaultValue = true;
const bool _readerModeDefaultValue = true; const bool _readerModeDefaultValue = true;
const bool _markReadStoriesModeDefaultValue = true; const bool _markReadStoriesModeDefaultValue = true;
const bool _metadataModeDefaultValue = true; const bool _metadataModeDefaultValue = true;
const bool _storyUrlModeDefaultValue = true; const bool _storyUrlModeDefaultValue = true;
const bool _collapseModeDefaultValue = true; const bool _collapseModeDefaultValue = true;
const bool _autoScrollModeDefaultValue = true;
final int _fetchModeDefaultValue = FetchMode.eager.index; final int _fetchModeDefaultValue = FetchMode.eager.index;
final int _commentsOrderDefaultValue = CommentsOrder.natural.index; final int _commentsOrderDefaultValue = CommentsOrder.natural.index;
final int _fontSizeDefaultValue = FontSize.regular.index; final int _fontSizeDefaultValue = FontSize.regular.index;
@ -127,6 +129,26 @@ class CollapseModePreference extends BooleanPreference {
'''if disabled, tap on the top of comment tile to collapse.'''; '''if disabled, tap on the top of comment tile to collapse.''';
} }
class AutoScrollModePreference extends BooleanPreference {
const AutoScrollModePreference({bool? val})
: super(val: val ?? _autoScrollModeDefaultValue);
@override
AutoScrollModePreference copyWith({required bool? val}) {
return AutoScrollModePreference(val: val);
}
@override
String get key => 'autoScrollMode';
@override
String get title => 'Auto-scroll on collapsing';
@override
String get subtitle =>
'''Automatically scroll to next comment when you collapse a comment.''';
}
/// The value deciding whether or not the story /// The value deciding whether or not the story
/// tile should display link preview. Defaults to true. /// tile should display link preview. Defaults to true.
class DisplayModePreference extends BooleanPreference { class DisplayModePreference extends BooleanPreference {

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_fadein/flutter_fadein.dart'; import 'package:flutter_fadein/flutter_fadein.dart';
import 'package:hacki/config/constants.dart'; import 'package:hacki/config/constants.dart';
@ -32,31 +31,9 @@ class _SearchScreenState extends State<SearchScreen> {
final RefreshController refreshController = RefreshController(); final RefreshController refreshController = RefreshController();
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
final Debouncer debouncer = Debouncer(delay: Durations.oneSecond); final Debouncer debouncer = Debouncer(delay: Durations.oneSecond);
bool showChips = true;
bool shouldOffStageChips = false;
static const Duration chipsAnimationDuration = Durations.ms300; static const Duration chipsAnimationDuration = Durations.ms300;
@override
void initState() {
super.initState();
scrollController.addListener(() {
if (scrollController.position.userScrollDirection ==
ScrollDirection.reverse &&
showChips) {
setState(() {
showChips = false;
});
} else if (scrollController.position.userScrollDirection ==
ScrollDirection.forward &&
!showChips) {
setState(() {
showChips = true;
});
}
});
}
@override @override
void dispose() { void dispose() {
refreshController.dispose(); refreshController.dispose();
@ -108,16 +85,13 @@ class _SearchScreenState extends State<SearchScreen> {
), ),
AnimatedCrossFade( AnimatedCrossFade(
duration: chipsAnimationDuration, duration: chipsAnimationDuration,
crossFadeState: showChips crossFadeState: state.showDateRangeShortcutChips
? CrossFadeState.showSecond ? CrossFadeState.showSecond
: CrossFadeState.showFirst, : CrossFadeState.showFirst,
firstChild: SizedBox.fromSize(), firstChild: SizedBox.fromSize(),
secondChild: Column( secondChild: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
if (state.hasDateFilter &&
state.dateFilter?.startTime != null &&
state.dateFilter?.endTime != null)
SingleChildScrollView( SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
@ -129,8 +103,8 @@ class _SearchScreenState extends State<SearchScreen> {
onDateTimeRangeUpdated: context onDateTimeRangeUpdated: context
.read<SearchCubit>() .read<SearchCubit>()
.onDateTimeRangeUpdated, .onDateTimeRangeUpdated,
startDate: state.dateFilter!.startTime!, startDate: state.dateFilter?.startTime,
endDate: state.dateFilter!.endTime!, endDate: state.dateFilter?.endTime,
), ),
const SizedBox( const SizedBox(
width: Dimens.pt8, width: Dimens.pt8,
@ -139,8 +113,8 @@ class _SearchScreenState extends State<SearchScreen> {
onDateTimeRangeUpdated: context onDateTimeRangeUpdated: context
.read<SearchCubit>() .read<SearchCubit>()
.onDateTimeRangeUpdated, .onDateTimeRangeUpdated,
startDate: state.dateFilter!.startTime!, startDate: state.dateFilter?.startTime,
endDate: state.dateFilter!.endTime!, endDate: state.dateFilter?.endTime,
), ),
const SizedBox( const SizedBox(
width: Dimens.pt8, width: Dimens.pt8,
@ -149,8 +123,8 @@ class _SearchScreenState extends State<SearchScreen> {
onDateTimeRangeUpdated: context onDateTimeRangeUpdated: context
.read<SearchCubit>() .read<SearchCubit>()
.onDateTimeRangeUpdated, .onDateTimeRangeUpdated,
startDate: state.dateFilter!.startTime!, startDate: state.dateFilter?.startTime,
endDate: state.dateFilter!.endTime!, endDate: state.dateFilter?.endTime,
), ),
const SizedBox( const SizedBox(
width: Dimens.pt8, width: Dimens.pt8,
@ -159,8 +133,8 @@ class _SearchScreenState extends State<SearchScreen> {
onDateTimeRangeUpdated: context onDateTimeRangeUpdated: context
.read<SearchCubit>() .read<SearchCubit>()
.onDateTimeRangeUpdated, .onDateTimeRangeUpdated,
startDate: state.dateFilter!.startTime!, startDate: state.dateFilter?.startTime,
endDate: state.dateFilter!.endTime!, endDate: state.dateFilter?.endTime,
), ),
const SizedBox( const SizedBox(
width: Dimens.pt8, width: Dimens.pt8,
@ -169,8 +143,8 @@ class _SearchScreenState extends State<SearchScreen> {
onDateTimeRangeUpdated: context onDateTimeRangeUpdated: context
.read<SearchCubit>() .read<SearchCubit>()
.onDateTimeRangeUpdated, .onDateTimeRangeUpdated,
startDate: state.dateFilter!.startTime!, startDate: state.dateFilter?.startTime,
endDate: state.dateFilter!.endTime!, endDate: state.dateFilter?.endTime,
), ),
const SizedBox( const SizedBox(
width: Dimens.pt8, width: Dimens.pt8,
@ -179,8 +153,11 @@ class _SearchScreenState extends State<SearchScreen> {
onDateTimeRangeUpdated: context onDateTimeRangeUpdated: context
.read<SearchCubit>() .read<SearchCubit>()
.onDateTimeRangeUpdated, .onDateTimeRangeUpdated,
startDate: state.dateFilter!.startTime!, startDate: state.dateFilter?.startTime,
endDate: state.dateFilter!.endTime!, endDate: state.dateFilter?.endTime,
),
],
),
), ),
], ],
), ),
@ -208,9 +185,8 @@ class _SearchScreenState extends State<SearchScreen> {
), ),
PostedByFilterChip( PostedByFilterChip(
filter: state.params.get<PostedByFilter>(), filter: state.params.get<PostedByFilter>(),
onChanged: context onChanged:
.read<SearchCubit>() context.read<SearchCubit>().onPostedByChanged,
.onPostedByChanged,
), ),
const SizedBox( const SizedBox(
width: Dimens.pt8, width: Dimens.pt8,
@ -249,9 +225,8 @@ class _SearchScreenState extends State<SearchScreen> {
width: Dimens.pt8, width: Dimens.pt8,
), ),
CustomChip( CustomChip(
onSelected: (_) => context onSelected: (_) =>
.read<SearchCubit>() context.read<SearchCubit>().onToggled(filter),
.onToggled(filter),
selected: context selected: context
.read<SearchCubit>() .read<SearchCubit>()
.state .state
@ -264,9 +239,6 @@ class _SearchScreenState extends State<SearchScreen> {
], ],
), ),
), ),
],
),
),
if (state.status == SearchStatus.loading && if (state.status == SearchStatus.loading &&
state.results.isEmpty) ...<Widget>[ state.results.isEmpty) ...<Widget>[
const SizedBox( const SizedBox(

View File

@ -68,8 +68,8 @@ class DateTimeShortcutChip extends StatelessWidget {
_calculator = ((DateTime date) => date.add(const Duration(days: 30))); _calculator = ((DateTime date) => date.add(const Duration(days: 30)));
final void Function(DateTime, DateTime) onDateTimeRangeUpdated; final void Function(DateTime, DateTime) onDateTimeRangeUpdated;
final DateTime startDate; final DateTime? startDate;
final DateTime endDate; final DateTime? endDate;
final String label; final String label;
final Calculator _calculator; final Calculator _calculator;
@ -77,8 +77,9 @@ class DateTimeShortcutChip extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CustomChip( return CustomChip(
onSelected: (bool value) { onSelected: (bool value) {
final DateTime updatedStartDate = _calculator(startDate); if (startDate == null || endDate == null) return;
final DateTime updatedEndDate = _calculator(endDate); final DateTime updatedStartDate = _calculator(startDate!);
final DateTime updatedEndDate = _calculator(endDate!);
onDateTimeRangeUpdated(updatedStartDate, updatedEndDate); onDateTimeRangeUpdated(updatedStartDate, updatedEndDate);
}, },
selected: false, selected: false,

View File

@ -349,7 +349,8 @@ class CommentTile extends StatelessWidget {
void _collapse(BuildContext context) { void _collapse(BuildContext context) {
HapticFeedbackUtil.selection(); HapticFeedbackUtil.selection();
context.read<CollapseCubit>().collapse(); context.read<CollapseCubit>().collapse();
if (context.read<CollapseCubit>().state.collapsed) { if (context.read<CollapseCubit>().state.collapsed &&
context.read<PreferenceCubit>().state.autoScrollEnabled) {
Future<void>.delayed( Future<void>.delayed(
Durations.ms300, Durations.ms300,
() { () {

View File

@ -237,7 +237,7 @@ class LinkView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
SizedBox( SizedBox(
height: isUsingSerifFont! ? Dimens.pt2 : Dimens.pt4, height: isUsingSerifFont! ? Dimens.zero : Dimens.pt4,
), ),
Text( Text(
title, title,

View File

@ -1,6 +1,6 @@
name: hacki name: hacki
description: A Hacker News reader. description: A Hacker News reader.
version: 1.7.1+112 version: 1.7.2+113
publish_to: none publish_to: none
environment: environment: