mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-07-23 14:40:58 -04:00
checkpoint
This commit is contained in:
parent
3f8b4d2a41
commit
17211f3515
22 changed files with 701 additions and 353 deletions
|
@ -38,9 +38,23 @@ class PerAccountCollectionBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
Future<void> removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(TypedKey key, LocalAccount value) async {
|
Future<void> updateState(
|
||||||
|
TypedKey key, LocalAccount? oldValue, LocalAccount newValue) async {
|
||||||
|
// Don't replace unless this is a totally different account
|
||||||
|
// The sub-cubit's subscription will update our state later
|
||||||
|
if (oldValue != null) {
|
||||||
|
if (oldValue.superIdentity.recordKey !=
|
||||||
|
newValue.superIdentity.recordKey) {
|
||||||
|
throw StateError(
|
||||||
|
'should remove LocalAccount and make a new one, not change it, if '
|
||||||
|
'the superidentity record key has changed');
|
||||||
|
}
|
||||||
|
// This never changes anything that should result in rebuildin the
|
||||||
|
// sub-cubit
|
||||||
|
return;
|
||||||
|
}
|
||||||
await _addPerAccountCollectionCubit(
|
await _addPerAccountCollectionCubit(
|
||||||
superIdentityRecordKey: value.superIdentity.recordKey);
|
superIdentityRecordKey: newValue.superIdentity.recordKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -136,14 +136,16 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||||
: (accountInfo, contactListRecordPointer));
|
: (accountInfo, contactListRecordPointer));
|
||||||
|
|
||||||
// WaitingInvitationsBlocMapCubit
|
// WaitingInvitationsBlocMapCubit
|
||||||
final waitingInvitationsBlocMapCubit =
|
final waitingInvitationsBlocMapCubit = waitingInvitationsBlocMapCubitUpdater
|
||||||
waitingInvitationsBlocMapCubitUpdater.update(
|
.update(accountInfo.userLogin == null ||
|
||||||
accountInfo.userLogin == null || contactInvitationListCubit == null
|
contactInvitationListCubit == null ||
|
||||||
|
contactListCubit == null
|
||||||
? null
|
? null
|
||||||
: (
|
: (
|
||||||
accountInfo,
|
accountInfo,
|
||||||
accountRecordCubit!,
|
accountRecordCubit!,
|
||||||
contactInvitationListCubit
|
contactInvitationListCubit,
|
||||||
|
contactListCubit,
|
||||||
));
|
));
|
||||||
|
|
||||||
// ActiveChatCubit
|
// ActiveChatCubit
|
||||||
|
@ -179,15 +181,11 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||||
final activeSingleContactChatBlocMapCubit =
|
final activeSingleContactChatBlocMapCubit =
|
||||||
activeSingleContactChatBlocMapCubitUpdater.update(
|
activeSingleContactChatBlocMapCubitUpdater.update(
|
||||||
accountInfo.userLogin == null ||
|
accountInfo.userLogin == null ||
|
||||||
activeConversationsBlocMapCubit == null ||
|
activeConversationsBlocMapCubit == null
|
||||||
chatListCubit == null ||
|
|
||||||
contactListCubit == null
|
|
||||||
? null
|
? null
|
||||||
: (
|
: (
|
||||||
accountInfo,
|
accountInfo,
|
||||||
activeConversationsBlocMapCubit,
|
activeConversationsBlocMapCubit,
|
||||||
chatListCubit,
|
|
||||||
contactListCubit
|
|
||||||
));
|
));
|
||||||
|
|
||||||
// Update available blocs in our state
|
// Update available blocs in our state
|
||||||
|
@ -260,11 +258,18 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||||
));
|
));
|
||||||
final waitingInvitationsBlocMapCubitUpdater = BlocUpdater<
|
final waitingInvitationsBlocMapCubitUpdater = BlocUpdater<
|
||||||
WaitingInvitationsBlocMapCubit,
|
WaitingInvitationsBlocMapCubit,
|
||||||
(AccountInfo, AccountRecordCubit, ContactInvitationListCubit)>(
|
(
|
||||||
|
AccountInfo,
|
||||||
|
AccountRecordCubit,
|
||||||
|
ContactInvitationListCubit,
|
||||||
|
ContactListCubit
|
||||||
|
)>(
|
||||||
create: (params) => WaitingInvitationsBlocMapCubit(
|
create: (params) => WaitingInvitationsBlocMapCubit(
|
||||||
accountInfo: params.$1,
|
accountInfo: params.$1,
|
||||||
accountRecordCubit: params.$2,
|
accountRecordCubit: params.$2,
|
||||||
contactInvitationListCubit: params.$3));
|
contactInvitationListCubit: params.$3,
|
||||||
|
contactListCubit: params.$4,
|
||||||
|
));
|
||||||
final activeChatCubitUpdater =
|
final activeChatCubitUpdater =
|
||||||
BlocUpdater<ActiveChatCubit, bool>(create: (_) => ActiveChatCubit(null));
|
BlocUpdater<ActiveChatCubit, bool>(create: (_) => ActiveChatCubit(null));
|
||||||
final chatListCubitUpdater = BlocUpdater<ChatListCubit,
|
final chatListCubitUpdater = BlocUpdater<ChatListCubit,
|
||||||
|
@ -286,13 +291,9 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||||
(
|
(
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
ActiveConversationsBlocMapCubit,
|
ActiveConversationsBlocMapCubit,
|
||||||
ChatListCubit,
|
|
||||||
ContactListCubit
|
|
||||||
)>(
|
)>(
|
||||||
create: (params) => ActiveSingleContactChatBlocMapCubit(
|
create: (params) => ActiveSingleContactChatBlocMapCubit(
|
||||||
accountInfo: params.$1,
|
accountInfo: params.$1,
|
||||||
activeConversationsBlocMapCubit: params.$2,
|
activeConversationsBlocMapCubit: params.$2,
|
||||||
chatListCubit: params.$3,
|
|
||||||
contactListCubit: params.$4,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,14 @@ class _EditAccountPageState extends State<EditAccountPage> {
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
// Look up account cubit for this specific account
|
// Look up account cubit for this specific account
|
||||||
final accountRecordCubit = context.read<AccountRecordCubit>();
|
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
|
// Update account profile DHT record
|
||||||
// This triggers ConversationCubits to update
|
// This triggers ConversationCubits to update
|
||||||
|
|
|
@ -12,6 +12,7 @@ import 'package:scroll_to_index/scroll_to_index.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 '../../contacts/contacts.dart';
|
||||||
import '../../conversation/conversation.dart';
|
import '../../conversation/conversation.dart';
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import '../models/chat_component_state.dart';
|
import '../models/chat_component_state.dart';
|
||||||
|
@ -28,10 +29,12 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||||
ChatComponentCubit._({
|
ChatComponentCubit._({
|
||||||
required AccountInfo accountInfo,
|
required AccountInfo accountInfo,
|
||||||
required AccountRecordCubit accountRecordCubit,
|
required AccountRecordCubit accountRecordCubit,
|
||||||
|
required ContactListCubit contactListCubit,
|
||||||
required List<ActiveConversationCubit> conversationCubits,
|
required List<ActiveConversationCubit> conversationCubits,
|
||||||
required SingleContactMessagesCubit messagesCubit,
|
required SingleContactMessagesCubit messagesCubit,
|
||||||
}) : _accountInfo = accountInfo,
|
}) : _accountInfo = accountInfo,
|
||||||
_accountRecordCubit = accountRecordCubit,
|
_accountRecordCubit = accountRecordCubit,
|
||||||
|
_contactListCubit = contactListCubit,
|
||||||
_conversationCubits = conversationCubits,
|
_conversationCubits = conversationCubits,
|
||||||
_messagesCubit = messagesCubit,
|
_messagesCubit = messagesCubit,
|
||||||
super(ChatComponentState(
|
super(ChatComponentState(
|
||||||
|
@ -51,11 +54,13 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||||
factory ChatComponentCubit.singleContact(
|
factory ChatComponentCubit.singleContact(
|
||||||
{required AccountInfo accountInfo,
|
{required AccountInfo accountInfo,
|
||||||
required AccountRecordCubit accountRecordCubit,
|
required AccountRecordCubit accountRecordCubit,
|
||||||
|
required ContactListCubit contactListCubit,
|
||||||
required ActiveConversationCubit activeConversationCubit,
|
required ActiveConversationCubit activeConversationCubit,
|
||||||
required SingleContactMessagesCubit messagesCubit}) =>
|
required SingleContactMessagesCubit messagesCubit}) =>
|
||||||
ChatComponentCubit._(
|
ChatComponentCubit._(
|
||||||
accountInfo: accountInfo,
|
accountInfo: accountInfo,
|
||||||
accountRecordCubit: accountRecordCubit,
|
accountRecordCubit: accountRecordCubit,
|
||||||
|
contactListCubit: contactListCubit,
|
||||||
conversationCubits: [activeConversationCubit],
|
conversationCubits: [activeConversationCubit],
|
||||||
messagesCubit: messagesCubit,
|
messagesCubit: messagesCubit,
|
||||||
);
|
);
|
||||||
|
@ -82,6 +87,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
await _accountRecordSubscription.cancel();
|
await _accountRecordSubscription.cancel();
|
||||||
await _messagesSubscription.cancel();
|
await _messagesSubscription.cancel();
|
||||||
|
await _conversationSubscriptions.values.map((v) => v.cancel()).wait;
|
||||||
await super.close();
|
await super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +152,12 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||||
// Private Implementation
|
// Private Implementation
|
||||||
|
|
||||||
void _onChangedAccountRecord(AsyncValue<proto.Account> avAccount) {
|
void _onChangedAccountRecord(AsyncValue<proto.Account> avAccount) {
|
||||||
|
// Update local 'User'
|
||||||
final account = avAccount.asData?.value;
|
final account = avAccount.asData?.value;
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
emit(state.copyWith(localUser: null));
|
emit(state.copyWith(localUser: null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Make local 'User'
|
|
||||||
final localUser = types.User(
|
final localUser = types.User(
|
||||||
id: _localUserIdentityKey.toString(),
|
id: _localUserIdentityKey.toString(),
|
||||||
firstName: account.profile.name,
|
firstName: account.profile.name,
|
||||||
|
@ -168,15 +174,40 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||||
TypedKey remoteIdentityPublicKey,
|
TypedKey remoteIdentityPublicKey,
|
||||||
AsyncValue<ActiveConversationState> avConversationState,
|
AsyncValue<ActiveConversationState> avConversationState,
|
||||||
) {
|
) {
|
||||||
//
|
// Update remote 'User'
|
||||||
|
final activeConversationState = avConversationState.asData?.value;
|
||||||
|
if (activeConversationState == null) {
|
||||||
|
// Don't change user information on loading state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit(state.copyWith(
|
||||||
|
remoteUsers: state.remoteUsers.add(
|
||||||
|
remoteIdentityPublicKey,
|
||||||
|
_convertRemoteUser(
|
||||||
|
remoteIdentityPublicKey, activeConversationState))));
|
||||||
}
|
}
|
||||||
|
|
||||||
types.User _convertRemoteUser(TypedKey remoteIdentityPublicKey,
|
types.User _convertRemoteUser(TypedKey remoteIdentityPublicKey,
|
||||||
ActiveConversationState activeConversationState) =>
|
ActiveConversationState activeConversationState) {
|
||||||
types.User(
|
// See if we have a contact for this remote user
|
||||||
|
final contacts = _contactListCubit.state.state.asData?.value;
|
||||||
|
if (contacts != null) {
|
||||||
|
final contactIdx = contacts.indexWhere((x) =>
|
||||||
|
x.value.identityPublicKey.toVeilid() == remoteIdentityPublicKey);
|
||||||
|
if (contactIdx != -1) {
|
||||||
|
final contact = contacts[contactIdx].value;
|
||||||
|
return types.User(
|
||||||
id: remoteIdentityPublicKey.toString(),
|
id: remoteIdentityPublicKey.toString(),
|
||||||
firstName: activeConversationState.contact.displayName,
|
firstName: contact.displayName,
|
||||||
metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey});
|
metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.User(
|
||||||
|
id: remoteIdentityPublicKey.toString(),
|
||||||
|
firstName: activeConversationState.remoteConversation.profile.name,
|
||||||
|
metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey});
|
||||||
|
}
|
||||||
|
|
||||||
types.User _convertUnknownUser(TypedKey remoteIdentityPublicKey) =>
|
types.User _convertUnknownUser(TypedKey remoteIdentityPublicKey) =>
|
||||||
types.User(
|
types.User(
|
||||||
|
@ -376,6 +407,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||||
final _initWait = WaitSet<void>();
|
final _initWait = WaitSet<void>();
|
||||||
final AccountInfo _accountInfo;
|
final AccountInfo _accountInfo;
|
||||||
final AccountRecordCubit _accountRecordCubit;
|
final AccountRecordCubit _accountRecordCubit;
|
||||||
|
final ContactListCubit _contactListCubit;
|
||||||
final List<ActiveConversationCubit> _conversationCubits;
|
final List<ActiveConversationCubit> _conversationCubits;
|
||||||
final SingleContactMessagesCubit _messagesCubit;
|
final SingleContactMessagesCubit _messagesCubit;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:flutter_chat_ui/flutter_chat_ui.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 '../../contacts/contacts.dart';
|
||||||
import '../../conversation/conversation.dart';
|
import '../../conversation/conversation.dart';
|
||||||
import '../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
import '../chat.dart';
|
import '../chat.dart';
|
||||||
|
@ -28,10 +29,13 @@ class ChatComponentWidget extends StatelessWidget {
|
||||||
// Get the account record cubit
|
// Get the account record cubit
|
||||||
final accountRecordCubit = context.read<AccountRecordCubit>();
|
final accountRecordCubit = context.read<AccountRecordCubit>();
|
||||||
|
|
||||||
|
// Get the contact list cubit
|
||||||
|
final contactListCubit = context.watch<ContactListCubit>();
|
||||||
|
|
||||||
// Get the active conversation cubit
|
// Get the active conversation cubit
|
||||||
final activeConversationCubit = context
|
final activeConversationCubit = context
|
||||||
.select<ActiveConversationsBlocMapCubit, ActiveConversationCubit?>(
|
.select<ActiveConversationsBlocMapCubit, ActiveConversationCubit?>(
|
||||||
(x) => x.tryOperate(localConversationRecordKey,
|
(x) => x.tryOperateSync(localConversationRecordKey,
|
||||||
closure: (cubit) => cubit));
|
closure: (cubit) => cubit));
|
||||||
if (activeConversationCubit == null) {
|
if (activeConversationCubit == null) {
|
||||||
return waitingPage();
|
return waitingPage();
|
||||||
|
@ -41,7 +45,7 @@ class ChatComponentWidget extends StatelessWidget {
|
||||||
final messagesCubit = context.select<
|
final messagesCubit = context.select<
|
||||||
ActiveSingleContactChatBlocMapCubit,
|
ActiveSingleContactChatBlocMapCubit,
|
||||||
SingleContactMessagesCubit?>(
|
SingleContactMessagesCubit?>(
|
||||||
(x) => x.tryOperate(localConversationRecordKey,
|
(x) => x.tryOperateSync(localConversationRecordKey,
|
||||||
closure: (cubit) => cubit));
|
closure: (cubit) => cubit));
|
||||||
if (messagesCubit == null) {
|
if (messagesCubit == null) {
|
||||||
return waitingPage();
|
return waitingPage();
|
||||||
|
@ -49,9 +53,11 @@ class ChatComponentWidget extends StatelessWidget {
|
||||||
|
|
||||||
// Make chat component state
|
// Make chat component state
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
|
key: key,
|
||||||
create: (context) => ChatComponentCubit.singleContact(
|
create: (context) => ChatComponentCubit.singleContact(
|
||||||
accountInfo: accountInfo,
|
accountInfo: accountInfo,
|
||||||
accountRecordCubit: accountRecordCubit,
|
accountRecordCubit: accountRecordCubit,
|
||||||
|
contactListCubit: contactListCubit,
|
||||||
activeConversationCubit: activeConversationCubit,
|
activeConversationCubit: activeConversationCubit,
|
||||||
messagesCubit: messagesCubit,
|
messagesCubit: messagesCubit,
|
||||||
),
|
),
|
||||||
|
|
|
@ -54,6 +54,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||||
// Make local copy so we don't share the buffer
|
// Make local copy so we don't share the buffer
|
||||||
final localConversationRecordKey =
|
final localConversationRecordKey =
|
||||||
contact.localConversationRecordKey.toVeilid();
|
contact.localConversationRecordKey.toVeilid();
|
||||||
|
final remoteIdentityPublicKey = contact.identityPublicKey.toVeilid();
|
||||||
final remoteConversationRecordKey =
|
final remoteConversationRecordKey =
|
||||||
contact.remoteConversationRecordKey.toVeilid();
|
contact.remoteConversationRecordKey.toVeilid();
|
||||||
|
|
||||||
|
@ -67,18 +68,38 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||||
throw Exception('Failed to get chat');
|
throw Exception('Failed to get chat');
|
||||||
}
|
}
|
||||||
final c = proto.Chat.fromBuffer(cbuf);
|
final c = proto.Chat.fromBuffer(cbuf);
|
||||||
if (c.localConversationRecordKey ==
|
|
||||||
|
switch (c.whichKind()) {
|
||||||
|
case proto.Chat_Kind.direct:
|
||||||
|
if (c.direct.localConversationRecordKey ==
|
||||||
contact.localConversationRecordKey) {
|
contact.localConversationRecordKey) {
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case proto.Chat_Kind.group:
|
||||||
|
if (c.group.localConversationRecordKey ==
|
||||||
|
contact.localConversationRecordKey) {
|
||||||
|
throw StateError('direct conversation record key should'
|
||||||
|
' not be used for group chats!');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case proto.Chat_Kind.notSet:
|
||||||
|
throw StateError('unknown chat kind');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 1:1 conversation type Chat
|
// Create 1:1 conversation type Chat
|
||||||
final chat = proto.Chat()
|
final chatMember = proto.ChatMember()
|
||||||
|
..remoteIdentityPublicKey = remoteIdentityPublicKey.toProto()
|
||||||
|
..remoteConversationRecordKey = remoteConversationRecordKey.toProto();
|
||||||
|
|
||||||
|
final directChat = proto.DirectChat()
|
||||||
..settings = await getDefaultChatSettings(contact)
|
..settings = await getDefaultChatSettings(contact)
|
||||||
..localConversationRecordKey = localConversationRecordKey.toProto()
|
..localConversationRecordKey = localConversationRecordKey.toProto()
|
||||||
..remoteConversationRecordKey = remoteConversationRecordKey.toProto();
|
..remoteMember = chatMember;
|
||||||
|
|
||||||
|
final chat = proto.Chat()..direct = directChat;
|
||||||
|
|
||||||
// Add chat
|
// Add chat
|
||||||
await writer.add(chat.writeToBuffer());
|
await writer.add(chat.writeToBuffer());
|
||||||
|
@ -88,9 +109,6 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||||
/// Delete a chat
|
/// Delete a chat
|
||||||
Future<void> deleteChat(
|
Future<void> deleteChat(
|
||||||
{required TypedKey localConversationRecordKey}) async {
|
{required TypedKey localConversationRecordKey}) async {
|
||||||
final localConversationRecordKeyProto =
|
|
||||||
localConversationRecordKey.toProto();
|
|
||||||
|
|
||||||
// Remove Chat from account's list
|
// Remove Chat from account's list
|
||||||
// if this fails, don't keep retrying, user can try again later
|
// if this fails, don't keep retrying, user can try again later
|
||||||
final deletedItem =
|
final deletedItem =
|
||||||
|
@ -104,9 +122,9 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
throw Exception('Failed to get chat');
|
throw Exception('Failed to get chat');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.localConversationRecordKey ==
|
if (c.localConversationRecordKey ==
|
||||||
localConversationRecordKeyProto) {
|
localConversationRecordKey) {
|
||||||
// Found the right chat
|
|
||||||
await writer.remove(i);
|
await writer.remove(i);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +151,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||||
return IMap();
|
return IMap();
|
||||||
}
|
}
|
||||||
return IMap.fromIterable(stateValue,
|
return IMap.fromIterable(stateValue,
|
||||||
keyMapper: (e) => e.value.localConversationRecordKey.toVeilid(),
|
keyMapper: (e) => e.value.localConversationRecordKey,
|
||||||
valueMapper: (e) => e.value);
|
valueMapper: (e) => e.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
96
lib/chat_list/views/chat_list_widget.dart
Normal file
96
lib/chat_list/views/chat_list_widget.dart
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
import 'package:searchable_listview/searchable_listview.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
|
import '../../contacts/contacts.dart';
|
||||||
|
import '../../proto/proto.dart' as proto;
|
||||||
|
import '../../proto/proto.dart';
|
||||||
|
import '../../theme/theme.dart';
|
||||||
|
import '../chat_list.dart';
|
||||||
|
|
||||||
|
class ChatListWidget extends StatelessWidget {
|
||||||
|
const ChatListWidget({super.key});
|
||||||
|
|
||||||
|
Widget _itemBuilderDirect(proto.DirectChat direct,
|
||||||
|
IMap<proto.TypedKey, proto.Contact> contactMap, bool busy) {
|
||||||
|
final contact = contactMap[direct.localConversationRecordKey];
|
||||||
|
if (contact == null) {
|
||||||
|
return const Text('...');
|
||||||
|
}
|
||||||
|
return ChatSingleContactItemWidget(contact: contact, disabled: busy)
|
||||||
|
.paddingLTRB(0, 4, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<proto.Chat> _itemFilter(IMap<proto.TypedKey, proto.Contact> contactMap,
|
||||||
|
IList<DHTShortArrayElementState<Chat>> chatList, String filter) {
|
||||||
|
final lowerValue = filter.toLowerCase();
|
||||||
|
return chatList.map((x) => x.value).where((c) {
|
||||||
|
switch (c.whichKind()) {
|
||||||
|
case proto.Chat_Kind.direct:
|
||||||
|
final contact = contactMap[c.direct.localConversationRecordKey];
|
||||||
|
if (contact == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return contact.nickname.toLowerCase().contains(lowerValue) ||
|
||||||
|
contact.profile.name.toLowerCase().contains(lowerValue) ||
|
||||||
|
contact.profile.pronouns.toLowerCase().contains(lowerValue);
|
||||||
|
case proto.Chat_Kind.group:
|
||||||
|
// xxx: how to filter group chats
|
||||||
|
return true;
|
||||||
|
case proto.Chat_Kind.notSet:
|
||||||
|
throw StateError('unknown chat kind');
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: prefer_expression_function_bodies
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final contactListV = context.watch<ContactListCubit>().state;
|
||||||
|
|
||||||
|
return contactListV.builder((context, contactList) {
|
||||||
|
final contactMap = IMap.fromIterable(contactList,
|
||||||
|
keyMapper: (c) => c.value.localConversationRecordKey,
|
||||||
|
valueMapper: (c) => c.value);
|
||||||
|
|
||||||
|
final chatListV = context.watch<ChatListCubit>().state;
|
||||||
|
return chatListV
|
||||||
|
.builder((context, chatList) => SizedBox.expand(
|
||||||
|
child: styledTitleContainer(
|
||||||
|
context: context,
|
||||||
|
title: translate('chat_list.chats'),
|
||||||
|
child: SizedBox.expand(
|
||||||
|
child: (chatList.isEmpty)
|
||||||
|
? const EmptyChatListWidget()
|
||||||
|
: SearchableList<proto.Chat>(
|
||||||
|
initialList: chatList.map((x) => x.value).toList(),
|
||||||
|
itemBuilder: (c) {
|
||||||
|
switch (c.whichKind()) {
|
||||||
|
case proto.Chat_Kind.direct:
|
||||||
|
return _itemBuilderDirect(
|
||||||
|
c.direct,
|
||||||
|
contactMap,
|
||||||
|
contactListV.busy || chatListV.busy);
|
||||||
|
case proto.Chat_Kind.group:
|
||||||
|
return const Text(
|
||||||
|
'group chats not yet supported!');
|
||||||
|
case proto.Chat_Kind.notSet:
|
||||||
|
throw StateError('unknown chat kind');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filter: (value) =>
|
||||||
|
_itemFilter(contactMap, chatList, value),
|
||||||
|
spaceBetweenSearchAndList: 4,
|
||||||
|
inputDecoration: InputDecoration(
|
||||||
|
labelText: translate('chat_list.search'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).paddingAll(8))))
|
||||||
|
.paddingLTRB(8, 0, 8, 8);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,76 +0,0 @@
|
||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
|
||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
|
||||||
import 'package:searchable_listview/searchable_listview.dart';
|
|
||||||
|
|
||||||
import '../../contacts/contacts.dart';
|
|
||||||
import '../../proto/proto.dart' as proto;
|
|
||||||
import '../../theme/theme.dart';
|
|
||||||
import '../chat_list.dart';
|
|
||||||
|
|
||||||
class ChatSingleContactListWidget extends StatelessWidget {
|
|
||||||
const ChatSingleContactListWidget({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
// ignore: prefer_expression_function_bodies
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final contactListV = context.watch<ContactListCubit>().state;
|
|
||||||
|
|
||||||
return contactListV.builder((context, contactList) {
|
|
||||||
final contactMap = IMap.fromIterable(contactList,
|
|
||||||
keyMapper: (c) => c.value.localConversationRecordKey,
|
|
||||||
valueMapper: (c) => c.value);
|
|
||||||
|
|
||||||
final chatListV = context.watch<ChatListCubit>().state;
|
|
||||||
return chatListV
|
|
||||||
.builder((context, chatList) => SizedBox.expand(
|
|
||||||
child: styledTitleContainer(
|
|
||||||
context: context,
|
|
||||||
title: translate('chat_list.chats'),
|
|
||||||
child: SizedBox.expand(
|
|
||||||
child: (chatList.isEmpty)
|
|
||||||
? const EmptyChatListWidget()
|
|
||||||
: SearchableList<proto.Chat>(
|
|
||||||
initialList: chatList.map((x) => x.value).toList(),
|
|
||||||
itemBuilder: (c) {
|
|
||||||
final contact =
|
|
||||||
contactMap[c.localConversationRecordKey];
|
|
||||||
if (contact == null) {
|
|
||||||
return const Text('...');
|
|
||||||
}
|
|
||||||
return ChatSingleContactItemWidget(
|
|
||||||
contact: contact,
|
|
||||||
disabled: contactListV.busy)
|
|
||||||
.paddingLTRB(0, 4, 0, 0);
|
|
||||||
},
|
|
||||||
filter: (value) {
|
|
||||||
final lowerValue = value.toLowerCase();
|
|
||||||
return chatList.map((x) => x.value).where((c) {
|
|
||||||
final contact =
|
|
||||||
contactMap[c.localConversationRecordKey];
|
|
||||||
if (contact == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return contact.nickname
|
|
||||||
.toLowerCase()
|
|
||||||
.contains(lowerValue) ||
|
|
||||||
contact.profile.name
|
|
||||||
.toLowerCase()
|
|
||||||
.contains(lowerValue) ||
|
|
||||||
contact.profile.pronouns
|
|
||||||
.toLowerCase()
|
|
||||||
.contains(lowerValue);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
spaceBetweenSearchAndList: 4,
|
|
||||||
inputDecoration: InputDecoration(
|
|
||||||
labelText: translate('chat_list.search'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).paddingAll(8))))
|
|
||||||
.paddingLTRB(8, 0, 8, 8);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,3 @@
|
||||||
|
export 'chat_list_widget.dart';
|
||||||
export 'chat_single_contact_item_widget.dart';
|
export 'chat_single_contact_item_widget.dart';
|
||||||
export 'chat_single_contact_list_widget.dart';
|
|
||||||
export 'empty_chat_list_widget.dart';
|
export 'empty_chat_list_widget.dart';
|
||||||
|
|
|
@ -59,8 +59,11 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
||||||
// Verify
|
// Verify
|
||||||
final idcs = await contactSuperIdentity.currentInstance.cryptoSystem;
|
final idcs = await contactSuperIdentity.currentInstance.cryptoSystem;
|
||||||
final signature = signedContactResponse.identitySignature.toVeilid();
|
final signature = signedContactResponse.identitySignature.toVeilid();
|
||||||
await idcs.verify(contactSuperIdentity.currentInstance.publicKey,
|
if (!await idcs.verify(contactSuperIdentity.currentInstance.publicKey,
|
||||||
contactResponseBytes, signature);
|
contactResponseBytes, signature)) {
|
||||||
|
// Could not verify signature of contact response
|
||||||
|
return AsyncValue.error('Invalid signature on contact response.');
|
||||||
|
}
|
||||||
|
|
||||||
// Check for rejection
|
// Check for rejection
|
||||||
if (!contactResponse.accept) {
|
if (!contactResponse.accept) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:bloc_advanced_tools/bloc_advanced_tools.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 '../../contacts/contacts.dart';
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import 'cubits.dart';
|
import 'cubits.dart';
|
||||||
|
|
||||||
|
@ -20,13 +21,26 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
WaitingInvitationsBlocMapCubit(
|
WaitingInvitationsBlocMapCubit(
|
||||||
{required AccountInfo accountInfo,
|
{required AccountInfo accountInfo,
|
||||||
required AccountRecordCubit accountRecordCubit,
|
required AccountRecordCubit accountRecordCubit,
|
||||||
required ContactInvitationListCubit contactInvitationListCubit})
|
required ContactInvitationListCubit contactInvitationListCubit,
|
||||||
|
required ContactListCubit contactListCubit})
|
||||||
: _accountInfo = accountInfo,
|
: _accountInfo = accountInfo,
|
||||||
_accountRecordCubit = accountRecordCubit {
|
_accountRecordCubit = accountRecordCubit,
|
||||||
|
_contactInvitationListCubit = contactInvitationListCubit,
|
||||||
|
_contactListCubit = contactListCubit {
|
||||||
|
// React to invitation status changes
|
||||||
|
_singleInvitationStatusProcessor.follow(
|
||||||
|
stream, state, _invitationStatusListener);
|
||||||
|
|
||||||
// Follow the contact invitation list cubit
|
// Follow the contact invitation list cubit
|
||||||
follow(contactInvitationListCubit);
|
follow(contactInvitationListCubit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _singleInvitationStatusProcessor.unfollow();
|
||||||
|
await super.close();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _addWaitingInvitation(
|
Future<void> _addWaitingInvitation(
|
||||||
{required proto.ContactInvitationRecord
|
{required proto.ContactInvitationRecord
|
||||||
contactInvitationRecord}) async =>
|
contactInvitationRecord}) async =>
|
||||||
|
@ -40,16 +54,60 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
accountRecordCubit: _accountRecordCubit,
|
accountRecordCubit: _accountRecordCubit,
|
||||||
contactInvitationRecord: contactInvitationRecord)));
|
contactInvitationRecord: contactInvitationRecord)));
|
||||||
|
|
||||||
|
// Process all accepted or rejected invitations
|
||||||
|
Future<void> _invitationStatusListener(
|
||||||
|
WaitingInvitationsBlocMapState newState) async {
|
||||||
|
for (final entry in newState.entries) {
|
||||||
|
final contactRequestInboxRecordKey = entry.key;
|
||||||
|
final invStatus = entry.value.asData?.value;
|
||||||
|
// Skip invitations that have not yet been accepted or rejected
|
||||||
|
if (invStatus == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete invitation and process the accepted or rejected contact
|
||||||
|
final acceptedContact = invStatus.acceptedContact;
|
||||||
|
if (acceptedContact != null) {
|
||||||
|
await _contactInvitationListCubit.deleteInvitation(
|
||||||
|
accepted: true,
|
||||||
|
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
|
||||||
|
|
||||||
|
// Accept
|
||||||
|
await _contactListCubit.createContact(
|
||||||
|
profile: acceptedContact.remoteProfile,
|
||||||
|
remoteSuperIdentity: acceptedContact.remoteIdentity,
|
||||||
|
remoteConversationRecordKey:
|
||||||
|
acceptedContact.remoteConversationRecordKey,
|
||||||
|
localConversationRecordKey:
|
||||||
|
acceptedContact.localConversationRecordKey,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Reject
|
||||||
|
await _contactInvitationListCubit.deleteInvitation(
|
||||||
|
accepted: false,
|
||||||
|
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
Future<void> removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(TypedKey key, proto.ContactInvitationRecord value) =>
|
Future<void> updateState(
|
||||||
_addWaitingInvitation(contactInvitationRecord: value);
|
TypedKey key,
|
||||||
|
proto.ContactInvitationRecord? oldValue,
|
||||||
|
proto.ContactInvitationRecord newValue) async {
|
||||||
|
await _addWaitingInvitation(contactInvitationRecord: newValue);
|
||||||
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
final AccountInfo _accountInfo;
|
final AccountInfo _accountInfo;
|
||||||
final AccountRecordCubit _accountRecordCubit;
|
final AccountRecordCubit _accountRecordCubit;
|
||||||
|
final ContactInvitationListCubit _contactInvitationListCubit;
|
||||||
|
final ContactListCubit _contactListCubit;
|
||||||
|
final _singleInvitationStatusProcessor =
|
||||||
|
SingleStateProcessor<WaitingInvitationsBlocMapState>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,27 @@ import '../conversation.dart';
|
||||||
@immutable
|
@immutable
|
||||||
class ActiveConversationState extends Equatable {
|
class ActiveConversationState extends Equatable {
|
||||||
const ActiveConversationState({
|
const ActiveConversationState({
|
||||||
required this.contact,
|
required this.remoteIdentityPublicKey,
|
||||||
|
required this.localConversationRecordKey,
|
||||||
|
required this.remoteConversationRecordKey,
|
||||||
required this.localConversation,
|
required this.localConversation,
|
||||||
required this.remoteConversation,
|
required this.remoteConversation,
|
||||||
});
|
});
|
||||||
|
|
||||||
final proto.Contact contact;
|
final TypedKey remoteIdentityPublicKey;
|
||||||
|
final TypedKey localConversationRecordKey;
|
||||||
|
final TypedKey remoteConversationRecordKey;
|
||||||
final proto.Conversation localConversation;
|
final proto.Conversation localConversation;
|
||||||
final proto.Conversation remoteConversation;
|
final proto.Conversation remoteConversation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [contact, localConversation, remoteConversation];
|
List<Object?> get props => [
|
||||||
|
remoteIdentityPublicKey,
|
||||||
|
localConversationRecordKey,
|
||||||
|
remoteConversationRecordKey,
|
||||||
|
localConversation,
|
||||||
|
remoteConversation
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ActiveConversationCubit = TransformerCubit<
|
typedef ActiveConversationCubit = TransformerCubit<
|
||||||
|
@ -37,9 +47,11 @@ typedef ActiveConversationsBlocMapState
|
||||||
// Map of localConversationRecordKey to ActiveConversationCubit
|
// Map of localConversationRecordKey to ActiveConversationCubit
|
||||||
// Wraps a conversation cubit to only expose completely built conversations
|
// Wraps a conversation cubit to only expose completely built conversations
|
||||||
// Automatically follows the state of a ChatListCubit.
|
// Automatically follows the state of a ChatListCubit.
|
||||||
// Even though 'conversations' are per-contact and not per-chat
|
|
||||||
// We currently only build the cubits for the chats that are active, not
|
// We currently only build the cubits for the chats that are active, not
|
||||||
// archived chats or contacts that are not actively in a chat.
|
// archived chats or contacts that are not actively in a chat.
|
||||||
|
//
|
||||||
|
// TODO: Polling contacts for new inactive chats is yet to be done
|
||||||
|
//
|
||||||
class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
AsyncValue<ActiveConversationState>, ActiveConversationCubit>
|
AsyncValue<ActiveConversationState>, ActiveConversationCubit>
|
||||||
with StateMapFollower<ChatListCubitState, TypedKey, proto.Chat> {
|
with StateMapFollower<ChatListCubitState, TypedKey, proto.Chat> {
|
||||||
|
@ -62,14 +74,11 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
// Private Implementation
|
// Private Implementation
|
||||||
|
|
||||||
// Add an active conversation to be tracked for changes
|
// Add an active conversation to be tracked for changes
|
||||||
Future<void> _addConversation({required proto.Contact contact}) async =>
|
Future<void> _addDirectConversation(
|
||||||
|
{required TypedKey remoteIdentityPublicKey,
|
||||||
|
required TypedKey localConversationRecordKey,
|
||||||
|
required TypedKey remoteConversationRecordKey}) async =>
|
||||||
add(() {
|
add(() {
|
||||||
final remoteIdentityPublicKey = contact.identityPublicKey.toVeilid();
|
|
||||||
final localConversationRecordKey =
|
|
||||||
contact.localConversationRecordKey.toVeilid();
|
|
||||||
final remoteConversationRecordKey =
|
|
||||||
contact.remoteConversationRecordKey.toVeilid();
|
|
||||||
|
|
||||||
// Conversation cubit the tracks the state between the local
|
// Conversation cubit the tracks the state between the local
|
||||||
// and remote halves of a contact's relationship with this account
|
// and remote halves of a contact's relationship with this account
|
||||||
final conversationCubit = ConversationCubit(
|
final conversationCubit = ConversationCubit(
|
||||||
|
@ -105,14 +114,16 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
data.remoteConversation == null)
|
data.remoteConversation == null)
|
||||||
? const AsyncValue.loading()
|
? const AsyncValue.loading()
|
||||||
: AsyncValue.data(ActiveConversationState(
|
: AsyncValue.data(ActiveConversationState(
|
||||||
contact: contact,
|
|
||||||
localConversation: data.localConversation!,
|
localConversation: data.localConversation!,
|
||||||
remoteConversation: data.remoteConversation!)),
|
remoteConversation: data.remoteConversation!,
|
||||||
|
remoteIdentityPublicKey: remoteIdentityPublicKey,
|
||||||
|
localConversationRecordKey: localConversationRecordKey,
|
||||||
|
remoteConversationRecordKey:
|
||||||
|
remoteConversationRecordKey)),
|
||||||
loading: AsyncValue.loading,
|
loading: AsyncValue.loading,
|
||||||
error: AsyncValue.error));
|
error: AsyncValue.error));
|
||||||
|
|
||||||
return MapEntry(
|
return MapEntry(localConversationRecordKey, transformedCubit);
|
||||||
contact.localConversationRecordKey.toVeilid(), transformedCubit);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
@ -121,20 +132,44 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
Future<void> removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(TypedKey key, proto.Chat value) async {
|
Future<void> updateState(
|
||||||
final contactList = _contactListCubit.state.state.asData?.value;
|
TypedKey key, proto.Chat? oldValue, proto.Chat newValue) async {
|
||||||
if (contactList == null) {
|
switch (newValue.whichKind()) {
|
||||||
await addState(key, const AsyncValue.loading());
|
case proto.Chat_Kind.notSet:
|
||||||
|
throw StateError('unknown chat kind');
|
||||||
|
case proto.Chat_Kind.direct:
|
||||||
|
final localConversationRecordKey =
|
||||||
|
newValue.direct.localConversationRecordKey.toVeilid();
|
||||||
|
final remoteIdentityPublicKey =
|
||||||
|
newValue.direct.remoteMember.remoteIdentityPublicKey.toVeilid();
|
||||||
|
final remoteConversationRecordKey =
|
||||||
|
newValue.direct.remoteMember.remoteConversationRecordKey.toVeilid();
|
||||||
|
|
||||||
|
if (oldValue != null) {
|
||||||
|
final oldLocalConversationRecordKey =
|
||||||
|
oldValue.direct.localConversationRecordKey.toVeilid();
|
||||||
|
final oldRemoteIdentityPublicKey =
|
||||||
|
oldValue.direct.remoteMember.remoteIdentityPublicKey.toVeilid();
|
||||||
|
final oldRemoteConversationRecordKey = oldValue
|
||||||
|
.direct.remoteMember.remoteConversationRecordKey
|
||||||
|
.toVeilid();
|
||||||
|
|
||||||
|
if (oldLocalConversationRecordKey == localConversationRecordKey &&
|
||||||
|
oldRemoteIdentityPublicKey == remoteIdentityPublicKey &&
|
||||||
|
oldRemoteConversationRecordKey == remoteConversationRecordKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final contactIndex = contactList.indexWhere(
|
|
||||||
(c) => c.value.localConversationRecordKey.toVeilid() == key);
|
|
||||||
if (contactIndex == -1) {
|
|
||||||
await addState(key, AsyncValue.error('Contact not found'));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
final contact = contactList[contactIndex];
|
|
||||||
await _addConversation(contact: contact.value);
|
await _addDirectConversation(
|
||||||
|
remoteIdentityPublicKey: remoteIdentityPublicKey,
|
||||||
|
localConversationRecordKey: localConversationRecordKey,
|
||||||
|
remoteConversationRecordKey: remoteConversationRecordKey);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case proto.Chat_Kind.group:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
|
|
|
@ -2,16 +2,42 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:async_tools/async_tools.dart';
|
import 'package:async_tools/async_tools.dart';
|
||||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:meta/meta.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 '../../chat/chat.dart';
|
import '../../chat/chat.dart';
|
||||||
import '../../chat_list/cubits/chat_list_cubit.dart';
|
|
||||||
import '../../contacts/contacts.dart';
|
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import '../conversation.dart';
|
import '../conversation.dart';
|
||||||
import 'active_conversations_bloc_map_cubit.dart';
|
import 'active_conversations_bloc_map_cubit.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class _SingleContactChatState extends Equatable {
|
||||||
|
const _SingleContactChatState(
|
||||||
|
{required this.remoteIdentityPublicKey,
|
||||||
|
required this.localConversationRecordKey,
|
||||||
|
required this.remoteConversationRecordKey,
|
||||||
|
required this.localMessagesRecordKey,
|
||||||
|
required this.remoteMessagesRecordKey});
|
||||||
|
|
||||||
|
final TypedKey remoteIdentityPublicKey;
|
||||||
|
final TypedKey localConversationRecordKey;
|
||||||
|
final TypedKey remoteConversationRecordKey;
|
||||||
|
final TypedKey localMessagesRecordKey;
|
||||||
|
final TypedKey remoteMessagesRecordKey;
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement props
|
||||||
|
List<Object?> get props => [
|
||||||
|
remoteIdentityPublicKey,
|
||||||
|
localConversationRecordKey,
|
||||||
|
remoteConversationRecordKey,
|
||||||
|
localMessagesRecordKey,
|
||||||
|
remoteMessagesRecordKey
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Map of localConversationRecordKey to MessagesCubit
|
// Map of localConversationRecordKey to MessagesCubit
|
||||||
// Wraps a MessagesCubit to stream the latest messages to the state
|
// Wraps a MessagesCubit to stream the latest messages to the state
|
||||||
// Automatically follows the state of a ActiveConversationsBlocMapCubit.
|
// Automatically follows the state of a ActiveConversationsBlocMapCubit.
|
||||||
|
@ -20,36 +46,42 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
with
|
with
|
||||||
StateMapFollower<ActiveConversationsBlocMapState, TypedKey,
|
StateMapFollower<ActiveConversationsBlocMapState, TypedKey,
|
||||||
AsyncValue<ActiveConversationState>> {
|
AsyncValue<ActiveConversationState>> {
|
||||||
ActiveSingleContactChatBlocMapCubit(
|
ActiveSingleContactChatBlocMapCubit({
|
||||||
{required AccountInfo accountInfo,
|
required AccountInfo accountInfo,
|
||||||
required ActiveConversationsBlocMapCubit activeConversationsBlocMapCubit,
|
required ActiveConversationsBlocMapCubit activeConversationsBlocMapCubit,
|
||||||
required ContactListCubit contactListCubit,
|
}) : _accountInfo = accountInfo {
|
||||||
required ChatListCubit chatListCubit})
|
|
||||||
: _accountInfo = accountInfo,
|
|
||||||
_contactListCubit = contactListCubit,
|
|
||||||
_chatListCubit = chatListCubit {
|
|
||||||
// Follow the active conversations bloc map cubit
|
// Follow the active conversations bloc map cubit
|
||||||
follow(activeConversationsBlocMapCubit);
|
follow(activeConversationsBlocMapCubit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addConversationMessages(
|
Future<void> _addConversationMessages(_SingleContactChatState state) async =>
|
||||||
{required proto.Contact contact,
|
|
||||||
required proto.Chat chat,
|
|
||||||
required proto.Conversation localConversation,
|
|
||||||
required proto.Conversation remoteConversation}) async =>
|
|
||||||
add(() => MapEntry(
|
add(() => MapEntry(
|
||||||
contact.localConversationRecordKey.toVeilid(),
|
state.localConversationRecordKey,
|
||||||
SingleContactMessagesCubit(
|
SingleContactMessagesCubit(
|
||||||
accountInfo: _accountInfo,
|
accountInfo: _accountInfo,
|
||||||
remoteIdentityPublicKey: contact.identityPublicKey.toVeilid(),
|
remoteIdentityPublicKey: state.remoteIdentityPublicKey,
|
||||||
localConversationRecordKey:
|
localConversationRecordKey: state.localConversationRecordKey,
|
||||||
contact.localConversationRecordKey.toVeilid(),
|
remoteConversationRecordKey: state.remoteConversationRecordKey,
|
||||||
remoteConversationRecordKey:
|
localMessagesRecordKey: state.localMessagesRecordKey,
|
||||||
contact.remoteConversationRecordKey.toVeilid(),
|
remoteMessagesRecordKey: state.remoteMessagesRecordKey,
|
||||||
localMessagesRecordKey: localConversation.messages.toVeilid(),
|
|
||||||
remoteMessagesRecordKey: remoteConversation.messages.toVeilid(),
|
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
_SingleContactChatState? _mapStateValue(
|
||||||
|
AsyncValue<ActiveConversationState> avInputState) {
|
||||||
|
final inputState = avInputState.asData?.value;
|
||||||
|
if (inputState == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _SingleContactChatState(
|
||||||
|
remoteIdentityPublicKey: inputState.remoteIdentityPublicKey,
|
||||||
|
localConversationRecordKey: inputState.localConversationRecordKey,
|
||||||
|
remoteConversationRecordKey: inputState.remoteConversationRecordKey,
|
||||||
|
localMessagesRecordKey:
|
||||||
|
inputState.localConversation.messages.toVeilid(),
|
||||||
|
remoteMessagesRecordKey:
|
||||||
|
inputState.remoteConversation.messages.toVeilid());
|
||||||
|
}
|
||||||
|
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -57,49 +89,27 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(
|
Future<void> updateState(
|
||||||
TypedKey key, AsyncValue<ActiveConversationState> value) async {
|
TypedKey key,
|
||||||
// Get the contact object for this single contact chat
|
AsyncValue<ActiveConversationState>? oldValue,
|
||||||
final contactList = _contactListCubit.state.state.asData?.value;
|
AsyncValue<ActiveConversationState> newValue) async {
|
||||||
if (contactList == null) {
|
final newState = _mapStateValue(newValue);
|
||||||
|
if (oldValue != null) {
|
||||||
|
final oldState = _mapStateValue(oldValue);
|
||||||
|
if (oldState == newState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newState != null) {
|
||||||
|
await _addConversationMessages(newState);
|
||||||
|
} else if (newValue.isLoading) {
|
||||||
await addState(key, const AsyncValue.loading());
|
await addState(key, const AsyncValue.loading());
|
||||||
return;
|
} else {
|
||||||
|
final (error, stackTrace) =
|
||||||
|
(newValue.asError!.error, newValue.asError!.stackTrace);
|
||||||
|
await addState(key, AsyncValue.error(error, stackTrace));
|
||||||
}
|
}
|
||||||
final contactIndex = contactList.indexWhere(
|
|
||||||
(c) => c.value.localConversationRecordKey.toVeilid() == key);
|
|
||||||
if (contactIndex == -1) {
|
|
||||||
await addState(
|
|
||||||
key, AsyncValue.error('Contact not found for conversation'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final contact = contactList[contactIndex].value;
|
|
||||||
|
|
||||||
// Get the chat object for this single contact chat
|
|
||||||
final chatList = _chatListCubit.state.state.asData?.value;
|
|
||||||
if (chatList == null) {
|
|
||||||
await addState(key, const AsyncValue.loading());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final chatIndex = chatList.indexWhere(
|
|
||||||
(c) => c.value.localConversationRecordKey.toVeilid() == key);
|
|
||||||
if (contactIndex == -1) {
|
|
||||||
await addState(key, AsyncValue.error('Chat not found for conversation'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final chat = chatList[chatIndex].value;
|
|
||||||
|
|
||||||
await value.when(
|
|
||||||
data: (state) => _addConversationMessages(
|
|
||||||
contact: contact,
|
|
||||||
chat: chat,
|
|
||||||
localConversation: state.localConversation,
|
|
||||||
remoteConversation: state.remoteConversation),
|
|
||||||
loading: () => addState(key, const AsyncValue.loading()),
|
|
||||||
error: (error, stackTrace) =>
|
|
||||||
addState(key, AsyncValue.error(error, stackTrace)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
final AccountInfo _accountInfo;
|
final AccountInfo _accountInfo;
|
||||||
final ContactListCubit _contactListCubit;
|
|
||||||
final ChatListCubit _chatListCubit;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class ChatsPageState extends State<ChatsPage> {
|
||||||
// ignore: prefer_expression_function_bodies
|
// ignore: prefer_expression_function_bodies
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(children: <Widget>[
|
return Column(children: <Widget>[
|
||||||
const ChatSingleContactListWidget().expanded(),
|
const ChatListWidget().expanded(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,12 @@ import 'dart:math';
|
||||||
|
|
||||||
import 'package:async_tools/async_tools.dart';
|
import 'package:async_tools/async_tools.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
|
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.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 '../../chat/chat.dart';
|
import '../../chat/chat.dart';
|
||||||
import '../../contact_invitation/contact_invitation.dart';
|
|
||||||
import '../../contacts/contacts.dart';
|
|
||||||
import '../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
import '../../tools/tools.dart';
|
import '../../tools/tools.dart';
|
||||||
import 'active_account_page_controller_wrapper.dart';
|
import 'active_account_page_controller_wrapper.dart';
|
||||||
|
@ -39,48 +36,6 @@ class HomeScreenState extends State<HomeScreen> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all accepted or rejected invitations
|
|
||||||
void _invitationStatusListener(
|
|
||||||
BuildContext context, WaitingInvitationsBlocMapState state) {
|
|
||||||
_singleInvitationStatusProcessor.updateState(state, (newState) async {
|
|
||||||
final contactListCubit = context.read<ContactListCubit>();
|
|
||||||
final contactInvitationListCubit =
|
|
||||||
context.read<ContactInvitationListCubit>();
|
|
||||||
|
|
||||||
for (final entry in newState.entries) {
|
|
||||||
final contactRequestInboxRecordKey = entry.key;
|
|
||||||
final invStatus = entry.value.asData?.value;
|
|
||||||
// Skip invitations that have not yet been accepted or rejected
|
|
||||||
if (invStatus == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete invitation and process the accepted or rejected contact
|
|
||||||
final acceptedContact = invStatus.acceptedContact;
|
|
||||||
if (acceptedContact != null) {
|
|
||||||
await contactInvitationListCubit.deleteInvitation(
|
|
||||||
accepted: true,
|
|
||||||
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
|
|
||||||
|
|
||||||
// Accept
|
|
||||||
await contactListCubit.createContact(
|
|
||||||
profile: acceptedContact.remoteProfile,
|
|
||||||
remoteSuperIdentity: acceptedContact.remoteIdentity,
|
|
||||||
remoteConversationRecordKey:
|
|
||||||
acceptedContact.remoteConversationRecordKey,
|
|
||||||
localConversationRecordKey:
|
|
||||||
acceptedContact.localConversationRecordKey,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Reject
|
|
||||||
await contactInvitationListCubit.deleteInvitation(
|
|
||||||
accepted: false,
|
|
||||||
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAccountReadyDeviceSpecific(BuildContext context) {
|
Widget _buildAccountReadyDeviceSpecific(BuildContext context) {
|
||||||
final hasActiveChat = context.watch<ActiveChatCubit>().state != null;
|
final hasActiveChat = context.watch<ActiveChatCubit>().state != null;
|
||||||
if (responsiveVisibility(
|
if (responsiveVisibility(
|
||||||
|
@ -110,24 +65,18 @@ class HomeScreenState extends State<HomeScreen> {
|
||||||
|
|
||||||
// Re-export all ready blocs to the account display subtree
|
// Re-export all ready blocs to the account display subtree
|
||||||
return perAccountCollectionState.provide(
|
return perAccountCollectionState.provide(
|
||||||
child: MultiBlocListener(listeners: [
|
child: Builder(builder: _buildAccountReadyDeviceSpecific));
|
||||||
BlocListener<WaitingInvitationsBlocMapCubit,
|
|
||||||
WaitingInvitationsBlocMapState>(
|
|
||||||
listener: _invitationStatusListener,
|
|
||||||
)
|
|
||||||
], child: Builder(builder: _buildAccountReadyDeviceSpecific)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAccountPageView(BuildContext context) {
|
Widget _buildAccountPageView(BuildContext context) {
|
||||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||||
final activeLocalAccountCubit =
|
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
|
||||||
context.watch<ActiveLocalAccountCubit>().state;
|
|
||||||
final perAccountCollectionBlocMapState =
|
final perAccountCollectionBlocMapState =
|
||||||
context.watch<PerAccountCollectionBlocMapCubit>().state;
|
context.watch<PerAccountCollectionBlocMapCubit>().state;
|
||||||
|
|
||||||
final activeIndex = localAccounts.indexWhere(
|
final activeIndex = localAccounts
|
||||||
(x) => x.superIdentity.recordKey == activeLocalAccountCubit);
|
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
|
||||||
if (activeIndex == -1) {
|
if (activeIndex == -1) {
|
||||||
return const HomeNoActive();
|
return const HomeNoActive();
|
||||||
}
|
}
|
||||||
|
@ -208,6 +157,4 @@ class HomeScreenState extends State<HomeScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final _zoomDrawerController = ZoomDrawerController();
|
final _zoomDrawerController = ZoomDrawerController();
|
||||||
final _singleInvitationStatusProcessor =
|
|
||||||
SingleStateProcessor<WaitingInvitationsBlocMapState>();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,3 +34,16 @@ extension ContactExt on proto.Contact {
|
||||||
String get displayName =>
|
String get displayName =>
|
||||||
nickname.isNotEmpty ? '$nickname (${profile.name})' : profile.name;
|
nickname.isNotEmpty ? '$nickname (${profile.name})' : profile.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ChatExt on proto.Chat {
|
||||||
|
TypedKey get localConversationRecordKey {
|
||||||
|
switch (whichKind()) {
|
||||||
|
case proto.Chat_Kind.direct:
|
||||||
|
return direct.localConversationRecordKey.toVeilid();
|
||||||
|
case proto.Chat_Kind.group:
|
||||||
|
return group.localConversationRecordKey.toVeilid();
|
||||||
|
case proto.Chat_Kind.notSet:
|
||||||
|
throw StateError('unknown chat kind');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1255,16 +1255,15 @@ class Conversation extends $pb.GeneratedMessage {
|
||||||
$0.TypedKey ensureMessages() => $_ensure(2);
|
$0.TypedKey ensureMessages() => $_ensure(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Chat extends $pb.GeneratedMessage {
|
class ChatMember extends $pb.GeneratedMessage {
|
||||||
factory Chat() => create();
|
factory ChatMember() => create();
|
||||||
Chat._() : super();
|
ChatMember._() : super();
|
||||||
factory Chat.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
factory ChatMember.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||||
factory Chat.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
factory ChatMember.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Chat', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ChatMember', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
..aOM<ChatSettings>(1, _omitFieldNames ? '' : 'settings', subBuilder: ChatSettings.create)
|
..aOM<$0.TypedKey>(1, _omitFieldNames ? '' : 'remoteIdentityPublicKey', subBuilder: $0.TypedKey.create)
|
||||||
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: $0.TypedKey.create)
|
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1272,22 +1271,79 @@ class Chat extends $pb.GeneratedMessage {
|
||||||
'Using this can add significant overhead to your binary. '
|
'Using this can add significant overhead to your binary. '
|
||||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||||
'Will be removed in next major version')
|
'Will be removed in next major version')
|
||||||
Chat clone() => Chat()..mergeFromMessage(this);
|
ChatMember clone() => ChatMember()..mergeFromMessage(this);
|
||||||
@$core.Deprecated(
|
@$core.Deprecated(
|
||||||
'Using this can add significant overhead to your binary. '
|
'Using this can add significant overhead to your binary. '
|
||||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||||
'Will be removed in next major version')
|
'Will be removed in next major version')
|
||||||
Chat copyWith(void Function(Chat) updates) => super.copyWith((message) => updates(message as Chat)) as Chat;
|
ChatMember copyWith(void Function(ChatMember) updates) => super.copyWith((message) => updates(message as ChatMember)) as ChatMember;
|
||||||
|
|
||||||
$pb.BuilderInfo get info_ => _i;
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
@$core.pragma('dart2js:noInline')
|
@$core.pragma('dart2js:noInline')
|
||||||
static Chat create() => Chat._();
|
static ChatMember create() => ChatMember._();
|
||||||
Chat createEmptyInstance() => create();
|
ChatMember createEmptyInstance() => create();
|
||||||
static $pb.PbList<Chat> createRepeated() => $pb.PbList<Chat>();
|
static $pb.PbList<ChatMember> createRepeated() => $pb.PbList<ChatMember>();
|
||||||
@$core.pragma('dart2js:noInline')
|
@$core.pragma('dart2js:noInline')
|
||||||
static Chat getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Chat>(create);
|
static ChatMember getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ChatMember>(create);
|
||||||
static Chat? _defaultInstance;
|
static ChatMember? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$0.TypedKey get remoteIdentityPublicKey => $_getN(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set remoteIdentityPublicKey($0.TypedKey v) { setField(1, v); }
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasRemoteIdentityPublicKey() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearRemoteIdentityPublicKey() => clearField(1);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$0.TypedKey ensureRemoteIdentityPublicKey() => $_ensure(0);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$0.TypedKey get remoteConversationRecordKey => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set remoteConversationRecordKey($0.TypedKey v) { setField(2, v); }
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasRemoteConversationRecordKey() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearRemoteConversationRecordKey() => clearField(2);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$0.TypedKey ensureRemoteConversationRecordKey() => $_ensure(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DirectChat extends $pb.GeneratedMessage {
|
||||||
|
factory DirectChat() => create();
|
||||||
|
DirectChat._() : super();
|
||||||
|
factory DirectChat.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||||
|
factory DirectChat.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DirectChat', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
|
..aOM<ChatSettings>(1, _omitFieldNames ? '' : 'settings', subBuilder: ChatSettings.create)
|
||||||
|
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
|
..aOM<ChatMember>(3, _omitFieldNames ? '' : 'remoteMember', subBuilder: ChatMember.create)
|
||||||
|
..hasRequiredFields = false
|
||||||
|
;
|
||||||
|
|
||||||
|
@$core.Deprecated(
|
||||||
|
'Using this can add significant overhead to your binary. '
|
||||||
|
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||||
|
'Will be removed in next major version')
|
||||||
|
DirectChat clone() => DirectChat()..mergeFromMessage(this);
|
||||||
|
@$core.Deprecated(
|
||||||
|
'Using this can add significant overhead to your binary. '
|
||||||
|
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||||
|
'Will be removed in next major version')
|
||||||
|
DirectChat copyWith(void Function(DirectChat) updates) => super.copyWith((message) => updates(message as DirectChat)) as DirectChat;
|
||||||
|
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static DirectChat create() => DirectChat._();
|
||||||
|
DirectChat createEmptyInstance() => create();
|
||||||
|
static $pb.PbList<DirectChat> createRepeated() => $pb.PbList<DirectChat>();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static DirectChat getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DirectChat>(create);
|
||||||
|
static DirectChat? _defaultInstance;
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
ChatSettings get settings => $_getN(0);
|
ChatSettings get settings => $_getN(0);
|
||||||
|
@ -1312,15 +1368,15 @@ class Chat extends $pb.GeneratedMessage {
|
||||||
$0.TypedKey ensureLocalConversationRecordKey() => $_ensure(1);
|
$0.TypedKey ensureLocalConversationRecordKey() => $_ensure(1);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$0.TypedKey get remoteConversationRecordKey => $_getN(2);
|
ChatMember get remoteMember => $_getN(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
set remoteConversationRecordKey($0.TypedKey v) { setField(3, v); }
|
set remoteMember(ChatMember v) { setField(3, v); }
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.bool hasRemoteConversationRecordKey() => $_has(2);
|
$core.bool hasRemoteMember() => $_has(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
void clearRemoteConversationRecordKey() => clearField(3);
|
void clearRemoteMember() => clearField(3);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$0.TypedKey ensureRemoteConversationRecordKey() => $_ensure(2);
|
ChatMember ensureRemoteMember() => $_ensure(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupChat extends $pb.GeneratedMessage {
|
class GroupChat extends $pb.GeneratedMessage {
|
||||||
|
@ -1331,8 +1387,10 @@ class GroupChat extends $pb.GeneratedMessage {
|
||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GroupChat', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GroupChat', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
..aOM<ChatSettings>(1, _omitFieldNames ? '' : 'settings', subBuilder: ChatSettings.create)
|
..aOM<ChatSettings>(1, _omitFieldNames ? '' : 'settings', subBuilder: ChatSettings.create)
|
||||||
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<Membership>(2, _omitFieldNames ? '' : 'membership', subBuilder: Membership.create)
|
||||||
..pc<$0.TypedKey>(3, _omitFieldNames ? '' : 'remoteConversationRecordKeys', $pb.PbFieldType.PM, subBuilder: $0.TypedKey.create)
|
..aOM<Permissions>(3, _omitFieldNames ? '' : 'permissions', subBuilder: Permissions.create)
|
||||||
|
..aOM<$0.TypedKey>(4, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
|
..pc<ChatMember>(5, _omitFieldNames ? '' : 'remoteMembers', $pb.PbFieldType.PM, subBuilder: ChatMember.create)
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1369,18 +1427,111 @@ class GroupChat extends $pb.GeneratedMessage {
|
||||||
ChatSettings ensureSettings() => $_ensure(0);
|
ChatSettings ensureSettings() => $_ensure(0);
|
||||||
|
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$0.TypedKey get localConversationRecordKey => $_getN(1);
|
Membership get membership => $_getN(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
set localConversationRecordKey($0.TypedKey v) { setField(2, v); }
|
set membership(Membership v) { setField(2, v); }
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$core.bool hasLocalConversationRecordKey() => $_has(1);
|
$core.bool hasMembership() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearLocalConversationRecordKey() => clearField(2);
|
void clearMembership() => clearField(2);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$0.TypedKey ensureLocalConversationRecordKey() => $_ensure(1);
|
Membership ensureMembership() => $_ensure(1);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.List<$0.TypedKey> get remoteConversationRecordKeys => $_getList(2);
|
Permissions get permissions => $_getN(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set permissions(Permissions v) { setField(3, v); }
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasPermissions() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearPermissions() => clearField(3);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
Permissions ensurePermissions() => $_ensure(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$0.TypedKey get localConversationRecordKey => $_getN(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set localConversationRecordKey($0.TypedKey v) { setField(4, v); }
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasLocalConversationRecordKey() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearLocalConversationRecordKey() => clearField(4);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$0.TypedKey ensureLocalConversationRecordKey() => $_ensure(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.List<ChatMember> get remoteMembers => $_getList(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Chat_Kind {
|
||||||
|
direct,
|
||||||
|
group,
|
||||||
|
notSet
|
||||||
|
}
|
||||||
|
|
||||||
|
class Chat extends $pb.GeneratedMessage {
|
||||||
|
factory Chat() => create();
|
||||||
|
Chat._() : super();
|
||||||
|
factory Chat.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||||
|
factory Chat.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||||
|
|
||||||
|
static const $core.Map<$core.int, Chat_Kind> _Chat_KindByTag = {
|
||||||
|
1 : Chat_Kind.direct,
|
||||||
|
2 : Chat_Kind.group,
|
||||||
|
0 : Chat_Kind.notSet
|
||||||
|
};
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Chat', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
|
..oo(0, [1, 2])
|
||||||
|
..aOM<DirectChat>(1, _omitFieldNames ? '' : 'direct', subBuilder: DirectChat.create)
|
||||||
|
..aOM<GroupChat>(2, _omitFieldNames ? '' : 'group', subBuilder: GroupChat.create)
|
||||||
|
..hasRequiredFields = false
|
||||||
|
;
|
||||||
|
|
||||||
|
@$core.Deprecated(
|
||||||
|
'Using this can add significant overhead to your binary. '
|
||||||
|
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||||
|
'Will be removed in next major version')
|
||||||
|
Chat clone() => Chat()..mergeFromMessage(this);
|
||||||
|
@$core.Deprecated(
|
||||||
|
'Using this can add significant overhead to your binary. '
|
||||||
|
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||||
|
'Will be removed in next major version')
|
||||||
|
Chat copyWith(void Function(Chat) updates) => super.copyWith((message) => updates(message as Chat)) as Chat;
|
||||||
|
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Chat create() => Chat._();
|
||||||
|
Chat createEmptyInstance() => create();
|
||||||
|
static $pb.PbList<Chat> createRepeated() => $pb.PbList<Chat>();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Chat getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Chat>(create);
|
||||||
|
static Chat? _defaultInstance;
|
||||||
|
|
||||||
|
Chat_Kind whichKind() => _Chat_KindByTag[$_whichOneof(0)]!;
|
||||||
|
void clearKind() => clearField($_whichOneof(0));
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
DirectChat get direct => $_getN(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set direct(DirectChat v) { setField(1, v); }
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasDirect() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearDirect() => clearField(1);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
DirectChat ensureDirect() => $_ensure(0);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
GroupChat get group => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set group(GroupChat v) { setField(2, v); }
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasGroup() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearGroup() => clearField(2);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
GroupChat ensureGroup() => $_ensure(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Profile extends $pb.GeneratedMessage {
|
class Profile extends $pb.GeneratedMessage {
|
||||||
|
|
|
@ -365,41 +365,76 @@ final $typed_data.Uint8List conversationDescriptor = $convert.base64Decode(
|
||||||
'JvZmlsZRIuChNzdXBlcl9pZGVudGl0eV9qc29uGAIgASgJUhFzdXBlcklkZW50aXR5SnNvbhIs'
|
'JvZmlsZRIuChNzdXBlcl9pZGVudGl0eV9qc29uGAIgASgJUhFzdXBlcklkZW50aXR5SnNvbhIs'
|
||||||
'CghtZXNzYWdlcxgDIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIIbWVzc2FnZXM=');
|
'CghtZXNzYWdlcxgDIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIIbWVzc2FnZXM=');
|
||||||
|
|
||||||
@$core.Deprecated('Use chatDescriptor instead')
|
@$core.Deprecated('Use chatMemberDescriptor instead')
|
||||||
const Chat$json = {
|
const ChatMember$json = {
|
||||||
'1': 'Chat',
|
'1': 'ChatMember',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
{'1': 'remote_identity_public_key', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteIdentityPublicKey'},
|
||||||
{'1': 'local_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
{'1': 'remote_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
||||||
{'1': 'remote_conversation_record_key', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `Chat`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `ChatMember`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List chatDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List chatMemberDescriptor = $convert.base64Decode(
|
||||||
'CgRDaGF0EjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlkY2hhdC5DaGF0U2V0dGluZ3NSCHNldH'
|
'CgpDaGF0TWVtYmVyEk0KGnJlbW90ZV9pZGVudGl0eV9wdWJsaWNfa2V5GAEgASgLMhAudmVpbG'
|
||||||
'RpbmdzElMKHWxvY2FsX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2V5GAIgASgLMhAudmVpbGlkLlR5'
|
'lkLlR5cGVkS2V5UhdyZW1vdGVJZGVudGl0eVB1YmxpY0tleRJVCh5yZW1vdGVfY29udmVyc2F0'
|
||||||
'cGVkS2V5Uhpsb2NhbENvbnZlcnNhdGlvblJlY29yZEtleRJVCh5yZW1vdGVfY29udmVyc2F0aW'
|
'aW9uX3JlY29yZF9rZXkYAiABKAsyEC52ZWlsaWQuVHlwZWRLZXlSG3JlbW90ZUNvbnZlcnNhdG'
|
||||||
'9uX3JlY29yZF9rZXkYAyABKAsyEC52ZWlsaWQuVHlwZWRLZXlSG3JlbW90ZUNvbnZlcnNhdGlv'
|
'lvblJlY29yZEtleQ==');
|
||||||
'blJlY29yZEtleQ==');
|
|
||||||
|
@$core.Deprecated('Use directChatDescriptor instead')
|
||||||
|
const DirectChat$json = {
|
||||||
|
'1': 'DirectChat',
|
||||||
|
'2': [
|
||||||
|
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
||||||
|
{'1': 'local_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
||||||
|
{'1': 'remote_member', '3': 3, '4': 1, '5': 11, '6': '.veilidchat.ChatMember', '10': 'remoteMember'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `DirectChat`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List directChatDescriptor = $convert.base64Decode(
|
||||||
|
'CgpEaXJlY3RDaGF0EjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlkY2hhdC5DaGF0U2V0dGluZ3'
|
||||||
|
'NSCHNldHRpbmdzElMKHWxvY2FsX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2V5GAIgASgLMhAudmVp'
|
||||||
|
'bGlkLlR5cGVkS2V5Uhpsb2NhbENvbnZlcnNhdGlvblJlY29yZEtleRI7Cg1yZW1vdGVfbWVtYm'
|
||||||
|
'VyGAMgASgLMhYudmVpbGlkY2hhdC5DaGF0TWVtYmVyUgxyZW1vdGVNZW1iZXI=');
|
||||||
|
|
||||||
@$core.Deprecated('Use groupChatDescriptor instead')
|
@$core.Deprecated('Use groupChatDescriptor instead')
|
||||||
const GroupChat$json = {
|
const GroupChat$json = {
|
||||||
'1': 'GroupChat',
|
'1': 'GroupChat',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
||||||
{'1': 'local_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
{'1': 'membership', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.Membership', '10': 'membership'},
|
||||||
{'1': 'remote_conversation_record_keys', '3': 3, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKeys'},
|
{'1': 'permissions', '3': 3, '4': 1, '5': 11, '6': '.veilidchat.Permissions', '10': 'permissions'},
|
||||||
|
{'1': 'local_conversation_record_key', '3': 4, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
||||||
|
{'1': 'remote_members', '3': 5, '4': 3, '5': 11, '6': '.veilidchat.ChatMember', '10': 'remoteMembers'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `GroupChat`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `GroupChat`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List groupChatDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List groupChatDescriptor = $convert.base64Decode(
|
||||||
'CglHcm91cENoYXQSNAoIc2V0dGluZ3MYASABKAsyGC52ZWlsaWRjaGF0LkNoYXRTZXR0aW5nc1'
|
'CglHcm91cENoYXQSNAoIc2V0dGluZ3MYASABKAsyGC52ZWlsaWRjaGF0LkNoYXRTZXR0aW5nc1'
|
||||||
'IIc2V0dGluZ3MSUwodbG9jYWxfY29udmVyc2F0aW9uX3JlY29yZF9rZXkYAiABKAsyEC52ZWls'
|
'IIc2V0dGluZ3MSNgoKbWVtYmVyc2hpcBgCIAEoCzIWLnZlaWxpZGNoYXQuTWVtYmVyc2hpcFIK'
|
||||||
'aWQuVHlwZWRLZXlSGmxvY2FsQ29udmVyc2F0aW9uUmVjb3JkS2V5ElcKH3JlbW90ZV9jb252ZX'
|
'bWVtYmVyc2hpcBI5CgtwZXJtaXNzaW9ucxgDIAEoCzIXLnZlaWxpZGNoYXQuUGVybWlzc2lvbn'
|
||||||
'JzYXRpb25fcmVjb3JkX2tleXMYAyADKAsyEC52ZWlsaWQuVHlwZWRLZXlSHHJlbW90ZUNvbnZl'
|
'NSC3Blcm1pc3Npb25zElMKHWxvY2FsX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2V5GAQgASgLMhAu'
|
||||||
'cnNhdGlvblJlY29yZEtleXM=');
|
'dmVpbGlkLlR5cGVkS2V5Uhpsb2NhbENvbnZlcnNhdGlvblJlY29yZEtleRI9Cg5yZW1vdGVfbW'
|
||||||
|
'VtYmVycxgFIAMoCzIWLnZlaWxpZGNoYXQuQ2hhdE1lbWJlclINcmVtb3RlTWVtYmVycw==');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use chatDescriptor instead')
|
||||||
|
const Chat$json = {
|
||||||
|
'1': 'Chat',
|
||||||
|
'2': [
|
||||||
|
{'1': 'direct', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.DirectChat', '9': 0, '10': 'direct'},
|
||||||
|
{'1': 'group', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.GroupChat', '9': 0, '10': 'group'},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': 'kind'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Chat`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List chatDescriptor = $convert.base64Decode(
|
||||||
|
'CgRDaGF0EjAKBmRpcmVjdBgBIAEoCzIWLnZlaWxpZGNoYXQuRGlyZWN0Q2hhdEgAUgZkaXJlY3'
|
||||||
|
'QSLQoFZ3JvdXAYAiABKAsyFS52ZWlsaWRjaGF0Lkdyb3VwQ2hhdEgAUgVncm91cEIGCgRraW5k');
|
||||||
|
|
||||||
@$core.Deprecated('Use profileDescriptor instead')
|
@$core.Deprecated('Use profileDescriptor instead')
|
||||||
const Profile$json = {
|
const Profile$json = {
|
||||||
|
|
|
@ -267,15 +267,23 @@ message Conversation {
|
||||||
veilid.TypedKey messages = 3;
|
veilid.TypedKey messages = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either a 1-1 conversation or a group chat
|
// A member of chat which may or may not be associated with a contact
|
||||||
|
message ChatMember {
|
||||||
|
// The identity public key most recently associated with the chat member
|
||||||
|
veilid.TypedKey remote_identity_public_key = 1;
|
||||||
|
// Conversation key for the other party
|
||||||
|
veilid.TypedKey remote_conversation_record_key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 1-1 chat
|
||||||
// Privately encrypted, this is the local user's copy of the chat
|
// Privately encrypted, this is the local user's copy of the chat
|
||||||
message Chat {
|
message DirectChat {
|
||||||
// Settings
|
// Settings
|
||||||
ChatSettings settings = 1;
|
ChatSettings settings = 1;
|
||||||
// Conversation key for this user
|
// Conversation key for this user
|
||||||
veilid.TypedKey local_conversation_record_key = 2;
|
veilid.TypedKey local_conversation_record_key = 2;
|
||||||
// Conversation key for the other party
|
// Conversation key for the other party
|
||||||
veilid.TypedKey remote_conversation_record_key = 3;
|
ChatMember remote_member = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A group chat
|
// A group chat
|
||||||
|
@ -283,10 +291,22 @@ message Chat {
|
||||||
message GroupChat {
|
message GroupChat {
|
||||||
// Settings
|
// Settings
|
||||||
ChatSettings settings = 1;
|
ChatSettings settings = 1;
|
||||||
|
// Membership
|
||||||
|
Membership membership = 2;
|
||||||
|
// Permissions
|
||||||
|
Permissions permissions = 3;
|
||||||
// Conversation key for this user
|
// Conversation key for this user
|
||||||
veilid.TypedKey local_conversation_record_key = 2;
|
veilid.TypedKey local_conversation_record_key = 4;
|
||||||
// Conversation keys for the other parties
|
// Conversation keys for the other parties
|
||||||
repeated veilid.TypedKey remote_conversation_record_keys = 3;
|
repeated ChatMember remote_members = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some kind of chat
|
||||||
|
message Chat {
|
||||||
|
oneof kind {
|
||||||
|
DirectChat direct = 1;
|
||||||
|
GroupChat group = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -609,7 +609,7 @@ class _DHTLogSpine {
|
||||||
// Don't watch for local changes because this class already handles
|
// Don't watch for local changes because this class already handles
|
||||||
// notifying listeners and knows when it makes local changes
|
// notifying listeners and knows when it makes local changes
|
||||||
_subscription ??=
|
_subscription ??=
|
||||||
await _spineRecord.listen(localChanges: false, _onSpineChanged);
|
await _spineRecord.listen(localChanges: true, _onSpineChanged);
|
||||||
} on Exception {
|
} on Exception {
|
||||||
// If anything fails, try to cancel the watches
|
// If anything fails, try to cancel the watches
|
||||||
await cancelWatch();
|
await cancelWatch();
|
||||||
|
|
|
@ -12,14 +12,6 @@ class DefaultDHTRecordCubit<T> extends DHTRecordCubit<T> {
|
||||||
stateFunction: _makeStateFunction(decodeState),
|
stateFunction: _makeStateFunction(decodeState),
|
||||||
watchFunction: _makeWatchFunction());
|
watchFunction: _makeWatchFunction());
|
||||||
|
|
||||||
// DefaultDHTRecordCubit.value({
|
|
||||||
// required super.record,
|
|
||||||
// required T Function(List<int> data) decodeState,
|
|
||||||
// }) : super.value(
|
|
||||||
// initialStateFunction: _makeInitialStateFunction(decodeState),
|
|
||||||
// stateFunction: _makeStateFunction(decodeState),
|
|
||||||
// watchFunction: _makeWatchFunction());
|
|
||||||
|
|
||||||
static InitialStateFunction<T> _makeInitialStateFunction<T>(
|
static InitialStateFunction<T> _makeInitialStateFunction<T>(
|
||||||
T Function(List<int> data) decodeState) =>
|
T Function(List<int> data) decodeState) =>
|
||||||
(record) async {
|
(record) async {
|
||||||
|
|
|
@ -29,20 +29,6 @@ class DHTRecordCubit<T> extends Cubit<AsyncValue<T>> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHTRecordCubit.value({
|
|
||||||
// required DHTRecord record,
|
|
||||||
// required InitialStateFunction<T> initialStateFunction,
|
|
||||||
// required StateFunction<T> stateFunction,
|
|
||||||
// required WatchFunction watchFunction,
|
|
||||||
// }) : _record = record,
|
|
||||||
// _stateFunction = stateFunction,
|
|
||||||
// _wantsCloseRecord = false,
|
|
||||||
// super(const AsyncValue.loading()) {
|
|
||||||
// Future.delayed(Duration.zero, () async {
|
|
||||||
// await _init(initialStateFunction, stateFunction, watchFunction);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
Future<void> _init(
|
Future<void> _init(
|
||||||
InitialStateFunction<T> initialStateFunction,
|
InitialStateFunction<T> initialStateFunction,
|
||||||
StateFunction<T> stateFunction,
|
StateFunction<T> stateFunction,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue