Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MyThingsViewModel.cs
1using System.ComponentModel;
2using System.Globalization;
3using CommunityToolkit.Mvvm.ComponentModel;
17
19{
24 public partial class MyThingsViewModel(MyThingsNavigationArgs? Args)
26 {
27 private readonly Dictionary<CaseInsensitiveString, List<ContactInfoModel>> byBareJid = [];
28 private TaskCompletionSource<ContactInfoModel?>? result = Args?.ThingToShare;
29
31 public override async Task OnInitializeAsync()
32 {
33 await base.OnInitializeAsync();
34
35 ServiceRef.XmppService.OnPresence += this.Xmpp_OnPresence;
36 ServiceRef.NotificationService.OnNewNotification += this.NotificationService_OnNewNotification;
37 ServiceRef.NotificationService.OnNotificationsDeleted += this.NotificationService_OnNotificationsDeleted;
38 }
39
41 public override async Task OnAppearingAsync()
42 {
43 await base.OnAppearingAsync();
44 this.SelectedThing = null;
45
46 if (this.result is not null && this.result.Task.IsCompleted)
47 await this.GoBack();
48 else
49 {
50 this.result = null;
51
52 await this.LoadThings();
53 }
54 }
55
56 private async Task LoadThings()
57 {
58 SortedDictionary<string, ContactInfo> SortedByName = [];
59 SortedDictionary<string, ContactInfo> SortedByAddress = [];
60 string Name;
61 string Suffix;
62 string Key;
63 int i;
64
65 foreach (ContactInfo Info in await Database.Find<ContactInfo>(new FilterFieldEqualTo("IsThing", true)))
66 {
67 Name = Info.FriendlyName;
68 if (SortedByName.ContainsKey(Name))
69 {
70 i = 1;
71
72 do
73 {
74 Suffix = " " + (++i).ToString(CultureInfo.InvariantCulture);
75 }
76 while (SortedByName.ContainsKey(Name + Suffix));
77
78 SortedByName[Name + Suffix] = Info;
79 }
80 else
81 SortedByName[Name] = Info;
82
83 Key = Info.BareJid + ", " + Info.SourceId + ", " + Info.Partition + ", " + Info.NodeId;
84 SortedByAddress[Key] = Info;
85 }
86
87 SearchResultThing[] MyDevices = await ServiceRef.XmppService.GetAllMyDevices();
88 foreach (SearchResultThing Thing in MyDevices)
89 {
90 Property[] MetaData = ViewClaimThing.ViewClaimThingViewModel.ToProperties(Thing.Tags);
91
92 Key = Thing.Jid + ", " + Thing.Node.SourceId + ", " + Thing.Node.Partition + ", " + Thing.Node.NodeId;
93 if (SortedByAddress.TryGetValue(Key, out ContactInfo? Info))
94 {
95 if (!Info.Owner.HasValue || !Info.Owner.Value || !AreSame(Info.MetaData, MetaData))
96 {
97 Info.Owner = true;
98 Info.MetaData = MetaData;
99 Info.FriendlyName = ViewClaimThing.ViewClaimThingViewModel.GetFriendlyName(MetaData) ?? string.Empty;
100
101 await Database.Update(Info);
102 }
103
104 continue;
105 }
106
107 Info = new ContactInfo()
108 {
109 BareJid = Thing.Jid,
110 LegalId = string.Empty,
111 LegalIdentity = null,
112 FriendlyName = ViewClaimThing.ViewClaimThingViewModel.GetFriendlyName(Thing.Tags) ?? string.Empty,
113 IsThing = true,
114 SourceId = Thing.Node.SourceId,
115 Partition = Thing.Node.Partition,
116 NodeId = Thing.Node.NodeId,
117 Owner = true,
118 MetaData = MetaData,
119 RegistryJid = ServiceRef.XmppService.RegistryServiceJid
120 };
121
122 foreach (MetaDataTag Tag in Thing.Tags)
123 {
124 if (Tag.Name.Equals("R", StringComparison.OrdinalIgnoreCase))
125 Info.RegistryJid = Tag.StringValue;
126 }
127
128 await Database.Insert(Info);
129
130 Name = Info.FriendlyName;
131 if (SortedByName.ContainsKey(Name))
132 {
133 i = 1;
134
135 do
136 {
137 Suffix = " " + (++i).ToString(CultureInfo.InvariantCulture);
138 }
139 while (SortedByName.ContainsKey(Name + Suffix));
140
141 SortedByName[Name + Suffix] = Info;
142 }
143 else
144 SortedByName[Name] = Info;
145
146 SortedByAddress[Key] = Info;
147 }
148
149 await Database.Provider.Flush();
150
151 this.byBareJid.Clear();
152
153 ObservableItemGroup<IUniqueItem> NewThings = new(nameof(this.Things), []);
154
155 foreach (ContactInfo Info in SortedByName.Values)
156 {
157 NotificationEvent[]? Events = GetNotificationEvents(Info);
158
159 ContactInfoModel InfoModel = new(Info, Events ?? []);
160 NewThings.Add(InfoModel);
161
162 if (!this.byBareJid.TryGetValue(Info.BareJid, out List<ContactInfoModel>? Contacts))
163 {
164 Contacts = [];
165 this.byBareJid[Info.BareJid] = Contacts;
166 }
167
168 Contacts.Add(InfoModel);
169 }
170
171 this.ShowThingsMissing = SortedByName.Count == 0;
172
173 MainThread.BeginInvokeOnMainThread(() => ObservableItemGroup<IUniqueItem>.UpdateGroupsItems(this.Things, NewThings));
174 }
175
176
182 public static NotificationEvent[]? GetNotificationEvents(ContactInfo Thing)
183 {
184 if (!string.IsNullOrEmpty(Thing.SourceId) ||
185 !string.IsNullOrEmpty(Thing.Partition) ||
186 !string.IsNullOrEmpty(Thing.NodeId) ||
188 {
189 ContactEvents = null;
190 }
191
193 ThingEvents = null;
194
195 return ThingEvents is null ? ContactEvents : (ContactEvents?.Join(ThingEvents) ?? ThingEvents);
196 }
197
199 public override async Task OnDisposeAsync()
200 {
201 ServiceRef.XmppService.OnPresence -= this.Xmpp_OnPresence;
202 ServiceRef.NotificationService.OnNewNotification -= this.NotificationService_OnNewNotification;
203 ServiceRef.NotificationService.OnNotificationsDeleted -= this.NotificationService_OnNotificationsDeleted;
204
205 this.ShowThingsMissing = false;
206 this.Things.Clear();
207
208 this.result?.TrySetResult(null);
209
210 await base.OnDisposeAsync();
211 }
212
219 public static bool AreSame(Property[]? MetaData1, Property[]? MetaData2)
220 {
221 if ((MetaData1 is null) ^ (MetaData2 is null))
222 return false;
223
224 if (MetaData1 is null || MetaData2 is null) // Second only necessary to avoid compiler warnings.
225 return true;
226
227 int i, c = MetaData1.Length;
228 if (MetaData2.Length != c)
229 return false;
230
231 for (i = 0; i < c; i++)
232 {
233 if ((MetaData1[i].Name != MetaData2[i].Name) || (MetaData1[i].Value != MetaData2[i].Value))
234 return false;
235 }
236
237 return true;
238 }
239
243 [ObservableProperty]
244 private bool showThingsMissing;
245
249 public ObservableItemGroup<IUniqueItem> Things { get; } = new(nameof(Things), []);
250
254 [ObservableProperty]
255 private ContactInfoModel? selectedThing;
256
257 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
258 {
259 base.OnPropertyChanged(e);
260
261 switch (e.PropertyName)
262 {
263 case nameof(this.SelectedThing):
264 if (this.SelectedThing is not null)
265 this.OnSelected(this.SelectedThing);
266 break;
267 }
268 }
269
270 private void OnSelected(ContactInfoModel Thing)
271 {
272 if (Thing.Contact is null)
273 return;
274
275 MainThread.BeginInvokeOnMainThread(async () =>
276 {
277 if (this.result is null)
278 {
279 ViewThingNavigationArgs Args = new(Thing.Contact, Thing.Events);
281 }
282 else
283 {
284 this.result.TrySetResult(Thing);
285 await this.GoBack();
286 }
287 });
288 }
289
290 private Task Xmpp_OnPresence(object? Sender, PresenceEventArgs e)
291 {
292 if (this.byBareJid.TryGetValue(e.FromBareJID, out List<ContactInfoModel>? Contacts))
293 {
294 foreach (ContactInfoModel Contact in Contacts)
295 Contact.PresenceUpdated();
296 }
297
298 return Task.CompletedTask;
299 }
300
301 private Task NotificationService_OnNotificationsDeleted(object? Sender, NotificationEventsArgs e)
302 {
303 MainThread.BeginInvokeOnMainThread(() =>
304 {
305 foreach (NotificationEvent Event in e.Events)
306 {
307 switch (Event.Type)
308 {
309 case NotificationEventType.Contacts:
310 foreach (IUniqueItem Item in this.Things)
311 {
312 if (Item is ContactInfoModel Thing)
313 {
314
315 if (!string.IsNullOrEmpty(Thing.NodeId) ||
316 !string.IsNullOrEmpty(Thing.SourceId) ||
317 !string.IsNullOrEmpty(Thing.Partition))
318 {
319 continue;
320 }
321
322 if (Event.Category != Thing.BareJid)
323 continue;
324
325 Thing.RemoveEvent(Event);
326 }
327 }
328 break;
329
330 case NotificationEventType.Things:
331 foreach (IUniqueItem Item in this.Things)
332 {
333 if (Item is ContactInfoModel Thing)
334 {
335
336 if (Event.Category != ContactInfo.GetThingNotificationCategoryKey(Thing.BareJid, Thing.NodeId, Thing.SourceId, Thing.Partition))
337 continue;
338
339 Thing.RemoveEvent(Event);
340 }
341 }
342 break;
343
344 default:
345 return;
346 }
347 }
348 });
349
350 return Task.CompletedTask;
351 }
352
353 private Task NotificationService_OnNewNotification(object? Sender, NotificationEventArgs e)
354 {
355 MainThread.BeginInvokeOnMainThread(() =>
356 {
357 try
358 {
359 switch (e.Event.Type)
360 {
361 case NotificationEventType.Contacts:
362 foreach (IUniqueItem Item in this.Things)
363 {
364 if (Item is ContactInfoModel Thing)
365 {
366 if (!string.IsNullOrEmpty(Thing.NodeId) ||
367 !string.IsNullOrEmpty(Thing.SourceId) ||
368 !string.IsNullOrEmpty(Thing.Partition))
369 {
370 continue;
371 }
372
373 if (e.Event.Category != Thing.BareJid)
374 continue;
375
376 Thing.AddEvent(e.Event);
377 }
378 }
379 break;
380
381 case NotificationEventType.Things:
382 foreach (IUniqueItem Item in this.Things)
383 {
384 if (Item is ContactInfoModel Thing)
385 {
386 if (e.Event.Category != ContactInfo.GetThingNotificationCategoryKey(Thing.BareJid, Thing.NodeId, Thing.SourceId, Thing.Partition))
387 continue;
388
389 Thing.AddEvent(e.Event);
390 }
391 }
392 break;
393
394 default:
395 return;
396 }
397 }
398 catch (Exception ex)
399 {
400 ServiceRef.LogService.LogException(ex);
401 }
402 });
403
404 return Task.CompletedTask;
405 }
406
407 }
408}
Contains information about a contact.
Definition: ContactInfo.cs:22
string ThingNotificationCategoryKey
Category key used for thing notifications.
Definition: ContactInfo.cs:696
static string GetThingNotificationCategoryKey(string BareJid, string? NodeId, string? SourceId, string? Partition)
Category key used for thing notifications.
Definition: ContactInfo.cs:711
CaseInsensitiveString BareJid
Bare JID of contact.
Definition: ContactInfo.cs:63
Base class that references services in the app.
Definition: ServiceRef.cs:43
static ILogService LogService
Log service.
Definition: ServiceRef.cs:214
static INavigationService NavigationService
The navigation service for navigating between pages.
Definition: ServiceRef.cs:178
static INotificationService NotificationService
Service for managing notifications for the user.
Definition: ServiceRef.cs:334
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:190
A base class for all view models, inheriting from the BindableObject. NOTE: using this class requir...
virtual async Task OnInitializeAsync()
Method called when view is initialized for the first time. Use this method to implement registration ...
Contact Information model, including related notification information.
void PresenceUpdated()
Method called when presence for contact has been updated.
ContactInfo? Contact
Contact Information object in database.
void OnPropertyChanged(string PropertyName)
Called when a property has changed.
Holds navigation parameters specific to viewing things.
A page that displays information about a thing and allows the user to interact with it.
Event arguments for presence events.
string FromBareJID
Bare JID of resource sending the presence.
Abstract base class for all meta-data tags.
Definition: MetaDataTag.cs:10
abstract string StringValue
String-representation of meta-data tag value.
Definition: MetaDataTag.cs:31
Contains information about a thing in a search result.
ThingReference Node
Node reference. Can be equal to ThingReference.Empty if not behind a concentrator.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:59
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
This filter selects objects that have a named field equal to a given value.
bool TryGetNotificationEvents(NotificationEventType Type, CaseInsensitiveString Category, [NotNullWhen(true)] out NotificationEvent[]? Events)
Tries to get available notification events.
Task GoToAsync(string Route)
Navigates to the specified route and pushes the page onto the navigation stack.
Task Flush()
Persists any pending changes.
abstract class NotificationEvent()
Abstract base class of notification events.
class NotificationEventsArgs(NotificationEvent[] Events)
Event argument for notification events.
class NotificationEventArgs(NotificationEvent Event)
Event argument for notification events.
NotificationEventType
Button on which event is to be displayed.
BackMethod
Navigation Back Method
Definition: BackMethod.cs:7
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.