messages wip

This commit is contained in:
Christien Rioux 2024-05-27 18:04:00 -04:00
parent 17f6dfce46
commit 9c5feed732
18 changed files with 274 additions and 171 deletions

View File

@ -4,7 +4,7 @@ import 'package:veilid_support/veilid_support.dart';
class ActiveChatCubit extends Cubit<TypedKey?> {
ActiveChatCubit(super.initialState);
void setActiveChat(TypedKey? activeChatRemoteConversationRecordKey) {
emit(activeChatRemoteConversationRecordKey);
void setActiveChat(TypedKey? activeChatLocalConversationRecordKey) {
emit(activeChatLocalConversationRecordKey);
}
}

View File

@ -53,14 +53,12 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
required TypedKey localMessagesRecordKey,
required TypedKey remoteConversationRecordKey,
required TypedKey remoteMessagesRecordKey,
required OwnedDHTRecordPointer reconciledChatRecord,
}) : _activeAccountInfo = activeAccountInfo,
_remoteIdentityPublicKey = remoteIdentityPublicKey,
_localConversationRecordKey = localConversationRecordKey,
_localMessagesRecordKey = localMessagesRecordKey,
_remoteConversationRecordKey = remoteConversationRecordKey,
_remoteMessagesRecordKey = remoteMessagesRecordKey,
_reconciledChatRecord = reconciledChatRecord,
super(const AsyncValue.loading()) {
// Async Init
_initWait.add(_init);
@ -420,7 +418,14 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
emit(AsyncValue.data(renderedState));
}
void addMessage({required proto.Message message}) {
void addTextMessage({required proto.Message_Text messageText}) {
final message = proto.Message()
..author = _activeAccountInfo.localAccount.identityMaster
.identityPublicTypedKey()
.toProto()
..timestamp = Veilid.instance.now().toInt64()
..text = messageText;
_unreconciledMessagesQueue.addSync(message);
_sendingMessagesQueue.addSync(message);
@ -428,6 +433,21 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
_renderState();
}
/////////////////////////////////////////////////////////////////////////
static Future<void> cleanupAndDeleteMessages(
{required TypedKey localConversationRecordKey}) async {
final recmsgdbname =
_reconciledMessagesTableDBName(localConversationRecordKey);
await Veilid.instance.deleteTableDB(recmsgdbname);
}
static String _reconciledMessagesTableDBName(
TypedKey localConversationRecordKey) =>
'msg_$localConversationRecordKey';
/////////////////////////////////////////////////////////////////////////
final WaitSet<void> _initWait = WaitSet();
final ActiveAccountInfo _activeAccountInfo;
final TypedKey _remoteIdentityPublicKey;
@ -435,7 +455,6 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
final TypedKey _localMessagesRecordKey;
final TypedKey _remoteConversationRecordKey;
final TypedKey _remoteMessagesRecordKey;
final OwnedDHTRecordPointer _reconciledChatRecord;
late final VeilidCrypto _messagesCrypto;

View File

@ -30,10 +30,28 @@ class MessageState with _$MessageState {
required proto.Message content,
// Received or delivered timestamp
required Timestamp timestamp,
// The state of the mssage
// The state of the message
required MessageSendState? sendState,
}) = _MessageState;
factory MessageState.fromJson(dynamic json) =>
_$MessageStateFromJson(json as Map<String, dynamic>);
}
extension MessageStateExt on MessageState {
String get uniqueId {
final author = content.author.toVeilid().toString();
final id = base64UrlNoPadEncode(content.id);
return '$author|$id';
}
static (proto.TypedKey, Uint8List) splitUniqueId(String uniqueId) {
final parts = uniqueId.split('|');
if (parts.length != 2) {
throw Exception('invalid unique id');
}
final author = TypedKey.fromString(parts[0]).toProto();
final id = base64UrlNoPadDecode(parts[1]);
return (author, id);
}
}

View File

@ -25,7 +25,7 @@ mixin _$MessageState {
proto.Message get content =>
throw _privateConstructorUsedError; // Received or delivered timestamp
Timestamp get timestamp =>
throw _privateConstructorUsedError; // The state of the mssage
throw _privateConstructorUsedError; // The state of the message
MessageSendState? get sendState => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -147,7 +147,7 @@ class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState {
// Received or delivered timestamp
@override
final Timestamp timestamp;
// The state of the mssage
// The state of the message
@override
final MessageSendState? sendState;
@ -211,7 +211,7 @@ abstract class _MessageState implements MessageState {
proto.Message get content;
@override // Received or delivered timestamp
Timestamp get timestamp;
@override // The state of the mssage
@override // The state of the message
MessageSendState? get sendState;
@override
@JsonKey(ignore: true)

View File

@ -1,5 +1,8 @@
import 'dart:typed_data';
import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
@ -13,6 +16,10 @@ import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../chat.dart';
const String metadataKeyExpirationDuration = 'expiration';
const String metadataKeyViewLimit = 'view_limit';
const String metadataKeyAttachments = 'attachments';
class ChatComponent extends StatelessWidget {
const ChatComponent._(
{required TypedKey localUserIdentityKey,
@ -35,7 +42,7 @@ class ChatComponent extends StatelessWidget {
// Builder wrapper function that takes care of state management requirements
static Widget builder(
{required TypedKey remoteConversationRecordKey, Key? key}) =>
{required TypedKey localConversationRecordKey, Key? key}) =>
Builder(builder: (context) {
// Get all watched dependendies
final activeAccountInfo = context.watch<ActiveAccountInfo>();
@ -51,7 +58,7 @@ class ChatComponent extends StatelessWidget {
}
final avconversation = context.select<ActiveConversationsBlocMapCubit,
AsyncValue<ActiveConversationState>?>(
(x) => x.state[remoteConversationRecordKey]);
(x) => x.state[localConversationRecordKey]);
if (avconversation == null) {
return waitingPage();
}
@ -77,7 +84,7 @@ class ChatComponent extends StatelessWidget {
// Get the messages cubit
final messages = context.select<ActiveSingleContactChatBlocMapCubit,
(SingleContactMessagesCubit, SingleContactMessagesState)?>(
(x) => x.tryOperate(remoteConversationRecordKey,
(x) => x.tryOperate(localConversationRecordKey,
closure: (cubit) => (cubit, cubit.state)));
// Get the messages to display
@ -97,8 +104,8 @@ class ChatComponent extends StatelessWidget {
/////////////////////////////////////////////////////////////////////
types.Message messageToChatMessage(MessageState message) {
final isLocal = message.author == _localUserIdentityKey;
types.Message? messageToChatMessage(MessageState message) {
final isLocal = message.content.author.toVeilid() == _localUserIdentityKey;
types.Status? status;
if (message.sendState != null) {
@ -113,31 +120,83 @@ class ChatComponent extends StatelessWidget {
}
}
switch (message.content.whichKind()) {
case proto.Message_Kind.text:
final contextText = message.content.text;
final textMessage = types.TextMessage(
author: isLocal ? _localUser : _remoteUser,
createdAt: (message.timestamp.value ~/ BigInt.from(1000)).toInt(),
id: message.timestamp.toString(),
text: message.text,
id: message.uniqueId,
text: contextText.text,
showStatus: status != null,
status: status);
return textMessage;
case proto.Message_Kind.secret:
case proto.Message_Kind.delete:
case proto.Message_Kind.erase:
case proto.Message_Kind.settings:
case proto.Message_Kind.permissions:
case proto.Message_Kind.membership:
case proto.Message_Kind.moderation:
case proto.Message_Kind.notSet:
return null;
}
}
void _addMessage(proto.Message message) {
if (message.text.isEmpty) {
return;
void _addTextMessage(
{required String text,
String? topic,
Uint8List? replyId,
Timestamp? expiration,
int? viewLimit,
List<proto.Attachment> attachments = const []}) {
final protoMessageText = proto.Message_Text()..text = text;
if (topic != null) {
protoMessageText.topic = topic;
}
_messagesCubit.addMessage(message: message);
if (replyId != null) {
protoMessageText.replyId = replyId;
}
protoMessageText
..expiration = expiration?.toInt64() ?? Int64.ZERO
..viewLimit = viewLimit ?? 0;
protoMessageText.attachments.addAll(attachments);
_messagesCubit.addTextMessage(messageText: protoMessageText);
}
void _handleSendPressed(types.PartialText message) {
final protoMessage = proto.Message()
..author = _localUserIdentityKey.toProto()
..timestamp = Veilid.instance.now().toInt64()
..text = message.text;
//..signature = signature;
final text = message.text;
final replyId = (message.repliedMessage != null)
? MessageStateExt.splitUniqueId(message.repliedMessage!.id).$2
: null;
Timestamp? expiration;
int? viewLimit;
List<proto.Attachment>? attachments;
final metadata = message.metadata;
if (metadata != null) {
final expirationValue =
metadata[metadataKeyExpirationDuration] as TimestampDuration?;
if (expirationValue != null) {
expiration = Veilid.instance.now().offset(expirationValue);
}
final viewLimitValue = metadata[metadataKeyViewLimit] as int?;
if (viewLimitValue != null) {
viewLimit = viewLimitValue;
}
final attachmentsValue =
metadata[metadataKeyAttachments] as List<proto.Attachment>?;
if (attachmentsValue != null) {
attachments = attachmentsValue;
}
}
_addMessage(protoMessage);
_addTextMessage(
text: text,
replyId: replyId,
expiration: expiration,
viewLimit: viewLimit,
attachments: attachments ?? []);
}
// void _handleAttachmentPressed() async {
@ -161,6 +220,9 @@ class ChatComponent extends StatelessWidget {
final tsSet = <String>{};
for (final message in messages) {
final chatMessage = messageToChatMessage(message);
if (chatMessage == null) {
continue;
}
chatMessages.insert(0, chatMessage);
if (!tsSet.add(chatMessage.id)) {
// ignore: avoid_print

View File

@ -31,7 +31,7 @@ typedef ActiveConversationCubit = TransformerCubit<
typedef ActiveConversationsBlocMapState
= BlocMapState<TypedKey, AsyncValue<ActiveConversationState>>;
// Map of remoteConversationRecordKey to ActiveConversationCubit
// Map of localConversationRecordKey to ActiveConversationCubit
// Wraps a conversation cubit to only expose completely built conversations
// Automatically follows the state of a ChatListCubit.
// Even though 'conversations' are per-contact and not per-chat
@ -49,7 +49,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
// Add an active conversation to be tracked for changes
Future<void> _addConversation({required proto.Contact contact}) async =>
add(() => MapEntry(
contact.remoteConversationRecordKey.toVeilid(),
contact.localConversationRecordKey.toVeilid(),
TransformerCubit(
ConversationCubit(
activeAccountInfo: _activeAccountInfo,
@ -86,7 +86,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
return;
}
final contactIndex = contactList.indexWhere(
(c) => c.value.remoteConversationRecordKey.toVeilid() == key);
(c) => c.value.localConversationRecordKey.toVeilid() == key);
if (contactIndex == -1) {
await addState(key, AsyncValue.error('Contact not found'));
return;

View File

@ -11,7 +11,7 @@ import '../../proto/proto.dart' as proto;
import 'active_conversations_bloc_map_cubit.dart';
import 'chat_list_cubit.dart';
// Map of remoteConversationRecordKey to MessagesCubit
// Map of localConversationRecordKey to MessagesCubit
// Wraps a MessagesCubit to stream the latest messages to the state
// Automatically follows the state of a ActiveConversationsBlocMapCubit.
class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
@ -33,7 +33,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
required proto.Conversation localConversation,
required proto.Conversation remoteConversation}) async =>
add(() => MapEntry(
contact.remoteConversationRecordKey.toVeilid(),
contact.localConversationRecordKey.toVeilid(),
SingleContactMessagesCubit(
activeAccountInfo: _activeAccountInfo,
remoteIdentityPublicKey: contact.identityPublicKey.toVeilid(),
@ -43,7 +43,6 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
contact.remoteConversationRecordKey.toVeilid(),
localMessagesRecordKey: localConversation.messages.toVeilid(),
remoteMessagesRecordKey: remoteConversation.messages.toVeilid(),
reconciledChatRecord: chat.reconciledChatRecord.toVeilid(),
)));
/// StateFollower /////////////////////////
@ -61,7 +60,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
return;
}
final contactIndex = contactList.indexWhere(
(c) => c.value.remoteConversationRecordKey.toVeilid() == key);
(c) => c.value.localConversationRecordKey.toVeilid() == key);
if (contactIndex == -1) {
await addState(
key, AsyncValue.error('Contact not found for conversation'));
@ -76,7 +75,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
return;
}
final chatIndex = chatList.indexWhere(
(c) => c.value.remoteConversationRecordKey.toVeilid() == key);
(c) => c.value.localConversationRecordKey.toVeilid() == key);
if (contactIndex == -1) {
await addState(key, AsyncValue.error('Chat not found for conversation'));
return;

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:fixnum/fixnum.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
@ -21,8 +22,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
required ActiveAccountInfo activeAccountInfo,
required proto.Account account,
required this.activeChatCubit,
}) : _activeAccountInfo = activeAccountInfo,
super(
}) : super(
open: () => _open(activeAccountInfo, account),
decodeElement: proto.Chat.fromBuffer);
@ -39,16 +39,30 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
return dhtRecord;
}
Future<proto.ChatSettings> getDefaultChatSettings(
proto.Contact contact) async {
final pronouns = contact.editedProfile.pronouns.isEmpty
? ''
: ' (${contact.editedProfile.pronouns})';
return proto.ChatSettings()
..title = '${contact.editedProfile.name}$pronouns'
..description = ''
..defaultExpiration = Int64.ZERO;
}
/// Create a new chat (singleton for single contact chats)
Future<void> getOrCreateChatSingleContact({
required TypedKey remoteConversationRecordKey,
required proto.Contact contact,
}) async {
// Make local copy so we don't share the buffer
final localConversationRecordKey =
contact.localConversationRecordKey.toVeilid();
final remoteConversationRecordKey =
contact.remoteConversationRecordKey.toVeilid();
// Add Chat to account's list
// if this fails, don't keep retrying, user can try again later
await operateWrite((writer) async {
final remoteConversationRecordKeyProto =
remoteConversationRecordKey.toProto();
// See if we have added this chat already
for (var i = 0; i < writer.length; i++) {
final cbuf = await writer.getItem(i);
@ -56,26 +70,18 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
throw Exception('Failed to get chat');
}
final c = proto.Chat.fromBuffer(cbuf);
if (c.remoteConversationRecordKey == remoteConversationRecordKeyProto) {
if (c.localConversationRecordKey ==
contact.localConversationRecordKey) {
// Nothing to do here
return;
}
}
final accountRecordKey = _activeAccountInfo
.userLogin.accountRecordInfo.accountRecord.recordKey;
// Make a record that can store the reconciled version of the chat
final reconciledChatRecord = await (await DHTLog.create(
debugName:
'ChatListCubit::getOrCreateChatSingleContact::ReconciledChat',
parent: accountRecordKey))
.scope((r) async => r.recordPointer);
// Create conversation type Chat
// Create 1:1 conversation type Chat
final chat = proto.Chat()
..type = proto.ChatType.SINGLE_CONTACT
..remoteConversationRecordKey = remoteConversationRecordKeyProto
..reconciledChatRecord = reconciledChatRecord.toProto();
..settings = await getDefaultChatSettings(contact)
..localConversationRecordKey = localConversationRecordKey.toProto()
..remoteConversationRecordKey = remoteConversationRecordKey.toProto();
// Add chat
final added = await writer.tryAddItem(chat.writeToBuffer());
@ -87,15 +93,16 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
/// Delete a chat
Future<void> deleteChat(
{required TypedKey remoteConversationRecordKey}) async {
final remoteConversationKey = remoteConversationRecordKey.toProto();
{required TypedKey localConversationRecordKey}) async {
final localConversationRecordKeyProto =
localConversationRecordKey.toProto();
// 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 {
if (activeChatCubit.state == remoteConversationRecordKey) {
if (activeChatCubit.state == localConversationRecordKey) {
activeChatCubit.setActiveChat(null);
}
for (var i = 0; i < writer.length; i++) {
@ -104,7 +111,8 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
if (c == null) {
throw Exception('Failed to get chat');
}
if (c.remoteConversationRecordKey == remoteConversationKey) {
if (c.localConversationRecordKey ==
localConversationRecordKeyProto) {
// Found the right chat
await writer.removeItem(i);
return c;
@ -116,10 +124,10 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
// chat record now
if (deletedItem != null) {
try {
await DHTRecordPool.instance.deleteRecord(
deletedItem.reconciledChatRecord.toVeilid().recordKey);
await SingleContactMessagesCubit.cleanupAndDeleteMessages(
localConversationRecordKey: localConversationRecordKey);
} on Exception catch (e) {
log.debug('error removing reconciled chat record: $e', e);
log.debug('error removing reconciled chat table: $e', e);
}
}
}
@ -132,10 +140,9 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
return IMap();
}
return IMap.fromIterable(stateValue,
keyMapper: (e) => e.value.remoteConversationRecordKey.toVeilid(),
keyMapper: (e) => e.value.localConversationRecordKey.toVeilid(),
valueMapper: (e) => e.value);
}
final ActiveChatCubit activeChatCubit;
final ActiveAccountInfo _activeAccountInfo;
}

View File

@ -24,9 +24,9 @@ class ChatSingleContactItemWidget extends StatelessWidget {
BuildContext context,
) {
final activeChatCubit = context.watch<ActiveChatCubit>();
final remoteConversationRecordKey =
_contact.remoteConversationRecordKey.toVeilid();
final selected = activeChatCubit.state == remoteConversationRecordKey;
final localConversationRecordKey =
_contact.localConversationRecordKey.toVeilid();
final selected = activeChatCubit.state == localConversationRecordKey;
return SliderTile(
key: ObjectKey(_contact),
@ -38,7 +38,7 @@ class ChatSingleContactItemWidget extends StatelessWidget {
icon: Icons.chat,
onTap: () {
singleFuture(activeChatCubit, () async {
activeChatCubit.setActiveChat(remoteConversationRecordKey);
activeChatCubit.setActiveChat(localConversationRecordKey);
});
},
endActions: [
@ -49,7 +49,7 @@ class ChatSingleContactItemWidget extends StatelessWidget {
onPressed: (context) async {
final chatListCubit = context.read<ChatListCubit>();
await chatListCubit.deleteChat(
remoteConversationRecordKey: remoteConversationRecordKey);
localConversationRecordKey: localConversationRecordKey);
})
],
);

View File

@ -20,7 +20,7 @@ class ChatSingleContactListWidget extends StatelessWidget {
return contactListV.builder((context, contactList) {
final contactMap = IMap.fromIterable(contactList,
keyMapper: (c) => c.value.remoteConversationRecordKey,
keyMapper: (c) => c.value.localConversationRecordKey,
valueMapper: (c) => c.value);
final chatListV = context.watch<ChatListCubit>().state;
@ -36,7 +36,7 @@ class ChatSingleContactListWidget extends StatelessWidget {
initialList: chatList.map((x) => x.value).toList(),
itemBuilder: (c) {
final contact =
contactMap[c.remoteConversationRecordKey];
contactMap[c.localConversationRecordKey];
if (contact == null) {
return const Text('...');
}
@ -49,7 +49,7 @@ class ChatSingleContactListWidget extends StatelessWidget {
final lowerValue = value.toLowerCase();
return chatList.map((x) => x.value).where((c) {
final contact =
contactMap[c.remoteConversationRecordKey];
contactMap[c.localConversationRecordKey];
if (contact == null) {
return false;
}

View File

@ -27,12 +27,12 @@ class ContactInvitationItemWidget extends StatelessWidget {
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
// final remoteConversationKey =
// contact.remoteConversationRecordKey.toVeilid();
// final localConversationKey =
// contact.localConversationRecordKey.toVeilid();
const selected =
false; // xxx: eventually when we have selectable invitations:
// activeContactCubit.state == remoteConversationRecordKey;
// activeContactCubit.state == localConversationRecordKey;
final tileDisabled =
disabled || context.watch<ContactInvitationListCubit>().isBusy;

View File

@ -76,8 +76,8 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
if (item == null) {
throw Exception('Failed to get contact');
}
if (item.remoteConversationRecordKey ==
contact.remoteConversationRecordKey) {
if (item.localConversationRecordKey ==
contact.localConversationRecordKey) {
await writer.removeItem(i);
return item;
}

View File

@ -29,11 +29,11 @@ class ContactItemWidget extends StatelessWidget {
Widget build(
BuildContext context,
) {
final remoteConversationKey =
contact.remoteConversationRecordKey.toVeilid();
final localConversationRecordKey =
contact.localConversationRecordKey.toVeilid();
const selected = false; // xxx: eventually when we have selectable contacts:
// activeContactCubit.state == remoteConversationRecordKey;
// activeContactCubit.state == localConversationRecordKey;
final tileDisabled = disabled || context.watch<ContactListCubit>().isBusy;
@ -49,8 +49,7 @@ class ContactItemWidget extends StatelessWidget {
// Start a chat
final chatListCubit = context.read<ChatListCubit>();
await chatListCubit.getOrCreateChatSingleContact(
remoteConversationRecordKey: remoteConversationKey);
await chatListCubit.getOrCreateChatSingleContact(contact: contact);
// Click over to chats
if (context.mounted) {
await MainPager.of(context)
@ -69,7 +68,7 @@ class ContactItemWidget extends StatelessWidget {
// Remove any chats for this contact
await chatListCubit.deleteChat(
remoteConversationRecordKey: remoteConversationKey);
localConversationRecordKey: localConversationRecordKey);
// Delete the contact itself
await contactListCubit.deleteContact(contact: contact);

View File

@ -34,7 +34,7 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
return const EmptyChatWidget();
}
return ChatComponent.builder(
remoteConversationRecordKey: activeChatRemoteConversationKey);
localConversationRecordKey: activeChatRemoteConversationKey);
}
@override

View File

@ -66,13 +66,13 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
Material(color: Colors.transparent, child: buildUserPanel()));
Widget buildTabletRightPane(BuildContext context) {
final activeChatRemoteConversationKey =
final activeChatLocalConversationKey =
context.watch<ActiveChatCubit>().state;
if (activeChatRemoteConversationKey == null) {
if (activeChatLocalConversationKey == null) {
return const EmptyChatWidget();
}
return ChatComponent.builder(
remoteConversationRecordKey: activeChatRemoteConversationKey);
localConversationRecordKey: activeChatLocalConversationKey);
}
// ignore: prefer_expression_function_bodies

View File

@ -350,9 +350,9 @@ class Message_Text extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Message.Text', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..aOS(1, _omitFieldNames ? '' : 'text')
..aOS(2, _omitFieldNames ? '' : 'topic')
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'replyId', subBuilder: $0.TypedKey.create)
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'replyId', $pb.PbFieldType.OY)
..a<$fixnum.Int64>(4, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'viewLimit', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..a<$core.int>(5, _omitFieldNames ? '' : 'viewLimit', $pb.PbFieldType.OU3)
..pc<Attachment>(6, _omitFieldNames ? '' : 'attachments', $pb.PbFieldType.PM, subBuilder: Attachment.create)
..hasRequiredFields = false
;
@ -397,15 +397,13 @@ class Message_Text extends $pb.GeneratedMessage {
void clearTopic() => clearField(2);
@$pb.TagNumber(3)
$0.TypedKey get replyId => $_getN(2);
$core.List<$core.int> get replyId => $_getN(2);
@$pb.TagNumber(3)
set replyId($0.TypedKey v) { setField(3, v); }
set replyId($core.List<$core.int> v) { $_setBytes(2, v); }
@$pb.TagNumber(3)
$core.bool hasReplyId() => $_has(2);
@$pb.TagNumber(3)
void clearReplyId() => clearField(3);
@$pb.TagNumber(3)
$0.TypedKey ensureReplyId() => $_ensure(2);
@$pb.TagNumber(4)
$fixnum.Int64 get expiration => $_getI64(3);
@ -417,9 +415,9 @@ class Message_Text extends $pb.GeneratedMessage {
void clearExpiration() => clearField(4);
@$pb.TagNumber(5)
$fixnum.Int64 get viewLimit => $_getI64(4);
$core.int get viewLimit => $_getIZ(4);
@$pb.TagNumber(5)
set viewLimit($fixnum.Int64 v) { $_setInt64(4, v); }
set viewLimit($core.int v) { $_setUnsignedInt32(4, v); }
@$pb.TagNumber(5)
$core.bool hasViewLimit() => $_has(4);
@$pb.TagNumber(5)
@ -517,13 +515,13 @@ class Message_ControlDelete extends $pb.GeneratedMessage {
$core.List<$0.TypedKey> get ids => $_getList(0);
}
class Message_ControlClear extends $pb.GeneratedMessage {
factory Message_ControlClear() => create();
Message_ControlClear._() : super();
factory Message_ControlClear.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Message_ControlClear.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
class Message_ControlErase extends $pb.GeneratedMessage {
factory Message_ControlErase() => create();
Message_ControlErase._() : super();
factory Message_ControlErase.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Message_ControlErase.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Message.ControlClear', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Message.ControlErase', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'timestamp', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..hasRequiredFields = false
;
@ -532,22 +530,22 @@ class Message_ControlClear extends $pb.GeneratedMessage {
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
Message_ControlClear clone() => Message_ControlClear()..mergeFromMessage(this);
Message_ControlErase clone() => Message_ControlErase()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
Message_ControlClear copyWith(void Function(Message_ControlClear) updates) => super.copyWith((message) => updates(message as Message_ControlClear)) as Message_ControlClear;
Message_ControlErase copyWith(void Function(Message_ControlErase) updates) => super.copyWith((message) => updates(message as Message_ControlErase)) as Message_ControlErase;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static Message_ControlClear create() => Message_ControlClear._();
Message_ControlClear createEmptyInstance() => create();
static $pb.PbList<Message_ControlClear> createRepeated() => $pb.PbList<Message_ControlClear>();
static Message_ControlErase create() => Message_ControlErase._();
Message_ControlErase createEmptyInstance() => create();
static $pb.PbList<Message_ControlErase> createRepeated() => $pb.PbList<Message_ControlErase>();
@$core.pragma('dart2js:noInline')
static Message_ControlClear getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Message_ControlClear>(create);
static Message_ControlClear? _defaultInstance;
static Message_ControlErase getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Message_ControlErase>(create);
static Message_ControlErase? _defaultInstance;
@$pb.TagNumber(1)
$fixnum.Int64 get timestamp => $_getI64(0);
@ -735,7 +733,7 @@ enum Message_Kind {
text,
secret,
delete,
clear_7,
erase,
settings,
permissions,
membership,
@ -753,7 +751,7 @@ class Message extends $pb.GeneratedMessage {
4 : Message_Kind.text,
5 : Message_Kind.secret,
6 : Message_Kind.delete,
7 : Message_Kind.clear_7,
7 : Message_Kind.erase,
8 : Message_Kind.settings,
9 : Message_Kind.permissions,
10 : Message_Kind.membership,
@ -762,13 +760,13 @@ class Message extends $pb.GeneratedMessage {
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Message', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..oo(0, [4, 5, 6, 7, 8, 9, 10, 11])
..aOM<$0.TypedKey>(1, _omitFieldNames ? '' : 'id', subBuilder: $0.TypedKey.create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OY)
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'author', subBuilder: $0.TypedKey.create)
..a<$fixnum.Int64>(3, _omitFieldNames ? '' : 'timestamp', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..aOM<Message_Text>(4, _omitFieldNames ? '' : 'text', subBuilder: Message_Text.create)
..aOM<Message_Secret>(5, _omitFieldNames ? '' : 'secret', subBuilder: Message_Secret.create)
..aOM<Message_ControlDelete>(6, _omitFieldNames ? '' : 'delete', subBuilder: Message_ControlDelete.create)
..aOM<Message_ControlClear>(7, _omitFieldNames ? '' : 'clear', subBuilder: Message_ControlClear.create)
..aOM<Message_ControlErase>(7, _omitFieldNames ? '' : 'erase', subBuilder: Message_ControlErase.create)
..aOM<Message_ControlSettings>(8, _omitFieldNames ? '' : 'settings', subBuilder: Message_ControlSettings.create)
..aOM<Message_ControlPermissions>(9, _omitFieldNames ? '' : 'permissions', subBuilder: Message_ControlPermissions.create)
..aOM<Message_ControlMembership>(10, _omitFieldNames ? '' : 'membership', subBuilder: Message_ControlMembership.create)
@ -802,15 +800,13 @@ class Message extends $pb.GeneratedMessage {
void clearKind() => clearField($_whichOneof(0));
@$pb.TagNumber(1)
$0.TypedKey get id => $_getN(0);
$core.List<$core.int> get id => $_getN(0);
@$pb.TagNumber(1)
set id($0.TypedKey v) { setField(1, v); }
set id($core.List<$core.int> v) { $_setBytes(0, v); }
@$pb.TagNumber(1)
$core.bool hasId() => $_has(0);
@$pb.TagNumber(1)
void clearId() => clearField(1);
@$pb.TagNumber(1)
$0.TypedKey ensureId() => $_ensure(0);
@$pb.TagNumber(2)
$0.TypedKey get author => $_getN(1);
@ -866,15 +862,15 @@ class Message extends $pb.GeneratedMessage {
Message_ControlDelete ensureDelete() => $_ensure(5);
@$pb.TagNumber(7)
Message_ControlClear get clear_7 => $_getN(6);
Message_ControlErase get erase => $_getN(6);
@$pb.TagNumber(7)
set clear_7(Message_ControlClear v) { setField(7, v); }
set erase(Message_ControlErase v) { setField(7, v); }
@$pb.TagNumber(7)
$core.bool hasClear_7() => $_has(6);
$core.bool hasErase() => $_has(6);
@$pb.TagNumber(7)
void clearClear_7() => clearField(7);
void clearErase() => clearField(7);
@$pb.TagNumber(7)
Message_ControlClear ensureClear_7() => $_ensure(6);
Message_ControlErase ensureErase() => $_ensure(6);
@$pb.TagNumber(8)
Message_ControlSettings get settings => $_getN(7);

View File

@ -159,20 +159,20 @@ final $typed_data.Uint8List chatSettingsDescriptor = $convert.base64Decode(
const Message$json = {
'1': 'Message',
'2': [
{'1': 'id', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'id'},
{'1': 'id', '3': 1, '4': 1, '5': 12, '10': 'id'},
{'1': 'author', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'author'},
{'1': 'timestamp', '3': 3, '4': 1, '5': 4, '10': 'timestamp'},
{'1': 'text', '3': 4, '4': 1, '5': 11, '6': '.veilidchat.Message.Text', '9': 0, '10': 'text'},
{'1': 'secret', '3': 5, '4': 1, '5': 11, '6': '.veilidchat.Message.Secret', '9': 0, '10': 'secret'},
{'1': 'delete', '3': 6, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlDelete', '9': 0, '10': 'delete'},
{'1': 'clear', '3': 7, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlClear', '9': 0, '10': 'clear'},
{'1': 'erase', '3': 7, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlErase', '9': 0, '10': 'erase'},
{'1': 'settings', '3': 8, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlSettings', '9': 0, '10': 'settings'},
{'1': 'permissions', '3': 9, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlPermissions', '9': 0, '10': 'permissions'},
{'1': 'membership', '3': 10, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlMembership', '9': 0, '10': 'membership'},
{'1': 'moderation', '3': 11, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlModeration', '9': 0, '10': 'moderation'},
{'1': 'signature', '3': 12, '4': 1, '5': 11, '6': '.veilid.Signature', '10': 'signature'},
],
'3': [Message_Text$json, Message_Secret$json, Message_ControlDelete$json, Message_ControlClear$json, Message_ControlSettings$json, Message_ControlPermissions$json, Message_ControlMembership$json, Message_ControlModeration$json],
'3': [Message_Text$json, Message_Secret$json, Message_ControlDelete$json, Message_ControlErase$json, Message_ControlSettings$json, Message_ControlPermissions$json, Message_ControlMembership$json, Message_ControlModeration$json],
'8': [
{'1': 'kind'},
],
@ -183,12 +183,16 @@ const Message_Text$json = {
'1': 'Text',
'2': [
{'1': 'text', '3': 1, '4': 1, '5': 9, '10': 'text'},
{'1': 'topic', '3': 2, '4': 1, '5': 9, '10': 'topic'},
{'1': 'reply_id', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'replyId'},
{'1': 'topic', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'topic', '17': true},
{'1': 'reply_id', '3': 3, '4': 1, '5': 12, '9': 1, '10': 'replyId', '17': true},
{'1': 'expiration', '3': 4, '4': 1, '5': 4, '10': 'expiration'},
{'1': 'view_limit', '3': 5, '4': 1, '5': 4, '10': 'viewLimit'},
{'1': 'view_limit', '3': 5, '4': 1, '5': 13, '10': 'viewLimit'},
{'1': 'attachments', '3': 6, '4': 3, '5': 11, '6': '.veilidchat.Attachment', '10': 'attachments'},
],
'8': [
{'1': '_topic'},
{'1': '_reply_id'},
],
};
@$core.Deprecated('Use messageDescriptor instead')
@ -209,8 +213,8 @@ const Message_ControlDelete$json = {
};
@$core.Deprecated('Use messageDescriptor instead')
const Message_ControlClear$json = {
'1': 'ControlClear',
const Message_ControlErase$json = {
'1': 'ControlErase',
'2': [
{'1': 'timestamp', '3': 1, '4': 1, '5': 4, '10': 'timestamp'},
],
@ -251,32 +255,32 @@ const Message_ControlModeration$json = {
/// Descriptor for `Message`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List messageDescriptor = $convert.base64Decode(
'CgdNZXNzYWdlEiAKAmlkGAEgASgLMhAudmVpbGlkLlR5cGVkS2V5UgJpZBIoCgZhdXRob3IYAi'
'ABKAsyEC52ZWlsaWQuVHlwZWRLZXlSBmF1dGhvchIcCgl0aW1lc3RhbXAYAyABKARSCXRpbWVz'
'dGFtcBIuCgR0ZXh0GAQgASgLMhgudmVpbGlkY2hhdC5NZXNzYWdlLlRleHRIAFIEdGV4dBI0Cg'
'ZzZWNyZXQYBSABKAsyGi52ZWlsaWRjaGF0Lk1lc3NhZ2UuU2VjcmV0SABSBnNlY3JldBI7CgZk'
'ZWxldGUYBiABKAsyIS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbERlbGV0ZUgAUgZkZWxldG'
'USOAoFY2xlYXIYByABKAsyIC52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbENsZWFySABSBWNs'
'ZWFyEkEKCHNldHRpbmdzGAggASgLMiMudmVpbGlkY2hhdC5NZXNzYWdlLkNvbnRyb2xTZXR0aW'
'5nc0gAUghzZXR0aW5ncxJKCgtwZXJtaXNzaW9ucxgJIAEoCzImLnZlaWxpZGNoYXQuTWVzc2Fn'
'ZS5Db250cm9sUGVybWlzc2lvbnNIAFILcGVybWlzc2lvbnMSRwoKbWVtYmVyc2hpcBgKIAEoCz'
'IlLnZlaWxpZGNoYXQuTWVzc2FnZS5Db250cm9sTWVtYmVyc2hpcEgAUgptZW1iZXJzaGlwEkcK'
'Cm1vZGVyYXRpb24YCyABKAsyJS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbE1vZGVyYXRpb2'
'5IAFIKbW9kZXJhdGlvbhIvCglzaWduYXR1cmUYDCABKAsyES52ZWlsaWQuU2lnbmF0dXJlUglz'
'aWduYXR1cmUa1gEKBFRleHQSEgoEdGV4dBgBIAEoCVIEdGV4dBIUCgV0b3BpYxgCIAEoCVIFdG'
'9waWMSKwoIcmVwbHlfaWQYAyABKAsyEC52ZWlsaWQuVHlwZWRLZXlSB3JlcGx5SWQSHgoKZXhw'
'aXJhdGlvbhgEIAEoBFIKZXhwaXJhdGlvbhIdCgp2aWV3X2xpbWl0GAUgASgEUgl2aWV3TGltaX'
'QSOAoLYXR0YWNobWVudHMYBiADKAsyFi52ZWlsaWRjaGF0LkF0dGFjaG1lbnRSC2F0dGFjaG1l'
'bnRzGkgKBlNlY3JldBIeCgpjaXBoZXJ0ZXh0GAEgASgMUgpjaXBoZXJ0ZXh0Eh4KCmV4cGlyYX'
'Rpb24YAiABKARSCmV4cGlyYXRpb24aMwoNQ29udHJvbERlbGV0ZRIiCgNpZHMYASADKAsyEC52'
'ZWlsaWQuVHlwZWRLZXlSA2lkcxosCgxDb250cm9sQ2xlYXISHAoJdGltZXN0YW1wGAEgASgEUg'
'l0aW1lc3RhbXAaRwoPQ29udHJvbFNldHRpbmdzEjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlk'
'Y2hhdC5DaGF0U2V0dGluZ3NSCHNldHRpbmdzGk8KEkNvbnRyb2xQZXJtaXNzaW9ucxI5CgtwZX'
'JtaXNzaW9ucxgBIAEoCzIXLnZlaWxpZGNoYXQuUGVybWlzc2lvbnNSC3Blcm1pc3Npb25zGksK'
'EUNvbnRyb2xNZW1iZXJzaGlwEjYKCm1lbWJlcnNoaXAYASABKAsyFi52ZWlsaWRjaGF0Lk1lbW'
'JlcnNoaXBSCm1lbWJlcnNoaXAafQoRQ29udHJvbE1vZGVyYXRpb24SMwoMYWNjZXB0ZWRfaWRz'
'GAEgAygLMhAudmVpbGlkLlR5cGVkS2V5UgthY2NlcHRlZElkcxIzCgxyZWplY3RlZF9pZHMYAi'
'ADKAsyEC52ZWlsaWQuVHlwZWRLZXlSC3JlamVjdGVkSWRzQgYKBGtpbmQ=');
'CgdNZXNzYWdlEg4KAmlkGAEgASgMUgJpZBIoCgZhdXRob3IYAiABKAsyEC52ZWlsaWQuVHlwZW'
'RLZXlSBmF1dGhvchIcCgl0aW1lc3RhbXAYAyABKARSCXRpbWVzdGFtcBIuCgR0ZXh0GAQgASgL'
'MhgudmVpbGlkY2hhdC5NZXNzYWdlLlRleHRIAFIEdGV4dBI0CgZzZWNyZXQYBSABKAsyGi52ZW'
'lsaWRjaGF0Lk1lc3NhZ2UuU2VjcmV0SABSBnNlY3JldBI7CgZkZWxldGUYBiABKAsyIS52ZWls'
'aWRjaGF0Lk1lc3NhZ2UuQ29udHJvbERlbGV0ZUgAUgZkZWxldGUSOAoFZXJhc2UYByABKAsyIC'
'52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbEVyYXNlSABSBWVyYXNlEkEKCHNldHRpbmdzGAgg'
'ASgLMiMudmVpbGlkY2hhdC5NZXNzYWdlLkNvbnRyb2xTZXR0aW5nc0gAUghzZXR0aW5ncxJKCg'
'twZXJtaXNzaW9ucxgJIAEoCzImLnZlaWxpZGNoYXQuTWVzc2FnZS5Db250cm9sUGVybWlzc2lv'
'bnNIAFILcGVybWlzc2lvbnMSRwoKbWVtYmVyc2hpcBgKIAEoCzIlLnZlaWxpZGNoYXQuTWVzc2'
'FnZS5Db250cm9sTWVtYmVyc2hpcEgAUgptZW1iZXJzaGlwEkcKCm1vZGVyYXRpb24YCyABKAsy'
'JS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbE1vZGVyYXRpb25IAFIKbW9kZXJhdGlvbhIvCg'
'lzaWduYXR1cmUYDCABKAsyES52ZWlsaWQuU2lnbmF0dXJlUglzaWduYXR1cmUa5QEKBFRleHQS'
'EgoEdGV4dBgBIAEoCVIEdGV4dBIZCgV0b3BpYxgCIAEoCUgAUgV0b3BpY4gBARIeCghyZXBseV'
'9pZBgDIAEoDEgBUgdyZXBseUlkiAEBEh4KCmV4cGlyYXRpb24YBCABKARSCmV4cGlyYXRpb24S'
'HQoKdmlld19saW1pdBgFIAEoDVIJdmlld0xpbWl0EjgKC2F0dGFjaG1lbnRzGAYgAygLMhYudm'
'VpbGlkY2hhdC5BdHRhY2htZW50UgthdHRhY2htZW50c0IICgZfdG9waWNCCwoJX3JlcGx5X2lk'
'GkgKBlNlY3JldBIeCgpjaXBoZXJ0ZXh0GAEgASgMUgpjaXBoZXJ0ZXh0Eh4KCmV4cGlyYXRpb2'
'4YAiABKARSCmV4cGlyYXRpb24aMwoNQ29udHJvbERlbGV0ZRIiCgNpZHMYASADKAsyEC52ZWls'
'aWQuVHlwZWRLZXlSA2lkcxosCgxDb250cm9sRXJhc2USHAoJdGltZXN0YW1wGAEgASgEUgl0aW'
'1lc3RhbXAaRwoPQ29udHJvbFNldHRpbmdzEjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlkY2hh'
'dC5DaGF0U2V0dGluZ3NSCHNldHRpbmdzGk8KEkNvbnRyb2xQZXJtaXNzaW9ucxI5CgtwZXJtaX'
'NzaW9ucxgBIAEoCzIXLnZlaWxpZGNoYXQuUGVybWlzc2lvbnNSC3Blcm1pc3Npb25zGksKEUNv'
'bnRyb2xNZW1iZXJzaGlwEjYKCm1lbWJlcnNoaXAYASABKAsyFi52ZWlsaWRjaGF0Lk1lbWJlcn'
'NoaXBSCm1lbWJlcnNoaXAafQoRQ29udHJvbE1vZGVyYXRpb24SMwoMYWNjZXB0ZWRfaWRzGAEg'
'AygLMhAudmVpbGlkLlR5cGVkS2V5UgthY2NlcHRlZElkcxIzCgxyZWplY3RlZF9pZHMYAiADKA'
'syEC52ZWlsaWQuVHlwZWRLZXlSC3JlamVjdGVkSWRzQgYKBGtpbmQ=');
@$core.Deprecated('Use reconciledMessageDescriptor instead')
const ReconciledMessage$json = {

View File

@ -123,13 +123,13 @@ message Message {
// Text of the message
string text = 1;
// Topic of the message / Content warning
string topic = 2;
optional string topic = 2;
// Message id replied to
veilid.TypedKey reply_id = 3;
optional bytes reply_id = 3;
// Message expiration timestamp
uint64 expiration = 4;
// Message view limit before deletion
uint64 view_limit = 5;
uint32 view_limit = 5;
// Attachments on the message
repeated Attachment attachments = 6;
}
@ -148,9 +148,9 @@ message Message {
message ControlDelete {
repeated veilid.TypedKey ids = 1;
}
// A 'clear' control message
// An 'erase' control message
// Deletes a set of messages from before some timestamp
message ControlClear {
message ControlErase {
// The latest timestamp to delete messages before
// If this is zero then all messages are cleared
uint64 timestamp = 1;
@ -181,10 +181,9 @@ message Message {
//////////////////////////////////////////////////////////////////////////
// Hash of previous message from the same author,
// including its previous hash.
// Also serves as a unique key for the message.
veilid.TypedKey id = 1;
// Unique id for this author stream
// Calculated from the hash of the previous message from this author
bytes id = 1;
// Author of the message (identity public key)
veilid.TypedKey author = 2;
// Time the message was sent according to sender
@ -195,7 +194,7 @@ message Message {
Text text = 4;
Secret secret = 5;
ControlDelete delete = 6;
ControlClear clear = 7;
ControlErase erase = 7;
ControlSettings settings = 8;
ControlPermissions permissions = 9;
ControlMembership membership = 10;