Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
CanControlViewModel.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
11using System.Collections.ObjectModel;
12using System.ComponentModel;
17using Waher.Things;
18
20{
24 public partial class CanControlViewModel : XmppViewModel
25 {
26 private readonly NotificationEvent? @event;
27 private readonly CanControlNavigationArgs? navigationArguments;
28
34 : base()
35 {
36 this.navigationArguments = Args;
37
38 this.Tags = [];
39 this.CallerTags = [];
40 this.Parameters = [];
41 this.RuleRanges = [];
42
43 if (Args is not null)
44 {
45 this.@event = Args.Event;
46 this.BareJid = Args.BareJid;
47 this.FriendlyName = Args.FriendlyName;
48 this.RemoteJid = Args.RemoteJid;
49 this.RemoteFriendlyName = Args.RemoteFriendlyName;
50 this.Key = Args.Key;
51 this.ProvisioningService = Args.ProvisioningService;
52 this.NodeId = Args.NodeId;
53 this.SourceId = Args.SourceId;
54 this.PartitionId = Args.PartitionId;
55
56 if (this.FriendlyName == this.BareJid)
57 this.FriendlyName = ServiceRef.Localizer[nameof(AppResources.NotAvailable)];
58
59 this.RemoteFriendlyNameAvailable = this.RemoteFriendlyName != this.RemoteJid;
60 if (!this.RemoteFriendlyNameAvailable)
61 this.RemoteFriendlyName = ServiceRef.Localizer[nameof(AppResources.NotAvailable)];
62 }
63 }
64
66 protected override async Task OnInitialize()
67 {
68 await base.OnInitialize();
69
70 if (this.navigationArguments is not null)
71 {
72 this.Tags.Clear();
73 this.CallerTags.Clear();
74
75 ContactInfo Thing = await ContactInfo.FindByBareJid(this.BareJid ?? string.Empty);
76 if (Thing?.MetaData is not null)
77 {
78 foreach (Property Tag in Thing.MetaData)
79 this.Tags.Add(new HumanReadableTag(Tag));
80 }
81
82 ContactInfo Caller = await ContactInfo.FindByBareJid(this.RemoteJid ?? string.Empty);
83 this.CallerInContactsList = Caller is not null;
84 if (Caller?.MetaData is not null)
85 {
86 foreach (Property Tag in Caller.MetaData)
87 this.CallerTags.Add(new HumanReadableTag(Tag));
88 }
89
90 if (this.navigationArguments.UserTokens is not null && this.navigationArguments.UserTokens.Length > 0)
91 {
92 foreach (ProvisioningToken Token in this.navigationArguments.UserTokens)
93 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.User)], Token.FriendlyName ?? Token.Token)));
94
95 this.HasUser = true;
96 }
97
98 if (this.navigationArguments.ServiceTokens is not null && this.navigationArguments.ServiceTokens.Length > 0)
99 {
100 foreach (ProvisioningToken Token in this.navigationArguments.ServiceTokens)
101 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.Service)], Token.FriendlyName ?? Token.Token)));
102
103 this.HasService = true;
104 }
105
106 if (this.navigationArguments.DeviceTokens is not null && this.navigationArguments.DeviceTokens.Length > 0)
107 {
108 foreach (ProvisioningToken Token in this.navigationArguments.DeviceTokens)
109 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.Device)], Token.FriendlyName ?? Token.Token)));
110
111 this.HasDevice = true;
112 }
113
114 this.Parameters.Clear();
115
116 SortedDictionary<string, bool> PermittedParameters = [];
117 string[]? AllParameters = this.navigationArguments.AllParameters;
118
119 if (AllParameters is null && !string.IsNullOrEmpty(this.BareJid))
120 {
121 AllParameters = await CanControlNotificationEvent.GetAvailableParameterNames(this.BareJid,
122 new ThingReference(this.NodeId, this.SourceId, this.PartitionId));
123
124 if (AllParameters is not null && this.navigationArguments?.Event is not null)
125 {
126 this.navigationArguments.Event.AllParameters = AllParameters;
127 await Database.Update(this.navigationArguments.Event);
128 }
129 }
130
131 bool AllParametersPermitted = this.navigationArguments?.Parameters is null;
132
133 if (AllParameters is not null)
134 {
135 foreach (string Parameter in AllParameters)
136 PermittedParameters[Parameter] = AllParametersPermitted;
137 }
138
139 if (!AllParametersPermitted && this.navigationArguments?.Parameters is not null)
140 {
141 foreach (string Parameter in this.navigationArguments.Parameters)
142 PermittedParameters[Parameter] = true;
143 }
144
145 foreach (KeyValuePair<string, bool> P in PermittedParameters)
146 {
147 FieldReference Parameter = new(P.Key, P.Value);
148 this.Parameters.Add(Parameter);
149 }
150
151 this.RuleRanges.Clear();
152 this.RuleRanges.Add(new RuleRangeModel(RuleRange.Caller, ServiceRef.Localizer[nameof(AppResources.CallerOnly)]));
153
154 if (this.HasUser)
155 this.RuleRanges.Add(new RuleRangeModel("User", ServiceRef.Localizer[nameof(AppResources.ToUser)]));
156
157 if (this.HasUser)
158 this.RuleRanges.Add(new RuleRangeModel("Service", ServiceRef.Localizer[nameof(AppResources.ToService)]));
159
160 if (this.HasUser)
161 this.RuleRanges.Add(new RuleRangeModel("Device", ServiceRef.Localizer[nameof(AppResources.ToDevice)]));
162
163 this.RuleRanges.Add(new RuleRangeModel(RuleRange.Domain, ServiceRef.Localizer[nameof(AppResources.EntireDomain), XmppClient.GetDomain(this.RemoteJid)]));
164 this.RuleRanges.Add(new RuleRangeModel(RuleRange.All, ServiceRef.Localizer[nameof(AppResources.Everyone)]));
165
166 this.SelectedRuleRangeIndex = 0;
167 }
168 }
169
170 #region Properties
171
175 public ObservableCollection<HumanReadableTag> Tags { get; }
176
180 public ObservableCollection<HumanReadableTag> CallerTags { get; }
181
185 public ObservableCollection<FieldReference> Parameters { get; }
186
190 public ObservableCollection<RuleRangeModel> RuleRanges { get; }
191
195 [ObservableProperty]
196 private string? bareJid;
197
201 [ObservableProperty]
202 private string? friendlyName;
203
207 [ObservableProperty]
208 private string? remoteJid;
209
210
214 [ObservableProperty]
215 private string? remoteFriendlyName;
216
220 [ObservableProperty]
221 private bool remoteFriendlyNameAvailable;
222
226 [ObservableProperty]
227 private string? key;
228
232 [ObservableProperty]
233 private bool hasUser;
234
238 [ObservableProperty]
239 private bool hasService;
240
244 [ObservableProperty]
245 private bool hasDevice;
246
250 [ObservableProperty]
251 private string? provisioningService;
252
256 [ObservableProperty]
257 private bool callerInContactsList;
258
262 [ObservableProperty]
263 private int selectedRuleRangeIndex;
264
268 [ObservableProperty]
269 private string? nodeId;
270
274 [ObservableProperty]
275 private string? sourceId;
276
280 [ObservableProperty]
281 private string? partitionId;
282
283 #endregion
284
288 [RelayCommand]
289 private static Task Click(object obj)
290 {
291 if (obj is HumanReadableTag Tag)
292 return ViewClaimThingViewModel.LabelClicked(Tag.Name, Tag.Value, Tag.LocalizedValue);
293 else if (obj is string s)
294 return ViewClaimThingViewModel.LabelClicked(string.Empty, s, s);
295 else
296 return Task.CompletedTask;
297 }
298
302 [RelayCommand]
303 private async Task AddContact()
304 {
305 if (!this.CallerInContactsList)
306 {
307 ContactInfo Info = new()
308 {
309 BareJid = this.RemoteJid,
310 FriendlyName = (this.RemoteFriendlyNameAvailable ? this.RemoteFriendlyName : this.RemoteJid) ?? string.Empty
311 };
312
313 await Database.Insert(Info);
314
315 this.CallerInContactsList = true;
316 }
317 }
318
322 [RelayCommand]
323 private async Task RemoveContact()
324 {
325 if (this.CallerInContactsList && !string.IsNullOrEmpty(this.RemoteJid))
326 {
327 ContactInfo Info = await ContactInfo.FindByBareJid(this.RemoteJid);
328 if (Info is not null)
329 await Database.Delete(Info);
330
331 this.CallerInContactsList = false;
332 }
333 }
334
338 [RelayCommand(CanExecute = nameof(CanExecuteAccept))]
339 private void Accept()
340 {
341 this.Respond(true);
342 }
343
345 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
346 {
347 base.OnPropertyChanged(e);
348
349 switch (e.PropertyName)
350 {
351 case nameof(this.IsConnected):
352 this.AcceptCommand.NotifyCanExecuteChanged();
353 break;
354 }
355 }
356
357 private bool CanExecuteAccept()
358 {
359 if (!this.IsConnected)
360 return false;
361
362 foreach (FieldReference Parameter in this.Parameters)
363 {
364 if (Parameter.Permitted)
365 return true;
366 }
367
368 return false;
369 }
370
374 [RelayCommand(CanExecute = nameof(IsConnected))]
375 private void Reject()
376 {
377 this.Respond(false);
378 }
379
380 private void Respond(bool Accepts)
381 {
382 if (this.SelectedRuleRangeIndex >= 0)
383 {
384 RuleRangeModel Range = this.RuleRanges[this.SelectedRuleRangeIndex];
385 ThingReference Thing = new(this.NodeId, this.SourceId, this.PartitionId);
386
387 if (Range.RuleRange is RuleRange RuleRange)
388 {
389 ControlRequestResolver Resolver = new(this.BareJid!, this.RemoteFriendlyName ?? string.Empty, RuleRange);
390
391 switch (RuleRange)
392 {
393 case RuleRange.Caller:
394 default:
395 ServiceRef.XmppService.CanControlResponseCaller(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
396 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, this.GetParameters(), Thing, this.ResponseHandler, Resolver);
397 break;
398
399 case RuleRange.Domain:
400 ServiceRef.XmppService.CanControlResponseDomain(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
401 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, this.GetParameters(), Thing, this.ResponseHandler, Resolver);
402 break;
403
404 case RuleRange.All:
405 ServiceRef.XmppService.CanControlResponseAll(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
406 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, this.GetParameters(), Thing, this.ResponseHandler, Resolver);
407 break;
408
409 }
410 }
411 else if (Range.RuleRange is ProvisioningToken Token)
412 {
413 ControlRequestResolver Resolver = new(this.BareJid!, this.RemoteFriendlyName ?? string.Empty, Token);
414
415 switch (Token.Type)
416 {
417 case TokenType.User:
418 ServiceRef.XmppService.CanControlResponseUser(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
419 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, this.GetParameters(), Token.Token, Thing, this.ResponseHandler, Resolver);
420 break;
421
422 case TokenType.Service:
423 ServiceRef.XmppService.CanControlResponseService(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
424 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, this.GetParameters(), Token.Token, Thing, this.ResponseHandler, Resolver);
425 break;
426
427 case TokenType.Device:
428 ServiceRef.XmppService.CanControlResponseDevice(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
429 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, this.GetParameters(), Token.Token, Thing, this.ResponseHandler, Resolver);
430 break;
431 }
432 }
433 }
434 }
435
436 private string[]? GetParameters()
437 {
438 List<string> Result = [];
439 bool AllPermitted = true;
440
441 foreach (FieldReference Parameter in this.Parameters)
442 {
443 if (Parameter.Permitted)
444 Result.Add(Parameter.Name);
445 else
446 AllPermitted = false;
447 }
448
449 if (AllPermitted)
450 return null;
451 else
452 return [.. Result];
453 }
454
455 private async Task ResponseHandler(object? Sender, IqResultEventArgs e)
456 {
457 if (e.Ok)
458 {
459 if (this.@event is not null)
461
463
464 MainThread.BeginInvokeOnMainThread(async () =>
465 {
466 await this.GoBack();
467 });
468 }
469 else
470 {
471 MainThread.BeginInvokeOnMainThread(async () => await ServiceRef.UiService.DisplayException(e.StanzaError ??
472 new Exception(ServiceRef.Localizer[nameof(AppResources.UnableToRespond)])));
473 }
474 }
475
479 [RelayCommand]
480 private async Task Ignore()
481 {
482 if (this.@event is not null)
484
485 await this.GoBack();
486 }
487
491 [RelayCommand]
492 private void AllParameters()
493 {
494 foreach (FieldReference Parameter in this.Parameters)
495 Parameter.Permitted = true;
496 }
497
501 [RelayCommand]
502 private void NoParameters()
503 {
504 foreach (FieldReference Parameter in this.Parameters)
505 Parameter.Permitted = false;
506 }
507
508 }
509}
Contains information about a contact.
Definition: ContactInfo.cs:21
Property?[] MetaData
Meta-data related to a thing.
Definition: ContactInfo.cs:210
static Task< ContactInfo > FindByBareJid(string BareJid)
Finds information about a contact, given its Bare JID.
Definition: ContactInfo.cs:220
static async Task< string[]?> GetAvailableParameterNames(string BareJid, ThingReference Thing)
Gets available fields for a thing.
Base class that references services in the app.
Definition: ServiceRef.cs:31
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:55
static INotificationService NotificationService
Service for managing notifications for the user.
Definition: ServiceRef.cs:211
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:79
static IStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:235
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:67
virtual async Task GoBack()
Method called when user wants to navigate to the previous screen.
Holds navigation parameters specific to displaying the can-control provisioning question.
new? CanControlNotificationEvent Event
Notification event objcet.
The view model to bind to when displaying a thing.
override void OnPropertyChanged(PropertyChangedEventArgs e)
ObservableCollection< RuleRangeModel > RuleRanges
Available Rule Ranges
CanControlViewModel(CanControlNavigationArgs? Args)
Creates an instance of the CanControlViewModel class.
ObservableCollection< HumanReadableTag > Tags
Holds a list of meta-data tags associated with a thing.
override async Task OnInitialize()
Method called when view is initialized for the first time. Use this method to implement registration ...
ObservableCollection< HumanReadableTag > CallerTags
Holds a list of meta-data tags associated with the caller.
ObservableCollection< FieldReference > Parameters
Holds a list of parameters that will be permitted.
Class used to present a meta-data tag in a human interface.
string? RemoteJid
Bare JID of remote entity trying to connect to device.
The view model to bind to for when displaying thing claim information.
static async Task LabelClicked(string Name, string Value, string LocalizedValue)
Processes the click of a localized meta-data label.
A view model that holds the XMPP state.
Abstract base class for contractual parameters
Definition: Parameter.cs:17
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
static string GetDomain(string JID)
Gets the domain part of a JID.
Definition: XmppClient.cs:6929
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
Contains a reference to a thing
Interface for event resolvers. Such can be used to resolve multiple pending notifications at once.
Task DeleteEvents(NotificationEventType Type, CaseInsensitiveString Category)
Deletes events for a given button and category.
Task DeleteResolvedEvents(IEventResolver Resolver)
Deletes pending events that have already been resolved.
abstract class NotificationEvent()
Abstract base class of notification events.
class RuleRangeModel(object RuleRange, string Label)
Rule Range model
RuleRange
Range of a rule change
Definition: RuleRange.cs:7