From b05763135edba06eee38d99d09e3f1c66da1e2f8 Mon Sep 17 00:00:00 2001 From: sercan Date: Fri, 30 Apr 2021 19:55:37 +0300 Subject: [PATCH 01/10] (apps/chat) Adjustments and optimizations for smaller devices --- .../apps/chat/chats/chats.component.html | 8 +++--- .../conversation/conversation.component.html | 4 +-- .../conversation/conversation.component.ts | 25 +++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/app/modules/admin/apps/chat/chats/chats.component.html b/src/app/modules/admin/apps/chat/chats/chats.component.html index bcab6292..440c18a7 100644 --- a/src/app/modules/admin/apps/chat/chats/chats.component.html +++ b/src/app/modules/admin/apps/chat/chats/chats.component.html @@ -6,7 +6,7 @@ @@ -28,7 +28,7 @@ -
+
@@ -176,9 +176,9 @@
+ 'hidden lg:flex': !selectedChat || !selectedChat.id}">
diff --git a/src/app/modules/admin/apps/chat/conversation/conversation.component.html b/src/app/modules/admin/apps/chat/conversation/conversation.component.html index 9c3fa082..fd1cce18 100644 --- a/src/app/modules/admin/apps/chat/conversation/conversation.component.html +++ b/src/app/modules/admin/apps/chat/conversation/conversation.component.html @@ -8,9 +8,9 @@ diff --git a/src/app/modules/admin/apps/chat/conversation/conversation.component.ts b/src/app/modules/admin/apps/chat/conversation/conversation.component.ts index 89ce4752..9bd0c4f9 100644 --- a/src/app/modules/admin/apps/chat/conversation/conversation.component.ts +++ b/src/app/modules/admin/apps/chat/conversation/conversation.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { Chat } from 'app/modules/admin/apps/chat/chat.types'; import { ChatService } from 'app/modules/admin/apps/chat/chat.service'; @@ -14,6 +15,7 @@ export class ConversationComponent implements OnInit, OnDestroy { @ViewChild('messageInput') messageInput: ElementRef; chat: Chat; + drawerMode: 'over' | 'side' = 'side'; drawerOpened: boolean = false; private _unsubscribeAll: Subject = new Subject(); @@ -23,6 +25,7 @@ export class ConversationComponent implements OnInit, OnDestroy constructor( private _changeDetectorRef: ChangeDetectorRef, private _chatService: ChatService, + private _fuseMediaWatcherService: FuseMediaWatcherService, private _ngZone: NgZone ) { @@ -43,6 +46,25 @@ export class ConversationComponent implements OnInit, OnDestroy .subscribe((chat: Chat) => { this.chat = chat; + // Mark for check + this._changeDetectorRef.markForCheck(); + }); + + // Subscribe to media changes + this._fuseMediaWatcherService.onMediaChange$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({matchingAliases}) => { + + // Set the drawerMode if the given breakpoint is active + if ( matchingAliases.includes('lg') ) + { + this.drawerMode = 'side'; + } + else + { + this.drawerMode = 'over'; + } + // Mark for check this._changeDetectorRef.markForCheck(); }); @@ -81,6 +103,9 @@ export class ConversationComponent implements OnInit, OnDestroy { this._chatService.resetChat(); + // Close the contact info in case it's opened + this.drawerOpened = false; + // Mark for check this._changeDetectorRef.markForCheck(); } From e7a1d386a665803d1a4e6b0377284c244f1871c7 Mon Sep 17 00:00:00 2001 From: sercan Date: Mon, 3 May 2021 18:47:57 +0300 Subject: [PATCH 02/10] (fuse/masonry) Added a new component (and its docs) for creating Masonry layouts --- src/@fuse/components/masonry/index.ts | 1 + .../components/masonry/masonry.component.html | 3 + .../components/masonry/masonry.component.scss | 0 .../components/masonry/masonry.component.ts | 97 +++++ .../components/masonry/masonry.module.ts | 18 + src/@fuse/components/masonry/public-api.ts | 2 + .../components/masonry/masonry.component.html | 334 ++++++++++++++++++ .../components/masonry/masonry.component.ts | 81 +++++ .../core-features/core-features.component.ts | 6 + .../core-features/core-features.module.ts | 4 + .../core-features/core-features.routing.ts | 5 + 11 files changed, 551 insertions(+) create mode 100644 src/@fuse/components/masonry/index.ts create mode 100644 src/@fuse/components/masonry/masonry.component.html create mode 100644 src/@fuse/components/masonry/masonry.component.scss create mode 100644 src/@fuse/components/masonry/masonry.component.ts create mode 100644 src/@fuse/components/masonry/masonry.module.ts create mode 100644 src/@fuse/components/masonry/public-api.ts create mode 100644 src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html create mode 100644 src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts diff --git a/src/@fuse/components/masonry/index.ts b/src/@fuse/components/masonry/index.ts new file mode 100644 index 00000000..ac978a78 --- /dev/null +++ b/src/@fuse/components/masonry/index.ts @@ -0,0 +1 @@ +export * from '@fuse/components/card/public-api'; diff --git a/src/@fuse/components/masonry/masonry.component.html b/src/@fuse/components/masonry/masonry.component.html new file mode 100644 index 00000000..4b0a6719 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/@fuse/components/masonry/masonry.component.scss b/src/@fuse/components/masonry/masonry.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/@fuse/components/masonry/masonry.component.ts b/src/@fuse/components/masonry/masonry.component.ts new file mode 100644 index 00000000..b8ae5043 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.component.ts @@ -0,0 +1,97 @@ +import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { FuseAnimations } from '@fuse/animations'; +import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; + +@Component({ + selector : 'fuse-masonry', + templateUrl : './masonry.component.html', + styleUrls : ['./masonry.component.scss'], + encapsulation: ViewEncapsulation.None, + animations : FuseAnimations, + exportAs : 'fuseMasonry' +}) +export class FuseMasonryComponent implements OnChanges, AfterViewInit +{ + @Input() columnsTemplate: TemplateRef; + @Input() columns: number; + @Input() items: any[] = []; + distributedColumns: any[] = []; + + /** + * Constructor + */ + constructor(private _fuseMediaWatcherService: FuseMediaWatcherService) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On changes + * + * @param changes + */ + ngOnChanges(changes: SimpleChanges): void + { + // Columns + if ( 'columns' in changes ) + { + if ( changes.columns.isFirstChange() ) + { + return; + } + + // Distribute the items + this._distributeItems(); + } + + // Items + if ( 'items' in changes ) + { + if ( changes.columns.isFirstChange() ) + { + return; + } + + // Distribute the items + this._distributeItems(); + } + } + + /** + * After view init + */ + ngAfterViewInit(): void + { + // Distribute the items for the first time + this._distributeItems(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Distribute items into columns + */ + private _distributeItems(): void + { + // Return an empty array if there are no items + if ( this.items.length === 0 ) + { + this.distributedColumns = []; + return; + } + + // Prepare the distributed columns array + this.distributedColumns = Array.from(Array(this.columns), item => ({items: []})); + + // Distribute the items to columns + for ( let i = 0; i < this.items.length; i++ ) + { + this.distributedColumns[i % this.columns].items.push(this.items[i]); + } + } +} diff --git a/src/@fuse/components/masonry/masonry.module.ts b/src/@fuse/components/masonry/masonry.module.ts new file mode 100644 index 00000000..651c5510 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FuseMasonryComponent } from '@fuse/components/masonry/masonry.component'; + +@NgModule({ + declarations: [ + FuseMasonryComponent + ], + imports : [ + CommonModule + ], + exports : [ + FuseMasonryComponent + ] +}) +export class FuseMasonryModule +{ +} diff --git a/src/@fuse/components/masonry/public-api.ts b/src/@fuse/components/masonry/public-api.ts new file mode 100644 index 00000000..e074c48c --- /dev/null +++ b/src/@fuse/components/masonry/public-api.ts @@ -0,0 +1,2 @@ +export * from '@fuse/components/masonry/masonry.component'; +export * from '@fuse/components/masonry/masonry.module'; diff --git a/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html new file mode 100644 index 00000000..04fd53cf --- /dev/null +++ b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html @@ -0,0 +1,334 @@ +
+ + +
+
+ +
+ + +
+ + Components +
+
+ +
+

+ Masonry +

+
+
+ +
+ +
+ +

+ fuse-masonry is a basic Masonry layout component to show items in Masonry grid layout. Since it doesn't use any other third party library or complex calculations to + keep everything smooth and fast; +

+
    +
  • It does NOT work with elements with different widths +
  • It does NOT sort items based on their heights (This here is the real performance killer)
  • +
+

+ If you don't need to mix and match different width items and don't need to sort items based on their heights, fuse-masonry will do the job for you and it will do it very smoothly. +

+

+ Exported as: fuseMasonry +

+ +

Module

+ + +

Properties

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionDefault
+
@Input()
+
columnsTemplate: TemplateRef
+
+ Column template for masonry component to lay out. + + undefined +
+
@Input()
+
columns: number
+
+ Number of columns to create + + undefined +
+
@Input()
+
items: any[]
+
+ Items to distribute into columns + + [] +
+
+ +

Methods

+

This component doesn't have any public methods.

+ +

Usage

+

+ fuse-masonry doesn't provide a default template or styling to the items. You can think of it as a helper function but in a component form: +

+ +
+ +
+
Example
+
+ + + + + + + +
+
+ + + + + +
+ + +
+ {{item}} +
+
+
+
+
+
+
+ +
+ +
+ + + + + + + + + +
+ +
+ +

Responsive

+

+ fuse-masonry doesn't provide a way of changing the column count depending on breakpoints but this can easily be handled by + combining fuse-masonry with FuseMediaWatcherService: +

+ +
+ +
+
Example
+
+ + + + + + + +
+
+ + + + + + +
+ + +
+ {{item}} +
+
+
+
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
diff --git a/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts new file mode 100644 index 00000000..1d65a474 --- /dev/null +++ b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts @@ -0,0 +1,81 @@ +import { Component, OnInit } from '@angular/core'; +import { CoreFeaturesComponent } from 'app/modules/admin/docs/core-features/core-features.component'; +import { Subject } from 'rxjs'; +import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector : 'masonry', + templateUrl: './masonry.component.html', + styles : [''] +}) +export class MasonryComponent implements OnInit +{ + columns: number = 4; + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _coreFeaturesComponent: CoreFeaturesComponent, + private _fuseMediaWatcherService: FuseMediaWatcherService + ) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void + { + // Subscribe to media changes + this._fuseMediaWatcherService.onMediaChange$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({matchingAliases}) => { + + // Set the masonry columns + // + // This if block structured in a way so that only the + // biggest matching alias will be used to set the column + // count. + if ( matchingAliases.includes('xl') ) + { + this.columns = 5; + } + else if ( matchingAliases.includes('lg') ) + { + this.columns = 4; + } + else if ( matchingAliases.includes('md') ) + { + this.columns = 3; + } + else if ( matchingAliases.includes('sm') ) + { + this.columns = 2; + } + else + { + this.columns = 1; + } + }); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Toggle the drawer + */ + toggleDrawer(): void + { + // Toggle the drawer + this._coreFeaturesComponent.matDrawer.toggle(); + } +} diff --git a/src/app/modules/admin/docs/core-features/core-features.component.ts b/src/app/modules/admin/docs/core-features/core-features.component.ts index 23637cec..7d0d10cd 100644 --- a/src/app/modules/admin/docs/core-features/core-features.component.ts +++ b/src/app/modules/admin/docs/core-features/core-features.component.ts @@ -77,6 +77,12 @@ export class CoreFeaturesComponent implements OnInit, OnDestroy type : 'basic', link : '/docs/core-features/components/highlight' }, + { + id : 'core-features.components.masonry', + title: 'Masonry', + type : 'basic', + link : '/docs/core-features/components/masonry' + }, { id : 'core-features.components.navigation', title: 'Navigation', diff --git a/src/app/modules/admin/docs/core-features/core-features.module.ts b/src/app/modules/admin/docs/core-features/core-features.module.ts index 07a2801d..d927298f 100644 --- a/src/app/modules/admin/docs/core-features/core-features.module.ts +++ b/src/app/modules/admin/docs/core-features/core-features.module.ts @@ -10,6 +10,7 @@ import { FuseDateRangeModule } from '@fuse/components/date-range'; import { FuseDrawerModule } from '@fuse/components/drawer'; import { FuseHighlightModule } from '@fuse/components/highlight'; import { FuseAlertModule } from '@fuse/components/alert'; +import { FuseMasonryModule } from '@fuse/components/masonry/masonry.module'; import { FuseNavigationModule } from '@fuse/components/navigation'; import { FuseScrollResetModule } from '@fuse/directives/scroll-reset'; import { SharedModule } from 'app/shared/shared.module'; @@ -21,6 +22,7 @@ import { DateRangeComponent } from 'app/modules/admin/docs/core-features/compone import { DrawerComponent } from 'app/modules/admin/docs/core-features/components/drawer/drawer.component'; import { HighlightComponent } from 'app/modules/admin/docs/core-features/components/highlight/highlight.component'; import { NavigationComponent } from 'app/modules/admin/docs/core-features/components/navigation/navigation.component'; +import { MasonryComponent } from 'app/modules/admin/docs/core-features/components/masonry/masonry.component'; import { AutogrowComponent } from 'app/modules/admin/docs/core-features/directives/autogrow/autogrow.component'; import { ScrollbarComponent } from 'app/modules/admin/docs/core-features/directives/scrollbar/scrollbar.component'; import { ScrollResetComponent } from 'app/modules/admin/docs/core-features/directives/scroll-reset/scroll-reset.component'; @@ -40,6 +42,7 @@ import { coreFeaturesRoutes } from 'app/modules/admin/docs/core-features/core-fe DateRangeComponent, DrawerComponent, HighlightComponent, + MasonryComponent, NavigationComponent, AutogrowComponent, ScrollbarComponent, @@ -62,6 +65,7 @@ import { coreFeaturesRoutes } from 'app/modules/admin/docs/core-features/core-fe FuseDateRangeModule, FuseDrawerModule, FuseHighlightModule, + FuseMasonryModule, FuseNavigationModule, FuseScrollResetModule, SharedModule diff --git a/src/app/modules/admin/docs/core-features/core-features.routing.ts b/src/app/modules/admin/docs/core-features/core-features.routing.ts index d7d39b35..0ab76c3f 100644 --- a/src/app/modules/admin/docs/core-features/core-features.routing.ts +++ b/src/app/modules/admin/docs/core-features/core-features.routing.ts @@ -6,6 +6,7 @@ import { CardComponent } from 'app/modules/admin/docs/core-features/components/c import { DateRangeComponent } from 'app/modules/admin/docs/core-features/components/date-range/date-range.component'; import { DrawerComponent } from 'app/modules/admin/docs/core-features/components/drawer/drawer.component'; import { HighlightComponent } from 'app/modules/admin/docs/core-features/components/highlight/highlight.component'; +import { MasonryComponent } from 'app/modules/admin/docs/core-features/components/masonry/masonry.component'; import { NavigationComponent } from 'app/modules/admin/docs/core-features/components/navigation/navigation.component'; import { AutogrowComponent } from 'app/modules/admin/docs/core-features/directives/autogrow/autogrow.component'; import { ScrollbarComponent } from 'app/modules/admin/docs/core-features/directives/scrollbar/scrollbar.component'; @@ -63,6 +64,10 @@ export const coreFeaturesRoutes: Route[] = [ path : 'highlight', component: HighlightComponent }, + { + path : 'masonry', + component: MasonryComponent + }, { path : 'navigation', component: NavigationComponent From 623b43a94c7a5fcb17f7ede1afb2e8d7a0d0791d Mon Sep 17 00:00:00 2001 From: sercan Date: Mon, 3 May 2021 18:49:00 +0300 Subject: [PATCH 03/10] (fuse/styles) Fixed: fuse-highlight doesn't have a margin around in Docs --- src/@fuse/styles/components/example-viewer.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/@fuse/styles/components/example-viewer.scss b/src/@fuse/styles/components/example-viewer.scss index cdee5c7e..fe0fd303 100644 --- a/src/@fuse/styles/components/example-viewer.scss +++ b/src/@fuse/styles/components/example-viewer.scss @@ -36,7 +36,6 @@ .mat-tab-body-content { .fuse-highlight { - margin: -24px; pre { margin: 0; From e4442d683b0eb38f78fe6ea1bd0e153e8ba5a0ee Mon Sep 17 00:00:00 2001 From: sercan Date: Wed, 5 May 2021 17:27:12 +0300 Subject: [PATCH 04/10] (apps/tasks) Tweaked the hover color on task list for better consistency --- src/app/modules/admin/apps/tasks/list/list.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/admin/apps/tasks/list/list.component.html b/src/app/modules/admin/apps/tasks/list/list.component.html index 58996f66..a1dcbef4 100644 --- a/src/app/modules/admin/apps/tasks/list/list.component.html +++ b/src/app/modules/admin/apps/tasks/list/list.component.html @@ -65,7 +65,7 @@
Date: Wed, 5 May 2021 17:27:39 +0300 Subject: [PATCH 05/10] (apps/mailbox) Use shadow on threads for better consistency --- .../modules/admin/apps/mailbox/details/details.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/admin/apps/mailbox/details/details.component.html b/src/app/modules/admin/apps/mailbox/details/details.component.html index 755aad04..ceb40866 100644 --- a/src/app/modules/admin/apps/mailbox/details/details.component.html +++ b/src/app/modules/admin/apps/mailbox/details/details.component.html @@ -132,7 +132,7 @@ fuseScrollReset> -
+
From b0f1e1de95eb2913dd6bd2a707a7ec37f2d5605a Mon Sep 17 00:00:00 2001 From: sercan Date: Wed, 5 May 2021 17:52:10 +0300 Subject: [PATCH 06/10] (apps/mailbox) App title font size adjustment for better consistency --- .../modules/admin/apps/mailbox/sidebar/sidebar.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/admin/apps/mailbox/sidebar/sidebar.component.html b/src/app/modules/admin/apps/mailbox/sidebar/sidebar.component.html index 93eb5b02..fe322b40 100644 --- a/src/app/modules/admin/apps/mailbox/sidebar/sidebar.component.html +++ b/src/app/modules/admin/apps/mailbox/sidebar/sidebar.component.html @@ -1,6 +1,6 @@
-
Mailbox
+
Mailbox
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+
+
+ + +
+
+
+ + +
+ +
+
+ {{label.title}} +
+ +
+
+
+
+ + +
+ + +
+
+ + +
+
+ +
+ + +
+ + + + + + + + + + + + + + + +
+ + +
+
+
+ + +
diff --git a/src/app/modules/admin/apps/notes/details/details.component.ts b/src/app/modules/admin/apps/notes/details/details.component.ts new file mode 100644 index 00000000..9be71363 --- /dev/null +++ b/src/app/modules/admin/apps/notes/details/details.component.ts @@ -0,0 +1,346 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { debounceTime, map, switchMap, takeUntil, tap } from 'rxjs/operators'; +import { Observable, of, Subject } from 'rxjs'; +import { NotesService } from 'app/modules/admin/apps/notes/notes.service'; +import { Label, Note, Task } from 'app/modules/admin/apps/notes/notes.types'; + +@Component({ + selector : 'notes-details', + templateUrl : './details.component.html', + encapsulation : ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class NotesDetailsComponent implements OnInit, OnDestroy +{ + note$: Observable; + labels$: Observable; + + noteChanged: Subject = new Subject(); + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _changeDetectorRef: ChangeDetectorRef, + @Inject(MAT_DIALOG_DATA) private _data: { note: Note }, + private _notesService: NotesService, + private _matDialogRef: MatDialogRef + ) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void + { + // Edit + if ( this._data.note.id ) + { + // Request the data from the server + this._notesService.getNoteById(this._data.note.id).subscribe(); + + // Get the note + this.note$ = this._notesService.note$; + } + // Add + else + { + // Create an empty note + const note = { + id : null, + title : '', + content : '', + tasks : null, + image : null, + reminder : null, + labels : [], + archived : false, + createdAt: null, + updatedAt: null + }; + + this.note$ = of(note); + } + + // Get the labels + this.labels$ = this._notesService.labels$; + + // Subscribe to note updates + this.noteChanged + .pipe( + takeUntil(this._unsubscribeAll), + debounceTime(500), + switchMap((note) => this._notesService.updateNote(note))) + .subscribe(() => { + + // Mark for check + this._changeDetectorRef.markForCheck(); + }); + } + + /** + * On destroy + */ + ngOnDestroy(): void + { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(); + this._unsubscribeAll.complete(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Create a new note + * + * @param note + */ + createNote(note: Note): void + { + this._notesService.createNote(note).pipe( + map(() => { + // Get the note + this.note$ = this._notesService.note$; + })).subscribe(); + } + + /** + * Upload image to given note + * + * @param note + * @param fileList + */ + uploadImage(note: Note, fileList: FileList): void + { + // Return if canceled + if ( !fileList.length ) + { + return; + } + + const allowedTypes = ['image/jpeg', 'image/png']; + const file = fileList[0]; + + // Return if the file is not allowed + if ( !allowedTypes.includes(file.type) ) + { + return; + } + + this._readAsDataURL(file).then((data) => { + + // Update the image + note.image = data; + + // Update the note + this.noteChanged.next(note); + }); + } + + /** + * Remove the image on the given note + * + * @param note + */ + removeImage(note: Note): void + { + note.image = null; + + // Update the note + this.noteChanged.next(note); + } + + /** + * Add an empty tasks array to note + * + * @param note + */ + addTasksToNote(note): void + { + if ( !note.tasks ) + { + note.tasks = []; + } + } + + /** + * Add task to the given note + * + * @param note + * @param task + */ + addTaskToNote(note: Note, task: string): void + { + if ( task.trim() === '' ) + { + return; + } + + // Add the task + this._notesService.addTask(note, task).subscribe(); + } + + /** + * Remove the given task from given note + * + * @param note + * @param task + */ + removeTaskFromNote(note: Note, task: Task): void + { + // Remove the task + note.tasks = note.tasks.filter((item) => item.id !== task.id); + + // Update the note + this.noteChanged.next(note); + } + + /** + * Update the given task on the given note + * + * @param note + * @param task + */ + updateTaskOnNote(note: Note, task: Task): void + { + // If the task is already available on the item + if ( task.id ) + { + // Update the note + this.noteChanged.next(note); + } + } + + /** + * Is the given note has the given label + * + * @param note + * @param label + */ + isNoteHasLabel(note: Note, label: Label): boolean + { + return !!note.labels.find((item) => item.id === label.id); + } + + /** + * Toggle the given label on the given note + * + * @param note + * @param label + */ + toggleLabelOnNote(note: Note, label: Label): void + { + // If the note already has the label + if ( this.isNoteHasLabel(note, label) ) + { + note.labels = note.labels.filter((item) => item.id !== label.id); + } + // Otherwise + else + { + note.labels.push(label); + } + + // Update the note + this.noteChanged.next(note); + } + + /** + * Toggle archived status on the given note + * + * @param note + */ + toggleArchiveOnNote(note: Note): void + { + note.archived = !note.archived; + + // Update the note + this.noteChanged.next(note); + + // Close the dialog + this._matDialogRef.close(); + } + + /** + * Update the note details + * + * @param note + */ + updateNoteDetails(note: Note): void + { + this.noteChanged.next(note); + } + + /** + * Delete the given note + * + * @param note + */ + deleteNote(note: Note): void + { + this._notesService.deleteNote(note) + .subscribe((isDeleted) => { + + // Return if the note wasn't deleted... + if ( !isDeleted ) + { + return; + } + + // Close the dialog + this._matDialogRef.close(); + }); + } + + /** + * Track by function for ngFor loops + * + * @param index + * @param item + */ + trackByFn(index: number, item: any): any + { + return item.id || index; + } + + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Read the given file for demonstration purposes + * + * @param file + */ + private _readAsDataURL(file: File): Promise + { + // Return a new promise + return new Promise((resolve, reject) => { + + // Create a new reader + const reader = new FileReader(); + + // Resolve the promise on success + reader.onload = () => { + resolve(reader.result); + }; + + // Reject the promise on error + reader.onerror = (e) => { + reject(e); + }; + + // Read the file as the + reader.readAsDataURL(file); + }); + } +} diff --git a/src/app/modules/admin/apps/notes/labels/labels.component.html b/src/app/modules/admin/apps/notes/labels/labels.component.html new file mode 100644 index 00000000..6f9f8c6d --- /dev/null +++ b/src/app/modules/admin/apps/notes/labels/labels.component.html @@ -0,0 +1,54 @@ +
+ +
+
Edit labels
+ +
+ + + + + + +
+ + + + + + + + +
+
diff --git a/src/app/modules/admin/apps/notes/labels/labels.component.ts b/src/app/modules/admin/apps/notes/labels/labels.component.ts new file mode 100644 index 00000000..3e25b56a --- /dev/null +++ b/src/app/modules/admin/apps/notes/labels/labels.component.ts @@ -0,0 +1,112 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { NotesService } from 'app/modules/admin/apps/notes/notes.service'; +import { Label } from 'app/modules/admin/apps/notes/notes.types'; +import { debounceTime, filter, switchMap, takeUntil } from 'rxjs/operators'; +import { Observable, Subject } from 'rxjs'; + +@Component({ + selector : 'notes-labels', + templateUrl : './labels.component.html', + encapsulation : ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class NotesLabelsComponent implements OnInit, OnDestroy +{ + labels$: Observable; + + labelChanged: Subject