ui cleanup

This commit is contained in:
Christien Rioux 2024-06-21 22:44:35 -04:00
parent 7b400ed08b
commit 152c8bdff4
15 changed files with 827 additions and 772 deletions

View File

@ -4,7 +4,6 @@ import 'package:async_tools/async_tools.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:protobuf/protobuf.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
@ -82,6 +81,16 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
await _sentMessagesCubit?.close(); await _sentMessagesCubit?.close();
await _rcvdMessagesCubit?.close(); await _rcvdMessagesCubit?.close();
await _reconciledMessagesCubit?.close(); await _reconciledMessagesCubit?.close();
// If the local conversation record is gone, then delete the reconciled
// messages table as well
final conversationDead = await DHTRecordPool.instance
.isDeletedRecordKey(_localConversationRecordKey);
if (conversationDead) {
await SingleContactMessagesCubit.cleanupAndDeleteMessages(
localConversationRecordKey: _localConversationRecordKey);
}
await super.close(); await super.close();
} }
@ -292,8 +301,14 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
previousMessage = message; previousMessage = message;
} }
// _sendingMessages = messages;
// _renderState();
await _sentMessagesCubit!.operateAppendEventual((writer) => await _sentMessagesCubit!.operateAppendEventual((writer) =>
writer.addAll(messages.map((m) => m.writeToBuffer()).toList())); writer.addAll(messages.map((m) => m.writeToBuffer()).toList()));
// _sendingMessages = const IList.empty();
} }
// Produce a state for this cubit from the input cubits and queues // Produce a state for this cubit from the input cubits and queues
@ -304,7 +319,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
// Get all sent messages // Get all sent messages
final sentMessages = _sentMessagesCubit?.state.state.asData?.value; final sentMessages = _sentMessagesCubit?.state.state.asData?.value;
//Get all items in the unsent queue //Get all items in the unsent queue
final unsentMessages = _unsentMessagesQueue.queue; //final unsentMessages = _unsentMessagesQueue.queue;
// If we aren't ready to render a state, say we're loading // If we aren't ready to render a state, say we're loading
if (reconciledMessages == null || sentMessages == null) { if (reconciledMessages == null || sentMessages == null) {
@ -329,7 +344,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
// ); // );
final renderedElements = <RenderStateElement>[]; final renderedElements = <RenderStateElement>[];
final renderedIds = <String>{};
for (final m in reconciledMessages.windowElements) { for (final m in reconciledMessages.windowElements) {
final isLocal = final isLocal =
m.content.author.toVeilid() == _accountInfo.identityTypedPublicKey; m.content.author.toVeilid() == _accountInfo.identityTypedPublicKey;
@ -346,13 +361,22 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
sent: sent, sent: sent,
sentOffline: sentOffline, sentOffline: sentOffline,
)); ));
renderedIds.add(m.content.authorUniqueIdString);
} }
for (final m in unsentMessages) {
renderedElements.add(RenderStateElement( // Render in-flight messages at the bottom
message: (m.deepCopy())..id = m.timestamp.toBytes(), // for (final m in _sendingMessages) {
isLocal: true, // if (renderedIds.contains(m.authorUniqueIdString)) {
)); // continue;
} // }
// renderedElements.add(RenderStateElement(
// message: m,
// isLocal: true,
// sent: true,
// sentOffline: true,
// ));
// }
// Render the state // Render the state
final messages = renderedElements final messages = renderedElements
@ -426,7 +450,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
late final MessageReconciliation _reconciliation; late final MessageReconciliation _reconciliation;
late final PersistentQueue<proto.Message> _unsentMessagesQueue; late final PersistentQueue<proto.Message> _unsentMessagesQueue;
// IList<proto.Message> _sendingMessages = const IList.empty();
StreamSubscription<DHTLogBusyState<proto.Message>>? _sentSubscription; StreamSubscription<DHTLogBusyState<proto.Message>>? _sentSubscription;
StreamSubscription<DHTLogBusyState<proto.Message>>? _rcvdSubscription; StreamSubscription<DHTLogBusyState<proto.Message>>? _rcvdSubscription;
StreamSubscription<TableDBArrayProtobufBusyState<proto.ReconciledMessage>>? StreamSubscription<TableDBArrayProtobufBusyState<proto.ReconciledMessage>>?

View File

@ -8,7 +8,6 @@ import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
import '../../chat/chat.dart'; import '../../chat/chat.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
////////////////////////////////////////////////// //////////////////////////////////////////////////
@ -58,9 +57,20 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
final remoteConversationRecordKey = final remoteConversationRecordKey =
contact.remoteConversationRecordKey.toVeilid(); contact.remoteConversationRecordKey.toVeilid();
// Create 1:1 conversation type Chat
final chatMember = proto.ChatMember()
..remoteIdentityPublicKey = remoteIdentityPublicKey.toProto()
..remoteConversationRecordKey = remoteConversationRecordKey.toProto();
final directChat = proto.DirectChat()
..settings = await getDefaultChatSettings(contact)
..localConversationRecordKey = localConversationRecordKey.toProto()
..remoteMember = chatMember;
final chat = proto.Chat()..direct = directChat;
// Add Chat to account's list // Add Chat to account's list
// if this fails, don't keep retrying, user can try again later await operateWriteEventual((writer) async {
await operateWrite((writer) async {
// See if we have added this chat already // See if we have added this chat already
for (var i = 0; i < writer.length; i++) { for (var i = 0; i < writer.length; i++) {
final cbuf = await writer.get(i); final cbuf = await writer.get(i);
@ -89,18 +99,6 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
} }
} }
// Create 1:1 conversation type Chat
final chatMember = proto.ChatMember()
..remoteIdentityPublicKey = remoteIdentityPublicKey.toProto()
..remoteConversationRecordKey = remoteConversationRecordKey.toProto();
final directChat = proto.DirectChat()
..settings = await getDefaultChatSettings(contact)
..localConversationRecordKey = localConversationRecordKey.toProto()
..remoteMember = chatMember;
final chat = proto.Chat()..direct = directChat;
// Add chat // Add chat
await writer.add(chat.writeToBuffer()); await writer.add(chat.writeToBuffer());
}); });
@ -110,37 +108,22 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
Future<void> deleteChat( Future<void> deleteChat(
{required TypedKey localConversationRecordKey}) async { {required TypedKey localConversationRecordKey}) async {
// Remove Chat from account's list // Remove Chat from account's list
// if this fails, don't keep retrying, user can try again later await operateWriteEventual((writer) async {
final deletedItem = if (_activeChatCubit.state == localConversationRecordKey) {
// Ensure followers get their changes before we return _activeChatCubit.setActiveChat(null);
await syncFollowers(() => operateWrite((writer) async {
if (_activeChatCubit.state == localConversationRecordKey) {
_activeChatCubit.setActiveChat(null);
}
for (var i = 0; i < writer.length; i++) {
final c = await writer.getProtobuf(proto.Chat.fromBuffer, i);
if (c == null) {
throw Exception('Failed to get chat');
}
if (c.localConversationRecordKey ==
localConversationRecordKey) {
await writer.remove(i);
return c;
}
}
return null;
}));
// Since followers are synced, we can safetly remove the reconciled
// chat record now
if (deletedItem != null) {
try {
await SingleContactMessagesCubit.cleanupAndDeleteMessages(
localConversationRecordKey: localConversationRecordKey);
} on Exception catch (e) {
log.debug('error removing reconciled chat table: $e', e);
} }
} for (var i = 0; i < writer.length; i++) {
final c = await writer.getProtobuf(proto.Chat.fromBuffer, i);
if (c == null) {
throw Exception('Failed to get chat');
}
if (c.localConversationRecordKey == localConversationRecordKey) {
await writer.remove(i);
return;
}
}
});
} }
/// StateMapFollowable ///////////////////////// /// StateMapFollowable /////////////////////////

View File

@ -152,8 +152,7 @@ class ContactInvitationListCubit
..message = message; ..message = message;
// Add ContactInvitationRecord to account's list // Add ContactInvitationRecord to account's list
// if this fails, don't keep retrying, user can try again later await operateWriteEventual((writer) async {
await operateWrite((writer) async {
await writer.add(cinvrec.writeToBuffer()); await writer.add(cinvrec.writeToBuffer());
}); });
}); });

View File

@ -46,7 +46,6 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
//final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme; final textTheme = theme.textTheme;
final signedContactInvitationBytesV = final signedContactInvitationBytesV =
@ -58,6 +57,9 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
return PopControl( return PopControl(
dismissible: !signedContactInvitationBytesV.isLoading, dismissible: !signedContactInvitationBytesV.isLoading,
child: Dialog( child: Dialog(
shape: RoundedRectangleBorder(
side: const BorderSide(width: 2),
borderRadius: BorderRadius.circular(16)),
backgroundColor: Colors.white, backgroundColor: Colors.white,
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
@ -90,6 +92,10 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
.paddingAll(8), .paddingAll(8),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.copy), icon: const Icon(Icons.copy),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: Colors.white,
side: const BorderSide()),
label: Text(translate( label: Text(translate(
'create_invitation_dialog.copy_invitation')), 'create_invitation_dialog.copy_invitation')),
onPressed: () async { onPressed: () async {

View File

@ -6,7 +6,6 @@ import 'package:protobuf/protobuf.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
import '../../conversation/conversation.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart'; import '../../tools/tools.dart';
@ -17,8 +16,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
ContactListCubit({ ContactListCubit({
required AccountInfo accountInfo, required AccountInfo accountInfo,
required OwnedDHTRecordPointer contactListRecordPointer, required OwnedDHTRecordPointer contactListRecordPointer,
}) : _accountInfo = accountInfo, }) : super(
super(
open: () => open: () =>
_open(accountInfo.accountRecordKey, contactListRecordPointer), _open(accountInfo.accountRecordKey, contactListRecordPointer),
decodeElement: proto.Contact.fromBuffer); decodeElement: proto.Contact.fromBuffer);
@ -98,8 +96,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
..showAvailability = false; ..showAvailability = false;
// Add Contact to account's list // Add Contact to account's list
// if this fails, don't keep retrying, user can try again later await operateWriteEventual((writer) async {
await operateWrite((writer) async {
await writer.add(contact.writeToBuffer()); await writer.add(contact.writeToBuffer());
}); });
} }
@ -107,7 +104,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
Future<void> deleteContact( Future<void> deleteContact(
{required TypedKey localConversationRecordKey}) async { {required TypedKey localConversationRecordKey}) async {
// Remove Contact from account's list // Remove Contact from account's list
final deletedItem = await operateWrite((writer) async { final deletedItem = await operateWriteEventual((writer) async {
for (var i = 0; i < writer.length; i++) { for (var i = 0; i < writer.length; i++) {
final item = await writer.getProtobuf(proto.Contact.fromBuffer, i); final item = await writer.getProtobuf(proto.Contact.fromBuffer, i);
if (item == null) { if (item == null) {
@ -124,18 +121,11 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
if (deletedItem != null) { if (deletedItem != null) {
try { try {
// Make a conversation cubit to manipulate the conversation // Mark the conversation records for deletion
final conversationCubit = ConversationCubit( await DHTRecordPool.instance
accountInfo: _accountInfo, .deleteRecord(deletedItem.localConversationRecordKey.toVeilid());
remoteIdentityPublicKey: deletedItem.identityPublicKey.toVeilid(), await DHTRecordPool.instance
localConversationRecordKey: .deleteRecord(deletedItem.remoteConversationRecordKey.toVeilid());
deletedItem.localConversationRecordKey.toVeilid(),
remoteConversationRecordKey:
deletedItem.remoteConversationRecordKey.toVeilid(),
);
// Delete the local and remote conversation records
await conversationCubit.delete();
} on Exception catch (e) { } on Exception catch (e) {
log.debug('error deleting conversation records: $e', e); log.debug('error deleting conversation records: $e', e);
} }
@ -144,5 +134,4 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
final _contactProfileUpdateMap = final _contactProfileUpdateMap =
SingleStateProcessorMap<TypedKey, proto.Profile?>(); SingleStateProcessorMap<TypedKey, proto.Profile?>();
final AccountInfo _accountInfo;
} }

View File

@ -74,13 +74,13 @@ class ContactItemWidget extends StatelessWidget {
final contactListCubit = context.read<ContactListCubit>(); final contactListCubit = context.read<ContactListCubit>();
final chatListCubit = context.read<ChatListCubit>(); final chatListCubit = context.read<ChatListCubit>();
// Remove any chats for this contact
await chatListCubit.deleteChat(
localConversationRecordKey: localConversationRecordKey);
// Delete the contact itself // Delete the contact itself
await contactListCubit.deleteContact( await contactListCubit.deleteContact(
localConversationRecordKey: localConversationRecordKey); localConversationRecordKey: localConversationRecordKey);
// Remove any chats for this contact
await chatListCubit.deleteChat(
localConversationRecordKey: localConversationRecordKey);
}) })
], ],
); );

View File

@ -28,7 +28,6 @@ class _SingleContactChatState extends Equatable {
final TypedKey remoteMessagesRecordKey; final TypedKey remoteMessagesRecordKey;
@override @override
// TODO: implement props
List<Object?> get props => [ List<Object?> get props => [
remoteIdentityPublicKey, remoteIdentityPublicKey,
localConversationRecordKey, localConversationRecordKey,

View File

@ -14,7 +14,6 @@ import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
const _sfUpdateAccountChange = 'updateAccountChange'; const _sfUpdateAccountChange = 'updateAccountChange';
@ -116,7 +115,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
final accountRecordKey = _accountInfo.accountRecordKey; final accountRecordKey = _accountInfo.accountRecordKey;
final writer = _accountInfo.identityWriter; final writer = _accountInfo.identityWriter;
// Open with SMPL scheme for identity writer // Open with SMPL schema for identity writer
late final DHTRecord localConversationRecord; late final DHTRecord localConversationRecord;
if (existingConversationRecordKey != null) { if (existingConversationRecordKey != null) {
localConversationRecord = await pool.openRecordWrite( localConversationRecord = await pool.openRecordWrite(
@ -171,57 +170,6 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
return out; return out;
} }
/// Delete the conversation keys associated with this conversation
Future<bool> delete() async {
final pool = DHTRecordPool.instance;
await _initWait();
final localConversationCubit = _localConversationCubit;
final remoteConversationCubit = _remoteConversationCubit;
final deleteSet = DelayedWaitSet<void>();
if (localConversationCubit != null) {
final data = localConversationCubit.state.asData;
if (data == null) {
log.warning('could not delete local conversation');
return false;
}
deleteSet.add(() async {
_localConversationCubit = null;
await localConversationCubit.close();
final conversation = data.value;
final messagesKey = conversation.messages.toVeilid();
await pool.deleteRecord(messagesKey);
await pool.deleteRecord(_localConversationRecordKey!);
_localConversationRecordKey = null;
});
}
if (remoteConversationCubit != null) {
final data = remoteConversationCubit.state.asData;
if (data == null) {
log.warning('could not delete remote conversation');
return false;
}
deleteSet.add(() async {
_remoteConversationCubit = null;
await remoteConversationCubit.close();
final conversation = data.value;
final messagesKey = conversation.messages.toVeilid();
await pool.deleteRecord(messagesKey);
await pool.deleteRecord(_remoteConversationRecordKey!);
});
}
// Commit the delete futures
await deleteSet();
return true;
}
/// Force refresh of conversation keys /// Force refresh of conversation keys
Future<void> refresh() async { Future<void> refresh() async {
await _initWait(); await _initWait();

View File

@ -71,11 +71,22 @@ class _DrawerMenuState extends State<DrawerMenu> {
shortname = abbrev; shortname = abbrev;
} }
final avatar = AvatarImage( final avatar = Container(
size: 32, height: 34,
backgroundColor: loggedIn ? scale.primary : scale.elementBackground, width: 34,
foregroundColor: loggedIn ? scale.primaryText : scale.subtleText, decoration: BoxDecoration(
child: Text(shortname, style: theme.textTheme.titleLarge)); shape: BoxShape.circle,
border: Border.all(
color: loggedIn ? scale.border : scale.subtleBorder,
width: 2,
strokeAlign: BorderSide.strokeAlignOutside),
color: Colors.blue,
),
child: AvatarImage(
//size: 32,
backgroundColor: loggedIn ? scale.primary : scale.elementBackground,
foregroundColor: loggedIn ? scale.primaryText : scale.subtleText,
child: Text(shortname, style: theme.textTheme.titleLarge)));
return AnimatedPadding( return AnimatedPadding(
padding: EdgeInsets.fromLTRB(selected ? 0 : 0, 0, selected ? 0 : 8, 0), padding: EdgeInsets.fromLTRB(selected ? 0 : 0, 0, selected ? 0 : 8, 0),
@ -234,6 +245,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
//final textTheme = theme.textTheme; //final textTheme = theme.textTheme;
final localAccounts = context.watch<LocalAccountsCubit>().state; final localAccounts = context.watch<LocalAccountsCubit>().state;
final perAccountCollectionBlocMapState = final perAccountCollectionBlocMapState =
@ -269,13 +281,17 @@ class _DrawerMenuState extends State<DrawerMenu> {
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: Row(children: [ child: Row(children: [
SvgPicture.asset( SvgPicture.asset(
height: 48, height: 48,
'assets/images/icon.svg', 'assets/images/icon.svg',
).paddingLTRB(0, 0, 16, 0), colorFilter: scaleConfig.useVisualIndicators
? grayColorFilter
: null)
.paddingLTRB(0, 0, 16, 0),
SvgPicture.asset( SvgPicture.asset(
height: 48, height: 48,
'assets/images/title.svg', 'assets/images/title.svg',
), colorFilter:
scaleConfig.useVisualIndicators ? grayColorFilter : null),
])), ])),
const Spacer(), const Spacer(),
_getAccountList( _getAccountList(

View File

@ -89,11 +89,9 @@ class ScaleScheme extends ThemeExtension<ScaleScheme> {
onError: errorScale.primaryText, onError: errorScale.primaryText,
// errorContainer: errorScale.hoverElementBackground, // errorContainer: errorScale.hoverElementBackground,
// onErrorContainer: errorScale.subtleText, // onErrorContainer: errorScale.subtleText,
background: grayScale.appBackground, // reviewed
onBackground: grayScale.appText, // reviewed
surface: primaryScale.primary, // reviewed surface: primaryScale.primary, // reviewed
onSurface: primaryScale.primaryText, // reviewed onSurface: primaryScale.primaryText, // reviewed
surfaceVariant: secondaryScale.primary, surfaceContainerHighest: secondaryScale.primary,
onSurfaceVariant: secondaryScale.primaryText, // ?? reviewed a little onSurfaceVariant: secondaryScale.primaryText, // ?? reviewed a little
outline: primaryScale.border, outline: primaryScale.border,
outlineVariant: secondaryScale.border, outlineVariant: secondaryScale.border,

View File

@ -5,7 +5,6 @@ import 'package:blurry_modal_progress_hud/blurry_modal_progress_hud.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:motion_toast/motion_toast.dart'; import 'package:motion_toast/motion_toast.dart';
import 'package:quickalert/quickalert.dart'; import 'package:quickalert/quickalert.dart';
@ -122,36 +121,45 @@ Future<void> showErrorModal(
} }
void showErrorToast(BuildContext context, String message) { void showErrorToast(BuildContext context, String message) {
MotionToast.error( final theme = Theme.of(context);
title: Text(translate('toast.error')), final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
MotionToast(
//title: Text(translate('toast.error')),
description: Text(message), description: Text(message),
constraints: BoxConstraints.loose(const Size(400, 100)),
contentPadding: const EdgeInsets.all(16),
primaryColor: scale.errorScale.elementBackground,
secondaryColor: scale.errorScale.calloutBackground,
borderRadius: 16,
toastDuration: const Duration(seconds: 4),
animationDuration: const Duration(milliseconds: 1000),
displayBorder: scaleConfig.useVisualIndicators,
icon: Icons.error,
).show(context); ).show(context);
} }
void showInfoToast(BuildContext context, String message) { void showInfoToast(BuildContext context, String message) {
MotionToast.info( final theme = Theme.of(context);
title: Text(translate('toast.info')), final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
MotionToast(
//title: Text(translate('toast.info')),
description: Text(message), description: Text(message),
constraints: BoxConstraints.loose(const Size(400, 100)),
contentPadding: const EdgeInsets.all(16),
primaryColor: scale.tertiaryScale.elementBackground,
secondaryColor: scale.tertiaryScale.calloutBackground,
borderRadius: 16,
toastDuration: const Duration(seconds: 2),
animationDuration: const Duration(milliseconds: 500),
displayBorder: scaleConfig.useVisualIndicators,
icon: Icons.info,
).show(context); ).show(context);
} }
// Widget insetBorder(
// {required BuildContext context,
// required bool enabled,
// required Color color,
// required Widget child}) {
// if (!enabled) {
// return child;
// }
// return Stack({
// children: [] {
// DecoratedBox(decoration: BoxDecoration()
// child,
// }
// })
// }
Widget styledTitleContainer({ Widget styledTitleContainer({
required BuildContext context, required BuildContext context,
required String title, required String title,
@ -230,3 +238,26 @@ Widget styledBottomSheet({
bool get isPlatformDark => bool get isPlatformDark =>
WidgetsBinding.instance.platformDispatcher.platformBrightness == WidgetsBinding.instance.platformDispatcher.platformBrightness ==
Brightness.dark; Brightness.dark;
const grayColorFilter = ColorFilter.matrix(<double>[
0.2126,
0.7152,
0.0722,
0,
0,
0.2126,
0.7152,
0.0722,
0,
0,
0.2126,
0.7152,
0.0722,
0,
0,
0,
0,
0,
1,
0,
]);

View File

@ -1,7 +1,5 @@
part of 'dht_record_pool.dart'; part of 'dht_record_pool.dart';
const _sfListen = 'listen';
@immutable @immutable
class DHTRecordWatchChange extends Equatable { class DHTRecordWatchChange extends Equatable {
const DHTRecordWatchChange( const DHTRecordWatchChange(
@ -41,7 +39,7 @@ enum DHTRecordRefreshMode {
class DHTRecord implements DHTDeleteable<DHTRecord> { class DHTRecord implements DHTDeleteable<DHTRecord> {
DHTRecord._( DHTRecord._(
{required VeilidRoutingContext routingContext, {required VeilidRoutingContext routingContext,
required SharedDHTRecordData sharedDHTRecordData, required _SharedDHTRecordData sharedDHTRecordData,
required int defaultSubkey, required int defaultSubkey,
required KeyPair? writer, required KeyPair? writer,
required VeilidCrypto crypto, required VeilidCrypto crypto,
@ -241,7 +239,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
// if so, shortcut and don't bother decrypting it // if so, shortcut and don't bother decrypting it
if (newValueData.data.equals(encryptedNewValue)) { if (newValueData.data.equals(encryptedNewValue)) {
if (isUpdated) { if (isUpdated) {
DHTRecordPool.instance.processLocalValueChange(key, newValue, subkey); DHTRecordPool.instance._processLocalValueChange(key, newValue, subkey);
} }
return null; return null;
} }
@ -251,7 +249,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
await (crypto ?? _crypto).decrypt(newValueData.data); await (crypto ?? _crypto).decrypt(newValueData.data);
if (isUpdated) { if (isUpdated) {
DHTRecordPool.instance DHTRecordPool.instance
.processLocalValueChange(key, decryptedNewValue, subkey); ._processLocalValueChange(key, decryptedNewValue, subkey);
} }
return decryptedNewValue; return decryptedNewValue;
} }
@ -298,7 +296,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
final isUpdated = newValueData.seq != lastSeq; final isUpdated = newValueData.seq != lastSeq;
if (isUpdated) { if (isUpdated) {
DHTRecordPool.instance.processLocalValueChange(key, newValue, subkey); DHTRecordPool.instance._processLocalValueChange(key, newValue, subkey);
} }
} }
@ -419,7 +417,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
// Set up watch requirements which will get picked up by the next tick // Set up watch requirements which will get picked up by the next tick
final oldWatchState = watchState; final oldWatchState = watchState;
watchState = watchState =
WatchState(subkeys: subkeys, expiration: expiration, count: count); _WatchState(subkeys: subkeys, expiration: expiration, count: count);
if (oldWatchState != watchState) { if (oldWatchState != watchState) {
_sharedDHTRecordData.needsWatchStateUpdate = true; _sharedDHTRecordData.needsWatchStateUpdate = true;
} }
@ -544,7 +542,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
final SharedDHTRecordData _sharedDHTRecordData; final _SharedDHTRecordData _sharedDHTRecordData;
final VeilidRoutingContext _routingContext; final VeilidRoutingContext _routingContext;
final int _defaultSubkey; final int _defaultSubkey;
final KeyPair? _writer; final KeyPair? _writer;
@ -554,5 +552,5 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
int _openCount; int _openCount;
StreamController<DHTRecordWatchChange>? _watchController; StreamController<DHTRecordWatchChange>? _watchController;
@internal @internal
WatchState? watchState; _WatchState? watchState;
} }

View File

@ -0,0 +1,77 @@
part of 'dht_record_pool.dart';
const int _watchBackoffMultiplier = 2;
const int _watchBackoffMax = 30;
const int? _defaultWatchDurationSecs = null; // 600
const int _watchRenewalNumerator = 4;
const int _watchRenewalDenominator = 5;
// DHT crypto domain
const String _cryptoDomainDHT = 'dht';
// Singlefuture keys
const _sfPollWatch = '_pollWatch';
const _sfListen = 'listen';
/// Watch state
@immutable
class _WatchState extends Equatable {
const _WatchState(
{required this.subkeys,
required this.expiration,
required this.count,
this.realExpiration,
this.renewalTime});
final List<ValueSubkeyRange>? subkeys;
final Timestamp? expiration;
final int? count;
final Timestamp? realExpiration;
final Timestamp? renewalTime;
@override
List<Object?> get props =>
[subkeys, expiration, count, realExpiration, renewalTime];
}
/// Data shared amongst all DHTRecord instances
class _SharedDHTRecordData {
_SharedDHTRecordData(
{required this.recordDescriptor,
required this.defaultWriter,
required this.defaultRoutingContext});
DHTRecordDescriptor recordDescriptor;
KeyPair? defaultWriter;
VeilidRoutingContext defaultRoutingContext;
bool needsWatchStateUpdate = false;
_WatchState? unionWatchState;
}
// Per opened record data
class _OpenedRecordInfo {
_OpenedRecordInfo(
{required DHTRecordDescriptor recordDescriptor,
required KeyPair? defaultWriter,
required VeilidRoutingContext defaultRoutingContext})
: shared = _SharedDHTRecordData(
recordDescriptor: recordDescriptor,
defaultWriter: defaultWriter,
defaultRoutingContext: defaultRoutingContext);
_SharedDHTRecordData shared;
Set<DHTRecord> records = {};
String get debugNames {
final r = records.toList()
..sort((a, b) => a.key.toString().compareTo(b.key.toString()));
return '[${r.map((x) => x.debugName).join(',')}]';
}
String get details {
final r = records.toList()
..sort((a, b) => a.key.toString().compareTo(b.key.toString()));
return '[${r.map((x) => "writer=${x._writer} "
"defaultSubkey=${x._defaultSubkey}").join(',')}]';
}
String get sharedDetails => shared.toString();
}

View File

@ -8,8 +8,7 @@ import 'package:protobuf/protobuf.dart';
import 'table_db.dart'; import 'table_db.dart';
class PersistentQueue<T extends GeneratedMessage> class PersistentQueue<T extends GeneratedMessage>
/*extends Cubit<AsyncValue<IList<T>>>*/ with with TableDBBackedFromBuffer<IList<T>> {
TableDBBackedFromBuffer<IList<T>> {
// //
PersistentQueue( PersistentQueue(
{required String table, {required String table,