mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-29 07:33:19 +03:00
Completed notification functionality
Minor code cleanup
This commit is contained in:
@@ -2764,27 +2764,30 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
type: string
|
$ref: '#/components/schemas/NotificationType'
|
||||||
text:
|
|
||||||
type: string
|
|
||||||
uid:
|
uid:
|
||||||
type: string
|
type: string
|
||||||
|
user_uid:
|
||||||
|
type: string
|
||||||
action:
|
action:
|
||||||
$ref: '#/components/schemas/NotificationAction'
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/NotificationAction'
|
||||||
read:
|
read:
|
||||||
type: boolean
|
type: boolean
|
||||||
data:
|
data:
|
||||||
type: object
|
type: object
|
||||||
NotificationAction:
|
NotificationAction:
|
||||||
required:
|
type: string
|
||||||
- type
|
enum:
|
||||||
- icon
|
- play
|
||||||
type: object
|
- retry_download
|
||||||
properties:
|
- view_download_error
|
||||||
type:
|
NotificationType:
|
||||||
type: string
|
type: string
|
||||||
icon:
|
enum:
|
||||||
type: string
|
- download_complete
|
||||||
|
- download_error
|
||||||
BaseChangePermissionsRequest:
|
BaseChangePermissionsRequest:
|
||||||
required:
|
required:
|
||||||
- permission
|
- permission
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const { create } = require('xmlbuilder2');
|
|||||||
const categories_api = require('./categories');
|
const categories_api = require('./categories');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const db_api = require('./db');
|
const db_api = require('./db');
|
||||||
|
const notifications_api = require('./notifications');
|
||||||
|
|
||||||
const mutex = new Mutex();
|
const mutex = new Mutex();
|
||||||
let should_check_downloads = true;
|
let should_check_downloads = true;
|
||||||
@@ -341,6 +342,8 @@ async function downloadQueuedFile(download_uid) {
|
|||||||
// registers file in DB
|
// registers file in DB
|
||||||
const file_obj = await db_api.registerFileDB(full_file_path, type, download['user_uid'], category, download['sub_id'] ? download['sub_id'] : null, options.cropFileSettings);
|
const file_obj = await db_api.registerFileDB(full_file_path, type, download['user_uid'], category, download['sub_id'] ? download['sub_id'] : null, options.cropFileSettings);
|
||||||
|
|
||||||
|
notifications_api.sendDownloadNotification(file_obj, download['user_uid']);
|
||||||
|
|
||||||
file_objs.push(file_obj);
|
file_objs.push(file_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,32 @@
|
|||||||
const utils = require('./utils');
|
const { uuid } = require('uuidv4');
|
||||||
const logger = require('./logger');
|
|
||||||
const db_api = require('./db');
|
const db_api = require('./db');
|
||||||
|
|
||||||
exports.sendNotification = async () => {
|
exports.sendNotification = async (notification) => {
|
||||||
// TODO: hook into third party service
|
// TODO: hook into third party service
|
||||||
|
|
||||||
const notification = {}
|
|
||||||
|
|
||||||
await db_api.insertRecordIntoTable('notifications', notification);
|
await db_api.insertRecordIntoTable('notifications', notification);
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.sendDownloadNotification = async (file, user_uid) => {
|
||||||
|
const data = {file_uid: file.uid, file_title: file.title};
|
||||||
|
const notification = exports.createNotification('download_complete', ['play'], data, user_uid);
|
||||||
|
return await exports.sendNotification(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.sendDownloadErrorNotification = async (download, user_uid) => {
|
||||||
|
const data = {download_uid: download.uid, download_url: download.url};
|
||||||
|
const notification = exports.createNotification('download_error', ['view_download_error', 'retry_download'], data, user_uid);
|
||||||
|
return await exports.sendNotification(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createNotification = (type, actions, data, user_uid) => {
|
||||||
|
const notification = {
|
||||||
|
type: type,
|
||||||
|
actions: actions,
|
||||||
|
data: data,
|
||||||
|
user_uid: user_uid,
|
||||||
|
uid: uuid(),
|
||||||
|
read: false
|
||||||
|
}
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ export type { inline_response_200_15 } from './models/inline_response_200_15';
|
|||||||
export type { LoginRequest } from './models/LoginRequest';
|
export type { LoginRequest } from './models/LoginRequest';
|
||||||
export type { LoginResponse } from './models/LoginResponse';
|
export type { LoginResponse } from './models/LoginResponse';
|
||||||
export type { Notification } from './models/Notification';
|
export type { Notification } from './models/Notification';
|
||||||
export type { NotificationAction } from './models/NotificationAction';
|
export { NotificationAction } from './models/NotificationAction';
|
||||||
|
export { NotificationType } from './models/NotificationType';
|
||||||
export type { Playlist } from './models/Playlist';
|
export type { Playlist } from './models/Playlist';
|
||||||
export type { RegisterRequest } from './models/RegisterRequest';
|
export type { RegisterRequest } from './models/RegisterRequest';
|
||||||
export type { RegisterResponse } from './models/RegisterResponse';
|
export type { RegisterResponse } from './models/RegisterResponse';
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
import type { NotificationAction } from './NotificationAction';
|
import type { NotificationAction } from './NotificationAction';
|
||||||
|
import type { NotificationType } from './NotificationType';
|
||||||
|
|
||||||
export type Notification = {
|
export type Notification = {
|
||||||
type: string;
|
type: NotificationType;
|
||||||
text: string;
|
|
||||||
uid: string;
|
uid: string;
|
||||||
action?: NotificationAction;
|
user_uid?: string;
|
||||||
|
action?: Array<NotificationAction>;
|
||||||
read: boolean;
|
read: boolean;
|
||||||
data?: any;
|
data?: any;
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
export type NotificationAction = {
|
export enum NotificationAction {
|
||||||
type: string;
|
PLAY = 'play',
|
||||||
icon: string;
|
RETRY_DOWNLOAD = 'retry_download',
|
||||||
};
|
VIEW_DOWNLOAD_ERROR = 'view_download_error',
|
||||||
|
}
|
||||||
8
src/api-types/models/NotificationType.ts
Normal file
8
src/api-types/models/NotificationType.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
export enum NotificationType {
|
||||||
|
DOWNLOAD_COMPLETE = 'download_complete',
|
||||||
|
DOWNLOAD_ERROR = 'download_error',
|
||||||
|
}
|
||||||
@@ -11,7 +11,11 @@
|
|||||||
{{topBarTitle}}
|
{{topBarTitle}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-column" style="text-align: right; align-items: flex-end;">
|
<div class="flex-column" style="text-align: right; align-items: flex-end; display: inline-block">
|
||||||
|
<button [matMenuTriggerFor]="notificationsMenu" mat-icon-button><mat-icon [matBadge]="notification_count" matBadgeColor="warn" matBadgeSize="small" *ngIf="notification_count > 0">notifications</mat-icon><mat-icon *ngIf="notification_count === 0">notifications_none</mat-icon></button>
|
||||||
|
<mat-menu (close)="notificationMenuClosed()" #notificationsMenu="matMenu">
|
||||||
|
<app-notifications #notifications (notificationCount)="notificationCountUpdate($event)" (click)="$event.stopPropagation()"></app-notifications>
|
||||||
|
</mat-menu>
|
||||||
<button [matMenuTriggerFor]="menuSettings" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
<button [matMenuTriggerFor]="menuSettings" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||||
<mat-menu #menuSettings="matMenu">
|
<mat-menu #menuSettings="matMenu">
|
||||||
<button (click)="openProfileDialog()" *ngIf="postsService.isLoggedIn" mat-menu-item>
|
<button (click)="openProfileDialog()" *ngIf="postsService.isLoggedIn" mat-menu-item>
|
||||||
@@ -23,10 +27,6 @@
|
|||||||
<span i18n="Dark mode toggle label">Dark</span>
|
<span i18n="Dark mode toggle label">Dark</span>
|
||||||
<mat-slide-toggle class="theme-slide-toggle" [checked]="postsService.theme.key === 'dark'"></mat-slide-toggle>
|
<mat-slide-toggle class="theme-slide-toggle" [checked]="postsService.theme.key === 'dark'"></mat-slide-toggle>
|
||||||
</button>
|
</button>
|
||||||
<!-- <button *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('settings')))" (click)="openSettingsDialog()" mat-menu-item>
|
|
||||||
<mat-icon>settings</mat-icon>
|
|
||||||
<span i18n="Settings menu label">Settings</span>
|
|
||||||
</button> -->
|
|
||||||
<button (click)="openAboutDialog()" mat-menu-item>
|
<button (click)="openAboutDialog()" mat-menu-item>
|
||||||
<mat-icon>info</mat-icon>
|
<mat-icon>info</mat-icon>
|
||||||
<span i18n="About menu label">About</span>
|
<span i18n="About menu label">About</span>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { SettingsComponent } from './settings/settings.component';
|
|||||||
import { AboutDialogComponent } from './dialogs/about-dialog/about-dialog.component';
|
import { AboutDialogComponent } from './dialogs/about-dialog/about-dialog.component';
|
||||||
import { UserProfileDialogComponent } from './dialogs/user-profile-dialog/user-profile-dialog.component';
|
import { UserProfileDialogComponent } from './dialogs/user-profile-dialog/user-profile-dialog.component';
|
||||||
import { SetDefaultAdminDialogComponent } from './dialogs/set-default-admin-dialog/set-default-admin-dialog.component';
|
import { SetDefaultAdminDialogComponent } from './dialogs/set-default-admin-dialog/set-default-admin-dialog.component';
|
||||||
|
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@@ -45,9 +46,12 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
enableDownloadsManager = false;
|
enableDownloadsManager = false;
|
||||||
|
|
||||||
@ViewChild('sidenav') sidenav: MatSidenav;
|
@ViewChild('sidenav') sidenav: MatSidenav;
|
||||||
|
@ViewChild('notifications') notifications: NotificationsComponent;
|
||||||
@ViewChild('hamburgerMenu', { read: ElementRef }) hamburgerMenuButton: ElementRef;
|
@ViewChild('hamburgerMenu', { read: ElementRef }) hamburgerMenuButton: ElementRef;
|
||||||
navigator: string = null;
|
navigator: string = null;
|
||||||
|
|
||||||
|
notification_count = 0;
|
||||||
|
|
||||||
constructor(public postsService: PostsService, public snackBar: MatSnackBar, private dialog: MatDialog,
|
constructor(public postsService: PostsService, public snackBar: MatSnackBar, private dialog: MatDialog,
|
||||||
public router: Router, public overlayContainer: OverlayContainer, private elementRef: ElementRef) {
|
public router: Router, public overlayContainer: OverlayContainer, private elementRef: ElementRef) {
|
||||||
|
|
||||||
@@ -71,7 +75,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit(): void {
|
||||||
if (localStorage.getItem('theme')) {
|
if (localStorage.getItem('theme')) {
|
||||||
this.setTheme(localStorage.getItem('theme'));
|
this.setTheme(localStorage.getItem('theme'));
|
||||||
}
|
}
|
||||||
@@ -90,15 +94,15 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit(): void {
|
||||||
this.postsService.sidenav = this.sidenav;
|
this.postsService.sidenav = this.sidenav;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSidenav() {
|
toggleSidenav(): void {
|
||||||
this.sidenav.toggle();
|
this.sidenav.toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadConfig() {
|
loadConfig(): void {
|
||||||
// loading config
|
// loading config
|
||||||
this.topBarTitle = this.postsService.config['Extra']['title_top'];
|
this.topBarTitle = this.postsService.config['Extra']['title_top'];
|
||||||
const themingExists = this.postsService.config['Themes'];
|
const themingExists = this.postsService.config['Themes'];
|
||||||
@@ -164,7 +168,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
this.componentCssClass = theme;
|
this.componentCssClass = theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
flipTheme() {
|
flipTheme(): void {
|
||||||
if (this.postsService.theme.key === 'default') {
|
if (this.postsService.theme.key === 'default') {
|
||||||
this.setTheme('dark');
|
this.setTheme('dark');
|
||||||
} else if (this.postsService.theme.key === 'dark') {
|
} else if (this.postsService.theme.key === 'dark') {
|
||||||
@@ -172,17 +176,12 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
themeMenuItemClicked(event) {
|
themeMenuItemClicked(event): void {
|
||||||
this.flipTheme();
|
this.flipTheme();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSubscriptions() {
|
goBack(): void {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
goBack() {
|
|
||||||
if (!this.navigator) {
|
if (!this.navigator) {
|
||||||
this.router.navigate(['/home']);
|
this.router.navigate(['/home']);
|
||||||
} else {
|
} else {
|
||||||
@@ -190,23 +189,31 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openSettingsDialog() {
|
openSettingsDialog(): void {
|
||||||
const dialogRef = this.dialog.open(SettingsComponent, {
|
this.dialog.open(SettingsComponent, {
|
||||||
width: '80vw'
|
width: '80vw'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openAboutDialog() {
|
openAboutDialog(): void {
|
||||||
const dialogRef = this.dialog.open(AboutDialogComponent, {
|
this.dialog.open(AboutDialogComponent, {
|
||||||
width: '80vw'
|
width: '80vw'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openProfileDialog() {
|
openProfileDialog(): void {
|
||||||
const dialogRef = this.dialog.open(UserProfileDialogComponent, {
|
this.dialog.open(UserProfileDialogComponent, {
|
||||||
width: '60vw'
|
width: '60vw'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notificationCountUpdate(new_count: number): void {
|
||||||
|
this.notification_count = new_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationMenuClosed(): void {
|
||||||
|
this.notifications.setNotificationsToRead();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import { MatPaginatorModule } from '@angular/material/paginator';
|
|||||||
import { MatSortModule } from '@angular/material/sort';
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { ClipboardModule } from '@angular/cdk/clipboard';
|
import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||||
import { TextFieldModule } from '@angular/cdk/text-field';
|
import { TextFieldModule } from '@angular/cdk/text-field';
|
||||||
@@ -51,7 +53,6 @@ import { SubscribeDialogComponent } from './dialogs/subscribe-dialog/subscribe-d
|
|||||||
import { SubscriptionComponent } from './subscription//subscription/subscription.component';
|
import { SubscriptionComponent } from './subscription//subscription/subscription.component';
|
||||||
import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component';
|
import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component';
|
||||||
import { SettingsComponent } from './settings/settings.component';
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
import { MatChipsModule } from '@angular/material/chips';
|
|
||||||
import { NgxFileDropModule } from 'ngx-file-drop';
|
import { NgxFileDropModule } from 'ngx-file-drop';
|
||||||
import { AvatarModule } from 'ngx-avatars';
|
import { AvatarModule } from 'ngx-avatars';
|
||||||
import { ContentLoaderModule } from '@ngneat/content-loader';
|
import { ContentLoaderModule } from '@ngneat/content-loader';
|
||||||
@@ -88,6 +89,7 @@ import { TasksComponent } from './components/tasks/tasks.component';
|
|||||||
import { UpdateTaskScheduleDialogComponent } from './dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component';
|
import { UpdateTaskScheduleDialogComponent } from './dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component';
|
||||||
import { RestoreDbDialogComponent } from './dialogs/restore-db-dialog/restore-db-dialog.component';
|
import { RestoreDbDialogComponent } from './dialogs/restore-db-dialog/restore-db-dialog.component';
|
||||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||||
|
import { NotificationsListComponent } from './components/notifications-list/notifications-list.component';
|
||||||
|
|
||||||
registerLocaleData(es, 'es');
|
registerLocaleData(es, 'es');
|
||||||
|
|
||||||
@@ -134,7 +136,8 @@ registerLocaleData(es, 'es');
|
|||||||
TasksComponent,
|
TasksComponent,
|
||||||
UpdateTaskScheduleDialogComponent,
|
UpdateTaskScheduleDialogComponent,
|
||||||
RestoreDbDialogComponent,
|
RestoreDbDialogComponent,
|
||||||
NotificationsComponent
|
NotificationsComponent,
|
||||||
|
NotificationsListComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -172,6 +175,7 @@ registerLocaleData(es, 'es');
|
|||||||
MatTableModule,
|
MatTableModule,
|
||||||
MatDatepickerModule,
|
MatDatepickerModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
|
MatBadgeModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
TextFieldModule,
|
TextFieldModule,
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div *ngFor="let notification of notifications; let i = index;">
|
||||||
|
<mat-divider class="notification-divider"></mat-divider>
|
||||||
|
<div style="display: inline-block">
|
||||||
|
<ng-container *ngIf="NOTIFICATION_PREFIX[notification.type]">
|
||||||
|
{{NOTIFICATION_PREFIX[notification.type]}}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="NOTIFICATION_SUFFIX_KEY[notification.type]">
|
||||||
|
{{notification['data'][NOTIFICATION_SUFFIX_KEY[notification.type]]}}
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div style="margin-left: 10px; float: right;" *ngIf="notification.actions?.length > 0">
|
||||||
|
<button (click)="emitDeleteNotification(notification.uid)" mat-icon-button><mat-icon>close</mat-icon></button>
|
||||||
|
<span *ngFor="let action of notification.actions">
|
||||||
|
<button [matTooltip]="NOTIFICATION_ACTION_TO_STRING[action]" (click)="emitNotificationAction(notification)" mat-icon-button><mat-icon>{{NOTIFICATION_ICON[action]}}</mat-icon></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.notification-divider {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NotificationsListComponent } from './notifications-list.component';
|
||||||
|
|
||||||
|
describe('NotificationsListComponent', () => {
|
||||||
|
let component: NotificationsListComponent;
|
||||||
|
let fixture: ComponentFixture<NotificationsListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ NotificationsListComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(NotificationsListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { Notification } from 'api-types';
|
||||||
|
import { NotificationAction } from 'api-types/models/NotificationAction';
|
||||||
|
import { NotificationType } from 'api-types/models/NotificationType';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-notifications-list',
|
||||||
|
templateUrl: './notifications-list.component.html',
|
||||||
|
styleUrls: ['./notifications-list.component.scss']
|
||||||
|
})
|
||||||
|
export class NotificationsListComponent {
|
||||||
|
@Input() notifications = null;
|
||||||
|
@Output() deleteNotification = new EventEmitter<string>();
|
||||||
|
@Output() notificationAction = new EventEmitter<{notification: Notification, action: NotificationAction}>();
|
||||||
|
|
||||||
|
NOTIFICATION_PREFIX: { [key in NotificationType]: string } = {
|
||||||
|
download_complete: $localize`Finished downloading:`,
|
||||||
|
download_error: $localize`Download failed:`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attaches string to the end of the notification text
|
||||||
|
NOTIFICATION_SUFFIX_KEY: { [key in NotificationType]: string } = {
|
||||||
|
download_complete: 'file_title',
|
||||||
|
download_error: 'file_url'
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTIFICATION_ACTION_TO_STRING: { [key in NotificationAction]: string } = {
|
||||||
|
play: $localize`Play`,
|
||||||
|
retry_download: $localize`Retry download`,
|
||||||
|
view_download_error: $localize`View error`
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTIFICATION_COLOR: { [key in NotificationAction]: string } = {
|
||||||
|
play: 'primary',
|
||||||
|
retry_download: 'primary',
|
||||||
|
view_download_error: 'warn'
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTIFICATION_ICON: { [key in NotificationAction]: string } = {
|
||||||
|
play: 'smart_display',
|
||||||
|
retry_download: 'restart_alt',
|
||||||
|
view_download_error: 'warning'
|
||||||
|
}
|
||||||
|
|
||||||
|
emitNotificationAction(notification: Notification, action: NotificationAction): void {
|
||||||
|
this.notificationAction.emit({notification: notification, action: action});
|
||||||
|
}
|
||||||
|
|
||||||
|
emitDeleteNotification(uid: string): void {
|
||||||
|
this.deleteNotification.emit(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
.notification-divider {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-title {
|
.notification-title {
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|||||||
@@ -1,32 +1,10 @@
|
|||||||
<h4 *ngIf="notifications !== null && notifications.length === 0 && read_notifications.length === 0" style="text-align: center; margin: 10px;">No notifications available</h4>
|
<h4 *ngIf="notifications !== null && notifications.length === 0 && read_notifications.length === 0" style="text-align: center; margin: 10px;" i18n="No notifications available">No notifications available</h4>
|
||||||
<div style="margin: 10px;" *ngIf="notifications?.length > 0">
|
<div style="margin: 10px;" *ngIf="notifications?.length > 0">
|
||||||
<h4 class="notification-title">New notifications</h4>
|
<h4 class="notification-title" i18n="New notifications">New notifications</h4>
|
||||||
<div *ngFor="let notification of appService.notifications; let i = index;">
|
<app-notifications-list (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="notifications"></app-notifications-list>
|
||||||
<mat-divider class="notification-divider"></mat-divider>
|
|
||||||
<div style="display: inline-block;">
|
|
||||||
<button (click)="deleteNotification(notification.id, i, false)" mat-icon-button><mat-icon>close</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
<div style="display: inline-block">
|
|
||||||
{{notification.title}}
|
|
||||||
</div>
|
|
||||||
<div style="margin-left: 10px; float: right;" *ngIf="notification.action">
|
|
||||||
<button (click)="notificationAction(notification)" [color]="notification.action.warn ? 'warn' : 'primary'" mat-raised-button>{{notification.action.title}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin: 10px;" *ngIf="read_notifications?.length > 0">
|
<div style="margin: 10px;" *ngIf="read_notifications?.length > 0">
|
||||||
<h4 class="notification-title">Old notifications</h4>
|
<h4 class="notification-title" i18n="Old notifications">Old notifications</h4>
|
||||||
<div *ngFor="let notification of appService.read_notifications; let i = index">
|
<app-notifications-list (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="read_notifications"></app-notifications-list>
|
||||||
<mat-divider class="notification-divider"></mat-divider>
|
|
||||||
<div style="display: inline-block;">
|
|
||||||
<button (click)="deleteNotification(notification.id, i, true)" mat-icon-button><mat-icon>close</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
<div style="display: inline-block">
|
|
||||||
{{notification.title}}
|
|
||||||
</div>
|
|
||||||
<div style="margin-left: 10px; float: right" *ngIf="notification.action">
|
|
||||||
<button (click)="notificationAction(notification)" [color]="notification.action.warn ? 'warn' : 'primary'" mat-raised-button>{{notification.action.title}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import { Component, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenuTrigger } from '@angular/material/menu';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { PostsService } from 'app/posts.services';
|
import { PostsService } from 'app/posts.services';
|
||||||
import { Notification } from 'api-types';
|
import { Notification } from 'api-types';
|
||||||
|
import { NotificationAction } from 'api-types/models/NotificationAction';
|
||||||
// TODO: fill this out
|
|
||||||
const NOTIFICATION_ACTION_TO_STRING = {}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-notifications',
|
selector: 'app-notifications',
|
||||||
@@ -17,7 +15,6 @@ export class NotificationsComponent implements OnInit {
|
|||||||
notifications = null;
|
notifications = null;
|
||||||
read_notifications = null;
|
read_notifications = null;
|
||||||
|
|
||||||
@Input() menu: MatMenuTrigger;
|
|
||||||
@Output() notificationCount = new EventEmitter<number>();
|
@Output() notificationCount = new EventEmitter<number>();
|
||||||
|
|
||||||
constructor(public postsService: PostsService, private router: Router, private elRef: ElementRef) { }
|
constructor(public postsService: PostsService, private router: Router, private elRef: ElementRef) { }
|
||||||
@@ -37,21 +34,35 @@ export class NotificationsComponent implements OnInit {
|
|||||||
|
|
||||||
getNotifications(): void {
|
getNotifications(): void {
|
||||||
this.postsService.getNotifications().subscribe(res => {
|
this.postsService.getNotifications().subscribe(res => {
|
||||||
this.notifications = res['notifications'].filter(notification => notification.read == false);
|
this.notifications = res['notifications'].filter(notification => !notification.read);
|
||||||
this.read_notifications = res['notifications'].filter(notification => notification.read == true);
|
this.read_notifications = res['notifications'].filter(notification => notification.read);
|
||||||
this.notificationCount.emit(this.notifications.length);
|
this.notificationCount.emit(this.notifications.length);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationAction(notification: Notification): void {
|
notificationAction(action_info: {notification: Notification, action: NotificationAction}): void {
|
||||||
// TODO: implement
|
switch (action_info['action']) {
|
||||||
|
case NotificationAction.PLAY:
|
||||||
|
this.router.navigate(['player', {uid: action_info['notification']['data']['file_uid']}]);
|
||||||
|
break;
|
||||||
|
case NotificationAction.VIEW_DOWNLOAD_ERROR:
|
||||||
|
this.router.navigate(['downloads']);
|
||||||
|
break;
|
||||||
|
case NotificationAction.RETRY_DOWNLOAD:
|
||||||
|
this.postsService.restartDownload(action_info['notification']['data']['download_uid'])
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Notification action ${action_info['action']} does not exist!`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteNotification(uid: string, index: number): void {
|
deleteNotification(uid: string): void {
|
||||||
this.postsService.deleteNotification(uid).subscribe(res => {
|
this.postsService.deleteNotification(uid).subscribe(res => {
|
||||||
console.log(res);
|
this.notifications.filter(notification => notification['uid'] !== uid);
|
||||||
// TODO: remove from array
|
this.read_notifications.filter(read_notification => read_notification['uid'] !== uid);
|
||||||
this.notificationCount.emit(this.notifications.length);
|
this.notificationCount.emit(this.notifications.length);
|
||||||
|
this.getNotifications();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,4 +84,10 @@ export class NotificationsComponent implements OnInit {
|
|||||||
this.notificationCount.emit(0);
|
this.notificationCount.emit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notificationMenuClosed(): void {
|
||||||
|
if (this.notifications.length > 0) {
|
||||||
|
this.setNotificationsToRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user