mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
feat(favorites): add global favorites retrieval and update layout for workspace support
This commit is contained in:
1 parent
09076d1046
commit
5e46731d26
4 files changed
+95
-26
No files matched your search
@@ -108,3 +108,45 @@ ipcMain.handle('DB_GET_FAVORITES', async (event, playlistId: string) => {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get global favorites across all playlists
|
||||
*/
|
||||
ipcMain.handle('DB_GET_GLOBAL_FAVORITES', async () => {
|
||||
try {
|
||||
const db = await getDatabase();
|
||||
const result = await db
|
||||
.select({
|
||||
id: schema.content.id,
|
||||
category_id: schema.content.categoryId,
|
||||
title: schema.content.title,
|
||||
rating: schema.content.rating,
|
||||
added: schema.content.added,
|
||||
poster_url: schema.content.posterUrl,
|
||||
xtream_id: schema.content.xtreamId,
|
||||
type: schema.content.type,
|
||||
playlist_id: schema.playlists.id,
|
||||
playlist_name: schema.playlists.name,
|
||||
added_at: schema.favorites.addedAt,
|
||||
})
|
||||
.from(schema.favorites)
|
||||
.innerJoin(
|
||||
schema.content,
|
||||
eq(schema.favorites.contentId, schema.content.id)
|
||||
)
|
||||
.innerJoin(
|
||||
schema.categories,
|
||||
eq(schema.content.categoryId, schema.categories.id)
|
||||
)
|
||||
.innerJoin(
|
||||
schema.playlists,
|
||||
eq(schema.categories.playlistId, schema.playlists.id)
|
||||
)
|
||||
.orderBy(desc(schema.favorites.addedAt))
|
||||
.limit(300);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Error getting global favorites:', error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
+28
-25
@@ -1,31 +1,30 @@
|
||||
<div
|
||||
class="sidebar"
|
||||
appResizable
|
||||
[minWidth]="250"
|
||||
[maxWidth]="600"
|
||||
[defaultWidth]="400"
|
||||
storageKey="sidebar-width"
|
||||
>
|
||||
<div class="sidebar-header">
|
||||
<app-playlist-switcher
|
||||
class="switcher"
|
||||
[currentTitle]="playlistTitle()"
|
||||
[subtitle]="playlistSubtitle()"
|
||||
@if (!isWorkspaceLayout) {
|
||||
<div
|
||||
class="sidebar"
|
||||
appResizable
|
||||
[minWidth]="250"
|
||||
[maxWidth]="600"
|
||||
[defaultWidth]="400"
|
||||
storageKey="sidebar-width"
|
||||
>
|
||||
<div class="sidebar-header">
|
||||
<app-playlist-switcher
|
||||
class="switcher"
|
||||
[currentTitle]="playlistTitle()"
|
||||
[subtitle]="playlistSubtitle()"
|
||||
/>
|
||||
</div>
|
||||
<app-category-view
|
||||
[items]="categories()"
|
||||
[selectedCategoryId]="$any(selectedCategoryId())"
|
||||
(categoryClicked)="setCategoryId($event.category_id)"
|
||||
/>
|
||||
</div>
|
||||
<app-category-view
|
||||
[items]="categories()"
|
||||
[selectedCategoryId]="$any(selectedCategoryId())"
|
||||
(categoryClicked)="setCategoryId($event.category_id)"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="main-content">
|
||||
<div class="content-header">
|
||||
<h2>
|
||||
<button mat-icon-button disabled>
|
||||
<mat-icon>movie</mat-icon>
|
||||
</button>
|
||||
{{
|
||||
(titleTranslationString() | translate) +
|
||||
' (' +
|
||||
@@ -33,11 +32,11 @@
|
||||
')'
|
||||
}}
|
||||
</h2>
|
||||
@if (showHeaderAction()) {
|
||||
@if (showHeaderAction() && !isWorkspaceLayout) {
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
[matTooltip]="headerActionTooltip()"
|
||||
[disabled]="!favorites().length"
|
||||
(click)="onHeaderActionClick()"
|
||||
>
|
||||
<mat-icon>{{ headerActionIcon() }}</mat-icon>
|
||||
@@ -50,7 +49,11 @@
|
||||
@for (item of favorites(); track $index) {
|
||||
<app-content-card
|
||||
[posterUrl]="item.poster_url || item.cover"
|
||||
[title]="item?.title || item?.details?.info?.name || 'Unknown'"
|
||||
[title]="
|
||||
item?.title ||
|
||||
item?.details?.info?.name ||
|
||||
'Unknown'
|
||||
"
|
||||
[type]="item.type"
|
||||
[date]="item.added_at"
|
||||
[showRemoveButton]="true"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { Component, inject, input, output } from '@angular/core';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { PlaylistSwitcherComponent, ResizableDirective } from 'components';
|
||||
import { CategoryViewComponent } from '../../../xtream-electron/category-view/category-view.component';
|
||||
@@ -26,6 +27,10 @@ import { ContentCardComponent } from '../content-card/content-card.component';
|
||||
],
|
||||
})
|
||||
export class FavoritesLayoutComponent {
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
readonly isWorkspaceLayout =
|
||||
this.route.snapshot.data['layout'] === 'workspace';
|
||||
|
||||
readonly categories = input<any[]>([]);
|
||||
readonly favorites = input<any[]>([]);
|
||||
readonly playlistSubtitle = input<string>('');
|
||||
|
||||
@@ -48,6 +48,12 @@ export interface GlobalRecentItem extends XtreamContent {
|
||||
viewed_at: string;
|
||||
}
|
||||
|
||||
export interface GlobalFavoriteItem extends XtreamContent {
|
||||
playlist_id: string;
|
||||
playlist_name: string;
|
||||
added_at: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
@@ -296,6 +302,19 @@ export class DatabaseService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global favorites across all playlists
|
||||
*/
|
||||
async getGlobalFavorites(): Promise<GlobalFavoriteItem[]> {
|
||||
try {
|
||||
const items = await window.electron.dbGetGlobalFavorites();
|
||||
return items || [];
|
||||
} catch (error) {
|
||||
console.error('Error getting global favorites:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear recently viewed items
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user