done all
This commit is contained in:
parent
9368cc9cc6
commit
a436c85ba6
@ -24,7 +24,7 @@ class ApiReceiptRepository implements ReceiptRepository {
|
||||
required String copyFor,
|
||||
}) async {
|
||||
final uri = _normalizeLocalhostForAndroid(
|
||||
Uri.parse('$baseUrl/transaction/pdf-html').replace(
|
||||
Uri.parse('$baseUrl/transaction/pdf').replace(
|
||||
queryParameters: <String, String>{
|
||||
'transactionId': transactionId,
|
||||
'copyFor': copyFor,
|
||||
@ -70,7 +70,7 @@ class ApiReceiptRepository implements ReceiptRepository {
|
||||
required String copyFor,
|
||||
}) async {
|
||||
final uri = _normalizeLocalhostForAndroid(
|
||||
Uri.parse('$baseUrl/transaction/pdf-html').replace(
|
||||
Uri.parse('$baseUrl/transaction/pdf').replace(
|
||||
queryParameters: <String, String>{
|
||||
'transactionId': transactionId,
|
||||
'copyFor': copyFor,
|
||||
|
||||
@ -2,7 +2,6 @@ import 'dart:io';
|
||||
|
||||
import 'package:e_receipt_mobile/core/config/app_config.dart';
|
||||
import 'package:e_receipt_mobile/data/repositories/api_receipt_repository.dart';
|
||||
import 'package:e_receipt_mobile/domain/entities/receipt_content.dart';
|
||||
import 'package:e_receipt_mobile/domain/repositories/receipt_repository.dart';
|
||||
import 'package:e_receipt_mobile/presentation/auth/session_controller.dart';
|
||||
import 'package:e_receipt_mobile/presentation/login/login_view_model.dart';
|
||||
@ -39,12 +38,6 @@ class ReceiptPdfViewData extends ReceiptViewData {
|
||||
final File file;
|
||||
}
|
||||
|
||||
class ReceiptHtmlViewData extends ReceiptViewData {
|
||||
const ReceiptHtmlViewData(this.html);
|
||||
|
||||
final String html;
|
||||
}
|
||||
|
||||
final receiptRepositoryProvider = Provider<ReceiptRepository>((ref) {
|
||||
return ApiReceiptRepository(
|
||||
baseUrl: AppConfig.apiBaseUrl,
|
||||
@ -64,27 +57,20 @@ final transactionReceiptViewDataProvider =
|
||||
throw Exception('No active session');
|
||||
}
|
||||
|
||||
final content = await ref
|
||||
final bytes = await ref
|
||||
.watch(receiptRepositoryProvider)
|
||||
.getTransactionReceipt(
|
||||
.getTransactionReceiptPdfBytes(
|
||||
token: sessionUser.token,
|
||||
transactionId: query.transactionId,
|
||||
copyFor: query.copyFor,
|
||||
);
|
||||
|
||||
switch (content) {
|
||||
case ReceiptPdfContent():
|
||||
final dir = await Directory.systemTemp.createTemp(
|
||||
'e_receipt_mobile',
|
||||
);
|
||||
final file = File(
|
||||
'${dir.path}/receipt_${query.transactionId}_${query.copyFor}.pdf',
|
||||
);
|
||||
await file.writeAsBytes(content.bytes, flush: true);
|
||||
return ReceiptPdfViewData(file);
|
||||
case ReceiptHtmlContent():
|
||||
return ReceiptHtmlViewData(content.html);
|
||||
}
|
||||
final dir = await Directory.systemTemp.createTemp('e_receipt_mobile');
|
||||
final file = File(
|
||||
'${dir.path}/receipt_${query.transactionId}_${query.copyFor}.pdf',
|
||||
);
|
||||
await file.writeAsBytes(bytes, flush: true);
|
||||
return ReceiptPdfViewData(file);
|
||||
} catch (e, st) {
|
||||
debugPrint('[RECEIPT][ERROR] ${Error.safeToString(e)}\n$st');
|
||||
throw Exception(Error.safeToString(e));
|
||||
|
||||
@ -439,7 +439,7 @@ class _TerminalNextScreenState extends ConsumerState<TerminalNextScreen> {
|
||||
_first(raw, const ['DE4']) ?? item.amount?.toString() ?? '-';
|
||||
final currency = _first(raw, const ['DE49']) ?? 'MMK';
|
||||
final status = _statusLabel(
|
||||
_first(raw, const ['description', 'DE39', 'status']) ??
|
||||
_first(raw, const ['DE39']) ??
|
||||
item.status,
|
||||
);
|
||||
final type = _first(raw, const ['DE3']) ?? item.type ?? '-';
|
||||
@ -605,16 +605,15 @@ class _TerminalNextScreenState extends ConsumerState<TerminalNextScreen> {
|
||||
}
|
||||
|
||||
String _statusLabel(String? status) {
|
||||
final normalized = (status ?? '').trim().toUpperCase();
|
||||
if (normalized == 'A' ||
|
||||
normalized == 'SUCCESS' ||
|
||||
normalized == 'PAY_SUCCESS') {
|
||||
final normalized = status?.trim().toUpperCase();
|
||||
if (normalized == 'A') {
|
||||
return 'SUCCESS';
|
||||
}
|
||||
if (normalized == 'E' || normalized == 'FAILED') {
|
||||
if (normalized == 'E' ) {
|
||||
return 'FAILED';
|
||||
}
|
||||
return normalized.isEmpty ? '-' : normalized;
|
||||
return 'SUCCESS';
|
||||
|
||||
}
|
||||
|
||||
String? _sanitizeMultiline(String? value) {
|
||||
|
||||
@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:e_receipt_mobile/domain/entities/receipt_content.dart';
|
||||
import 'package:e_receipt_mobile/domain/entities/terminal.dart';
|
||||
import 'package:e_receipt_mobile/domain/entities/transaction_record.dart';
|
||||
import 'package:e_receipt_mobile/presentation/auth/session_controller.dart';
|
||||
@ -13,7 +12,6 @@ import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class TransactionReceiptScreen extends ConsumerWidget {
|
||||
const TransactionReceiptScreen({
|
||||
@ -146,10 +144,6 @@ class TransactionReceiptScreen extends ConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
ReceiptHtmlViewData() => _ReceiptViewport(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 12),
|
||||
child: _ReceiptHtmlView(html: viewData.html),
|
||||
),
|
||||
},
|
||||
),
|
||||
SafeArea(
|
||||
@ -356,22 +350,13 @@ Future<Uint8List> _fetchReceiptPdfBytes({
|
||||
throw Exception('No active session');
|
||||
}
|
||||
|
||||
final content = await ref.read(receiptRepositoryProvider).getTransactionReceipt(
|
||||
return ref
|
||||
.read(receiptRepositoryProvider)
|
||||
.getTransactionReceiptPdfBytes(
|
||||
token: sessionUser.token,
|
||||
transactionId: transactionId,
|
||||
copyFor: copyFor,
|
||||
);
|
||||
|
||||
switch (content) {
|
||||
case ReceiptPdfContent():
|
||||
return content.bytes;
|
||||
case ReceiptHtmlContent():
|
||||
try {
|
||||
return await Printing.convertHtml(html: _wrapHtml(content.html));
|
||||
} catch (e) {
|
||||
throw Exception('Server did not return PDF, and HTML→PDF failed: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<T> _runWithProgress<T>(
|
||||
@ -416,146 +401,3 @@ Future<T> _runWithProgress<T>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ReceiptHtmlWebView extends StatefulWidget {
|
||||
const _ReceiptHtmlWebView({required this.html});
|
||||
|
||||
final String html;
|
||||
|
||||
@override
|
||||
State<_ReceiptHtmlWebView> createState() => _ReceiptHtmlWebViewState();
|
||||
}
|
||||
|
||||
class _ReceiptHtmlWebViewState extends State<_ReceiptHtmlWebView> {
|
||||
late final WebViewController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.disabled)
|
||||
..setBackgroundColor(Colors.transparent)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onNavigationRequest: (request) => NavigationDecision.prevent,
|
||||
),
|
||||
)
|
||||
..loadHtmlString(_wrapHtml(widget.html));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WebViewWidget(controller: _controller);
|
||||
}
|
||||
}
|
||||
|
||||
class _ReceiptHtmlView extends StatelessWidget {
|
||||
const _ReceiptHtmlView({required this.html});
|
||||
|
||||
final String html;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final platform = defaultTargetPlatform;
|
||||
final supportsWebView =
|
||||
platform == TargetPlatform.android || platform == TargetPlatform.iOS;
|
||||
if (!supportsWebView) {
|
||||
return _ReceiptHtmlFallbackText(html: html);
|
||||
}
|
||||
|
||||
return _ReceiptHtmlWebView(html: html);
|
||||
}
|
||||
}
|
||||
|
||||
class _ReceiptHtmlFallbackText extends StatelessWidget {
|
||||
const _ReceiptHtmlFallbackText({required this.html});
|
||||
|
||||
final String html;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final plainText = _htmlToPlainText(html);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: SelectionArea(
|
||||
child: DefaultTextStyle(
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontFamily: 'monospace',
|
||||
height: 1.35,
|
||||
) ??
|
||||
const TextStyle(fontFamily: 'monospace', height: 1.35),
|
||||
child: Text(plainText),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _wrapHtml(String html) {
|
||||
// Keep backend HTML intact but ensure a sane viewport and remove default
|
||||
// margins so it looks like the web "receipt iframe" container.
|
||||
return '''
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; background: transparent; }
|
||||
body { -webkit-text-size-adjust: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>$html</body>
|
||||
</html>
|
||||
''';
|
||||
}
|
||||
|
||||
String _htmlToPlainText(String html) {
|
||||
var s = html;
|
||||
|
||||
s = s.replaceAll(
|
||||
RegExp(
|
||||
r'<(script|style)[^>]*>.*?</\1>',
|
||||
caseSensitive: false,
|
||||
dotAll: true,
|
||||
),
|
||||
'',
|
||||
);
|
||||
s = s.replaceAll(RegExp(r'<br\s*/?>', caseSensitive: false), '\n');
|
||||
s = s.replaceAll(
|
||||
RegExp(
|
||||
r'</(div|p|tr|pre|table|section|header|footer)>',
|
||||
caseSensitive: false,
|
||||
),
|
||||
'\n',
|
||||
);
|
||||
s = s.replaceAll(RegExp(r'<li[^>]*>', caseSensitive: false), '• ');
|
||||
s = s.replaceAll(RegExp(r'</li>', caseSensitive: false), '\n');
|
||||
|
||||
s = s.replaceAll(RegExp(r'<[^>]+>'), '');
|
||||
|
||||
s = s.replaceAll(' ', ' ');
|
||||
s = s.replaceAll('&', '&');
|
||||
s = s.replaceAll('<', '<');
|
||||
s = s.replaceAll('>', '>');
|
||||
s = s.replaceAll('"', '"');
|
||||
s = s.replaceAll(''', "'");
|
||||
|
||||
s = s.replaceAllMapped(RegExp(r'&#(\d+);'), (m) {
|
||||
final code = int.tryParse(m.group(1) ?? '');
|
||||
if (code == null) return '';
|
||||
return String.fromCharCode(code);
|
||||
});
|
||||
s = s.replaceAllMapped(RegExp(r'&#x([0-9a-fA-F]+);'), (m) {
|
||||
final code = int.tryParse(m.group(1) ?? '', radix: 16);
|
||||
if (code == null) return '';
|
||||
return String.fromCharCode(code);
|
||||
});
|
||||
|
||||
s = s.replaceAll(RegExp(r'[ \t]+\n'), '\n');
|
||||
s = s.replaceAll(RegExp(r'\n{3,}'), '\n\n');
|
||||
|
||||
return s.trim();
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:e_receipt_mobile/domain/entities/terminal.dart';
|
||||
@ -57,6 +59,7 @@ class TerminalListView extends StatelessWidget {
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 10),
|
||||
itemBuilder: (context, index) {
|
||||
final terminal = terminals[index];
|
||||
print('this is terminal ${terminal.status}');
|
||||
final enabled = terminal.tid != null || terminal.id != null;
|
||||
final title =
|
||||
(_sanitizeMultiline(terminal.name)?.isNotEmpty ?? false)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user