diff --git a/lib/services/config/service.dart b/lib/services/config/service.dart index 4b1b3ef0..aa9fda6c 100644 --- a/lib/services/config/service.dart +++ b/lib/services/config/service.dart @@ -157,19 +157,29 @@ class ConfigService { _pref.setConfigs(response); - final configs = (response as List).map((e) => Config.fromJson(e)).toList(); + // The API returns an array of wrapper objects with the config nested in the 'json' field + final configs = (response as List).map((e) { + final configData = e['json'] as Map; + return Config.fromJson(configData); + }).toList(); return configs; } Future> getLocalConfigs() async { - final localConfigs = jsonDecode(await rootBundle.loadString( - 'assets/config/v$version/$communityConfigListFileName.json')); + try { + final localConfigs = jsonDecode(await rootBundle.loadString( + 'assets/config/v$version/$communityConfigListFileName.json')); - final configs = - (localConfigs as List).map((e) => Config.fromJson(e)).toList(); + final configs = + (localConfigs as List).map((e) => Config.fromJson(e)).toList(); - return configs; + return configs; + } catch (e, s) { + debugPrint('ERROR in getLocalConfigs: $e'); + debugPrintStack(stackTrace: s); + return []; + } } Future getRemoteConfig(String remoteConfigUrl) async { @@ -190,7 +200,19 @@ class ConfigService { final dynamic response = await remote.get(url: '?cachebuster=${generateCacheBusterValue()}'); - final config = Config.fromJson(response); + if (response == null) { + debugPrint('Empty response for remote config'); + return null; + } + + // The API returns a wrapper object with the config nested in the 'json' field + final configData = response['json'] as Map?; + if (configData == null) { + debugPrint('No json field in response for remote config'); + return null; + } + + final config = Config.fromJson(configData); return config; } catch (e, s) { @@ -214,8 +236,11 @@ class ConfigService { final List response = await _api.get(url: '/api/communities'); - final List communities = - response.map((item) => Config.fromJson(item)).toList(); + // The API returns an array of wrapper objects with the config nested in the 'json' field + final List communities = response.map((item) { + final configData = item['json'] as Map; + return Config.fromJson(configData); + }).toList(); return communities; } @@ -232,7 +257,17 @@ class ConfigService { try { final response = await _api.get(url: '/api/communities/$alias'); - return Config.fromJson(response); + if (response == null) { + debugPrint('Empty response for config: $alias'); + return null; + } + // The API returns a wrapper object with the config nested in the 'json' field + final configData = response['json'] as Map?; + if (configData == null) { + debugPrint('No json field in response for config: $alias'); + return null; + } + return Config.fromJson(configData); } catch (e, s) { debugPrint('Error fetching config for $alias: $e'); debugPrint('Stacktrace: $s'); diff --git a/lib/services/db/app/communities.dart b/lib/services/db/app/communities.dart index 46eefac1..7ed443a8 100644 --- a/lib/services/db/app/communities.dart +++ b/lib/services/db/app/communities.dart @@ -3,6 +3,7 @@ import 'package:citizenwallet/services/config/config.dart'; import 'package:citizenwallet/services/config/legacy.dart'; import 'package:citizenwallet/services/config/service.dart'; import 'package:citizenwallet/services/db/db.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:sqflite/sqflite.dart'; @@ -28,6 +29,50 @@ Future> legacyToV4(Database db, String name) async { return v4Configs; } +Future> V5Migration(Database db, String name) async { + try { + final ConfigService config = ConfigService(); + final localConfigs = await config.getLocalConfigs(); + + final List> maps = await db.query(name); + final existingCommunities = List.generate(maps.length, (i) { + return DBCommunity.fromMap(maps[i]); + }); + + final List updatedConfigs = []; + + for (final localConfig in localConfigs) { + final existingCommunity = existingCommunities.firstWhereOrNull( + (c) => c.alias == localConfig.community.alias, + ); + + if (existingCommunity != null) { + // Update existing community, preserve online status + final updatedCommunity = DBCommunity( + alias: localConfig.community.alias, + config: localConfig.toJson(), + hidden: localConfig.community.hidden, + version: localConfig.version, + online: existingCommunity.online, + ); + updatedConfigs.add(updatedCommunity); + } else { + // New community in v5 + updatedConfigs.add(DBCommunity.fromConfig(localConfig)); + } + } + + return updatedConfigs; + } catch (e, s) { + debugPrint('ERROR in V5Migration: $e'); + debugPrintStack(stackTrace: s); + + // Return existing data unchanged on error + final List> maps = await db.query(name); + return List.generate(maps.length, (i) => DBCommunity.fromMap(maps[i])); + } +} + class DBCommunity { final String alias; // index final bool hidden; @@ -118,6 +163,9 @@ class CommunityTable extends DBTable { 2: [ 'V4Migration', ], + 3: [ + 'V5Migration', + ], }; for (var i = oldVersion + 1; i <= newVersion; i++) { @@ -131,6 +179,11 @@ class CommunityTable extends DBTable { final updatedConfigs = await legacyToV4(db, name); await upsert(updatedConfigs); continue; + case 'V5Migration': + debugPrint('V5Migration'); + final updatedConfigs = await V5Migration(db, name); + await upsert(updatedConfigs); + continue; } await db.execute(query); @@ -144,26 +197,32 @@ class CommunityTable extends DBTable { } Future seed() async { - final localConfigs = await _config.getLocalConfigs(); + try { + // Check if the table is empty + final count = Sqflite.firstIntValue( + await db.rawQuery('SELECT COUNT(*) FROM $name')); - // Check if the table is empty - final count = - Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM $name')); - if (count != null && count > 0) { - return; // Table is not empty, skip seeding - } + if (count != null && count > 0) { + return; // Table is not empty, skip seeding + } - // Prepare batch operation for efficient insertion - final batch = db.batch(); + final localConfigs = await _config.getLocalConfigs(); - for (final config in localConfigs) { - batch.insert( - name, - DBCommunity.fromConfig(config).toMap(), - ); - } + // Prepare batch operation for efficient insertion + final batch = db.batch(); - await batch.commit(noResult: true); + for (final config in localConfigs) { + batch.insert( + name, + DBCommunity.fromConfig(config).toMap(), + ); + } + + await batch.commit(noResult: true); + } catch (e, s) { + print('Error seeding communities table: $e'); + print('Stack trace: $s'); + } } Future upsert(List communities) async { diff --git a/lib/services/db/app/db.dart b/lib/services/db/app/db.dart index f73c927d..65e25b60 100644 --- a/lib/services/db/app/db.dart +++ b/lib/services/db/app/db.dart @@ -27,7 +27,7 @@ class AppDBService extends DBService { await communities.migrate(db, oldVersion, newVersion); return; }, - version: 2, + version: 3, ); final db = await databaseFactory.openDatabase(