From a15769489757a96c319c5d33be43a7d7d56db7b3 Mon Sep 17 00:00:00 2001 From: SurendarSuri30 Date: Thu, 28 May 2026 18:30:32 +0530 Subject: [PATCH] ecard download --- lib/config/environment.dart | 2 +- .../helpers/ecard_download_service_io.dart | 57 +++++++++++++++++++ lib/pages/postEnrollment/home.dart | 1 + .../postEnrollment/service/api_service.dart | 15 +++++ lib/pages/service/TokenService.dart | 6 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 9 files changed, 86 insertions(+), 2 deletions(-) diff --git a/lib/config/environment.dart b/lib/config/environment.dart index 151210b..027fdeb 100755 --- a/lib/config/environment.dart +++ b/lib/config/environment.dart @@ -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; diff --git a/lib/pages/helpers/ecard_download_service_io.dart b/lib/pages/helpers/ecard_download_service_io.dart index 8b21711..f096295 100644 --- a/lib/pages/helpers/ecard_download_service_io.dart +++ b/lib/pages/helpers/ecard_download_service_io.dart @@ -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 downloadEcardImpl({ required String fileName, Map? 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 downloadEcardImpl({ } } +Future _downloadAndShareOnIos({ + required String url, + required String fileName, + Map? 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'; diff --git a/lib/pages/postEnrollment/home.dart b/lib/pages/postEnrollment/home.dart index 24919a2..6ef773c 100755 --- a/lib/pages/postEnrollment/home.dart +++ b/lib/pages/postEnrollment/home.dart @@ -104,6 +104,7 @@ class _HomeState extends State { void initState() { super.initState(); apiService = ApiService(context); // Initialize ApiService here + dataManager.init(apiService); if (session.client_id == null || session.client_id!.isEmpty || diff --git a/lib/pages/postEnrollment/service/api_service.dart b/lib/pages/postEnrollment/service/api_service.dart index 1a5bffb..36d6896 100755 --- a/lib/pages/postEnrollment/service/api_service.dart +++ b/lib/pages/postEnrollment/service/api_service.dart @@ -383,6 +383,11 @@ class ApiService { Future> _makeGetRequest( Uri url, Map 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> _makePostRequest( Uri url, Map body, Map 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> _makePostRequestWithoutFormData( Uri url, String body, Map 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'; diff --git a/lib/pages/service/TokenService.dart b/lib/pages/service/TokenService.dart index 85af6af..a2a5104 100755 --- a/lib/pages/service/TokenService.dart +++ b/lib/pages/service/TokenService.dart @@ -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 saveTokens({String? postToken, String? preToken}) async { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ac0f1f6..d5df3bb 100755 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -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")) diff --git a/pubspec.yaml b/pubspec.yaml index 26a7b01..d52a144 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 56d9857..8520ad9 100755 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include 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")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index dda9a85..072c051 100755 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST local_auth_windows permission_handler_windows printing + share_plus url_launcher_windows )