Skip to content

Commit ed5ce01

Browse files
committed
Fix navigation
1 parent 6ba5574 commit ed5ce01

File tree

10 files changed

+266
-182
lines changed

10 files changed

+266
-182
lines changed

src/Bible.Alarm/Common/Interfaces/UI/INavigationService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@ namespace Bible.Alarm.Common.Interfaces.UI;
22

33
public interface INavigationService
44
{
5+
Task NavigateToScheduleAsync();
56
Task NavigateToMusicSelectionAsync();
7+
Task NavigateToSongBookSelectionAsync();
8+
Task NavigateToTrackSelectionAsync();
69
Task NavigateToBibleSelectionAsync();
10+
Task NavigateToBookSelectionAsync();
11+
Task NavigateToChapterSelectionAsync();
712
Task OpenNumberOfChaptersModalAsync(object bindingContext);
13+
Task OpenLanguageModalAsync(object bindingContext);
814
Task CloseModalAsync();
915
}
1016

src/Bible.Alarm/Services/UI/NavigationService.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using Bible.Alarm.Common.Interfaces.UI;
2+
using Bible.Alarm.ViewModels;
23
using Bible.Alarm.ViewModels.Bible;
34
using Bible.Alarm.ViewModels.Music;
45
using Bible.Alarm.Views.Bible;
56
using Bible.Alarm.Views.Music;
67
using Bible.Alarm.Views.General;
78
using Bible.Alarm.Views.Schedule;
9+
using Bible.Alarm.Views.Shared;
810
using Microsoft.Extensions.DependencyInjection;
911

1012
namespace Bible.Alarm.Services.UI;
@@ -17,6 +19,15 @@ public class NavigationService(
1719
private readonly INavigation _navigation = navigation;
1820
private readonly IServiceScopeFactory _scopeFactory = scopeFactory;
1921

22+
public async Task NavigateToScheduleAsync()
23+
{
24+
using var scope = _scopeFactory.CreateScope();
25+
var viewModel = scope.ServiceProvider.GetRequiredService<ScheduleViewModel>();
26+
var page = scope.ServiceProvider.GetRequiredService<Schedule>();
27+
page.BindingContext = viewModel;
28+
await _navigation.PushAsync(page);
29+
}
30+
2031
public async Task NavigateToMusicSelectionAsync()
2132
{
2233
using var scope = _scopeFactory.CreateScope();
@@ -26,6 +37,24 @@ public async Task NavigateToMusicSelectionAsync()
2637
await _navigation.PushAsync(page);
2738
}
2839

40+
public async Task NavigateToSongBookSelectionAsync()
41+
{
42+
using var scope = _scopeFactory.CreateScope();
43+
var viewModel = scope.ServiceProvider.GetRequiredService<SongBookSelectionViewModel>();
44+
var page = scope.ServiceProvider.GetRequiredService<SongBookSelection>();
45+
page.BindingContext = viewModel;
46+
await _navigation.PushAsync(page);
47+
}
48+
49+
public async Task NavigateToTrackSelectionAsync()
50+
{
51+
using var scope = _scopeFactory.CreateScope();
52+
var viewModel = scope.ServiceProvider.GetRequiredService<TrackSelectionViewModel>();
53+
var page = scope.ServiceProvider.GetRequiredService<TrackSelection>();
54+
page.BindingContext = viewModel;
55+
await _navigation.PushAsync(page);
56+
}
57+
2958
public async Task NavigateToBibleSelectionAsync()
3059
{
3160
using var scope = _scopeFactory.CreateScope();
@@ -35,6 +64,24 @@ public async Task NavigateToBibleSelectionAsync()
3564
await _navigation.PushAsync(page);
3665
}
3766

67+
public async Task NavigateToBookSelectionAsync()
68+
{
69+
using var scope = _scopeFactory.CreateScope();
70+
var viewModel = scope.ServiceProvider.GetRequiredService<BookSelectionViewModel>();
71+
var page = scope.ServiceProvider.GetRequiredService<BookSelection>();
72+
page.BindingContext = viewModel;
73+
await _navigation.PushAsync(page);
74+
}
75+
76+
public async Task NavigateToChapterSelectionAsync()
77+
{
78+
using var scope = _scopeFactory.CreateScope();
79+
var viewModel = scope.ServiceProvider.GetRequiredService<ChapterSelectionViewModel>();
80+
var page = scope.ServiceProvider.GetRequiredService<ChapterSelection>();
81+
page.BindingContext = viewModel;
82+
await _navigation.PushAsync(page);
83+
}
84+
3885
public async Task OpenNumberOfChaptersModalAsync(object bindingContext)
3986
{
4087
using var scope = _scopeFactory.CreateScope();
@@ -43,6 +90,14 @@ public async Task OpenNumberOfChaptersModalAsync(object bindingContext)
4390
await _navigation.PushModalAsync(modal);
4491
}
4592

93+
public async Task OpenLanguageModalAsync(object bindingContext)
94+
{
95+
using var scope = _scopeFactory.CreateScope();
96+
var modal = scope.ServiceProvider.GetRequiredService<LanguageModal>();
97+
modal.BindingContext = bindingContext;
98+
await _navigation.PushModalAsync(modal);
99+
}
100+
46101
public async Task CloseModalAsync()
47102
{
48103
if (_navigation.ModalStack.Count > 0)

src/Bible.Alarm/ViewModels/Bible/BibleSelectionViewModel.cs

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class BibleSelectionViewModel : ObservableObject, IListViewModel
2020
{
2121
private readonly MediaService _mediaService;
2222
private readonly IServiceScopeFactory _scopeFactory;
23+
private readonly INavigationService _navigationService;
2324
private readonly IDispatcher _dispatcher;
2425
private readonly IState<ApplicationState> _state;
2526

@@ -32,28 +33,15 @@ public class BibleSelectionViewModel : ObservableObject, IListViewModel
3233
public ICommand CloseModalCommand { get; set; }
3334
public ICommand SelectLanguageCommand { get; set; }
3435

35-
public BibleSelectionViewModel(MediaService mediaService, IServiceScopeFactory scopeFactory)
36+
public BibleSelectionViewModel(MediaService mediaService, IServiceScopeFactory scopeFactory, INavigationService navigationService)
3637
{
3738
_mediaService = mediaService;
3839
_scopeFactory = scopeFactory;
40+
_navigationService = navigationService;
3941
_state = MauiAppHolder.Services.GetRequiredService<IState<ApplicationState>>();
4042
_dispatcher = MauiAppHolder.Services.GetRequiredService<IDispatcher>();
4143

42-
EventHandler onBibleReadingInitialized = null;
43-
onBibleReadingInitialized = (_, _) =>
44-
{
45-
var stateValue = _state.Value;
46-
if (stateValue.CurrentBibleReadingSchedule == null || stateValue.TentativeBibleReadingSchedule == null) return;
47-
_current = stateValue.CurrentBibleReadingSchedule;
48-
_tentative = stateValue.TentativeBibleReadingSchedule;
49-
Task.Run(async () =>
50-
{
51-
await Initialize(_tentative.LanguageCode);
52-
await MainThread.InvokeOnMainThreadAsync(() => IsBusy = false);
53-
});
54-
_state.StateChanged -= onBibleReadingInitialized;
55-
};
56-
_state.StateChanged += onBibleReadingInitialized;
44+
_state.StateChanged += OnBibleReadingInitialized;
5745

5846
BibleReadingSchedule lastCurrent = null;
5947
BibleReadingSchedule lastTentative = null;
@@ -63,29 +51,19 @@ public BibleSelectionViewModel(MediaService mediaService, IServiceScopeFactory s
6351
BookSelectionCommand = new AsyncRelayCommand<PublicationListViewItemModel>(async x =>
6452
{
6553
IsBusy = true;
54+
await _navigationService.NavigateToBookSelectionAsync();
6655
_dispatcher.Dispatch(new BookSelectionAction(new BibleReadingSchedule
6756
{
6857
PublicationCode = x.Code,
6958
LanguageCode = CurrentLanguage.Code
7059
}));
71-
using var scope = _scopeFactory.CreateScope();
72-
var navigation = scope.ServiceProvider.GetRequiredService<INavigation>();
73-
var viewModel = scope.ServiceProvider.GetRequiredService<BookSelectionViewModel>();
74-
var page = scope.ServiceProvider.GetRequiredService<BookSelection>();
75-
page.BindingContext = viewModel;
76-
await navigation.PushAsync(page);
77-
7860
IsBusy = false;
7961
});
8062

8163
OpenModalCommand = new AsyncRelayCommand(async () =>
8264
{
8365
IsBusy = true;
84-
using var scope = _scopeFactory.CreateScope();
85-
var navigation = scope.ServiceProvider.GetRequiredService<INavigation>();
86-
var modal = scope.ServiceProvider.GetRequiredService<LanguageModal>();
87-
modal.BindingContext = this;
88-
await navigation.PushModalAsync(modal);
66+
await _navigationService.OpenLanguageModalAsync(this);
8967
IsBusy = false;
9068
});
9169

@@ -101,13 +79,7 @@ public BibleSelectionViewModel(MediaService mediaService, IServiceScopeFactory s
10179
CloseModalCommand = new AsyncRelayCommand(async () =>
10280
{
10381
IsBusy = true;
104-
using var scope = _scopeFactory.CreateScope();
105-
var navigation = scope.ServiceProvider.GetRequiredService<INavigation>();
106-
if (navigation.ModalStack.Count > 0)
107-
{
108-
var modal = await navigation.PopModalAsync();
109-
if (modal.BindingContext is IDisposable disposable) disposable.Dispose();
110-
}
82+
await _navigationService.CloseModalAsync();
11183
IsBusy = false;
11284
});
11385

@@ -119,17 +91,26 @@ public BibleSelectionViewModel(MediaService mediaService, IServiceScopeFactory s
11991
CurrentLanguage = x;
12092
CurrentLanguage.IsSelected = true;
12193

122-
using var scope = _scopeFactory.CreateScope();
123-
var navigation = scope.ServiceProvider.GetRequiredService<INavigation>();
124-
if (navigation.ModalStack.Count > 0)
125-
{
126-
var modal = await navigation.PopModalAsync();
127-
if (modal.BindingContext is IDisposable disposable) disposable.Dispose();
128-
}
94+
await _navigationService.CloseModalAsync();
12995
await PopulateTranslations(x.Code);
13096

13197
IsBusy = false;
13298
});
99+
return;
100+
101+
void OnBibleReadingInitialized(object o, EventArgs eventArgs)
102+
{
103+
var stateValue = _state.Value;
104+
if (stateValue.CurrentBibleReadingSchedule == null || stateValue.TentativeBibleReadingSchedule == null) return;
105+
_current = stateValue.CurrentBibleReadingSchedule;
106+
_tentative = stateValue.TentativeBibleReadingSchedule;
107+
Task.Run(async () =>
108+
{
109+
await Initialize(_tentative.LanguageCode);
110+
await MainThread.InvokeOnMainThreadAsync(() => IsBusy = false);
111+
});
112+
_state.StateChanged -= OnBibleReadingInitialized;
113+
}
133114

134115
void OnBibleReadingChanged(object sender, EventArgs e)
135116
{

src/Bible.Alarm/ViewModels/Bible/BookSelectionViewModel.cs

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.ObjectModel;
22
using System.Windows.Input;
33
using Bible.Alarm.Common;
4+
using Bible.Alarm.Common.Interfaces.UI;
45
using Bible.Alarm.Models.Schedule;
56
using Bible.Alarm.Services.Media;
67
using Bible.Alarm.Shared.Models.Media.Bible;
@@ -21,6 +22,7 @@ public class BookSelectionViewModel : ObservableObject, IDisposable
2122

2223
private readonly MediaService _mediaService;
2324
private readonly IServiceScopeFactory _scopeFactory;
25+
private readonly INavigationService _navigationService;
2426
private readonly IDispatcher _dispatcher;
2527
private readonly IState<ApplicationState> _state;
2628
private EventHandler _onBibleReadingChanged;
@@ -29,10 +31,11 @@ public class BookSelectionViewModel : ObservableObject, IDisposable
2931
public ICommand BackCommand { get; set; }
3032
public ICommand ChapterSelectionCommand { get; set; }
3133

32-
public BookSelectionViewModel(MediaService mediaService, IServiceScopeFactory scopeFactory)
34+
public BookSelectionViewModel(MediaService mediaService, IServiceScopeFactory scopeFactory, INavigationService navigationService)
3335
{
3436
_mediaService = mediaService;
3537
_scopeFactory = scopeFactory;
38+
_navigationService = navigationService;
3639
_state = MauiAppHolder.Services.GetRequiredService<IState<ApplicationState>>();
3740
_dispatcher = MauiAppHolder.Services.GetRequiredService<IDispatcher>();
3841

@@ -48,45 +51,24 @@ public BookSelectionViewModel(MediaService mediaService, IServiceScopeFactory sc
4851
ChapterSelectionCommand = new AsyncRelayCommand<BibleBookListViewItemModel>(async x =>
4952
{
5053
IsBusy = true;
54+
await _navigationService.NavigateToChapterSelectionAsync();
5155
_dispatcher.Dispatch(new ChapterSelectionAction(new BibleReadingSchedule
5256
{
5357
LanguageCode = _tentative.LanguageCode,
5458
PublicationCode = _tentative.PublicationCode,
5559
BookNumber = x.Number
5660
}));
57-
58-
using var scope = _scopeFactory.CreateScope();
59-
var navigation = scope.ServiceProvider.GetRequiredService<INavigation>();
60-
var viewModel = scope.ServiceProvider.GetRequiredService<ChapterSelectionViewModel>();
61-
var page = scope.ServiceProvider.GetRequiredService<ChapterSelection>();
62-
page.BindingContext = viewModel;
63-
await navigation.PushAsync(page);
6461
IsBusy = false;
6562
});
6663

67-
_onBibleReadingInitialized = (_, _) =>
68-
{
69-
var stateValue = _state.Value;
70-
if (stateValue.CurrentBibleReadingSchedule == null ||
71-
stateValue.TentativeBibleReadingSchedule == null) return;
72-
_current = stateValue.CurrentBibleReadingSchedule;
73-
_tentative = stateValue.TentativeBibleReadingSchedule;
74-
Task.Run(async () =>
75-
{
76-
await MainThread.InvokeOnMainThreadAsync(() => IsBusy = true);
77-
await Initialize(_tentative.LanguageCode, _tentative.PublicationCode);
78-
await MainThread.InvokeOnMainThreadAsync(() => IsBusy = false);
79-
});
80-
81-
if (_onBibleReadingInitialized == null) return;
82-
_state.StateChanged -= _onBibleReadingInitialized;
83-
_onBibleReadingInitialized = null;
84-
};
85-
_state.StateChanged += _onBibleReadingInitialized;
86-
8764
// Subscribe to current schedule changes (but skip first one)
8865
BibleReadingSchedule lastCurrent = null;
8966

67+
_state.StateChanged += OnBibleReadingInitialized;
68+
_state.StateChanged += OnBibleReadingChanged;
69+
70+
return;
71+
9072
// Define handler BEFORE subscription and BEFORE any return statement
9173
void OnBibleReadingChanged(object sender, EventArgs e)
9274
{
@@ -97,14 +79,26 @@ void OnBibleReadingChanged(object sender, EventArgs e)
9779
lastCurrent = _current;
9880

9981
// Update selected book when state changes (e.g., after navigating back)
100-
MainThread.BeginInvokeOnMainThread(() =>
82+
MainThread.BeginInvokeOnMainThread(SetSelectedBook);
83+
}
84+
85+
void OnBibleReadingInitialized(object o, EventArgs eventArgs)
86+
{
87+
var stateValue = _state.Value;
88+
if (stateValue.CurrentBibleReadingSchedule == null || stateValue.TentativeBibleReadingSchedule == null) return;
89+
_current = stateValue.CurrentBibleReadingSchedule;
90+
_tentative = stateValue.TentativeBibleReadingSchedule;
91+
Task.Run(async () =>
10192
{
102-
SetSelectedBook();
93+
await MainThread.InvokeOnMainThreadAsync(() => IsBusy = true);
94+
await Initialize(_tentative.LanguageCode, _tentative.PublicationCode);
95+
await MainThread.InvokeOnMainThreadAsync(() => IsBusy = false);
10396
});
104-
}
10597

106-
_onBibleReadingChanged = OnBibleReadingChanged;
107-
_state.StateChanged += _onBibleReadingChanged;
98+
if (_onBibleReadingInitialized == null) return;
99+
_state.StateChanged -= _onBibleReadingInitialized;
100+
_onBibleReadingInitialized = null;
101+
}
108102
}
109103

110104
public void Dispose()

0 commit comments

Comments
 (0)