ecard download

This commit is contained in:
Surendiran 2026-05-28 18:30:32 +05:30
parent cbb2f9f33d
commit a157694897
9 changed files with 86 additions and 2 deletions

View File

@ -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;

View File

@ -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';

View File

@ -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 ||

View File

@ -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';

View File

@ -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 {

View File

@ -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"))

View File

@ -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:

View File

@ -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"));
}

View File

@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
local_auth_windows
permission_handler_windows
printing
share_plus
url_launcher_windows
)