mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 01:16:15 -08:00
10 files changed
+184
-7
No files matched your search
@@ -18,6 +18,7 @@ import {
|
||||
PLAYLIST_PARSE_RESPONSE,
|
||||
PLAYLIST_UPDATE,
|
||||
PLAYLIST_UPDATE_RESPONSE,
|
||||
PLAYLIST_UPDATE_POSITIONS,
|
||||
} from './shared/ipc-commands';
|
||||
|
||||
const fs = require('fs');
|
||||
@@ -205,6 +206,17 @@ export class Api {
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.on(
|
||||
PLAYLIST_UPDATE_POSITIONS,
|
||||
(event, playlists: Partial<Playlist[]>) =>
|
||||
playlists.forEach((list, index) => {
|
||||
this.updatePlaylistById(list._id, {
|
||||
...list,
|
||||
position: index,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
this.refreshPlaylists();
|
||||
}
|
||||
|
||||
@@ -261,8 +273,9 @@ export class Api {
|
||||
autoRefresh: 1,
|
||||
updateDate: 1,
|
||||
updateState: 1,
|
||||
position: 1,
|
||||
})
|
||||
.sort({ importDate: -1 });
|
||||
.sort({ position: 1, importDate: -1 });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ export const PLAYLIST_PARSE = 'PLAYLIST:PARSE_PLAYLIST';
|
||||
export const PLAYLIST_PARSE_RESPONSE = 'PLAYLIST:PARSE_PLAYLIST_RESPONSE';
|
||||
export const PLAYLIST_UPDATE = 'PLAYLIST:UPDATE';
|
||||
export const PLAYLIST_UPDATE_RESPONSE = 'PLAYLIST:UPDATE_RESPONSE';
|
||||
export const PLAYLIST_UPDATE_POSITIONS = 'PLAYLIST:UPDATE_POSITIONS';
|
||||
|
||||
// General
|
||||
export const SHOW_WHATS_NEW = 'SHOW_WHATS_NEW';
|
||||
|
||||
@@ -28,4 +28,5 @@ export interface Playlist {
|
||||
autoRefresh: boolean;
|
||||
updateDate?: number;
|
||||
updateState?: PlaylistUpdateState;
|
||||
position?: number;
|
||||
}
|
||||
@@ -28,6 +28,7 @@ export type PlaylistMeta = Pick<
|
||||
| 'filePath'
|
||||
| 'updateDate'
|
||||
| 'updateState'
|
||||
| 'position'
|
||||
>;
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -8,9 +8,16 @@ import { RecentPlaylistsComponent } from './recent-playlists/recent-playlists.co
|
||||
import { HomeRoutingModule } from './home.routing';
|
||||
import { NgxUploaderModule } from 'ngx-uploader';
|
||||
import { PlaylistInfoComponent } from './recent-playlists/playlist-info/playlist-info.component';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, HomeRoutingModule, NgxUploaderModule, SharedModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HomeRoutingModule,
|
||||
NgxUploaderModule,
|
||||
SharedModule,
|
||||
DragDropModule,
|
||||
],
|
||||
declarations: [
|
||||
HomeComponent,
|
||||
FileUploadComponent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<mat-nav-list>
|
||||
<mat-nav-list cdkDropList (cdkDropListDropped)="drop($event)">
|
||||
<mat-list-item *ngIf="playlists.length === 0">
|
||||
<mat-icon mat-list-icon>cloud</mat-icon>
|
||||
<div mat-line>{{ 'HOME.PLAYLISTS.NO_PLAYLISTS' | translate }}</div>
|
||||
@@ -6,11 +6,14 @@
|
||||
{{ 'HOME.PLAYLISTS.ADD_FIRST' | translate }}
|
||||
</div>
|
||||
</mat-list-item>
|
||||
<a
|
||||
mat-list-item
|
||||
<mat-list-item
|
||||
cdkDrag
|
||||
*ngFor="let item of playlists; last as last"
|
||||
(click)="playlistClicked.emit(item._id)"
|
||||
>
|
||||
<mat-icon mat-list-icon cdkDragHandle class="drag-icon"
|
||||
>drag_indicator</mat-icon
|
||||
>
|
||||
<mat-icon
|
||||
mat-list-icon
|
||||
*ngIf="item?.url"
|
||||
@@ -72,5 +75,5 @@
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
<mat-divider *ngIf="!last"></mat-divider>
|
||||
</a>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
@@ -9,6 +9,7 @@ mat-list-item {
|
||||
mat-nav-list {
|
||||
height: calc(100vh - 192px);
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.meta {
|
||||
@@ -16,3 +17,43 @@ mat-nav-list {
|
||||
color: #666;
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
|
||||
::ng-deep .cdk-drag-preview .mat-list-item-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
position: relative;
|
||||
height: inherit;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
|
||||
.mat-list-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: auto;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
padding-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.cdk-drag-preview {
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.cdk-drag-placeholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cdk-drag-animating {
|
||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.drag-icon {
|
||||
cursor: move;
|
||||
margin-left: -20px;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { MockModule, MockPipe, MockProvider } from 'ng-mocks';
|
||||
import { ElectronService } from '../../services/electron.service';
|
||||
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||
import { RecentPlaylistsComponent } from './recent-playlists.component';
|
||||
import { PlaylistMeta } from '../home.component';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
export class ElectronServiceStub {
|
||||
sendIpcEvent(): void {}
|
||||
}
|
||||
|
||||
describe('RecentPlaylistsComponent', () => {
|
||||
let component: RecentPlaylistsComponent;
|
||||
let fixture: ComponentFixture<RecentPlaylistsComponent>;
|
||||
let electronService: ElectronService;
|
||||
let dialog: MatDialog;
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
RecentPlaylistsComponent,
|
||||
MockPipe(TranslatePipe),
|
||||
],
|
||||
imports: [
|
||||
MockModule(MatDialogModule),
|
||||
MockModule(MatListModule),
|
||||
MockModule(MatIconModule),
|
||||
MockModule(MatTooltipModule),
|
||||
],
|
||||
providers: [
|
||||
{ provide: ElectronService, useClass: ElectronServiceStub },
|
||||
MockProvider(TranslateService),
|
||||
],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RecentPlaylistsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.playlists = [];
|
||||
dialog = TestBed.inject(MatDialog);
|
||||
electronService = TestBed.inject(ElectronService);
|
||||
TestBed.inject(ElectronService);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should open the info dialog', () => {
|
||||
spyOn(dialog, 'open');
|
||||
component.openInfoDialog({} as PlaylistMeta);
|
||||
expect(dialog.open).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should send an ipc event after drop event', () => {
|
||||
const event = {
|
||||
previousIndex: 0,
|
||||
currentIndex: 1,
|
||||
item: undefined,
|
||||
container: undefined,
|
||||
previousContainer: undefined,
|
||||
isPointerOverContainer: true,
|
||||
distance: { x: 0, y: 0 },
|
||||
dropPoint: { x: 0, y: 0 },
|
||||
};
|
||||
spyOn(electronService, 'sendIpcEvent');
|
||||
component.drop(event);
|
||||
expect(electronService.sendIpcEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,10 @@
|
||||
import { PLAYLIST_UPDATE_POSITIONS } from './../../../../shared/ipc-commands';
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { PlaylistInfoComponent } from './playlist-info/playlist-info.component';
|
||||
import { PlaylistMeta } from './../home.component';
|
||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { ElectronService } from '../../services/electron.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recent-playlists',
|
||||
@@ -24,8 +27,12 @@ export class RecentPlaylistsComponent {
|
||||
/**
|
||||
* Creates an instance of the component
|
||||
* @param dialog angular material dialog reference
|
||||
* @param electronService electron service
|
||||
*/
|
||||
constructor(public dialog: MatDialog) {}
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private electronService: ElectronService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Opens the details dialog with the information about the provided playlist
|
||||
@@ -36,4 +43,20 @@ export class RecentPlaylistsComponent {
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop event handler - applies the new sort order to the playlists array
|
||||
* @param event drop event
|
||||
*/
|
||||
drop(event: CdkDragDrop<PlaylistMeta[]>): void {
|
||||
moveItemInArray(
|
||||
this.playlists,
|
||||
event.previousIndex,
|
||||
event.currentIndex
|
||||
);
|
||||
this.electronService.sendIpcEvent(
|
||||
PLAYLIST_UPDATE_POSITIONS,
|
||||
this.playlists
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -37,4 +37,13 @@ export class ElectronService {
|
||||
getAppVersion(): string {
|
||||
return this.remote.app.getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an IPC event from render to the main process
|
||||
* @param type event type
|
||||
* @param payload data payload
|
||||
*/
|
||||
sendIpcEvent(type: string, payload: unknown): void {
|
||||
this.ipcRenderer.send(type, payload);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user