Skip to content

Commit 7dec430

Browse files
committed
Fix filter logic
1 parent 3ecb22c commit 7dec430

File tree

11 files changed

+248
-52
lines changed

11 files changed

+248
-52
lines changed

src/Bible.Alarm.Shared/Services/Media/BiblePublicationService.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,47 @@ public async Task<List<string>> GetAvailablePublicationCodesAsync(string languag
201201
}
202202
}
203203

204+
public async Task<string?> GetFirstPublicationCodeByOrderAsync(string languageCode, string? categoryName = null, CancellationToken cancellationToken = default)
205+
{
206+
try
207+
{
208+
using var scope = scopeFactory.CreateScope();
209+
var dbContext = scope.ServiceProvider.GetRequiredService<MediaDbContext>();
210+
211+
var normalizedLanguageCode = languageCode.ToUpperInvariant();
212+
213+
// Query PublicationLanguages table ordered by Id (first by ID order)
214+
var query = dbContext.PublicationLanguages
215+
.AsNoTracking()
216+
.Include(x => x.Language)
217+
.Include(x => x.Category)
218+
.Where(x => x.Language != null && x.Language.LanguageCode == normalizedLanguageCode);
219+
220+
// Filter by category if provided
221+
if (!string.IsNullOrWhiteSpace(categoryName))
222+
{
223+
query = query.Where(x => x.Category != null && x.Category.CategoryName == categoryName);
224+
}
225+
226+
// Order by Id to get the first publication by ID order
227+
var firstPublicationCode = await query
228+
.OrderBy(x => x.Id)
229+
.Select(x => x.PublicationCode)
230+
.FirstOrDefaultAsync(cancellationToken);
231+
232+
logger.Debug("GetFirstPublicationCodeByOrderAsync: Found first publication code={PublicationCode} for language={LanguageCode}, category={CategoryName}",
233+
firstPublicationCode ?? "(null)", languageCode, categoryName ?? "all");
234+
235+
return firstPublicationCode;
236+
}
237+
catch (Exception ex)
238+
{
239+
logger.Error(ex, "Error getting first publication code by order from PublicationLanguages. LanguageCode={LanguageCode}, CategoryName={CategoryName}",
240+
languageCode, categoryName);
241+
throw;
242+
}
243+
}
244+
204245
public void Dispose()
205246
{
206247
if (isDisposed)

src/Bible.Alarm.Shared/Services/Media/Interfaces/IBiblePublicationService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,11 @@ public interface IBiblePublicationService : IDisposable
3838
/// This shows all discovered publications, even if not yet downloaded.
3939
/// </summary>
4040
Task<List<string>> GetAvailablePublicationCodesAsync(string languageCode, string? categoryName = null, CancellationToken cancellationToken = default);
41+
42+
/// <summary>
43+
/// Gets the first available publication code by ID order from PublicationLanguages for a given language and category.
44+
/// Used for cascade downloading when a language is selected.
45+
/// </summary>
46+
Task<string?> GetFirstPublicationCodeByOrderAsync(string languageCode, string? categoryName = null, CancellationToken cancellationToken = default);
4147
}
4248

src/Bible.Alarm/Services/Media/Interfaces/IMediaService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Bible.Alarm.Services.Media.Interfaces;
77
public interface IMediaService : IDisposable
88
{
99
Task<Dictionary<string, Language>> GetBiblePublicationLanguages(string? categoryName = null);
10-
Task<Dictionary<string, BiblePublication>> GetBiblePublications(string languageCode, string? categoryName = null);
10+
Task<Dictionary<string, BiblePublication>> GetBiblePublications(string languageCode, string? categoryName = null, bool downloadAll = false);
1111
Task<SortedDictionary<int, BiblePublicationSection>> GetBiblePublicationSections(string languageCode, string versionCode);
1212
Task<SortedDictionary<int, BiblePublicationSection>> GetMusicSections(string publicationCode);
1313
Task<BiblePublicationSection> GetBiblePublicationSection(string languageCode, string versionCode, int sectionNumber);
@@ -17,7 +17,7 @@ public interface IMediaService : IDisposable
1717
Task<SortedDictionary<int, MusicTrack>> GetMelodyMusicTracks(string publicationCode);
1818
Task<SortedDictionary<int, MusicTrack>> GetMelodyMusicTracksBySection(string publicationCode, string sectionCode);
1919
Task<Dictionary<string, Language>> GetVocalMusicLanguages();
20-
Task<Dictionary<string, VocalMusic>> GetVocalMusicReleases(string languageCode);
20+
Task<Dictionary<string, VocalMusic>> GetVocalMusicReleases(string languageCode, bool downloadAll = false);
2121
Task<SortedDictionary<int, MusicTrack>> GetVocalMusicTracks(string languageCode, string publicationCode);
2222
Task UpdateBiblePublicationTrackUrl(string languageCode, string versionCode, int sectionNumber, int trackNumber, string url);
2323
Task UpdateVocalTrackUrl(string languageCode, string publicationCode, int trackNumber, string url);

src/Bible.Alarm/Services/Media/MediaService.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public async Task<Dictionary<string, Language>> GetBiblePublicationLanguages(str
4343
return await BiblePublicationService.GetDistinctLanguagesAsync(categoryName, cancellationTokenSource.Token);
4444
}
4545

46-
public async Task<Dictionary<string, BiblePublication>> GetBiblePublications(string languageCode, string? categoryName = null)
46+
public async Task<Dictionary<string, BiblePublication>> GetBiblePublications(string languageCode, string? categoryName = null, bool downloadAll = false)
4747
{
4848
await mediaIndexService.Verify();
4949

@@ -122,16 +122,17 @@ public async Task<Dictionary<string, BiblePublication>> GetBiblePublications(str
122122
Log.Information("GetBiblePublications: Returning {TotalCount} publications ({DownloadedCount} downloaded, {PlaceholderCount} placeholders) for language={LanguageCode}, category={CategoryName}",
123123
result.Count, downloadedPublications.Count, result.Count - downloadedPublications.Count, languageCode, categoryName ?? "all");
124124

125-
// Step 4: If language is not English and publications modal is opened, ensure all are downloaded in background
126-
// (This happens when user opens the publication modal)
127-
if (!string.IsNullOrEmpty(languageCode) && !languageCode.Equals("E", StringComparison.OrdinalIgnoreCase))
125+
// Step 4: Download publications based on downloadAll flag
126+
// - If downloadAll=true (publication modal opened): download all publications with their first sections and tracks
127+
// - If downloadAll=false (language selected): don't download here (will be done in cascade)
128+
if (downloadAll && !string.IsNullOrEmpty(languageCode) && !languageCode.Equals("E", StringComparison.OrdinalIgnoreCase))
128129
{
129130
// Fire and forget - don't block the UI
130131
_ = Task.Run(async () =>
131132
{
132133
try
133134
{
134-
Log.Information("Background: Ensuring all publications are downloaded for language {LanguageCode}", languageCode);
135+
Log.Information("Background: Ensuring all publications are downloaded for language {LanguageCode} (publication modal opened)", languageCode);
135136
await languageContentService.EnsureAllPublicationsForLanguageAsync(
136137
languageCode, categoryName, cancellationTokenSource.Token);
137138
}
@@ -276,7 +277,7 @@ public async Task<Dictionary<string, Language>> GetVocalMusicLanguages()
276277
return result;
277278
}
278279

279-
public async Task<Dictionary<string, VocalMusic>> GetVocalMusicReleases(string languageCode)
280+
public async Task<Dictionary<string, VocalMusic>> GetVocalMusicReleases(string languageCode, bool downloadAll = false)
280281
{
281282
await mediaIndexService.Verify();
282283

@@ -360,16 +361,17 @@ public async Task<Dictionary<string, VocalMusic>> GetVocalMusicReleases(string l
360361
Log.Information("GetVocalMusicReleases: Returning {TotalCount} vocal music releases ({DownloadedCount} downloaded, {PlaceholderCount} placeholders) for language={LanguageCode}",
361362
result.Count, downloadedReleases.Count, result.Count - downloadedReleases.Count, languageCode);
362363

363-
// Step 4: If language is not English and publications modal is opened, ensure all are downloaded in background
364-
// (This happens when user opens the publication modal)
365-
if (!string.IsNullOrEmpty(languageCode) && !languageCode.Equals("E", StringComparison.OrdinalIgnoreCase))
364+
// Step 4: Download publications based on downloadAll flag
365+
// - If downloadAll=true (publication modal opened): download all publications with their first sections and tracks
366+
// - If downloadAll=false (language selected): don't download here (will be done in cascade)
367+
if (downloadAll && !string.IsNullOrEmpty(languageCode) && !languageCode.Equals("E", StringComparison.OrdinalIgnoreCase))
366368
{
367369
// Fire and forget - don't block the UI
368370
_ = Task.Run(async () =>
369371
{
370372
try
371373
{
372-
Log.Information("Background: Ensuring all vocal music releases are downloaded for language {LanguageCode}", languageCode);
374+
Log.Information("Background: Ensuring all vocal music releases are downloaded for language {LanguageCode} (publication modal opened)", languageCode);
373375
await languageContentService.EnsureAllPublicationsForLanguageAsync(
374376
languageCode, "Music", cancellationTokenSource.Token);
375377
}

src/Bible.Alarm/ViewModels/BiblePublications/BiblePublicationSelectionViewModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public BiblePublicationSelectionViewModel(
5050
// Initialize services
5151
dataProvider = new BiblePublicationSelectionDataProvider(mediaService, state, dispatcher);
5252
stateHandler = new BiblePublicationSelectionStateHandler(mediaService, state, mapper, dataProvider);
53-
commandHandler = new BiblePublicationSelectionCommandHandler(mediaService, state, dispatcher, navigationService, mapper, biblePublicationService);
53+
var languageContentService = serviceProvider.GetService<ILanguageContentService>();
54+
commandHandler = new BiblePublicationSelectionCommandHandler(mediaService, state, dispatcher, navigationService, mapper, biblePublicationService, languageContentService);
5455
propertyManager = new BiblePublicationSelectionPropertyManager(state, dataProvider, stateHandler);
5556

5657
// Initialize current from state if available (map DTO to entity)

src/Bible.Alarm/ViewModels/BiblePublications/BibleSelectionViewModelHelpers/BiblePublicationSelectionCommandHandler.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public sealed class BiblePublicationSelectionCommandHandler
2424
{
2525
private readonly IMediaService mediaService;
2626
private readonly IBiblePublicationService? biblePublicationService;
27+
private readonly ILanguageContentService? languageContentService;
2728
private readonly IState<ApplicationState> state;
2829
private readonly IDispatcher dispatcher;
2930
private readonly INavigationService navigationService;
@@ -35,10 +36,12 @@ public BiblePublicationSelectionCommandHandler(
3536
IDispatcher dispatcher,
3637
INavigationService navigationService,
3738
IMapper mapper,
38-
IBiblePublicationService? biblePublicationService = null)
39+
IBiblePublicationService? biblePublicationService = null,
40+
ILanguageContentService? languageContentService = null)
3941
{
4042
this.mediaService = mediaService;
4143
this.biblePublicationService = biblePublicationService;
44+
this.languageContentService = languageContentService;
4245
this.state = state;
4346
this.dispatcher = dispatcher;
4447
this.navigationService = navigationService;
@@ -98,7 +101,7 @@ public ICommand CreateSectionSelectionCommand(
98101
Log.Debug("CreateSectionSelectionCommand: Calling GetSectionAndTrackForPublicationAsync for publication={PublicationCode}, language={LanguageCode}",
99102
x.Code, currentLanguage.Code);
100103

101-
var itemSelector = new BiblePublicationSelectionItemSelector(mediaService, state, biblePublicationService);
104+
var itemSelector = new BiblePublicationSelectionItemSelector(mediaService, state, biblePublicationService, languageContentService);
102105
var (sectionNumber, trackNumber, sectionName, trackTitle) = await itemSelector.GetSectionAndTrackForPublicationAsync(x, currentLanguage);
103106

104107
Log.Debug("CreateSectionSelectionCommand: Result sectionNumber={SectionNumber}, trackNumber={TrackNumber}, sectionName={SectionName}, trackTitle={TrackTitle}",
@@ -165,7 +168,7 @@ public ICommand CreateSelectLanguageCommand(
165168
updateSelectedLanguage(x);
166169
await navigationService.PopModalAsync();
167170

168-
var itemSelector = new BiblePublicationSelectionItemSelector(mediaService, state, biblePublicationService);
171+
var itemSelector = new BiblePublicationSelectionItemSelector(mediaService, state, biblePublicationService, languageContentService);
169172
var (publicationCode, sectionNumber, trackNumber, sectionName, publicationName, trackTitle) =
170173
await itemSelector.GetPublicationSectionAndTrackForLanguageAsync(x);
171174

src/Bible.Alarm/ViewModels/BiblePublications/BibleSelectionViewModelHelpers/BiblePublicationSelectionDataProvider.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ private static int GetPublicationSortPriority(string code)
127127
public async Task PopulatePublicationsAsync(
128128
string languageCode,
129129
ObservableCollection<PublicationListViewItemModel>? publications,
130-
bool languageChanged)
130+
bool languageChanged,
131+
bool downloadAll = false)
131132
{
132133
if (publications == null) return;
133134

@@ -141,7 +142,9 @@ public async Task PopulatePublicationsAsync(
141142
// Do ALL processing on background thread to avoid blocking spinner animation
142143
var (publicationVMs, newMapping, defaultPublication) = await Task.Run(async () =>
143144
{
144-
var publicationsData = await mediaService.GetBiblePublications(languageCode, currentCategoryName);
145+
// downloadAll=true when publication modal opens (download all publications with first sections and tracks)
146+
// downloadAll=false when language changes (only download first publication in cascade)
147+
var publicationsData = await mediaService.GetBiblePublications(languageCode, currentCategoryName, downloadAll);
145148
var vms = new List<PublicationListViewItemModel>();
146149
var mapping = new Dictionary<string, PublicationListViewItemModel>();
147150

src/Bible.Alarm/ViewModels/BiblePublications/BibleSelectionViewModelHelpers/BiblePublicationSelectionItemSelector.cs

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public sealed class BiblePublicationSelectionItemSelector
1818
{
1919
private readonly IMediaService mediaService;
2020
private readonly IBiblePublicationService? biblePublicationService;
21+
private readonly ILanguageContentService? languageContentService;
2122
private readonly IState<ApplicationState> state;
2223

2324
// Priority codes for Bible publications: nwt (2013 NWT), bi12 (1984 NWT)
@@ -26,11 +27,13 @@ public sealed class BiblePublicationSelectionItemSelector
2627
public BiblePublicationSelectionItemSelector(
2728
IMediaService mediaService,
2829
IState<ApplicationState> state,
29-
IBiblePublicationService? biblePublicationService = null)
30+
IBiblePublicationService? biblePublicationService = null,
31+
ILanguageContentService? languageContentService = null)
3032
{
3133
this.mediaService = mediaService;
3234
this.state = state;
3335
this.biblePublicationService = biblePublicationService;
36+
this.languageContentService = languageContentService;
3437
}
3538

3639
/// <summary>
@@ -90,37 +93,70 @@ public BiblePublicationSelectionItemSelector(
9093
{
9194
Log.Debug("GetPublicationSectionAndTrackForLanguageAsync: Starting for language={LanguageCode}", language.Code);
9295

93-
// Get all publications for this language
94-
var publications = await Task.Run(async () =>
95-
await mediaService.GetBiblePublications(language.Code));
96-
97-
Log.Debug("GetPublicationSectionAndTrackForLanguageAsync: Found {PublicationCount} publications for language={LanguageCode}",
98-
publications?.Count ?? 0, language.Code);
96+
// Step 1: Get the first publication code by ID order from PublicationLanguages
97+
// This is the publication that should be downloaded when language is selected
98+
string? firstPublicationCode = null;
99+
if (biblePublicationService != null)
100+
{
101+
var stateValue = state.Value;
102+
var categoryName = stateValue.CurrentSchedule?.BiblePublicationCategoryName;
103+
firstPublicationCode = await Task.Run(async () =>
104+
await biblePublicationService.GetFirstPublicationCodeByOrderAsync(language.Code, categoryName));
105+
}
99106

100-
if (publications == null || publications.Count == 0)
107+
if (string.IsNullOrEmpty(firstPublicationCode))
101108
{
102-
Log.Warning("GetPublicationSectionAndTrackForLanguageAsync: No publications found for language={LanguageCode}", language.Code);
109+
Log.Warning("GetPublicationSectionAndTrackForLanguageAsync: No first publication found for language={LanguageCode}", language.Code);
103110
return (null, 0, 0, string.Empty, string.Empty, string.Empty);
104111
}
105112

106-
// Select preferred publication: nwt first, then bi12, then first available
107-
string publicationCode;
108-
string publicationName;
109-
KeyValuePair<string, BiblePublication>? preferredPublication = null;
110-
111-
foreach (var priorityCode in PriorityPublicationCodes)
113+
Log.Debug("GetPublicationSectionAndTrackForLanguageAsync: First publication by ID order={PublicationCode} for language={LanguageCode}",
114+
firstPublicationCode, language.Code);
115+
116+
// Step 2: Download the first publication with its first section (if sectioned) and tracks
117+
// This happens when language is selected (cascade)
118+
// EnsurePublicationExistsAsync will download the publication, its first section (by ID order from SectionLanguages), and tracks
119+
var stateValue2 = state.Value;
120+
var categoryName2 = stateValue2.CurrentSchedule?.BiblePublicationCategoryName;
121+
122+
// Download the first publication if not English and languageContentService is available
123+
if (languageContentService != null && !language.Code.Equals("E", StringComparison.OrdinalIgnoreCase))
112124
{
113-
if (publications.TryGetValue(priorityCode, out var pub))
125+
Log.Information("Downloading first publication {PublicationCode} (by ID order) for language {LanguageCode} (cascade)",
126+
firstPublicationCode, language.Code);
127+
128+
try
129+
{
130+
// EnsurePublicationExistsAsync downloads the publication with its first section (by ID order) and tracks
131+
var fetchSuccess = await languageContentService.EnsurePublicationExistsAsync(
132+
firstPublicationCode, language.Code);
133+
134+
if (!fetchSuccess)
135+
{
136+
Log.Warning("Failed to download first publication {PublicationCode} for language {LanguageCode}",
137+
firstPublicationCode, language.Code);
138+
}
139+
}
140+
catch (Exception ex)
114141
{
115-
preferredPublication = new KeyValuePair<string, BiblePublication>(priorityCode, pub);
116-
break;
142+
Log.Warning(ex, "Error downloading first publication {PublicationCode} for language {LanguageCode}",
143+
firstPublicationCode, language.Code);
117144
}
118145
}
119146

120-
// Fall back to first publication if no priority publications found
121-
var selectedPublication = preferredPublication ?? publications.First();
122-
publicationCode = selectedPublication.Key;
123-
publicationName = selectedPublication.Value.Name;
147+
// Step 3: Get the downloaded publication
148+
var publications = await Task.Run(async () =>
149+
await mediaService.GetBiblePublications(language.Code, categoryName2, downloadAll: false));
150+
151+
if (publications == null || !publications.TryGetValue(firstPublicationCode, out var publication))
152+
{
153+
Log.Warning("GetPublicationSectionAndTrackForLanguageAsync: Publication {PublicationCode} not found for language={LanguageCode}",
154+
firstPublicationCode, language.Code);
155+
return (null, 0, 0, string.Empty, string.Empty, string.Empty);
156+
}
157+
158+
var publicationCode = firstPublicationCode;
159+
var publicationName = publication.Name;
124160

125161
Log.Debug("GetPublicationSectionAndTrackForLanguageAsync: Selected publication code={PublicationCode}, name={PublicationName}",
126162
publicationCode, publicationName);

0 commit comments

Comments
 (0)