feat(favorites): add global favorites retrieval and update layout for workspace support

This commit is contained in:
4gray committed 2026-02-24 19:16:41 +01:00
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;
}
});
@@ -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
*/