mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-07-23 14:40:58 -04:00
State machine work
This commit is contained in:
parent
61855521dc
commit
be8014c97a
34 changed files with 703 additions and 505 deletions
|
@ -213,7 +213,9 @@
|
||||||
},
|
},
|
||||||
"waiting_invitation": {
|
"waiting_invitation": {
|
||||||
"accepted": "Contact invitation accepted from {name}",
|
"accepted": "Contact invitation accepted from {name}",
|
||||||
"rejected": "Contact invitation was rejected"
|
"rejected": "Contact invitation was rejected",
|
||||||
|
"invalid": "Contact invitation was not valid",
|
||||||
|
"init_failed": "Contact initialization failed"
|
||||||
},
|
},
|
||||||
"paste_invitation_dialog": {
|
"paste_invitation_dialog": {
|
||||||
"title": "Paste Contact Invite",
|
"title": "Paste Contact Invite",
|
||||||
|
|
|
@ -22,11 +22,11 @@ class PerAccountCollectionBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add account record cubit
|
// Add account record cubit
|
||||||
Future<void> _addPerAccountCollectionCubit(
|
void _addPerAccountCollectionCubit(
|
||||||
{required TypedKey superIdentityRecordKey}) async =>
|
{required TypedKey superIdentityRecordKey}) =>
|
||||||
add(
|
add(
|
||||||
superIdentityRecordKey,
|
superIdentityRecordKey,
|
||||||
() async => PerAccountCollectionCubit(
|
() => PerAccountCollectionCubit(
|
||||||
locator: _locator,
|
locator: _locator,
|
||||||
accountInfoCubit: AccountInfoCubit(
|
accountInfoCubit: AccountInfoCubit(
|
||||||
accountRepository: _accountRepository,
|
accountRepository: _accountRepository,
|
||||||
|
@ -35,11 +35,11 @@ class PerAccountCollectionBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
void removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(
|
void updateState(
|
||||||
TypedKey key, LocalAccount? oldValue, LocalAccount newValue) async {
|
TypedKey key, LocalAccount? oldValue, LocalAccount newValue) {
|
||||||
// Don't replace unless this is a totally different account
|
// Don't replace unless this is a totally different account
|
||||||
// The sub-cubit's subscription will update our state later
|
// The sub-cubit's subscription will update our state later
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
|
@ -53,7 +53,7 @@ class PerAccountCollectionBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
// sub-cubit
|
// sub-cubit
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _addPerAccountCollectionCubit(
|
_addPerAccountCollectionCubit(
|
||||||
superIdentityRecordKey: newValue.superIdentity.recordKey);
|
superIdentityRecordKey: newValue.superIdentity.recordKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ abstract mixin class $PerAccountCollectionStateCopyWith<$Res> {
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{AccountInfo accountInfo,
|
{AccountInfo accountInfo,
|
||||||
AsyncValue<Account>? avAccountRecordState,
|
AsyncValue<AccountRecordState>? avAccountRecordState,
|
||||||
AccountInfoCubit? accountInfoCubit,
|
AccountInfoCubit? accountInfoCubit,
|
||||||
AccountRecordCubit? accountRecordCubit,
|
AccountRecordCubit? accountRecordCubit,
|
||||||
ContactInvitationListCubit? contactInvitationListCubit,
|
ContactInvitationListCubit? contactInvitationListCubit,
|
||||||
|
@ -147,9 +147,9 @@ class _$PerAccountCollectionStateCopyWithImpl<$Res>
|
||||||
: accountInfo // ignore: cast_nullable_to_non_nullable
|
: accountInfo // ignore: cast_nullable_to_non_nullable
|
||||||
as AccountInfo,
|
as AccountInfo,
|
||||||
avAccountRecordState: freezed == avAccountRecordState
|
avAccountRecordState: freezed == avAccountRecordState
|
||||||
? _self.avAccountRecordState!
|
? _self.avAccountRecordState
|
||||||
: avAccountRecordState // ignore: cast_nullable_to_non_nullable
|
: avAccountRecordState // ignore: cast_nullable_to_non_nullable
|
||||||
as AsyncValue<Account>?,
|
as AsyncValue<AccountRecordState>?,
|
||||||
accountInfoCubit: freezed == accountInfoCubit
|
accountInfoCubit: freezed == accountInfoCubit
|
||||||
? _self.accountInfoCubit
|
? _self.accountInfoCubit
|
||||||
: accountInfoCubit // ignore: cast_nullable_to_non_nullable
|
: accountInfoCubit // ignore: cast_nullable_to_non_nullable
|
||||||
|
@ -227,7 +227,7 @@ class _PerAccountCollectionState extends PerAccountCollectionState {
|
||||||
@override
|
@override
|
||||||
final AccountInfo accountInfo;
|
final AccountInfo accountInfo;
|
||||||
@override
|
@override
|
||||||
final AsyncValue<Account>? avAccountRecordState;
|
final AsyncValue<AccountRecordState>? avAccountRecordState;
|
||||||
@override
|
@override
|
||||||
final AccountInfoCubit? accountInfoCubit;
|
final AccountInfoCubit? accountInfoCubit;
|
||||||
@override
|
@override
|
||||||
|
@ -326,7 +326,7 @@ abstract mixin class _$PerAccountCollectionStateCopyWith<$Res>
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{AccountInfo accountInfo,
|
{AccountInfo accountInfo,
|
||||||
AsyncValue<Account>? avAccountRecordState,
|
AsyncValue<AccountRecordState>? avAccountRecordState,
|
||||||
AccountInfoCubit? accountInfoCubit,
|
AccountInfoCubit? accountInfoCubit,
|
||||||
AccountRecordCubit? accountRecordCubit,
|
AccountRecordCubit? accountRecordCubit,
|
||||||
ContactInvitationListCubit? contactInvitationListCubit,
|
ContactInvitationListCubit? contactInvitationListCubit,
|
||||||
|
@ -375,7 +375,7 @@ class __$PerAccountCollectionStateCopyWithImpl<$Res>
|
||||||
avAccountRecordState: freezed == avAccountRecordState
|
avAccountRecordState: freezed == avAccountRecordState
|
||||||
? _self.avAccountRecordState
|
? _self.avAccountRecordState
|
||||||
: avAccountRecordState // ignore: cast_nullable_to_non_nullable
|
: avAccountRecordState // ignore: cast_nullable_to_non_nullable
|
||||||
as AsyncValue<Account>?,
|
as AsyncValue<AccountRecordState>?,
|
||||||
accountInfoCubit: freezed == accountInfoCubit
|
accountInfoCubit: freezed == accountInfoCubit
|
||||||
? _self.accountInfoCubit
|
? _self.accountInfoCubit
|
||||||
: accountInfoCubit // ignore: cast_nullable_to_non_nullable
|
: accountInfoCubit // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
|
@ -67,8 +67,8 @@ abstract mixin class $UserLoginCopyWith<$Res> {
|
||||||
_$UserLoginCopyWithImpl;
|
_$UserLoginCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> superIdentityRecordKey,
|
{TypedKey superIdentityRecordKey,
|
||||||
Typed<FixedEncodedString43> identitySecret,
|
TypedSecret identitySecret,
|
||||||
AccountRecordInfo accountRecordInfo,
|
AccountRecordInfo accountRecordInfo,
|
||||||
Timestamp lastActive});
|
Timestamp lastActive});
|
||||||
|
|
||||||
|
@ -94,13 +94,13 @@ class _$UserLoginCopyWithImpl<$Res> implements $UserLoginCopyWith<$Res> {
|
||||||
}) {
|
}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
superIdentityRecordKey: null == superIdentityRecordKey
|
superIdentityRecordKey: null == superIdentityRecordKey
|
||||||
? _self.superIdentityRecordKey!
|
? _self.superIdentityRecordKey
|
||||||
: superIdentityRecordKey // ignore: cast_nullable_to_non_nullable
|
: superIdentityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as TypedKey,
|
||||||
identitySecret: null == identitySecret
|
identitySecret: null == identitySecret
|
||||||
? _self.identitySecret!
|
? _self.identitySecret
|
||||||
: identitySecret // ignore: cast_nullable_to_non_nullable
|
: identitySecret // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as TypedSecret,
|
||||||
accountRecordInfo: null == accountRecordInfo
|
accountRecordInfo: null == accountRecordInfo
|
||||||
? _self.accountRecordInfo
|
? _self.accountRecordInfo
|
||||||
: accountRecordInfo // ignore: cast_nullable_to_non_nullable
|
: accountRecordInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
@ -136,10 +136,10 @@ class _UserLogin implements UserLogin {
|
||||||
// SuperIdentity record key for the user
|
// SuperIdentity record key for the user
|
||||||
// used to index the local accounts table
|
// used to index the local accounts table
|
||||||
@override
|
@override
|
||||||
final Typed<FixedEncodedString43> superIdentityRecordKey;
|
final TypedKey superIdentityRecordKey;
|
||||||
// The identity secret as unlocked from the local accounts table
|
// The identity secret as unlocked from the local accounts table
|
||||||
@override
|
@override
|
||||||
final Typed<FixedEncodedString43> identitySecret;
|
final TypedSecret identitySecret;
|
||||||
// The account record key, owner key and secret pulled from the identity
|
// The account record key, owner key and secret pulled from the identity
|
||||||
@override
|
@override
|
||||||
final AccountRecordInfo accountRecordInfo;
|
final AccountRecordInfo accountRecordInfo;
|
||||||
|
@ -197,8 +197,8 @@ abstract mixin class _$UserLoginCopyWith<$Res>
|
||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> superIdentityRecordKey,
|
{TypedKey superIdentityRecordKey,
|
||||||
Typed<FixedEncodedString43> identitySecret,
|
TypedSecret identitySecret,
|
||||||
AccountRecordInfo accountRecordInfo,
|
AccountRecordInfo accountRecordInfo,
|
||||||
Timestamp lastActive});
|
Timestamp lastActive});
|
||||||
|
|
||||||
|
@ -227,11 +227,11 @@ class __$UserLoginCopyWithImpl<$Res> implements _$UserLoginCopyWith<$Res> {
|
||||||
superIdentityRecordKey: null == superIdentityRecordKey
|
superIdentityRecordKey: null == superIdentityRecordKey
|
||||||
? _self.superIdentityRecordKey
|
? _self.superIdentityRecordKey
|
||||||
: superIdentityRecordKey // ignore: cast_nullable_to_non_nullable
|
: superIdentityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as TypedKey,
|
||||||
identitySecret: null == identitySecret
|
identitySecret: null == identitySecret
|
||||||
? _self.identitySecret
|
? _self.identitySecret
|
||||||
: identitySecret // ignore: cast_nullable_to_non_nullable
|
: identitySecret // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as TypedSecret,
|
||||||
accountRecordInfo: null == accountRecordInfo
|
accountRecordInfo: null == accountRecordInfo
|
||||||
? _self.accountRecordInfo
|
? _self.accountRecordInfo
|
||||||
: accountRecordInfo // ignore: cast_nullable_to_non_nullable
|
: accountRecordInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
|
@ -214,9 +214,9 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
// Look up account cubit for this specific account
|
// Look up account cubit for this specific account
|
||||||
final perAccountCollectionBlocMapCubit =
|
final perAccountCollectionBlocMapCubit =
|
||||||
context.read<PerAccountCollectionBlocMapCubit>();
|
context.read<PerAccountCollectionBlocMapCubit>();
|
||||||
final accountRecordCubit = await perAccountCollectionBlocMapCubit
|
final accountRecordCubit = perAccountCollectionBlocMapCubit
|
||||||
.operate(widget.superIdentityRecordKey,
|
.entry(widget.superIdentityRecordKey)
|
||||||
closure: (c) async => c.accountRecordCubit);
|
?.accountRecordCubit;
|
||||||
if (accountRecordCubit == null) {
|
if (accountRecordCubit == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,12 +176,11 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||||
_remoteIdentityPublicKey, rcvdMessagesDHTLog);
|
_remoteIdentityPublicKey, rcvdMessagesDHTLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateRemoteMessagesRecordKey(
|
void updateRemoteMessagesRecordKey(TypedKey? remoteMessagesRecordKey) {
|
||||||
TypedKey? remoteMessagesRecordKey) async {
|
|
||||||
await _initWait();
|
|
||||||
|
|
||||||
_sspRemoteConversationRecordKey.updateState(remoteMessagesRecordKey,
|
_sspRemoteConversationRecordKey.updateState(remoteMessagesRecordKey,
|
||||||
(remoteMessagesRecordKey) async {
|
(remoteMessagesRecordKey) async {
|
||||||
|
await _initWait();
|
||||||
|
|
||||||
// Don't bother if nothing is changing
|
// Don't bother if nothing is changing
|
||||||
if (_remoteMessagesRecordKey == remoteMessagesRecordKey) {
|
if (_remoteMessagesRecordKey == remoteMessagesRecordKey) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -67,9 +67,9 @@ abstract mixin class $ChatComponentStateCopyWith<$Res> {
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{User? localUser,
|
{User? localUser,
|
||||||
IMap<String, User> remoteUsers,
|
IMap<UserID, User> remoteUsers,
|
||||||
IMap<String, User> historicalRemoteUsers,
|
IMap<UserID, User> historicalRemoteUsers,
|
||||||
IMap<String, User> unknownUsers,
|
IMap<UserID, User> unknownUsers,
|
||||||
AsyncValue<WindowState<Message>> messageWindow,
|
AsyncValue<WindowState<Message>> messageWindow,
|
||||||
String title});
|
String title});
|
||||||
|
|
||||||
|
@ -103,17 +103,17 @@ class _$ChatComponentStateCopyWithImpl<$Res>
|
||||||
: localUser // ignore: cast_nullable_to_non_nullable
|
: localUser // ignore: cast_nullable_to_non_nullable
|
||||||
as User?,
|
as User?,
|
||||||
remoteUsers: null == remoteUsers
|
remoteUsers: null == remoteUsers
|
||||||
? _self.remoteUsers!
|
? _self.remoteUsers
|
||||||
: remoteUsers // ignore: cast_nullable_to_non_nullable
|
: remoteUsers // ignore: cast_nullable_to_non_nullable
|
||||||
as IMap<String, User>,
|
as IMap<UserID, User>,
|
||||||
historicalRemoteUsers: null == historicalRemoteUsers
|
historicalRemoteUsers: null == historicalRemoteUsers
|
||||||
? _self.historicalRemoteUsers!
|
? _self.historicalRemoteUsers
|
||||||
: historicalRemoteUsers // ignore: cast_nullable_to_non_nullable
|
: historicalRemoteUsers // ignore: cast_nullable_to_non_nullable
|
||||||
as IMap<String, User>,
|
as IMap<UserID, User>,
|
||||||
unknownUsers: null == unknownUsers
|
unknownUsers: null == unknownUsers
|
||||||
? _self.unknownUsers!
|
? _self.unknownUsers
|
||||||
: unknownUsers // ignore: cast_nullable_to_non_nullable
|
: unknownUsers // ignore: cast_nullable_to_non_nullable
|
||||||
as IMap<String, User>,
|
as IMap<UserID, User>,
|
||||||
messageWindow: null == messageWindow
|
messageWindow: null == messageWindow
|
||||||
? _self.messageWindow
|
? _self.messageWindow
|
||||||
: messageWindow // ignore: cast_nullable_to_non_nullable
|
: messageWindow // ignore: cast_nullable_to_non_nullable
|
||||||
|
@ -167,13 +167,13 @@ class _ChatComponentState implements ChatComponentState {
|
||||||
final User? localUser;
|
final User? localUser;
|
||||||
// Active remote users
|
// Active remote users
|
||||||
@override
|
@override
|
||||||
final IMap<String, User> remoteUsers;
|
final IMap<UserID, User> remoteUsers;
|
||||||
// Historical remote users
|
// Historical remote users
|
||||||
@override
|
@override
|
||||||
final IMap<String, User> historicalRemoteUsers;
|
final IMap<UserID, User> historicalRemoteUsers;
|
||||||
// Unknown users
|
// Unknown users
|
||||||
@override
|
@override
|
||||||
final IMap<String, User> unknownUsers;
|
final IMap<UserID, User> unknownUsers;
|
||||||
// Messages state
|
// Messages state
|
||||||
@override
|
@override
|
||||||
final AsyncValue<WindowState<Message>> messageWindow;
|
final AsyncValue<WindowState<Message>> messageWindow;
|
||||||
|
@ -227,9 +227,9 @@ abstract mixin class _$ChatComponentStateCopyWith<$Res>
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{User? localUser,
|
{User? localUser,
|
||||||
IMap<String, User> remoteUsers,
|
IMap<UserID, User> remoteUsers,
|
||||||
IMap<String, User> historicalRemoteUsers,
|
IMap<UserID, User> historicalRemoteUsers,
|
||||||
IMap<String, User> unknownUsers,
|
IMap<UserID, User> unknownUsers,
|
||||||
AsyncValue<WindowState<Message>> messageWindow,
|
AsyncValue<WindowState<Message>> messageWindow,
|
||||||
String title});
|
String title});
|
||||||
|
|
||||||
|
@ -267,15 +267,15 @@ class __$ChatComponentStateCopyWithImpl<$Res>
|
||||||
remoteUsers: null == remoteUsers
|
remoteUsers: null == remoteUsers
|
||||||
? _self.remoteUsers
|
? _self.remoteUsers
|
||||||
: remoteUsers // ignore: cast_nullable_to_non_nullable
|
: remoteUsers // ignore: cast_nullable_to_non_nullable
|
||||||
as IMap<String, User>,
|
as IMap<UserID, User>,
|
||||||
historicalRemoteUsers: null == historicalRemoteUsers
|
historicalRemoteUsers: null == historicalRemoteUsers
|
||||||
? _self.historicalRemoteUsers
|
? _self.historicalRemoteUsers
|
||||||
: historicalRemoteUsers // ignore: cast_nullable_to_non_nullable
|
: historicalRemoteUsers // ignore: cast_nullable_to_non_nullable
|
||||||
as IMap<String, User>,
|
as IMap<UserID, User>,
|
||||||
unknownUsers: null == unknownUsers
|
unknownUsers: null == unknownUsers
|
||||||
? _self.unknownUsers
|
? _self.unknownUsers
|
||||||
: unknownUsers // ignore: cast_nullable_to_non_nullable
|
: unknownUsers // ignore: cast_nullable_to_non_nullable
|
||||||
as IMap<String, User>,
|
as IMap<UserID, User>,
|
||||||
messageWindow: null == messageWindow
|
messageWindow: null == messageWindow
|
||||||
? _self.messageWindow
|
? _self.messageWindow
|
||||||
: messageWindow // ignore: cast_nullable_to_non_nullable
|
: messageWindow // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
|
@ -54,10 +54,9 @@ class ChatComponentWidget extends StatefulWidget {
|
||||||
final contactListCubit = context.watch<ContactListCubit>();
|
final contactListCubit = context.watch<ContactListCubit>();
|
||||||
|
|
||||||
// Get the active conversation cubit
|
// Get the active conversation cubit
|
||||||
final activeConversationCubit = context
|
final activeConversationCubit = context.select<
|
||||||
.select<ActiveConversationsBlocMapCubit, ActiveConversationCubit?>(
|
ActiveConversationsBlocMapCubit,
|
||||||
(x) => x.tryOperateSync(localConversationRecordKey,
|
ActiveConversationCubit?>((x) => x.entry(localConversationRecordKey));
|
||||||
closure: (cubit) => cubit));
|
|
||||||
if (activeConversationCubit == null) {
|
if (activeConversationCubit == null) {
|
||||||
return waitingPage(onCancel: onCancel);
|
return waitingPage(onCancel: onCancel);
|
||||||
}
|
}
|
||||||
|
@ -65,8 +64,7 @@ class ChatComponentWidget extends StatefulWidget {
|
||||||
// Get the messages cubit
|
// Get the messages cubit
|
||||||
final messagesCubit = context.select<ActiveSingleContactChatBlocMapCubit,
|
final messagesCubit = context.select<ActiveSingleContactChatBlocMapCubit,
|
||||||
SingleContactMessagesCubit?>(
|
SingleContactMessagesCubit?>(
|
||||||
(x) => x.tryOperateSync(localConversationRecordKey,
|
(x) => x.entry(localConversationRecordKey));
|
||||||
closure: (cubit) => cubit));
|
|
||||||
if (messagesCubit == null) {
|
if (messagesCubit == null) {
|
||||||
return waitingPage(onCancel: onCancel);
|
return waitingPage(onCancel: onCancel);
|
||||||
}
|
}
|
||||||
|
@ -106,10 +104,11 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
_textEditingController = TextEditingController();
|
_textEditingController = TextEditingController();
|
||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
_chatStateProcessor = SingleStateProcessor<ChatComponentState>();
|
_chatStateProcessor = SingleStateProcessor<ChatComponentState>();
|
||||||
|
_focusNode = FocusNode();
|
||||||
|
|
||||||
final _chatComponentCubit = context.read<ChatComponentCubit>();
|
final chatComponentCubit = context.read<ChatComponentCubit>();
|
||||||
_chatStateProcessor.follow(_chatComponentCubit.stream,
|
_chatStateProcessor.follow(
|
||||||
_chatComponentCubit.state, _updateChatState);
|
chatComponentCubit.stream, chatComponentCubit.state, _updateChatState);
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
@ -118,6 +117,7 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
unawaited(_chatStateProcessor.close());
|
unawaited(_chatStateProcessor.close());
|
||||||
|
|
||||||
|
_focusNode.dispose();
|
||||||
_chatController.dispose();
|
_chatController.dispose();
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
_textEditingController.dispose();
|
_textEditingController.dispose();
|
||||||
|
@ -281,6 +281,7 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
// Composer builder
|
// Composer builder
|
||||||
composerBuilder: (ctx) => VcComposerWidget(
|
composerBuilder: (ctx) => VcComposerWidget(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
focusNode: _focusNode,
|
||||||
textInputAction: isAnyMobile
|
textInputAction: isAnyMobile
|
||||||
? TextInputAction.newline
|
? TextInputAction.newline
|
||||||
: TextInputAction.send,
|
: TextInputAction.send,
|
||||||
|
@ -399,6 +400,8 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleSendPressed(ChatComponentCubit chatComponentCubit, String text) {
|
void _handleSendPressed(ChatComponentCubit chatComponentCubit, String text) {
|
||||||
|
_focusNode.requestFocus();
|
||||||
|
|
||||||
if (text.startsWith('/')) {
|
if (text.startsWith('/')) {
|
||||||
chatComponentCubit.runCommand(text);
|
chatComponentCubit.runCommand(text);
|
||||||
return;
|
return;
|
||||||
|
@ -491,4 +494,5 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
late final TextEditingController _textEditingController;
|
late final TextEditingController _textEditingController;
|
||||||
late final ScrollController _scrollController;
|
late final ScrollController _scrollController;
|
||||||
late final SingleStateProcessor<ChatComponentState> _chatStateProcessor;
|
late final SingleStateProcessor<ChatComponentState> _chatStateProcessor;
|
||||||
|
late final FocusNode _focusNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
import 'package:async_tools/async_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 '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
|
|
||||||
// Watch subkey #1 of the ContactRequest record for accept/reject
|
// Watch subkey #1 of the ContactRequest record for accept/reject
|
||||||
|
typedef ContactRequestInboxState = AsyncValue<proto.SignedContactResponse?>;
|
||||||
|
|
||||||
class ContactRequestInboxCubit
|
class ContactRequestInboxCubit
|
||||||
extends DefaultDHTRecordCubit<proto.SignedContactResponse?> {
|
extends DefaultDHTRecordCubit<proto.SignedContactResponse?> {
|
||||||
ContactRequestInboxCubit(
|
ContactRequestInboxCubit(
|
||||||
|
|
|
@ -9,10 +9,62 @@ import 'package:veilid_support/veilid_support.dart';
|
||||||
import '../../account_manager/account_manager.dart';
|
import '../../account_manager/account_manager.dart';
|
||||||
import '../../conversation/conversation.dart';
|
import '../../conversation/conversation.dart';
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import '../../tools/tools.dart';
|
|
||||||
import '../models/accepted_contact.dart';
|
import '../models/accepted_contact.dart';
|
||||||
import 'contact_request_inbox_cubit.dart';
|
import 'contact_request_inbox_cubit.dart';
|
||||||
|
|
||||||
|
/// State of WaitingInvitationCubit
|
||||||
|
sealed class WaitingInvitationState
|
||||||
|
implements StateMachineState<WaitingInvitationState> {
|
||||||
|
WaitingInvitationState({required this.global});
|
||||||
|
final WaitingInvitationStateGlobal global;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WaitingInvitationStateGlobal {
|
||||||
|
WaitingInvitationStateGlobal(
|
||||||
|
{required this.accountInfo,
|
||||||
|
required this.accountRecordCubit,
|
||||||
|
required this.contactInvitationRecord});
|
||||||
|
final AccountInfo accountInfo;
|
||||||
|
final AccountRecordCubit accountRecordCubit;
|
||||||
|
final proto.ContactInvitationRecord contactInvitationRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State of WaitingInvitationCubit:
|
||||||
|
/// Signature was invalid
|
||||||
|
class WaitingInvitationStateInvalidSignature
|
||||||
|
with StateMachineEndState<WaitingInvitationState>
|
||||||
|
implements WaitingInvitationState {
|
||||||
|
const WaitingInvitationStateInvalidSignature({required this.global});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WaitingInvitationStateGlobal global;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State of WaitingInvitationCubit:
|
||||||
|
/// Failed to initialize
|
||||||
|
class WaitingInvitationStateInitFailed
|
||||||
|
with StateMachineEndState<WaitingInvitationState>
|
||||||
|
implements WaitingInvitationState {
|
||||||
|
const WaitingInvitationStateInitFailed(
|
||||||
|
{required this.global, required this.exception});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WaitingInvitationStateGlobal global;
|
||||||
|
final Exception exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State of WaitingInvitationCubit:
|
||||||
|
/// Finished normally with an invitation status
|
||||||
|
class WaitingInvitationStateInvitationStatus
|
||||||
|
with StateMachineEndState<WaitingInvitationState>
|
||||||
|
implements WaitingInvitationState {
|
||||||
|
const WaitingInvitationStateInvitationStatus(
|
||||||
|
{required this.global, required this.status});
|
||||||
|
@override
|
||||||
|
final WaitingInvitationStateGlobal global;
|
||||||
|
final InvitationStatus status;
|
||||||
|
}
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class InvitationStatus extends Equatable {
|
class InvitationStatus extends Equatable {
|
||||||
const InvitationStatus({required this.acceptedContact});
|
const InvitationStatus({required this.acceptedContact});
|
||||||
|
@ -22,39 +74,55 @@ class InvitationStatus extends Equatable {
|
||||||
List<Object?> get props => [acceptedContact];
|
List<Object?> get props => [acceptedContact];
|
||||||
}
|
}
|
||||||
|
|
||||||
class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
/// State of WaitingInvitationCubit:
|
||||||
proto.SignedContactResponse?> {
|
/// Waiting for the invited contact to accept/reject the invitation
|
||||||
WaitingInvitationCubit(
|
class WaitingInvitationStateWaitForContactResponse
|
||||||
ContactRequestInboxCubit super.input, {
|
extends AsyncCubitReactorState<
|
||||||
required AccountInfo accountInfo,
|
WaitingInvitationState,
|
||||||
required AccountRecordCubit accountRecordCubit,
|
ContactRequestInboxState,
|
||||||
required proto.ContactInvitationRecord contactInvitationRecord,
|
ContactRequestInboxCubit> implements WaitingInvitationState {
|
||||||
}) : super(
|
WaitingInvitationStateWaitForContactResponse(super.create,
|
||||||
transform: (signedContactResponse) => _transform(
|
{required this.global})
|
||||||
signedContactResponse,
|
: super(onState: (ctx) async {
|
||||||
accountInfo: accountInfo,
|
final signedContactResponse = ctx.state.asData?.value;
|
||||||
accountRecordCubit: accountRecordCubit,
|
|
||||||
contactInvitationRecord: contactInvitationRecord));
|
|
||||||
|
|
||||||
static Future<AsyncValue<InvitationStatus>> _transform(
|
|
||||||
proto.SignedContactResponse? signedContactResponse,
|
|
||||||
{required AccountInfo accountInfo,
|
|
||||||
required AccountRecordCubit accountRecordCubit,
|
|
||||||
required proto.ContactInvitationRecord contactInvitationRecord}) async {
|
|
||||||
if (signedContactResponse == null) {
|
if (signedContactResponse == null) {
|
||||||
return const AsyncValue.loading();
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final contactResponse = proto.ContactResponse.fromBuffer(
|
||||||
|
signedContactResponse.contactResponse);
|
||||||
|
final contactSuperRecordKey =
|
||||||
|
contactResponse.superIdentityRecordKey.toVeilid();
|
||||||
|
|
||||||
|
// Fetch the remote contact's account superidentity
|
||||||
|
return WaitingInvitationStateWaitForContactSuperIdentity(
|
||||||
|
() => SuperIdentityCubit(superRecordKey: contactSuperRecordKey),
|
||||||
|
global: global,
|
||||||
|
signedContactResponse: signedContactResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WaitingInvitationStateGlobal global;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State of WaitingInvitationCubit:
|
||||||
|
/// Once an accept/reject happens, get the SuperIdentity of the recipient
|
||||||
|
class WaitingInvitationStateWaitForContactSuperIdentity
|
||||||
|
extends AsyncCubitReactorState<WaitingInvitationState, SuperIdentityState,
|
||||||
|
SuperIdentityCubit> implements WaitingInvitationState {
|
||||||
|
WaitingInvitationStateWaitForContactSuperIdentity(super.create,
|
||||||
|
{required this.global,
|
||||||
|
required proto.SignedContactResponse signedContactResponse})
|
||||||
|
: super(onState: (ctx) async {
|
||||||
|
final contactSuperIdentity = ctx.state.asData?.value;
|
||||||
|
if (contactSuperIdentity == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final contactResponseBytes =
|
final contactResponseBytes =
|
||||||
Uint8List.fromList(signedContactResponse.contactResponse);
|
Uint8List.fromList(signedContactResponse.contactResponse);
|
||||||
final contactResponse =
|
final contactResponse =
|
||||||
proto.ContactResponse.fromBuffer(contactResponseBytes);
|
proto.ContactResponse.fromBuffer(contactResponseBytes);
|
||||||
final contactIdentityMasterRecordKey =
|
|
||||||
contactResponse.superIdentityRecordKey.toVeilid();
|
|
||||||
|
|
||||||
// Fetch the remote contact's account master
|
|
||||||
final contactSuperIdentity = await SuperIdentity.open(
|
|
||||||
superRecordKey: contactIdentityMasterRecordKey);
|
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
final idcs = await contactSuperIdentity.currentInstance.cryptoSystem;
|
final idcs = await contactSuperIdentity.currentInstance.cryptoSystem;
|
||||||
|
@ -62,54 +130,104 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
||||||
if (!await idcs.verify(contactSuperIdentity.currentInstance.publicKey,
|
if (!await idcs.verify(contactSuperIdentity.currentInstance.publicKey,
|
||||||
contactResponseBytes, signature)) {
|
contactResponseBytes, signature)) {
|
||||||
// Could not verify signature of contact response
|
// Could not verify signature of contact response
|
||||||
return AsyncValue.error('Invalid signature on contact response.');
|
return WaitingInvitationStateInvalidSignature(
|
||||||
|
global: global,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for rejection
|
// Check for rejection
|
||||||
if (!contactResponse.accept) {
|
if (!contactResponse.accept) {
|
||||||
// Rejection
|
// Rejection
|
||||||
return const AsyncValue.data(InvitationStatus(acceptedContact: null));
|
return WaitingInvitationStateInvitationStatus(
|
||||||
|
global: global,
|
||||||
|
status: const InvitationStatus(acceptedContact: null),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull profile from remote conversation key
|
// Pull profile from remote conversation key
|
||||||
final remoteConversationRecordKey =
|
final remoteConversationRecordKey =
|
||||||
contactResponse.remoteConversationRecordKey.toVeilid();
|
contactResponse.remoteConversationRecordKey.toVeilid();
|
||||||
|
|
||||||
final conversation = ConversationCubit(
|
return WaitingInvitationStateWaitForConversation(
|
||||||
accountInfo: accountInfo,
|
() => ConversationCubit(
|
||||||
|
accountInfo: global.accountInfo,
|
||||||
remoteIdentityPublicKey:
|
remoteIdentityPublicKey:
|
||||||
contactSuperIdentity.currentInstance.typedPublicKey,
|
contactSuperIdentity.currentInstance.typedPublicKey,
|
||||||
remoteConversationRecordKey: remoteConversationRecordKey);
|
remoteConversationRecordKey: remoteConversationRecordKey),
|
||||||
|
global: global,
|
||||||
|
remoteConversationRecordKey: remoteConversationRecordKey,
|
||||||
|
contactSuperIdentity: contactSuperIdentity,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// wait for remote conversation for up to 20 seconds
|
@override
|
||||||
proto.Conversation? remoteConversation;
|
final WaitingInvitationStateGlobal global;
|
||||||
var retryCount = 20;
|
}
|
||||||
do {
|
|
||||||
await conversation.refresh();
|
/// State of WaitingInvitationCubit:
|
||||||
remoteConversation = conversation.state.asData?.value.remoteConversation;
|
/// Wait for the conversation cubit to initialize so we can return the
|
||||||
if (remoteConversation != null) {
|
/// accepted invitation
|
||||||
break;
|
class WaitingInvitationStateWaitForConversation extends AsyncCubitReactorState<
|
||||||
}
|
WaitingInvitationState,
|
||||||
log.info('Remote conversation could not be read. Waiting...');
|
AsyncValue<ConversationState>,
|
||||||
await Future<void>.delayed(const Duration(seconds: 1));
|
ConversationCubit> implements WaitingInvitationState {
|
||||||
retryCount--;
|
WaitingInvitationStateWaitForConversation(super.create,
|
||||||
} while (retryCount > 0);
|
{required this.global,
|
||||||
if (remoteConversation == null) {
|
required TypedKey remoteConversationRecordKey,
|
||||||
return AsyncValue.error('Invitation accept timed out.');
|
required SuperIdentity contactSuperIdentity})
|
||||||
|
: super(onState: (ctx) async {
|
||||||
|
final remoteConversation = ctx.state.asData?.value.remoteConversation;
|
||||||
|
final localConversation = ctx.state.asData?.value.localConversation;
|
||||||
|
if (remoteConversation == null || localConversation != null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop reacting to the conversation cubit
|
||||||
|
ctx.stop();
|
||||||
|
|
||||||
// Complete the local conversation now that we have the remote profile
|
// Complete the local conversation now that we have the remote profile
|
||||||
final remoteProfile = remoteConversation.profile;
|
final remoteProfile = remoteConversation.profile;
|
||||||
final localConversationRecordKey =
|
final localConversationRecordKey = global
|
||||||
contactInvitationRecord.localConversationRecordKey.toVeilid();
|
.contactInvitationRecord.localConversationRecordKey
|
||||||
return conversation.initLocalConversation(
|
.toVeilid();
|
||||||
profile: accountRecordCubit.state.asData!.value.profile,
|
|
||||||
existingConversationRecordKey: localConversationRecordKey,
|
try {
|
||||||
callback: (localConversation) async => AsyncValue.data(InvitationStatus(
|
await ctx.cubit.initLocalConversation(
|
||||||
|
profile: global.accountRecordCubit.state.asData!.value.profile,
|
||||||
|
existingConversationRecordKey: localConversationRecordKey);
|
||||||
|
} on Exception catch (e) {
|
||||||
|
return WaitingInvitationStateInitFailed(
|
||||||
|
global: global, exception: e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WaitingInvitationStateInvitationStatus(
|
||||||
|
global: global,
|
||||||
|
status: InvitationStatus(
|
||||||
acceptedContact: AcceptedContact(
|
acceptedContact: AcceptedContact(
|
||||||
remoteProfile: remoteProfile,
|
remoteProfile: remoteProfile,
|
||||||
remoteIdentity: contactSuperIdentity,
|
remoteIdentity: contactSuperIdentity,
|
||||||
remoteConversationRecordKey: remoteConversationRecordKey,
|
remoteConversationRecordKey: remoteConversationRecordKey,
|
||||||
localConversationRecordKey: localConversationRecordKey))));
|
localConversationRecordKey: localConversationRecordKey)));
|
||||||
}
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WaitingInvitationStateGlobal global;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invitation state processor for sent invitations
|
||||||
|
class WaitingInvitationCubit extends StateMachineCubit<WaitingInvitationState> {
|
||||||
|
WaitingInvitationCubit({
|
||||||
|
required ContactRequestInboxCubit Function() initialStateCreate,
|
||||||
|
required AccountInfo accountInfo,
|
||||||
|
required AccountRecordCubit accountRecordCubit,
|
||||||
|
required proto.ContactInvitationRecord contactInvitationRecord,
|
||||||
|
}) : super(
|
||||||
|
WaitingInvitationStateWaitForContactResponse(
|
||||||
|
initialStateCreate,
|
||||||
|
global: WaitingInvitationStateGlobal(
|
||||||
|
accountInfo: accountInfo,
|
||||||
|
accountRecordCubit: accountRecordCubit,
|
||||||
|
contactInvitationRecord: contactInvitationRecord),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,13 @@ import '../../proto/proto.dart' as proto;
|
||||||
import 'cubits.dart';
|
import 'cubits.dart';
|
||||||
|
|
||||||
typedef WaitingInvitationsBlocMapState
|
typedef WaitingInvitationsBlocMapState
|
||||||
= BlocMapState<TypedKey, AsyncValue<InvitationStatus>>;
|
= BlocMapState<TypedKey, WaitingInvitationState>;
|
||||||
|
|
||||||
// Map of contactRequestInboxRecordKey to WaitingInvitationCubit
|
// Map of contactRequestInboxRecordKey to WaitingInvitationCubit
|
||||||
// Wraps a contact invitation cubit to watch for accept/reject
|
// Wraps a contact invitation cubit to watch for accept/reject
|
||||||
// Automatically follows the state of a ContactInvitationListCubit.
|
// Automatically follows the state of a ContactInvitationListCubit.
|
||||||
class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
AsyncValue<InvitationStatus>, WaitingInvitationCubit>
|
WaitingInvitationState, WaitingInvitationCubit>
|
||||||
with
|
with
|
||||||
StateMapFollower<DHTShortArrayCubitState<proto.ContactInvitationRecord>,
|
StateMapFollower<DHTShortArrayCubitState<proto.ContactInvitationRecord>,
|
||||||
TypedKey, proto.ContactInvitationRecord> {
|
TypedKey, proto.ContactInvitationRecord> {
|
||||||
|
@ -45,13 +45,12 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
await super.close();
|
await super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addWaitingInvitation(
|
void _addWaitingInvitation(
|
||||||
{required proto.ContactInvitationRecord
|
{required proto.ContactInvitationRecord contactInvitationRecord}) =>
|
||||||
contactInvitationRecord}) async =>
|
|
||||||
add(
|
add(
|
||||||
contactInvitationRecord.contactRequestInbox.recordKey.toVeilid(),
|
contactInvitationRecord.contactRequestInbox.recordKey.toVeilid(),
|
||||||
() async => WaitingInvitationCubit(
|
() => WaitingInvitationCubit(
|
||||||
ContactRequestInboxCubit(
|
initialStateCreate: () => ContactRequestInboxCubit(
|
||||||
accountInfo: _accountInfo,
|
accountInfo: _accountInfo,
|
||||||
contactInvitationRecord: contactInvitationRecord),
|
contactInvitationRecord: contactInvitationRecord),
|
||||||
accountInfo: _accountInfo,
|
accountInfo: _accountInfo,
|
||||||
|
@ -63,11 +62,30 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
WaitingInvitationsBlocMapState newState) async {
|
WaitingInvitationsBlocMapState newState) async {
|
||||||
for (final entry in newState.entries) {
|
for (final entry in newState.entries) {
|
||||||
final contactRequestInboxRecordKey = entry.key;
|
final contactRequestInboxRecordKey = entry.key;
|
||||||
final invStatus = entry.value.asData?.value;
|
|
||||||
// Skip invitations that have not yet been accepted or rejected
|
switch (entry.value) {
|
||||||
if (invStatus == null) {
|
case WaitingInvitationStateInvalidSignature():
|
||||||
continue;
|
// Signature was invalid, display an error and treat like a rejection
|
||||||
}
|
await _contactInvitationListCubit.deleteInvitation(
|
||||||
|
accepted: false,
|
||||||
|
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
|
||||||
|
|
||||||
|
// Notify about error state
|
||||||
|
_notificationsCubit.error(
|
||||||
|
text: translate('waiting_invitation.invalid'));
|
||||||
|
case final WaitingInvitationStateInitFailed st:
|
||||||
|
// Initialization error, display an error and treat like a rejection
|
||||||
|
await _contactInvitationListCubit.deleteInvitation(
|
||||||
|
accepted: false,
|
||||||
|
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
|
||||||
|
|
||||||
|
// Notify about error state
|
||||||
|
_notificationsCubit.error(
|
||||||
|
text: '${translate('waiting_invitation.init_failed')}\n'
|
||||||
|
'${st.exception}');
|
||||||
|
|
||||||
|
case final WaitingInvitationStateInvitationStatus st:
|
||||||
|
final invStatus = st.status;
|
||||||
|
|
||||||
// Delete invitation and process the accepted or rejected contact
|
// Delete invitation and process the accepted or rejected contact
|
||||||
final acceptedContact = invStatus.acceptedContact;
|
final acceptedContact = invStatus.acceptedContact;
|
||||||
|
@ -102,20 +120,28 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
'waiting_invitation.rejected',
|
'waiting_invitation.rejected',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
case WaitingInvitationStateWaitForContactResponse():
|
||||||
|
// Do nothing, still waiting for contact response
|
||||||
|
break;
|
||||||
|
case WaitingInvitationStateWaitForContactSuperIdentity():
|
||||||
|
// Do nothing, still waiting for contact SuperIdentity
|
||||||
|
break;
|
||||||
|
case WaitingInvitationStateWaitForConversation():
|
||||||
|
// Do nothing, still waiting for conversation
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
void removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(
|
void updateState(TypedKey key, proto.ContactInvitationRecord? oldValue,
|
||||||
TypedKey key,
|
proto.ContactInvitationRecord newValue) {
|
||||||
proto.ContactInvitationRecord? oldValue,
|
_addWaitingInvitation(contactInvitationRecord: newValue);
|
||||||
proto.ContactInvitationRecord newValue) async {
|
|
||||||
await _addWaitingInvitation(contactInvitationRecord: newValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
|
|
|
@ -47,12 +47,12 @@ class ValidContactInvitation {
|
||||||
accountInfo: _accountInfo,
|
accountInfo: _accountInfo,
|
||||||
remoteIdentityPublicKey:
|
remoteIdentityPublicKey:
|
||||||
_contactSuperIdentity.currentInstance.typedPublicKey);
|
_contactSuperIdentity.currentInstance.typedPublicKey);
|
||||||
return conversation.initLocalConversation(
|
final localConversationRecordKey =
|
||||||
profile: profile,
|
await conversation.initLocalConversation(profile: profile);
|
||||||
callback: (localConversation) async {
|
|
||||||
final contactResponse = proto.ContactResponse()
|
final contactResponse = proto.ContactResponse()
|
||||||
..accept = true
|
..accept = true
|
||||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
..remoteConversationRecordKey = localConversationRecordKey.toProto()
|
||||||
..superIdentityRecordKey =
|
..superIdentityRecordKey =
|
||||||
_accountInfo.superIdentityRecordKey.toProto();
|
_accountInfo.superIdentityRecordKey.toProto();
|
||||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||||
|
@ -66,18 +66,17 @@ class ValidContactInvitation {
|
||||||
..identitySignature = identitySignature.toProto();
|
..identitySignature = identitySignature.toProto();
|
||||||
|
|
||||||
// Write the acceptance to the inbox
|
// Write the acceptance to the inbox
|
||||||
await contactRequestInbox
|
await contactRequestInbox.eventualWriteProtobuf(signedContactResponse,
|
||||||
.eventualWriteProtobuf(signedContactResponse, subkey: 1);
|
subkey: 1);
|
||||||
|
|
||||||
return AcceptedContact(
|
return AcceptedContact(
|
||||||
remoteProfile: _contactRequestPrivate.profile,
|
remoteProfile: _contactRequestPrivate.profile,
|
||||||
remoteIdentity: _contactSuperIdentity,
|
remoteIdentity: _contactSuperIdentity,
|
||||||
remoteConversationRecordKey:
|
remoteConversationRecordKey:
|
||||||
_contactRequestPrivate.chatRecordKey.toVeilid(),
|
_contactRequestPrivate.chatRecordKey.toVeilid(),
|
||||||
localConversationRecordKey: localConversation.key,
|
localConversationRecordKey: localConversationRecordKey,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
log.debug('exception: $e', e);
|
log.debug('exception: $e', e);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -112,8 +112,8 @@ class PasteInvitationDialogState extends State<PasteInvitationDialog> {
|
||||||
constraints: const BoxConstraints(maxHeight: 200),
|
constraints: const BoxConstraints(maxHeight: 200),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
enabled: !dialogState.isValidating,
|
enabled: !dialogState.isValidating,
|
||||||
onChanged: (text) async =>
|
autofocus: true,
|
||||||
_onPasteChanged(text, validateInviteData),
|
onChanged: (text) => _onPasteChanged(text, validateInviteData),
|
||||||
style: monoStyle,
|
style: monoStyle,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
|
@ -129,14 +129,11 @@ class PasteInvitationDialogState extends State<PasteInvitationDialog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: prefer_expression_function_bodies
|
Widget build(BuildContext context) => InvitationDialog(
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InvitationDialog(
|
|
||||||
locator: widget._locator,
|
locator: widget._locator,
|
||||||
onValidationCancelled: onValidationCancelled,
|
onValidationCancelled: onValidationCancelled,
|
||||||
onValidationSuccess: onValidationSuccess,
|
onValidationSuccess: onValidationSuccess,
|
||||||
onValidationFailed: onValidationFailed,
|
onValidationFailed: onValidationFailed,
|
||||||
inviteControlIsValid: inviteControlIsValid,
|
inviteControlIsValid: inviteControlIsValid,
|
||||||
buildInviteControl: buildInviteControl);
|
buildInviteControl: buildInviteControl);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@ class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
scanWindow: scanWindow,
|
scanWindow: scanWindow,
|
||||||
controller: cameraController,
|
controller: cameraController,
|
||||||
errorBuilder: (context, error, child) =>
|
errorBuilder: (context, error) =>
|
||||||
ScannerErrorWidget(error: error),
|
ScannerErrorWidget(error: error),
|
||||||
onDetect: (c) {
|
onDetect: (c) {
|
||||||
final barcode = c.barcodes.firstOrNull;
|
final barcode = c.barcodes.firstOrNull;
|
||||||
|
@ -242,6 +242,10 @@ class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||||
return const Icon(Icons.camera_front);
|
return const Icon(Icons.camera_front);
|
||||||
case CameraFacing.back:
|
case CameraFacing.back:
|
||||||
return const Icon(Icons.camera_rear);
|
return const Icon(Icons.camera_rear);
|
||||||
|
case CameraFacing.external:
|
||||||
|
return const Icon(Icons.camera_alt);
|
||||||
|
case CameraFacing.unknown:
|
||||||
|
return const Icon(Icons.question_mark);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -27,8 +27,7 @@ class ContactDetailsWidget extends StatefulWidget {
|
||||||
final void Function(bool)? onModifiedState;
|
final void Function(bool)? onModifiedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ContactDetailsWidgetState extends State<ContactDetailsWidget>
|
class _ContactDetailsWidgetState extends State<ContactDetailsWidget> {
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => SingleChildScrollView(
|
Widget build(BuildContext context) => SingleChildScrollView(
|
||||||
child: EditContactForm(
|
child: EditContactForm(
|
||||||
|
|
|
@ -23,7 +23,6 @@ class ContactItemWidget extends StatelessWidget {
|
||||||
_onDelete = onDelete;
|
_onDelete = onDelete;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: prefer_expression_function_bodies
|
|
||||||
Widget build(
|
Widget build(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import 'package:searchable_listview/searchable_listview.dart';
|
||||||
import 'package:star_menu/star_menu.dart';
|
import 'package:star_menu/star_menu.dart';
|
||||||
import 'package:veilid_support/veilid_support.dart';
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
import '../../chat_list/chat_list.dart';
|
|
||||||
import '../../contact_invitation/contact_invitation.dart';
|
import '../../contact_invitation/contact_invitation.dart';
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import '../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
|
@ -45,6 +44,7 @@ class ContactsBrowserElement {
|
||||||
class ContactsBrowser extends StatefulWidget {
|
class ContactsBrowser extends StatefulWidget {
|
||||||
const ContactsBrowser(
|
const ContactsBrowser(
|
||||||
{required this.onContactSelected,
|
{required this.onContactSelected,
|
||||||
|
required this.onContactDeleted,
|
||||||
required this.onStartChat,
|
required this.onStartChat,
|
||||||
this.selectedContactRecordKey,
|
this.selectedContactRecordKey,
|
||||||
super.key});
|
super.key});
|
||||||
|
@ -52,6 +52,7 @@ class ContactsBrowser extends StatefulWidget {
|
||||||
State<ContactsBrowser> createState() => _ContactsBrowserState();
|
State<ContactsBrowser> createState() => _ContactsBrowserState();
|
||||||
|
|
||||||
final Future<void> Function(proto.Contact? contact) onContactSelected;
|
final Future<void> Function(proto.Contact? contact) onContactSelected;
|
||||||
|
final Future<void> Function(proto.Contact contact) onContactDeleted;
|
||||||
final Future<void> Function(proto.Contact contact) onStartChat;
|
final Future<void> Function(proto.Contact contact) onStartChat;
|
||||||
final TypedKey? selectedContactRecordKey;
|
final TypedKey? selectedContactRecordKey;
|
||||||
|
|
||||||
|
@ -66,7 +67,10 @@ class ContactsBrowser extends StatefulWidget {
|
||||||
'onContactSelected', onContactSelected))
|
'onContactSelected', onContactSelected))
|
||||||
..add(
|
..add(
|
||||||
ObjectFlagProperty<Future<void> Function(proto.Contact contact)>.has(
|
ObjectFlagProperty<Future<void> Function(proto.Contact contact)>.has(
|
||||||
'onStartChat', onStartChat));
|
'onStartChat', onStartChat))
|
||||||
|
..add(
|
||||||
|
ObjectFlagProperty<Future<void> Function(proto.Contact contact)>.has(
|
||||||
|
'onContactDeleted', onContactDeleted));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +93,6 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
final menuParams = StarMenuParameters(
|
final menuParams = StarMenuParameters(
|
||||||
shape: MenuShape.linear,
|
shape: MenuShape.linear,
|
||||||
centerOffset: const Offset(0, 64),
|
centerOffset: const Offset(0, 64),
|
||||||
// backgroundParams:
|
|
||||||
// BackgroundParams(backgroundColor: theme.shadowColor.withAlpha(128)),
|
|
||||||
boundaryBackground: BoundaryBackground(
|
boundaryBackground: BoundaryBackground(
|
||||||
color: menuBackgroundColor,
|
color: menuBackgroundColor,
|
||||||
decoration: ShapeDecoration(
|
decoration: ShapeDecoration(
|
||||||
|
@ -145,7 +147,7 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
|
|
||||||
return StarMenu(
|
return StarMenu(
|
||||||
items: inviteMenuItems,
|
items: inviteMenuItems,
|
||||||
onItemTapped: (_index, controller) {
|
onItemTapped: (_, controller) {
|
||||||
controller.closeMenu!();
|
controller.closeMenu!();
|
||||||
},
|
},
|
||||||
controller: _invitationMenuController,
|
controller: _invitationMenuController,
|
||||||
|
@ -162,16 +164,13 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
//final scaleConfig = theme.extension<ScaleConfig>()!;
|
|
||||||
|
|
||||||
final cilState = context.watch<ContactInvitationListCubit>().state;
|
final cilState = context.watch<ContactInvitationListCubit>().state;
|
||||||
//final cilBusy = cilState.busy;
|
|
||||||
final contactInvitationRecordList =
|
final contactInvitationRecordList =
|
||||||
cilState.state.asData?.value.map((x) => x.value).toIList() ??
|
cilState.state.asData?.value.map((x) => x.value).toIList() ??
|
||||||
const IListConst([]);
|
const IListConst([]);
|
||||||
|
|
||||||
final ciState = context.watch<ContactListCubit>().state;
|
final ciState = context.watch<ContactListCubit>().state;
|
||||||
//final ciBusy = ciState.busy;
|
|
||||||
final contactList =
|
final contactList =
|
||||||
ciState.state.asData?.value.map((x) => x.value).toIList();
|
ciState.state.asData?.value.map((x) => x.value).toIList();
|
||||||
|
|
||||||
|
@ -201,8 +200,8 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
contact.localConversationRecordKey.toVeilid(),
|
contact.localConversationRecordKey.toVeilid(),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
onDoubleTap: _onStartChat,
|
onDoubleTap: _onStartChat,
|
||||||
onTap: _onSelectContact,
|
onTap: onContactSelected,
|
||||||
onDelete: _onDeleteContact)
|
onDelete: _onContactDeleted)
|
||||||
.paddingLTRB(0, 4, 0, 0);
|
.paddingLTRB(0, 4, 0, 0);
|
||||||
case ContactsBrowserElementKind.invitation:
|
case ContactsBrowserElementKind.invitation:
|
||||||
final invitation = element.invitation!;
|
final invitation = element.invitation!;
|
||||||
|
@ -261,7 +260,7 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onSelectContact(proto.Contact contact) async {
|
Future<void> onContactSelected(proto.Contact contact) async {
|
||||||
await widget.onContactSelected(contact);
|
await widget.onContactSelected(contact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,20 +268,8 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
await widget.onStartChat(contact);
|
await widget.onStartChat(contact);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onDeleteContact(proto.Contact contact) async {
|
Future<void> _onContactDeleted(proto.Contact contact) async {
|
||||||
final localConversationRecordKey =
|
await widget.onContactDeleted(contact);
|
||||||
contact.localConversationRecordKey.toVeilid();
|
|
||||||
|
|
||||||
final contactListCubit = context.read<ContactListCubit>();
|
|
||||||
final chatListCubit = context.read<ChatListCubit>();
|
|
||||||
|
|
||||||
// Delete the contact itself
|
|
||||||
await contactListCubit.deleteContact(
|
|
||||||
localConversationRecordKey: localConversationRecordKey);
|
|
||||||
|
|
||||||
// Remove any chats for this contact
|
|
||||||
await chatListCubit.deleteChat(
|
|
||||||
localConversationRecordKey: localConversationRecordKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -103,6 +103,7 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||||
.toVeilid(),
|
.toVeilid(),
|
||||||
onContactSelected: _onContactSelected,
|
onContactSelected: _onContactSelected,
|
||||||
onStartChat: _onChatStarted,
|
onStartChat: _onChatStarted,
|
||||||
|
onContactDeleted: _onContactDeleted,
|
||||||
).paddingLTRB(4, 0, 4, 8)))),
|
).paddingLTRB(4, 0, 4, 8)))),
|
||||||
if (enableRight && enableLeft)
|
if (enableRight && enableLeft)
|
||||||
Container(
|
Container(
|
||||||
|
@ -157,6 +158,40 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _onContactDeleted(proto.Contact contact) async {
|
||||||
|
if (contact == _selectedContact && _isModified) {
|
||||||
|
final ok = await showConfirmModal(
|
||||||
|
context: context,
|
||||||
|
title: translate('confirmation.discard_changes'),
|
||||||
|
text: translate('confirmation.are_you_sure_discard'));
|
||||||
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_selectedContact = null;
|
||||||
|
_isModified = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
final localConversationRecordKey =
|
||||||
|
contact.localConversationRecordKey.toVeilid();
|
||||||
|
|
||||||
|
final contactListCubit = context.read<ContactListCubit>();
|
||||||
|
final chatListCubit = context.read<ChatListCubit>();
|
||||||
|
|
||||||
|
// Delete the contact itself
|
||||||
|
await contactListCubit.deleteContact(
|
||||||
|
localConversationRecordKey: localConversationRecordKey);
|
||||||
|
|
||||||
|
// Remove any chats for this contact
|
||||||
|
await chatListCubit.deleteChat(
|
||||||
|
localConversationRecordKey: localConversationRecordKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
proto.Contact? _selectedContact;
|
proto.Contact? _selectedContact;
|
||||||
bool _isModified = false;
|
var _isModified = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,11 +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> _addDirectConversation(
|
void _addDirectConversation(
|
||||||
{required TypedKey remoteIdentityPublicKey,
|
{required TypedKey remoteIdentityPublicKey,
|
||||||
required TypedKey localConversationRecordKey,
|
required TypedKey localConversationRecordKey,
|
||||||
required TypedKey remoteConversationRecordKey}) async =>
|
required TypedKey remoteConversationRecordKey}) =>
|
||||||
add(localConversationRecordKey, () async {
|
add(localConversationRecordKey, () {
|
||||||
// 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(
|
||||||
|
@ -129,11 +129,10 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
void removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(
|
void updateState(TypedKey key, proto.Chat? oldValue, proto.Chat newValue) {
|
||||||
TypedKey key, proto.Chat? oldValue, proto.Chat newValue) async {
|
|
||||||
switch (newValue.whichKind()) {
|
switch (newValue.whichKind()) {
|
||||||
case proto.Chat_Kind.notSet:
|
case proto.Chat_Kind.notSet:
|
||||||
throw StateError('unknown chat kind');
|
throw StateError('unknown chat kind');
|
||||||
|
@ -161,7 +160,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _addDirectConversation(
|
_addDirectConversation(
|
||||||
remoteIdentityPublicKey: remoteIdentityPublicKey,
|
remoteIdentityPublicKey: remoteIdentityPublicKey,
|
||||||
localConversationRecordKey: localConversationRecordKey,
|
localConversationRecordKey: localConversationRecordKey,
|
||||||
remoteConversationRecordKey: remoteConversationRecordKey);
|
remoteConversationRecordKey: remoteConversationRecordKey);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
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:equatable/equatable.dart';
|
||||||
|
@ -53,12 +51,11 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
follow(activeConversationsBlocMapCubit);
|
follow(activeConversationsBlocMapCubit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addConversationMessages(_SingleContactChatState state) async {
|
void _addConversationMessages(_SingleContactChatState state) {
|
||||||
// xxx could use atomic update() function
|
update(state.localConversationRecordKey,
|
||||||
await update(state.localConversationRecordKey,
|
onUpdate: (cubit) =>
|
||||||
onUpdate: (cubit) async =>
|
|
||||||
cubit.updateRemoteMessagesRecordKey(state.remoteMessagesRecordKey),
|
cubit.updateRemoteMessagesRecordKey(state.remoteMessagesRecordKey),
|
||||||
onCreate: () async => SingleContactMessagesCubit(
|
onCreate: () => SingleContactMessagesCubit(
|
||||||
accountInfo: _accountInfo,
|
accountInfo: _accountInfo,
|
||||||
remoteIdentityPublicKey: state.remoteIdentityPublicKey,
|
remoteIdentityPublicKey: state.remoteIdentityPublicKey,
|
||||||
localConversationRecordKey: state.localConversationRecordKey,
|
localConversationRecordKey: state.localConversationRecordKey,
|
||||||
|
@ -87,13 +84,11 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
/// StateFollower /////////////////////////
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
void removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateState(
|
void updateState(TypedKey key, AsyncValue<ActiveConversationState>? oldValue,
|
||||||
TypedKey key,
|
AsyncValue<ActiveConversationState> newValue) {
|
||||||
AsyncValue<ActiveConversationState>? oldValue,
|
|
||||||
AsyncValue<ActiveConversationState> newValue) async {
|
|
||||||
final newState = _mapStateValue(newValue);
|
final newState = _mapStateValue(newValue);
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
final oldState = _mapStateValue(oldValue);
|
final oldState = _mapStateValue(oldValue);
|
||||||
|
@ -102,14 +97,14 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newState != null) {
|
if (newState != null) {
|
||||||
await _addConversationMessages(newState);
|
_addConversationMessages(newState);
|
||||||
} else if (newValue.isLoading) {
|
} else if (newValue.isLoading) {
|
||||||
await addState(key, const AsyncValue.loading());
|
addState(key, const AsyncValue.loading());
|
||||||
} else {
|
} else {
|
||||||
final (error, stackTrace) =
|
final (error, stackTrace) =
|
||||||
(newValue.asError!.error, newValue.asError!.stackTrace);
|
(newValue.asError!.error, newValue.asError!.stackTrace);
|
||||||
addError(error, stackTrace);
|
addError(error, stackTrace);
|
||||||
await addState(key, AsyncValue.error(error, stackTrace));
|
addState(key, AsyncValue.error(error, stackTrace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,12 @@ class ConversationState extends Equatable {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [localConversation, remoteConversation];
|
List<Object?> get props => [localConversation, remoteConversation];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'ConversationState('
|
||||||
|
'localConversation: ${DynamicDebug.toDebug(localConversation)}, '
|
||||||
|
'remoteConversation: ${DynamicDebug.toDebug(remoteConversation)}'
|
||||||
|
')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the control channel between two contacts
|
/// Represents the control channel between two contacts
|
||||||
|
@ -39,13 +45,11 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
TypedKey? localConversationRecordKey,
|
TypedKey? localConversationRecordKey,
|
||||||
TypedKey? remoteConversationRecordKey})
|
TypedKey? remoteConversationRecordKey})
|
||||||
: _accountInfo = accountInfo,
|
: _accountInfo = accountInfo,
|
||||||
_localConversationRecordKey = localConversationRecordKey,
|
|
||||||
_remoteIdentityPublicKey = remoteIdentityPublicKey,
|
_remoteIdentityPublicKey = remoteIdentityPublicKey,
|
||||||
_remoteConversationRecordKey = remoteConversationRecordKey,
|
|
||||||
super(const AsyncValue.loading()) {
|
super(const AsyncValue.loading()) {
|
||||||
_identityWriter = _accountInfo.identityWriter;
|
_identityWriter = _accountInfo.identityWriter;
|
||||||
|
|
||||||
if (_localConversationRecordKey != null) {
|
if (localConversationRecordKey != null) {
|
||||||
_initWait.add((_) async {
|
_initWait.add((_) async {
|
||||||
await _setLocalConversation(() async {
|
await _setLocalConversation(() async {
|
||||||
// Open local record key if it is specified
|
// Open local record key if it is specified
|
||||||
|
@ -54,7 +58,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
final writer = _identityWriter;
|
final writer = _identityWriter;
|
||||||
|
|
||||||
final record = await pool.openRecordWrite(
|
final record = await pool.openRecordWrite(
|
||||||
_localConversationRecordKey!, writer,
|
localConversationRecordKey, writer,
|
||||||
debugName: 'ConversationCubit::LocalConversation',
|
debugName: 'ConversationCubit::LocalConversation',
|
||||||
parent: accountInfo.accountRecordKey,
|
parent: accountInfo.accountRecordKey,
|
||||||
crypto: crypto);
|
crypto: crypto);
|
||||||
|
@ -64,17 +68,17 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_remoteConversationRecordKey != null) {
|
if (remoteConversationRecordKey != null) {
|
||||||
_initWait.add((cancel) async {
|
_initWait.add((cancel) async {
|
||||||
await _setRemoteConversation(() async {
|
await _setRemoteConversation(() async {
|
||||||
// Open remote record key if it is specified
|
// Open remote record key if it is specified
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
final crypto = await _cachedConversationCrypto();
|
final crypto = await _cachedConversationCrypto();
|
||||||
|
|
||||||
final record = await pool.openRecordRead(_remoteConversationRecordKey,
|
final record = await pool.openRecordRead(remoteConversationRecordKey,
|
||||||
debugName: 'ConversationCubit::RemoteConversation',
|
debugName: 'ConversationCubit::RemoteConversation',
|
||||||
parent:
|
parent:
|
||||||
await pool.getParentRecordKey(_remoteConversationRecordKey) ??
|
await pool.getParentRecordKey(remoteConversationRecordKey) ??
|
||||||
accountInfo.accountRecordKey,
|
accountInfo.accountRecordKey,
|
||||||
crypto: crypto);
|
crypto: crypto);
|
||||||
|
|
||||||
|
@ -104,13 +108,11 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
/// incomplete 'existingConversationRecord' that we need to fill
|
/// incomplete 'existingConversationRecord' that we need to fill
|
||||||
/// in now that we have the remote identity key
|
/// in now that we have the remote identity key
|
||||||
/// The ConversationCubit must not already have a local conversation
|
/// The ConversationCubit must not already have a local conversation
|
||||||
/// The callback allows for more initialization to occur and for
|
/// Returns the local conversation record key that was initialized
|
||||||
/// cleanup to delete records upon failure of the callback
|
Future<TypedKey> initLocalConversation(
|
||||||
Future<T> initLocalConversation<T>(
|
|
||||||
{required proto.Profile profile,
|
{required proto.Profile profile,
|
||||||
required FutureOr<T> Function(DHTRecord) callback,
|
|
||||||
TypedKey? existingConversationRecordKey}) async {
|
TypedKey? existingConversationRecordKey}) async {
|
||||||
assert(_localConversationRecordKey == null,
|
assert(_localConversationCubit == null,
|
||||||
'must not have a local conversation yet');
|
'must not have a local conversation yet');
|
||||||
|
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
|
@ -138,11 +140,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
schema: DHTSchema.smpl(
|
schema: DHTSchema.smpl(
|
||||||
oCnt: 0, members: [DHTSchemaMember(mKey: writer.key, mCnt: 1)]));
|
oCnt: 0, members: [DHTSchemaMember(mKey: writer.key, mCnt: 1)]));
|
||||||
}
|
}
|
||||||
final out = localConversationRecord
|
await localConversationRecord.deleteScope((localConversation) async {
|
||||||
// ignore: prefer_expression_function_bodies
|
await _initLocalMessages(
|
||||||
.deleteScope((localConversation) async {
|
|
||||||
// Make messages log
|
|
||||||
return _initLocalMessages(
|
|
||||||
localConversationKey: localConversation.key,
|
localConversationKey: localConversation.key,
|
||||||
callback: (messages) async {
|
callback: (messages) async {
|
||||||
// Create initial local conversation key contents
|
// Create initial local conversation key contents
|
||||||
|
@ -158,36 +157,31 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
throw Exception('Failed to write local conversation');
|
throw Exception('Failed to write local conversation');
|
||||||
}
|
}
|
||||||
final out = await callback(localConversation);
|
|
||||||
|
|
||||||
// Upon success emit the local conversation record to the state
|
// If success, save the new local conversation
|
||||||
_updateLocalConversationState(AsyncValue.data(conversation));
|
// record key in this object
|
||||||
|
localConversation.ref();
|
||||||
return out;
|
await _setLocalConversation(() async => localConversation);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// If success, save the new local conversation record key in this object
|
return localConversationRecord.key;
|
||||||
_localConversationRecordKey = localConversationRecord.key;
|
|
||||||
await _setLocalConversation(() async => localConversationRecord);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Force refresh of conversation keys
|
/// Force refresh of conversation keys
|
||||||
Future<void> refresh() async {
|
// Future<void> refresh() async {
|
||||||
await _initWait();
|
// await _initWait();
|
||||||
|
|
||||||
final lcc = _localConversationCubit;
|
// final lcc = _localConversationCubit;
|
||||||
final rcc = _remoteConversationCubit;
|
// final rcc = _remoteConversationCubit;
|
||||||
|
|
||||||
if (lcc != null) {
|
// if (lcc != null) {
|
||||||
await lcc.refreshDefault();
|
// await lcc.refreshDefault();
|
||||||
}
|
// }
|
||||||
if (rcc != null) {
|
// if (rcc != null) {
|
||||||
await rcc.refreshDefault();
|
// await rcc.refreshDefault();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Watch for account record changes and update the conversation
|
/// Watch for account record changes and update the conversation
|
||||||
void watchAccountChanges(Stream<AsyncValue<proto.Account>> accountStream,
|
void watchAccountChanges(Stream<AsyncValue<proto.Account>> accountStream,
|
||||||
|
@ -226,12 +220,6 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
_incrementalState = ConversationState(
|
_incrementalState = ConversationState(
|
||||||
localConversation: conv,
|
localConversation: conv,
|
||||||
remoteConversation: _incrementalState.remoteConversation);
|
remoteConversation: _incrementalState.remoteConversation);
|
||||||
// return loading still if state isn't complete
|
|
||||||
if (_localConversationRecordKey != null &&
|
|
||||||
_incrementalState.localConversation == null) {
|
|
||||||
return const AsyncValue<ConversationState>.loading();
|
|
||||||
}
|
|
||||||
// local state is complete, all remote state is emitted incrementally
|
|
||||||
return AsyncValue.data(_incrementalState);
|
return AsyncValue.data(_incrementalState);
|
||||||
},
|
},
|
||||||
loading: AsyncValue<ConversationState>.loading,
|
loading: AsyncValue<ConversationState>.loading,
|
||||||
|
@ -246,12 +234,6 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
_incrementalState = ConversationState(
|
_incrementalState = ConversationState(
|
||||||
localConversation: _incrementalState.localConversation,
|
localConversation: _incrementalState.localConversation,
|
||||||
remoteConversation: conv);
|
remoteConversation: conv);
|
||||||
// return loading still if the local state isn't complete
|
|
||||||
if (_localConversationRecordKey != null &&
|
|
||||||
_incrementalState.localConversation == null) {
|
|
||||||
return const AsyncValue<ConversationState>.loading();
|
|
||||||
}
|
|
||||||
// local state is complete, all remote state is emitted incrementally
|
|
||||||
return AsyncValue.data(_incrementalState);
|
return AsyncValue.data(_incrementalState);
|
||||||
},
|
},
|
||||||
loading: AsyncValue<ConversationState>.loading,
|
loading: AsyncValue<ConversationState>.loading,
|
||||||
|
@ -263,9 +245,12 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
// Open local converation key
|
// Open local converation key
|
||||||
Future<void> _setLocalConversation(Future<DHTRecord> Function() open) async {
|
Future<void> _setLocalConversation(Future<DHTRecord> Function() open) async {
|
||||||
assert(_localConversationCubit == null,
|
assert(_localConversationCubit == null,
|
||||||
'shoud not set local conversation twice');
|
'should not set local conversation twice');
|
||||||
_localConversationCubit = DefaultDHTRecordCubit(
|
_localConversationCubit = DefaultDHTRecordCubit(
|
||||||
open: open, decodeState: proto.Conversation.fromBuffer);
|
open: open, decodeState: proto.Conversation.fromBuffer);
|
||||||
|
|
||||||
|
await _localConversationCubit!.ready();
|
||||||
|
|
||||||
_localSubscription =
|
_localSubscription =
|
||||||
_localConversationCubit!.stream.listen(_updateLocalConversationState);
|
_localConversationCubit!.stream.listen(_updateLocalConversationState);
|
||||||
_updateLocalConversationState(_localConversationCubit!.state);
|
_updateLocalConversationState(_localConversationCubit!.state);
|
||||||
|
@ -274,9 +259,12 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
// Open remote converation key
|
// Open remote converation key
|
||||||
Future<void> _setRemoteConversation(Future<DHTRecord> Function() open) async {
|
Future<void> _setRemoteConversation(Future<DHTRecord> Function() open) async {
|
||||||
assert(_remoteConversationCubit == null,
|
assert(_remoteConversationCubit == null,
|
||||||
'shoud not set remote conversation twice');
|
'should not set remote conversation twice');
|
||||||
_remoteConversationCubit = DefaultDHTRecordCubit(
|
_remoteConversationCubit = DefaultDHTRecordCubit(
|
||||||
open: open, decodeState: proto.Conversation.fromBuffer);
|
open: open, decodeState: proto.Conversation.fromBuffer);
|
||||||
|
|
||||||
|
await _remoteConversationCubit!.ready();
|
||||||
|
|
||||||
_remoteSubscription =
|
_remoteSubscription =
|
||||||
_remoteConversationCubit!.stream.listen(_updateRemoteConversationState);
|
_remoteConversationCubit!.stream.listen(_updateRemoteConversationState);
|
||||||
_updateRemoteConversationState(_remoteConversationCubit!.state);
|
_updateRemoteConversationState(_remoteConversationCubit!.state);
|
||||||
|
@ -316,14 +304,12 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||||
final AccountInfo _accountInfo;
|
final AccountInfo _accountInfo;
|
||||||
late final KeyPair _identityWriter;
|
late final KeyPair _identityWriter;
|
||||||
final TypedKey _remoteIdentityPublicKey;
|
final TypedKey _remoteIdentityPublicKey;
|
||||||
TypedKey? _localConversationRecordKey;
|
|
||||||
final TypedKey? _remoteConversationRecordKey;
|
|
||||||
DefaultDHTRecordCubit<proto.Conversation>? _localConversationCubit;
|
DefaultDHTRecordCubit<proto.Conversation>? _localConversationCubit;
|
||||||
DefaultDHTRecordCubit<proto.Conversation>? _remoteConversationCubit;
|
DefaultDHTRecordCubit<proto.Conversation>? _remoteConversationCubit;
|
||||||
StreamSubscription<AsyncValue<proto.Conversation>>? _localSubscription;
|
StreamSubscription<AsyncValue<proto.Conversation>>? _localSubscription;
|
||||||
StreamSubscription<AsyncValue<proto.Conversation>>? _remoteSubscription;
|
StreamSubscription<AsyncValue<proto.Conversation>>? _remoteSubscription;
|
||||||
StreamSubscription<AsyncValue<proto.Account>>? _accountSubscription;
|
StreamSubscription<AsyncValue<proto.Account>>? _accountSubscription;
|
||||||
ConversationState _incrementalState = const ConversationState(
|
var _incrementalState = const ConversationState(
|
||||||
localConversation: null, remoteConversation: null);
|
localConversation: null, remoteConversation: null);
|
||||||
VeilidCrypto? _conversationCrypto;
|
VeilidCrypto? _conversationCrypto;
|
||||||
final WaitSet<void, void> _initWait = WaitSet();
|
final WaitSet<void, void> _initWait = WaitSet();
|
||||||
|
|
|
@ -17,6 +17,7 @@ const Map<String, LogLevel> _blocChangeLogLevels = {
|
||||||
'PreferencesCubit': LogLevel.debug,
|
'PreferencesCubit': LogLevel.debug,
|
||||||
'ConversationCubit': LogLevel.debug,
|
'ConversationCubit': LogLevel.debug,
|
||||||
'DefaultDHTRecordCubit<Conversation>': LogLevel.debug,
|
'DefaultDHTRecordCubit<Conversation>': LogLevel.debug,
|
||||||
|
'WaitingInvitationCubit': LogLevel.debug,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Map<String, LogLevel> _blocCreateCloseLogLevels = {};
|
const Map<String, LogLevel> _blocCreateCloseLogLevels = {};
|
||||||
|
|
|
@ -2,7 +2,8 @@ PODS:
|
||||||
- file_saver (0.0.1):
|
- file_saver (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- mobile_scanner (6.0.2):
|
- mobile_scanner (7.0.0):
|
||||||
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- package_info_plus (0.0.1):
|
- package_info_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
@ -33,7 +34,7 @@ PODS:
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`)
|
- file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
|
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`)
|
||||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
||||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
|
@ -52,7 +53,7 @@ EXTERNAL SOURCES:
|
||||||
FlutterMacOS:
|
FlutterMacOS:
|
||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
mobile_scanner:
|
mobile_scanner:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||||
pasteboard:
|
pasteboard:
|
||||||
|
@ -79,7 +80,7 @@ EXTERNAL SOURCES:
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
|
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
mobile_scanner: 0e365ed56cad24f28c0fd858ca04edefb40dfac3
|
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
||||||
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
||||||
pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7
|
pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7
|
||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
|
|
|
@ -5,18 +5,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57
|
sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "80.0.0"
|
version: "82.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e"
|
sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.0"
|
version: "7.4.5"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -29,10 +29,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
version: "2.13.0"
|
||||||
async_tools:
|
async_tools:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -53,10 +53,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: bloc_advanced_tools
|
name: bloc_advanced_tools
|
||||||
sha256: "7c7f294b425552c2d4831b01ad0d3e1f33f2bdf9acfb7b639caa072781d228cf"
|
sha256: dfb142569814952af8d93e7fe045972d847e29382471687db59913e253202f6e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.10"
|
version: "0.1.12"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -89,6 +89,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
cli_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_config
|
||||||
|
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -117,10 +125,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: coverage
|
name: coverage
|
||||||
sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43
|
sha256: "802bd084fb82e55df091ec8ad1553a7331b61c08251eef19a508b6f3f3a9858d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.13.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -149,18 +157,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.3"
|
||||||
fast_immutable_collections:
|
fast_immutable_collections:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fast_immutable_collections
|
name: fast_immutable_collections
|
||||||
sha256: "95a69b9380483dff49ae2c12c9eb92e2b4e1aeff481a33c2a20883471771598a"
|
sha256: d1aa3d7788fab06cce7f303f4969c7a16a10c865e1bd2478291a8ebcbee084e5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.3"
|
version: "11.0.4"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -299,10 +307,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.8"
|
version: "10.0.9"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -323,10 +331,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: lint_hard
|
name: lint_hard
|
||||||
sha256: ffe7058cb49e021d244d67e650a63380445b56643c2849c6929e938246b99058
|
sha256: "2073d4e83ac4e3f2b87cc615fff41abb5c2c5618e117edcd3d71f40f2186f4d5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.1.1"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -411,10 +419,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12"
|
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.16"
|
version: "2.2.17"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -483,10 +491,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: protobuf
|
name: protobuf
|
||||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
sha256: "579fe5557eae58e3adca2e999e38f02441d8aa908703854a9e0a0f47fa857731"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "4.1.0"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -658,7 +666,7 @@ packages:
|
||||||
path: "../../../../veilid/veilid-flutter"
|
path: "../../../../veilid/veilid-flutter"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.4.4"
|
version: "0.4.6"
|
||||||
veilid_support:
|
veilid_support:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -677,10 +685,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.3.1"
|
version: "15.0.0"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -701,26 +709,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket
|
name: web_socket
|
||||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "1.0.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
webdriver:
|
webdriver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webdriver
|
name: webdriver
|
||||||
sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
|
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.4"
|
version: "3.1.0"
|
||||||
webkit_inspection_protocol:
|
webkit_inspection_protocol:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -613,7 +613,7 @@ class _DHTLogSpine {
|
||||||
// xxx: Don't watch for local changes because this class already handles
|
// xxx: Don't watch for local changes because this class already handles
|
||||||
// xxx: notifying listeners and knows when it makes local changes
|
// xxx: notifying listeners and knows when it makes local changes
|
||||||
_subscription ??=
|
_subscription ??=
|
||||||
await _spineRecord.listen(localChanges: true, _onSpineChanged);
|
await _spineRecord.listen(localChanges: false, _onSpineChanged);
|
||||||
await _spineRecord.watch(subkeys: [ValueSubkeyRange.single(0)]);
|
await _spineRecord.watch(subkeys: [ValueSubkeyRange.single(0)]);
|
||||||
} on Exception {
|
} on Exception {
|
||||||
// If anything fails, try to cancel the watches
|
// If anything fails, try to cancel the watches
|
||||||
|
|
|
@ -6,14 +6,14 @@ import '../../../veilid_support.dart';
|
||||||
class DefaultDHTRecordCubit<T> extends DHTRecordCubit<T> {
|
class DefaultDHTRecordCubit<T> extends DHTRecordCubit<T> {
|
||||||
DefaultDHTRecordCubit({
|
DefaultDHTRecordCubit({
|
||||||
required super.open,
|
required super.open,
|
||||||
required T Function(List<int> data) decodeState,
|
required T Function(Uint8List data) decodeState,
|
||||||
}) : super(
|
}) : super(
|
||||||
initialStateFunction: _makeInitialStateFunction(decodeState),
|
initialStateFunction: _makeInitialStateFunction(decodeState),
|
||||||
stateFunction: _makeStateFunction(decodeState),
|
stateFunction: _makeStateFunction(decodeState),
|
||||||
watchFunction: _makeWatchFunction());
|
watchFunction: _makeWatchFunction());
|
||||||
|
|
||||||
static InitialStateFunction<T> _makeInitialStateFunction<T>(
|
static InitialStateFunction<T> _makeInitialStateFunction<T>(
|
||||||
T Function(List<int> data) decodeState) =>
|
T Function(Uint8List data) decodeState) =>
|
||||||
(record) async {
|
(record) async {
|
||||||
final initialData = await record.get();
|
final initialData = await record.get();
|
||||||
if (initialData == null) {
|
if (initialData == null) {
|
||||||
|
@ -23,7 +23,7 @@ class DefaultDHTRecordCubit<T> extends DHTRecordCubit<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
static StateFunction<T> _makeStateFunction<T>(
|
static StateFunction<T> _makeStateFunction<T>(
|
||||||
T Function(List<int> data) decodeState) =>
|
T Function(Uint8List data) decodeState) =>
|
||||||
(record, subkeys, updatedata) async {
|
(record, subkeys, updatedata) async {
|
||||||
final defaultSubkey = record.subkeyOrDefault(-1);
|
final defaultSubkey = record.subkeyOrDefault(-1);
|
||||||
if (subkeys.containsSubkey(defaultSubkey)) {
|
if (subkeys.containsSubkey(defaultSubkey)) {
|
||||||
|
|
|
@ -87,6 +87,10 @@ abstract class DHTRecordCubit<T> extends Cubit<AsyncValue<T>> {
|
||||||
await super.close();
|
await super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> ready() async {
|
||||||
|
await initWait();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> refresh(List<ValueSubkeyRange> subkeys) async {
|
Future<void> refresh(List<ValueSubkeyRange> subkeys) async {
|
||||||
await initWait();
|
await initWait();
|
||||||
|
|
||||||
|
@ -111,8 +115,6 @@ abstract class DHTRecordCubit<T> extends Cubit<AsyncValue<T>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHTRecord get record => _record;
|
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
final WaitSet<void, bool> initWait = WaitSet();
|
final WaitSet<void, bool> initWait = WaitSet();
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,5 @@ export 'exceptions.dart';
|
||||||
export 'identity.dart';
|
export 'identity.dart';
|
||||||
export 'identity_instance.dart';
|
export 'identity_instance.dart';
|
||||||
export 'super_identity.dart';
|
export 'super_identity.dart';
|
||||||
|
export 'super_identity_cubit.dart';
|
||||||
export 'writable_super_identity.dart';
|
export 'writable_super_identity.dart';
|
||||||
|
|
|
@ -64,7 +64,40 @@ sealed class SuperIdentity with _$SuperIdentity {
|
||||||
|
|
||||||
const SuperIdentity._();
|
const SuperIdentity._();
|
||||||
|
|
||||||
/// Opens an existing super identity and validates it
|
/// Ensure a SuperIdentity is valid
|
||||||
|
Future<void> validate({required TypedKey superRecordKey}) async {
|
||||||
|
// Validate current IdentityInstance
|
||||||
|
if (!await currentInstance.validateIdentityInstance(
|
||||||
|
superRecordKey: superRecordKey, superPublicKey: publicKey)) {
|
||||||
|
// Invalid current IdentityInstance signature(s)
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate deprecated IdentityInstances
|
||||||
|
for (final deprecatedInstance in deprecatedInstances) {
|
||||||
|
if (!await deprecatedInstance.validateIdentityInstance(
|
||||||
|
superRecordKey: superRecordKey, superPublicKey: publicKey)) {
|
||||||
|
// Invalid deprecated IdentityInstance signature(s)
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate SuperIdentity
|
||||||
|
final deprecatedInstancesSignatures =
|
||||||
|
deprecatedInstances.map((x) => x.signature).toList();
|
||||||
|
if (!await _validateSuperIdentitySignature(
|
||||||
|
recordKey: recordKey,
|
||||||
|
currentInstanceSignature: currentInstance.signature,
|
||||||
|
deprecatedInstancesSignatures: deprecatedInstancesSignatures,
|
||||||
|
deprecatedSuperRecordKeys: deprecatedSuperRecordKeys,
|
||||||
|
publicKey: publicKey,
|
||||||
|
signature: signature)) {
|
||||||
|
// Invalid SuperIdentity signature
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens an existing super identity, validates it, and returns it
|
||||||
static Future<SuperIdentity> open({required TypedKey superRecordKey}) async {
|
static Future<SuperIdentity> open({required TypedKey superRecordKey}) async {
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
|
@ -75,37 +108,7 @@ sealed class SuperIdentity with _$SuperIdentity {
|
||||||
final superIdentity = (await superRec.getJson(SuperIdentity.fromJson,
|
final superIdentity = (await superRec.getJson(SuperIdentity.fromJson,
|
||||||
refreshMode: DHTRecordRefreshMode.network))!;
|
refreshMode: DHTRecordRefreshMode.network))!;
|
||||||
|
|
||||||
// Validate current IdentityInstance
|
await superIdentity.validate(superRecordKey: superRecordKey);
|
||||||
if (!await superIdentity.currentInstance.validateIdentityInstance(
|
|
||||||
superRecordKey: superRecordKey,
|
|
||||||
superPublicKey: superIdentity.publicKey)) {
|
|
||||||
// Invalid current IdentityInstance signature(s)
|
|
||||||
throw IdentityException.invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate deprecated IdentityInstances
|
|
||||||
for (final deprecatedInstance in superIdentity.deprecatedInstances) {
|
|
||||||
if (!await deprecatedInstance.validateIdentityInstance(
|
|
||||||
superRecordKey: superRecordKey,
|
|
||||||
superPublicKey: superIdentity.publicKey)) {
|
|
||||||
// Invalid deprecated IdentityInstance signature(s)
|
|
||||||
throw IdentityException.invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate SuperIdentity
|
|
||||||
final deprecatedInstancesSignatures =
|
|
||||||
superIdentity.deprecatedInstances.map((x) => x.signature).toList();
|
|
||||||
if (!await _validateSuperIdentitySignature(
|
|
||||||
recordKey: superIdentity.recordKey,
|
|
||||||
currentInstanceSignature: superIdentity.currentInstance.signature,
|
|
||||||
deprecatedInstancesSignatures: deprecatedInstancesSignatures,
|
|
||||||
deprecatedSuperRecordKeys: superIdentity.deprecatedSuperRecordKeys,
|
|
||||||
publicKey: superIdentity.publicKey,
|
|
||||||
signature: superIdentity.signature)) {
|
|
||||||
// Invalid SuperIdentity signature
|
|
||||||
throw IdentityException.invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return superIdentity;
|
return superIdentity;
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:async_tools/async_tools.dart';
|
||||||
|
|
||||||
|
import '../veilid_support.dart';
|
||||||
|
|
||||||
|
typedef SuperIdentityState = AsyncValue<SuperIdentity>;
|
||||||
|
|
||||||
|
class SuperIdentityCubit extends DefaultDHTRecordCubit<SuperIdentity> {
|
||||||
|
SuperIdentityCubit({required TypedKey superRecordKey})
|
||||||
|
: super(
|
||||||
|
open: () => _open(superRecordKey: superRecordKey),
|
||||||
|
decodeState: (buf) => jsonDecodeBytes(SuperIdentity.fromJson, buf));
|
||||||
|
|
||||||
|
static Future<DHTRecord> _open({required TypedKey superRecordKey}) async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
|
return pool.openRecordRead(
|
||||||
|
superRecordKey,
|
||||||
|
debugName: 'SuperIdentityCubit::_open::SuperIdentityRecord',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,18 +5,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57
|
sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "80.0.0"
|
version: "82.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e"
|
sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.0"
|
version: "7.4.5"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -53,10 +53,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: bloc_advanced_tools
|
name: bloc_advanced_tools
|
||||||
sha256: "7c7f294b425552c2d4831b01ad0d3e1f33f2bdf9acfb7b639caa072781d228cf"
|
sha256: dfb142569814952af8d93e7fe045972d847e29382471687db59913e253202f6e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.10"
|
version: "0.1.12"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -161,6 +161,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.3"
|
||||||
|
cli_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_config
|
||||||
|
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -189,10 +197,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: coverage
|
name: coverage
|
||||||
sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43
|
sha256: "802bd084fb82e55df091ec8ad1553a7331b61c08251eef19a508b6f3f3a9858d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.13.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -205,10 +213,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac"
|
sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.1.0"
|
||||||
equatable:
|
equatable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -221,10 +229,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: fast_immutable_collections
|
name: fast_immutable_collections
|
||||||
sha256: "95a69b9380483dff49ae2c12c9eb92e2b4e1aeff481a33c2a20883471771598a"
|
sha256: d1aa3d7788fab06cce7f303f4969c7a16a10c865e1bd2478291a8ebcbee084e5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.3"
|
version: "11.0.4"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -263,10 +271,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: freezed
|
name: freezed
|
||||||
sha256: "7ed2ddaa47524976d5f2aa91432a79da36a76969edd84170777ac5bea82d797c"
|
sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.4"
|
version: "3.0.6"
|
||||||
freezed_annotation:
|
freezed_annotation:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -311,10 +319,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -367,18 +375,18 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
sha256: "81f04dee10969f89f604e1249382d46b97a1ccad53872875369622b5bfc9e58a"
|
sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.9.4"
|
version: "6.9.5"
|
||||||
lint_hard:
|
lint_hard:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: lint_hard
|
name: lint_hard
|
||||||
sha256: ffe7058cb49e021d244d67e650a63380445b56643c2849c6929e938246b99058
|
sha256: "2073d4e83ac4e3f2b87cc615fff41abb5c2c5618e117edcd3d71f40f2186f4d5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.1.1"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -463,10 +471,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12"
|
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.16"
|
version: "2.2.17"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -527,10 +535,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: protobuf
|
name: protobuf
|
||||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
sha256: "579fe5557eae58e3adca2e999e38f02441d8aa908703854a9e0a0f47fa857731"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "4.1.0"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -684,26 +692,26 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
|
sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.25.15"
|
version: "1.26.2"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
version: "0.7.6"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
|
sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.8"
|
version: "0.6.11"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -734,15 +742,15 @@ packages:
|
||||||
path: "../../../veilid/veilid-flutter"
|
path: "../../../veilid/veilid-flutter"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.4.4"
|
version: "0.4.6"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
sha256: "6f82e9ee8e7339f5d8b699317f6f3afc17c80a68ebef1bc0d6f52a678c14b1e6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.0.0"
|
version: "15.0.1"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -763,18 +771,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket
|
name: web_socket
|
||||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "1.0.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
webkit_inspection_protocol:
|
webkit_inspection_protocol:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -9,7 +9,7 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
async_tools: ^0.1.9
|
async_tools: ^0.1.9
|
||||||
bloc: ^9.0.0
|
bloc: ^9.0.0
|
||||||
bloc_advanced_tools: ^0.1.10
|
bloc_advanced_tools: ^0.1.12
|
||||||
charcode: ^1.4.0
|
charcode: ^1.4.0
|
||||||
collection: ^1.19.1
|
collection: ^1.19.1
|
||||||
convert: ^3.1.2
|
convert: ^3.1.2
|
||||||
|
@ -23,7 +23,7 @@ dependencies:
|
||||||
|
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
protobuf: ^3.1.0
|
protobuf: ^4.1.0
|
||||||
veilid:
|
veilid:
|
||||||
# veilid: ^0.0.1
|
# veilid: ^0.0.1
|
||||||
path: ../../../veilid/veilid-flutter
|
path: ../../../veilid/veilid-flutter
|
||||||
|
|
74
pubspec.lock
74
pubspec.lock
|
@ -157,10 +157,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: bloc_advanced_tools
|
name: bloc_advanced_tools
|
||||||
sha256: "7c7f294b425552c2d4831b01ad0d3e1f33f2bdf9acfb7b639caa072781d228cf"
|
sha256: dfb142569814952af8d93e7fe045972d847e29382471687db59913e253202f6e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.10"
|
version: "0.1.12"
|
||||||
blurry_modal_progress_hud:
|
blurry_modal_progress_hud:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -277,10 +277,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: camera_android_camerax
|
name: camera_android_camerax
|
||||||
sha256: ea7e40bd63afb8f55058e48ec529ce96562be9d08393f79631a06f781161fd0d
|
sha256: "9fb44e73e0fea3647a904dc26d38db24055e5b74fc68fd2b6d3abfa1bd20f536"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.16"
|
version: "0.6.17"
|
||||||
camera_avfoundation:
|
camera_avfoundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -437,10 +437,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac"
|
sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.1.0"
|
||||||
diffutil_dart:
|
diffutil_dart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -477,10 +477,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: expansion_tile_group
|
name: expansion_tile_group
|
||||||
sha256: "3be10b81d6d99d1213fe76a285993be0ea6092565ac100152deb6cdf9f5521dc"
|
sha256: "894c5088d94dda5d1ddde50463881935ff41b15850fe674605b9003d09716c8e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.3.0"
|
||||||
fast_immutable_collections:
|
fast_immutable_collections:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -554,18 +554,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_chat_core
|
name: flutter_chat_core
|
||||||
sha256: "529959634622e9df3b96a4a3764ecc61ec6f0dfa3258a52c139ae10a56ccad80"
|
sha256: "7875785bc4aa0b1dce56a76d2a8bd65841c130a3deb2c527878ebfdf8c54f971"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.3.0"
|
||||||
flutter_chat_ui:
|
flutter_chat_ui:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_chat_ui
|
name: flutter_chat_ui
|
||||||
sha256: c63df9cd05fe86a3588b4e47f184fbb9e9c3b86153b8a97f3a789e6edb03d28e
|
sha256: "012aa0d9cc2898b8f89b48f66adb106de9547e466ba21ad54ccef25515f68dcc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.3.0"
|
||||||
flutter_form_builder:
|
flutter_form_builder:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -631,10 +631,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_sticky_header
|
name: flutter_sticky_header
|
||||||
sha256: "7f76d24d119424ca0c95c146b8627a457e8de8169b0d584f766c2c545db8f8be"
|
sha256: fb4fda6164ef3e5fc7ab73aba34aad253c17b7c6ecf738fa26f1a905b7d2d1e2
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.0"
|
version: "0.8.0"
|
||||||
flutter_svg:
|
flutter_svg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -724,10 +724,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3
|
sha256: "0b1e06223bee260dee31a171fb1153e306907563a0b0225e8c1733211911429a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.8.1"
|
version: "15.1.2"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -788,10 +788,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: idb_shim
|
name: idb_shim
|
||||||
sha256: d3dae2085f2dcc9d05b851331fddb66d57d3447ff800de9676b396795436e135
|
sha256: "40e872276d79a1a97cc2c1ea0ecf046b8e34d788f16a8ea8f0da3e9b337d42da"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.5+1"
|
version: "2.6.6+1"
|
||||||
image:
|
image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -812,10 +812,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.19.0"
|
version: "0.20.2"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -916,10 +916,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobile_scanner
|
name: mobile_scanner
|
||||||
sha256: f536c5b8cadcf73d764bdce09c94744f06aa832264730f8971b21a60c5666826
|
sha256: "72f06a071aa8b14acea3ab43ea7949eefe4a2469731ae210e006ba330a033a8c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.10"
|
version: "7.0.0"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -964,10 +964,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: pasteboard
|
name: pasteboard
|
||||||
sha256: "7bf733f3a00c7188ec1f2c6f0612854248b302cf91ef3611a2b7bb141c0f9d55"
|
sha256: "9ff73ada33f79a59ff91f6c01881fd4ed0a0031cfc4ae2d86c0384471525fca1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.4.0"
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1132,10 +1132,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: protobuf
|
name: protobuf
|
||||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
sha256: "579fe5557eae58e3adca2e999e38f02441d8aa908703854a9e0a0f47fa857731"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "4.1.0"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1317,18 +1317,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
|
sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.1.4"
|
version: "11.0.0"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
|
sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.2"
|
version: "6.0.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1603,10 +1603,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "6c7653816b1c938e121b69ff63a33c9dc68102b65a5fb0a5c0f9786256ed33e6"
|
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.5"
|
version: "0.7.6"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1731,10 +1731,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: value_layout_builder
|
name: value_layout_builder
|
||||||
sha256: c02511ea91ca5c643b514a33a38fa52536f74aa939ec367d02938b5ede6807fa
|
sha256: ab4b7d98bac8cefeb9713154d43ee0477490183f5aa23bb4ffa5103d9bbf6275
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.0"
|
version: "0.5.0"
|
||||||
vector_graphics:
|
vector_graphics:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1755,10 +1755,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics_compiler
|
name: vector_graphics_compiler
|
||||||
sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad"
|
sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.16"
|
version: "1.1.17"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1887,4 +1887,4 @@ packages:
|
||||||
version: "1.1.2"
|
version: "1.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0 <4.0.0"
|
dart: ">=3.7.0 <4.0.0"
|
||||||
flutter: ">=3.29.0"
|
flutter: ">=3.32.0"
|
||||||
|
|
39
pubspec.yaml
39
pubspec.yaml
|
@ -5,7 +5,7 @@ version: 0.4.7+20
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.2.0 <4.0.0"
|
sdk: ">=3.2.0 <4.0.0"
|
||||||
flutter: ">=3.22.1"
|
flutter: ">=3.32.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
accordion: ^2.6.0
|
accordion: ^2.6.0
|
||||||
|
@ -21,7 +21,7 @@ dependencies:
|
||||||
badges: ^3.1.2
|
badges: ^3.1.2
|
||||||
basic_utils: ^5.8.2
|
basic_utils: ^5.8.2
|
||||||
bloc: ^9.0.0
|
bloc: ^9.0.0
|
||||||
bloc_advanced_tools: ^0.1.10
|
bloc_advanced_tools: ^0.1.12
|
||||||
blurry_modal_progress_hud: ^1.1.1
|
blurry_modal_progress_hud: ^1.1.1
|
||||||
change_case: ^2.2.0
|
change_case: ^2.2.0
|
||||||
charcode: ^1.4.0
|
charcode: ^1.4.0
|
||||||
|
@ -46,29 +46,29 @@ dependencies:
|
||||||
flutter_native_splash: ^2.4.5
|
flutter_native_splash: ^2.4.5
|
||||||
flutter_slidable: ^4.0.0
|
flutter_slidable: ^4.0.0
|
||||||
flutter_spinkit: ^5.2.1
|
flutter_spinkit: ^5.2.1
|
||||||
flutter_sticky_header: ^0.7.0
|
flutter_sticky_header: ^0.8.0
|
||||||
flutter_svg: ^2.0.17
|
flutter_svg: ^2.0.17
|
||||||
flutter_translate: ^4.1.0
|
flutter_translate: ^4.1.0
|
||||||
flutter_zoom_drawer: ^3.2.0
|
flutter_zoom_drawer: ^3.2.0
|
||||||
form_builder_validators: ^11.1.2
|
form_builder_validators: ^11.1.2
|
||||||
freezed_annotation: ^3.0.0
|
freezed_annotation: ^3.0.0
|
||||||
go_router: ^14.8.1
|
go_router: ^15.1.2
|
||||||
image: ^4.5.3
|
image: ^4.5.3
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
keyboard_avoider: ^0.2.0
|
keyboard_avoider: ^0.2.0
|
||||||
loggy: ^2.0.3
|
loggy: ^2.0.3
|
||||||
meta: ^1.16.0
|
meta: ^1.16.0
|
||||||
mobile_scanner: ^6.0.7
|
mobile_scanner: ^7.0.0
|
||||||
package_info_plus: ^8.3.0
|
package_info_plus: ^8.3.0
|
||||||
pasteboard: ^0.3.0
|
pasteboard: ^0.4.0
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
pdf: ^3.11.3
|
pdf: ^3.11.3
|
||||||
pinput: ^5.0.1
|
pinput: ^5.0.1
|
||||||
preload_page_view: ^0.2.0
|
preload_page_view: ^0.2.0
|
||||||
printing: ^5.14.2
|
printing: ^5.14.2
|
||||||
protobuf: ^3.1.0
|
protobuf: ^4.1.0
|
||||||
provider: ^6.1.2
|
provider: ^6.1.2
|
||||||
qr_code_dart_scan: ^0.10.0
|
qr_code_dart_scan: ^0.10.0
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
|
@ -81,7 +81,7 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://gitlab.com/veilid/Searchable-Listview.git
|
url: https://gitlab.com/veilid/Searchable-Listview.git
|
||||||
ref: main
|
ref: main
|
||||||
share_plus: ^10.1.4
|
share_plus: ^11.0.0
|
||||||
shared_preferences: ^2.5.2
|
shared_preferences: ^2.5.2
|
||||||
signal_strength_indicator: ^0.4.1
|
signal_strength_indicator: ^0.4.1
|
||||||
sliver_expandable: ^1.1.2
|
sliver_expandable: ^1.1.2
|
||||||
|
@ -108,17 +108,18 @@ dependencies:
|
||||||
xterm: ^4.0.0
|
xterm: ^4.0.0
|
||||||
zxing2: ^0.2.3
|
zxing2: ^0.2.3
|
||||||
|
|
||||||
# dependency_overrides:
|
dependency_overrides:
|
||||||
# async_tools:
|
intl: ^0.20.2 # Until flutter_translate updates intl
|
||||||
# path: ../dart_async_tools
|
# async_tools:
|
||||||
# bloc_advanced_tools:
|
# path: ../dart_async_tools
|
||||||
# path: ../bloc_advanced_tools
|
# bloc_advanced_tools:
|
||||||
# searchable_listview:
|
# path: ../bloc_advanced_tools
|
||||||
# path: ../Searchable-Listview
|
# searchable_listview:
|
||||||
# flutter_chat_core:
|
# path: ../Searchable-Listview
|
||||||
# path: ../flutter_chat_ui/packages/flutter_chat_core
|
# flutter_chat_core:
|
||||||
# flutter_chat_ui:
|
# path: ../flutter_chat_ui/packages/flutter_chat_core
|
||||||
# path: ../flutter_chat_ui/packages/flutter_chat_ui
|
# flutter_chat_ui:
|
||||||
|
# path: ../flutter_chat_ui/packages/flutter_chat_ui
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.15
|
build_runner: ^2.4.15
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue