mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
ui cleanup
This commit is contained in:
parent
7b400ed08b
commit
152c8bdff4
@ -4,7 +4,6 @@ import 'package:async_tools/async_tools.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -82,6 +81,16 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
await _sentMessagesCubit?.close();
|
||||
await _rcvdMessagesCubit?.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();
|
||||
}
|
||||
|
||||
@ -292,8 +301,14 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
previousMessage = message;
|
||||
}
|
||||
|
||||
// _sendingMessages = messages;
|
||||
|
||||
// _renderState();
|
||||
|
||||
await _sentMessagesCubit!.operateAppendEventual((writer) =>
|
||||
writer.addAll(messages.map((m) => m.writeToBuffer()).toList()));
|
||||
|
||||
// _sendingMessages = const IList.empty();
|
||||
}
|
||||
|
||||
// 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
|
||||
final sentMessages = _sentMessagesCubit?.state.state.asData?.value;
|
||||
//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 (reconciledMessages == null || sentMessages == null) {
|
||||
@ -329,7 +344,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
// );
|
||||
|
||||
final renderedElements = <RenderStateElement>[];
|
||||
|
||||
final renderedIds = <String>{};
|
||||
for (final m in reconciledMessages.windowElements) {
|
||||
final isLocal =
|
||||
m.content.author.toVeilid() == _accountInfo.identityTypedPublicKey;
|
||||
@ -346,13 +361,22 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
sent: sent,
|
||||
sentOffline: sentOffline,
|
||||
));
|
||||
|
||||
renderedIds.add(m.content.authorUniqueIdString);
|
||||
}
|
||||
for (final m in unsentMessages) {
|
||||
renderedElements.add(RenderStateElement(
|
||||
message: (m.deepCopy())..id = m.timestamp.toBytes(),
|
||||
isLocal: true,
|
||||
));
|
||||
}
|
||||
|
||||
// Render in-flight messages at the bottom
|
||||
// for (final m in _sendingMessages) {
|
||||
// if (renderedIds.contains(m.authorUniqueIdString)) {
|
||||
// continue;
|
||||
// }
|
||||
// renderedElements.add(RenderStateElement(
|
||||
// message: m,
|
||||
// isLocal: true,
|
||||
// sent: true,
|
||||
// sentOffline: true,
|
||||
// ));
|
||||
// }
|
||||
|
||||
// Render the state
|
||||
final messages = renderedElements
|
||||
@ -426,7 +450,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
late final MessageReconciliation _reconciliation;
|
||||
|
||||
late final PersistentQueue<proto.Message> _unsentMessagesQueue;
|
||||
|
||||
// IList<proto.Message> _sendingMessages = const IList.empty();
|
||||
StreamSubscription<DHTLogBusyState<proto.Message>>? _sentSubscription;
|
||||
StreamSubscription<DHTLogBusyState<proto.Message>>? _rcvdSubscription;
|
||||
StreamSubscription<TableDBArrayProtobufBusyState<proto.ReconciledMessage>>?
|
||||
|
@ -8,7 +8,6 @@ import 'package:veilid_support/veilid_support.dart';
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../chat/chat.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
@ -58,9 +57,20 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
final remoteConversationRecordKey =
|
||||
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
|
||||
// if this fails, don't keep retrying, user can try again later
|
||||
await operateWrite((writer) async {
|
||||
await operateWriteEventual((writer) async {
|
||||
// See if we have added this chat already
|
||||
for (var i = 0; i < writer.length; 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
|
||||
await writer.add(chat.writeToBuffer());
|
||||
});
|
||||
@ -110,10 +108,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
Future<void> deleteChat(
|
||||
{required TypedKey localConversationRecordKey}) async {
|
||||
// Remove Chat from account's list
|
||||
// if this fails, don't keep retrying, user can try again later
|
||||
final deletedItem =
|
||||
// Ensure followers get their changes before we return
|
||||
await syncFollowers(() => operateWrite((writer) async {
|
||||
await operateWriteEventual((writer) async {
|
||||
if (_activeChatCubit.state == localConversationRecordKey) {
|
||||
_activeChatCubit.setActiveChat(null);
|
||||
}
|
||||
@ -123,24 +118,12 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
throw Exception('Failed to get chat');
|
||||
}
|
||||
|
||||
if (c.localConversationRecordKey ==
|
||||
localConversationRecordKey) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// StateMapFollowable /////////////////////////
|
||||
|
@ -152,8 +152,7 @@ class ContactInvitationListCubit
|
||||
..message = message;
|
||||
|
||||
// Add ContactInvitationRecord to account's list
|
||||
// if this fails, don't keep retrying, user can try again later
|
||||
await operateWrite((writer) async {
|
||||
await operateWriteEventual((writer) async {
|
||||
await writer.add(cinvrec.writeToBuffer());
|
||||
});
|
||||
});
|
||||
|
@ -46,7 +46,6 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
//final scale = theme.extension<ScaleScheme>()!;
|
||||
final textTheme = theme.textTheme;
|
||||
|
||||
final signedContactInvitationBytesV =
|
||||
@ -58,6 +57,9 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
|
||||
return PopControl(
|
||||
dismissible: !signedContactInvitationBytesV.isLoading,
|
||||
child: Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: const BorderSide(width: 2),
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
backgroundColor: Colors.white,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
@ -90,6 +92,10 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
|
||||
.paddingAll(8),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.copy),
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: Colors.white,
|
||||
side: const BorderSide()),
|
||||
label: Text(translate(
|
||||
'create_invitation_dialog.copy_invitation')),
|
||||
onPressed: () async {
|
||||
|
@ -6,7 +6,6 @@ import 'package:protobuf/protobuf.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../conversation/conversation.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
|
||||
@ -17,8 +16,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
ContactListCubit({
|
||||
required AccountInfo accountInfo,
|
||||
required OwnedDHTRecordPointer contactListRecordPointer,
|
||||
}) : _accountInfo = accountInfo,
|
||||
super(
|
||||
}) : super(
|
||||
open: () =>
|
||||
_open(accountInfo.accountRecordKey, contactListRecordPointer),
|
||||
decodeElement: proto.Contact.fromBuffer);
|
||||
@ -98,8 +96,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
..showAvailability = false;
|
||||
|
||||
// Add Contact to account's list
|
||||
// if this fails, don't keep retrying, user can try again later
|
||||
await operateWrite((writer) async {
|
||||
await operateWriteEventual((writer) async {
|
||||
await writer.add(contact.writeToBuffer());
|
||||
});
|
||||
}
|
||||
@ -107,7 +104,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
Future<void> deleteContact(
|
||||
{required TypedKey localConversationRecordKey}) async {
|
||||
// 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++) {
|
||||
final item = await writer.getProtobuf(proto.Contact.fromBuffer, i);
|
||||
if (item == null) {
|
||||
@ -124,18 +121,11 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
|
||||
if (deletedItem != null) {
|
||||
try {
|
||||
// Make a conversation cubit to manipulate the conversation
|
||||
final conversationCubit = ConversationCubit(
|
||||
accountInfo: _accountInfo,
|
||||
remoteIdentityPublicKey: deletedItem.identityPublicKey.toVeilid(),
|
||||
localConversationRecordKey:
|
||||
deletedItem.localConversationRecordKey.toVeilid(),
|
||||
remoteConversationRecordKey:
|
||||
deletedItem.remoteConversationRecordKey.toVeilid(),
|
||||
);
|
||||
|
||||
// Delete the local and remote conversation records
|
||||
await conversationCubit.delete();
|
||||
// Mark the conversation records for deletion
|
||||
await DHTRecordPool.instance
|
||||
.deleteRecord(deletedItem.localConversationRecordKey.toVeilid());
|
||||
await DHTRecordPool.instance
|
||||
.deleteRecord(deletedItem.remoteConversationRecordKey.toVeilid());
|
||||
} on Exception catch (e) {
|
||||
log.debug('error deleting conversation records: $e', e);
|
||||
}
|
||||
@ -144,5 +134,4 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
|
||||
final _contactProfileUpdateMap =
|
||||
SingleStateProcessorMap<TypedKey, proto.Profile?>();
|
||||
final AccountInfo _accountInfo;
|
||||
}
|
||||
|
@ -74,13 +74,13 @@ class ContactItemWidget extends StatelessWidget {
|
||||
final contactListCubit = context.read<ContactListCubit>();
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
|
||||
// Remove any chats for this contact
|
||||
await chatListCubit.deleteChat(
|
||||
localConversationRecordKey: localConversationRecordKey);
|
||||
|
||||
// Delete the contact itself
|
||||
await contactListCubit.deleteContact(
|
||||
localConversationRecordKey: localConversationRecordKey);
|
||||
|
||||
// Remove any chats for this contact
|
||||
await chatListCubit.deleteChat(
|
||||
localConversationRecordKey: localConversationRecordKey);
|
||||
})
|
||||
],
|
||||
);
|
||||
|
@ -28,7 +28,6 @@ class _SingleContactChatState extends Equatable {
|
||||
final TypedKey remoteMessagesRecordKey;
|
||||
|
||||
@override
|
||||
// TODO: implement props
|
||||
List<Object?> get props => [
|
||||
remoteIdentityPublicKey,
|
||||
localConversationRecordKey,
|
||||
|
@ -14,7 +14,6 @@ import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
|
||||
const _sfUpdateAccountChange = 'updateAccountChange';
|
||||
|
||||
@ -116,7 +115,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final accountRecordKey = _accountInfo.accountRecordKey;
|
||||
final writer = _accountInfo.identityWriter;
|
||||
|
||||
// Open with SMPL scheme for identity writer
|
||||
// Open with SMPL schema for identity writer
|
||||
late final DHTRecord localConversationRecord;
|
||||
if (existingConversationRecordKey != null) {
|
||||
localConversationRecord = await pool.openRecordWrite(
|
||||
@ -171,57 +170,6 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
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
|
||||
Future<void> refresh() async {
|
||||
await _initWait();
|
||||
|
@ -71,11 +71,22 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
shortname = abbrev;
|
||||
}
|
||||
|
||||
final avatar = AvatarImage(
|
||||
size: 32,
|
||||
final avatar = Container(
|
||||
height: 34,
|
||||
width: 34,
|
||||
decoration: BoxDecoration(
|
||||
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));
|
||||
child: Text(shortname, style: theme.textTheme.titleLarge)));
|
||||
|
||||
return AnimatedPadding(
|
||||
padding: EdgeInsets.fromLTRB(selected ? 0 : 0, 0, selected ? 0 : 8, 0),
|
||||
@ -234,6 +245,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
//final textTheme = theme.textTheme;
|
||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||
final perAccountCollectionBlocMapState =
|
||||
@ -271,11 +283,15 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
SvgPicture.asset(
|
||||
height: 48,
|
||||
'assets/images/icon.svg',
|
||||
).paddingLTRB(0, 0, 16, 0),
|
||||
colorFilter: scaleConfig.useVisualIndicators
|
||||
? grayColorFilter
|
||||
: null)
|
||||
.paddingLTRB(0, 0, 16, 0),
|
||||
SvgPicture.asset(
|
||||
height: 48,
|
||||
'assets/images/title.svg',
|
||||
),
|
||||
colorFilter:
|
||||
scaleConfig.useVisualIndicators ? grayColorFilter : null),
|
||||
])),
|
||||
const Spacer(),
|
||||
_getAccountList(
|
||||
|
@ -89,11 +89,9 @@ class ScaleScheme extends ThemeExtension<ScaleScheme> {
|
||||
onError: errorScale.primaryText,
|
||||
// errorContainer: errorScale.hoverElementBackground,
|
||||
// onErrorContainer: errorScale.subtleText,
|
||||
background: grayScale.appBackground, // reviewed
|
||||
onBackground: grayScale.appText, // reviewed
|
||||
surface: primaryScale.primary, // reviewed
|
||||
onSurface: primaryScale.primaryText, // reviewed
|
||||
surfaceVariant: secondaryScale.primary,
|
||||
surfaceContainerHighest: secondaryScale.primary,
|
||||
onSurfaceVariant: secondaryScale.primaryText, // ?? reviewed a little
|
||||
outline: primaryScale.border,
|
||||
outlineVariant: secondaryScale.border,
|
||||
|
@ -5,7 +5,6 @@ import 'package:blurry_modal_progress_hud/blurry_modal_progress_hud.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:motion_toast/motion_toast.dart';
|
||||
import 'package:quickalert/quickalert.dart';
|
||||
|
||||
@ -122,36 +121,45 @@ Future<void> showErrorModal(
|
||||
}
|
||||
|
||||
void showErrorToast(BuildContext context, String message) {
|
||||
MotionToast.error(
|
||||
title: Text(translate('toast.error')),
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
MotionToast(
|
||||
//title: Text(translate('toast.error')),
|
||||
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);
|
||||
}
|
||||
|
||||
void showInfoToast(BuildContext context, String message) {
|
||||
MotionToast.info(
|
||||
title: Text(translate('toast.info')),
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
MotionToast(
|
||||
//title: Text(translate('toast.info')),
|
||||
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);
|
||||
}
|
||||
|
||||
// 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({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
@ -230,3 +238,26 @@ Widget styledBottomSheet({
|
||||
bool get isPlatformDark =>
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
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,
|
||||
]);
|
||||
|
@ -1,7 +1,5 @@
|
||||
part of 'dht_record_pool.dart';
|
||||
|
||||
const _sfListen = 'listen';
|
||||
|
||||
@immutable
|
||||
class DHTRecordWatchChange extends Equatable {
|
||||
const DHTRecordWatchChange(
|
||||
@ -41,7 +39,7 @@ enum DHTRecordRefreshMode {
|
||||
class DHTRecord implements DHTDeleteable<DHTRecord> {
|
||||
DHTRecord._(
|
||||
{required VeilidRoutingContext routingContext,
|
||||
required SharedDHTRecordData sharedDHTRecordData,
|
||||
required _SharedDHTRecordData sharedDHTRecordData,
|
||||
required int defaultSubkey,
|
||||
required KeyPair? writer,
|
||||
required VeilidCrypto crypto,
|
||||
@ -241,7 +239,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
|
||||
// if so, shortcut and don't bother decrypting it
|
||||
if (newValueData.data.equals(encryptedNewValue)) {
|
||||
if (isUpdated) {
|
||||
DHTRecordPool.instance.processLocalValueChange(key, newValue, subkey);
|
||||
DHTRecordPool.instance._processLocalValueChange(key, newValue, subkey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -251,7 +249,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
|
||||
await (crypto ?? _crypto).decrypt(newValueData.data);
|
||||
if (isUpdated) {
|
||||
DHTRecordPool.instance
|
||||
.processLocalValueChange(key, decryptedNewValue, subkey);
|
||||
._processLocalValueChange(key, decryptedNewValue, subkey);
|
||||
}
|
||||
return decryptedNewValue;
|
||||
}
|
||||
@ -298,7 +296,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
|
||||
|
||||
final isUpdated = newValueData.seq != lastSeq;
|
||||
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
|
||||
final oldWatchState = watchState;
|
||||
watchState =
|
||||
WatchState(subkeys: subkeys, expiration: expiration, count: count);
|
||||
_WatchState(subkeys: subkeys, expiration: expiration, count: count);
|
||||
if (oldWatchState != watchState) {
|
||||
_sharedDHTRecordData.needsWatchStateUpdate = true;
|
||||
}
|
||||
@ -544,7 +542,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
final SharedDHTRecordData _sharedDHTRecordData;
|
||||
final _SharedDHTRecordData _sharedDHTRecordData;
|
||||
final VeilidRoutingContext _routingContext;
|
||||
final int _defaultSubkey;
|
||||
final KeyPair? _writer;
|
||||
@ -554,5 +552,5 @@ class DHTRecord implements DHTDeleteable<DHTRecord> {
|
||||
int _openCount;
|
||||
StreamController<DHTRecordWatchChange>? _watchController;
|
||||
@internal
|
||||
WatchState? watchState;
|
||||
_WatchState? watchState;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
}
|
@ -8,8 +8,7 @@ import 'package:protobuf/protobuf.dart';
|
||||
import 'table_db.dart';
|
||||
|
||||
class PersistentQueue<T extends GeneratedMessage>
|
||||
/*extends Cubit<AsyncValue<IList<T>>>*/ with
|
||||
TableDBBackedFromBuffer<IList<T>> {
|
||||
with TableDBBackedFromBuffer<IList<T>> {
|
||||
//
|
||||
PersistentQueue(
|
||||
{required String table,
|
||||
|
Loading…
Reference in New Issue
Block a user