This commit is contained in:
Christien Rioux 2024-07-03 20:59:54 -04:00
parent 8c89ce91cf
commit 9dfb8c3f71
16 changed files with 305 additions and 162 deletions

View File

@ -37,11 +37,17 @@
"pronouns": "Pronouns", "pronouns": "Pronouns",
"remove_account": "Remove Account", "remove_account": "Remove Account",
"delete_identity": "Delete Identity", "delete_identity": "Delete Identity",
"remove_account_confirm": "Confirm Account Removal?", "remove_account_confirm": "Confirm Account Removal",
"remove_account_description": "Remove account from this device only", "remove_account_description": "Remove account from this device only",
"delete_identity_description": "Delete identity and all messages completely", "remove_account_confirm_message": " • Your account will be removed from this device ONLY\n • Your identity will remain recoverable with the recovery key\n • Your messages and contacts will remain available on other devices\n",
"delete_identity_confirm_message": "This action is PERMANENT, and your identity will no longer be recoverable with the recovery key. This will not remove your messages you have sent from other people's devices.", "delete_identity_description": "Delete identity from all devices everywhere",
"confirm_are_you_sure": "Are you sure you want to do this?" "delete_identity_confirm_message": "This action is PERMANENT, and your identity will no longer be recoverable with the recovery key. Restoring from backups will not recover your account!",
"delete_identity_confirm_message_details": "You will lose access to:\n • Your entire message history\n • Your contacts\n • This will not remove your messages you have sent from other people's devices\n",
"confirm_are_you_sure": "Are you sure you want to do this?",
"failed_to_remove": "Failed to remove account.\n\nTry again when you have a more stable network connection.",
"failed_to_delete": "Failed to delete identity.\n\nTry again when you have a more stable network connection.",
"account_removed": "Account removed successfully",
"identity_deleted": "Identity deleted successfully"
}, },
"show_recovery_key_page": { "show_recovery_key_page": {
"titlebar": "Save Recovery Key", "titlebar": "Save Recovery Key",
@ -58,6 +64,8 @@
"accept": "Accept", "accept": "Accept",
"reject": "Reject", "reject": "Reject",
"finish": "Finish", "finish": "Finish",
"yes_proceed": "Yes, proceed",
"no_cancel": "No, cancel",
"waiting_for_network": "Waiting For Network" "waiting_for_network": "Waiting For Network"
}, },
"toast": { "toast": {

View File

@ -168,7 +168,15 @@ class AccountRepository {
} }
/// Remove an account and wipe the messages for this account from this device /// Remove an account and wipe the messages for this account from this device
Future<bool> deleteLocalAccount(TypedKey superIdentityRecordKey) async { Future<bool> deleteLocalAccount(TypedKey superIdentityRecordKey,
OwnedDHTRecordPointer? accountRecord) async {
// Delete the account record locally which causes a deep delete
// of all the contacts, invites, chats, and messages in the dht record
// pool
if (accountRecord != null) {
await DHTRecordPool.instance.deleteRecord(accountRecord.recordKey);
}
await logout(superIdentityRecordKey); await logout(superIdentityRecordKey);
final localAccounts = await _localAccounts.get(); final localAccounts = await _localAccounts.get();
@ -178,8 +186,6 @@ class AccountRepository {
await _localAccounts.set(newLocalAccounts); await _localAccounts.set(newLocalAccounts);
_streamController.add(AccountRepositoryChange.localAccounts); _streamController.add(AccountRepositoryChange.localAccounts);
// TO DO: wipe messages
return true; return true;
} }
@ -367,6 +373,11 @@ class AccountRepository {
return; return;
} }
if (logoutUser == activeLocalAccount) {
await switchToAccount(
_localAccounts.value.firstOrNull?.superIdentity.recordKey);
}
final logoutUserLogin = fetchUserLogin(logoutUser); final logoutUserLogin = fetchUserLogin(logoutUser);
if (logoutUserLogin == null) { if (logoutUserLogin == null) {
// Already logged out // Already logged out

View File

@ -20,6 +20,7 @@ class EditAccountPage extends StatefulWidget {
const EditAccountPage( const EditAccountPage(
{required this.superIdentityRecordKey, {required this.superIdentityRecordKey,
required this.existingProfile, required this.existingProfile,
required this.accountRecord,
super.key}); super.key});
@override @override
@ -27,6 +28,7 @@ class EditAccountPage extends StatefulWidget {
final TypedKey superIdentityRecordKey; final TypedKey superIdentityRecordKey;
final proto.Profile existingProfile; final proto.Profile existingProfile;
final OwnedDHTRecordPointer accountRecord;
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties); super.debugFillProperties(properties);
@ -34,7 +36,9 @@ class EditAccountPage extends StatefulWidget {
..add(DiagnosticsProperty<TypedKey>( ..add(DiagnosticsProperty<TypedKey>(
'superIdentityRecordKey', superIdentityRecordKey)) 'superIdentityRecordKey', superIdentityRecordKey))
..add(DiagnosticsProperty<proto.Profile>( ..add(DiagnosticsProperty<proto.Profile>(
'existingProfile', existingProfile)); 'existingProfile', existingProfile))
..add(DiagnosticsProperty<OwnedDHTRecordPointer>(
'accountRecord', accountRecord));
} }
} }
@ -67,92 +71,179 @@ class _EditAccountPageState extends State<EditAccountPage> {
}, },
); );
Future<void> _onRemoveAccount() async {
final confirmed = await StyledDialog.show<bool>(
context: context,
title: translate('edit_account_page.remove_account_confirm'),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Text(translate('edit_account_page.remove_account_confirm_message'))
.paddingLTRB(24, 24, 24, 0),
Text(translate('edit_account_page.confirm_are_you_sure'))
.paddingAll(8),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Row(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0),
Text(translate('button.no_cancel')).paddingLTRB(0, 0, 4, 0)
])),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: Row(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
Text(translate('button.yes_proceed')).paddingLTRB(0, 0, 4, 0)
]))
]).paddingAll(24)
]));
if (confirmed != null && confirmed && mounted) {
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus();
try {
setState(() {
_isInAsyncCall = true;
});
try {
final success = await AccountRepository.instance.deleteLocalAccount(
widget.superIdentityRecordKey, widget.accountRecord);
if (success && mounted) {
showInfoToast(
context, translate('edit_account_page.account_removed'));
GoRouterHelper(context).pop();
} else if (mounted) {
showErrorToast(
context, translate('edit_account_page.failed_to_remove'));
}
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (mounted) {
await showErrorModal(
context, translate('new_account_page.error'), 'Exception: $e');
}
}
}
}
Future<void> _onDeleteIdentity() async {
//
}
Future<void> _onSubmit(GlobalKey<FormBuilderState> formKey) async {
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus();
try {
final name = formKey
.currentState!.fields[EditProfileForm.formFieldName]!.value as String;
final pronouns = formKey.currentState!
.fields[EditProfileForm.formFieldPronouns]!.value as String? ??
'';
final newProfile = widget.existingProfile.deepCopy()
..name = name
..pronouns = pronouns
..timestamp = Veilid.instance.now().toInt64();
setState(() {
_isInAsyncCall = true;
});
try {
// Look up account cubit for this specific account
final perAccountCollectionBlocMapCubit =
context.read<PerAccountCollectionBlocMapCubit>();
final accountRecordCubit = await perAccountCollectionBlocMapCubit
.operate(widget.superIdentityRecordKey,
closure: (c) async => c.accountRecordCubit);
if (accountRecordCubit == null) {
return;
}
// Update account profile DHT record
// This triggers ConversationCubits to update
await accountRecordCubit.updateProfile(newProfile);
// Update local account profile
await AccountRepository.instance
.editAccountProfile(widget.superIdentityRecordKey, newProfile);
if (mounted) {
Navigator.canPop(context)
? GoRouterHelper(context).pop()
: GoRouterHelper(context).go('/');
}
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (mounted) {
await showErrorModal(
context, translate('edit_account_page.error'), 'Exception: $e');
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final displayModalHUD = _isInAsyncCall; final displayModalHUD = _isInAsyncCall;
return Scaffold( return Scaffold(
// resizeToAvoidBottomInset: false, // resizeToAvoidBottomInset: false,
appBar: DefaultAppBar( appBar: DefaultAppBar(
title: Text(translate('edit_account_page.titlebar')), title: Text(translate('edit_account_page.titlebar')),
leading: Navigator.canPop(context) leading: Navigator.canPop(context)
? IconButton( ? IconButton(
icon: const Icon(Icons.arrow_back), icon: const Icon(Icons.arrow_back),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
}, },
) )
: null, : null,
actions: [ actions: [
const SignalStrengthMeterWidget(), const SignalStrengthMeterWidget(),
IconButton( IconButton(
icon: const Icon(Icons.settings), icon: const Icon(Icons.settings),
tooltip: translate('menu.settings_tooltip'), tooltip: translate('menu.settings_tooltip'),
onPressed: () async { onPressed: () async {
await GoRouterHelper(context).push('/settings'); await GoRouterHelper(context).push('/settings');
}) })
]), ]),
body: _editAccountForm( body: Column(children: [
context, _editAccountForm(
onSubmit: (formKey) async { context,
// dismiss the keyboard by unfocusing the textfield onSubmit: _onSubmit,
FocusScope.of(context).unfocus(); ).expanded(),
Text(translate('edit_account_page.remove_account_description')),
try { ElevatedButton(
final name = formKey.currentState! onPressed: _onRemoveAccount,
.fields[EditProfileForm.formFieldName]!.value as String; child: Row(mainAxisSize: MainAxisSize.min, children: [
final pronouns = formKey const Icon(Icons.person_remove_alt_1, size: 16)
.currentState! .paddingLTRB(0, 0, 4, 0),
.fields[EditProfileForm.formFieldPronouns]! Text(translate('edit_account_page.remove_account'))
.value as String? ?? .paddingLTRB(0, 0, 4, 0)
''; ])).paddingLTRB(0, 8, 0, 24),
final newProfile = widget.existingProfile.deepCopy() Text(translate('edit_account_page.delete_identity_description')),
..name = name ElevatedButton(
..pronouns = pronouns onPressed: _onDeleteIdentity,
..timestamp = Veilid.instance.now().toInt64(); child: Row(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.person_off, size: 16)
setState(() { .paddingLTRB(0, 0, 4, 0),
_isInAsyncCall = true; Text(translate('edit_account_page.delete_identity'))
}); .paddingLTRB(0, 0, 4, 0)
try { ])).paddingLTRB(0, 8, 0, 24)
// Look up account cubit for this specific account ]).paddingSymmetric(horizontal: 24, vertical: 8))
final perAccountCollectionBlocMapCubit = .withModalHUD(context, displayModalHUD);
context.read<PerAccountCollectionBlocMapCubit>();
final accountRecordCubit = await perAccountCollectionBlocMapCubit
.operate(widget.superIdentityRecordKey,
closure: (c) async => c.accountRecordCubit);
if (accountRecordCubit == null) {
return;
}
// Update account profile DHT record
// This triggers ConversationCubits to update
await accountRecordCubit.updateProfile(newProfile);
// Update local account profile
await AccountRepository.instance.editAccountProfile(
widget.superIdentityRecordKey, newProfile);
if (context.mounted) {
Navigator.canPop(context)
? GoRouterHelper(context).pop()
: GoRouterHelper(context).go('/');
}
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (context.mounted) {
await showErrorModal(context,
translate('edit_account_page.error'), 'Exception: $e');
}
}
},
).paddingSymmetric(horizontal: 24, vertical: 8),
).withModalHUD(context, displayModalHUD);
} }
} }

View File

@ -52,6 +52,43 @@ class _NewAccountPageState extends State<NewAccountPage> {
onSubmit: !canSubmit ? null : onSubmit); onSubmit: !canSubmit ? null : onSubmit);
} }
Future<void> _onSubmit(GlobalKey<FormBuilderState> formKey) async {
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus();
try {
final name = formKey
.currentState!.fields[EditProfileForm.formFieldName]!.value as String;
final pronouns = formKey.currentState!
.fields[EditProfileForm.formFieldPronouns]!.value as String? ??
'';
final newProfile = proto.Profile()
..name = name
..pronouns = pronouns;
setState(() {
_isInAsyncCall = true;
});
try {
final superSecret = await AccountRepository.instance
.createWithNewSuperIdentity(newProfile);
GoRouterHelper(context)
.pushReplacement('/new_account/recovery_key', extra: superSecret);
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (mounted) {
await showErrorModal(
context, translate('new_account_page.error'), 'Exception: $e');
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final displayModalHUD = _isInAsyncCall; final displayModalHUD = _isInAsyncCall;
@ -79,45 +116,7 @@ class _NewAccountPageState extends State<NewAccountPage> {
]), ]),
body: _newAccountForm( body: _newAccountForm(
context, context,
onSubmit: (formKey) async { onSubmit: _onSubmit,
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus();
try {
final name = formKey.currentState!
.fields[EditProfileForm.formFieldName]!.value as String;
final pronouns = formKey
.currentState!
.fields[EditProfileForm.formFieldPronouns]!
.value as String? ??
'';
final newProfile = proto.Profile()
..name = name
..pronouns = pronouns;
setState(() {
_isInAsyncCall = true;
});
try {
final superSecret = await AccountRepository.instance
.createWithNewSuperIdentity(newProfile);
GoRouterHelper(context).pushReplacement(
'/new_account/recovery_key',
extra: superSecret);
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (context.mounted) {
await showErrorModal(context, translate('new_account_page.error'),
'Exception: $e');
}
}
},
).paddingSymmetric(horizontal: 24, vertical: 8), ).paddingSymmetric(horizontal: 24, vertical: 8),
).withModalHUD(context, displayModalHUD); ).withModalHUD(context, displayModalHUD);
} }

View File

@ -99,9 +99,13 @@ class _EditProfileFormState extends State<EditProfileForm> {
await widget.onSubmit!(_formKey); await widget.onSubmit!(_formKey);
} }
}, },
child: Text((widget.onSubmit == null) child: Row(mainAxisSize: MainAxisSize.min, children: [
? widget.submitDisabledText const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
: widget.submitText), Text((widget.onSubmit == null)
? widget.submitDisabledText
: widget.submitText)
.paddingLTRB(0, 0, 4, 0)
]),
).paddingSymmetric(vertical: 4).alignAtCenterRight(), ).paddingSymmetric(vertical: 4).alignAtCenterRight(),
], ],
), ),

View File

@ -15,7 +15,7 @@ class VeilidChatGlobalInit {
Future<void> _initializeVeilid() async { Future<void> _initializeVeilid() async {
// Init Veilid // Init Veilid
Veilid.instance.initializeVeilidCore( Veilid.instance.initializeVeilidCore(
getDefaultVeilidPlatformConfig(kIsWeb, VeilidChatApp.name)); await getDefaultVeilidPlatformConfig(kIsWeb, VeilidChatApp.name));
// Veilid logging // Veilid logging
initVeilidLog(kDebugMode); initVeilidLog(kDebugMode);

View File

@ -39,11 +39,11 @@ class _DrawerMenuState extends State<DrawerMenu> {
}); });
} }
void _doEditClick( void _doEditClick(TypedKey superIdentityRecordKey,
TypedKey superIdentityRecordKey, proto.Profile existingProfile) { proto.Profile existingProfile, OwnedDHTRecordPointer accountRecord) {
singleFuture(this, () async { singleFuture(this, () async {
await GoRouterHelper(context).push('/edit_account', await GoRouterHelper(context).push('/edit_account',
extra: [superIdentityRecordKey, existingProfile]); extra: [superIdentityRecordKey, existingProfile, accountRecord]);
}); });
} }
@ -128,10 +128,10 @@ class _DrawerMenuState extends State<DrawerMenu> {
final superIdentityRecordKey = la.superIdentity.recordKey; final superIdentityRecordKey = la.superIdentity.recordKey;
// See if this account is logged in // See if this account is logged in
final avAccountRecordState = perAccountCollectionBlocMapState final perAccountState =
.get(superIdentityRecordKey) perAccountCollectionBlocMapState.get(superIdentityRecordKey);
?.avAccountRecordState; final avAccountRecordState = perAccountState?.avAccountRecordState;
if (avAccountRecordState != null) { if (perAccountState != null && avAccountRecordState != null) {
// Account is logged in // Account is logged in
final scale = theme.extension<ScaleScheme>()!.tertiaryScale; final scale = theme.extension<ScaleScheme>()!.tertiaryScale;
final loggedInAccount = avAccountRecordState.when( final loggedInAccount = avAccountRecordState.when(
@ -144,7 +144,11 @@ class _DrawerMenuState extends State<DrawerMenu> {
_doSwitchClick(superIdentityRecordKey); _doSwitchClick(superIdentityRecordKey);
}, },
footerCallback: () { footerCallback: () {
_doEditClick(superIdentityRecordKey, value.profile); _doEditClick(
superIdentityRecordKey,
value.profile,
perAccountState.accountInfo.userLogin!.accountRecordInfo
.accountRecord);
}), }),
loading: () => _wrapInBox( loading: () => _wrapInBox(
child: buildProgressIndicator(), child: buildProgressIndicator(),

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../chat/chat.dart'; import '../../../chat/chat.dart';
import '../../../tools/tools.dart';
class HomeAccountReadyChat extends StatefulWidget { class HomeAccountReadyChat extends StatefulWidget {
const HomeAccountReadyChat({super.key}); const HomeAccountReadyChat({super.key});
@ -15,11 +14,6 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.normal);
});
} }
@override @override

View File

@ -23,11 +23,6 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.normal);
});
} }
Widget buildUserPanel() => Builder(builder: (context) { Widget buildUserPanel() => Builder(builder: (context) {

View File

@ -45,8 +45,20 @@ class HomeScreenState extends State<HomeScreen>
curve: Curves.easeInOut, curve: Curves.easeInOut,
)); ));
// Account animation setup WidgetsBinding.instance.addPostFrameCallback((_) async {
final localAccounts = context.read<LocalAccountsCubit>().state;
final activeLocalAccount = context.read<ActiveLocalAccountCubit>().state;
final activeIndex = localAccounts
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
final canClose = activeIndex != -1;
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.normal);
if (!canClose) {
await _zoomDrawerController.open!();
}
});
super.initState(); super.initState();
} }
@ -152,6 +164,12 @@ class HomeScreenState extends State<HomeScreen>
scale.tertiaryScale.appBackground, scale.tertiaryScale.appBackground,
]); ]);
final localAccounts = context.watch<LocalAccountsCubit>().state;
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
final activeIndex = localAccounts
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
final canClose = activeIndex != -1;
return SafeArea( return SafeArea(
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration(gradient: gradient), decoration: BoxDecoration(gradient: gradient),
@ -178,9 +196,9 @@ class HomeScreenState extends State<HomeScreen>
openCurve: Curves.fastEaseInToSlowEaseOut, openCurve: Curves.fastEaseInToSlowEaseOut,
// duration: const Duration(milliseconds: 250), // duration: const Duration(milliseconds: 250),
// reverseDuration: const Duration(milliseconds: 250), // reverseDuration: const Duration(milliseconds: 250),
menuScreenTapClose: true, menuScreenTapClose: canClose,
mainScreenTapClose: true, mainScreenTapClose: canClose,
//disableDragGesture: false, disableDragGesture: !canClose,
mainScreenScale: .25, mainScreenScale: .25,
slideWidth: min(360, MediaQuery.of(context).size.width * 0.9), slideWidth: min(360, MediaQuery.of(context).size.width * 0.9),
))); )));

View File

@ -69,6 +69,7 @@ class RouterCubit extends Cubit<RouterState> {
return EditAccountPage( return EditAccountPage(
superIdentityRecordKey: extra[0]! as TypedKey, superIdentityRecordKey: extra[0]! as TypedKey,
existingProfile: extra[1]! as proto.Profile, existingProfile: extra[1]! as proto.Profile,
accountRecord: extra[2]! as OwnedDHTRecordPointer,
); );
}, },
), ),

View File

@ -5,7 +5,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import '../src/veilid_log.dart'; import '../src/veilid_log.dart';
import '../veilid_support.dart'; import '../veilid_support.dart';
import 'exceptions.dart';
part 'identity_instance.freezed.dart'; part 'identity_instance.freezed.dart';
part 'identity_instance.g.dart'; part 'identity_instance.g.dart';

View File

@ -1,5 +1,7 @@
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:veilid/veilid.dart'; import 'package:veilid/veilid.dart';
// ignore: do_not_use_environment // ignore: do_not_use_environment
@ -8,8 +10,8 @@ const bool _kReleaseMode = bool.fromEnvironment('dart.vm.product');
const bool _kProfileMode = bool.fromEnvironment('dart.vm.profile'); const bool _kProfileMode = bool.fromEnvironment('dart.vm.profile');
const bool _kDebugMode = !_kReleaseMode && !_kProfileMode; const bool _kDebugMode = !_kReleaseMode && !_kProfileMode;
Map<String, dynamic> getDefaultVeilidPlatformConfig( Future<Map<String, dynamic>> getDefaultVeilidPlatformConfig(
bool isWeb, String appName) { bool isWeb, String appName) async {
final ignoreLogTargetsStr = final ignoreLogTargetsStr =
// ignore: do_not_use_environment // ignore: do_not_use_environment
const String.fromEnvironment('IGNORE_LOG_TARGETS').trim(); const String.fromEnvironment('IGNORE_LOG_TARGETS').trim();
@ -17,6 +19,16 @@ Map<String, dynamic> getDefaultVeilidPlatformConfig(
? <String>[] ? <String>[]
: ignoreLogTargetsStr.split(',').map((e) => e.trim()).toList(); : ignoreLogTargetsStr.split(',').map((e) => e.trim()).toList();
// ignore: do_not_use_environment
var flamePathStr = const String.fromEnvironment('FLAME').trim();
if (flamePathStr == '1') {
flamePathStr = p.join(
(await getApplicationSupportDirectory()).absolute.path,
'$appName.folded');
// ignore: avoid_print
print('Flame data logged to $flamePathStr');
}
if (isWeb) { if (isWeb) {
return VeilidWASMConfig( return VeilidWASMConfig(
logging: VeilidWASMConfigLogging( logging: VeilidWASMConfigLogging(
@ -52,7 +64,9 @@ Map<String, dynamic> getDefaultVeilidPlatformConfig(
api: VeilidFFIConfigLoggingApi( api: VeilidFFIConfigLoggingApi(
enabled: true, enabled: true,
level: VeilidConfigLogLevel.info, level: VeilidConfigLogLevel.info,
ignoreLogTargets: ignoreLogTargets))) ignoreLogTargets: ignoreLogTargets),
flame: VeilidFFIConfigLoggingFlame(
enabled: flamePathStr.isNotEmpty, path: flamePathStr)))
.toJson(); .toJson();
} }

View File

@ -428,7 +428,7 @@ packages:
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
@ -436,7 +436,7 @@ packages:
source: hosted source: hosted
version: "1.9.0" version: "1.9.0"
path_provider: path_provider:
dependency: transitive dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161

View File

@ -19,6 +19,8 @@ dependencies:
loggy: ^2.0.3 loggy: ^2.0.3
meta: ^1.12.0 meta: ^1.12.0
path: ^1.9.0
path_provider: ^2.1.3
protobuf: ^3.1.0 protobuf: ^3.1.0
veilid: veilid:
# veilid: ^0.0.1 # veilid: ^0.0.1

3
process_flame.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
cat "/Users/$USER/Library/Containers/com.veilid.veilidchat/Data/Library/Application Support/com.veilid.veilidchat/VeilidChat.folded" | inferno-flamegraph -c purple --fontsize 8 --height 24 --title "VeilidChat" --factor 0.000000001 --countname secs > /tmp/veilidchat.svg
cat "/Users/$USER/Library/Containers/com.veilid.veilidchat/Data/Library/Application Support/com.veilid.veilidchat/VeilidChat.folded" | inferno-flamegraph --reverse -c aqua --fontsize 8 --height 24 --title "VeilidChat Reverse" --factor 0.000000001 --countname secs > /tmp/veilidchat-reverse.svg