nc-photos/app/lib/widget/viewer.dart

273 lines
9.5 KiB
Dart
Raw Normal View History

import 'dart:async';
2021-04-10 06:28:12 +02:00
import 'dart:math';
2024-10-06 19:15:45 +02:00
import 'package:copy_with/copy_with.dart';
2021-04-10 06:28:12 +02:00
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
2023-04-17 18:15:29 +02:00
import 'package:flutter_bloc/flutter_bloc.dart';
2024-05-29 15:55:16 +02:00
import 'package:intl/intl.dart';
2022-01-25 11:17:19 +01:00
import 'package:kiwi/kiwi.dart';
2021-04-10 06:28:12 +02:00
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
2021-07-25 07:00:38 +02:00
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/asset.dart';
2024-10-06 19:15:45 +02:00
import 'package:nc_photos/bloc_util.dart';
2023-04-17 18:15:29 +02:00
import 'package:nc_photos/controller/account_controller.dart';
import 'package:nc_photos/controller/collection_items_controller.dart';
2024-10-06 19:15:45 +02:00
import 'package:nc_photos/controller/collections_controller.dart';
import 'package:nc_photos/controller/files_controller.dart';
import 'package:nc_photos/controller/pref_controller.dart';
2022-01-25 11:17:19 +01:00
import 'package:nc_photos/di_container.dart';
2021-09-28 22:56:44 +02:00
import 'package:nc_photos/download_handler.dart';
2023-04-17 18:15:29 +02:00
import 'package:nc_photos/entity/collection.dart';
import 'package:nc_photos/entity/collection/adapter.dart';
import 'package:nc_photos/entity/collection_item.dart';
import 'package:nc_photos/entity/file_descriptor.dart';
2024-10-06 19:15:45 +02:00
import 'package:nc_photos/exception_event.dart';
2022-09-07 11:37:50 +02:00
import 'package:nc_photos/flutter_util.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/live_photo_util.dart';
2022-05-04 10:42:46 +02:00
import 'package:nc_photos/platform/features.dart' as features;
import 'package:nc_photos/share_handler.dart';
2023-04-17 18:15:29 +02:00
import 'package:nc_photos/snack_bar_manager.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/theme.dart';
2021-08-23 19:28:25 +02:00
import 'package:nc_photos/widget/disposable.dart';
2024-10-06 19:15:45 +02:00
import 'package:nc_photos/widget/file_content_view.dart';
import 'package:nc_photos/widget/handler/remove_selection_handler.dart';
2021-07-31 17:38:26 +02:00
import 'package:nc_photos/widget/horizontal_page_viewer.dart';
2022-07-12 22:11:27 +02:00
import 'package:nc_photos/widget/image_editor.dart';
import 'package:nc_photos/widget/image_enhancer.dart';
2024-10-06 19:15:45 +02:00
import 'package:nc_photos/widget/page_visibility_mixin.dart';
import 'package:nc_photos/widget/png_icon.dart';
2021-09-14 23:00:24 +02:00
import 'package:nc_photos/widget/slideshow_dialog.dart';
import 'package:nc_photos/widget/slideshow_viewer.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/widget/viewer_detail_pane.dart';
2021-09-14 23:00:24 +02:00
import 'package:nc_photos/widget/viewer_mixin.dart';
2022-12-16 16:01:04 +01:00
import 'package:np_codegen/np_codegen.dart';
2024-10-06 19:15:45 +02:00
import 'package:np_collection/np_collection.dart';
import 'package:np_common/object_util.dart';
import 'package:np_common/or_null.dart';
2024-10-06 19:15:45 +02:00
import 'package:np_common/unique.dart';
2024-05-29 15:55:16 +02:00
import 'package:np_platform_util/np_platform_util.dart';
2024-10-06 19:15:45 +02:00
import 'package:to_string/to_string.dart';
2024-10-09 14:47:47 +02:00
part 'viewer.g.dart';
2024-10-06 19:15:45 +02:00
part 'viewer/app_bar.dart';
2024-10-09 14:47:47 +02:00
part 'viewer/app_bar_buttons.dart';
2024-10-06 19:15:45 +02:00
part 'viewer/bloc.dart';
part 'viewer/detail_pane.dart';
part 'viewer/state_event.dart';
part 'viewer/type.dart';
part 'viewer/view.dart';
2021-04-10 06:28:12 +02:00
class ViewerArguments {
2023-04-17 18:15:29 +02:00
const ViewerArguments(
2024-10-06 19:15:45 +02:00
this.fileIds,
2021-08-13 22:18:35 +02:00
this.startIndex, {
2024-10-06 19:15:45 +02:00
this.collectionId,
2021-08-13 22:18:35 +02:00
});
2021-04-10 06:28:12 +02:00
2024-10-06 19:15:45 +02:00
final List<int> fileIds;
2021-04-10 06:28:12 +02:00
final int startIndex;
2024-10-06 19:15:45 +02:00
final String? collectionId;
2021-04-10 06:28:12 +02:00
}
2024-10-06 19:15:45 +02:00
class Viewer extends StatelessWidget {
2021-04-10 06:28:12 +02:00
static const routeName = "/viewer";
2022-09-07 11:37:50 +02:00
static Route buildRoute(ViewerArguments args) =>
CustomizableMaterialPageRoute(
transitionDuration: k.heroDurationNormal,
reverseTransitionDuration: k.heroDurationNormal,
builder: (_) => Viewer.fromArgs(args),
2021-07-23 22:05:57 +02:00
);
2021-09-15 08:58:06 +02:00
const Viewer({
2024-05-28 17:10:33 +02:00
super.key,
2024-10-06 19:15:45 +02:00
required this.fileIds,
2021-07-23 22:05:57 +02:00
required this.startIndex,
2024-10-06 19:15:45 +02:00
this.collectionId,
2024-05-28 17:10:33 +02:00
});
2021-04-10 06:28:12 +02:00
2021-07-23 22:05:57 +02:00
Viewer.fromArgs(ViewerArguments args, {Key? key})
2021-04-10 06:28:12 +02:00
: this(
key: key,
2024-10-06 19:15:45 +02:00
fileIds: args.fileIds,
2021-04-10 06:28:12 +02:00
startIndex: args.startIndex,
2024-10-06 19:15:45 +02:00
collectionId: args.collectionId,
2021-04-10 06:28:12 +02:00
);
@override
2024-10-06 19:15:45 +02:00
Widget build(BuildContext context) {
final accountController = context.read<AccountController>();
return BlocProvider(
create: (_) => _Bloc(
KiwiContainer().resolve(),
account: accountController.account,
filesController: accountController.filesController,
collectionsController: accountController.collectionsController,
prefController: context.read(),
fileIds: fileIds,
startIndex: startIndex,
brightness: Theme.of(context).brightness,
collectionId: collectionId,
)..add(const _Init()),
child: const _WrappedViewer(),
);
}
2021-04-10 06:28:12 +02:00
2024-10-06 19:15:45 +02:00
final List<int> fileIds;
2021-04-10 06:28:12 +02:00
final int startIndex;
2021-08-13 22:18:35 +02:00
2024-10-06 19:15:45 +02:00
/// ID of the collection these files belongs to, or null
final String? collectionId;
2021-04-10 06:28:12 +02:00
}
2024-10-06 19:15:45 +02:00
class _WrappedViewer extends StatefulWidget {
const _WrappedViewer();
@override
2024-10-06 19:15:45 +02:00
State<StatefulWidget> createState() => _WrappedViewerState();
}
2024-10-06 19:15:45 +02:00
@npLog
class _WrappedViewerState extends State<_WrappedViewer>
with
DisposableManagerMixin<_WrappedViewer>,
ViewerControllersMixin<_WrappedViewer>,
RouteAware,
PageVisibilityMixin {
2021-04-10 06:28:12 +02:00
@override
2024-10-06 19:15:45 +02:00
Widget build(BuildContext context) {
2022-11-12 10:55:33 +01:00
return Theme(
data: buildDarkTheme(context),
2023-08-04 21:51:28 +02:00
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.black,
systemNavigationBarIconBrightness: Brightness.dark,
),
2024-10-06 19:15:45 +02:00
child: MultiBlocListener(
listeners: [
_BlocListenerT(
selector: (state) => state.imageEditorRequest,
listener: (context, imageEditorRequest) {
if (imageEditorRequest.value != null) {
Navigator.of(context).pushNamed(ImageEditor.routeName,
arguments: imageEditorRequest.value);
}
},
2021-04-10 06:28:12 +02:00
),
2024-10-06 19:15:45 +02:00
_BlocListenerT(
selector: (state) => state.imageEnhancerRequest,
listener: (context, imageEnhancerRequest) {
if (imageEnhancerRequest.value != null) {
Navigator.of(context).pushNamed(ImageEnhancer.routeName,
arguments: imageEnhancerRequest.value);
}
},
),
2024-10-06 19:15:45 +02:00
_BlocListenerT(
selector: (state) => state.shareRequest,
listener: (context, shareRequest) {
if (shareRequest.value != null) {
ShareHandler(
KiwiContainer().resolve<DiContainer>(),
context: context,
).shareFiles(
context.bloc.account, [shareRequest.value!.file]);
}
},
2024-05-29 15:55:16 +02:00
),
2024-10-06 19:15:45 +02:00
_BlocListenerT(
selector: (state) => state.slideshowRequest,
listener: _onSlideshowRequest,
),
_BlocListenerT(
selector: (state) => state.error,
listener: (context, error) {
if (error != null && isPageVisible()) {
SnackBarManager().showSnackBarForException(error.error);
}
},
),
],
child: _BlocBuilder(
buildWhen: (previous, current) =>
previous.isShowAppBar != current.isShowAppBar ||
previous.isDetailPaneActive != current.isDetailPaneActive,
builder: (context, state) => Scaffold(
extendBodyBehindAppBar: true,
extendBody: true,
appBar: state.isShowAppBar
? const PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: _AppBar(),
)
: null,
bottomNavigationBar:
state.isShowAppBar && !state.isDetailPaneActive
? const _BottomAppBar()
: null,
body: const _ContentBody(),
2022-01-21 19:50:33 +01:00
),
2021-04-10 06:28:12 +02:00
),
),
),
);
}
2024-10-06 19:15:45 +02:00
Future<void> _onSlideshowRequest(
BuildContext context,
Unique<_SlideshowRequest?> slideshowRequest,
) async {
if (slideshowRequest.value == null) {
2022-05-04 10:42:46 +02:00
return;
}
2021-09-14 23:00:24 +02:00
final result = await showDialog<SlideshowConfig>(
context: context,
builder: (_) => SlideshowDialog(
2024-10-06 19:15:45 +02:00
duration: context.bloc.prefController.slideshowDurationValue,
isShuffle: context.bloc.prefController.isSlideshowShuffleValue,
isRepeat: context.bloc.prefController.isSlideshowRepeatValue,
isReverse: context.bloc.prefController.isSlideshowReverseValue,
2021-09-14 23:00:24 +02:00
),
);
2024-10-06 19:15:45 +02:00
if (!context.mounted || result == null) {
return;
}
2024-10-06 19:15:45 +02:00
unawaited(
context.bloc.prefController.setSlideshowDuration(result.duration));
unawaited(
context.bloc.prefController.setSlideshowShuffle(result.isShuffle));
unawaited(context.bloc.prefController.setSlideshowRepeat(result.isRepeat));
unawaited(
context.bloc.prefController.setSlideshowReverse(result.isReverse));
final newIndex = await Navigator.of(context).pushNamed<int>(
SlideshowViewer.routeName,
2024-10-06 19:15:45 +02:00
arguments: SlideshowViewerArguments(
slideshowRequest.value!.account,
slideshowRequest.value!.files,
slideshowRequest.value!.startIndex,
result,
),
2021-09-14 23:00:24 +02:00
);
2024-10-06 19:15:45 +02:00
_log.info("[_onSlideshowRequest] Slideshow ended, jump to: $newIndex");
if (newIndex != null && context.mounted) {
2024-10-06 19:15:45 +02:00
context.addEvent(_RequestPage(newIndex));
}
}
2021-04-10 06:28:12 +02:00
}
2024-10-06 19:15:45 +02:00
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>;
2024-05-29 15:55:16 +02:00
2024-10-06 19:15:45 +02:00
extension on BuildContext {
_Bloc get bloc => read<_Bloc>();
_State get state => bloc.state;
void addEvent(_Event event) => bloc.add(event);
2024-05-29 15:55:16 +02:00
}