1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
11using System.Collections.ObjectModel;
12using System.Globalization;
13using System.Reflection;
26 private readonly
bool isReadOnly;
27 private readonly PhotosLoader photosLoader;
28 private DateTime skipContractEvent = DateTime.MinValue;
37 this.photosLoader =
new PhotosLoader(this.
Photos);
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;
50 this.isReadOnly =
true;
52 this.IsProposal =
false;
59 await base.OnInitialize();
61 ServiceRef.XmppService.ContractUpdated += this.ContractsClient_ContractUpdatedOrSigned;
62 ServiceRef.XmppService.ContractSigned += this.ContractsClient_ContractUpdatedOrSigned;
67 if (DateTime.Now.Subtract(TP).TotalSeconds < 5)
70 this.Contract.FormatParameterDisplay += this.Contract_FormatParameterDisplay;
72 await this.DisplayContract();
80 this.Contract.FormatParameterDisplay -= this.Contract_FormatParameterDisplay;
82 ServiceRef.XmppService.ContractUpdated -= this.ContractsClient_ContractUpdatedOrSigned;
83 ServiceRef.XmppService.ContractSigned -= this.ContractsClient_ContractUpdatedOrSigned;
87 await base.OnDispose();
96 private Task ContractsClient_ContractUpdatedOrSigned(
object? Sender, ContractReferenceEventArgs e)
98 if (e.ContractId ==
this.Contract?.ContractId && DateTime.Now.Subtract(
this.skipContractEvent).TotalSeconds > 5)
99 this.ReloadContract(e.ContractId);
101 return Task.CompletedTask;
104 private async
void ReloadContract(
string ContractId)
110 MainThread.BeginInvokeOnMainThread(async () => await this.ContractUpdated(
Contract));
120 this.ClearContract();
124 if (this.Contract is not
null)
125 await this.DisplayContract();
134 private string? contractId;
146 private DateTime? created;
152 private DateTime? updated;
170 private DateTime? from;
176 private DateTime? to;
182 private Duration? archivingOptional;
188 private Duration? archivingRequired;
194 private bool? canActAsTemplate;
200 private string? templateId;
206 private byte[]? contentSchemaDigest;
212 private string? contentSchemaHashFunction;
218 private string? machineLocalName;
224 private string? machineNamespace;
230 private string? role;
236 private bool isProposal;
242 private string? proposal;
260 private Grid? parameters;
266 private VerticalStackLayout? humanReadableText;
272 private Grid? clientSignatures;
278 private Grid? serverSignatures;
283 public ObservableCollection<Photo>
Photos {
get; }
294 private bool hasPhotos;
300 private bool hasRoles;
306 private bool hasParts;
312 private bool hasParameters;
318 private bool hasHumanReadableText;
324 private bool hasClientSignatures;
330 private bool hasServerSignatures;
336 private bool canDeleteContract;
342 private bool canObsoleteContract;
346 private void ClearContract()
348 this.photosLoader.CancelLoadPhotos();
349 this.Contract =
null;
350 this.ContractId =
null;
354 this.Visibility =
null;
355 this.Duration =
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;
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;
385 private async Task DisplayContract()
387 if (this.Contract is
null)
392 bool HasSigned =
false;
393 bool AcceptsSignatures =
394 (this.Contract is not
null) &&
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;
402 this.State = this.Contract.
State;
403 this.Created = this.Contract.
Created;
404 this.Updated = this.Contract.Updated == DateTime.MinValue ? null : this.Contract.
Updated;
406 this.Duration = this.Contract.
Duration;
407 this.From = this.Contract.
From;
408 this.To = this.Contract.
To;
425 if (!NrSignatures.TryGetValue(
Signature.Role, out
int count))
428 NrSignatures[
Signature.Role] = count + 1;
432 if (this.Contract.
Roles is not
null)
441 this.Contract.State == ContractState.Approved ||
442 this.Contract.State == ContractState.BeingSigned ||
458 if (this.Contract.
Roles is not
null)
460 Grid RolesLayout =
new()
464 new ColumnDefinition(GridLength.Auto),
465 new ColumnDefinition(GridLength.Star)
476 if (!this.isReadOnly && AcceptsSignatures && !HasSigned && this.Contract.
PartsMode ==
ContractParts.Open &&
478 (!
this.IsProposal ||
Role.
Name ==
this.Role))
480 Button Button =
new()
486 Button.Clicked += this.SignButton_Clicked;
487 RolesLayout.Add(Button, 0, RolesLayout.RowDefinitions.Count);
489 Grid.SetColumnSpan(Button, 2);
493 this.Roles = RolesLayout;
498 Grid PartsLayout =
new()
502 new ColumnDefinition(GridLength.Auto),
503 new ColumnDefinition(GridLength.Star)
508 AddKeyValueLabelPair(PartsLayout,
ServiceRef.
Localizer[nameof(AppResources.SignAfter)],
this.Contract.SignAfter.Value.ToString(CultureInfo.CurrentCulture));
511 AddKeyValueLabelPair(PartsLayout,
ServiceRef.
Localizer[nameof(AppResources.SignBefore)],
this.Contract.SignBefore.Value.ToString(CultureInfo.CurrentCulture));
513 AddKeyValueLabelPair(PartsLayout,
ServiceRef.
Localizer[nameof(AppResources.Mode)],
this.Contract.PartsMode.ToString());
515 if (this.Contract.
Parts is not
null)
517 TapGestureRecognizer OpenLegalId =
new();
518 OpenLegalId.Tapped += this.Part_Tapped;
526 Button Button =
new()
532 Button.Clicked += this.SignButton_Clicked;
533 PartsLayout.RowDefinitions.Add(
new RowDefinition(GridLength.Auto));
535 PartsLayout.Add(Button, 0, PartsLayout.RowDefinitions.Count - 1);
540 this.Parts = PartsLayout;
546 Grid ParametersLayout =
new()
550 new ColumnDefinition(GridLength.Auto),
551 new ColumnDefinition(GridLength.Star)
558 AddKeyValueLabelPair(ParametersLayout,
Parameter.
Name, b ?
"✔" :
"✗");
563 this.Parameters = ParametersLayout;
568 VerticalStackLayout HumanReadableTextLayout = [];
569 string Xaml = await this.Contract.ToMauiXaml(this.Contract.DeviceLanguage());
570 VerticalStackLayout HumanReadableXaml =
new VerticalStackLayout().LoadFromXaml(Xaml);
572 List<IView>
Children = [.. HumanReadableXaml.Children];
576 if (View is ContentView ContentView)
578 foreach (Element InnerView
in ContentView.Children)
580 if (InnerView is Label Label)
585 HumanReadableTextLayout.Children.Add(View);
588 this.HumanReadableText = HumanReadableTextLayout;
593 Grid ClientSignaturesLayout =
new()
597 new ColumnDefinition(GridLength.Auto),
598 new ColumnDefinition(GridLength.Star)
601 TapGestureRecognizer openClientSignature =
new();
602 openClientSignature.Tapped += this.ClientSignature_Tapped;
607 StringBuilder sb =
new();
616 AddKeyValueLabelPair(ClientSignaturesLayout,
Signature.Role, sb.ToString(),
false, Sign, openClientSignature);
619 this.ClientSignatures = ClientSignaturesLayout;
625 Grid ServerSignaturesLayout =
new()
629 new ColumnDefinition(GridLength.Auto),
630 new ColumnDefinition(GridLength.Star)
634 TapGestureRecognizer openServerSignature =
new();
635 openServerSignature.Tapped += this.ServerSignature_Tapped;
637 StringBuilder sb =
new();
640 sb.Append(Convert.ToBase64String(
this.Contract.ServerSignature.DigitalSignature));
642 AddKeyValueLabelPair(ServerSignaturesLayout, this.Contract.
Provider, sb.ToString(),
false,
this.Contract.ContractId, openServerSignature);
643 this.ServerSignatures = ServerSignaturesLayout;
647 this.CanObsoleteContract = this.CanDeleteContract || CanObsolete;
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;
656 if (this.Contract.
Attachments is not
null &&
this.Contract.Attachments.Length > 0)
658 _ = this.photosLoader.LoadPhotos(this.Contract.
Attachments,
SignWith.LatestApprovedId, () =>
660 MainThread.BeginInvokeOnMainThread(() => this.HasPhotos = this.Photos.Count > 0);
664 this.HasPhotos =
false;
668 IEnumerable<KeyValuePair<string, object?>> Tags = this.GetClassAndMethod(MethodBase.GetCurrentMethod());
669 Tags = Tags.Append(
new KeyValuePair<string, object?>(
"ContractId", this.Contract?.ContractId ??
string.Empty));
673 this.ClearContract();
678 private static string GenerateMinMaxCountString(
int min,
int max)
685 return " (" + max.ToString(CultureInfo.InvariantCulture) +
")";
688 return " (" + min.ToString(CultureInfo.InvariantCulture) +
" - " + max.ToString(CultureInfo.InvariantCulture) +
")";
691 private static void AddKeyValueLabelPair(Grid Container,
string Key,
string Value)
693 AddKeyValueLabelPair(Container, Key, Value,
false,
string.Empty,
null);
696 private static void AddKeyValueLabelPair(Grid Container,
string Key,
string Value,
bool IsHtml,
697 TapGestureRecognizer TapGestureRecognizer)
699 AddKeyValueLabelPair(Container, Key, Value, IsHtml, Value, TapGestureRecognizer);
702 private static void AddKeyValueLabelPair(Grid Container,
string Key,
703 string Value,
bool IsHtml,
string StyleId, TapGestureRecognizer? TapGestureRecognizer)
705 int Row = Container.RowDefinitions.Count;
707 Container.RowDefinitions.Add(
new RowDefinition(GridLength.Auto));
709 Label KeyLabel =
new()
712 Style = AppStyles.KeyLabel
715 Label ValueLabel =
new()
718 TextType = IsHtml ? TextType.Html : TextType.Text,
720 Style = IsHtml ? AppStyles.FormattedValueLabel : TapGestureRecognizer is
null ? AppStyles.ValueLabel : AppStyles.ClickableValueLabel
723 Container.Add(KeyLabel, 0, Row);
724 Container.Add(ValueLabel, 1, Row);
726 if (TapGestureRecognizer is not
null)
727 ValueLabel.GestureRecognizers.Add(TapGestureRecognizer);
730 private async
void SignButton_Clicked(
object? Sender, EventArgs e)
732 if (this.Contract is
null)
737 if (Sender is Button Button && !
string.IsNullOrEmpty(Button.StyleId))
739 string Role = Button.StyleId;
747 this.skipContractEvent = DateTime.Now;
750 await this.ContractUpdated(
Contract);
763 private async
void Part_Tapped(
object? Sender, EventArgs e)
767 if (Sender is Label Label && !
string.IsNullOrEmpty(Label.StyleId))
780 private async
void ContractId_Tapped(
object? Sender, EventArgs e)
784 if (Sender is Label Label && !
string.IsNullOrEmpty(Label.StyleId))
797 private async
void Link_Tapped(
object? Sender, EventArgs e)
801 if (Sender is Label Label && !
string.IsNullOrEmpty(Label.StyleId))
811 private async
void CopyToClipboard_Tapped(
object? Sender, EventArgs e)
815 if (Sender is Label Label && !
string.IsNullOrEmpty(Label.StyleId))
817 await Clipboard.SetTextAsync(Label.StyleId);
828 private async
void ClientSignature_Tapped(
object? Sender, EventArgs e)
832 if (Sender is Label Label && !
string.IsNullOrEmpty(Label.StyleId))
834 string sign = Label.StyleId;
836 if (signature is not
null)
838 string legalId = signature.
LegalId;
853 private async
void ServerSignature_Tapped(
object? Sender, EventArgs e)
857 if (Sender is Label Label && !
string.IsNullOrEmpty(Label.StyleId))
874 private async Task ObsoleteContract()
876 if (this.Contract is
null)
887 this.skipContractEvent = DateTime.Now;
890 await this.ContractUpdated(obsoletedContract);
905 private async Task DeleteContract()
907 if (this.Contract is
null)
918 this.skipContractEvent = DateTime.Now;
921 await this.ContractUpdated(deletedContract);
936 private async Task ShowDetails()
938 if (this.Contract is
null)
943 byte[] Bin = Encoding.UTF8.GetBytes(this.Contract.
ForMachines.OuterXml);
950 await this.Copy(e.
GetUrl);
965 private async Task Copy(
object Item)
971 if (Item is
string Label)
973 if (Label == this.ContractId)
982 await Clipboard.SetTextAsync(Label);
990 await Clipboard.SetTextAsync(Item?.
ToString() ??
string.Empty);
1012 private static async Task OpenContract(
object Item)
1014 if (Item is
string ContractId)
1023 private async Task OpenLink(
object Item)
1025 if (Item is
string Url)
1028 await this.Copy(Url);
1031 await this.Copy(Item);
1034 #region ILinkableView
1039 public override string?
Link {
get; }
The Application class, representing an instance of the Neuro-Access app.
static Task< bool > AuthenticateUser(AuthenticationPurpose Purpose, bool Force=false)
Authenticates the user using the configured authentication method.
static Task< bool > OpenUrlAsync(string Url)
Opens an URL in the application.
static readonly TimeSpan UploadFile
Upload file timeout
const string IotSc
The IoT Smart Contract URI Scheme (iotsc)
static string CreateSmartContractUri(string id)
Generates a IoT Scan Uri form the specified id.
A set of never changing property constants and helpful values.
Base class that references services in the app.
static ILogService LogService
Log service.
static IUiService UiService
Service serializing and managing UI-related tasks.
static ITagProfile TagProfile
TAG Profile service.
static IStringLocalizer Localizer
Localization service
static IContractOrchestratorService ContractOrchestratorService
Contract orchestrator service.
static IXmppService XmppService
The XMPP service for XMPP communication.
Static class that gives access to app-specific themed colors
static Color PrimaryForeground
Primary foreground color.
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.
The data model for a contract.
static async Task< string > GetName(Contract? Contract)
Gets a displayable name for a contract.
The view model to bind to for when displaying contracts.
Contract? Contract
The contract to display.
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...
override? string Link
Link to the current view
override Task< string > Title
Title of the current view
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.
A page that displays a client signature.
Holds navigation parameters specific to views displaying a server signature.
A page that displays 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
DateTime Created
When the contract was created
byte[] ContentSchemaDigest
The hash digest of the schema used to validate the machine-readable contents (ForMachines) of the sma...
string ForMachinesLocalName
Local name used by the root node of the machine-readable contents of the contract (ForMachines).
Security.HashFunction ContentSchemaHashFunction
Hash function of the schema used to validate the machine-readable contents (ForMachines) of the smart...
Parameter[] Parameters
Defined parameters for the smart contract.
Duration? ArchiveRequired
Requied time to archive a signed smart contract, after it becomes obsolete.
ClientSignature[] ClientSignatures
Client signatures of the contract.
DateTime From
From when the contract is valid (if signed)
DateTime Updated
When the contract was last updated
async Task< bool > IsLegallyBinding(bool CheckCurrentTime, ContractsClient Client)
Checks if a contract is legally binding.
Attachment[] Attachments
Attachments assigned to the legal identity.
DateTime To
Until when the contract is valid (if signed)
string Provider
JID of the Trust Provider hosting the contract
Duration? ArchiveOptional
Optional time to archive a signed smart contract, after it becomes obsolete, and after its required a...
bool CanActAsTemplate
If the contract can act as a template for other contracts.
XmlElement ForMachines
Machine-readable contents of the contract.
Role[] Roles
Roles defined in the smart contract.
DateTime? SignAfter
Signatures will only be accepted after this point in time.
ContractParts PartsMode
How parts are defined in the smart contract.
ContractState State
Contract state
string ContractId
Contract identity
Part[] Parts
Defined parts for the smart contract.
Duration? Duration
Duration of the contract. Is counted from the time it is signed by the required parties.
string ForMachinesNamespace
Namespace used by the root node of the machine-readable contents of the contract (ForMachines).
DateTime? SignBefore
Signatures will only be accepted until this point in time.
ServerSignature ServerSignature
Server signature attesting to the validity of the contents of the contract.
ContractVisibility Visibility
Contrat Visibility
Contract()
Contains the definition of a contract
string TemplateId
Contract identity of template, if one was used to create the contract.
Task< string > ToHTML(string Language, Contract Contract)
Creates a human-readable HTML document for the contract.
Abstract base class for contractual parameters
string Name
Parameter name
abstract object ObjectValue
Parameter value.
Class defining a part in a contract
string LegalId
Legal identity of part
string Role
Role of the part in the contract
bool CanRevoke
If parts having this role, can revoke their signature, once signed.
int MaxCount
Largest amount of signatures of this role required for a legally binding contract.
int MinCount
Smallest amount of signatures of this role required for a legally binding contract.
string Name
Name of the role.
Abstract base class of signatures
byte[] DigitalSignature
Digital Signature
DateTime Timestamp
Timestamp of signature.
bool Ok
If the response is an OK result response (true), or an error response (false).
XmppException StanzaError
Any stanza error returned.
string ErrorText
Any error specific text.
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.
ContractParts
How the parts of the contract are defined.
ContractVisibility
Visibility types for contracts.
ContractState
Recognized contract states
Represents a duration value, as defined by the xsd:duration data type: http://www....