-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Check if establishing relationship is possible #451
base: main
Are you sure you want to change the base?
Changes from 53 commits
412251c
9a6e4d6
a0023ab
c770208
a987ba4
9caf3a1
ed4745d
68f7ef3
a7a9acf
191ec92
0a01251
628670e
031b7a9
4a793fd
a0d0568
ae9f013
5d959bd
f9e8218
6c26bce
833eaf3
fb0d10a
322941f
bfda5d8
c506b76
1512018
2ee33d2
61f25a4
f3ca19c
41d4af0
7275fd7
00a6f24
ed79eff
394b829
633a5ed
0042d6f
1327e90
1a64d55
b1b8b32
cae4e24
2b5dab4
4be6322
6fa46bd
9418c3c
7082fe5
59ca19d
386f716
229399c
345a316
f28247c
8acd660
229883c
4841b2a
fc030eb
d60786d
fee9eec
d0c98ea
15a4073
4b95ffa
9a594c5
b103be1
06c25ab
57702ee
2b67052
0f1e8ad
9e90491
f3239a9
52177dd
4adcd9d
b4539d1
5d839ff
b26e478
af9f197
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ class DismissibleContactItem extends StatefulWidget { | |
final IdentityDVO contact; | ||
final VoidCallback onTap; | ||
final void Function(BuildContext) onDeletePressed; | ||
final bool isRequestExpired; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should pass the request instead of this boolean, for this widget the variable name and type is very meaningless |
||
final Widget? trailing; | ||
final Widget? subtitle; | ||
final String? query; | ||
|
@@ -17,6 +18,7 @@ class DismissibleContactItem extends StatefulWidget { | |
required this.contact, | ||
required this.onTap, | ||
required this.onDeletePressed, | ||
required this.isRequestExpired, | ||
this.trailing, | ||
this.subtitle, | ||
this.query, | ||
|
@@ -53,8 +55,9 @@ class _DismissibleContactItemState extends State<DismissibleContactItem> with Si | |
@override | ||
Widget build(BuildContext context) { | ||
final coloringStatus = [RelationshipStatus.Terminated, RelationshipStatus.DeletionProposed]; | ||
|
||
final tileColor = | ||
widget.contact.relationship == null || coloringStatus.contains(widget.contact.relationship!.status) | ||
(widget.contact.relationship == null && !widget.isRequestExpired) || coloringStatus.contains(widget.contact.relationship?.status) | ||
? Theme.of(context).colorScheme.primaryContainer | ||
: null; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:go_router/go_router.dart'; | ||
|
||
import '/core/utils/extensions.dart'; | ||
|
||
class CreateRelationshipErrorDialog extends StatelessWidget { | ||
final String errorCode; | ||
|
||
const CreateRelationshipErrorDialog({required this.errorCode, super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return AlertDialog( | ||
icon: _icon(context), | ||
title: Text(_title(context)), | ||
content: Text(_content(context), textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium), | ||
actions: [ | ||
FilledButton( | ||
onPressed: errorCode == 'error.transport.relationships.relationshipTemplateIsExpired' ? () => context.pop(true) : () => context.pop(false), | ||
child: Text( | ||
errorCode == 'error.transport.relationships.relationshipTemplateIsExpired' | ||
? context.l10n.error_deleteRequest | ||
: context.l10n.error_understood, | ||
), | ||
), | ||
], | ||
actionsAlignment: MainAxisAlignment.center, | ||
); | ||
} | ||
|
||
Icon _icon(BuildContext context) => switch (errorCode) { | ||
'error.transport.relationships.activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate' => Icon( | ||
Icons.cancel, | ||
color: Theme.of(context).colorScheme.error, | ||
), | ||
_ => Icon(Icons.error, color: Theme.of(context).colorScheme.error), | ||
}; | ||
|
||
String _title(BuildContext context) => switch (errorCode) { | ||
'error.transport.relationships.relationshipNotYetDecomposedByPeer' => context.l10n.errorDialog_relationshipNotYetDecomposedByPeer_title, | ||
'error.transport.relationships.activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate' => | ||
context.l10n.errorDialog_activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate_title, | ||
'error.transport.relationships.relationshipTemplateIsExpired' => context.l10n.errorDialog_relationshipTemplateIsExpired_title, | ||
'error.relationshipTemplateProcessedModule.requestExpired' => context.l10n.errorDialog_requestExpired_title, | ||
_ => context.l10n.errorDialog_title, | ||
}; | ||
|
||
String _content(BuildContext context) => switch (errorCode) { | ||
'error.transport.relationships.relationshipNotYetDecomposedByPeer' => context.l10n.errorDialog_relationshipNotYetDecomposedByPeer_description, | ||
'error.transport.relationships.activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate' => | ||
context.l10n.errorDialog_activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate_description, | ||
'error.transport.relationships.relationshipTemplateIsExpired' => context.l10n.errorDialog_relationshipTemplateIsExpired_description, | ||
'error.relationshipTemplateProcessedModule.requestExpired' => context.l10n.errorDialog_requestExpired_description, | ||
_ => context.l10n.errorDialog_description, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,7 +82,11 @@ Future<List<IdentityDVO>> getContacts({required Session session}) async { | |
Future<List<LocalRequestDVO>> incomingOpenRequestsFromRelationshipTemplate({required Session session}) async { | ||
final incomingRequestResult = await session.consumptionServices.incomingRequests.getRequests( | ||
query: { | ||
'status': QueryValue.stringList([LocalRequestStatus.DecisionRequired.name, LocalRequestStatus.ManualDecisionRequired.name]), | ||
'status': QueryValue.stringList([ | ||
LocalRequestStatus.DecisionRequired.name, | ||
LocalRequestStatus.ManualDecisionRequired.name, | ||
LocalRequestStatus.Expired.name, | ||
]), | ||
'source.type': QueryValue.string(LocalRequestSourceType.RelationshipTemplate.name), | ||
}, | ||
); | ||
|
@@ -147,3 +151,19 @@ Future<void> deleteContact({ | |
|
||
onContactDeleted(); | ||
} | ||
|
||
Future<({bool success, String? errorCode})> validateRelationshipCreation({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again - this is not enough. type RelationshipTemplate only means that it originates from a RelationshipTemplate, but it could also originate from onExistingRelationship, then we wouldn't create a relationship and instead answer using a message. Please talk to the runtime team how to handle this properly, I suggest @Milena-Czierlinski as a sparring partner. Maybe we can make the requestDVO event smarter so that we KNOW if it would be answered using a Relationship or a Message ;) If you discussed this please tell me the results. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We think it could be enough to check for localRequestDVO.peer.hasRelationship and then do the validation if its false. |
||
required String accountId, | ||
required Session session, | ||
LocalRequestSourceDVO? localRequestSource, | ||
}) async { | ||
if (localRequestSource == null) return (success: true, errorCode: null); | ||
|
||
final response = await session.transportServices.relationships.canCreateRelationship(templateId: localRequestSource.reference); | ||
nicole-eb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (response.value.isSuccess) return (success: true, errorCode: null); | ||
|
||
final failureResponse = response.value as CanCreateRelationshipFailureResponse; | ||
|
||
return (success: false, errorCode: failureResponse.code); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ import 'package:logger/logger.dart'; | |
import 'package:renderers/renderers.dart'; | ||
|
||
import '../modals/create_attribute.dart'; | ||
import '../modals/create_relationship_error_dialog.dart'; | ||
import '../types/types.dart'; | ||
import '../utils/utils.dart'; | ||
import 'contact_circle_avatar.dart'; | ||
|
@@ -24,6 +25,7 @@ class RequestDVORenderer extends StatefulWidget { | |
final String validationErrorDescription; | ||
final VoidCallback onAfterAccept; | ||
final bool showHeader; | ||
final bool validateCreateRelationship; | ||
final LocalRequestDVO? requestDVO; | ||
final String? description; | ||
|
||
|
@@ -35,6 +37,7 @@ class RequestDVORenderer extends StatefulWidget { | |
required this.validationErrorDescription, | ||
required this.onAfterAccept, | ||
this.showHeader = true, | ||
this.validateCreateRelationship = false, | ||
this.requestDVO, | ||
this.description, | ||
super.key, | ||
|
@@ -55,6 +58,7 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
GetIdentityInfoResponse? _identityInfo; | ||
|
||
bool _loading = false; | ||
late bool _canAcceptRequest; | ||
|
||
@override | ||
void initState() { | ||
|
@@ -63,13 +67,17 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
final session = GetIt.I.get<EnmeshedRuntime>().getSession(widget.accountId); | ||
_request = widget.requestDVO; | ||
|
||
_canAcceptRequest = !widget.validateCreateRelationship; | ||
|
||
_updateIdentityInfo(); | ||
|
||
if (_request == null) { | ||
_loadRequest(session); | ||
} else { | ||
_setController(session, _request!); | ||
} | ||
|
||
_canCreateRelationship(); | ||
} | ||
|
||
@override | ||
|
@@ -95,6 +103,8 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
} else { | ||
_setController(session, _request!); | ||
} | ||
|
||
_canCreateRelationship(); | ||
} | ||
|
||
super.didUpdateWidget(oldWidget); | ||
|
@@ -165,7 +175,7 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
children: [ | ||
OutlinedButton(onPressed: _loading && _request != null ? null : _rejectRequest, child: Text(context.l10n.reject)), | ||
Gaps.w8, | ||
FilledButton(onPressed: _acceptRequest, child: Text(widget.acceptRequestText)), | ||
FilledButton(onPressed: _onAcceptButtonPressed, child: Text(widget.acceptRequestText)), | ||
], | ||
), | ||
), | ||
|
@@ -194,6 +204,14 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
|
||
void _setController(Session session, LocalRequestDVO request) => _controller = RequestRendererController(request: request); | ||
|
||
Future<void> _onAcceptButtonPressed() async { | ||
await _canCreateRelationship(); | ||
|
||
if (!_canAcceptRequest) return; | ||
|
||
await _acceptRequest(); | ||
} | ||
|
||
Future<void> _acceptRequest() async { | ||
if (_loading) return; | ||
|
||
|
@@ -230,6 +248,13 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
widget.onAfterAccept(); | ||
} | ||
|
||
Future<void> _deleteRequest() async { | ||
final session = GetIt.I.get<EnmeshedRuntime>().getSession(widget.accountId); | ||
final deleteResult = await session.consumptionServices.incomingRequests.delete(requestId: _request!.id); | ||
|
||
if (deleteResult.isError) GetIt.I.get<Logger>().e(deleteResult.error); | ||
} | ||
|
||
Future<void> _rejectRequest() async { | ||
setState(() => _loading = true); | ||
|
||
|
@@ -287,6 +312,34 @@ class _RequestDVORendererState extends State<RequestDVORenderer> { | |
|
||
return choice; | ||
} | ||
|
||
Future<void> _canCreateRelationship() async { | ||
if (!widget.validateCreateRelationship) return; | ||
|
||
final session = GetIt.I.get<EnmeshedRuntime>().getSession(widget.accountId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we discussed a very important part that was just ignored here. This function now runs every time a request is rendered that originates from a template. But I miss the check for the existing relationship. Also, is this really the correct place to run this check? Can't we move this to the request screen? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In RequestScreen i do "validateCreateRelationship: requestDVO?.source?.type == LocalRequestSourceType.RelationshipTemplate" and pass that to the param There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But i guess i could do this whole check in RequestScreen There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, as you see it's not a good idea to split this information |
||
|
||
final validateRelationshipCreationResponse = await validateRelationshipCreation( | ||
accountId: widget.accountId, | ||
localRequestSource: _request?.source, | ||
session: session, | ||
); | ||
|
||
setState(() => _canAcceptRequest = validateRelationshipCreationResponse.success); | ||
|
||
if (_canAcceptRequest || !mounted) return; | ||
|
||
final result = await showDialog<bool>( | ||
barrierDismissible: false, | ||
context: context, | ||
builder: (context) => CreateRelationshipErrorDialog(errorCode: validateRelationshipCreationResponse.errorCode!), | ||
); | ||
|
||
if (result != null && result) await _deleteRequest(); | ||
|
||
if (!mounted) return; | ||
|
||
context.pop(); | ||
} | ||
} | ||
|
||
class _AttributeSwitcher extends StatefulWidget { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result can be null, but i use now
if (result ?? false)
instead