nc-photos/lib/widget/home_photos.dart

733 lines
22 KiB
Dart
Raw Normal View History

2021-05-19 21:11:06 +02:00
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
2021-04-10 06:28:12 +02:00
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:intl/intl.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/bloc/scan_dir.dart';
import 'package:nc_photos/entity/album.dart';
2021-07-05 09:54:01 +02:00
import 'package:nc_photos/entity/album/item.dart';
2021-06-24 18:26:56 +02:00
import 'package:nc_photos/entity/album/provider.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/entity/file.dart';
2021-05-24 09:09:25 +02:00
import 'package:nc_photos/entity/file/data_source.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/k.dart' as k;
2021-04-11 11:57:08 +02:00
import 'package:nc_photos/metadata_task_manager.dart';
import 'package:nc_photos/platform/k.dart' as platform_k;
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/pref.dart';
import 'package:nc_photos/primitive.dart';
import 'package:nc_photos/share_handler.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/use_case/remove.dart';
import 'package:nc_photos/use_case/update_album.dart';
2021-05-28 21:44:09 +02:00
import 'package:nc_photos/use_case/update_property.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/widget/album_picker_dialog.dart';
import 'package:nc_photos/widget/home_app_bar.dart';
2021-05-19 21:11:06 +02:00
import 'package:nc_photos/widget/measure.dart';
import 'package:nc_photos/widget/page_visibility_mixin.dart';
2021-05-06 13:29:20 +02:00
import 'package:nc_photos/widget/photo_list_item.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/widget/popup_menu_zoom.dart';
2021-04-23 09:55:53 +02:00
import 'package:nc_photos/widget/selectable_item_stream_list_mixin.dart';
2021-04-10 06:28:12 +02:00
import 'package:nc_photos/widget/viewer.dart';
class HomePhotos extends StatefulWidget {
HomePhotos({
Key key,
@required this.account,
}) : super(key: key);
@override
createState() => _HomePhotosState();
final Account account;
}
2021-04-23 09:55:53 +02:00
class _HomePhotosState extends State<HomePhotos>
with
SelectableItemStreamListMixin<HomePhotos>,
RouteAware,
PageVisibilityMixin {
2021-04-10 06:28:12 +02:00
@override
initState() {
super.initState();
_thumbZoomLevel = Pref.inst().getHomePhotosZoomLevel(0);
_initBloc();
2021-04-10 06:28:12 +02:00
}
@override
build(BuildContext context) {
return BlocListener<ScanDirBloc, ScanDirBlocState>(
bloc: _bloc,
listener: (context, state) => _onStateChange(context, state),
child: BlocBuilder<ScanDirBloc, ScanDirBlocState>(
bloc: _bloc,
2021-04-23 09:55:53 +02:00
builder: (context, state) => _buildContent(context, state),
2021-04-10 06:28:12 +02:00
),
);
}
void _initBloc() {
2021-04-11 17:38:01 +02:00
_bloc = ScanDirBloc.of(widget.account);
2021-04-10 06:28:12 +02:00
if (_bloc.state is ScanDirBlocInit) {
_log.info("[_initBloc] Initialize bloc");
2021-04-11 11:52:39 +02:00
_reqQuery();
2021-04-10 06:28:12 +02:00
} else {
// process the current state
2021-07-03 13:32:23 +02:00
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_onStateChange(context, _bloc.state);
});
});
2021-04-10 06:28:12 +02:00
}
}
Widget _buildContent(BuildContext context, ScanDirBlocState state) {
2021-05-19 21:11:06 +02:00
return LayoutBuilder(builder: (context, constraints) {
final scrollExtent = _getScrollViewExtent(constraints);
return Stack(
children: [
buildItemStreamListOuter(
context,
child: Theme(
data: Theme.of(context).copyWith(
accentColor: AppTheme.getOverscrollIndicatorColor(context),
),
child: DraggableScrollbar.semicircle(
controller: _scrollController,
overrideMaxScrollExtent: scrollExtent,
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context)
.copyWith(scrollbars: false),
child: CustomScrollView(
controller: _scrollController,
slivers: [
_buildAppBar(context),
SliverPadding(
2021-07-05 07:23:01 +02:00
padding: const EdgeInsets.symmetric(vertical: 8),
2021-07-06 07:10:36 +02:00
sliver: buildItemStreamList(
maxCrossAxisExtent: _thumbSize.toDouble(),
onMaxExtentChanged: (value) {
setState(() {
_itemListMaxExtent = value;
});
},
),
),
],
),
2021-04-10 06:28:12 +02:00
),
2021-05-19 21:11:06 +02:00
),
2021-04-23 09:55:53 +02:00
),
2021-04-10 06:28:12 +02:00
),
2021-05-19 21:11:06 +02:00
if (state is ScanDirBlocLoading)
Align(
alignment: Alignment.bottomCenter,
child: const LinearProgressIndicator(),
),
],
);
});
2021-04-10 06:28:12 +02:00
}
Widget _buildAppBar(BuildContext context) {
2021-04-23 09:55:53 +02:00
if (isSelectionMode) {
2021-04-10 06:28:12 +02:00
return _buildSelectionAppBar(context);
} else {
return _buildNormalAppBar(context);
}
}
Widget _buildSelectionAppBar(BuildContext conetxt) {
return Theme(
data: Theme.of(context).copyWith(
appBarTheme: AppTheme.getContextualAppBarTheme(context),
),
child: SliverAppBar(
pinned: true,
leading: IconButton(
icon: const Icon(Icons.close),
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
onPressed: () {
setState(() {
2021-04-23 09:55:53 +02:00
clearSelectedItems();
2021-04-10 06:28:12 +02:00
});
},
),
title: Text(AppLocalizations.of(context)
2021-04-23 09:55:53 +02:00
.selectionAppBarTitle(selectedListItems.length)),
2021-04-10 06:28:12 +02:00
actions: [
if (platform_k.isAndroid)
IconButton(
icon: const Icon(Icons.share),
tooltip: AppLocalizations.of(context).shareSelectedTooltip,
onPressed: () {
_onSelectionAppBarSharePressed(context);
},
),
2021-04-10 06:28:12 +02:00
IconButton(
icon: const Icon(Icons.playlist_add),
tooltip: AppLocalizations.of(context).addSelectedToAlbumTooltip,
onPressed: () {
_onSelectionAppBarAddToAlbumPressed(context);
},
),
2021-05-28 21:44:09 +02:00
PopupMenuButton(
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
itemBuilder: (context) => [
PopupMenuItem(
value: _SelectionAppBarMenuOption.archive,
child:
Text(AppLocalizations.of(context).archiveSelectedMenuLabel),
),
2021-07-10 16:31:57 +02:00
PopupMenuItem(
value: _SelectionAppBarMenuOption.delete,
child: Text(AppLocalizations.of(context).deleteSelectedTooltip),
),
2021-05-28 21:44:09 +02:00
],
onSelected: (option) {
_onSelectionAppBarMenuSelected(context, option);
},
),
2021-04-10 06:28:12 +02:00
],
),
);
}
Widget _buildNormalAppBar(BuildContext context) {
2021-05-19 21:11:06 +02:00
return SliverMeasureExtent(
onChange: (extent) {
_appBarExtent = extent;
2021-04-11 11:57:08 +02:00
},
2021-05-19 21:11:06 +02:00
child: HomeSliverAppBar(
account: widget.account,
actions: [
PopupMenuButton(
icon: const Icon(Icons.zoom_in),
tooltip: AppLocalizations.of(context).zoomTooltip,
itemBuilder: (context) => [
PopupMenuZoom(
initialValue: _thumbZoomLevel,
minValue: -1,
maxValue: 2,
onChanged: (value) {
setState(() {
_setThumbZoomLevel(value.round());
});
Pref.inst().setHomePhotosZoomLevel(_thumbZoomLevel);
},
),
],
),
],
menuActions: [
PopupMenuItem(
value: _menuValueRefresh,
child: Text(AppLocalizations.of(context).refreshMenuLabel),
),
],
onSelectedMenuActions: (option) {
switch (option) {
case _menuValueRefresh:
_onRefreshSelected();
break;
}
},
),
2021-04-10 06:28:12 +02:00
);
}
void _onStateChange(BuildContext context, ScanDirBlocState state) {
if (state is ScanDirBlocInit) {
2021-04-23 09:55:53 +02:00
itemStreamListItems = [];
2021-04-10 06:28:12 +02:00
} else if (state is ScanDirBlocSuccess || state is ScanDirBlocLoading) {
_transformItems(state.files);
if (state is ScanDirBlocSuccess) {
if (Pref.inst().isEnableExif() && !_hasFiredMetadataTask.value) {
KiwiContainer()
.resolve<MetadataTaskManager>()
.addTask(MetadataTask(widget.account));
_hasFiredMetadataTask.value = true;
}
}
2021-04-10 06:28:12 +02:00
} else if (state is ScanDirBlocFailure) {
_transformItems(state.files);
if (isPageVisible()) {
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(state.exception, context)),
duration: k.snackBarDurationNormal,
));
}
2021-04-10 06:28:12 +02:00
} else if (state is ScanDirBlocInconsistent) {
2021-04-11 11:52:39 +02:00
_reqQuery();
2021-04-10 06:28:12 +02:00
}
}
2021-04-23 09:55:53 +02:00
void _onItemTap(int index) {
Navigator.pushNamed(context, Viewer.routeName,
arguments: ViewerArguments(widget.account, _backingFiles, index));
2021-04-10 06:28:12 +02:00
}
void _onSelectionAppBarSharePressed(BuildContext context) {
assert(platform_k.isAndroid);
final selected = selectedListItems
.whereType<_FileListItem>()
.map((e) => e.file)
.toList();
ShareHandler().shareFiles(context, widget.account, selected).then((_) {
setState(() {
clearSelectedItems();
});
});
}
2021-04-10 06:28:12 +02:00
void _onSelectionAppBarAddToAlbumPressed(BuildContext context) {
showDialog(
context: context,
builder: (_) => AlbumPickerDialog(
account: widget.account,
),
).then((value) {
if (value == null) {
// user cancelled the dialog
} else if (value is Album) {
2021-04-26 18:18:20 +02:00
_log.info(
"[_onSelectionAppBarAddToAlbumPressed] Album picked: ${value.name}");
2021-04-10 06:28:12 +02:00
_addSelectedToAlbum(context, value).then((_) {
setState(() {
2021-04-23 09:55:53 +02:00
clearSelectedItems();
2021-04-10 06:28:12 +02:00
});
SnackBarManager().showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context)
.addSelectedToAlbumSuccessNotification(value.name)),
duration: k.snackBarDurationNormal,
));
}).catchError((_) {});
} else {
SnackBarManager().showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context)
.addSelectedToAlbumFailureNotification),
duration: k.snackBarDurationNormal,
));
}
}).catchError((e, stacktrace) {
_log.severe(
"[_onSelectionAppBarAddToAlbumPressed] Failed while showDialog",
e,
stacktrace);
SnackBarManager().showSnackBar(SnackBar(
content: Text(
"${AppLocalizations.of(context).addSelectedToAlbumFailureNotification}: "
"${exception_util.toUserString(e, context)}"),
duration: k.snackBarDurationNormal,
));
});
}
Future<void> _addSelectedToAlbum(BuildContext context, Album album) async {
2021-06-24 18:26:56 +02:00
assert(album.provider is AlbumStaticProvider);
2021-04-23 09:55:53 +02:00
final selected = selectedListItems
.whereType<_FileListItem>()
2021-04-10 06:28:12 +02:00
.map((e) => AlbumFileItem(file: e.file))
.toList();
try {
final albumRepo = AlbumRepo(AlbumCachedDataSource());
await UpdateAlbum(albumRepo)(
widget.account,
album.copyWith(
2021-06-24 18:26:56 +02:00
provider: AlbumStaticProvider(
items: makeDistinctAlbumItems([
...selected,
...AlbumStaticProvider.of(album).items,
2021-06-24 18:26:56 +02:00
]),
),
2021-04-10 06:28:12 +02:00
));
} catch (e, stacktrace) {
2021-04-27 22:06:16 +02:00
_log.shout(
2021-04-10 06:28:12 +02:00
"[_addSelectedToAlbum] Failed while updating album", e, stacktrace);
SnackBarManager().showSnackBar(SnackBar(
content: Text(
"${AppLocalizations.of(context).addSelectedToAlbumFailureNotification}: "
"${exception_util.toUserString(e, context)}"),
duration: k.snackBarDurationNormal,
));
rethrow;
}
}
Future<void> _onSelectionAppBarDeletePressed(BuildContext context) async {
SnackBarManager().showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context)
2021-04-23 09:55:53 +02:00
.deleteSelectedProcessingNotification(selectedListItems.length)),
2021-04-10 06:28:12 +02:00
duration: k.snackBarDurationShort,
));
2021-04-23 09:55:53 +02:00
final selectedFiles = selectedListItems
.whereType<_FileListItem>()
.map((e) => e.file)
.toList();
2021-04-10 06:28:12 +02:00
setState(() {
2021-04-23 09:55:53 +02:00
clearSelectedItems();
2021-04-10 06:28:12 +02:00
});
final fileRepo = FileRepo(FileCachedDataSource());
final albumRepo = AlbumRepo(AlbumCachedDataSource());
final failures = <File>[];
for (final f in selectedFiles) {
try {
await Remove(fileRepo, albumRepo)(widget.account, f);
} catch (e, stacktrace) {
2021-04-27 22:06:16 +02:00
_log.shout(
"[_onSelectionAppBarDeletePressed] Failed while removing file" +
(kDebugMode ? ": ${f.path}" : ""),
2021-04-10 06:28:12 +02:00
e,
stacktrace);
failures.add(f);
}
}
if (failures.isEmpty) {
SnackBarManager().showSnackBar(SnackBar(
content: Text(
AppLocalizations.of(context).deleteSelectedSuccessNotification),
duration: k.snackBarDurationNormal,
));
} else {
SnackBarManager().showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context)
.deleteSelectedFailureNotification(failures.length)),
duration: k.snackBarDurationNormal,
));
}
}
2021-05-28 21:44:09 +02:00
void _onSelectionAppBarMenuSelected(
BuildContext context, _SelectionAppBarMenuOption option) {
switch (option) {
case _SelectionAppBarMenuOption.archive:
_onSelectionAppBarArchivePressed(context);
break;
2021-07-10 16:31:57 +02:00
case _SelectionAppBarMenuOption.delete:
_onSelectionAppBarDeletePressed(context);
break;
2021-05-28 21:44:09 +02:00
default:
_log.shout("[_onSelectionAppBarMenuSelected] Unknown option: $option");
break;
}
}
Future<void> _onSelectionAppBarArchivePressed(BuildContext context) async {
SnackBarManager().showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context)
.archiveSelectedProcessingNotification(selectedListItems.length)),
duration: k.snackBarDurationShort,
));
final selectedFiles = selectedListItems
.whereType<_FileListItem>()
.map((e) => e.file)
.toList();
setState(() {
clearSelectedItems();
});
final fileRepo = FileRepo(FileCachedDataSource());
final failures = <File>[];
for (final f in selectedFiles) {
try {
await UpdateProperty(fileRepo)
2021-05-28 21:44:09 +02:00
.updateIsArchived(widget.account, f, true);
} catch (e, stacktrace) {
_log.shout(
"[_onSelectionAppBarArchivePressed] Failed while archiving file" +
(kDebugMode ? ": ${f.path}" : ""),
e,
stacktrace);
failures.add(f);
}
}
if (failures.isEmpty) {
SnackBarManager().showSnackBar(SnackBar(
content: Text(
AppLocalizations.of(context).archiveSelectedSuccessNotification),
duration: k.snackBarDurationNormal,
));
} else {
SnackBarManager().showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context)
.archiveSelectedFailureNotification(failures.length)),
duration: k.snackBarDurationNormal,
));
}
}
2021-04-11 11:57:08 +02:00
void _onRefreshSelected() {
_hasFiredMetadataTask.value = false;
_reqRefresh();
2021-04-11 11:57:08 +02:00
}
2021-04-10 06:28:12 +02:00
/// Transform a File list to grid items
void _transformItems(List<File> files) {
_backingFiles = files
2021-05-28 20:45:00 +02:00
.where((element) =>
file_util.isSupportedFormat(element) && element.isArchived != true)
2021-04-10 06:28:12 +02:00
.sorted(compareFileDateTimeDescending);
DateTime currentDate;
final isMonthOnly = _thumbZoomLevel < 0;
2021-04-23 09:55:53 +02:00
itemStreamListItems = () sync* {
for (int i = 0; i < _backingFiles.length; ++i) {
final f = _backingFiles[i];
2021-04-28 14:55:26 +02:00
final newDate = f.bestDateTime?.toLocal();
if (newDate?.year != currentDate?.year ||
newDate?.month != currentDate?.month ||
(!isMonthOnly && newDate?.day != currentDate?.day)) {
yield _DateListItem(date: newDate, isMonthOnly: isMonthOnly);
currentDate = newDate;
2021-04-23 09:55:53 +02:00
}
2021-04-28 14:55:26 +02:00
2021-05-05 22:03:39 +02:00
final previewUrl = api_util.getFilePreviewUrl(widget.account, f,
width: _thumbSize, height: _thumbSize);
2021-05-06 13:36:20 +02:00
if (file_util.isSupportedImageFormat(f)) {
yield _ImageListItem(
file: f,
account: widget.account,
previewUrl: previewUrl,
onTap: () => _onItemTap(i),
);
} else if (file_util.isSupportedVideoFormat(f)) {
yield _VideoListItem(
file: f,
account: widget.account,
previewUrl: previewUrl,
onTap: () => _onItemTap(i),
);
} else {
_log.shout(
"[_transformItems] Unsupported file format: ${f.contentType}");
}
2021-04-10 06:28:12 +02:00
}
2021-07-07 20:51:34 +02:00
}()
.toList();
2021-04-10 06:28:12 +02:00
}
2021-04-11 11:52:39 +02:00
void _reqQuery() {
2021-04-10 06:28:12 +02:00
_bloc.add(ScanDirBlocQuery(
widget.account,
2021-04-11 11:52:39 +02:00
widget.account.roots
2021-04-10 06:28:12 +02:00
.map((e) => File(
path:
"${api_util.getWebdavRootUrlRelative(widget.account)}/$e"))
.toList()));
}
void _reqRefresh() {
_bloc.add(ScanDirBlocRefresh(
widget.account,
widget.account.roots
.map((e) => File(
path:
"${api_util.getWebdavRootUrlRelative(widget.account)}/$e"))
.toList()));
}
2021-04-28 14:55:26 +02:00
void _setThumbZoomLevel(int level) {
final prevLevel = _thumbZoomLevel;
_thumbZoomLevel = level;
if ((prevLevel >= 0) != (level >= 0)) {
_transformItems(_backingFiles);
}
}
2021-05-19 21:11:06 +02:00
/// Return the estimated scroll extent of the custom scroll view, or null
double _getScrollViewExtent(BoxConstraints constraints) {
2021-07-06 07:10:36 +02:00
if (_itemListMaxExtent != null &&
2021-05-19 21:11:06 +02:00
constraints.hasBoundedHeight &&
_appBarExtent != null) {
// scroll extent = list height - widget viewport height + sliver app bar height + list padding
2021-05-19 21:11:06 +02:00
final scrollExtent =
_itemListMaxExtent - constraints.maxHeight + _appBarExtent + 16;
2021-05-19 21:11:06 +02:00
_log.info(
"[_getScrollViewExtent] $_itemListMaxExtent - ${constraints.maxHeight} + $_appBarExtent + 16 = $scrollExtent");
2021-05-19 21:11:06 +02:00
return scrollExtent;
} else {
return null;
}
}
2021-04-10 06:28:12 +02:00
int get _thumbSize {
switch (_thumbZoomLevel) {
2021-04-28 14:55:26 +02:00
case -1:
return 96;
2021-04-10 06:28:12 +02:00
case 1:
return 176;
case 2:
return 256;
case 0:
default:
return 112;
}
}
Primitive<bool> get _hasFiredMetadataTask {
final blocId =
"${widget.account.scheme}://${widget.account.username}@${widget.account.address}";
try {
_log.fine("[_hasFiredMetadataTask] Resolving bloc for '$blocId'");
return KiwiContainer().resolve<Primitive<bool>>(
"HomePhotosState.hasFiredMetadataTask($blocId)");
} catch (_) {
_log.info(
"[_hasFiredMetadataTask] New bloc instance for account: ${widget.account}");
final obj = Primitive(false);
KiwiContainer().registerInstance<Primitive<bool>>(obj,
name: "HomePhotosState.hasFiredMetadataTask($blocId)");
return obj;
}
}
2021-04-10 06:28:12 +02:00
ScanDirBloc _bloc;
var _backingFiles = <File>[];
var _thumbZoomLevel = 0;
2021-05-19 21:11:06 +02:00
final ScrollController _scrollController = ScrollController();
double _appBarExtent;
2021-07-06 07:10:36 +02:00
double _itemListMaxExtent;
2021-05-19 21:11:06 +02:00
2021-04-10 06:28:12 +02:00
static final _log = Logger("widget.home_photos._HomePhotosState");
2021-04-11 11:57:08 +02:00
static const _menuValueRefresh = 0;
2021-04-10 06:28:12 +02:00
}
2021-07-06 15:03:29 +02:00
abstract class _ListItem implements SelectableItem {
_ListItem({
VoidCallback onTap,
}) : _onTap = onTap;
@override
get onTap => _onTap;
@override
get isSelectable => true;
@override
get staggeredTile => const StaggeredTile.count(1, 1);
final VoidCallback _onTap;
}
class _DateListItem extends _ListItem {
_DateListItem({
@required this.date,
this.isMonthOnly = false,
2021-07-06 15:03:29 +02:00
});
@override
get isSelectable => false;
@override
get staggeredTile => const StaggeredTile.extent(99, 32);
2021-04-10 06:28:12 +02:00
2021-04-23 09:55:53 +02:00
@override
buildWidget(BuildContext context) {
String subtitle = "";
if (date != null) {
final pattern =
isMonthOnly ? DateFormat.YEAR_MONTH : DateFormat.YEAR_MONTH_DAY;
subtitle =
DateFormat(pattern, Localizations.localeOf(context).languageCode)
.format(date.toLocal());
}
2021-04-23 09:55:53 +02:00
return Align(
alignment: AlignmentDirectional.centerStart,
2021-07-05 07:23:01 +02:00
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
subtitle,
style: Theme.of(context).textTheme.caption.copyWith(
color: AppTheme.getPrimaryTextColor(context),
fontWeight: FontWeight.bold,
),
),
2021-04-23 09:55:53 +02:00
),
);
}
2021-04-10 06:28:12 +02:00
final DateTime date;
final bool isMonthOnly;
2021-04-10 06:28:12 +02:00
}
2021-07-06 15:03:29 +02:00
abstract class _FileListItem extends _ListItem {
2021-04-23 09:55:53 +02:00
_FileListItem({
@required this.file,
VoidCallback onTap,
2021-07-06 15:03:29 +02:00
}) : super(onTap: onTap);
2021-04-10 06:28:12 +02:00
2021-04-23 09:55:53 +02:00
@override
operator ==(Object other) {
return other is _FileListItem && file.path == other.file.path;
}
2021-04-10 06:28:12 +02:00
2021-04-23 09:55:53 +02:00
@override
get hashCode => file.path.hashCode;
2021-04-10 06:28:12 +02:00
final File file;
}
2021-04-23 09:55:53 +02:00
class _ImageListItem extends _FileListItem {
_ImageListItem({
@required File file,
@required this.account,
@required this.previewUrl,
VoidCallback onTap,
}) : super(file: file, onTap: onTap);
@override
buildWidget(BuildContext context) {
2021-05-06 13:29:20 +02:00
return PhotoListImage(
account: account,
previewUrl: previewUrl,
2021-06-22 07:24:37 +02:00
isGif: file.contentType == "image/gif",
2021-04-23 09:55:53 +02:00
);
}
2021-04-10 06:28:12 +02:00
2021-04-23 09:55:53 +02:00
final Account account;
2021-04-10 06:28:12 +02:00
final String previewUrl;
}
2021-05-06 13:36:20 +02:00
class _VideoListItem extends _FileListItem {
_VideoListItem({
@required File file,
@required this.account,
@required this.previewUrl,
VoidCallback onTap,
}) : super(file: file, onTap: onTap);
@override
buildWidget(BuildContext context) {
return PhotoListVideo(
account: account,
previewUrl: previewUrl,
);
}
final Account account;
final String previewUrl;
}
2021-05-28 21:44:09 +02:00
enum _SelectionAppBarMenuOption {
archive,
delete,
2021-05-28 21:44:09 +02:00
}