mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 02:46:16 -08:00
10 files changed
+184
-7
No files matched your search
@@ -18,6 +18,7 @@ import {
|
|||||||
PLAYLIST_PARSE_RESPONSE,
|
PLAYLIST_PARSE_RESPONSE,
|
||||||
PLAYLIST_UPDATE,
|
PLAYLIST_UPDATE,
|
||||||
PLAYLIST_UPDATE_RESPONSE,
|
PLAYLIST_UPDATE_RESPONSE,
|
||||||
|
PLAYLIST_UPDATE_POSITIONS,
|
||||||
} from './shared/ipc-commands';
|
} from './shared/ipc-commands';
|
||||||
|
|
||||||
const fs = require('fs');
|
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();
|
this.refreshPlaylists();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,8 +273,9 @@ export class Api {
|
|||||||
autoRefresh: 1,
|
autoRefresh: 1,
|
||||||
updateDate: 1,
|
updateDate: 1,
|
||||||
updateState: 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_PARSE_RESPONSE = 'PLAYLIST:PARSE_PLAYLIST_RESPONSE';
|
||||||
export const PLAYLIST_UPDATE = 'PLAYLIST:UPDATE';
|
export const PLAYLIST_UPDATE = 'PLAYLIST:UPDATE';
|
||||||
export const PLAYLIST_UPDATE_RESPONSE = 'PLAYLIST:UPDATE_RESPONSE';
|
export const PLAYLIST_UPDATE_RESPONSE = 'PLAYLIST:UPDATE_RESPONSE';
|
||||||
|
export const PLAYLIST_UPDATE_POSITIONS = 'PLAYLIST:UPDATE_POSITIONS';
|
||||||
|
|
||||||
// General
|
// General
|
||||||
export const SHOW_WHATS_NEW = 'SHOW_WHATS_NEW';
|
export const SHOW_WHATS_NEW = 'SHOW_WHATS_NEW';
|
||||||
|
|||||||
@@ -28,4 +28,5 @@ export interface Playlist {
|
|||||||
autoRefresh: boolean;
|
autoRefresh: boolean;
|
||||||
updateDate?: number;
|
updateDate?: number;
|
||||||
updateState?: PlaylistUpdateState;
|
updateState?: PlaylistUpdateState;
|
||||||
|
position?: number;
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@ export type PlaylistMeta = Pick<
|
|||||||
| 'filePath'
|
| 'filePath'
|
||||||
| 'updateDate'
|
| 'updateDate'
|
||||||
| 'updateState'
|
| 'updateState'
|
||||||
|
| 'position'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -8,9 +8,16 @@ import { RecentPlaylistsComponent } from './recent-playlists/recent-playlists.co
|
|||||||
import { HomeRoutingModule } from './home.routing';
|
import { HomeRoutingModule } from './home.routing';
|
||||||
import { NgxUploaderModule } from 'ngx-uploader';
|
import { NgxUploaderModule } from 'ngx-uploader';
|
||||||
import { PlaylistInfoComponent } from './recent-playlists/playlist-info/playlist-info.component';
|
import { PlaylistInfoComponent } from './recent-playlists/playlist-info/playlist-info.component';
|
||||||
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, HomeRoutingModule, NgxUploaderModule, SharedModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
HomeRoutingModule,
|
||||||
|
NgxUploaderModule,
|
||||||
|
SharedModule,
|
||||||
|
DragDropModule,
|
||||||
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
FileUploadComponent,
|
FileUploadComponent,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-nav-list>
|
<mat-nav-list cdkDropList (cdkDropListDropped)="drop($event)">
|
||||||
<mat-list-item *ngIf="playlists.length === 0">
|
<mat-list-item *ngIf="playlists.length === 0">
|
||||||
<mat-icon mat-list-icon>cloud</mat-icon>
|
<mat-icon mat-list-icon>cloud</mat-icon>
|
||||||
<div mat-line>{{ 'HOME.PLAYLISTS.NO_PLAYLISTS' | translate }}</div>
|
<div mat-line>{{ 'HOME.PLAYLISTS.NO_PLAYLISTS' | translate }}</div>
|
||||||
@@ -6,11 +6,14 @@
|
|||||||
{{ 'HOME.PLAYLISTS.ADD_FIRST' | translate }}
|
{{ 'HOME.PLAYLISTS.ADD_FIRST' | translate }}
|
||||||
</div>
|
</div>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
<a
|
<mat-list-item
|
||||||
mat-list-item
|
cdkDrag
|
||||||
*ngFor="let item of playlists; last as last"
|
*ngFor="let item of playlists; last as last"
|
||||||
(click)="playlistClicked.emit(item._id)"
|
(click)="playlistClicked.emit(item._id)"
|
||||||
>
|
>
|
||||||
|
<mat-icon mat-list-icon cdkDragHandle class="drag-icon"
|
||||||
|
>drag_indicator</mat-icon
|
||||||
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
mat-list-icon
|
mat-list-icon
|
||||||
*ngIf="item?.url"
|
*ngIf="item?.url"
|
||||||
@@ -72,5 +75,5 @@
|
|||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-divider *ngIf="!last"></mat-divider>
|
<mat-divider *ngIf="!last"></mat-divider>
|
||||||
</a>
|
</mat-list-item>
|
||||||
</mat-nav-list>
|
</mat-nav-list>
|
||||||
@@ -9,6 +9,7 @@ mat-list-item {
|
|||||||
mat-nav-list {
|
mat-nav-list {
|
||||||
height: calc(100vh - 192px);
|
height: calc(100vh - 192px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta {
|
.meta {
|
||||||
@@ -16,3 +17,43 @@ mat-nav-list {
|
|||||||
color: #666;
|
color: #666;
|
||||||
margin-top: 2px !important;
|
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 { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { PlaylistInfoComponent } from './playlist-info/playlist-info.component';
|
import { PlaylistInfoComponent } from './playlist-info/playlist-info.component';
|
||||||
import { PlaylistMeta } from './../home.component';
|
import { PlaylistMeta } from './../home.component';
|
||||||
|
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
|
import { ElectronService } from '../../services/electron.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recent-playlists',
|
selector: 'app-recent-playlists',
|
||||||
@@ -24,8 +27,12 @@ export class RecentPlaylistsComponent {
|
|||||||
/**
|
/**
|
||||||
* Creates an instance of the component
|
* Creates an instance of the component
|
||||||
* @param dialog angular material dialog reference
|
* @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
|
* Opens the details dialog with the information about the provided playlist
|
||||||
@@ -36,4 +43,20 @@ export class RecentPlaylistsComponent {
|
|||||||
data,
|
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 {
|
getAppVersion(): string {
|
||||||
return this.remote.app.getVersion();
|
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