mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
feat: add settings page
This commit is contained in:
1 parent
2f86e43f10
commit
1a44ecd995
16 files changed
+211
-25
No files matched your search
@@ -13,6 +13,11 @@ const routes: Routes = [
|
||||
path: 'iptv',
|
||||
component: VideoPlayerComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
loadChildren: () =>
|
||||
import('./settings/settings.module').then((m) => m.SettingsModule),
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
component: PageNotFoundComponent,
|
||||
|
||||
@@ -3,7 +3,6 @@ import '../polyfills';
|
||||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
|
||||
@@ -22,7 +21,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RecentPlaylistsComponent } from './recent-playlists/recent-playlists.component';
|
||||
import { ChannelListContainerComponent } from './channel-list-container/channel-list-container.component';
|
||||
import { FilterPipeModule } from 'ngx-filter-pipe';
|
||||
import { HeaderComponent } from './playlist-uploader/header/header.component';
|
||||
|
||||
// AoT requires an exported function for factories
|
||||
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
|
||||
@@ -33,7 +31,6 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ChannelListContainerComponent,
|
||||
HeaderComponent,
|
||||
PlaylistUploaderComponent,
|
||||
VideoPlayerComponent,
|
||||
RecentPlaylistsComponent,
|
||||
@@ -42,7 +39,6 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
|
||||
CommonModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
SharedModule,
|
||||
AppRoutingModule,
|
||||
|
||||
@@ -6,11 +6,13 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
|
||||
@NgModule({
|
||||
exports: [
|
||||
@@ -21,11 +23,13 @@ import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatProgressBarModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatSnackBarModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
ScrollingModule,
|
||||
MatGridListModule,
|
||||
],
|
||||
})
|
||||
export class MaterialModule {}
|
||||
@@ -1,9 +0,0 @@
|
||||
<div fxFlex="100px">
|
||||
<img src="./assets/icons/icon-tv-256.png" height="100" />
|
||||
</div>
|
||||
<div fxFlex="100" fxLayoutAlign="start center">
|
||||
<div fxLayout="column">
|
||||
<div fxFlex class="title">IPTVnator</div>
|
||||
<div class="subtitle">Please select a playlist from the list or add a new playlist via file upload or via url</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss'],
|
||||
})
|
||||
export class HeaderComponent {}
|
||||
@@ -1,4 +1,4 @@
|
||||
<app-header fxLayout="row"></app-header>
|
||||
<app-header fxLayout="row" title="IPTVnator" subtitle="Please select a playlist from the list or add a new playlist via file upload or via url"></app-header>
|
||||
<div fxLayout="row" fxFlexAlign="start start">
|
||||
<div fxFlex="100">
|
||||
<mat-tab-group [dynamicHeight]="true" color="primary" backgroundColor="primary" mat-stretch-tabs>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<app-header title="Settings / IPTVnator" subtitle="Change the configuration of the application"></app-header>
|
||||
<form [formGroup]="settingsForm" (ngSubmit)="onSubmit()" novalidate>
|
||||
<mat-card>
|
||||
<mat-card-header class="card-header">
|
||||
<div mat-card-subtitle>Please adapt the options from the list below and confirm with a save button</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-list>
|
||||
<mat-list-item>
|
||||
<div matLine fxLayout="row">
|
||||
<p fxFlex>Video player</p>
|
||||
<p fxFlex fxLayoutAlign="start center">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Select an option</mat-label>
|
||||
<mat-select formControlName="player">
|
||||
<mat-option *ngFor="let player of players" [value]="player.id">{{player.label}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-button color="primary" type="submit" [disabled]="settingsForm.pristine">
|
||||
<mat-icon>save</mat-icon> Save changes
|
||||
</button>
|
||||
<div fxFlex></div>
|
||||
<button mat-button color="primary" type="reset" (click)="backToHome()">
|
||||
<mat-icon>chevron_left</mat-icon> Back to home
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</form>
|
||||
@@ -0,0 +1,12 @@
|
||||
.card-header {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer !important;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
::ng-deep .mat-card {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {
|
||||
FormGroup,
|
||||
FormControl,
|
||||
FormBuilder,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { Router } from '@angular/router';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrls: ['./settings.component.scss'],
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
/** Player options */
|
||||
players = [
|
||||
{
|
||||
id: 'html5',
|
||||
label: ' HTML5 Video Player',
|
||||
},
|
||||
{
|
||||
id: 'videojs',
|
||||
label: ' VideoJs Player',
|
||||
},
|
||||
];
|
||||
|
||||
/** Settings form object */
|
||||
settingsForm: FormGroup;
|
||||
|
||||
/**
|
||||
* Creates an instance of SettingsComponent and injects some dependencies into the component
|
||||
* @param formBuilder
|
||||
* @param router
|
||||
* @param snackBar
|
||||
* @param storage
|
||||
*/
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private snackBar: MatSnackBar,
|
||||
private storage: StorageMap
|
||||
) {
|
||||
this.settingsForm = this.formBuilder.group({
|
||||
player: ['html5'], // default value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the config object from the browsers storage (indexed db)
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.storage.get('settings').subscribe((settings) => {
|
||||
if (settings) {
|
||||
this.settingsForm.setValue(settings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers on form submit and saves the config object to the indexed db store
|
||||
*/
|
||||
onSubmit(): void {
|
||||
this.storage.set('settings', this.settingsForm.value).subscribe(() => {
|
||||
this.settingsForm.markAsPristine();
|
||||
this.snackBar.open('Success! Configuration was saved.', null, {
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the applications homepage
|
||||
*/
|
||||
backToHome(): void {
|
||||
this.router.navigateByUrl('/', { skipLocationChange: true });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SettingsComponent } from './settings.component';
|
||||
import { SettingsRoutingModule } from './settings.routing';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SettingsRoutingModule, SharedModule],
|
||||
declarations: [SettingsComponent],
|
||||
})
|
||||
export class SettingsModule {}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: SettingsComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class SettingsRoutingModule {}
|
||||
@@ -0,0 +1,14 @@
|
||||
<div fxFlex="100px">
|
||||
<img src="./assets/icons/icon-tv-256.png" height="100" />
|
||||
</div>
|
||||
<div fxFlex="100" fxLayoutAlign="start center">
|
||||
<div fxLayout="column">
|
||||
<div fxFlex class="title">{{title}}</div>
|
||||
<div class="subtitle">{{subtitle}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="column" fxFlex="50px" fxLayoutAlign="center center">
|
||||
<button mat-icon-button (click)="openSettings()">
|
||||
<mat-icon>settings</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
File renamed without changes.
@@ -0,0 +1,26 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss'],
|
||||
})
|
||||
export class HeaderComponent {
|
||||
/** Title of the header */
|
||||
@Input() title: string;
|
||||
/** Subtitle of the header */
|
||||
@Input() subtitle: string;
|
||||
|
||||
/** Creates an instance of SettingsComponent and injects angulars router module
|
||||
* @param router angulars router
|
||||
*/
|
||||
constructor(private router: Router) {}
|
||||
|
||||
/**
|
||||
* Navigates to the settings page
|
||||
*/
|
||||
openSettings(): void {
|
||||
this.router.navigate(['/settings']);
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './page-not-found/page-not-found.component';
|
||||
export * from './header/header.component';
|
||||
@@ -3,19 +3,20 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { PageNotFoundComponent } from './components/';
|
||||
import { PageNotFoundComponent, HeaderComponent } from './components/';
|
||||
import { WebviewDirective } from './directives/';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MaterialModule } from 'app/material.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PageNotFoundComponent, WebviewDirective],
|
||||
declarations: [PageNotFoundComponent, HeaderComponent, WebviewDirective],
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslateModule,
|
||||
FormsModule,
|
||||
MaterialModule,
|
||||
FlexLayoutModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
exports: [
|
||||
TranslateModule,
|
||||
@@ -23,6 +24,8 @@ import { MaterialModule } from 'app/material.module';
|
||||
FormsModule,
|
||||
MaterialModule,
|
||||
FlexLayoutModule,
|
||||
HeaderComponent,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
})
|
||||
export class SharedModule {}
|
||||
Reference in new issue
Block a user