Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ViewContractViewModel.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
11using System.Collections.ObjectModel;
12using System.Globalization;
13using System.Reflection;
14using System.Text;
15using Waher.Content;
18
20{
25 {
26 private readonly bool isReadOnly;
27 private readonly PhotosLoader photosLoader;
28 private DateTime skipContractEvent = DateTime.MinValue;
29
34 public ViewContractViewModel(ViewContractNavigationArgs? Args)
35 {
36 this.Photos = [];
37 this.photosLoader = new PhotosLoader(this.Photos);
38
39 if (Args is not null)
40 {
41 this.Contract = Args.Contract;
42 this.isReadOnly = Args.IsReadOnly;
43 this.Role = Args.Role;
44 this.IsProposal = !string.IsNullOrEmpty(this.Role);
45 this.Proposal = string.IsNullOrEmpty(Args.Proposal) ? ServiceRef.Localizer[nameof(AppResources.YouHaveReceivedAProposal)] : Args.Proposal;
46 }
47 else
48 {
49 this.Contract = null;
50 this.isReadOnly = true;
51 this.Role = null;
52 this.IsProposal = false;
53 }
54 }
55
57 protected override async Task OnInitialize()
58 {
59 await base.OnInitialize();
60
61 ServiceRef.XmppService.ContractUpdated += this.ContractsClient_ContractUpdatedOrSigned;
62 ServiceRef.XmppService.ContractSigned += this.ContractsClient_ContractUpdatedOrSigned;
63
64 if (this.Contract is not null)
65 {
66 DateTime TP = ServiceRef.XmppService.GetTimeOfLastContractEvent(this.Contract.ContractId);
67 if (DateTime.Now.Subtract(TP).TotalSeconds < 5)
68 this.Contract = await ServiceRef.XmppService.GetContract(this.Contract.ContractId);
69
70 this.Contract.FormatParameterDisplay += this.Contract_FormatParameterDisplay;
71
72 await this.DisplayContract();
73 }
74 }
75
77 protected override async Task OnDispose()
78 {
79 if (this.Contract is not null)
80 this.Contract.FormatParameterDisplay -= this.Contract_FormatParameterDisplay;
81
82 ServiceRef.XmppService.ContractUpdated -= this.ContractsClient_ContractUpdatedOrSigned;
83 ServiceRef.XmppService.ContractSigned -= this.ContractsClient_ContractUpdatedOrSigned;
84
85 this.ClearContract();
86
87 await base.OnDispose();
88 }
89
90 private void Contract_FormatParameterDisplay(object? Sender, Waher.Networking.XMPP.Contracts.EventArguments.ParameterValueFormattingEventArgs e)
91 {
92 if (e.Value is Duration Duration)
94 }
95
96 private Task ContractsClient_ContractUpdatedOrSigned(object? Sender, ContractReferenceEventArgs e)
97 {
98 if (e.ContractId == this.Contract?.ContractId && DateTime.Now.Subtract(this.skipContractEvent).TotalSeconds > 5)
99 this.ReloadContract(e.ContractId);
100
101 return Task.CompletedTask;
102 }
103
104 private async void ReloadContract(string ContractId)
105 {
106 try
107 {
108 Contract Contract = await ServiceRef.XmppService.GetContract(ContractId);
109
110 MainThread.BeginInvokeOnMainThread(async () => await this.ContractUpdated(Contract));
111 }
112 catch (Exception ex)
113 {
114 ServiceRef.LogService.LogException(ex);
115 }
116 }
117
118 private async Task ContractUpdated(Contract Contract)
119 {
120 this.ClearContract();
121
122 this.Contract = Contract;
123
124 if (this.Contract is not null)
125 await this.DisplayContract();
126 }
127
128 #region Properties
129
133 [ObservableProperty]
134 private string? contractId;
135
139 [ObservableProperty]
140 private ContractState? state;
141
145 [ObservableProperty]
146 private DateTime? created;
147
151 [ObservableProperty]
152 private DateTime? updated;
153
157 [ObservableProperty]
158 private ContractVisibility? visibility;
159
163 [ObservableProperty]
164 private Duration? duration;
165
169 [ObservableProperty]
170 private DateTime? from;
171
175 [ObservableProperty]
176 private DateTime? to;
177
181 [ObservableProperty]
182 private Duration? archivingOptional;
183
187 [ObservableProperty]
188 private Duration? archivingRequired;
189
193 [ObservableProperty]
194 private bool? canActAsTemplate;
195
199 [ObservableProperty]
200 private string? templateId;
201
205 [ObservableProperty]
206 private byte[]? contentSchemaDigest;
207
211 [ObservableProperty]
212 private string? contentSchemaHashFunction;
213
217 [ObservableProperty]
218 private string? machineLocalName;
219
223 [ObservableProperty]
224 private string? machineNamespace;
225
229 [ObservableProperty]
230 private string? role;
231
235 [ObservableProperty]
236 private bool isProposal;
237
241 [ObservableProperty]
242 private string? proposal;
243
247 [ObservableProperty]
248 private Grid? roles;
249
253 [ObservableProperty]
254 private Grid? parts;
255
259 [ObservableProperty]
260 private Grid? parameters;
261
265 [ObservableProperty]
266 private VerticalStackLayout? humanReadableText;
267
271 [ObservableProperty]
272 private Grid? clientSignatures;
273
277 [ObservableProperty]
278 private Grid? serverSignatures;
279
283 public ObservableCollection<Photo> Photos { get; }
284
288 public Contract? Contract { get; private set; }
289
293 [ObservableProperty]
294 private bool hasPhotos;
295
299 [ObservableProperty]
300 private bool hasRoles;
301
305 [ObservableProperty]
306 private bool hasParts;
307
311 [ObservableProperty]
312 private bool hasParameters;
313
317 [ObservableProperty]
318 private bool hasHumanReadableText;
319
323 [ObservableProperty]
324 private bool hasClientSignatures;
325
329 [ObservableProperty]
330 private bool hasServerSignatures;
331
335 [ObservableProperty]
336 private bool canDeleteContract;
337
341 [ObservableProperty]
342 private bool canObsoleteContract;
343
344 #endregion
345
346 private void ClearContract()
347 {
348 this.photosLoader.CancelLoadPhotos();
349 this.Contract = null;
350 this.ContractId = null;
351 this.State = null;
352 this.Created = null;
353 this.Updated = null;
354 this.Visibility = null;
355 this.Duration = null;
356 this.From = null;
357 this.To = null;
358 this.ArchivingOptional = null;
359 this.ArchivingRequired = null;
360 this.CanActAsTemplate = null;
361 this.TemplateId = null;
362 this.ContentSchemaDigest = null;
363 this.ContentSchemaHashFunction = null;
364 this.MachineLocalName = null;
365 this.MachineNamespace = null;
366 this.Roles = null;
367 this.Parts = null;
368 this.Parameters = null;
369 this.HumanReadableText = null;
370 this.ClientSignatures = null;
371 this.ServerSignatures = null;
372 this.HasPhotos = false;
373 this.HasRoles = false;
374 this.HasParts = false;
375 this.HasParameters = false;
376 this.HasHumanReadableText = false;
377 this.HasClientSignatures = false;
378 this.HasServerSignatures = false;
379 this.CanDeleteContract = false;
380 this.CanObsoleteContract = false;
381
382 this.RemoveQrCode();
383 }
384
385 private async Task DisplayContract()
386 {
387 if (this.Contract is null)
388 return;
389
390 try
391 {
392 bool HasSigned = false;
393 bool AcceptsSignatures =
394 (this.Contract is not null) &&
395 (this.Contract.State == ContractState.Approved || this.Contract.State == ContractState.BeingSigned) &&
396 (!this.Contract.SignAfter.HasValue || this.Contract.SignAfter.Value < DateTime.Now) &&
397 (!this.Contract.SignBefore.HasValue || this.Contract.SignBefore.Value > DateTime.Now);
398 Dictionary<string, int> NrSignatures = [];
399 bool CanObsolete = false;
400
401 this.ContractId = this.Contract!.ContractId;
402 this.State = this.Contract.State;
403 this.Created = this.Contract.Created;
404 this.Updated = this.Contract.Updated == DateTime.MinValue ? null : this.Contract.Updated;
405 this.Visibility = this.Contract.Visibility;
406 this.Duration = this.Contract.Duration;
407 this.From = this.Contract.From;
408 this.To = this.Contract.To;
409 this.ArchivingOptional = this.Contract.ArchiveOptional;
410 this.ArchivingRequired = this.Contract.ArchiveRequired;
411 this.CanActAsTemplate = this.Contract.CanActAsTemplate;
412 this.TemplateId = this.Contract.TemplateId;
413 this.ContentSchemaDigest = this.Contract.ContentSchemaDigest;
414 this.ContentSchemaHashFunction = this.Contract.ContentSchemaHashFunction.ToString();
415 this.MachineLocalName = this.Contract.ForMachinesLocalName;
416 this.MachineNamespace = this.Contract.ForMachinesNamespace;
417
418 if (this.Contract.ClientSignatures is not null)
419 {
420 foreach (ClientSignature Signature in this.Contract.ClientSignatures)
421 {
422 if (Signature.LegalId == ServiceRef.TagProfile.LegalIdentity!.Id)
423 HasSigned = true;
424
425 if (!NrSignatures.TryGetValue(Signature.Role, out int count))
426 count = 0;
427
428 NrSignatures[Signature.Role] = count + 1;
429
430 if (string.Equals(Signature.BareJid, ServiceRef.XmppService.BareJid, StringComparison.OrdinalIgnoreCase))
431 {
432 if (this.Contract.Roles is not null)
433 {
434 foreach (Role Role in this.Contract.Roles)
435 {
436 if (Role.Name == Signature.Role)
437 {
438 if (Role.CanRevoke)
439 {
440 CanObsolete =
441 this.Contract.State == ContractState.Approved ||
442 this.Contract.State == ContractState.BeingSigned ||
443 this.Contract.State == ContractState.Signed;
444 }
445
446 break;
447 }
448 }
449 }
450 }
451 }
452 }
453
454 this.GenerateQrCode(Constants.UriSchemes.CreateSmartContractUri(this.Contract.ContractId));
455
456 // Roles
457
458 if (this.Contract.Roles is not null)
459 {
460 Grid RolesLayout = new()
461 {
462 ColumnDefinitions =
463 [
464 new ColumnDefinition(GridLength.Auto),
465 new ColumnDefinition(GridLength.Star)
466 ]
467 };
468
469 foreach (Role Role in this.Contract.Roles)
470 {
471 string Html = await Role.ToHTML(this.Contract.DeviceLanguage(), this.Contract);
473
474 AddKeyValueLabelPair(RolesLayout, Role.Name, Html + GenerateMinMaxCountString(Role.MinCount, Role.MaxCount), true, string.Empty, null);
475
476 if (!this.isReadOnly && AcceptsSignatures && !HasSigned && this.Contract.PartsMode == ContractParts.Open &&
477 (!NrSignatures.TryGetValue(Role.Name, out int count) || count < Role.MaxCount) &&
478 (!this.IsProposal || Role.Name == this.Role))
479 {
480 Button Button = new()
481 {
482 Text = ServiceRef.Localizer[nameof(AppResources.SignAsRole), Role.Name],
483 StyleId = Role.Name
484 };
485
486 Button.Clicked += this.SignButton_Clicked;
487 RolesLayout.Add(Button, 0, RolesLayout.RowDefinitions.Count);
488
489 Grid.SetColumnSpan(Button, 2);
490 }
491 }
492
493 this.Roles = RolesLayout;
494 }
495
496 // Parts
497
498 Grid PartsLayout = new()
499 {
500 ColumnDefinitions =
501 [
502 new ColumnDefinition(GridLength.Auto),
503 new ColumnDefinition(GridLength.Star)
504 ]
505 };
506
507 if (this.Contract.SignAfter.HasValue)
508 AddKeyValueLabelPair(PartsLayout, ServiceRef.Localizer[nameof(AppResources.SignAfter)], this.Contract.SignAfter.Value.ToString(CultureInfo.CurrentCulture));
509
510 if (this.Contract.SignBefore.HasValue)
511 AddKeyValueLabelPair(PartsLayout, ServiceRef.Localizer[nameof(AppResources.SignBefore)], this.Contract.SignBefore.Value.ToString(CultureInfo.CurrentCulture));
512
513 AddKeyValueLabelPair(PartsLayout, ServiceRef.Localizer[nameof(AppResources.Mode)], this.Contract.PartsMode.ToString());
514
515 if (this.Contract.Parts is not null)
516 {
517 TapGestureRecognizer OpenLegalId = new();
518 OpenLegalId.Tapped += this.Part_Tapped;
519
520 foreach (Part Part in this.Contract.Parts)
521 {
522 AddKeyValueLabelPair(PartsLayout, Part.Role, Part.LegalId, false, OpenLegalId);
523
524 if (!this.isReadOnly && AcceptsSignatures && !HasSigned && Part.LegalId == ServiceRef.TagProfile.LegalIdentity?.Id)
525 {
526 Button Button = new()
527 {
528 Text = ServiceRef.Localizer[nameof(AppResources.SignAsRole), Part.Role],
529 StyleId = Part.Role
530 };
531
532 Button.Clicked += this.SignButton_Clicked;
533 PartsLayout.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
534
535 PartsLayout.Add(Button, 0, PartsLayout.RowDefinitions.Count - 1);
536 }
537 }
538 }
539
540 this.Parts = PartsLayout;
541
542 // Parameters
543
544 if (this.Contract.Parameters is not null)
545 {
546 Grid ParametersLayout = new()
547 {
548 ColumnDefinitions =
549 [
550 new ColumnDefinition(GridLength.Auto),
551 new ColumnDefinition(GridLength.Star)
552 ]
553 };
554
555 foreach (Parameter Parameter in this.Contract.Parameters)
556 {
557 if (Parameter.ObjectValue is bool b)
558 AddKeyValueLabelPair(ParametersLayout, Parameter.Name, b ? "✔" : "✗");
559 else
560 AddKeyValueLabelPair(ParametersLayout, Parameter.Name, Parameter.ObjectValue?.ToString() ?? string.Empty);
561 }
562
563 this.Parameters = ParametersLayout;
564 }
565
566 // Human readable text
567
568 VerticalStackLayout HumanReadableTextLayout = [];
569 string Xaml = await this.Contract.ToMauiXaml(this.Contract.DeviceLanguage());
570 VerticalStackLayout HumanReadableXaml = new VerticalStackLayout().LoadFromXaml(Xaml);
571
572 List<IView> Children = [.. HumanReadableXaml.Children];
573
574 foreach (IView View in Children)
575 {
576 if (View is ContentView ContentView)
577 {
578 foreach (Element InnerView in ContentView.Children)
579 {
580 if (InnerView is Label Label)
581 Label.TextColor = AppColors.PrimaryForeground;
582 }
583 }
584
585 HumanReadableTextLayout.Children.Add(View);
586 }
587
588 this.HumanReadableText = HumanReadableTextLayout;
589
590 // Client signatures
591 if (this.Contract.ClientSignatures is not null)
592 {
593 Grid ClientSignaturesLayout = new()
594 {
595 ColumnDefinitions =
596 [
597 new ColumnDefinition(GridLength.Auto),
598 new ColumnDefinition(GridLength.Star)
599 ]
600 };
601 TapGestureRecognizer openClientSignature = new();
602 openClientSignature.Tapped += this.ClientSignature_Tapped;
603
604 foreach (ClientSignature Signature in this.Contract.ClientSignatures)
605 {
606 string Sign = Convert.ToBase64String(Signature.DigitalSignature);
607 StringBuilder sb = new();
608 sb.Append(Signature.LegalId);
609 sb.Append(", ");
610 sb.Append(Signature.BareJid);
611 sb.Append(", ");
612 sb.Append(Signature.Timestamp.ToString(CultureInfo.CurrentCulture));
613 sb.Append(", ");
614 sb.Append(Sign);
615
616 AddKeyValueLabelPair(ClientSignaturesLayout, Signature.Role, sb.ToString(), false, Sign, openClientSignature);
617 }
618
619 this.ClientSignatures = ClientSignaturesLayout;
620 }
621
622 // Server signature
623 if (this.Contract.ServerSignature is not null)
624 {
625 Grid ServerSignaturesLayout = new()
626 {
627 ColumnDefinitions =
628 [
629 new ColumnDefinition(GridLength.Auto),
630 new ColumnDefinition(GridLength.Star)
631 ]
632 };
633
634 TapGestureRecognizer openServerSignature = new();
635 openServerSignature.Tapped += this.ServerSignature_Tapped;
636
637 StringBuilder sb = new();
638 sb.Append(this.Contract.ServerSignature.Timestamp.ToString(CultureInfo.CurrentCulture));
639 sb.Append(", ");
640 sb.Append(Convert.ToBase64String(this.Contract.ServerSignature.DigitalSignature));
641
642 AddKeyValueLabelPair(ServerSignaturesLayout, this.Contract.Provider, sb.ToString(), false, this.Contract.ContractId, openServerSignature);
643 this.ServerSignatures = ServerSignaturesLayout;
644 }
645
646 this.CanDeleteContract = !this.isReadOnly && !await this.Contract.IsLegallyBinding(true, ServiceRef.XmppService.ContractsClient);
647 this.CanObsoleteContract = this.CanDeleteContract || CanObsolete;
648
649 this.HasRoles = this.Roles?.Children.Count > 0;
650 this.HasParts = this.Parts?.Children.Count > 0;
651 this.HasParameters = this.Parameters?.Children.Count > 0;
652 this.HasHumanReadableText = this.HumanReadableText?.Children.Count > 0;
653 this.HasClientSignatures = this.ClientSignatures?.Children.Count > 0;
654 this.HasServerSignatures = this.ServerSignatures?.Children.Count > 0;
655
656 if (this.Contract.Attachments is not null && this.Contract.Attachments.Length > 0)
657 {
658 _ = this.photosLoader.LoadPhotos(this.Contract.Attachments, SignWith.LatestApprovedId, () =>
659 {
660 MainThread.BeginInvokeOnMainThread(() => this.HasPhotos = this.Photos.Count > 0);
661 });
662 }
663 else
664 this.HasPhotos = false;
665 }
666 catch (Exception ex)
667 {
668 IEnumerable<KeyValuePair<string, object?>> Tags = this.GetClassAndMethod(MethodBase.GetCurrentMethod());
669 Tags = Tags.Append(new KeyValuePair<string, object?>("ContractId", this.Contract?.ContractId ?? string.Empty));
670
671 ServiceRef.LogService.LogException(ex, Tags.ToArray());
672
673 this.ClearContract();
674 await ServiceRef.UiService.DisplayException(ex);
675 }
676 }
677
678 private static string GenerateMinMaxCountString(int min, int max)
679 {
680 if (min == max)
681 {
682 if (max == 1)
683 return string.Empty;
684
685 return " (" + max.ToString(CultureInfo.InvariantCulture) + ")";
686 }
687
688 return " (" + min.ToString(CultureInfo.InvariantCulture) + " - " + max.ToString(CultureInfo.InvariantCulture) + ")";
689 }
690
691 private static void AddKeyValueLabelPair(Grid Container, string Key, string Value)
692 {
693 AddKeyValueLabelPair(Container, Key, Value, false, string.Empty, null);
694 }
695
696 private static void AddKeyValueLabelPair(Grid Container, string Key, string Value, bool IsHtml,
697 TapGestureRecognizer TapGestureRecognizer)
698 {
699 AddKeyValueLabelPair(Container, Key, Value, IsHtml, Value, TapGestureRecognizer);
700 }
701
702 private static void AddKeyValueLabelPair(Grid Container, string Key,
703 string Value, bool IsHtml, string StyleId, TapGestureRecognizer? TapGestureRecognizer)
704 {
705 int Row = Container.RowDefinitions.Count;
706
707 Container.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
708
709 Label KeyLabel = new()
710 {
711 Text = Key,
712 Style = AppStyles.KeyLabel
713 };
714
715 Label ValueLabel = new()
716 {
717 Text = Value,
718 TextType = IsHtml ? TextType.Html : TextType.Text,
719 StyleId = StyleId,
720 Style = IsHtml ? AppStyles.FormattedValueLabel : TapGestureRecognizer is null ? AppStyles.ValueLabel : AppStyles.ClickableValueLabel
721 };
722
723 Container.Add(KeyLabel, 0, Row);
724 Container.Add(ValueLabel, 1, Row);
725
726 if (TapGestureRecognizer is not null)
727 ValueLabel.GestureRecognizers.Add(TapGestureRecognizer);
728 }
729
730 private async void SignButton_Clicked(object? Sender, EventArgs e)
731 {
732 if (this.Contract is null)
733 return;
734
735 try
736 {
737 if (Sender is Button Button && !string.IsNullOrEmpty(Button.StyleId))
738 {
739 string Role = Button.StyleId;
740
741 if (!await AreYouSure(ServiceRef.Localizer[nameof(AppResources.AreYouSureYouWantToSignAs), Role]))
742 return;
743
744 if (!await App.AuthenticateUser(AuthenticationPurpose.SignContract, true))
745 return;
746
747 this.skipContractEvent = DateTime.Now;
748
749 Contract Contract = await ServiceRef.XmppService.SignContract(this.Contract, Role, false);
750 await this.ContractUpdated(Contract);
751
752 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
753 ServiceRef.Localizer[nameof(AppResources.ContractSuccessfullySigned)]);
754 }
755 }
756 catch (Exception ex)
757 {
758 ServiceRef.LogService.LogException(ex);
759 await ServiceRef.UiService.DisplayException(ex);
760 }
761 }
762
763 private async void Part_Tapped(object? Sender, EventArgs e)
764 {
765 try
766 {
767 if (Sender is Label Label && !string.IsNullOrEmpty(Label.StyleId))
768 {
769 await ServiceRef.ContractOrchestratorService.OpenLegalIdentity(Label.StyleId,
770 ServiceRef.Localizer[nameof(AppResources.PurposeReviewContract)]);
771 }
772 }
773 catch (Exception ex)
774 {
775 ServiceRef.LogService.LogException(ex);
776 await ServiceRef.UiService.DisplayException(ex);
777 }
778 }
779
780 private async void ContractId_Tapped(object? Sender, EventArgs e)
781 {
782 try
783 {
784 if (Sender is Label Label && !string.IsNullOrEmpty(Label.StyleId))
785 {
786 await ServiceRef.ContractOrchestratorService.OpenContract(Label.StyleId,
787 ServiceRef.Localizer[nameof(AppResources.PurposeReviewContract)], null);
788 }
789 }
790 catch (Exception ex)
791 {
792 ServiceRef.LogService.LogException(ex);
793 await ServiceRef.UiService.DisplayException(ex);
794 }
795 }
796
797 private async void Link_Tapped(object? Sender, EventArgs e)
798 {
799 try
800 {
801 if (Sender is Label Label && !string.IsNullOrEmpty(Label.StyleId))
802 await App.OpenUrlAsync(Label.StyleId);
803 }
804 catch (Exception ex)
805 {
806 ServiceRef.LogService.LogException(ex);
807 await ServiceRef.UiService.DisplayException(ex);
808 }
809 }
810
811 private async void CopyToClipboard_Tapped(object? Sender, EventArgs e)
812 {
813 try
814 {
815 if (Sender is Label Label && !string.IsNullOrEmpty(Label.StyleId))
816 {
817 await Clipboard.SetTextAsync(Label.StyleId);
818 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)], ServiceRef.Localizer[nameof(AppResources.TagValueCopiedToClipboard)]);
819 }
820 }
821 catch (Exception ex)
822 {
823 ServiceRef.LogService.LogException(ex);
824 await ServiceRef.UiService.DisplayException(ex);
825 }
826 }
827
828 private async void ClientSignature_Tapped(object? Sender, EventArgs e)
829 {
830 try
831 {
832 if (Sender is Label Label && !string.IsNullOrEmpty(Label.StyleId))
833 {
834 string sign = Label.StyleId;
835 ClientSignature? signature = this.Contract?.ClientSignatures.FirstOrDefault(x => sign == Convert.ToBase64String(x.DigitalSignature));
836 if (signature is not null)
837 {
838 string legalId = signature.LegalId;
839 LegalIdentity identity = await ServiceRef.XmppService.GetLegalIdentity(legalId);
840
841 await ServiceRef.UiService.GoToAsync(nameof(ClientSignaturePage),
842 new ClientSignatureNavigationArgs(signature, identity));
843 }
844 }
845 }
846 catch (Exception ex)
847 {
848 ServiceRef.LogService.LogException(ex);
849 await ServiceRef.UiService.DisplayException(ex);
850 }
851 }
852
853 private async void ServerSignature_Tapped(object? Sender, EventArgs e)
854 {
855 try
856 {
857 if (Sender is Label Label && !string.IsNullOrEmpty(Label.StyleId))
858 {
859 await ServiceRef.UiService.GoToAsync(nameof(ServerSignaturePage),
860 new ServerSignatureNavigationArgs(this.Contract));
861 }
862 }
863 catch (Exception ex)
864 {
865 ServiceRef.LogService.LogException(ex);
866 await ServiceRef.UiService.DisplayException(ex);
867 }
868 }
869
873 [RelayCommand]
874 private async Task ObsoleteContract()
875 {
876 if (this.Contract is null)
877 return;
878
879 try
880 {
881 if (!await AreYouSure(ServiceRef.Localizer[nameof(AppResources.AreYouSureYouWantToObsoleteContract)]))
882 return;
883
884 if (!await App.AuthenticateUser(AuthenticationPurpose.ObsoleteContract, true))
885 return;
886
887 this.skipContractEvent = DateTime.Now;
888
889 Contract obsoletedContract = await ServiceRef.XmppService.ObsoleteContract(this.Contract.ContractId);
890 await this.ContractUpdated(obsoletedContract);
891
892 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)], ServiceRef.Localizer[nameof(AppResources.ContractHasBeenObsoleted)]);
893 }
894 catch (Exception ex)
895 {
896 ServiceRef.LogService.LogException(ex);
897 await ServiceRef.UiService.DisplayException(ex);
898 }
899 }
900
904 [RelayCommand]
905 private async Task DeleteContract()
906 {
907 if (this.Contract is null)
908 return;
909
910 try
911 {
912 if (!await AreYouSure(ServiceRef.Localizer[nameof(AppResources.AreYouSureYouWantToDeleteContract)]))
913 return;
914
915 if (!await App.AuthenticateUser(AuthenticationPurpose.DeleteContract, true))
916 return;
917
918 this.skipContractEvent = DateTime.Now;
919
920 Contract deletedContract = await ServiceRef.XmppService.DeleteContract(this.Contract.ContractId);
921 await this.ContractUpdated(deletedContract);
922
923 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)], ServiceRef.Localizer[nameof(AppResources.ContractHasBeenDeleted)]);
924 }
925 catch (Exception ex)
926 {
927 ServiceRef.LogService.LogException(ex);
928 await ServiceRef.UiService.DisplayException(ex);
929 }
930 }
931
935 [RelayCommand]
936 private async Task ShowDetails()
937 {
938 if (this.Contract is null)
939 return;
940
941 try
942 {
943 byte[] Bin = Encoding.UTF8.GetBytes(this.Contract.ForMachines.OuterXml);
944 HttpFileUploadEventArgs e = await ServiceRef.XmppService.RequestUploadSlotAsync(this.Contract.ContractId + ".xml", "text/xml; charset=utf-8", Bin.Length);
945
946 if (e.Ok)
947 {
948 await e.PUT(Bin, "text/xml", (int)Constants.Timeouts.UploadFile.TotalMilliseconds);
949 if (!await App.OpenUrlAsync(e.GetUrl, false))
950 await this.Copy(e.GetUrl);
951 }
952 else
953 await ServiceRef.UiService.DisplayException(e.StanzaError ?? new Exception(e.ErrorText));
954 }
955 catch (Exception ex)
956 {
957 await ServiceRef.UiService.DisplayException(ex);
958 }
959 }
960
964 [RelayCommand]
965 private async Task Copy(object Item)
966 {
967 try
968 {
969 this.SetIsBusy(true);
970
971 if (Item is string Label)
972 {
973 if (Label == this.ContractId)
974 {
975 await Clipboard.SetTextAsync(Constants.UriSchemes.IotSc + ":" + this.ContractId);
976 await ServiceRef.UiService.DisplayAlert(
977 ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
978 ServiceRef.Localizer[nameof(AppResources.ContractIdCopiedSuccessfully)]);
979 }
980 else
981 {
982 await Clipboard.SetTextAsync(Label);
983 await ServiceRef.UiService.DisplayAlert(
984 ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
985 ServiceRef.Localizer[nameof(AppResources.TagValueCopiedToClipboard)]);
986 }
987 }
988 else
989 {
990 await Clipboard.SetTextAsync(Item?.ToString() ?? string.Empty);
991 await ServiceRef.UiService.DisplayAlert(
992 ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
993 ServiceRef.Localizer[nameof(AppResources.TagValueCopiedToClipboard)]);
994 }
995 }
996 catch (Exception ex)
997 {
998 ServiceRef.LogService.LogException(ex);
999 await ServiceRef.UiService.DisplayException(ex);
1000 }
1001 finally
1002 {
1003 this.SetIsBusy(false);
1004 }
1005 }
1006
1011 [RelayCommand]
1012 private static async Task OpenContract(object Item)
1013 {
1014 if (Item is string ContractId)
1015 await App.OpenUrlAsync(Constants.UriSchemes.IotSc + ":" + ContractId);
1016 }
1017
1022 [RelayCommand]
1023 private async Task OpenLink(object Item)
1024 {
1025 if (Item is string Url)
1026 {
1027 if (!await App.OpenUrlAsync(Url, false))
1028 await this.Copy(Url);
1029 }
1030 else
1031 await this.Copy(Item);
1032 }
1033
1034 #region ILinkableView
1035
1039 public override string? Link { get; }
1040
1044 public override Task<string> Title => ContractModel.GetName(this.Contract);
1045
1046 #endregion
1047
1048 }
1049}
The Application class, representing an instance of the Neuro-Access app.
Definition: App.xaml.cs:69
static Task< bool > AuthenticateUser(AuthenticationPurpose Purpose, bool Force=false)
Authenticates the user using the configured authentication method.
Definition: App.xaml.cs:981
static Task< bool > OpenUrlAsync(string Url)
Opens an URL in the application.
Definition: App.xaml.cs:919
static readonly TimeSpan UploadFile
Upload file timeout
Definition: Constants.cs:581
const string IotSc
The IoT Smart Contract URI Scheme (iotsc)
Definition: Constants.cs:109
static string CreateSmartContractUri(string id)
Generates a IoT Scan Uri form the specified id.
Definition: Constants.cs:188
A set of never changing property constants and helpful values.
Definition: Constants.cs:7
Base class that references services in the app.
Definition: ServiceRef.cs:31
static ILogService LogService
Log service.
Definition: ServiceRef.cs:91
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:55
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:79
static IStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:235
static IContractOrchestratorService ContractOrchestratorService
Contract orchestrator service.
Definition: ServiceRef.cs:115
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:67
Static class that gives access to app-specific themed colors
Definition: AppColors.cs:7
static Color PrimaryForeground
Primary foreground color.
Definition: AppColors.cs:62
Converts a Duration value to a String value.
static string ToString(Duration Duration)
Converts a Duration to a human-readable text.
static async Task< bool > AreYouSure(string Message)
Asks the user to confirm an action.
IEnumerable< BaseViewModel > Children
Gets the child view models.
virtual void SetIsBusy(bool IsBusy)
Sets the IsBusy property.
static async Task< string > GetName(Contract? Contract)
Gets a displayable name for a contract.
The view model to bind to for when displaying contracts.
ObservableCollection< Photo > Photos
Gets the list of photos associated with the contract.
override async Task OnDispose()
Method called when the view is disposed, and will not be used more. Use this method to unregister eve...
ViewContractViewModel(ViewContractNavigationArgs? Args)
Creates an instance of the ViewContractViewModel class.
override async Task OnInitialize()
Method called when view is initialized for the first time. Use this method to implement registration ...
A view model that holds the XMPP state.
void RemoveQrCode()
Removes the QR-code
void GenerateQrCode(string Uri)
Generates a QR-code
Holds navigation parameters specific to views displaying a client signature.
Holds navigation parameters specific to views displaying a server signature.
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
Represents a digital signature on a contract.
string LegalId
ID of legal identity signing the contract.
Contains the definition of a contract
Definition: Contract.cs:22
DateTime Created
When the contract was created
Definition: Contract.cs:130
byte[] ContentSchemaDigest
The hash digest of the schema used to validate the machine-readable contents (ForMachines) of the sma...
Definition: Contract.cs:221
string ForMachinesLocalName
Local name used by the root node of the machine-readable contents of the contract (ForMachines).
Definition: Contract.cs:294
Security.HashFunction ContentSchemaHashFunction
Hash function of the schema used to validate the machine-readable contents (ForMachines) of the smart...
Definition: Contract.cs:231
Parameter[] Parameters
Defined parameters for the smart contract.
Definition: Contract.cs:267
Duration? ArchiveRequired
Requied time to archive a signed smart contract, after it becomes obsolete.
Definition: Contract.cs:202
ClientSignature[] ClientSignatures
Client signatures of the contract.
Definition: Contract.cs:309
DateTime From
From when the contract is valid (if signed)
Definition: Contract.cs:148
DateTime Updated
When the contract was last updated
Definition: Contract.cs:139
async Task< bool > IsLegallyBinding(bool CheckCurrentTime, ContractsClient Client)
Checks if a contract is legally binding.
Definition: Contract.cs:1421
Attachment[] Attachments
Attachments assigned to the legal identity.
Definition: Contract.cs:318
DateTime To
Until when the contract is valid (if signed)
Definition: Contract.cs:157
string Provider
JID of the Trust Provider hosting the contract
Definition: Contract.cs:84
Duration? ArchiveOptional
Optional time to archive a signed smart contract, after it becomes obsolete, and after its required a...
Definition: Contract.cs:211
bool CanActAsTemplate
If the contract can act as a template for other contracts.
Definition: Contract.cs:336
XmlElement ForMachines
Machine-readable contents of the contract.
Definition: Contract.cs:276
Role[] Roles
Roles defined in the smart contract.
Definition: Contract.cs:240
DateTime? SignAfter
Signatures will only be accepted after this point in time.
Definition: Contract.cs:166
ContractParts PartsMode
How parts are defined in the smart contract.
Definition: Contract.cs:249
ContractState State
Contract state
Definition: Contract.cs:121
string ContractId
Contract identity
Definition: Contract.cs:65
Part[] Parts
Defined parts for the smart contract.
Definition: Contract.cs:258
Duration? Duration
Duration of the contract. Is counted from the time it is signed by the required parties.
Definition: Contract.cs:193
string ForMachinesNamespace
Namespace used by the root node of the machine-readable contents of the contract (ForMachines).
Definition: Contract.cs:289
DateTime? SignBefore
Signatures will only be accepted until this point in time.
Definition: Contract.cs:175
ServerSignature ServerSignature
Server signature attesting to the validity of the contents of the contract.
Definition: Contract.cs:327
ContractVisibility Visibility
Contrat Visibility
Definition: Contract.cs:184
Contract()
Contains the definition of a contract
Definition: Contract.cs:57
string TemplateId
Contract identity of template, if one was used to create the contract.
Definition: Contract.cs:93
Task< string > ToHTML(string Language, Contract Contract)
Creates a human-readable HTML document for the contract.
Abstract base class for contractual parameters
Definition: Parameter.cs:17
abstract object ObjectValue
Parameter value.
Definition: Parameter.cs:104
Class defining a part in a contract
Definition: Part.cs:30
string LegalId
Legal identity of part
Definition: Part.cs:38
string Role
Role of the part in the contract
Definition: Part.cs:57
Class defining a role
Definition: Role.cs:7
bool CanRevoke
If parts having this role, can revoke their signature, once signed.
Definition: Role.cs:44
int MaxCount
Largest amount of signatures of this role required for a legally binding contract.
Definition: Role.cs:35
int MinCount
Smallest amount of signatures of this role required for a legally binding contract.
Definition: Role.cs:26
string Name
Name of the role.
Definition: Role.cs:17
Abstract base class of signatures
Definition: Signature.cs:10
byte[] DigitalSignature
Digital Signature
Definition: Signature.cs:27
DateTime Timestamp
Timestamp of signature.
Definition: Signature.cs:18
bool Ok
If the response is an OK result response (true), or an error response (false).
XmppException StanzaError
Any stanza error returned.
Event arguments for HTTP File Upload callback methods.
async Task PUT(byte[] Content, string ContentType, int Timeout)
Uploads file content to the server.
AuthenticationPurpose
Purpose for requesting the user to authenticate itself.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
SignWith
Options on what keys to use when signing data.
Definition: Enumerations.cs:84
ContractParts
How the parts of the contract are defined.
Definition: Part.cs:9
ContractVisibility
Visibility types for contracts.
Definition: Enumerations.cs:58
ContractState
Recognized contract states
Definition: Enumerations.cs:9
Definition: App.xaml.cs:4
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:13