From 81cbc1e5d6fa85ae7db08fc15c2e5556c46a6439 Mon Sep 17 00:00:00 2001 From: Serkan Date: Sun, 29 Sep 2024 05:12:34 +0300 Subject: [PATCH] feat: add sorting button for VOD and series --- src/app/shared/pipes/sort.pipe.ts | 27 ++++++++++ .../category-content-view.component.html | 2 +- .../category-content-view.component.ts | 3 ++ .../navigation-bar.component.html | 52 +++++++++++++++---- .../navigation-bar.component.scss | 4 ++ .../navigation-bar.component.ts | 12 +++++ src/app/xtream/portal.store.ts | 13 ++++- .../xtream-main-container.component.html | 1 + src/assets/i18n/en.json | 6 ++- src/assets/i18n/tr.json | 6 ++- 10 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 src/app/shared/pipes/sort.pipe.ts diff --git a/src/app/shared/pipes/sort.pipe.ts b/src/app/shared/pipes/sort.pipe.ts new file mode 100644 index 000000000..7cd5a73c0 --- /dev/null +++ b/src/app/shared/pipes/sort.pipe.ts @@ -0,0 +1,27 @@ +import { Injectable, Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'sortBy', + standalone: true, +}) +@Injectable() +export class SortPipe implements PipeTransform { + transform(array: any[], sortType: string): any { + if (!array || !sortType) { + return array; + } + return array.sort((a, b) => { + if (sortType === 'date') { + return parseInt(b.added) - parseInt(a.added); + } else if (sortType === 'rating') { + const ratingA = isNaN(parseFloat(a.rating)) ? 0 : parseFloat(a.rating); + const ratingB = isNaN(parseFloat(b.rating)) ? 0 : parseFloat(b.rating); + return ratingB - ratingA; + } else if (sortType === 'alpha') { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + } else { + return 0; + } + }); + } +} diff --git a/src/app/xtream/category-content-view/category-content-view.component.html b/src/app/xtream/category-content-view/category-content-view.component.html index 4a3bdf9b1..b4bb8d484 100644 --- a/src/app/xtream/category-content-view/category-content-view.component.html +++ b/src/app/xtream/category-content-view/category-content-view.component.html @@ -1,6 +1,6 @@ @if (items?.length > 0) {
- @for (item of items | filterBy: searchPhrase() : 'name'; track $index) { + @for (item of items | filterBy: searchPhrase() : 'name' | sortBy: sortType(); track $index) { @if (item.stream_icon || item.cover || item.logo) { diff --git a/src/app/xtream/category-content-view/category-content-view.component.ts b/src/app/xtream/category-content-view/category-content-view.component.ts index bc6298cf9..f485e6b16 100644 --- a/src/app/xtream/category-content-view/category-content-view.component.ts +++ b/src/app/xtream/category-content-view/category-content-view.component.ts @@ -6,6 +6,7 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { XtreamItem } from '../../../../shared/xtream-item.interface'; import { FilterPipe } from '../../shared/pipes/filter.pipe'; +import { SortPipe } from '../../shared/pipes/sort.pipe'; import { PlaylistErrorViewComponent } from '../playlist-error-view/playlist-error-view.component'; import { PortalStore } from '../portal.store'; @@ -16,6 +17,7 @@ import { PortalStore } from '../portal.store'; standalone: true, imports: [ FilterPipe, + SortPipe, MatCardModule, MatIconModule, MatTooltipModule, @@ -30,4 +32,5 @@ export class CategoryContentViewComponent { portalStore = inject(PortalStore); searchPhrase = this.portalStore.searchPhrase; + sortType = this.portalStore.sortType; } diff --git a/src/app/xtream/navigation-bar/navigation-bar.component.html b/src/app/xtream/navigation-bar/navigation-bar.component.html index 7cdc6849b..5d5a76bd9 100644 --- a/src/app/xtream/navigation-bar/navigation-bar.component.html +++ b/src/app/xtream/navigation-bar/navigation-bar.component.html @@ -50,16 +50,46 @@ >
- diff --git a/src/app/xtream/navigation-bar/navigation-bar.component.scss b/src/app/xtream/navigation-bar/navigation-bar.component.scss index eecc6a736..8a6a2c078 100644 --- a/src/app/xtream/navigation-bar/navigation-bar.component.scss +++ b/src/app/xtream/navigation-bar/navigation-bar.component.scss @@ -40,6 +40,10 @@ } } + .search-container { + display: flex; + } + .search { text-align: center; } diff --git a/src/app/xtream/navigation-bar/navigation-bar.component.ts b/src/app/xtream/navigation-bar/navigation-bar.component.ts index af0707ff9..67847c3b3 100644 --- a/src/app/xtream/navigation-bar/navigation-bar.component.ts +++ b/src/app/xtream/navigation-bar/navigation-bar.component.ts @@ -17,6 +17,9 @@ import { Breadcrumb } from '../breadcrumb.interface'; import { ContentTypeNavigationItem } from '../content-type-navigation-item.interface'; import { ContentType } from '../content-type.enum'; import { PortalStore } from '../portal.store'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ selector: 'app-navigation-bar', @@ -34,12 +37,16 @@ import { PortalStore } from '../portal.store'; NgIf, RouterLink, TranslateModule, + MatMenuModule, + MatCheckboxModule, + MatTooltipModule ], }) export class NavigationBarComponent { @Input({ required: true }) breadcrumbs: Breadcrumb[]; @Input({ required: true }) contentType: ContentType; @Input() searchVisible = true; + @Input() sortVisible = false; @Input() contentTypeNavigationItems: ContentTypeNavigationItem[]; @Input() clientSideSearch = true; @Input() showCategories = false; @@ -56,6 +63,7 @@ export class NavigationBarComponent { searchPhrase = this.portalStore.searchPhrase; searchPhraseUpdate = new Subject(); currentPlaylist = this.store.selectSignal(selectCurrentPlaylist); + sortType = this.portalStore.sortType; constructor() { this.searchPhraseUpdate @@ -76,6 +84,10 @@ export class NavigationBarComponent { this.breadcrumbClicked.emit(item); } + setSortType(type: string) { + this.portalStore.setSortType(type); + } + setSearchText(text: string) { if (this.clientSideSearch) this.portalStore.setSearchPhrase(text); else this.searchTextChanged.emit(text); diff --git a/src/app/xtream/portal.store.ts b/src/app/xtream/portal.store.ts index 26a7f9fb7..1bff9098d 100644 --- a/src/app/xtream/portal.store.ts +++ b/src/app/xtream/portal.store.ts @@ -7,6 +7,7 @@ export interface PortalState { hideExternalInfoDialog: boolean; currentVod: any; currentSerial: any; + sortType: string; } @Injectable({ providedIn: 'root' }) @@ -19,6 +20,7 @@ export class PortalStore extends ComponentStore { content: [], hideExternalInfoDialog: localStorage.getItem('hideExternalInfoDialog') === 'true', + sortType: undefined }); } @@ -26,11 +28,13 @@ export class PortalStore extends ComponentStore { readonly searchPhrase = this.selectSignal((state) => state.searchPhrase); readonly currentSerial = this.selectSignal((state) => state.currentSerial); readonly currentVod = this.selectSignal((state) => state.currentVod); - + readonly hideExternalInfoDialog = this.selectSignal( (state) => state.hideExternalInfoDialog ); + readonly sortType = this.selectSignal((state) => state.sortType); + readonly getContentById = (id: string) => this.selectSignal((state) => state.content.find((i) => i.id === id)); @@ -72,4 +76,11 @@ export class PortalStore extends ComponentStore { return { ...state, hideExternalInfoDialog }; } ); + + readonly setSortType = this.updater( + (state, sortType: string): PortalState => ({ + ...state, + sortType, + }) + ); } diff --git a/src/app/xtream/xtream-main-container.component.html b/src/app/xtream/xtream-main-container.component.html index 57315f100..e32d28725 100644 --- a/src/app/xtream/xtream-main-container.component.html +++ b/src/app/xtream/xtream-main-container.component.html @@ -9,6 +9,7 @@ [searchVisible]=" currentLayout === 'category' || currentLayout === 'category_content' " + [sortVisible]="currentLayout === 'category_content'" [contentTypeNavigationItems]="navigationContentTypes" /> diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index c5516ae97..b87b9840a 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -155,7 +155,11 @@ "NO_FAVORITES": "No favorites yet", "USE_STAR_TO_FAVORITE": "Please use 'star' icon to add channels to your favorites.", "REMOVE_FAVORITE": "Remove from favorites", - "FAVORITES_UPDATED": "Favorites were updated!" + "FAVORITES_UPDATED": "Favorites were updated!", + "SORT_BY": "Sort by", + "SORT_BY_DATE": "Sort by Date Added (Newest First)", + "SORT_BY_RATING": "Sort by Rating", + "SORT_BY_NAME": "Sort by Name (A-Z)" }, "TOP_MENU": { "OPEN_CHANNELS_LIST": "Open channel list", diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index 75c248e2f..51d0e4e36 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -155,7 +155,11 @@ "NO_FAVORITES": "Henüz favori yok", "USE_STAR_TO_FAVORITE": "Kanalları favorilerinize eklemek için lütfen 'yıldız' simgesini kullanın.", "REMOVE_FAVORITE": "Favorilerden Kaldır", - "FAVORITES_UPDATED": "Favoriler güncellendi!" + "FAVORITES_UPDATED": "Favoriler güncellendi!", + "SORT_BY": "Sırala", + "SORT_BY_DATE": "Eklenme Tarihine Göre Sırala (Önce En Yeni)", + "SORT_BY_RATING": "Puana Göre Sırala", + "SORT_BY_NAME": "İsmine Göre Sırala (A-Z)" }, "TOP_MENU": { "OPEN_CHANNELS_LIST": "Kanal listesini aç",