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;
16
18{
22 public partial class MyThingsViewModel : BaseViewModel
23 {
24 private readonly Dictionary<CaseInsensitiveString, List<ContactInfoModel>> byBareJid = [];
25 private TaskCompletionSource<ContactInfoModel?>? result;
26
32 {
33 this.result = Args?.ThingToShare;
34 }
35
37 protected override async Task OnInitialize()
38 {
39 await base.OnInitialize();
40
41 ServiceRef.XmppService.OnPresence += this.Xmpp_OnPresence;
42 ServiceRef.NotificationService.OnNewNotification += this.NotificationService_OnNewNotification;
43 ServiceRef.NotificationService.OnNotificationsDeleted += this.NotificationService_OnNotificationsDeleted;
44 }
45
47 protected override async Task OnAppearing()
48 {
49 await base.OnAppearing();
50 this.SelectedThing = null;
51
52 if (this.result is not null && this.result.Task.IsCompleted)
53 await this.GoBack();
54 else
55 {
56 this.result = null;
57
58 await this.LoadThings();
59 }
60 }
61
62 private async Task LoadThings()
63 {
64 SortedDictionary<string, ContactInfo> SortedByName = [];
65 SortedDictionary<string, ContactInfo> SortedByAddress = [];
66 string Name;
67 string Suffix;
68 string Key;
69 int i;
70
71 foreach (ContactInfo Info in await Database.Find<ContactInfo>(new FilterFieldEqualTo("IsThing", true)))
72 {
73 Name = Info.FriendlyName;
74 if (SortedByName.ContainsKey(Name))
75 {
76 i = 1;
77
78 do
79 {
80 Suffix = " " + (++i).ToString(CultureInfo.InvariantCulture);
81 }
82 while (SortedByName.ContainsKey(Name + Suffix));
83
84 SortedByName[Name + Suffix] = Info;
85 }
86 else
87 SortedByName[Name] = Info;
88
89 Key = Info.BareJid + ", " + Info.SourceId + ", " + Info.Partition + ", " + Info.NodeId;
90 SortedByAddress[Key] = Info;
91 }
92
93 SearchResultThing[] MyDevices = await ServiceRef.XmppService.GetAllMyDevices();
94 foreach (SearchResultThing Thing in MyDevices)
95 {
96 Property[] MetaData = ViewClaimThing.ViewClaimThingViewModel.ToProperties(Thing.Tags);
97
98 Key = Thing.Jid + ", " + Thing.Node.SourceId + ", " + Thing.Node.Partition + ", " + Thing.Node.NodeId;
99 if (SortedByAddress.TryGetValue(Key, out ContactInfo? Info))
100 {
101 if (!Info.Owner.HasValue || !Info.Owner.Value || !AreSame(Info.MetaData, MetaData))
102 {
103 Info.Owner = true;
104 Info.MetaData = MetaData;
105 Info.FriendlyName = ViewClaimThing.ViewClaimThingViewModel.GetFriendlyName(MetaData) ?? string.Empty;
106
107 await Database.Update(Info);
108 }
109
110 continue;
111 }
112
113 Info = new ContactInfo()
114 {
115 BareJid = Thing.Jid,
116 LegalId = string.Empty,
117 LegalIdentity = null,
118 FriendlyName = ViewClaimThing.ViewClaimThingViewModel.GetFriendlyName(Thing.Tags) ?? string.Empty,
119 IsThing = true,
120 SourceId = Thing.Node.SourceId,
121 Partition = Thing.Node.Partition,
122 NodeId = Thing.Node.NodeId,
123 Owner = true,
124 MetaData = MetaData,
125 RegistryJid = ServiceRef.XmppService.RegistryServiceJid
126 };
127
128 foreach (MetaDataTag Tag in Thing.Tags)
129 {
130 if (Tag.Name.Equals("R", StringComparison.OrdinalIgnoreCase))
131 Info.RegistryJid = Tag.StringValue;
132 }
133
134 await Database.Insert(Info);
135
136 Name = Info.FriendlyName;
137 if (SortedByName.ContainsKey(Name))
138 {
139 i = 1;
140
141 do
142 {
143 Suffix = " " + (++i).ToString(CultureInfo.InvariantCulture);
144 }
145 while (SortedByName.ContainsKey(Name + Suffix));
146
147 SortedByName[Name + Suffix] = Info;
148 }
149 else
150 SortedByName[Name] = Info;
151
152 SortedByAddress[Key] = Info;
153 }
154
155 await Database.Provider.Flush();
156
157 this.byBareJid.Clear();
158
159 ObservableItemGroup<IUniqueItem> NewThings = new(nameof(this.Things), []);
160
161 foreach (ContactInfo Info in SortedByName.Values)
162 {
164
165 ContactInfoModel InfoModel = new(Info, Events ?? []);
166 NewThings.Add(InfoModel);
167
168 if (!this.byBareJid.TryGetValue(Info.BareJid, out List<ContactInfoModel>? Contacts))
169 {
170 Contacts = [];
171 this.byBareJid[Info.BareJid] = Contacts;
172 }
173
174 Contacts.Add(InfoModel);
175 }
176
177 this.ShowThingsMissing = SortedByName.Count == 0;
178
179 MainThread.BeginInvokeOnMainThread(() => ObservableItemGroup<IUniqueItem>.UpdateGroupsItems(this.Things, NewThings));
180 }
181
182
189 {
190 if (!string.IsNullOrEmpty(Thing.SourceId) ||
191 !string.IsNullOrEmpty(Thing.Partition) ||
192 !string.IsNullOrEmpty(Thing.NodeId) ||
194 {
195 ContactEvents = null;
196 }
197
199 ThingEvents = null;
200
201 return ThingEvents is null ? ContactEvents : (ContactEvents?.Join(ThingEvents) ?? ThingEvents);
202 }
203
205 protected override async Task OnDispose()
206 {
207 ServiceRef.XmppService.OnPresence -= this.Xmpp_OnPresence;
208 ServiceRef.NotificationService.OnNewNotification -= this.NotificationService_OnNewNotification;
209 ServiceRef.NotificationService.OnNotificationsDeleted -= this.NotificationService_OnNotificationsDeleted;
210
211 this.ShowThingsMissing = false;
212 this.Things.Clear();
213
214 this.result?.TrySetResult(null);
215
216 await base.OnDispose();
217 }
218
225 public static bool AreSame(Property[]? MetaData1, Property[]? MetaData2)
226 {
227 if ((MetaData1 is null) ^ (MetaData2 is null))
228 return false;
229
230 if (MetaData1 is null || MetaData2 is null) // Second only necessary to avoid compiler warnings.
231 return true;
232
233 int i, c = MetaData1.Length;
234 if (MetaData2.Length != c)
235 return false;
236
237 for (i = 0; i < c; i++)
238 {
239 if ((MetaData1[i].Name != MetaData2[i].Name) || (MetaData1[i].Value != MetaData2[i].Value))
240 return false;
241 }
242
243 return true;
244 }
245
249 [ObservableProperty]
250 private bool showThingsMissing;
251
255 public ObservableItemGroup<IUniqueItem> Things { get; } = new(nameof(Things), []);
256
260 [ObservableProperty]
261 private ContactInfoModel? selectedThing;
262
263 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
264 {
265 base.OnPropertyChanged(e);
266
267 switch (e.PropertyName)
268 {
269 case nameof(this.SelectedThing):
270 if (this.SelectedThing is not null)
271 this.OnSelected(this.SelectedThing);
272 break;
273 }
274 }
275
276 private void OnSelected(ContactInfoModel Thing)
277 {
278 if (Thing.Contact is null)
279 return;
280
281 MainThread.BeginInvokeOnMainThread(async () =>
282 {
283 if (this.result is null)
284 {
285 ViewThingNavigationArgs Args = new(Thing.Contact, Thing.Events);
286 await ServiceRef.UiService.GoToAsync(nameof(ViewThingPage), Args, BackMethod.Pop2);
287 }
288 else
289 {
290 this.result.TrySetResult(Thing);
291 await this.GoBack();
292 }
293 });
294 }
295
296 private Task Xmpp_OnPresence(object? Sender, PresenceEventArgs e)
297 {
298 if (this.byBareJid.TryGetValue(e.FromBareJID, out List<ContactInfoModel>? Contacts))
299 {
300 foreach (ContactInfoModel Contact in Contacts)
301 Contact.PresenceUpdated();
302 }
303
304 return Task.CompletedTask;
305 }
306
307 private void NotificationService_OnNotificationsDeleted(object? Sender, NotificationEventsArgs e)
308 {
309 MainThread.BeginInvokeOnMainThread(() =>
310 {
311 foreach (NotificationEvent Event in e.Events)
312 {
313 switch (Event.Type)
314 {
315 case NotificationEventType.Contacts:
316 foreach (IUniqueItem Item in this.Things)
317 {
318 if (Item is ContactInfoModel Thing)
319 {
320
321 if (!string.IsNullOrEmpty(Thing.NodeId) ||
322 !string.IsNullOrEmpty(Thing.SourceId) ||
323 !string.IsNullOrEmpty(Thing.Partition))
324 {
325 continue;
326 }
327
328 if (Event.Category != Thing.BareJid)
329 continue;
330
331 Thing.RemoveEvent(Event);
332 }
333 }
334 break;
335
336 case NotificationEventType.Things:
337 foreach (IUniqueItem Item in this.Things)
338 {
339 if (Item is ContactInfoModel Thing)
340 {
341
342 if (Event.Category != ContactInfo.GetThingNotificationCategoryKey(Thing.BareJid, Thing.NodeId, Thing.SourceId, Thing.Partition))
343 continue;
344
345 Thing.RemoveEvent(Event);
346 }
347 }
348 break;
349
350 default:
351 return;
352 }
353 }
354 });
355 }
356
357 private void NotificationService_OnNewNotification(object? Sender, NotificationEventArgs e)
358 {
359 MainThread.BeginInvokeOnMainThread(() =>
360 {
361 try
362 {
363 switch (e.Event.Type)
364 {
365 case NotificationEventType.Contacts:
366 foreach (IUniqueItem Item in this.Things)
367 {
368 if (Item is ContactInfoModel Thing)
369 {
370 if (!string.IsNullOrEmpty(Thing.NodeId) ||
371 !string.IsNullOrEmpty(Thing.SourceId) ||
372 !string.IsNullOrEmpty(Thing.Partition))
373 {
374 continue;
375 }
376
377 if (e.Event.Category != Thing.BareJid)
378 continue;
379
380 Thing.AddEvent(e.Event);
381 }
382 }
383 break;
384
385 case NotificationEventType.Things:
386 foreach (IUniqueItem Item in this.Things)
387 {
388 if (Item is ContactInfoModel Thing)
389 {
390 if (e.Event.Category != ContactInfo.GetThingNotificationCategoryKey(Thing.BareJid, Thing.NodeId, Thing.SourceId, Thing.Partition))
391 continue;
392
393 Thing.AddEvent(e.Event);
394 }
395 }
396 break;
397
398 default:
399 return;
400 }
401 }
402 catch (Exception ex)
403 {
404 ServiceRef.LogService.LogException(ex);
405 }
406 });
407 }
408
409 }
410}
Contains information about a contact.
Definition: ContactInfo.cs:21
string ThingNotificationCategoryKey
Category key used for thing notifications.
Definition: ContactInfo.cs:695
static string GetThingNotificationCategoryKey(string BareJid, string? NodeId, string? SourceId, string? Partition)
Category key used for thing notifications.
Definition: ContactInfo.cs:710
CaseInsensitiveString BareJid
Bare JID of contact.
Definition: ContactInfo.cs:62
Base class that references services in the app.
Definition: ServiceRef.cs:31
static ILogService LogService
Log service.
Definition: ServiceRef.cs:91
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 IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:67
A base class for all view models, inheriting from the BindableObject. NOTE: using this class requir...
virtual async Task GoBack()
Method called when user wants to navigate to the previous screen.
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.
TaskCompletionSource< ContactInfoModel?>? ThingToShare
Task completion source where the selected thing is placed.
The view model to bind to when displaying the list of things.
static ? NotificationEvent[] GetNotificationEvents(ContactInfo Thing)
Gets available notification events related to a thing.
override async Task OnAppearing()
Method called when view is appearing on the screen.
override async Task OnDispose()
Method called when the view is disposed, and will not be used more. Use this method to unregister eve...
override async Task OnInitialize()
Method called when view is initialized for the first time. Use this method to implement registration ...
static bool AreSame(Property[]? MetaData1, Property[]? MetaData2)
Checks to sets of meta-data about a thing, to see if they match.
ObservableItemGroup< IUniqueItem > Things
Holds the list of contacts to display.
MyThingsViewModel(MyThingsNavigationArgs? Args)
Creates an instance of the MyThingsViewModel class.
Holds navigation parameters specific to viewing things.
A page that displays information about a thing and allows the user to interact with it.
Abstract base class for all meta-data tags.
Definition: MetaDataTag.cs:9
abstract string StringValue
String-representation of meta-data tag value.
Definition: MetaDataTag.cs:30
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:19
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:57
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
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, BackMethod BackMethod=BackMethod.Inherited, string? UniqueId=null)
Navigates the AppShell to the specified route, with page arguments to match.
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.