-
Notifications
You must be signed in to change notification settings - Fork 0
/
present_signed_document_cubit.dart
143 lines (114 loc) · 4.63 KB
/
present_signed_document_cubit.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import 'dart:convert' show base64Decode;
import 'dart:io' show File, Platform;
import 'package:autogram_sign/autogram_sign.dart' show SignDocumentResponseBody;
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import '../app_service.dart';
import '../data/document_signing_type.dart';
import '../file_extensions.dart';
import '../file_system_entity_extensions.dart';
import '../ui/screens/present_signed_document_screen.dart';
import 'present_signed_document_state.dart';
export 'present_signed_document_state.dart';
/// Cubit for [PresentSignedDocumentScreen].
///
/// Allows saving document into public directory or getting [File] instance
/// which can be shared.
@injectable
class PresentSignedDocumentCubit extends Cubit<PresentSignedDocumentState> {
static final _log = Logger((PresentSignedDocumentCubit).toString());
static final _tsDateFormat = DateFormat('yyyyMMddHHmmss');
final AppService _appService;
final SignDocumentResponseBody signedDocument;
PresentSignedDocumentCubit({
required AppService appService,
@factoryParam required this.signedDocument,
@factoryParam required DocumentSigningType signingType,
}) : _appService = appService,
super(
signingType == DocumentSigningType.local
? const PresentSignedDocumentInitialState()
// Remote documents are not saved locally, so we go directly to success state
: const PresentSignedRemoteDocumentSuccessState(),
);
/// Saves [signedDocument] into public directory.
Future<void> saveDocument() async {
_log.info(
"Saving signed document: ${File(signedDocument.filename).redactedInfo}.");
emit(state.toLoading());
File? file;
try {
file = await _getTargetPath().then((path) => File(path));
// TODO Catch and still allow sharing
// Need to change PresentSignedDocumentSuccessState impl. to allow File?
await _saveDocumentIntoFile(file!);
_log.info("Signed Document was saved into ${file.redactedInfo}");
emit(state.toSuccess(file));
} catch (error, stackTrace) {
_log.severe("Error saving signed Document into ${file?.redactedInfo}.",
error, stackTrace);
emit(state.toError(error));
}
}
/// Gets the [File] for share action.
Future<File> getShareableFile() async {
final state = this.state;
if (state is PresentSignedLocalDocumentSuccessState) {
final file = state.file;
if (await file.exists()) {
return file;
}
}
final name = signedDocument.filename;
final directory = await getTemporaryDirectory();
final path = p.join(directory.path, name);
final file = File(path);
await _saveDocumentIntoFile(file);
return file;
}
/// Returns file path, where [signedDocument] content should be saved.
///
/// See also:
/// - [getTargetFileName]
Future<String> _getTargetPath() async {
final directory = await _appService.getDocumentsDirectory();
// Attempt to create Directory if not exists
if (!(await directory.exists()) && Platform.isAndroid) {
await directory.create(recursive: true);
}
final name = getTargetFileName(signedDocument.filename);
return p.join(directory.path, name);
}
/// Saves [signedDocument] content into given [file].
// TODO As extension function on SignDocumentResponseBody type
Future<void> _saveDocumentIntoFile(File file) {
return Future.microtask(() => base64Decode(signedDocument.content))
.then((bytes) => file.writeAsBytes(bytes, flush: true));
}
/// Gets the target file name.
/// Drops suffix and adds timestamp.
@visibleForTesting
static String getTargetFileName(
String name, [
// TODO This should get exact DateTime from previous cubit when it was actually signed
// SignDocumentCubit signingTime
ValueGetter<DateTime> clock = DateTime.now,
]) {
// NAME-signed-pades-baseline-b.pdf
// 1. Drop container type from name
final file = File(name);
final cleanName = file.basenameWithoutExtension
.replaceAll(RegExp('-signed-[cxp]ades-baseline-[bt]'), '-signed');
final ext = file.extension;
// 2. Get timestamp
final date = clock();
final ts = _tsDateFormat.format(date);
// 3. Final format
return "$cleanName-$ts$ext";
}
}