Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
CanReadViewModel.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
10using System.Collections.ObjectModel;
11using System.ComponentModel;
17using Waher.Things;
19
21{
25 public partial class CanReadViewModel : XmppViewModel
26 {
27 private readonly NotificationEvent? @event;
28 private readonly CanReadNavigationArgs? navigationArguments;
29
35 : base()
36 {
37 this.navigationArguments = Args;
38
39 this.Tags = [];
40 this.CallerTags = [];
41 this.Fields = [];
42 this.RuleRanges = [];
43
44 if (Args is not null)
45 {
46 this.@event = Args.Event;
47 this.BareJid = Args.BareJid;
48 this.FriendlyName = Args.FriendlyName;
49 this.RemoteJid = Args.RemoteJid;
50 this.RemoteFriendlyName = Args.RemoteFriendlyName;
51 this.Key = Args.Key;
52 this.ProvisioningService = Args.ProvisioningService;
53 this.FieldTypes = Args.FieldTypes;
54 this.NodeId = Args.NodeId;
55 this.SourceId = Args.SourceId;
56 this.PartitionId = Args.PartitionId;
57
58 this.PermitMomentary = Args.FieldTypes.HasFlag(FieldType.Momentary);
59 this.PermitIdentity = Args.FieldTypes.HasFlag(FieldType.Identity);
60 this.PermitStatus = Args.FieldTypes.HasFlag(FieldType.Status);
61 this.PermitComputed = Args.FieldTypes.HasFlag(FieldType.Computed);
62 this.PermitPeak = Args.FieldTypes.HasFlag(FieldType.Peak);
63 this.PermitHistorical = Args.FieldTypes.HasFlag(FieldType.Historical);
64
65 if (this.FriendlyName == this.BareJid)
66 this.FriendlyName = ServiceRef.Localizer[nameof(AppResources.NotAvailable)];
67
68 this.RemoteFriendlyNameAvailable = this.RemoteFriendlyName != this.RemoteJid;
69 if (!this.RemoteFriendlyNameAvailable)
70 this.RemoteFriendlyName = ServiceRef.Localizer[nameof(AppResources.NotAvailable)];
71 }
72 }
73
75 protected override async Task OnInitialize()
76 {
77 await base.OnInitialize();
78
79 if (this.navigationArguments is not null)
80 {
81 this.Tags.Clear();
82 this.CallerTags.Clear();
83
84 ContactInfo Thing = await ContactInfo.FindByBareJid(this.BareJid ?? string.Empty);
85 if (Thing?.MetaData is not null)
86 {
87 foreach (Property Tag in Thing.MetaData)
88 this.Tags.Add(new HumanReadableTag(Tag));
89 }
90
91 ContactInfo Caller = await ContactInfo.FindByBareJid(this.RemoteJid ?? string.Empty);
92 this.CallerInContactsList = Caller is not null;
93 if (Caller?.MetaData is not null)
94 {
95 foreach (Property Tag in Caller.MetaData)
96 this.CallerTags.Add(new HumanReadableTag(Tag));
97 }
98
99 if (this.navigationArguments.UserTokens is not null && this.navigationArguments.UserTokens.Length > 0)
100 {
101 foreach (ProvisioningToken Token in this.navigationArguments.UserTokens)
102 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.User)], Token.FriendlyName ?? Token.Token)));
103
104 this.HasUser = true;
105 }
106
107 if (this.navigationArguments.ServiceTokens is not null && this.navigationArguments.ServiceTokens.Length > 0)
108 {
109 foreach (ProvisioningToken Token in this.navigationArguments.ServiceTokens)
110 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.Service)], Token.FriendlyName ?? Token.Token)));
111
112 this.HasService = true;
113 }
114
115 if (this.navigationArguments.DeviceTokens is not null && this.navigationArguments.DeviceTokens.Length > 0)
116 {
117 foreach (ProvisioningToken Token in this.navigationArguments.DeviceTokens)
118 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.Device)], Token.FriendlyName ?? Token.Token)));
119
120 this.HasDevice = true;
121 }
122
123 this.Fields.Clear();
124
125 SortedDictionary<string, bool> PermittedFields = [];
126 string[]? AllFields = this.navigationArguments.AllFields;
127
128 if (AllFields is null && !string.IsNullOrEmpty(this.BareJid))
129 {
130 AllFields = await CanReadNotificationEvent.GetAvailableFieldNames(this.BareJid, new ThingReference(this.NodeId, this.SourceId, this.PartitionId));
131
132 if (AllFields is not null && this.navigationArguments.Event is not null)
133 {
134 this.navigationArguments.Event.AllFields = AllFields;
135 await Database.Update(this.navigationArguments.Event);
136 }
137 }
138
139 bool AllFieldsPermitted = this.navigationArguments.Fields is null;
140
141 if (AllFields is not null)
142 {
143 foreach (string Field in AllFields)
144 PermittedFields[Field] = AllFieldsPermitted;
145 }
146
147 if (!AllFieldsPermitted && this.navigationArguments?.Fields is not null)
148 {
149 foreach (string Field in this.navigationArguments.Fields)
150 PermittedFields[Field] = true;
151 }
152
153 foreach (KeyValuePair<string, bool> P in PermittedFields)
154 {
155 FieldReference Field = new(P.Key, P.Value);
156 this.Fields.Add(Field);
157 }
158
159 this.RuleRanges.Clear();
160 this.RuleRanges.Add(new RuleRangeModel(RuleRange.Caller, ServiceRef.Localizer[nameof(AppResources.CallerOnly)]));
161
162 if (this.HasUser)
163 this.RuleRanges.Add(new RuleRangeModel("User", ServiceRef.Localizer[nameof(AppResources.ToUser)]));
164
165 if (this.HasUser)
166 this.RuleRanges.Add(new RuleRangeModel("Service", ServiceRef.Localizer[nameof(AppResources.ToService)]));
167
168 if (this.HasUser)
169 this.RuleRanges.Add(new RuleRangeModel("Device", ServiceRef.Localizer[nameof(AppResources.ToDevice)]));
170
171 this.RuleRanges.Add(new RuleRangeModel(RuleRange.Domain, ServiceRef.Localizer[nameof(AppResources.EntireDomain), XmppClient.GetDomain(this.RemoteJid)]));
172 this.RuleRanges.Add(new RuleRangeModel(RuleRange.All, ServiceRef.Localizer[nameof(AppResources.Everyone)]));
173
174 this.SelectedRuleRangeIndex = 0;
175 }
176 }
177
178 #region Properties
179
183 public ObservableCollection<HumanReadableTag> Tags { get; }
184
188 public ObservableCollection<HumanReadableTag> CallerTags { get; }
189
193 public ObservableCollection<FieldReference> Fields { get; }
194
198 public ObservableCollection<RuleRangeModel> RuleRanges { get; }
199
203 [ObservableProperty]
204 private string? bareJid;
205
209 [ObservableProperty]
210 private string? friendlyName;
211
215 [ObservableProperty]
216 private string? remoteJid;
217
221 [ObservableProperty]
222 private string? remoteFriendlyName;
223
227 [ObservableProperty]
228 private bool remoteFriendlyNameAvailable;
229
233 [ObservableProperty]
234 private string? key;
235
239 [ObservableProperty]
240 private bool hasUser;
241
245 [ObservableProperty]
246 private bool hasService;
247
251 [ObservableProperty]
252 private bool hasDevice;
253
257 [ObservableProperty]
258 private string? provisioningService;
259
263 [ObservableProperty]
264 private bool callerInContactsList;
265
269 [ObservableProperty]
270 private int selectedRuleRangeIndex;
271
275 [ObservableProperty]
276 private FieldType fieldTypes;
277
281 [ObservableProperty]
282 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
283 private bool permitMomentary;
284
288 [ObservableProperty]
289 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
290 private bool permitIdentity;
291
295 [ObservableProperty]
296 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
297 private bool permitStatus;
298
302 [ObservableProperty]
303 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
304 private bool permitComputed;
305
309 [ObservableProperty]
310 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
311 private bool permitPeak;
312
316 [ObservableProperty]
317 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
318 private bool permitHistorical;
319
323 [ObservableProperty]
324 private string? nodeId;
325
329 [ObservableProperty]
330 private string? sourceId;
331
335 [ObservableProperty]
336 private string? partitionId;
337
338 #endregion
339
343 [RelayCommand]
344 private static Task Click(object obj)
345 {
346 if (obj is HumanReadableTag Tag)
347 return ViewClaimThingViewModel.LabelClicked(Tag.Name, Tag.Value, Tag.LocalizedValue);
348 else if (obj is string s)
349 return ViewClaimThingViewModel.LabelClicked(string.Empty, s, s);
350 else
351 return Task.CompletedTask;
352 }
353
357 [RelayCommand]
358 private async Task AddContact()
359 {
360 if (!this.CallerInContactsList)
361 {
362 ContactInfo Info = new()
363 {
364 BareJid = this.RemoteJid,
365 FriendlyName = (this.RemoteFriendlyNameAvailable ? this.RemoteFriendlyName : this.RemoteJid) ?? string.Empty
366 };
367
368 await Database.Insert(Info);
369
370 this.CallerInContactsList = true;
371 }
372 }
373
377 [RelayCommand]
378 private async Task RemoveContact()
379 {
380 if (this.CallerInContactsList)
381 {
382 ContactInfo Info = await ContactInfo.FindByBareJid(this.RemoteJid ?? string.Empty);
383 if (Info is not null)
384 await Database.Delete(Info);
385
386 this.CallerInContactsList = false;
387 }
388 }
389
393 [RelayCommand(CanExecute = nameof(CanExecuteAccept))]
394 private void Accept()
395 {
396 this.Respond(true);
397 }
398
400 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
401 {
402 base.OnPropertyChanged(e);
403
404 switch (e.PropertyName)
405 {
406 case nameof(this.IsConnected):
407 this.AcceptCommand.NotifyCanExecuteChanged();
408 break;
409 }
410 }
411
412 private bool CanExecuteAccept()
413 {
414 if (!this.IsConnected)
415 return false;
416
417 if (this.PermitMomentary || this.PermitIdentity || this.PermitStatus || this.PermitComputed || this.PermitPeak || this.PermitHistorical)
418 return true;
419
420 foreach (FieldReference Field in this.Fields)
421 {
422 if (Field.Permitted)
423 return true;
424 }
425
426 return false;
427 }
428
432 [RelayCommand(CanExecute = nameof(this.IsConnected))]
433 private void Reject()
434 {
435 this.Respond(false);
436 }
437
438 private void Respond(bool Accepts)
439 {
440 if (this.SelectedRuleRangeIndex >= 0)
441 {
442 RuleRangeModel Range = this.RuleRanges[this.SelectedRuleRangeIndex];
443 ThingReference Thing = new(this.NodeId, this.SourceId, this.PartitionId);
444 FieldType FieldTypes = (FieldType)0;
445
446 if (this.PermitMomentary)
447 FieldTypes |= FieldType.Momentary;
448
449 if (this.PermitIdentity)
450 FieldTypes |= FieldType.Identity;
451
452 if (this.PermitStatus)
453 FieldTypes |= FieldType.Status;
454
455 if (this.PermitComputed)
456 FieldTypes |= FieldType.Computed;
457
458 if (this.PermitPeak)
459 FieldTypes |= FieldType.Peak;
460
461 if (this.PermitHistorical)
462 FieldTypes |= FieldType.Historical;
463
464 if (Range.RuleRange is RuleRange RuleRange)
465 {
466 ReadoutRequestResolver Resolver = new(this.BareJid ?? string.Empty, this.RemoteFriendlyName ?? string.Empty, RuleRange);
467
468 switch (RuleRange)
469 {
470 case RuleRange.Caller:
471 default:
472 ServiceRef.XmppService.CanReadResponseCaller(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
473 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Thing, this.ResponseHandler, Resolver);
474 break;
475
476 case RuleRange.Domain:
477 ServiceRef.XmppService.CanReadResponseDomain(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
478 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Thing, this.ResponseHandler, Resolver);
479 break;
480
481 case RuleRange.All:
482 ServiceRef.XmppService.CanReadResponseAll(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
483 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Thing, this.ResponseHandler, Resolver);
484 break;
485
486 }
487 }
488 else if (Range.RuleRange is ProvisioningToken Token)
489 {
490 ReadoutRequestResolver Resolver = new(this.BareJid ?? string.Empty, this.RemoteFriendlyName ?? string.Empty, Token);
491
492 switch (Token.Type)
493 {
494 case TokenType.User:
495 ServiceRef.XmppService.CanReadResponseUser(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
496 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Token.Token, Thing, this.ResponseHandler, Resolver);
497 break;
498
499 case TokenType.Service:
500 ServiceRef.XmppService.CanReadResponseService(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
501 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Token.Token, Thing, this.ResponseHandler, Resolver);
502 break;
503
504 case TokenType.Device:
505 ServiceRef.XmppService.CanReadResponseDevice(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
506 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Token.Token, Thing, this.ResponseHandler, Resolver);
507 break;
508 }
509 }
510 }
511 }
512
513 private string[]? GetFields()
514 {
515 List<string> Result = [];
516 bool AllPermitted = true;
517
518 foreach (FieldReference Field in this.Fields)
519 {
520 if (Field.Permitted)
521 Result.Add(Field.Name);
522 else
523 AllPermitted = false;
524 }
525
526 if (AllPermitted)
527 return null;
528 else
529 return [.. Result];
530 }
531
532 private async Task ResponseHandler(object? Sender, IqResultEventArgs e)
533 {
534 if (e.Ok)
535 {
536 if (this.@event is not null)
538
540
541 MainThread.BeginInvokeOnMainThread(async () =>
542 {
543 await this.GoBack();
544 });
545 }
546 else
547 {
548 MainThread.BeginInvokeOnMainThread(async () => await ServiceRef.UiService.DisplayException(e.StanzaError ??
549 new Exception(ServiceRef.Localizer[nameof(AppResources.UnableToRespond)])));
550 }
551 }
552
556 [RelayCommand]
557 private async Task Ignore()
558 {
559 if (this.@event is not null)
561
562 await this.GoBack();
563 }
564
568 [RelayCommand]
569 private void AllFieldTypes()
570 {
571 this.PermitMomentary = true;
572 this.PermitIdentity = true;
573 this.PermitStatus = true;
574 this.PermitComputed = true;
575 this.PermitPeak = true;
576 this.PermitHistorical = true;
577 }
578
582 [RelayCommand]
583 private void NoFieldTypes()
584 {
585 this.PermitMomentary = false;
586 this.PermitIdentity = false;
587 this.PermitStatus = false;
588 this.PermitComputed = false;
589 this.PermitPeak = false;
590 this.PermitHistorical = false;
591 }
592
596 [RelayCommand]
597 private void AllFields()
598 {
599 foreach (FieldReference Field in this.Fields)
600 Field.Permitted = true;
601 }
602
606 [RelayCommand]
607 private void NoFields()
608 {
609 foreach (FieldReference Field in this.Fields)
610 Field.Permitted = false;
611 }
612
613 }
614}
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[]?> GetAvailableFieldNames(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-read provisioning question.
new? CanReadNotificationEvent Event
Notification event objcet.
The view model to bind to when displaying a thing.
ObservableCollection< HumanReadableTag > CallerTags
Holds a list of meta-data tags associated with the caller.
ObservableCollection< HumanReadableTag > Tags
Holds a list of meta-data tags associated with a thing.
override void OnPropertyChanged(PropertyChangedEventArgs e)
override async Task OnInitialize()
Method called when view is initialized for the first time. Use this method to implement registration ...
ObservableCollection< FieldReference > Fields
Holds a list of fields that will be permitted.
ObservableCollection< RuleRangeModel > RuleRanges
Available Rule Ranges
CanReadViewModel(CanReadNavigationArgs? Args)
Creates an instance of the CanReadViewModel class.
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.
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
Base class for all sensor data fields.
Definition: Field.cs:20
string Name
Unlocalized field name.
Definition: Field.cs:279
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
FieldType
Field Type flags
Definition: FieldType.cs:10