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 @@
>
-
-
- search
-
-
+
+
+
+
+
+
+ search
+
+
+
+
+
+
+
+
+
+
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ç",