Skip to content

Commit dbd4691

Browse files
committed
Separate ScheduleListItem view model from State
1 parent 29eada6 commit dbd4691

File tree

11 files changed

+180
-69
lines changed

11 files changed

+180
-69
lines changed

src/Bible.Alarm/Common/Extensions/CloneExtensions.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using Newtonsoft.Json;
2+
using Newtonsoft.Json.Serialization;
3+
using System.Reflection;
4+
using System.Windows.Input;
25

36
namespace Bible.Alarm.Common.Extensions;
47

@@ -9,9 +12,27 @@ public static T DeepClone<T>(this T obj)
912
var settings = new JsonSerializerSettings
1013
{
1114
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
12-
PreserveReferencesHandling = PreserveReferencesHandling.None
15+
PreserveReferencesHandling = PreserveReferencesHandling.None,
16+
ContractResolver = new IgnoreNonSerializableContractResolver()
1317
};
1418
var json = JsonConvert.SerializeObject(obj, settings);
1519
return JsonConvert.DeserializeObject<T>(json, settings);
1620
}
21+
22+
private class IgnoreNonSerializableContractResolver : DefaultContractResolver
23+
{
24+
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
25+
{
26+
var property = base.CreateProperty(member, memberSerialization);
27+
28+
// Ignore ICommand properties and other interfaces that can't be instantiated
29+
if (property.PropertyType == null) return property;
30+
if (!typeof(ICommand).IsAssignableFrom(property.PropertyType) &&
31+
!property.PropertyType.IsInterface) return property;
32+
property.ShouldSerialize = _ => false;
33+
property.Ignored = true;
34+
35+
return property;
36+
}
37+
}
1738
}

src/Bible.Alarm/ViewModels/HomeViewModel.cs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public class HomeViewModel : ObservableObject, IDisposable, IRecipient<Initializ
3333

3434
private readonly List<IDisposable> _subscriptions = [];
3535
private readonly Dictionary<ScheduleListItem, PropertyChangedEventHandler> _isEnabledHandlers = [];
36+
// Map AlarmSchedule IDs to ScheduleListItem ViewModels for UI binding
37+
private readonly Dictionary<long, ScheduleListItem> _scheduleViewModels = [];
3638

3739

3840
public HomeViewModel(
@@ -64,7 +66,7 @@ public HomeViewModel(
6466

6567
ReduxContainer.Store.Dispatch(new ViewScheduleAction
6668
{
67-
SelectedScheduleListItem = x
69+
SelectedSchedule = x.Schedule
6870
});
6971

7072
using var scope = _scopeFactory.CreateScope();
@@ -75,11 +77,11 @@ public HomeViewModel(
7577

7678
//set schedules from initial state.
7779
//this should fire only once (look at the where condition).
78-
ObservableHashSet<ScheduleListItem> lastSchedules = null;
80+
ObservableHashSet<AlarmSchedule> lastSchedules = null;
7981
var subscription = ReduxContainer.Store.Subscribe(state =>
8082
{
8183
if (state.Schedules == null || state.Schedules == lastSchedules) return;
82-
Schedules = state.Schedules;
84+
UpdateScheduleViewModels(state.Schedules);
8385
lastSchedules = state.Schedules;
8486
ListenIsEnabledChanges();
8587
IsBusy = false;
@@ -132,10 +134,9 @@ private async Task HandleInitialized()
132134
}
133135
}
134136

135-
var initialSchedules = new ObservableHashSet<ScheduleListItem>();
137+
var initialSchedules = new ObservableHashSet<AlarmSchedule>();
136138
foreach (var schedule in alarmSchedules)
137-
initialSchedules.Add(new ScheduleListItem(schedule,
138-
scope.ServiceProvider.GetRequiredService<ILogger>(), _scopeFactory));
139+
initialSchedules.Add(schedule);
139140

140141
ReduxContainer.Store.Dispatch(new InitializeAction { ScheduleList = initialSchedules });
141142

@@ -192,6 +193,50 @@ public ObservableHashSet<ScheduleListItem> Schedules
192193
set => SetProperty(ref _schedules, value);
193194
}
194195

196+
private void UpdateScheduleViewModels(ObservableHashSet<AlarmSchedule> schedules)
197+
{
198+
using var scope = _scopeFactory.CreateScope();
199+
var logger = scope.ServiceProvider.GetRequiredService<ILogger>();
200+
201+
var newViewModels = new ObservableHashSet<ScheduleListItem>();
202+
var currentViewModelIds = new HashSet<long>();
203+
204+
// Create or update ViewModels for each schedule
205+
foreach (var schedule in schedules)
206+
{
207+
currentViewModelIds.Add(schedule.Id);
208+
209+
if (_scheduleViewModels.TryGetValue(schedule.Id, out var existingViewModel))
210+
{
211+
// Update existing ViewModel's Schedule property
212+
existingViewModel.Schedule = schedule;
213+
existingViewModel.IsEnabled = schedule.IsEnabled;
214+
newViewModels.Add(existingViewModel);
215+
}
216+
else
217+
{
218+
// Create new ViewModel
219+
var viewModel = new ScheduleListItem(schedule, logger, _scopeFactory);
220+
_scheduleViewModels[schedule.Id] = viewModel;
221+
newViewModels.Add(viewModel);
222+
}
223+
}
224+
225+
// Dispose and remove ViewModels for schedules that no longer exist
226+
var toRemove = _scheduleViewModels.Keys.Where(id => !currentViewModelIds.Contains(id)).ToList();
227+
foreach (var id in toRemove)
228+
{
229+
if (_scheduleViewModels.TryGetValue(id, out var viewModel))
230+
{
231+
UnsubscribeFromIsEnabledChanges(viewModel);
232+
viewModel.Dispose();
233+
_scheduleViewModels.Remove(id);
234+
}
235+
}
236+
237+
Schedules = newViewModels;
238+
}
239+
195240
private bool _isBusy = true;
196241

197242
public bool IsBusy
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using Bible.Alarm.Common.DataStructures;
22
using Bible.Alarm.Common.Redux;
3+
using Bible.Alarm.Models.Schedule;
34

45
namespace Bible.Alarm.ViewModels.Redux.Actions;
56

67
public class InitializeAction : IAction
78
{
8-
public ObservableHashSet<ScheduleListItem> ScheduleList = [];
9+
public ObservableHashSet<AlarmSchedule> ScheduleList = [];
910
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using Bible.Alarm.Common.Redux;
2+
using Bible.Alarm.Models.Schedule;
23

34
namespace Bible.Alarm.ViewModels.Redux.Actions.Schedule;
45

56
public class AddScheduleAction : IAction
67
{
7-
public ScheduleListItem ScheduleListItem { get; set; }
8+
public AlarmSchedule Schedule { get; set; }
89
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using Bible.Alarm.Common.Redux;
2+
using Bible.Alarm.Models.Schedule;
23

34
namespace Bible.Alarm.ViewModels.Redux.Actions.Schedule;
45

56
public class RemoveScheduleAction : IAction
67
{
7-
public ScheduleListItem ScheduleListItem { get; set; }
8+
public AlarmSchedule Schedule { get; set; }
89
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using Bible.Alarm.Common.Redux;
2+
using Bible.Alarm.Models.Schedule;
23

34
namespace Bible.Alarm.ViewModels.Redux.Actions.Schedule;
45

56
public class UpdateScheduleAction : IAction
67
{
7-
public ScheduleListItem ScheduleListItem { get; set; }
8+
public AlarmSchedule Schedule { get; set; }
89
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using Bible.Alarm.Common.Redux;
2+
using Bible.Alarm.Models.Schedule;
23

34
namespace Bible.Alarm.ViewModels.Redux.Actions.Schedule;
45

56
public class ViewScheduleAction : IAction
67
{
7-
public ScheduleListItem SelectedScheduleListItem { get; set; }
8+
public AlarmSchedule SelectedSchedule { get; set; }
89
}

0 commit comments

Comments
 (0)