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;
18using Waher.Things;
20
22{
26 public partial class CanReadViewModel : XmppViewModel
27 {
28 private readonly NotificationEvent? @event;
29 private readonly CanReadNavigationArgs? navigationArguments;
30
36 : base()
37 {
38 this.navigationArguments = Args;
39
40 this.Tags = [];
41 this.CallerTags = [];
42 this.Fields = [];
43 this.RuleRanges = [];
44
45 if (Args is not null)
46 {
47 this.@event = Args.Event;
48 this.BareJid = Args.BareJid;
49 this.FriendlyName = Args.FriendlyName;
50 this.RemoteJid = Args.RemoteJid;
51 this.RemoteFriendlyName = Args.RemoteFriendlyName;
52 this.Key = Args.Key;
53 this.ProvisioningService = Args.ProvisioningService;
54 this.FieldTypes = Args.FieldTypes;
55 this.NodeId = Args.NodeId;
56 this.SourceId = Args.SourceId;
57 this.PartitionId = Args.PartitionId;
58
59 this.PermitMomentary = Args.FieldTypes.HasFlag(FieldType.Momentary);
60 this.PermitIdentity = Args.FieldTypes.HasFlag(FieldType.Identity);
61 this.PermitStatus = Args.FieldTypes.HasFlag(FieldType.Status);
62 this.PermitComputed = Args.FieldTypes.HasFlag(FieldType.Computed);
63 this.PermitPeak = Args.FieldTypes.HasFlag(FieldType.Peak);
64 this.PermitHistorical = Args.FieldTypes.HasFlag(FieldType.Historical);
65
66 if (this.FriendlyName == this.BareJid)
67 this.FriendlyName = ServiceRef.Localizer[nameof(AppResources.NotAvailable)];
68
69 this.RemoteFriendlyNameAvailable = this.RemoteFriendlyName != this.RemoteJid;
70 if (!this.RemoteFriendlyNameAvailable)
71 this.RemoteFriendlyName = ServiceRef.Localizer[nameof(AppResources.NotAvailable)];
72 }
73 }
74
76 public override async Task OnInitializeAsync()
77 {
78 await base.OnInitializeAsync();
79
80 if (this.navigationArguments is not null)
81 {
82 this.Tags.Clear();
83 this.CallerTags.Clear();
84
85 ContactInfo Thing = await ContactInfo.FindByBareJid(this.BareJid ?? string.Empty);
86 if (Thing?.MetaData is not null)
87 {
88 foreach (Property Tag in Thing.MetaData)
89 this.Tags.Add(new HumanReadableTag(Tag));
90 }
91
92 ContactInfo Caller = await ContactInfo.FindByBareJid(this.RemoteJid ?? string.Empty);
93 this.CallerInContactsList = Caller is not null;
94 if (Caller?.MetaData is not null)
95 {
96 foreach (Property Tag in Caller.MetaData)
97 this.CallerTags.Add(new HumanReadableTag(Tag));
98 }
99
100 if (this.navigationArguments.UserTokens is not null && this.navigationArguments.UserTokens.Length > 0)
101 {
102 foreach (ProvisioningToken Token in this.navigationArguments.UserTokens)
103 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.User)], Token.FriendlyName ?? Token.Token)));
104
105 this.HasUser = true;
106 }
107
108 if (this.navigationArguments.ServiceTokens is not null && this.navigationArguments.ServiceTokens.Length > 0)
109 {
110 foreach (ProvisioningToken Token in this.navigationArguments.ServiceTokens)
112
113 this.HasService = true;
114 }
115
116 if (this.navigationArguments.DeviceTokens is not null && this.navigationArguments.DeviceTokens.Length > 0)
117 {
118 foreach (ProvisioningToken Token in this.navigationArguments.DeviceTokens)
119 this.CallerTags.Add(new HumanReadableTag(new Property(ServiceRef.Localizer[nameof(AppResources.Device)], Token.FriendlyName ?? Token.Token)));
120
121 this.HasDevice = true;
122 }
123
124 this.Fields.Clear();
125
126 SortedDictionary<string, bool> PermittedFields = [];
127 string[]? AllFields = this.navigationArguments.AllFields;
128
129 if (AllFields is null && !string.IsNullOrEmpty(this.BareJid))
130 {
131 AllFields = await CanReadNotificationEvent.GetAvailableFieldNames(this.BareJid, new ThingReference(this.NodeId, this.SourceId, this.PartitionId));
132
133 if (AllFields is not null && this.navigationArguments.Event is not null)
134 {
135 this.navigationArguments.Event.AllFields = AllFields;
136 await Database.Update(this.navigationArguments.Event);
137 }
138 }
139
140 bool AllFieldsPermitted = this.navigationArguments.Fields is null;
141
142 if (AllFields is not null)
143 {
144 foreach (string Field in AllFields)
145 PermittedFields[Field] = AllFieldsPermitted;
146 }
147
148 if (!AllFieldsPermitted && this.navigationArguments?.Fields is not null)
149 {
150 foreach (string Field in this.navigationArguments.Fields)
151 PermittedFields[Field] = true;
152 }
153
154 foreach (KeyValuePair<string, bool> P in PermittedFields)
155 {
156 FieldReference Field = new(P.Key, P.Value);
157 this.Fields.Add(Field);
158 }
159
160 this.RuleRanges.Clear();
162
163 if (this.HasUser)
164 this.RuleRanges.Add(new RuleRangeModel("User", ServiceRef.Localizer[nameof(AppResources.ToUser)]));
165
166 if (this.HasUser)
167 this.RuleRanges.Add(new RuleRangeModel("Service", ServiceRef.Localizer[nameof(AppResources.ToService)]));
168
169 if (this.HasUser)
170 this.RuleRanges.Add(new RuleRangeModel("Device", ServiceRef.Localizer[nameof(AppResources.ToDevice)]));
171
174
175 this.SelectedRuleRangeIndex = 0;
176 }
177 }
178
179 #region Properties
180
184 public ObservableCollection<HumanReadableTag> Tags { get; }
185
189 public ObservableCollection<HumanReadableTag> CallerTags { get; }
190
194 public ObservableCollection<FieldReference> Fields { get; }
195
199 public ObservableCollection<RuleRangeModel> RuleRanges { get; }
200
204 [ObservableProperty]
205 private string? bareJid;
206
210 [ObservableProperty]
211 private string? friendlyName;
212
216 [ObservableProperty]
217 private string? remoteJid;
218
222 [ObservableProperty]
223 private string? remoteFriendlyName;
224
228 [ObservableProperty]
229 private bool remoteFriendlyNameAvailable;
230
234 [ObservableProperty]
235 private string? key;
236
240 [ObservableProperty]
241 private bool hasUser;
242
246 [ObservableProperty]
247 private bool hasService;
248
252 [ObservableProperty]
253 private bool hasDevice;
254
258 [ObservableProperty]
259 private string? provisioningService;
260
264 [ObservableProperty]
265 private bool callerInContactsList;
266
270 [ObservableProperty]
271 private int selectedRuleRangeIndex;
272
276 [ObservableProperty]
277 private FieldType fieldTypes;
278
282 [ObservableProperty]
283 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
284 private bool permitMomentary;
285
289 [ObservableProperty]
290 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
291 private bool permitIdentity;
292
296 [ObservableProperty]
297 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
298 private bool permitStatus;
299
303 [ObservableProperty]
304 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
305 private bool permitComputed;
306
310 [ObservableProperty]
311 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
312 private bool permitPeak;
313
317 [ObservableProperty]
318 [NotifyCanExecuteChangedFor(nameof(AcceptCommand))]
319 private bool permitHistorical;
320
324 [ObservableProperty]
325 private string? nodeId;
326
330 [ObservableProperty]
331 private string? sourceId;
332
336 [ObservableProperty]
337 private string? partitionId;
338
339 #endregion
340
344 [RelayCommand]
345 private static Task Click(object obj)
346 {
347 if (obj is HumanReadableTag Tag)
348 return ViewClaimThingViewModel.LabelClicked(Tag.Name, Tag.Value, Tag.LocalizedValue);
349 else if (obj is string s)
350 return ViewClaimThingViewModel.LabelClicked(string.Empty, s, s);
351 else
352 return Task.CompletedTask;
353 }
354
358 [RelayCommand]
359 private async Task AddContact()
360 {
361 if (!this.CallerInContactsList)
362 {
363 ContactInfo Info = new()
364 {
365 BareJid = this.RemoteJid,
366 FriendlyName = (this.RemoteFriendlyNameAvailable ? this.RemoteFriendlyName : this.RemoteJid) ?? string.Empty
367 };
368
369 await Database.Insert(Info);
370
371 this.CallerInContactsList = true;
372 }
373 }
374
378 [RelayCommand]
379 private async Task RemoveContact()
380 {
381 if (this.CallerInContactsList)
382 {
383 ContactInfo Info = await ContactInfo.FindByBareJid(this.RemoteJid ?? string.Empty);
384 if (Info is not null)
385 await Database.Delete(Info);
386
387 this.CallerInContactsList = false;
388 }
389 }
390
394 [RelayCommand(CanExecute = nameof(CanExecuteAccept))]
395 private void Accept()
396 {
397 this.Respond(true);
398 }
399
401 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
402 {
403 base.OnPropertyChanged(e);
404
405 switch (e.PropertyName)
406 {
407 case nameof(this.IsConnected):
408 this.AcceptCommand.NotifyCanExecuteChanged();
409 break;
410 }
411 }
412
413 private bool CanExecuteAccept()
414 {
415 if (!this.IsConnected)
416 return false;
417
418 if (this.PermitMomentary || this.PermitIdentity || this.PermitStatus || this.PermitComputed || this.PermitPeak || this.PermitHistorical)
419 return true;
420
421 foreach (FieldReference Field in this.Fields)
422 {
423 if (Field.Permitted)
424 return true;
425 }
426
427 return false;
428 }
429
433 [RelayCommand(CanExecute = nameof(IsConnected))]
434 private void Reject()
435 {
436 this.Respond(false);
437 }
438
439 private void Respond(bool Accepts)
440 {
441 if (this.SelectedRuleRangeIndex >= 0)
442 {
443 RuleRangeModel Range = this.RuleRanges[this.SelectedRuleRangeIndex];
444 ThingReference Thing = new(this.NodeId, this.SourceId, this.PartitionId);
445 FieldType FieldTypes = (FieldType)0;
446
447 if (this.PermitMomentary)
448 FieldTypes |= FieldType.Momentary;
449
450 if (this.PermitIdentity)
451 FieldTypes |= FieldType.Identity;
452
453 if (this.PermitStatus)
454 FieldTypes |= FieldType.Status;
455
456 if (this.PermitComputed)
457 FieldTypes |= FieldType.Computed;
458
459 if (this.PermitPeak)
460 FieldTypes |= FieldType.Peak;
461
462 if (this.PermitHistorical)
463 FieldTypes |= FieldType.Historical;
464
465 if (Range.RuleRange is RuleRange RuleRange)
466 {
467 ReadoutRequestResolver Resolver = new(this.BareJid ?? string.Empty, this.RemoteFriendlyName ?? string.Empty, RuleRange);
468
469 switch (RuleRange)
470 {
471 case RuleRange.Caller:
472 default:
473 ServiceRef.XmppService.CanReadResponseCaller(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
474 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Thing, this.ResponseHandler, Resolver);
475 break;
476
477 case RuleRange.Domain:
478 ServiceRef.XmppService.CanReadResponseDomain(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
479 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Thing, this.ResponseHandler, Resolver);
480 break;
481
482 case RuleRange.All:
483 ServiceRef.XmppService.CanReadResponseAll(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
484 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Thing, this.ResponseHandler, Resolver);
485 break;
486
487 }
488 }
489 else if (Range.RuleRange is ProvisioningToken Token)
490 {
491 ReadoutRequestResolver Resolver = new(this.BareJid ?? string.Empty, this.RemoteFriendlyName ?? string.Empty, Token);
492
493 switch (Token.Type)
494 {
495 case TokenType.User:
496 ServiceRef.XmppService.CanReadResponseUser(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
497 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Token.Token, Thing, this.ResponseHandler, Resolver);
498 break;
499
500 case TokenType.Service:
501 ServiceRef.XmppService.CanReadResponseService(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
502 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Token.Token, Thing, this.ResponseHandler, Resolver);
503 break;
504
505 case TokenType.Device:
506 ServiceRef.XmppService.CanReadResponseDevice(this.ProvisioningService ?? ServiceRef.TagProfile.ProvisioningJid ?? string.Empty,
507 this.BareJid!, this.RemoteJid!, this.Key!, Accepts, FieldTypes, this.GetFields(), Token.Token, Thing, this.ResponseHandler, Resolver);
508 break;
509 }
510 }
511 }
512 }
513
514 private string[]? GetFields()
515 {
516 List<string> Result = [];
517 bool AllPermitted = true;
518
519 foreach (FieldReference Field in this.Fields)
520 {
521 if (Field.Permitted)
522 Result.Add(Field.Name);
523 else
524 AllPermitted = false;
525 }
526
527 if (AllPermitted)
528 return null;
529 else
530 return [.. Result];
531 }
532
533 private async Task ResponseHandler(object? Sender, IqResultEventArgs e)
534 {
535 if (e.Ok)
536 {
537 if (this.@event is not null)
539
541
542 MainThread.BeginInvokeOnMainThread(async () =>
543 {
544 await this.GoBack();
545 });
546 }
547 else
548 {
549 MainThread.BeginInvokeOnMainThread(async () => await ServiceRef.UiService.DisplayException(e.StanzaError ??
550 new Exception(ServiceRef.Localizer[nameof(AppResources.UnableToRespond)])));
551 }
552 }
553
557 [RelayCommand]
558 private async Task Ignore()
559 {
560 if (this.@event is not null)
562
563 await this.GoBack();
564 }
565
569 [RelayCommand]
570 private void AllFieldTypes()
571 {
572 this.PermitMomentary = true;
573 this.PermitIdentity = true;
574 this.PermitStatus = true;
575 this.PermitComputed = true;
576 this.PermitPeak = true;
577 this.PermitHistorical = true;
578 }
579
583 [RelayCommand]
584 private void NoFieldTypes()
585 {
586 this.PermitMomentary = false;
587 this.PermitIdentity = false;
588 this.PermitStatus = false;
589 this.PermitComputed = false;
590 this.PermitPeak = false;
591 this.PermitHistorical = false;
592 }
593
597 [RelayCommand]
598 private void AllFields()
599 {
600 foreach (FieldReference Field in this.Fields)
601 Field.Permitted = true;
602 }
603
607 [RelayCommand]
608 private void NoFields()
609 {
610 foreach (FieldReference Field in this.Fields)
611 Field.Permitted = false;
612 }
613
614 }
615}
A strongly-typed resource class, for looking up localized strings, etc.
static string ToService
Looks up a localized string similar to To service, regardless of source.
static string NotAvailable
Looks up a localized string similar to Not Available.
static string Device
Looks up a localized string similar to Device.
static string UnableToRespond
Looks up a localized string similar to Unable to respond to request..
static string ToDevice
Looks up a localized string similar to To device, regardless of source.
static string CallerOnly
Looks up a localized string similar to Caller Only.
static string ToUser
Looks up a localized string similar to To user, regardless of source.
static string Service
Looks up a localized string similar to Service.
static string EntireDomain
Looks up a localized string similar to Everyone from {0}.
static string Everyone
Looks up a localized string similar to Everyone.
static string User
Looks up a localized string similar to User.
Contains information about a contact.
Definition: ContactInfo.cs:22
Property?[] MetaData
Meta-data related to a thing.
Definition: ContactInfo.cs:211
static Task< ContactInfo > FindByBareJid(string BareJid)
Finds information about a contact, given its Bare JID.
Definition: ContactInfo.cs:221
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:43
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:130
static INotificationService NotificationService
Service for managing notifications for the user.
Definition: ServiceRef.cs:334
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:202
static IReportingStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:370
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:190
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 OnInitializeAsync()
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.
Event arguments for responses to IQ queries.
bool Ok
If the response is an OK result response (true), or an error response (false).
object State
State object passed to the original request.
XmppException StanzaError
Any stanza error returned.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
static string GetDomain(string JID)
Gets the domain part of a JID.
Definition: XmppClient.cs:6986
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
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