mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 12:04:08 +00:00
Fix Flutter iOS build and add web support with web-safe storage (#878)
* Add Flutter web support and web-safe storage * Update mobile/web/index.html Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> * Product name instead of placeholder --------- Signed-off-by: Juan José Mata <jjmata@jjmata.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -5,7 +5,7 @@ class ApiConfig {
|
||||
// For local development, use: http://10.0.2.2:3000 (Android emulator)
|
||||
// For iOS simulator, use: http://localhost:3000
|
||||
// For production, use your actual server URL
|
||||
static String _baseUrl = 'http://10.0.2.2:3000';
|
||||
static String _baseUrl = 'https://app.sure.am';
|
||||
|
||||
static String get baseUrl => _baseUrl;
|
||||
|
||||
|
||||
@@ -6,11 +6,37 @@ import 'log_service.dart';
|
||||
class DatabaseHelper {
|
||||
static final DatabaseHelper instance = DatabaseHelper._init();
|
||||
static Database? _database;
|
||||
static final Map<String, Map<String, dynamic>> _memoryTransactions = {};
|
||||
static final Map<String, Map<String, dynamic>> _memoryAccounts = {};
|
||||
static bool _webStorageLogged = false;
|
||||
final LogService _log = LogService.instance;
|
||||
|
||||
DatabaseHelper._init();
|
||||
|
||||
bool get _useInMemoryStore => kIsWeb;
|
||||
|
||||
void _ensureWebStoreReady() {
|
||||
if (!_useInMemoryStore || _webStorageLogged) return;
|
||||
_webStorageLogged = true;
|
||||
_log.info(
|
||||
'DatabaseHelper',
|
||||
'Using in-memory storage on web (sqflite is not supported in browser builds).',
|
||||
);
|
||||
}
|
||||
|
||||
int _compareDesc(String? left, String? right) {
|
||||
return (right ?? '').compareTo(left ?? '');
|
||||
}
|
||||
|
||||
int _compareAsc(String? left, String? right) {
|
||||
return (left ?? '').compareTo(right ?? '');
|
||||
}
|
||||
|
||||
Future<Database> get database async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
throw StateError('sqflite database is not available on web.');
|
||||
}
|
||||
if (_database != null) return _database!;
|
||||
|
||||
try {
|
||||
@@ -124,6 +150,12 @@ class DatabaseHelper {
|
||||
|
||||
// Transaction CRUD operations
|
||||
Future<String> insertTransaction(Map<String, dynamic> transaction) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final localId = transaction['local_id'] as String;
|
||||
_memoryTransactions[localId] = Map<String, dynamic>.from(transaction);
|
||||
return localId;
|
||||
}
|
||||
final db = await database;
|
||||
_log.debug('DatabaseHelper', 'Inserting transaction: local_id=${transaction['local_id']}, account_id="${transaction['account_id']}", server_id=${transaction['server_id']}');
|
||||
await db.insert(
|
||||
@@ -136,6 +168,22 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getTransactions({String? accountId}) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final results = _memoryTransactions.values
|
||||
.where((transaction) {
|
||||
final storedAccountId = transaction['account_id'] as String?;
|
||||
return accountId == null || storedAccountId == accountId;
|
||||
})
|
||||
.map((transaction) => Map<String, dynamic>.from(transaction))
|
||||
.toList();
|
||||
results.sort((a, b) {
|
||||
final dateCompare = _compareDesc(a['date'] as String?, b['date'] as String?);
|
||||
if (dateCompare != 0) return dateCompare;
|
||||
return _compareDesc(a['created_at'] as String?, b['created_at'] as String?);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
final db = await database;
|
||||
|
||||
if (accountId != null) {
|
||||
@@ -160,6 +208,11 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getTransactionByLocalId(String localId) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final transaction = _memoryTransactions[localId];
|
||||
return transaction != null ? Map<String, dynamic>.from(transaction) : null;
|
||||
}
|
||||
final db = await database;
|
||||
final results = await db.query(
|
||||
'transactions',
|
||||
@@ -172,6 +225,15 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getTransactionByServerId(String serverId) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
for (final transaction in _memoryTransactions.values) {
|
||||
if (transaction['server_id'] == serverId) {
|
||||
return Map<String, dynamic>.from(transaction);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
final db = await database;
|
||||
final results = await db.query(
|
||||
'transactions',
|
||||
@@ -184,6 +246,17 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getPendingTransactions() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final results = _memoryTransactions.values
|
||||
.where((transaction) => transaction['sync_status'] == 'pending')
|
||||
.map((transaction) => Map<String, dynamic>.from(transaction))
|
||||
.toList();
|
||||
results.sort(
|
||||
(a, b) => _compareAsc(a['created_at'] as String?, b['created_at'] as String?),
|
||||
);
|
||||
return results;
|
||||
}
|
||||
final db = await database;
|
||||
return await db.query(
|
||||
'transactions',
|
||||
@@ -194,6 +267,17 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getPendingDeletes() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final results = _memoryTransactions.values
|
||||
.where((transaction) => transaction['sync_status'] == 'pending_delete')
|
||||
.map((transaction) => Map<String, dynamic>.from(transaction))
|
||||
.toList();
|
||||
results.sort(
|
||||
(a, b) => _compareAsc(a['updated_at'] as String?, b['updated_at'] as String?),
|
||||
);
|
||||
return results;
|
||||
}
|
||||
final db = await database;
|
||||
return await db.query(
|
||||
'transactions',
|
||||
@@ -204,6 +288,16 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<int> updateTransaction(String localId, Map<String, dynamic> transaction) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
if (!_memoryTransactions.containsKey(localId)) {
|
||||
return 0;
|
||||
}
|
||||
final updated = Map<String, dynamic>.from(transaction);
|
||||
updated['local_id'] = localId;
|
||||
_memoryTransactions[localId] = updated;
|
||||
return 1;
|
||||
}
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'transactions',
|
||||
@@ -214,6 +308,10 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<int> deleteTransaction(String localId) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
return _memoryTransactions.remove(localId) != null ? 1 : 0;
|
||||
}
|
||||
final db = await database;
|
||||
return await db.delete(
|
||||
'transactions',
|
||||
@@ -223,6 +321,19 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<int> deleteTransactionByServerId(String serverId) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
String? localIdToRemove;
|
||||
for (final entry in _memoryTransactions.entries) {
|
||||
if (entry.value['server_id'] == serverId) {
|
||||
localIdToRemove = entry.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (localIdToRemove == null) return 0;
|
||||
_memoryTransactions.remove(localIdToRemove);
|
||||
return 1;
|
||||
}
|
||||
final db = await database;
|
||||
return await db.delete(
|
||||
'transactions',
|
||||
@@ -232,11 +343,27 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<void> clearTransactions() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
_memoryTransactions.clear();
|
||||
return;
|
||||
}
|
||||
final db = await database;
|
||||
await db.delete('transactions');
|
||||
}
|
||||
|
||||
Future<void> clearSyncedTransactions() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final keysToRemove = _memoryTransactions.entries
|
||||
.where((entry) => entry.value['sync_status'] == 'synced')
|
||||
.map((entry) => entry.key)
|
||||
.toList();
|
||||
for (final key in keysToRemove) {
|
||||
_memoryTransactions.remove(key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final db = await database;
|
||||
_log.debug('DatabaseHelper', 'Clearing only synced transactions, keeping pending/failed');
|
||||
await db.delete(
|
||||
@@ -248,6 +375,12 @@ class DatabaseHelper {
|
||||
|
||||
// Account CRUD operations (for caching)
|
||||
Future<void> insertAccount(Map<String, dynamic> account) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final id = account['id'] as String;
|
||||
_memoryAccounts[id] = Map<String, dynamic>.from(account);
|
||||
return;
|
||||
}
|
||||
final db = await database;
|
||||
await db.insert(
|
||||
'accounts',
|
||||
@@ -257,6 +390,14 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<void> insertAccounts(List<Map<String, dynamic>> accounts) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
for (final account in accounts) {
|
||||
final id = account['id'] as String;
|
||||
_memoryAccounts[id] = Map<String, dynamic>.from(account);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final db = await database;
|
||||
final batch = db.batch();
|
||||
|
||||
@@ -272,11 +413,26 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getAccounts() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final results = _memoryAccounts.values
|
||||
.map((account) => Map<String, dynamic>.from(account))
|
||||
.toList();
|
||||
results.sort(
|
||||
(a, b) => _compareAsc(a['name'] as String?, b['name'] as String?),
|
||||
);
|
||||
return results;
|
||||
}
|
||||
final db = await database;
|
||||
return await db.query('accounts', orderBy: 'name ASC');
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getAccountById(String id) async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
final account = _memoryAccounts[id];
|
||||
return account != null ? Map<String, dynamic>.from(account) : null;
|
||||
}
|
||||
final db = await database;
|
||||
final results = await db.query(
|
||||
'accounts',
|
||||
@@ -289,18 +445,35 @@ class DatabaseHelper {
|
||||
}
|
||||
|
||||
Future<void> clearAccounts() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
_memoryAccounts.clear();
|
||||
return;
|
||||
}
|
||||
final db = await database;
|
||||
await db.delete('accounts');
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
Future<void> clearAllData() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
_memoryTransactions.clear();
|
||||
_memoryAccounts.clear();
|
||||
return;
|
||||
}
|
||||
final db = await database;
|
||||
await db.delete('transactions');
|
||||
await db.delete('accounts');
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
if (_useInMemoryStore) {
|
||||
_ensureWebStoreReady();
|
||||
_memoryTransactions.clear();
|
||||
_memoryAccounts.clear();
|
||||
return;
|
||||
}
|
||||
if (_database != null) {
|
||||
await _database!.close();
|
||||
_database = null;
|
||||
|
||||
Reference in New Issue
Block a user