mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
fix: restore ContentType import and optimize sorting logic in selection feature
This commit is contained in:
1 parent
225e9866ef
commit
0a8c890df5
2 files changed
+222
-185
No files matched your search
@@ -3,8 +3,8 @@ import { Component, computed, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { SearchResultItemComponent } from '../../shared/components/search-result-item/search-result-item.component';
|
||||
import { ContentType } from '../xtream-state';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { ContentType } from '../xtream-state';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recently-added',
|
||||
@@ -30,8 +30,10 @@ export class RecentlyAddedComponent {
|
||||
private getRecentlyAdded(items: any[], isSeries = false) {
|
||||
return [...items]
|
||||
.sort((a, b) => {
|
||||
const dateA = parseInt(isSeries ? a.last_modified : a.added) || 0;
|
||||
const dateB = parseInt(isSeries ? b.last_modified : b.added) || 0;
|
||||
const dateA =
|
||||
parseInt(isSeries ? a.last_modified : a.added) || 0;
|
||||
const dateB =
|
||||
parseInt(isSeries ? b.last_modified : b.added) || 0;
|
||||
return dateB - dateA;
|
||||
})
|
||||
.slice(0, 20);
|
||||
@@ -44,13 +46,16 @@ export class RecentlyAddedComponent {
|
||||
|
||||
openItem(item: any, type: ContentType) {
|
||||
this.xtreamStore.setSelectedContentType(type);
|
||||
|
||||
|
||||
if (type === 'live') {
|
||||
this.router.navigate(['..', type, item.category_id], {
|
||||
this.router.navigate(['..', type, item.category_id], {
|
||||
relativeTo: this.activatedRoute,
|
||||
});
|
||||
} else {
|
||||
const itemId = item.xtream_id || (type === 'series' ? item.series_id : item.stream_id);
|
||||
const itemId =
|
||||
item.xtream_id ||
|
||||
item.id ||
|
||||
(type === 'series' ? item.series_id : item.stream_id);
|
||||
this.router.navigate(['..', type, item.category_id, itemId], {
|
||||
relativeTo: this.activatedRoute,
|
||||
});
|
||||
|
||||
@@ -44,39 +44,10 @@ export function withSelection() {
|
||||
return signalStoreFeature(
|
||||
withState<SelectionState>(initialSelectionState),
|
||||
|
||||
withComputed((store) => ({
|
||||
/**
|
||||
* Get the selected category from the parent store's categories
|
||||
*/
|
||||
getSelectedCategory: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
if (!categoryId) {
|
||||
return { id: 0, name: 'All Items', type: store.selectedContentType() };
|
||||
}
|
||||
|
||||
// Access parent store categories (from withContent)
|
||||
const storeAny = store as any;
|
||||
const allCategories = [
|
||||
...(storeAny.vodCategories?.() || []),
|
||||
...(storeAny.liveCategories?.() || []),
|
||||
...(storeAny.serialCategories?.() || []),
|
||||
];
|
||||
|
||||
return allCategories.find(
|
||||
(c: any) => c.id === categoryId || c.category_id === String(categoryId)
|
||||
);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get the selected item by ID from content
|
||||
*/
|
||||
getSelectedItemById: computed(() => {
|
||||
withComputed((store) => {
|
||||
// Memoized sorted content - only recalculates when content/type changes
|
||||
const sortedContent = computed(() => {
|
||||
const categoryType = store.selectedContentType();
|
||||
const selectedItem = store.selectedItem();
|
||||
|
||||
if (!selectedItem) return null;
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
@@ -85,155 +56,213 @@ export function withSelection() {
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
return content.find(
|
||||
(item: any) =>
|
||||
item.stream_id === selectedItem.stream_id ||
|
||||
item.id === selectedItem.id ||
|
||||
item.series_id === selectedItem.series_id
|
||||
);
|
||||
}),
|
||||
return [...content].sort((a: any, b: any) => {
|
||||
const dateA =
|
||||
parseInt(
|
||||
categoryType === 'series'
|
||||
? a.last_modified
|
||||
: a.added
|
||||
) || 0;
|
||||
const dateB =
|
||||
parseInt(
|
||||
categoryType === 'series'
|
||||
? b.last_modified
|
||||
: b.added
|
||||
) || 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get paginated content for the selected category
|
||||
*/
|
||||
getPaginatedContent: computed(() => {
|
||||
const startIndex = store.page() * store.limit();
|
||||
const endIndex = startIndex + store.limit();
|
||||
const categoryId = store.selectedCategoryId();
|
||||
const categoryType = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
let filteredContent = content;
|
||||
if (categoryId) {
|
||||
filteredContent = content.filter((item: any) => Number(item.category_id) === categoryId);
|
||||
} else {
|
||||
// Filter nothing (all items), sort by added
|
||||
// Clone to avoid mutation
|
||||
filteredContent = [...content].sort((a: any, b: any) => {
|
||||
const dateA = parseInt(categoryType === 'series' ? a.last_modified : a.added) || 0;
|
||||
const dateB = parseInt(categoryType === 'series' ? b.last_modified : b.added) || 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
}
|
||||
|
||||
return filteredContent.slice(startIndex, endIndex);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get all items from the selected category (without pagination)
|
||||
*/
|
||||
selectItemsFromSelectedCategory: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
const categoryType = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
if (!categoryId) {
|
||||
// Return all items sorted by added
|
||||
return [...content].sort((a: any, b: any) => {
|
||||
const dateA = parseInt(categoryType === 'series' ? a.last_modified : a.added) || 0;
|
||||
const dateB = parseInt(categoryType === 'series' ? b.last_modified : b.added) || 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
}
|
||||
|
||||
return content.filter(
|
||||
(item: any) => Number(item.category_id) === categoryId
|
||||
);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get total pages for the selected category
|
||||
*/
|
||||
getTotalPages: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
const categoryType = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
let totalItems = 0;
|
||||
if (categoryId) {
|
||||
totalItems = content.filter(
|
||||
(item: any) => Number(item.category_id) === categoryId
|
||||
).length;
|
||||
} else {
|
||||
totalItems = content.length;
|
||||
}
|
||||
|
||||
return Math.ceil(totalItems / store.limit());
|
||||
}),
|
||||
|
||||
/**
|
||||
* Check if paginated content is loading
|
||||
*/
|
||||
isPaginatedContentLoading: computed(() => {
|
||||
// Access parent store loading state (from withContent)
|
||||
const storeAny = store as any;
|
||||
return storeAny.isLoadingContent?.() || false;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Memoized category item counts map
|
||||
*/
|
||||
getCategoryItemCounts: computed(() => {
|
||||
const type = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const streams =
|
||||
type === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: type === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
const countMap = new Map<number, number>();
|
||||
for (const item of streams) {
|
||||
const catId = Number(item.category_id);
|
||||
if (!isNaN(catId)) {
|
||||
countMap.set(catId, (countMap.get(catId) || 0) + 1);
|
||||
return {
|
||||
/**
|
||||
* Get the selected category from the parent store's categories
|
||||
*/
|
||||
getSelectedCategory: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
if (!categoryId) {
|
||||
return {
|
||||
id: 0,
|
||||
name: 'All Items',
|
||||
type: store.selectedContentType(),
|
||||
};
|
||||
}
|
||||
}
|
||||
return countMap;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get categories for the currently selected content type
|
||||
*/
|
||||
getCategoriesBySelectedType: computed(() => {
|
||||
const type = store.selectedContentType();
|
||||
// Access parent store categories (from withContent)
|
||||
const storeAny = store as any;
|
||||
const allCategories = [
|
||||
...(storeAny.vodCategories?.() || []),
|
||||
...(storeAny.liveCategories?.() || []),
|
||||
...(storeAny.serialCategories?.() || []),
|
||||
];
|
||||
|
||||
// Access parent store categories (from withContent)
|
||||
const storeAny = store as any;
|
||||
return type === 'live'
|
||||
? storeAny.liveCategories?.() || []
|
||||
: type === 'vod'
|
||||
? storeAny.vodCategories?.() || []
|
||||
: storeAny.serialCategories?.() || [];
|
||||
}),
|
||||
})),
|
||||
return allCategories.find(
|
||||
(c: any) =>
|
||||
c.id === categoryId ||
|
||||
c.category_id === String(categoryId)
|
||||
);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get the selected item by ID from content
|
||||
*/
|
||||
getSelectedItemById: computed(() => {
|
||||
const categoryType = store.selectedContentType();
|
||||
const selectedItem = store.selectedItem();
|
||||
|
||||
if (!selectedItem) return null;
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
return content.find(
|
||||
(item: any) =>
|
||||
item.stream_id === selectedItem.stream_id ||
|
||||
item.id === selectedItem.id ||
|
||||
item.series_id === selectedItem.series_id
|
||||
);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get paginated content for the selected category
|
||||
*/
|
||||
getPaginatedContent: computed(() => {
|
||||
const startIndex = store.page() * store.limit();
|
||||
const endIndex = startIndex + store.limit();
|
||||
const categoryId = store.selectedCategoryId();
|
||||
const categoryType = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
let filteredContent = content;
|
||||
if (categoryId) {
|
||||
filteredContent = content.filter(
|
||||
(item: any) =>
|
||||
Number(item.category_id) === categoryId
|
||||
);
|
||||
} else {
|
||||
filteredContent = sortedContent();
|
||||
}
|
||||
|
||||
return filteredContent.slice(startIndex, endIndex);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get all items from the selected category (without pagination)
|
||||
*/
|
||||
selectItemsFromSelectedCategory: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
const categoryType = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
if (!categoryId) {
|
||||
return sortedContent();
|
||||
}
|
||||
|
||||
return content.filter(
|
||||
(item: any) => Number(item.category_id) === categoryId
|
||||
);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get total pages for the selected category
|
||||
*/
|
||||
getTotalPages: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
const categoryType = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const content =
|
||||
categoryType === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: categoryType === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
let totalItems = 0;
|
||||
if (categoryId) {
|
||||
totalItems = content.filter(
|
||||
(item: any) =>
|
||||
Number(item.category_id) === categoryId
|
||||
).length;
|
||||
} else {
|
||||
totalItems = content.length;
|
||||
}
|
||||
|
||||
return Math.ceil(totalItems / store.limit());
|
||||
}),
|
||||
|
||||
/**
|
||||
* Check if paginated content is loading
|
||||
*/
|
||||
isPaginatedContentLoading: computed(() => {
|
||||
// Access parent store loading state (from withContent)
|
||||
const storeAny = store as any;
|
||||
return storeAny.isLoadingContent?.() || false;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Memoized category item counts map
|
||||
*/
|
||||
getCategoryItemCounts: computed(() => {
|
||||
const type = store.selectedContentType();
|
||||
|
||||
// Access parent store content (from withContent)
|
||||
const storeAny = store as any;
|
||||
const streams =
|
||||
type === 'live'
|
||||
? storeAny.liveStreams?.() || []
|
||||
: type === 'vod'
|
||||
? storeAny.vodStreams?.() || []
|
||||
: storeAny.serialStreams?.() || [];
|
||||
|
||||
const countMap = new Map<number, number>();
|
||||
for (const item of streams) {
|
||||
const catId = Number(item.category_id);
|
||||
if (!isNaN(catId)) {
|
||||
countMap.set(catId, (countMap.get(catId) || 0) + 1);
|
||||
}
|
||||
}
|
||||
return countMap;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get categories for the currently selected content type
|
||||
*/
|
||||
getCategoriesBySelectedType: computed(() => {
|
||||
const type = store.selectedContentType();
|
||||
|
||||
// Access parent store categories (from withContent)
|
||||
const storeAny = store as any;
|
||||
return type === 'live'
|
||||
? storeAny.liveCategories?.() || []
|
||||
: type === 'vod'
|
||||
? storeAny.vodCategories?.() || []
|
||||
: storeAny.serialCategories?.() || [];
|
||||
}),
|
||||
};
|
||||
}),
|
||||
|
||||
withMethods((store) => ({
|
||||
/**
|
||||
@@ -252,7 +281,8 @@ export function withSelection() {
|
||||
*/
|
||||
setSelectedCategory(categoryId: number | null): void {
|
||||
patchState(store, {
|
||||
selectedCategoryId: categoryId !== null ? Number(categoryId) : null,
|
||||
selectedCategoryId:
|
||||
categoryId !== null ? Number(categoryId) : null,
|
||||
page: 0,
|
||||
});
|
||||
},
|
||||
@@ -292,7 +322,9 @@ export function withSelection() {
|
||||
resetSelection(): void {
|
||||
patchState(store, {
|
||||
...initialSelectionState,
|
||||
limit: Number(localStorage.getItem('xtream-page-size') ?? 25),
|
||||
limit: Number(
|
||||
localStorage.getItem('xtream-page-size') ?? 25
|
||||
),
|
||||
});
|
||||
},
|
||||
}))
|
||||
|
||||
Reference in new issue
Block a user