ecard download
This commit is contained in:
parent
cbb2f9f33d
commit
a157694897
@ -5,7 +5,7 @@ class Environment {
|
||||
static Flavor flavor = Flavor.dev; // overwritten by each main_*.dart
|
||||
|
||||
/// Chatbot FAB + window. Set to `true` when ready to enable.
|
||||
static const bool chatbotEnabled = false;
|
||||
static const bool chatbotEnabled = true;
|
||||
|
||||
static bool get isProd => flavor == Flavor.prod || flavor == Flavor.prod1;
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import 'download_result.dart';
|
||||
|
||||
@ -32,6 +33,14 @@ Future<DownloadResult> downloadEcardImpl({
|
||||
required String fileName,
|
||||
Map<String, String>? headers,
|
||||
}) async {
|
||||
if (Platform.isIOS) {
|
||||
return _downloadAndShareOnIos(
|
||||
url: url,
|
||||
fileName: fileName,
|
||||
headers: headers,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final hasPermission = await ensureStorageAccessImpl();
|
||||
if (!hasPermission) {
|
||||
@ -89,6 +98,54 @@ Future<DownloadResult> downloadEcardImpl({
|
||||
}
|
||||
}
|
||||
|
||||
Future<DownloadResult> _downloadAndShareOnIos({
|
||||
required String url,
|
||||
required String fileName,
|
||||
Map<String, String>? headers,
|
||||
}) async {
|
||||
try {
|
||||
final safeFileName = _sanitizeFileName(fileName);
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final filePath = '${tempDir.path}/$safeFileName';
|
||||
final file = File(filePath);
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
|
||||
await _dio.download(
|
||||
url,
|
||||
filePath,
|
||||
options: Options(
|
||||
headers: headers,
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
deleteOnError: true,
|
||||
);
|
||||
|
||||
if (!await file.exists() || await file.length() == 0) {
|
||||
return DownloadResult.failure('Download failed');
|
||||
}
|
||||
|
||||
await Share.shareXFiles(
|
||||
[XFile(filePath)],
|
||||
text: 'Save eCard to Files',
|
||||
subject: safeFileName,
|
||||
);
|
||||
|
||||
return DownloadResult.success(
|
||||
message: 'Choose "Save to Files" to store eCard',
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
final hasStatusCode = e.response?.statusCode != null;
|
||||
final message = hasStatusCode
|
||||
? 'Download failed (${e.response!.statusCode})'
|
||||
: 'Download failed';
|
||||
return DownloadResult.failure(message);
|
||||
} catch (_) {
|
||||
return DownloadResult.failure('Download failed');
|
||||
}
|
||||
}
|
||||
|
||||
String _sanitizeFileName(String fileName) {
|
||||
final replaced = fileName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_').trim();
|
||||
if (replaced.isEmpty) return 'ecard.pdf';
|
||||
|
||||
@ -104,6 +104,7 @@ class _HomeState extends State<Home> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService(context); // Initialize ApiService here
|
||||
dataManager.init(apiService);
|
||||
|
||||
|
||||
if (session.client_id == null || session.client_id!.isEmpty ||
|
||||
|
||||
@ -383,6 +383,11 @@ class ApiService {
|
||||
|
||||
Future<Map<String, dynamic>> _makeGetRequest(
|
||||
Uri url, Map<String, String> headers) async {
|
||||
// Always refresh token at request time to avoid using stale login tokens.
|
||||
if (headers.containsKey('Authorization')) {
|
||||
_postToken = await TokenService.getPostToken();
|
||||
headers['Authorization'] = 'Bearer ${_postToken ?? ''}';
|
||||
}
|
||||
// Add global APP_SIGNATURE header
|
||||
headers['APP-SIGNATURE'] =
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
|
||||
@ -392,6 +397,11 @@ class ApiService {
|
||||
|
||||
Future<Map<String, dynamic>> _makePostRequest(
|
||||
Uri url, Map<String, String> body, Map<String, String> headers) async {
|
||||
// Always refresh token at request time to avoid using stale login tokens.
|
||||
if (headers.containsKey('Authorization')) {
|
||||
_postToken = await TokenService.getPostToken();
|
||||
headers['Authorization'] = 'Bearer ${_postToken ?? ''}';
|
||||
}
|
||||
// Add global APP_SIGNATURE header
|
||||
headers['APP-SIGNATURE'] =
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
|
||||
@ -401,6 +411,11 @@ class ApiService {
|
||||
|
||||
Future<Map<String, dynamic>> _makePostRequestWithoutFormData(
|
||||
Uri url, String body, Map<String, String> headers) async {
|
||||
// Always refresh token at request time to avoid using stale login tokens.
|
||||
if (headers.containsKey('Authorization')) {
|
||||
_postToken = await TokenService.getPostToken();
|
||||
headers['Authorization'] = 'Bearer ${_postToken ?? ''}';
|
||||
}
|
||||
// Add global APP_SIGNATURE header
|
||||
headers['APP-SIGNATURE'] =
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
|
||||
|
||||
@ -18,7 +18,11 @@ class TokenService {
|
||||
static const postTokenKey = 'post_token';
|
||||
static const preTokenKey = 'pre_token';
|
||||
|
||||
static final _secureStorage = FlutterSecureStorage();
|
||||
static const _secureStorage = FlutterSecureStorage(
|
||||
iOptions: IOSOptions(
|
||||
accessibility: KeychainAccessibility.first_unlock_this_device,
|
||||
),
|
||||
);
|
||||
|
||||
/// Web: tokens live only in [sessionStorage] (tab-scoped). Mobile: secure storage.
|
||||
static Future<void> saveTokens({String? postToken, String? preToken}) async {
|
||||
|
||||
@ -15,6 +15,7 @@ import local_auth_darwin
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import printing
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sqflite_darwin
|
||||
import url_launcher_macos
|
||||
@ -32,6 +33,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
|
||||
@ -79,6 +79,7 @@ dependencies:
|
||||
open_filex: ^4.7.0
|
||||
flutter_local_notifications: ^19.4.2
|
||||
permission_handler: ^12.0.1
|
||||
share_plus: ^11.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <local_auth_windows/local_auth_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <printing/printing_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
@ -27,6 +28,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
PrintingPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PrintingPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
local_auth_windows
|
||||
permission_handler_windows
|
||||
printing
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user