mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Rewrite double tap to exit code
This commit is contained in:
parent
59fff6d679
commit
f789d0473b
7 changed files with 405 additions and 165 deletions
63
app/lib/widget/double_tap_exit_container/bloc.dart
Normal file
63
app/lib/widget/double_tap_exit_container/bloc.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
part of 'double_tap_exit_container.dart';
|
||||
|
||||
@npLog
|
||||
class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||
_Bloc({
|
||||
required this.prefController,
|
||||
}) : super(_State.init(
|
||||
isDoubleTapExit: prefController.isDoubleTapExitValue,
|
||||
)) {
|
||||
on<_SetDoubleTapExit>(_onSetDoubleTapExit);
|
||||
on<_SetCanPop>(_onSetCanPop);
|
||||
on<_OnPopInvoked>(_onOnPopInvoked);
|
||||
|
||||
_subscriptions.add(prefController.isDoubleTapExitChange.listen((ev) {
|
||||
add(_SetDoubleTapExit(ev));
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
for (final s in _subscriptions) {
|
||||
s.cancel();
|
||||
}
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@override
|
||||
String get tag => _log.fullName;
|
||||
|
||||
void _onSetDoubleTapExit(_SetDoubleTapExit ev, _Emitter emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(isDoubleTapExit: ev.value));
|
||||
}
|
||||
|
||||
void _onSetCanPop(_SetCanPop ev, _Emitter emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(canPop: ev.value));
|
||||
}
|
||||
|
||||
void _onOnPopInvoked(_OnPopInvoked ev, _Emitter emit) {
|
||||
_log.info(ev);
|
||||
if (state.isDoubleTapExit && !state.canPop) {
|
||||
emit(state.copyWith(canPop: true));
|
||||
_timer?.cancel();
|
||||
_timer = Timer(
|
||||
const Duration(seconds: 5),
|
||||
() {
|
||||
add(const _SetCanPop(false));
|
||||
},
|
||||
);
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().doubleTapExitNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
final PrefController prefController;
|
||||
|
||||
final _subscriptions = <StreamSubscription>[];
|
||||
Timer? _timer;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/bloc_util.dart';
|
||||
import 'package:nc_photos/controller/pref_controller.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
||||
part 'bloc.dart';
|
||||
part 'double_tap_exit_container.g.dart';
|
||||
part 'state_event.dart';
|
||||
|
||||
class DoubleTapExitContainer extends StatelessWidget {
|
||||
const DoubleTapExitContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => _Bloc(
|
||||
prefController: context.read(),
|
||||
),
|
||||
child: _WrappedDoubleTapExitContainer(child: child),
|
||||
);
|
||||
}
|
||||
|
||||
final Widget child;
|
||||
}
|
||||
|
||||
class _WrappedDoubleTapExitContainer extends StatelessWidget {
|
||||
const _WrappedDoubleTapExitContainer({
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BlocBuilder(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.isDoubleTapExit != current.isDoubleTapExit ||
|
||||
previous.canPop != current.canPop,
|
||||
builder: (context, state) => PopScope(
|
||||
canPop: !state.isDoubleTapExit || state.canPop,
|
||||
onPopInvoked: (didPop) {
|
||||
context.addEvent(_OnPopInvoked(didPop));
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final Widget child;
|
||||
}
|
||||
|
||||
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||
// typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||
// typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
||||
// typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
||||
typedef _Emitter = Emitter<_State>;
|
||||
|
||||
extension on BuildContext {
|
||||
_Bloc get bloc => read<_Bloc>();
|
||||
// _State get state => bloc.state;
|
||||
void addEvent(_Event event) => bloc.add(event);
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'double_tap_exit_container.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithLintRuleGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: library_private_types_in_public_api, duplicate_ignore
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class $_StateCopyWithWorker {
|
||||
_State call({bool? isDoubleTapExit, bool? canPop});
|
||||
}
|
||||
|
||||
class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
||||
_$_StateCopyWithWorkerImpl(this.that);
|
||||
|
||||
@override
|
||||
_State call({dynamic isDoubleTapExit, dynamic canPop}) {
|
||||
return _State(
|
||||
isDoubleTapExit: isDoubleTapExit as bool? ?? that.isDoubleTapExit,
|
||||
canPop: canPop as bool? ?? that.canPop);
|
||||
}
|
||||
|
||||
final _State that;
|
||||
}
|
||||
|
||||
extension $_StateCopyWith on _State {
|
||||
$_StateCopyWithWorker get copyWith => _$copyWith;
|
||||
$_StateCopyWithWorker get _$copyWith => _$_StateCopyWithWorkerImpl(this);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_BlocNpLog on _Bloc {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log = Logger(
|
||||
"widget.double_tap_exit_container.double_tap_exit_container._Bloc");
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// ToStringGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {isDoubleTapExit: $isDoubleTapExit, canPop: $canPop}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetDoubleTapExitToString on _SetDoubleTapExit {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetDoubleTapExit {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetCanPopToString on _SetCanPop {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetCanPop {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_OnPopInvokedToString on _OnPopInvoked {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_OnPopInvoked {didPop: $didPop}";
|
||||
}
|
||||
}
|
56
app/lib/widget/double_tap_exit_container/state_event.dart
Normal file
56
app/lib/widget/double_tap_exit_container/state_event.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
part of 'double_tap_exit_container.dart';
|
||||
|
||||
@genCopyWith
|
||||
@toString
|
||||
class _State {
|
||||
const _State({
|
||||
required this.isDoubleTapExit,
|
||||
required this.canPop,
|
||||
});
|
||||
|
||||
factory _State.init({
|
||||
required bool isDoubleTapExit,
|
||||
}) =>
|
||||
_State(
|
||||
isDoubleTapExit: isDoubleTapExit,
|
||||
canPop: false,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool isDoubleTapExit;
|
||||
final bool canPop;
|
||||
}
|
||||
|
||||
abstract class _Event {}
|
||||
|
||||
@toString
|
||||
class _SetDoubleTapExit implements _Event {
|
||||
const _SetDoubleTapExit(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetCanPop implements _Event {
|
||||
const _SetCanPop(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _OnPopInvoked implements _Event {
|
||||
const _OnPopInvoked(this.didPop);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool didPop;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/entity/pref.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
|
||||
class DoubleTapExitHandler {
|
||||
factory DoubleTapExitHandler() => _inst;
|
||||
|
||||
DoubleTapExitHandler._();
|
||||
|
||||
/// Return if this back button event should actually exit the app
|
||||
bool call() {
|
||||
if (!Pref().isDoubleTapExitOr()) {
|
||||
return true;
|
||||
}
|
||||
final now = clock.now().toUtc();
|
||||
_lastBackButtonAt ??= now.subtract(const Duration(days: 1));
|
||||
if (now.difference(_lastBackButtonAt!) < const Duration(seconds: 5)) {
|
||||
return true;
|
||||
} else {
|
||||
_lastBackButtonAt = now;
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().doubleTapExitNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static final _inst = DoubleTapExitHandler._();
|
||||
|
||||
DateTime? _lastBackButtonAt;
|
||||
}
|
|
@ -33,9 +33,9 @@ import 'package:nc_photos/widget/album_importer.dart';
|
|||
import 'package:nc_photos/widget/archive_browser.dart';
|
||||
import 'package:nc_photos/widget/collection_browser.dart';
|
||||
import 'package:nc_photos/widget/collection_grid_item.dart';
|
||||
import 'package:nc_photos/widget/double_tap_exit_container/double_tap_exit_container.dart';
|
||||
import 'package:nc_photos/widget/enhanced_photo_browser.dart';
|
||||
import 'package:nc_photos/widget/fade_out_list.dart';
|
||||
import 'package:nc_photos/widget/handler/double_tap_exit_handler.dart';
|
||||
import 'package:nc_photos/widget/home_app_bar.dart';
|
||||
import 'package:nc_photos/widget/map_browser.dart';
|
||||
import 'package:nc_photos/widget/navigation_bar_blur_filter.dart';
|
||||
|
@ -46,7 +46,6 @@ import 'package:nc_photos/widget/selection_app_bar.dart';
|
|||
import 'package:nc_photos/widget/sharing_browser.dart';
|
||||
import 'package:nc_photos/widget/trashbin_browser.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:np_platform_util/np_platform_util.dart';
|
||||
import 'package:np_ui/np_ui.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
||||
|
@ -96,7 +95,7 @@ class _WrappedHomeCollectionsState extends State<_WrappedHomeCollections>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = MultiBlocListener(
|
||||
return MultiBlocListener(
|
||||
listeners: [
|
||||
_BlocListener(
|
||||
listenWhen: (previous, current) =>
|
||||
|
@ -127,103 +126,108 @@ class _WrappedHomeCollectionsState extends State<_WrappedHomeCollections>
|
|||
},
|
||||
),
|
||||
],
|
||||
child: Stack(
|
||||
children: [
|
||||
RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
_bloc.add(const _ReloadCollections());
|
||||
await _bloc.stream.first;
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
_BlocBuilder(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.selectedItems.isEmpty !=
|
||||
current.selectedItems.isEmpty,
|
||||
builder: (context, state) => state.selectedItems.isEmpty
|
||||
? const _AppBar()
|
||||
: const _SelectionAppBar(),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 8),
|
||||
),
|
||||
_BlocBuilder(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.transformedItems != current.transformedItems ||
|
||||
previous.selectedItems != current.selectedItems,
|
||||
builder: (context, state) => SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
sliver: SelectableItemList(
|
||||
maxCrossAxisExtent: 256,
|
||||
childBorderRadius: BorderRadius.zero,
|
||||
indicatorAlignment: const Alignment(-.92, -.92),
|
||||
items: state.transformedItems,
|
||||
itemBuilder: (_, __, item) {
|
||||
return _BlocSelector<int?>(
|
||||
selector: (state) =>
|
||||
state.itemCounts[item.collection.id],
|
||||
builder: (context, itemCount) => _ItemView(
|
||||
account: _bloc.account,
|
||||
item: item,
|
||||
collectionItemCountOverride: itemCount,
|
||||
),
|
||||
);
|
||||
},
|
||||
staggeredTileBuilder: (_, __) =>
|
||||
const StaggeredTile.count(1, 1),
|
||||
selectedItems: state.selectedItems,
|
||||
onSelectionChange: (_, selected) {
|
||||
_bloc.add(_SetSelectedItems(items: selected.cast()));
|
||||
},
|
||||
onItemTap: (context, _, item) {
|
||||
Navigator.of(context).pushNamed(
|
||||
CollectionBrowser.routeName,
|
||||
arguments:
|
||||
CollectionBrowserArguments(item.collection),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: AppDimension.of(context).homeBottomAppBarHeight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: NavigationBarBlurFilter(
|
||||
height: AppDimension.of(context).homeBottomAppBarHeight,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: _BlocSelector(
|
||||
selector: (state) => state.selectedItems.isEmpty,
|
||||
builder: (context, isSelectedEmpty) => isSelectedEmpty
|
||||
? const DoubleTapExitContainer(
|
||||
child: _BodyView(),
|
||||
)
|
||||
: PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (_) {
|
||||
context.addEvent(const _SetSelectedItems(items: {}));
|
||||
},
|
||||
child: const _BodyView(),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (getRawPlatform() == NpPlatform.android) {
|
||||
return WillPopScope(
|
||||
onWillPop: () => _onBackButtonPressed(context),
|
||||
child: content,
|
||||
);
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _onBackButtonPressed(BuildContext context) async {
|
||||
if (context.state.selectedItems.isEmpty) {
|
||||
return DoubleTapExitHandler()();
|
||||
} else {
|
||||
context.addEvent(const _SetSelectedItems(items: {}));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
late final _Bloc _bloc = context.read();
|
||||
}
|
||||
|
||||
class _BodyView extends StatelessWidget {
|
||||
const _BodyView();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
context.addEvent(const _ReloadCollections());
|
||||
await context.bloc.stream.first;
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
_BlocBuilder(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.selectedItems.isEmpty !=
|
||||
current.selectedItems.isEmpty,
|
||||
builder: (context, state) => state.selectedItems.isEmpty
|
||||
? const _AppBar()
|
||||
: const _SelectionAppBar(),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 8),
|
||||
),
|
||||
_BlocBuilder(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.transformedItems != current.transformedItems ||
|
||||
previous.selectedItems != current.selectedItems,
|
||||
builder: (context, state) => SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
sliver: SelectableItemList(
|
||||
maxCrossAxisExtent: 256,
|
||||
childBorderRadius: BorderRadius.zero,
|
||||
indicatorAlignment: const Alignment(-.92, -.92),
|
||||
items: state.transformedItems,
|
||||
itemBuilder: (_, __, item) {
|
||||
return _BlocSelector<int?>(
|
||||
selector: (state) =>
|
||||
state.itemCounts[item.collection.id],
|
||||
builder: (context, itemCount) => _ItemView(
|
||||
account: context.bloc.account,
|
||||
item: item,
|
||||
collectionItemCountOverride: itemCount,
|
||||
),
|
||||
);
|
||||
},
|
||||
staggeredTileBuilder: (_, __) =>
|
||||
const StaggeredTile.count(1, 1),
|
||||
selectedItems: state.selectedItems,
|
||||
onSelectionChange: (_, selected) {
|
||||
context
|
||||
.addEvent(_SetSelectedItems(items: selected.cast()));
|
||||
},
|
||||
onItemTap: (context, _, item) {
|
||||
Navigator.of(context).pushNamed(
|
||||
CollectionBrowser.routeName,
|
||||
arguments: CollectionBrowserArguments(item.collection),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: AppDimension.of(context).homeBottomAppBarHeight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: NavigationBarBlurFilter(
|
||||
height: AppDimension.of(context).homeBottomAppBarHeight,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||
typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||
// typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
||||
|
|
|
@ -45,9 +45,9 @@ import 'package:nc_photos/theme/dimension.dart';
|
|||
import 'package:nc_photos/url_launcher_util.dart';
|
||||
import 'package:nc_photos/widget/collection_browser.dart';
|
||||
import 'package:nc_photos/widget/collection_picker.dart';
|
||||
import 'package:nc_photos/widget/double_tap_exit_container/double_tap_exit_container.dart';
|
||||
import 'package:nc_photos/widget/file_sharer_dialog.dart';
|
||||
import 'package:nc_photos/widget/finger_listener.dart';
|
||||
import 'package:nc_photos/widget/handler/double_tap_exit_handler.dart';
|
||||
import 'package:nc_photos/widget/home_app_bar.dart';
|
||||
import 'package:nc_photos/widget/navigation_bar_blur_filter.dart';
|
||||
import 'package:nc_photos/widget/network_thumbnail.dart';
|
||||
|
@ -64,7 +64,6 @@ import 'package:np_common/object_util.dart';
|
|||
import 'package:np_common/or_null.dart';
|
||||
import 'package:np_datetime/np_datetime.dart';
|
||||
import 'package:np_db/np_db.dart';
|
||||
import 'package:np_platform_util/np_platform_util.dart';
|
||||
import 'package:np_ui/np_ui.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
@ -116,12 +115,12 @@ class _WrappedHomePhotosState extends State<_WrappedHomePhotos> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_bloc.add(const _LoadItems());
|
||||
context.addEvent(const _LoadItems());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = VisibilityDetector(
|
||||
return VisibilityDetector(
|
||||
key: _key,
|
||||
onVisibilityChanged: (info) {
|
||||
final isVisible = info.visibleFraction >= 0.2;
|
||||
|
@ -177,47 +176,49 @@ class _WrappedHomePhotosState extends State<_WrappedHomePhotos> {
|
|||
},
|
||||
),
|
||||
],
|
||||
child: _BlocSelector<bool>(
|
||||
selector: (state) =>
|
||||
state.files.isEmpty && state.syncProgress != null,
|
||||
builder: (context, isInitialSyncing) {
|
||||
if (isInitialSyncing) {
|
||||
return const _InitialSyncBody();
|
||||
} else {
|
||||
return Shimmer(
|
||||
linearGradient: Theme.of(context).photoGridShimmerGradient,
|
||||
child: const _Body(),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: _BlocSelector(
|
||||
selector: (state) => state.selectedItems.isEmpty,
|
||||
builder: (context, isSelectedEmpty) => isSelectedEmpty
|
||||
? const DoubleTapExitContainer(
|
||||
child: _BodyView(),
|
||||
)
|
||||
: PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (_) {
|
||||
context.addEvent(const _SetSelectedItems(items: {}));
|
||||
},
|
||||
child: const _BodyView(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (getRawPlatform() == NpPlatform.android) {
|
||||
return WillPopScope(
|
||||
onWillPop: () => _onBackButtonPressed(context),
|
||||
child: content,
|
||||
);
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _onBackButtonPressed(BuildContext context) async {
|
||||
if (context.state.selectedItems.isEmpty) {
|
||||
return DoubleTapExitHandler()();
|
||||
} else {
|
||||
context.addEvent(const _SetSelectedItems(items: {}));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
late final _bloc = context.bloc;
|
||||
|
||||
final _key = GlobalKey();
|
||||
bool? _isVisible;
|
||||
}
|
||||
|
||||
class _BodyView extends StatelessWidget {
|
||||
const _BodyView();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BlocSelector<bool>(
|
||||
selector: (state) => state.files.isEmpty && state.syncProgress != null,
|
||||
builder: (context, isInitialSyncing) {
|
||||
if (isInitialSyncing) {
|
||||
return const _InitialSyncBody();
|
||||
} else {
|
||||
return Shimmer(
|
||||
linearGradient: Theme.of(context).photoGridShimmerGradient,
|
||||
child: const _Body(),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InitialSyncBody extends StatelessWidget {
|
||||
const _InitialSyncBody();
|
||||
|
||||
|
@ -521,7 +522,7 @@ typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
|||
|
||||
extension on BuildContext {
|
||||
_Bloc get bloc => read<_Bloc>();
|
||||
_State get state => bloc.state;
|
||||
// _State get state => bloc.state;
|
||||
void addEvent(_Event event) => bloc.add(event);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue