mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
fix(settings): drop withheld Stalker search rows at relock time and keep the M3U unlock row when every group is locked
- A relock during a page-1 Stalker search now filters the rows already on screen at once, so old unlocked results are not clickable while the replacement page is pending. - When every M3U group is locked the groups rail still renders, with its "N locked · Enter PIN to show" row, instead of the plain empty state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cdc79da768
commit
3b892f4114
5 files changed
+63
-14
No files matched your search
@@ -255,6 +255,10 @@ on either side.
|
||||
`stalkerWithheldRowKey` (`id`, else `stream_id`/`movie_id`/`series_id`,
|
||||
else the row's `cmd`/`name`), never by a shared `''`, so a page of new
|
||||
locked rows is not mistaken for a stalled portal.
|
||||
- **Stalker search relock:** a lock change drops the withheld rows already
|
||||
on screen at once (`applyRelockToResults`, page 1 included) and closes an
|
||||
open detail of a now-withheld genre, before the replacement page is
|
||||
awaited — otherwise they stay clickable while that request is pending.
|
||||
- **Stalker search staleness:** portal requests are not aborted, so a
|
||||
search page issued before a relock can finish after it, filtered with the
|
||||
pre-relock withheld set; `isStalkerSearchRequestCurrent` keys the
|
||||
|
||||
@@ -460,6 +460,24 @@ describe('StalkerSearchComponent result paging', () => {
|
||||
expect(component.searchPage()).toBe(pageBefore + 1);
|
||||
});
|
||||
|
||||
it('drops withheld rows on screen at relock time, page 1 included', () => {
|
||||
component.applySearchPageSuccess(
|
||||
1,
|
||||
[
|
||||
{ id: 'news-1', name: 'News', category_id: '5' },
|
||||
{ id: 'adult-1', name: 'Adult', category_id: '9' },
|
||||
],
|
||||
2
|
||||
);
|
||||
expect(component.searchResults()).toHaveLength(2);
|
||||
|
||||
component.applyRelockToResults(new Set(['9']), 'vod');
|
||||
|
||||
expect(component.searchResults().map((item) => item.id)).toEqual([
|
||||
'news-1',
|
||||
]);
|
||||
});
|
||||
|
||||
it('closes an open detail whose genre became withheld on relock', () => {
|
||||
component.selectItem({ id: 'adult-9', name: 'A', category_id: '9' });
|
||||
expect(component.itemDetails()).not.toBeNull();
|
||||
|
||||
@@ -297,22 +297,13 @@ export class StalkerSearchComponent {
|
||||
this.searchResultsLockVersion !== params.parentalLockVersion;
|
||||
this.searchResultsLockVersion = params.parentalLockVersion;
|
||||
if (lockVersionChanged) {
|
||||
// A relock must also close an open detail of a genre that is
|
||||
// withheld now; the list alone hiding it is not enough.
|
||||
this.closeWithheldDetail(withheldCategoryIds);
|
||||
this.applyRelockToResults(withheldCategoryIds, contentType);
|
||||
}
|
||||
if (lockVersionChanged && params.page > 1) {
|
||||
// A lock flip past page 1: drop the withheld rows on screen
|
||||
// and rebuild from page 1 rather than appending to pages
|
||||
// accumulated under the old lock state.
|
||||
const retained = withoutWithheldStalkerItems(
|
||||
this.accumulatedSearchResults(),
|
||||
contentType,
|
||||
withheldCategoryIds
|
||||
);
|
||||
this.accumulatedSearchResults.set(retained);
|
||||
// A lock flip past page 1: rebuild from page 1 rather than
|
||||
// appending to pages accumulated under the old lock state.
|
||||
this.searchPage.set(1);
|
||||
return retained;
|
||||
return this.accumulatedSearchResults();
|
||||
}
|
||||
const withheldKey = JSON.stringify([
|
||||
params.playlistId,
|
||||
@@ -743,6 +734,27 @@ export class StalkerSearchComponent {
|
||||
* lock (Lock now, idle relock): the title, its playback actions and the
|
||||
* store's selected item must not outlive the list row.
|
||||
*/
|
||||
/**
|
||||
* A lock change reached the results on screen, which were read under
|
||||
* the old lock state: close an open detail of a now-withheld genre (the
|
||||
* list hiding it is not enough) and drop the withheld rows NOW, before
|
||||
* the replacement page is awaited — on page 1 too, or they stay
|
||||
* clickable while (or, if it hangs, after) that request is pending.
|
||||
*/
|
||||
applyRelockToResults(
|
||||
withheldCategoryIds: ReadonlySet<string>,
|
||||
contentType: Parameters<typeof withoutWithheldStalkerItems>[1]
|
||||
): void {
|
||||
this.closeWithheldDetail(withheldCategoryIds);
|
||||
this.accumulatedSearchResults.set(
|
||||
withoutWithheldStalkerItems(
|
||||
this.accumulatedSearchResults(),
|
||||
contentType,
|
||||
withheldCategoryIds
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
closeWithheldDetail(withheldCategoryIds: ReadonlySet<string>): void {
|
||||
const details = this.itemDetails();
|
||||
const categoryId = details?.category_id;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
@if (!hasAnyGroups()) {
|
||||
@if (!hasAnyGroups() && withheldGroupCount() === 0) {
|
||||
<div class="groups-view-empty-state">
|
||||
<mat-icon class="empty-state-icon">{{
|
||||
searchTerm() ? 'search_off' : 'folder_off'
|
||||
|
||||
+15
@@ -580,6 +580,21 @@ describe('GroupsViewComponent', () => {
|
||||
expect(parentalLock.requestUnlock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('keeps the rail and its unlock row when every group is withheld', () => {
|
||||
fixture.componentRef.setInput('groupedChannels', {});
|
||||
fixture.componentRef.setInput('withheldGroupCount', 3);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector('.groups-view-empty-state')
|
||||
).toBeNull();
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="groups-locked-row"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('offers no group lock menu while the feature is off', () => {
|
||||
const event = new MouseEvent('contextmenu', { cancelable: true });
|
||||
component.onGroupContextMenu('Sports', event);
|
||||
|
||||
Reference in new issue
Block a user