didvan-app/lib/views/pdf_viewer/pdf_viewer_page.dart

224 lines
7.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import 'package:flutter/foundation.dart';
import 'package:didvan/services/network/request.dart';
import 'package:universal_html/html.dart' as html;
class PdfViewerPage extends StatefulWidget {
final String pdfUrl;
final String title;
const PdfViewerPage({
super.key,
required this.pdfUrl,
required this.title,
});
@override
State<PdfViewerPage> createState() => _PdfViewerPageState();
}
class _PdfViewerPageState extends State<PdfViewerPage> {
final PdfViewerController _pdfViewerController = PdfViewerController();
bool _isLoading = true;
String? _errorMessage;
late String _fullPdfUrl;
@override
void initState() {
super.initState();
// اگر URL کامل (http/https) بود همان را استفاده کن؛ در غیر این صورت
// به دامنه‌ی legacy می‌چسبد. URLهای جدید content-service از s3-service
// به‌صورت presigned کامل می‌آیند.
_fullPdfUrl = widget.pdfUrl.startsWith('http')
? widget.pdfUrl
: 'https://api.didvan.app${widget.pdfUrl}';
if (kDebugMode) {
print('Original PDF URL: ${widget.pdfUrl}');
print('Full PDF URL : $_fullPdfUrl');
}
// برای وب، PDF را مستقیماً در مرورگر باز می‌کند
if (kIsWeb) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_openPdfInBrowser();
});
}
}
void _openPdfInBrowser() {
final token = RequestService.token;
final pdfUrlWithAuth =
'$_fullPdfUrl${_fullPdfUrl.contains('?') ? '&' : '?'}token=$token';
// روش سازگار با تمام مرورگرها شامل Safari
try {
final anchor = html.AnchorElement(href: pdfUrlWithAuth)
..setAttribute('target', '_blank')
..setAttribute('rel', 'noopener');
html.document.body?.append(anchor);
anchor.click();
anchor.remove();
if (kDebugMode) {
print('PDF opened successfully in new tab');
}
} catch (e) {
if (kDebugMode) {
print('Error opening PDF: $e');
}
}
Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
Navigator.of(context).pop();
}
});
}
@override
Widget build(BuildContext context) {
// برای وب، صفحه‌ای نشان می‌دهد که PDF در تب جدیدی باز می‌شود
if (kIsWeb) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: DidvanText(
widget.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
elevation: 0,
backgroundColor: Theme.of(context).colorScheme.surface,
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
DidvanText(
'درحال باز کردن PDF در مرورگر...',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
// برای پلتفرم‌های دیگر، از SfPdfViewer استفاده کن
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: DidvanText(
widget.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
elevation: 0,
backgroundColor: Theme.of(context).colorScheme.surface,
),
body: Stack(
children: [
SfPdfViewer.network(
_fullPdfUrl,
controller: _pdfViewerController,
enableDoubleTapZooming: true,
enableTextSelection: true,
canShowScrollHead: true,
canShowScrollStatus: true,
pageLayoutMode: PdfPageLayoutMode.continuous,
// فقط برای دامنه‌ی خودی توکن می‌فرستیم؛ presigned S3 با هدر
// Authorization پاسخ 403 می‌دهد.
headers: _fullPdfUrl.contains('didvan.com') ||
_fullPdfUrl.contains('didvan.app')
? {'Authorization': 'Bearer ${RequestService.token}'}
: const <String, String>{},
onDocumentLoaded: (PdfDocumentLoadedDetails details) {
setState(() {
_isLoading = false;
});
if (kDebugMode) {
print(
'PDF loaded successfully with ${details.document.pages.count} pages');
}
},
onDocumentLoadFailed: (PdfDocumentLoadFailedDetails details) {
setState(() {
_isLoading = false;
_errorMessage = details.error;
});
if (kDebugMode) {
print('PDF load failed: ${details.error}');
print('Description: ${details.description}');
}
},
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
),
if (_errorMessage != null)
Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
size: 64,
color: Colors.red,
),
const SizedBox(height: 16),
const DidvanText(
'خطا در بارگذاری PDF',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
DidvanText(
_errorMessage!,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
setState(() {
_isLoading = true;
_errorMessage = null;
});
},
child: const DidvanText('تلاش مجدد'),
),
],
),
),
),
],
),
);
}
@override
void dispose() {
_pdfViewerController.dispose();
super.dispose();
}
}