Enable profiles switch UI

This commit is contained in:
Janez T
2026-03-16 11:25:51 +01:00
parent fedd7b9a2b
commit 420690d683
30 changed files with 2858 additions and 269 deletions

View File

@@ -0,0 +1,56 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_sar_app/models/contact.dart';
import 'package:meshcore_sar_app/services/contact_storage_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
Contact createContact({required Uint8List key, required String name}) {
return Contact(
publicKey: key,
type: ContactType.chat,
flags: 0,
outPathLen: 0,
outPath: Uint8List(64),
advName: name,
lastAdvert: DateTime.now().millisecondsSinceEpoch ~/ 1000,
advLat: (46.0569 * 1e6).round(),
advLon: (14.5058 * 1e6).round(),
lastMod: DateTime.now().millisecondsSinceEpoch ~/ 1000,
);
}
setUp(() {
SharedPreferences.setMockInitialValues({});
});
test('keeps default and custom profile contact storage isolated', () async {
final storage = ContactStorageService();
final defaultContact = createContact(
key: Uint8List.fromList(List<int>.filled(32, 1)),
name: 'Default Contact',
);
final customContact = createContact(
key: Uint8List.fromList(List<int>.filled(32, 2)),
name: 'Custom Contact',
);
await storage.saveContacts([defaultContact]);
await storage.saveContacts([customContact], namespace: 'alpha');
final prefs = await SharedPreferences.getInstance();
expect(prefs.getString('stored_contacts'), isNotNull);
expect(prefs.getString('profile.alpha.stored_contacts'), isNotNull);
final defaultContacts = await storage.loadContacts();
final customContacts = await storage.loadContacts(namespace: 'alpha');
expect(defaultContacts.single.advName, defaultContact.advName);
expect(customContacts.single.advName, customContact.advName);
expect(defaultContacts.single.publicKey, defaultContact.publicKey);
expect(customContacts.single.publicKey, customContact.publicKey);
});
}

View File

@@ -112,4 +112,47 @@ void main() {
expect(restored, equals({'sar-1', 'sar-2'}));
});
test('keeps default and custom profile message storage isolated', () async {
final storage = MessageStorageService();
final defaultMessage = Message(
id: 'default-msg',
messageType: MessageType.channel,
senderPublicKeyPrefix: Uint8List.fromList([1, 1, 1, 1, 1, 1]),
channelIdx: 0,
pathLen: 0,
textType: MessageTextType.plain,
senderTimestamp: 1700001000,
text: 'Default profile',
receivedAt: DateTime.fromMillisecondsSinceEpoch(1700001000500),
isRead: true,
);
final customMessage = Message(
id: 'custom-msg',
messageType: MessageType.channel,
senderPublicKeyPrefix: Uint8List.fromList([2, 2, 2, 2, 2, 2]),
channelIdx: 1,
pathLen: 0,
textType: MessageTextType.plain,
senderTimestamp: 1700002000,
text: 'Custom profile',
receivedAt: DateTime.fromMillisecondsSinceEpoch(1700002000500),
isRead: false,
);
await storage.saveMessages([defaultMessage]);
await storage.saveMessages([customMessage], namespace: 'alpha');
final prefs = await SharedPreferences.getInstance();
expect(prefs.getString('stored_messages'), isNotNull);
expect(prefs.getString('profile.alpha.stored_messages'), isNotNull);
final defaultMessages = await storage.loadMessages();
final customMessages = await storage.loadMessages(namespace: 'alpha');
expect(defaultMessages.single.id, defaultMessage.id);
expect(defaultMessages.single.text, defaultMessage.text);
expect(customMessages.single.id, customMessage.id);
expect(customMessages.single.text, customMessage.text);
});
}

View File

@@ -0,0 +1,86 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_sar_app/models/config_profile.dart';
import 'package:meshcore_sar_app/services/profile_manager.dart';
import 'package:meshcore_sar_app/services/profiles_feature_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
SharedPreferences.setMockInitialValues({});
ProfileStorageScope.setScope(
profilesEnabled: false,
activeProfileId: ConfigProfile.defaultProfileId,
);
});
group('ProfileManager', () {
test(
'hides the built-in default profile until profiles are enabled',
() async {
final manager = ProfileManager();
await manager.initialize();
expect(manager.activeProfileId, ConfigProfile.defaultProfileId);
expect(manager.visibleProfiles, isEmpty);
expect(
manager.getProfile(ConfigProfile.defaultProfileId)?.isDefault,
isTrue,
);
expect(ProfileStorageScope.profilesEnabled, isFalse);
expect(ProfileStorageScope.effectiveNamespace, isNull);
await manager.setProfilesEnabled(true);
expect(manager.visibleProfiles, hasLength(1));
expect(
manager.visibleProfiles.single.id,
ConfigProfile.defaultProfileId,
);
expect(manager.visibleProfiles.single.name, 'Default');
expect(ProfileStorageScope.profilesEnabled, isTrue);
expect(ProfileStorageScope.effectiveNamespace, isNull);
},
);
test(
'persists custom profiles and restores scoped active profile state',
() async {
final manager = ProfileManager();
await manager.initialize();
await manager.setProfilesEnabled(true);
final profile = ConfigProfile(
id: 'profile-alpha',
name: 'Alpha',
createdAt: DateTime.parse('2026-03-16T12:00:00Z'),
updatedAt: DateTime.parse('2026-03-16T12:00:00Z'),
sections: const ConfigProfileSections(),
);
await manager.upsertProfile(profile);
await manager.setActiveProfileId(profile.id);
final reloaded = ProfileManager();
await reloaded.initialize();
expect(reloaded.profilesEnabled, isTrue);
expect(reloaded.activeProfileId, profile.id);
expect(reloaded.visibleProfiles.map((item) => item.id), [
ConfigProfile.defaultProfileId,
profile.id,
]);
expect(reloaded.getProfile(profile.id)?.name, profile.name);
expect(ProfileStorageScope.effectiveNamespace, profile.id);
await reloaded.setProfilesEnabled(false);
expect(ProfileStorageScope.profilesEnabled, isFalse);
expect(ProfileStorageScope.effectiveNamespace, isNull);
expect(reloaded.activeProfileId, profile.id);
},
);
});
}