print: show feedback for multipage entries

This commit is contained in:
Thibault Deckers 2021-01-27 21:16:43 +09:00
parent 10b4ce6898
commit c7b6e17a7f
6 changed files with 37 additions and 28 deletions

View file

@ -111,7 +111,9 @@ class MediaStoreSource extends CollectionSource {
final uriByContentId = Map.fromEntries(changedUris.map((uri) {
if (uri == null) return null;
final idString = Uri.parse(uri).pathSegments.last;
return MapEntry(int.tryParse(idString), uri);
final contentId = int.tryParse(idString);
if (contentId == null) return null;
return MapEntry(contentId, uri);
}).where((kv) => kv != null));
// clean up obsolete entries

View file

@ -79,14 +79,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
if (!await checkFreeSpaceForMove(context, selection, destinationAlbum, moveType)) return;
final copy = moveType == MoveType.copy;
final selectionCount = selection.length;
showOpReport<MoveOpEvent>(
context: context,
selection: selection,
opStream: ImageFileService.move(selection, copy: copy, destinationAlbum: destinationAlbum),
itemCount: selectionCount,
onDone: (processed) async {
final movedOps = processed.where((e) => e.success);
final movedCount = movedOps.length;
final selectionCount = selection.length;
if (movedCount < selectionCount) {
final count = selectionCount - movedCount;
showFeedback(context, 'Failed to ${copy ? 'copy' : 'move'} ${Intl.plural(count, one: '$count item', other: '$count items')}');
@ -132,14 +132,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
if (!await checkStoragePermission(context, selection)) return;
final selectionCount = selection.length;
showOpReport<ImageOpEvent>(
context: context,
selection: selection,
opStream: ImageFileService.delete(selection),
itemCount: selectionCount,
onDone: (processed) {
final deletedUris = processed.where((e) => e.success).map((e) => e.uri).toList();
final deletedCount = deletedUris.length;
final selectionCount = selection.length;
if (deletedCount < selectionCount) {
final count = selectionCount - deletedCount;
showFeedback(context, 'Failed to delete ${Intl.plural(count, one: '$count item', other: '$count items')}');

View file

@ -1,5 +1,3 @@
import 'package:aves/model/entry.dart';
import 'package:aves/services/image_op_events.dart';
import 'package:aves/theme/durations.dart';
import 'package:flushbar/flushbar.dart';
import 'package:flutter/material.dart';
@ -27,22 +25,20 @@ mixin FeedbackMixin {
// report overlay for multiple operations
OverlayEntry _opReportOverlayEntry;
void showOpReport<T extends ImageOpEvent>({
void showOpReport<T>({
@required BuildContext context,
@required Set<AvesEntry> selection,
@required Stream<T> opStream,
@required void Function(Set<T> processed) onDone,
@required int itemCount,
void Function(Set<T> processed) onDone,
}) {
OverlayEntry _opReportOverlayEntry;
_opReportOverlayEntry = OverlayEntry(
builder: (context) => ReportOverlay<T>(
opStream: opStream,
itemCount: selection.length,
itemCount: itemCount,
onDone: (processed) {
_opReportOverlayEntry?.remove();
_opReportOverlayEntry = null;
onDone(processed);
_opReportOverlayEntry.remove();
onDone?.call(processed);
},
),
);

View file

@ -79,14 +79,14 @@ class AlbumChipActionDelegate extends ChipActionDelegate with FeedbackMixin, Per
if (!await checkStoragePermission(context, selection)) return;
final selectionCount = selection.length;
showOpReport<ImageOpEvent>(
context: context,
selection: selection,
opStream: ImageFileService.delete(selection),
itemCount: selectionCount,
onDone: (processed) {
final deletedUris = processed.where((e) => e.success).map((e) => e.uri).toList();
final deletedCount = deletedUris.length;
final selectionCount = selection.length;
if (deletedCount < selectionCount) {
final count = selectionCount - deletedCount;
showFeedback(context, 'Failed to delete ${Intl.plural(count, one: '$count item', other: '$count items')}');
@ -113,14 +113,14 @@ class AlbumChipActionDelegate extends ChipActionDelegate with FeedbackMixin, Per
if (!await checkFreeSpaceForMove(context, selection, destinationAlbum, MoveType.move)) return;
final selectionCount = selection.length;
showOpReport<MoveOpEvent>(
context: context,
selection: selection,
opStream: ImageFileService.move(selection, copy: false, destinationAlbum: destinationAlbum),
itemCount: selectionCount,
onDone: (processed) async {
final movedOps = processed.where((e) => e.success);
final movedCount = movedOps.length;
final selectionCount = selection.length;
if (movedCount < selectionCount) {
final count = selectionCount - movedCount;
showFeedback(context, 'Failed to move ${Intl.plural(count, one: '$count item', other: '$count items')}');

View file

@ -54,7 +54,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
_showRenameDialog(context, entry);
break;
case EntryAction.print:
EntryPrinter(entry).print();
EntryPrinter(entry).print(context);
break;
case EntryAction.rotateCCW:
_rotate(context, entry, clockwise: false);
@ -181,14 +181,14 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
selection.add(entry);
}
final selectionCount = selection.length;
showOpReport<ExportOpEvent>(
context: context,
selection: selection,
opStream: ImageFileService.export(selection, destinationAlbum: destinationAlbum),
itemCount: selectionCount,
onDone: (processed) {
final movedOps = processed.where((e) => e.success);
final movedCount = movedOps.length;
final selectionCount = selection.length;
if (movedCount < selectionCount) {
final count = selectionCount - movedCount;
showFeedback(context, 'Failed to export ${Intl.plural(count, one: '$count page', other: '$count pages')}');

View file

@ -1,23 +1,26 @@
import 'dart:async';
import 'dart:convert';
import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_images.dart';
import 'package:aves/services/image_file_service.dart';
import 'package:aves/services/metadata_service.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:flutter/widgets.dart';
import 'package:pdf/widgets.dart' as pdf;
import 'package:pedantic/pedantic.dart';
import 'package:printing/printing.dart';
class EntryPrinter {
class EntryPrinter with FeedbackMixin {
final AvesEntry entry;
const EntryPrinter(this.entry);
EntryPrinter(this.entry);
Future<void> print() async {
Future<void> print(BuildContext context) async {
final documentName = entry.bestTitle ?? 'Aves';
final doc = pdf.Document(title: documentName);
final pages = await _buildPages();
final pages = await _buildPages(context);
if (pages.isNotEmpty) {
pages.forEach(doc.addPage); // Page
unawaited(Printing.layoutPdf(
@ -27,7 +30,7 @@ class EntryPrinter {
}
}
Future<List<pdf.Page>> _buildPages() async {
Future<List<pdf.Page>> _buildPages(BuildContext context) async {
final pages = <pdf.Page>[];
void _addPdfPage(pdf.Widget pdfChild) {
@ -47,10 +50,18 @@ class EntryPrinter {
if (entry.isMultipage) {
final multiPageInfo = await MetadataService.getMultiPageInfo(entry);
if (multiPageInfo.pageCount > 1) {
final streamController = StreamController<AvesEntry>.broadcast();
showOpReport<AvesEntry>(
context: context,
opStream: streamController.stream,
itemCount: multiPageInfo.pageCount,
);
for (final page in multiPageInfo.pages) {
final pageEntry = entry.getPageEntry(page);
_addPdfPage(await _buildPageImage(pageEntry));
streamController.sink.add(pageEntry);
}
await streamController.close();
}
}
if (pages.isEmpty) {