mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-01-25 15:06:12 -05:00
fix slow first message
This commit is contained in:
parent
6c4b803091
commit
b0d4e35c6f
@ -18,7 +18,7 @@
|
|||||||
"lock_type_password": "password"
|
"lock_type_password": "password"
|
||||||
},
|
},
|
||||||
"new_account_page": {
|
"new_account_page": {
|
||||||
"titlebar": "Create a new account",
|
"titlebar": "Create A New Account",
|
||||||
"header": "Account Profile",
|
"header": "Account Profile",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"instructions": "This information will be shared with the people you invite to connect with you on VeilidChat.",
|
"instructions": "This information will be shared with the people you invite to connect with you on VeilidChat.",
|
||||||
@ -26,12 +26,21 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"pronouns": "Pronouns"
|
"pronouns": "Pronouns"
|
||||||
},
|
},
|
||||||
|
"show_recovery_key_page": {
|
||||||
|
"titlebar": "Save Recovery Key",
|
||||||
|
"instructions": "You must save this recovery key somewhere safe. This key is the ONLY way to recover your VeilidChat account in the event of a forgotton password or a lost, stolen, or compromised device.",
|
||||||
|
"instructions_options": "Here are some options for your recovery key:",
|
||||||
|
"instructions_print": "Print the recovery key and keep it somewhere safe",
|
||||||
|
"instructions_write": "View the recovery key and write it down on paper",
|
||||||
|
"instructions_send": "Send the recovery key to another app to save it"
|
||||||
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"accept": "Accept",
|
"accept": "Accept",
|
||||||
"reject": "Reject",
|
"reject": "Reject",
|
||||||
|
"finish": "Finish",
|
||||||
"waiting_for_network": "Waiting For Network"
|
"waiting_for_network": "Waiting For Network"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
|
@ -168,7 +168,8 @@ class AccountRepository {
|
|||||||
/// Creates a new super identity, an identity instance, an account associated
|
/// Creates a new super identity, an identity instance, an account associated
|
||||||
/// with the identity instance, stores the account in the identity key and
|
/// with the identity instance, stores the account in the identity key and
|
||||||
/// then logs into that account with no password set at this time
|
/// then logs into that account with no password set at this time
|
||||||
Future<void> createWithNewSuperIdentity(NewProfileSpec newProfileSpec) async {
|
Future<SecretKey> createWithNewSuperIdentity(
|
||||||
|
NewProfileSpec newProfileSpec) async {
|
||||||
log.debug('Creating super identity');
|
log.debug('Creating super identity');
|
||||||
final wsi = await WritableSuperIdentity.create();
|
final wsi = await WritableSuperIdentity.create();
|
||||||
try {
|
try {
|
||||||
@ -181,6 +182,8 @@ class AccountRepository {
|
|||||||
final ok = await login(
|
final ok = await login(
|
||||||
localAccount.superIdentity.recordKey, EncryptionKeyType.none, '');
|
localAccount.superIdentity.recordKey, EncryptionKeyType.none, '');
|
||||||
assert(ok, 'login with none should never fail');
|
assert(ok, 'login with none should never fail');
|
||||||
|
|
||||||
|
return wsi.superSecret;
|
||||||
} on Exception catch (_) {
|
} on Exception catch (_) {
|
||||||
await wsi.delete();
|
await wsi.delete();
|
||||||
rethrow;
|
rethrow;
|
||||||
|
@ -140,8 +140,11 @@ class NewAccountPageState extends State<NewAccountPage> {
|
|||||||
final newProfileSpec =
|
final newProfileSpec =
|
||||||
NewProfileSpec(name: name, pronouns: pronouns);
|
NewProfileSpec(name: name, pronouns: pronouns);
|
||||||
|
|
||||||
await AccountRepository.instance
|
final superSecret = await AccountRepository.instance
|
||||||
.createWithNewSuperIdentity(newProfileSpec);
|
.createWithNewSuperIdentity(newProfileSpec);
|
||||||
|
|
||||||
|
GoRouterHelper(context).pushReplacement('/new_account/recovery_key',
|
||||||
|
extra: superSecret);
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
await showErrorModal(context, translate('new_account_page.error'),
|
await showErrorModal(context, translate('new_account_page.error'),
|
||||||
|
65
lib/account_manager/views/show_recovery_key_page.dart
Normal file
65
lib/account_manager/views/show_recovery_key_page.dart
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
|
import '../../layout/default_app_bar.dart';
|
||||||
|
import '../../theme/theme.dart';
|
||||||
|
import '../../tools/tools.dart';
|
||||||
|
import '../../veilid_processor/veilid_processor.dart';
|
||||||
|
import '../account_manager.dart';
|
||||||
|
|
||||||
|
class ShowRecoveryKeyPage extends StatefulWidget {
|
||||||
|
const ShowRecoveryKeyPage({required SecretKey secretKey, super.key})
|
||||||
|
: _secretKey = secretKey;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShowRecoveryKeyPageState createState() => ShowRecoveryKeyPageState();
|
||||||
|
|
||||||
|
final SecretKey _secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
await changeWindowSetup(
|
||||||
|
TitleBarStyle.normal, OrientationCapability.portraitOnly);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: prefer_expression_function_bodies
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final secretKey = widget._secretKey;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
// resizeToAvoidBottomInset: false,
|
||||||
|
appBar: DefaultAppBar(
|
||||||
|
title: Text(translate('show_recovery_key_page.titlebar')),
|
||||||
|
actions: [
|
||||||
|
const SignalStrengthMeterWidget(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.settings),
|
||||||
|
tooltip: translate('app_bar.settings_tooltip'),
|
||||||
|
onPressed: () async {
|
||||||
|
await GoRouterHelper(context).push('/settings');
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
body: Column(children: [
|
||||||
|
Text('ASS: $secretKey'),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
GoRouterHelper(context).go('/');
|
||||||
|
},
|
||||||
|
child: Text(translate('button.finish')))
|
||||||
|
]).paddingSymmetric(horizontal: 24, vertical: 8));
|
||||||
|
}
|
||||||
|
}
|
35
lib/account_manager/views/switch_account_widget.dart
Normal file
35
lib/account_manager/views/switch_account_widget.dart
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../proto/proto.dart' as proto;
|
||||||
|
import '../../theme/theme.dart';
|
||||||
|
import '../account_manager.dart';
|
||||||
|
|
||||||
|
class SwitchAccountWidget extends StatelessWidget {
|
||||||
|
const SwitchAccountWidget({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: prefer_expression_function_bodies
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
final textTheme = theme.textTheme;
|
||||||
|
|
||||||
|
final accountRepo = AccountRepository.instance;
|
||||||
|
final localAccounts = accountRepo.getLocalAccounts();
|
||||||
|
for (final la in localAccounts) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecoratedBox(
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
color: scale.primaryScale.border,
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
|
||||||
|
child: Column(children: []),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
export 'new_account_page.dart';
|
export 'new_account_page.dart';
|
||||||
export 'profile_widget.dart';
|
export 'profile_widget.dart';
|
||||||
|
export 'show_recovery_key_page.dart';
|
||||||
|
@ -3,6 +3,7 @@ import 'package:veilid_support/veilid_support.dart';
|
|||||||
|
|
||||||
import '../../../proto/proto.dart' as proto;
|
import '../../../proto/proto.dart' as proto;
|
||||||
|
|
||||||
|
import '../../../tools/tools.dart';
|
||||||
import 'author_input_source.dart';
|
import 'author_input_source.dart';
|
||||||
import 'message_integrity.dart';
|
import 'message_integrity.dart';
|
||||||
import 'output_position.dart';
|
import 'output_position.dart';
|
||||||
@ -84,6 +85,8 @@ class AuthorInputQueue {
|
|||||||
if (_lastMessage != null) {
|
if (_lastMessage != null) {
|
||||||
// Ensure the timestamp is not moving backward
|
// Ensure the timestamp is not moving backward
|
||||||
if (nextMessage.value.timestamp < _lastMessage!.timestamp) {
|
if (nextMessage.value.timestamp < _lastMessage!.timestamp) {
|
||||||
|
log.warning('timestamp backward: ${nextMessage.value.timestamp}'
|
||||||
|
' < ${_lastMessage!.timestamp}');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,11 +94,14 @@ class AuthorInputQueue {
|
|||||||
// Verify the id chain for the message
|
// Verify the id chain for the message
|
||||||
final matchId = await _messageIntegrity.generateMessageId(_lastMessage);
|
final matchId = await _messageIntegrity.generateMessageId(_lastMessage);
|
||||||
if (matchId.compare(nextMessage.value.idBytes) != 0) {
|
if (matchId.compare(nextMessage.value.idBytes) != 0) {
|
||||||
|
log.warning(
|
||||||
|
'id chain invalid: $matchId != ${nextMessage.value.idBytes}');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the signature for the message
|
// Verify the signature for the message
|
||||||
if (!await _messageIntegrity.verifyMessage(nextMessage.value)) {
|
if (!await _messageIntegrity.verifyMessage(nextMessage.value)) {
|
||||||
|
log.warning('invalid message signature: ${nextMessage.value}');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_transform/stream_transform.dart';
|
import 'package:stream_transform/stream_transform.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
import '../../../account_manager/account_manager.dart';
|
import '../../../account_manager/account_manager.dart';
|
||||||
import '../../layout/layout.dart';
|
import '../../layout/layout.dart';
|
||||||
@ -83,6 +84,11 @@ class RouterCubit extends Cubit<RouterState> {
|
|||||||
path: '/new_account',
|
path: '/new_account',
|
||||||
builder: (context, state) => const NewAccountPage(),
|
builder: (context, state) => const NewAccountPage(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/new_account/recovery_key',
|
||||||
|
builder: (context, state) =>
|
||||||
|
ShowRecoveryKeyPage(secretKey: state.extra! as SecretKey),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
builder: (context, state) => const SettingsPage(),
|
builder: (context, state) => const SettingsPage(),
|
||||||
@ -98,8 +104,6 @@ class RouterCubit extends Cubit<RouterState> {
|
|||||||
// No matter where we are, if there's not
|
// No matter where we are, if there's not
|
||||||
|
|
||||||
switch (goRouterState.matchedLocation) {
|
switch (goRouterState.matchedLocation) {
|
||||||
case '/new_account':
|
|
||||||
return state.hasAnyAccount ? '/' : null;
|
|
||||||
case '/':
|
case '/':
|
||||||
if (!state.hasAnyAccount) {
|
if (!state.hasAnyAccount) {
|
||||||
return '/new_account';
|
return '/new_account';
|
||||||
@ -130,6 +134,10 @@ class RouterCubit extends Cubit<RouterState> {
|
|||||||
return '/';
|
return '/';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
case '/new_account':
|
||||||
|
return null;
|
||||||
|
case '/new_account/recovery_key':
|
||||||
|
return null;
|
||||||
case '/settings':
|
case '/settings':
|
||||||
return null;
|
return null;
|
||||||
case '/developer':
|
case '/developer':
|
||||||
|
@ -71,6 +71,12 @@ class _DHTLogSpine {
|
|||||||
|
|
||||||
// Write new spine head record to the network
|
// Write new spine head record to the network
|
||||||
await spine.operate((spine) async {
|
await spine.operate((spine) async {
|
||||||
|
// Write first empty subkey
|
||||||
|
final subkeyData = _makeEmptySubkey();
|
||||||
|
final existingSubkeyData =
|
||||||
|
await spineRecord.tryWriteBytes(subkeyData, subkey: 1);
|
||||||
|
assert(existingSubkeyData == null, 'Should never conflict on create');
|
||||||
|
|
||||||
final success = await spine.writeSpineHead();
|
final success = await spine.writeSpineHead();
|
||||||
assert(success, 'false return should never happen on create');
|
assert(success, 'false return should never happen on create');
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user