mirror of
				https://github.com/richard-loafle/fuse-angular.git
				synced 2025-11-04 04:43:33 +00:00 
			
		
		
		
	(apps/chat) New and improved Chat app
This commit is contained in:
		
							parent
							
								
									ee48e11548
								
							
						
					
					
						commit
						e3821da077
					
				@ -84,6 +84,7 @@ export const appRoutes: Route[] = [
 | 
			
		||||
            {path: 'apps', children: [
 | 
			
		||||
                {path: 'academy', loadChildren: () => import('app/modules/admin/apps/academy/academy.module').then(m => m.AcademyModule)},
 | 
			
		||||
                {path: 'calendar', loadChildren: () => import('app/modules/admin/apps/calendar/calendar.module').then(m => m.CalendarModule)},
 | 
			
		||||
                {path: 'chat', loadChildren: () => import('app/modules/admin/apps/chat/chat.module').then(m => m.ChatModule)},
 | 
			
		||||
                {path: 'contacts', loadChildren: () => import('app/modules/admin/apps/contacts/contacts.module').then(m => m.ContactsModule)},
 | 
			
		||||
                {path: 'ecommerce', loadChildren: () => import('app/modules/admin/apps/ecommerce/ecommerce.module').then(m => m.ECommerceModule)},
 | 
			
		||||
                {path: 'file-manager', loadChildren: () => import('app/modules/admin/apps/file-manager/file-manager.module').then(m => m.FileManagerModule)},
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										167
									
								
								src/app/mock-api/apps/chat/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/app/mock-api/apps/chat/api.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,167 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { assign, cloneDeep, omit } from 'lodash-es';
 | 
			
		||||
import { FuseMockApiService } from '@fuse/lib/mock-api';
 | 
			
		||||
import { chats as chatsData, contacts as contactsData, messages as messagesData, profile as profileData } from 'app/mock-api/apps/chat/data';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ChatMockApi
 | 
			
		||||
{
 | 
			
		||||
    private _chats: any[] = chatsData;
 | 
			
		||||
    private _contacts: any[] = contactsData;
 | 
			
		||||
    private _messages: any[] = messagesData;
 | 
			
		||||
    private _profile: any = profileData;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(private _fuseMockApiService: FuseMockApiService)
 | 
			
		||||
    {
 | 
			
		||||
        // Register Mock API handlers
 | 
			
		||||
        this.registerHandlers();
 | 
			
		||||
 | 
			
		||||
        // Modify the chats array to attach certain data to it
 | 
			
		||||
        this._chats = this._chats.map((chat) => ({
 | 
			
		||||
            ...chat,
 | 
			
		||||
            // Get the actual contact object from the id and attach it to the chat
 | 
			
		||||
            contact: this._contacts.find((contact) => contact.id === chat.contactId),
 | 
			
		||||
            // Since we use same set of messages on all chats, we assign them here.
 | 
			
		||||
            messages: this._messages.map((message) => ({
 | 
			
		||||
                ...message,
 | 
			
		||||
                chatId   : chat.id,
 | 
			
		||||
                contactId: message.contactId === 'me' ? this._profile.id : chat.contactId,
 | 
			
		||||
                isMine   : message.contactId === 'me'
 | 
			
		||||
            }))
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Register Mock API handlers
 | 
			
		||||
     */
 | 
			
		||||
    registerHandlers(): void
 | 
			
		||||
    {
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        // @ Chats - GET
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        this._fuseMockApiService
 | 
			
		||||
            .onGet('api/apps/chat/chats')
 | 
			
		||||
            .reply(() => {
 | 
			
		||||
 | 
			
		||||
                // Clone the chats
 | 
			
		||||
                const chats = cloneDeep(this._chats);
 | 
			
		||||
 | 
			
		||||
                // Return the response
 | 
			
		||||
                return [200, chats];
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        // @ Chat - GET
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        this._fuseMockApiService
 | 
			
		||||
            .onGet('api/apps/chat/chat')
 | 
			
		||||
            .reply(({request}) => {
 | 
			
		||||
 | 
			
		||||
                // Get the chat id
 | 
			
		||||
                const id = request.params.get('id');
 | 
			
		||||
 | 
			
		||||
                // Clone the chats
 | 
			
		||||
                const chats = cloneDeep(this._chats);
 | 
			
		||||
 | 
			
		||||
                // Find the chat we need
 | 
			
		||||
                const chat = chats.find((item) => item.id === id);
 | 
			
		||||
 | 
			
		||||
                // Return the response
 | 
			
		||||
                return [200, chat];
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        // @ Chat - PATCH
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        this._fuseMockApiService
 | 
			
		||||
            .onPatch('api/apps/chat/chat')
 | 
			
		||||
            .reply(({request}) => {
 | 
			
		||||
 | 
			
		||||
                // Get the id and chat
 | 
			
		||||
                const id = request.body.id;
 | 
			
		||||
                const chat = cloneDeep(request.body.chat);
 | 
			
		||||
 | 
			
		||||
                // Prepare the updated chat
 | 
			
		||||
                let updatedChat = null;
 | 
			
		||||
 | 
			
		||||
                // Find the chat and update it
 | 
			
		||||
                this._chats.forEach((item, index, chats) => {
 | 
			
		||||
 | 
			
		||||
                    if ( item.id === id )
 | 
			
		||||
                    {
 | 
			
		||||
                        // Update the chat
 | 
			
		||||
                        chats[index] = assign({}, chats[index], chat);
 | 
			
		||||
 | 
			
		||||
                        // Store the updated chat
 | 
			
		||||
                        updatedChat = chats[index];
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Return the response
 | 
			
		||||
                return [200, updatedChat];
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        // @ Contacts - GET
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        this._fuseMockApiService
 | 
			
		||||
            .onGet('api/apps/chat/contacts')
 | 
			
		||||
            .reply(() => {
 | 
			
		||||
 | 
			
		||||
                // Clone the contacts
 | 
			
		||||
                let contacts = cloneDeep(this._contacts);
 | 
			
		||||
 | 
			
		||||
                // Sort the contacts by the name field by default
 | 
			
		||||
                contacts.sort((a, b) => a.name.localeCompare(b.name));
 | 
			
		||||
 | 
			
		||||
                // Omit details and attachments from contacts
 | 
			
		||||
                contacts = contacts.map((contact) => omit(contact, ['details', 'attachments']));
 | 
			
		||||
 | 
			
		||||
                // Return the response
 | 
			
		||||
                return [200, contacts];
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        // @ Contact Details - GET
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        this._fuseMockApiService
 | 
			
		||||
            .onGet('api/apps/chat/contact')
 | 
			
		||||
            .reply(({request}) => {
 | 
			
		||||
 | 
			
		||||
                // Get the contact id
 | 
			
		||||
                const id = request.params.get('id');
 | 
			
		||||
 | 
			
		||||
                // Clone the contacts
 | 
			
		||||
                const contacts = cloneDeep(this._contacts);
 | 
			
		||||
 | 
			
		||||
                // Find the contact
 | 
			
		||||
                const contact = contacts.find((item) => item.id === id);
 | 
			
		||||
 | 
			
		||||
                // Return the response
 | 
			
		||||
                return [200, contact];
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        // @ Profile - GET
 | 
			
		||||
        // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
        this._fuseMockApiService
 | 
			
		||||
            .onGet('api/apps/chat/profile')
 | 
			
		||||
            .reply(() => {
 | 
			
		||||
 | 
			
		||||
                // Clone the profile
 | 
			
		||||
                const profile = cloneDeep(this._profile);
 | 
			
		||||
 | 
			
		||||
                // Return the response
 | 
			
		||||
                return [200, profile];
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3007
									
								
								src/app/mock-api/apps/chat/data.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3007
									
								
								src/app/mock-api/apps/chat/data.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -2,6 +2,7 @@ import { AcademyMockApi } from 'app/mock-api/apps/academy/api';
 | 
			
		||||
import { AnalyticsMockApi } from 'app/mock-api/dashboards/analytics/api';
 | 
			
		||||
import { AuthMockApi } from 'app/mock-api/common/auth/api';
 | 
			
		||||
import { CalendarMockApi } from 'app/mock-api/apps/calendar/api';
 | 
			
		||||
import { ChatMockApi } from 'app/mock-api/apps/chat/api';
 | 
			
		||||
import { ContactsMockApi } from 'app/mock-api/apps/contacts/api';
 | 
			
		||||
import { ECommerceInventoryMockApi } from 'app/mock-api/apps/ecommerce/inventory/api';
 | 
			
		||||
import { FileManagerMockApi } from 'app/mock-api/apps/file-manager/api';
 | 
			
		||||
@ -22,6 +23,7 @@ export const mockApiServices = [
 | 
			
		||||
    AnalyticsMockApi,
 | 
			
		||||
    AuthMockApi,
 | 
			
		||||
    CalendarMockApi,
 | 
			
		||||
    ChatMockApi,
 | 
			
		||||
    ContactsMockApi,
 | 
			
		||||
    ECommerceInventoryMockApi,
 | 
			
		||||
    FileManagerMockApi,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								src/app/modules/admin/apps/chat/chat.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/app/modules/admin/apps/chat/chat.component.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden">
 | 
			
		||||
 | 
			
		||||
    <!-- Main -->
 | 
			
		||||
    <div class="flex flex-auto overflow-hidden">
 | 
			
		||||
        <router-outlet></router-outlet>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										17
									
								
								src/app/modules/admin/apps/chat/chat.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/app/modules/admin/apps/chat/chat.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector       : 'chat',
 | 
			
		||||
    templateUrl    : './chat.component.html',
 | 
			
		||||
    encapsulation  : ViewEncapsulation.None,
 | 
			
		||||
    changeDetection: ChangeDetectionStrategy.OnPush
 | 
			
		||||
})
 | 
			
		||||
export class ChatComponent
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								src/app/modules/admin/apps/chat/chat.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/app/modules/admin/apps/chat/chat.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { RouterModule } from '@angular/router';
 | 
			
		||||
import { MatButtonModule } from '@angular/material/button';
 | 
			
		||||
import { MatCheckboxModule } from '@angular/material/checkbox';
 | 
			
		||||
import { MatFormFieldModule } from '@angular/material/form-field';
 | 
			
		||||
import { MatIconModule } from '@angular/material/icon';
 | 
			
		||||
import { MatInputModule } from '@angular/material/input';
 | 
			
		||||
import { MatMenuModule } from '@angular/material/menu';
 | 
			
		||||
import { MatSidenavModule } from '@angular/material/sidenav';
 | 
			
		||||
import { FuseAutogrowModule } from '@fuse/directives/autogrow';
 | 
			
		||||
import { SharedModule } from 'app/shared/shared.module';
 | 
			
		||||
import { chatRoutes } from 'app/modules/admin/apps/chat/chat.routing';
 | 
			
		||||
import { ChatComponent } from 'app/modules/admin/apps/chat/chat.component';
 | 
			
		||||
import { ChatsComponent } from 'app/modules/admin/apps/chat/chats/chats.component';
 | 
			
		||||
import { ContactInfoComponent } from 'app/modules/admin/apps/chat/contact-info/contact-info.component';
 | 
			
		||||
import { ConversationComponent } from 'app/modules/admin/apps/chat/conversation/conversation.component';
 | 
			
		||||
import { NewChatComponent } from 'app/modules/admin/apps/chat/new-chat/new-chat.component';
 | 
			
		||||
import { ProfileComponent } from 'app/modules/admin/apps/chat/profile/profile.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        ChatComponent,
 | 
			
		||||
        ChatsComponent,
 | 
			
		||||
        ContactInfoComponent,
 | 
			
		||||
        ConversationComponent,
 | 
			
		||||
        NewChatComponent,
 | 
			
		||||
        ProfileComponent
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(chatRoutes),
 | 
			
		||||
        MatButtonModule,
 | 
			
		||||
        MatCheckboxModule,
 | 
			
		||||
        MatFormFieldModule,
 | 
			
		||||
        MatIconModule,
 | 
			
		||||
        MatInputModule,
 | 
			
		||||
        MatMenuModule,
 | 
			
		||||
        MatSidenavModule,
 | 
			
		||||
        FuseAutogrowModule,
 | 
			
		||||
        SharedModule,
 | 
			
		||||
    ]
 | 
			
		||||
})
 | 
			
		||||
export class ChatModule
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										147
									
								
								src/app/modules/admin/apps/chat/chat.resolvers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/app/modules/admin/apps/chat/chat.resolvers.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,147 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
 | 
			
		||||
import { Observable, throwError } from 'rxjs';
 | 
			
		||||
import { catchError } from 'rxjs/operators';
 | 
			
		||||
import { ChatService } from 'app/modules/admin/apps/chat/chat.service';
 | 
			
		||||
import { Chat, Contact, Profile } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ChatChatsResolver implements Resolve<any>
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        private _chatService: ChatService,
 | 
			
		||||
        private _router: Router
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolver
 | 
			
		||||
     *
 | 
			
		||||
     * @param route
 | 
			
		||||
     * @param state
 | 
			
		||||
     */
 | 
			
		||||
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Chat[]> | any
 | 
			
		||||
    {
 | 
			
		||||
        return this._chatService.getChats();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ChatChatResolver implements Resolve<any>
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        private _chatService: ChatService,
 | 
			
		||||
        private _router: Router
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolver
 | 
			
		||||
     *
 | 
			
		||||
     * @param route
 | 
			
		||||
     * @param state
 | 
			
		||||
     */
 | 
			
		||||
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Chat>
 | 
			
		||||
    {
 | 
			
		||||
        return this._chatService.getChatById(route.paramMap.get('id'))
 | 
			
		||||
                   .pipe(
 | 
			
		||||
                       // Error here means the requested chat is not available
 | 
			
		||||
                       catchError((error) => {
 | 
			
		||||
 | 
			
		||||
                           // Log the error
 | 
			
		||||
                           console.error(error);
 | 
			
		||||
 | 
			
		||||
                           // Get the parent url
 | 
			
		||||
                           const parentUrl = state.url.split('/').slice(0, -1).join('/');
 | 
			
		||||
 | 
			
		||||
                           // Navigate to there
 | 
			
		||||
                           this._router.navigateByUrl(parentUrl);
 | 
			
		||||
 | 
			
		||||
                           // Throw an error
 | 
			
		||||
                           return throwError(error);
 | 
			
		||||
                       })
 | 
			
		||||
                   );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ChatContactsResolver implements Resolve<any>
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        private _chatService: ChatService,
 | 
			
		||||
        private _router: Router
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolver
 | 
			
		||||
     *
 | 
			
		||||
     * @param route
 | 
			
		||||
     * @param state
 | 
			
		||||
     */
 | 
			
		||||
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Contact[]> | any
 | 
			
		||||
    {
 | 
			
		||||
        return this._chatService.getContacts();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ChatProfileResolver implements Resolve<any>
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        private _chatService: ChatService,
 | 
			
		||||
        private _router: Router
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolver
 | 
			
		||||
     *
 | 
			
		||||
     * @param route
 | 
			
		||||
     * @param state
 | 
			
		||||
     */
 | 
			
		||||
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Profile> | any
 | 
			
		||||
    {
 | 
			
		||||
        return this._chatService.getProfile();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								src/app/modules/admin/apps/chat/chat.routing.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/modules/admin/apps/chat/chat.routing.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
import { Route } from '@angular/router';
 | 
			
		||||
import { ChatChatResolver, ChatChatsResolver, ChatContactsResolver, ChatProfileResolver } from 'app/modules/admin/apps/chat/chat.resolvers';
 | 
			
		||||
import { ChatComponent } from 'app/modules/admin/apps/chat/chat.component';
 | 
			
		||||
import { ChatsComponent } from 'app/modules/admin/apps/chat/chats/chats.component';
 | 
			
		||||
import { ConversationComponent } from 'app/modules/admin/apps/chat/conversation/conversation.component';
 | 
			
		||||
 | 
			
		||||
export const chatRoutes: Route[] = [
 | 
			
		||||
    {
 | 
			
		||||
        path     : '',
 | 
			
		||||
        component: ChatComponent,
 | 
			
		||||
        resolve  : {
 | 
			
		||||
            chats   : ChatChatsResolver,
 | 
			
		||||
            contacts: ChatContactsResolver,
 | 
			
		||||
            profile : ChatProfileResolver
 | 
			
		||||
        },
 | 
			
		||||
        children : [
 | 
			
		||||
            {
 | 
			
		||||
                path     : '',
 | 
			
		||||
                component: ChatsComponent,
 | 
			
		||||
                children : [
 | 
			
		||||
                    {
 | 
			
		||||
                        path     : '',
 | 
			
		||||
                        component: ConversationComponent,
 | 
			
		||||
                        children : [
 | 
			
		||||
                            {
 | 
			
		||||
                                path   : ':id',
 | 
			
		||||
                                resolve: {
 | 
			
		||||
                                    conversation: ChatChatResolver
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
							
								
								
									
										202
									
								
								src/app/modules/admin/apps/chat/chat.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								src/app/modules/admin/apps/chat/chat.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient } from '@angular/common/http';
 | 
			
		||||
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
 | 
			
		||||
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
 | 
			
		||||
import { Chat, Contact, Profile } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ChatService
 | 
			
		||||
{
 | 
			
		||||
    private _chat: BehaviorSubject<Chat> = new BehaviorSubject(null);
 | 
			
		||||
    private _chats: BehaviorSubject<Chat[]> = new BehaviorSubject(null);
 | 
			
		||||
    private _contact: BehaviorSubject<Contact> = new BehaviorSubject(null);
 | 
			
		||||
    private _contacts: BehaviorSubject<Contact[]> = new BehaviorSubject(null);
 | 
			
		||||
    private _profile: BehaviorSubject<Profile> = new BehaviorSubject(null);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(private _httpClient: HttpClient)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Accessors
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Getter for chat
 | 
			
		||||
     */
 | 
			
		||||
    get chat$(): Observable<Chat>
 | 
			
		||||
    {
 | 
			
		||||
        return this._chat.asObservable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Getter for chats
 | 
			
		||||
     */
 | 
			
		||||
    get chats$(): Observable<Chat[]>
 | 
			
		||||
    {
 | 
			
		||||
        return this._chats.asObservable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Getter for contact
 | 
			
		||||
     */
 | 
			
		||||
    get contact$(): Observable<Contact>
 | 
			
		||||
    {
 | 
			
		||||
        return this._contact.asObservable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Getter for contacts
 | 
			
		||||
     */
 | 
			
		||||
    get contacts$(): Observable<Contact[]>
 | 
			
		||||
    {
 | 
			
		||||
        return this._contacts.asObservable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Getter for profile
 | 
			
		||||
     */
 | 
			
		||||
    get profile$(): Observable<Profile>
 | 
			
		||||
    {
 | 
			
		||||
        return this._profile.asObservable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get chats
 | 
			
		||||
     */
 | 
			
		||||
    getChats(): Observable<any>
 | 
			
		||||
    {
 | 
			
		||||
        return this._httpClient.get<Chat[]>('api/apps/chat/chats').pipe(
 | 
			
		||||
            tap((response: Chat[]) => {
 | 
			
		||||
                this._chats.next(response);
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get contact
 | 
			
		||||
     *
 | 
			
		||||
     * @param id
 | 
			
		||||
     */
 | 
			
		||||
    getContact(id: string): Observable<any>
 | 
			
		||||
    {
 | 
			
		||||
        return this._httpClient.get<Contact>('api/apps/chat/contacts', {params: {id}}).pipe(
 | 
			
		||||
            tap((response: Contact) => {
 | 
			
		||||
                this._contact.next(response);
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get contacts
 | 
			
		||||
     */
 | 
			
		||||
    getContacts(): Observable<any>
 | 
			
		||||
    {
 | 
			
		||||
        return this._httpClient.get<Contact[]>('api/apps/chat/contacts').pipe(
 | 
			
		||||
            tap((response: Contact[]) => {
 | 
			
		||||
                this._contacts.next(response);
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get profile
 | 
			
		||||
     */
 | 
			
		||||
    getProfile(): Observable<any>
 | 
			
		||||
    {
 | 
			
		||||
        return this._httpClient.get<Profile>('api/apps/chat/profile').pipe(
 | 
			
		||||
            tap((response: Profile) => {
 | 
			
		||||
                this._profile.next(response);
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get chat
 | 
			
		||||
     *
 | 
			
		||||
     * @param id
 | 
			
		||||
     */
 | 
			
		||||
    getChatById(id: string): Observable<any>
 | 
			
		||||
    {
 | 
			
		||||
        return this._httpClient.get<Chat>('api/apps/chat/chat', {params: {id}}).pipe(
 | 
			
		||||
            map((chat) => {
 | 
			
		||||
 | 
			
		||||
                // Update the chat
 | 
			
		||||
                this._chat.next(chat);
 | 
			
		||||
 | 
			
		||||
                // Return the chat
 | 
			
		||||
                return chat;
 | 
			
		||||
            }),
 | 
			
		||||
            switchMap((chat) => {
 | 
			
		||||
 | 
			
		||||
                if ( !chat )
 | 
			
		||||
                {
 | 
			
		||||
                    return throwError('Could not found chat with id of ' + id + '!');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return of(chat);
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update chat
 | 
			
		||||
     *
 | 
			
		||||
     * @param id
 | 
			
		||||
     * @param chat
 | 
			
		||||
     */
 | 
			
		||||
    updateChat(id: string, chat: Chat): Observable<Chat>
 | 
			
		||||
    {
 | 
			
		||||
        return this.chats$.pipe(
 | 
			
		||||
            take(1),
 | 
			
		||||
            switchMap(chats => this._httpClient.patch<Chat>('api/apps/chat/chat', {
 | 
			
		||||
                id,
 | 
			
		||||
                chat
 | 
			
		||||
            }).pipe(
 | 
			
		||||
                map((updatedChat) => {
 | 
			
		||||
 | 
			
		||||
                    // Find the index of the updated chat
 | 
			
		||||
                    const index = chats.findIndex(item => item.id === id);
 | 
			
		||||
 | 
			
		||||
                    // Update the chat
 | 
			
		||||
                    chats[index] = updatedChat;
 | 
			
		||||
 | 
			
		||||
                    // Update the chats
 | 
			
		||||
                    this._chats.next(chats);
 | 
			
		||||
 | 
			
		||||
                    // Return the updated contact
 | 
			
		||||
                    return updatedChat;
 | 
			
		||||
                }),
 | 
			
		||||
                switchMap(updatedChat => this.chat$.pipe(
 | 
			
		||||
                    take(1),
 | 
			
		||||
                    filter(item => item && item.id === id),
 | 
			
		||||
                    tap(() => {
 | 
			
		||||
 | 
			
		||||
                        // Update the chat if it's selected
 | 
			
		||||
                        this._chat.next(updatedChat);
 | 
			
		||||
 | 
			
		||||
                        // Return the updated chat
 | 
			
		||||
                        return updatedChat;
 | 
			
		||||
                    })
 | 
			
		||||
                ))
 | 
			
		||||
            ))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset the selected chat
 | 
			
		||||
     */
 | 
			
		||||
    resetChat(): void
 | 
			
		||||
    {
 | 
			
		||||
        this._chat.next(null);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								src/app/modules/admin/apps/chat/chat.types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/app/modules/admin/apps/chat/chat.types.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
export interface Profile
 | 
			
		||||
{
 | 
			
		||||
    id?: string;
 | 
			
		||||
    name?: string;
 | 
			
		||||
    email?: string;
 | 
			
		||||
    avatar?: string;
 | 
			
		||||
    about?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Contact
 | 
			
		||||
{
 | 
			
		||||
    id?: string;
 | 
			
		||||
    avatar?: string;
 | 
			
		||||
    name?: string;
 | 
			
		||||
    about?: string;
 | 
			
		||||
    details?: {
 | 
			
		||||
        emails?: {
 | 
			
		||||
            email?: string;
 | 
			
		||||
            label?: string;
 | 
			
		||||
        }[];
 | 
			
		||||
        phoneNumbers?: {
 | 
			
		||||
            country?: string;
 | 
			
		||||
            number?: string;
 | 
			
		||||
            label?: string;
 | 
			
		||||
        }[];
 | 
			
		||||
        title?: string;
 | 
			
		||||
        company?: string;
 | 
			
		||||
        birthday?: string;
 | 
			
		||||
        address?: string;
 | 
			
		||||
    };
 | 
			
		||||
    attachments?: {
 | 
			
		||||
        media?: any[]
 | 
			
		||||
        docs?: any[]
 | 
			
		||||
        links?: any[]
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Chat
 | 
			
		||||
{
 | 
			
		||||
    id?: string;
 | 
			
		||||
    contactId?: string;
 | 
			
		||||
    contact?: Contact;
 | 
			
		||||
    unreadCount?: number;
 | 
			
		||||
    muted?: boolean;
 | 
			
		||||
    lastMessage?: string;
 | 
			
		||||
    lastMessageAt?: string;
 | 
			
		||||
    messages?: {
 | 
			
		||||
        id?: string;
 | 
			
		||||
        chatId?: string;
 | 
			
		||||
        contactId?: string;
 | 
			
		||||
        isMine?: boolean;
 | 
			
		||||
        value?: string;
 | 
			
		||||
        createdAt?: string;
 | 
			
		||||
    }[];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										190
									
								
								src/app/modules/admin/apps/chat/chats/chats.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/app/modules/admin/apps/chat/chats/chats.component.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,190 @@
 | 
			
		||||
<div class="relative flex flex-auto w-full bg-card dark:bg-transparent">
 | 
			
		||||
 | 
			
		||||
    <mat-drawer-container
 | 
			
		||||
        class="flex-auto h-full"
 | 
			
		||||
        [hasBackdrop]="false">
 | 
			
		||||
 | 
			
		||||
        <!-- Drawer -->
 | 
			
		||||
        <mat-drawer
 | 
			
		||||
            class="w-100 border-r shadow-none dark:bg-gray-900"
 | 
			
		||||
            [autoFocus]="false"
 | 
			
		||||
            [(opened)]="drawerOpened"
 | 
			
		||||
            #drawer>
 | 
			
		||||
 | 
			
		||||
            <!-- New chat -->
 | 
			
		||||
            <ng-container *ngIf="drawerComponent === 'new-chat'">
 | 
			
		||||
                <chat-new-chat [drawer]="drawer"></chat-new-chat>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <!-- Profile -->
 | 
			
		||||
            <ng-container *ngIf="drawerComponent === 'profile'">
 | 
			
		||||
                <chat-profile [drawer]="drawer"></chat-profile>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
        </mat-drawer>
 | 
			
		||||
 | 
			
		||||
        <!-- Drawer content -->
 | 
			
		||||
        <mat-drawer-content class="flex overflow-hidden">
 | 
			
		||||
 | 
			
		||||
            <!-- Chats list -->
 | 
			
		||||
            <ng-container *ngIf="chats && chats.length > 0; else noChats">
 | 
			
		||||
                <div class="relative flex flex-auto flex-col w-full min-w-0 lg:min-w-100 lg:max-w-100 border-r bg-card">
 | 
			
		||||
 | 
			
		||||
                    <!-- Header -->
 | 
			
		||||
                    <div class="flex flex-col flex-0 py-4 px-8 border-b bg-gray-50 dark:bg-transparent">
 | 
			
		||||
                        <div class="flex items-center">
 | 
			
		||||
                            <div
 | 
			
		||||
                                class="flex items-center mr-1 cursor-pointer"
 | 
			
		||||
                                (click)="openProfile()">
 | 
			
		||||
                                <div class="w-10 h-10">
 | 
			
		||||
                                    <ng-container *ngIf="profile.avatar">
 | 
			
		||||
                                        <img
 | 
			
		||||
                                            class="object-cover w-full h-full rounded-full object-cover"
 | 
			
		||||
                                            [src]="profile.avatar"
 | 
			
		||||
                                            alt="Profile avatar"/>
 | 
			
		||||
                                    </ng-container>
 | 
			
		||||
                                    <ng-container *ngIf="!profile.avatar">
 | 
			
		||||
                                        <div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
 | 
			
		||||
                                            {{profile.name.charAt(0)}}
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </ng-container>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="ml-4 font-medium truncate">{{profile.name}}</div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <button
 | 
			
		||||
                                class="ml-auto"
 | 
			
		||||
                                mat-icon-button
 | 
			
		||||
                                (click)="openNewChat()">
 | 
			
		||||
                                <mat-icon [svgIcon]="'heroicons_outline:plus-circle'"></mat-icon>
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <button
 | 
			
		||||
                                class="ml-1 -mr-4"
 | 
			
		||||
                                mat-icon-button
 | 
			
		||||
                                [matMenuTriggerFor]="chatsHeaderMenu">
 | 
			
		||||
                                <mat-icon [svgIcon]="'heroicons_outline:dots-vertical'"></mat-icon>
 | 
			
		||||
                                <mat-menu #chatsHeaderMenu>
 | 
			
		||||
                                    <button mat-menu-item>
 | 
			
		||||
                                        <mat-icon [svgIcon]="'heroicons_outline:user-group'"></mat-icon>
 | 
			
		||||
                                        New group
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    <button mat-menu-item>
 | 
			
		||||
                                        <mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
 | 
			
		||||
                                        Create a room
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    <button
 | 
			
		||||
                                        mat-menu-item
 | 
			
		||||
                                        (click)="openProfile()">
 | 
			
		||||
                                        <mat-icon [svgIcon]="'heroicons_outline:user-circle'"></mat-icon>
 | 
			
		||||
                                        Profile
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    <button mat-menu-item>
 | 
			
		||||
                                        <mat-icon [svgIcon]="'heroicons_outline:archive'"></mat-icon>
 | 
			
		||||
                                        Archived
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    <button mat-menu-item>
 | 
			
		||||
                                        <mat-icon [svgIcon]="'heroicons_outline:star'"></mat-icon>
 | 
			
		||||
                                        Starred
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    <button mat-menu-item>
 | 
			
		||||
                                        <mat-icon [svgIcon]="'heroicons_outline:cog'"></mat-icon>
 | 
			
		||||
                                        Settings
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                </mat-menu>
 | 
			
		||||
                            </button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <!-- Search -->
 | 
			
		||||
                        <div class="mt-4">
 | 
			
		||||
                            <mat-form-field
 | 
			
		||||
                                class="fuse-mat-no-subscript fuse-mat-rounded fuse-mat-dense w-full"
 | 
			
		||||
                                [floatLabel]="'always'">
 | 
			
		||||
                                <mat-icon
 | 
			
		||||
                                    matPrefix
 | 
			
		||||
                                    class="icon-size-5"
 | 
			
		||||
                                    [svgIcon]="'heroicons_solid:search'"></mat-icon>
 | 
			
		||||
                                <input
 | 
			
		||||
                                    matInput
 | 
			
		||||
                                    [autocomplete]="'off'"
 | 
			
		||||
                                    [placeholder]="'Search or start new chat'"
 | 
			
		||||
                                    (input)="filterChats(searchField.value)"
 | 
			
		||||
                                    #searchField>
 | 
			
		||||
                            </mat-form-field>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <!-- Chats -->
 | 
			
		||||
                    <div class="flex-auto overflow-y-auto">
 | 
			
		||||
                        <ng-container *ngIf="filteredChats.length > 0; else noChats">
 | 
			
		||||
                            <ng-container *ngFor="let chat of filteredChats; trackBy: trackByFn">
 | 
			
		||||
                                <div
 | 
			
		||||
                                    class="z-20 flex items-center py-5 px-8 cursor-pointer border-b hover:bg-hover"
 | 
			
		||||
                                    [ngClass]="{'bg-primary-50 dark:bg-hover': selectedChat && selectedChat.id === chat.id}"
 | 
			
		||||
                                    [routerLink]="[chat.id]">
 | 
			
		||||
                                    <div class="relative flex flex-0 items-center justify-center w-10 h-10">
 | 
			
		||||
                                        <ng-container *ngIf="chat.unreadCount > 0">
 | 
			
		||||
                                            <div
 | 
			
		||||
                                                class="absolute bottom-0 right-0 flex-0 w-2 h-2 -ml-0.5 rounded-full ring-2 ring-bg-card dark:ring-gray-900 bg-primary dark:bg-primary-500 text-on-primary"
 | 
			
		||||
                                                [class.ring-primary-50]="selectedChat && selectedChat.id === chat.id"></div>
 | 
			
		||||
                                        </ng-container>
 | 
			
		||||
                                        <ng-container *ngIf="chat.contact.avatar">
 | 
			
		||||
                                            <img
 | 
			
		||||
                                                class="w-full h-full rounded-full object-cover"
 | 
			
		||||
                                                [src]="chat.contact.avatar"
 | 
			
		||||
                                                alt="Contact avatar"/>
 | 
			
		||||
                                        </ng-container>
 | 
			
		||||
                                        <ng-container *ngIf="!chat.contact.avatar">
 | 
			
		||||
                                            <div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
 | 
			
		||||
                                                {{chat.contact.name.charAt(0)}}
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </ng-container>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <div class="min-w-0 ml-4">
 | 
			
		||||
                                        <div class="font-medium leading-5 truncate">{{chat.contact.name}}</div>
 | 
			
		||||
                                        <div
 | 
			
		||||
                                            class="leading-5 truncate text-secondary"
 | 
			
		||||
                                            [class.text-primary]="chat.unreadCount > 0"
 | 
			
		||||
                                            [class.dark:text-primary-500]="chat.unreadCount > 0">
 | 
			
		||||
                                            {{chat.lastMessage}}
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <div class="flex flex-col items-end self-start ml-auto pl-2">
 | 
			
		||||
                                        <div class="text-sm leading-5 text-secondary">{{chat.lastMessageAt}}</div>
 | 
			
		||||
                                        <ng-container *ngIf="chat.muted">
 | 
			
		||||
                                            <mat-icon
 | 
			
		||||
                                                class="icon-size-5 text-hint"
 | 
			
		||||
                                                [svgIcon]="'heroicons_solid:volume-off'"></mat-icon>
 | 
			
		||||
                                        </ng-container>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </ng-container>
 | 
			
		||||
                        </ng-container>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <!-- No chats template -->
 | 
			
		||||
            <ng-template #noChats>
 | 
			
		||||
                <div class="flex flex-auto flex-col items-center justify-center h-full">
 | 
			
		||||
                    <mat-icon
 | 
			
		||||
                        class="icon-size-24"
 | 
			
		||||
                        [svgIcon]="'iconsmind:speach_bubble'"></mat-icon>
 | 
			
		||||
                    <div class="mt-4 text-2xl font-semibold tracking-tight text-secondary">No chats</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
 | 
			
		||||
            <!-- Conversation -->
 | 
			
		||||
            <ng-container *ngIf="chats && chats.length > 0">
 | 
			
		||||
                <div
 | 
			
		||||
                    class="flex-auto"
 | 
			
		||||
                    [ngClass]="{'z-20 absolute inset-0 lg:static lg:inset-auto flex': selectedChat && selectedChat.id,
 | 
			
		||||
                        'hidden lg:flex': !selectedChat || !selectedChat.id}">
 | 
			
		||||
                    <router-outlet></router-outlet>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
        </mat-drawer-content>
 | 
			
		||||
 | 
			
		||||
    </mat-drawer-container>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										138
									
								
								src/app/modules/admin/apps/chat/chats/chats.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/app/modules/admin/apps/chat/chats/chats.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,138 @@
 | 
			
		||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { takeUntil } from 'rxjs/operators';
 | 
			
		||||
import { Chat, Profile } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
import { ChatService } from 'app/modules/admin/apps/chat/chat.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector       : 'chat-chats',
 | 
			
		||||
    templateUrl    : './chats.component.html',
 | 
			
		||||
    encapsulation  : ViewEncapsulation.None,
 | 
			
		||||
    changeDetection: ChangeDetectionStrategy.OnPush
 | 
			
		||||
})
 | 
			
		||||
export class ChatsComponent implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
    chats: Chat[];
 | 
			
		||||
    drawerComponent: 'profile' | 'new-chat';
 | 
			
		||||
    drawerOpened: boolean = false;
 | 
			
		||||
    filteredChats: Chat[];
 | 
			
		||||
    profile: Profile;
 | 
			
		||||
    selectedChat: Chat;
 | 
			
		||||
    private _unsubscribeAll: Subject<any> = new Subject<any>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        private _chatService: ChatService,
 | 
			
		||||
        private _changeDetectorRef: ChangeDetectorRef
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Lifecycle hooks
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On init
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Chats
 | 
			
		||||
        this._chatService.chats$
 | 
			
		||||
            .pipe(takeUntil(this._unsubscribeAll))
 | 
			
		||||
            .subscribe((chats: Chat[]) => {
 | 
			
		||||
                this.chats = this.filteredChats = chats;
 | 
			
		||||
 | 
			
		||||
                // Mark for check
 | 
			
		||||
                this._changeDetectorRef.markForCheck();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // Profile
 | 
			
		||||
        this._chatService.profile$
 | 
			
		||||
            .pipe(takeUntil(this._unsubscribeAll))
 | 
			
		||||
            .subscribe((profile: Profile) => {
 | 
			
		||||
                this.profile = profile;
 | 
			
		||||
 | 
			
		||||
                // Mark for check
 | 
			
		||||
                this._changeDetectorRef.markForCheck();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // Selected chat
 | 
			
		||||
        this._chatService.chat$
 | 
			
		||||
            .pipe(takeUntil(this._unsubscribeAll))
 | 
			
		||||
            .subscribe((chat: Chat) => {
 | 
			
		||||
                this.selectedChat = chat;
 | 
			
		||||
 | 
			
		||||
                // Mark for check
 | 
			
		||||
                this._changeDetectorRef.markForCheck();
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On destroy
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Unsubscribe from all subscriptions
 | 
			
		||||
        this._unsubscribeAll.next();
 | 
			
		||||
        this._unsubscribeAll.complete();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Filter the chats
 | 
			
		||||
     *
 | 
			
		||||
     * @param query
 | 
			
		||||
     */
 | 
			
		||||
    filterChats(query: string): void
 | 
			
		||||
    {
 | 
			
		||||
        // Reset the filter
 | 
			
		||||
        if ( !query )
 | 
			
		||||
        {
 | 
			
		||||
            this.filteredChats = this.chats;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.filteredChats = this.chats.filter((chat) => chat.contact.name.toLowerCase().includes(query.toLowerCase()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the new chat sidebar
 | 
			
		||||
     */
 | 
			
		||||
    openNewChat(): void
 | 
			
		||||
    {
 | 
			
		||||
        this.drawerComponent = 'new-chat';
 | 
			
		||||
        this.drawerOpened = true;
 | 
			
		||||
 | 
			
		||||
        // Mark for check
 | 
			
		||||
        this._changeDetectorRef.markForCheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the profile sidebar
 | 
			
		||||
     */
 | 
			
		||||
    openProfile(): void
 | 
			
		||||
    {
 | 
			
		||||
        this.drawerComponent = 'profile';
 | 
			
		||||
        this.drawerOpened = true;
 | 
			
		||||
 | 
			
		||||
        // Mark for check
 | 
			
		||||
        this._changeDetectorRef.markForCheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Track by function for ngFor loops
 | 
			
		||||
     *
 | 
			
		||||
     * @param index
 | 
			
		||||
     * @param item
 | 
			
		||||
     */
 | 
			
		||||
    trackByFn(index: number, item: any): any
 | 
			
		||||
    {
 | 
			
		||||
        return item.id || index;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,85 @@
 | 
			
		||||
<div class="flex flex-col flex-auto overflow-y-auto bg-card dark:bg-default">
 | 
			
		||||
 | 
			
		||||
    <!-- Header -->
 | 
			
		||||
    <div class="flex flex-0 items-center h-18 px-4 border-b bg-gray-50 dark:bg-transparent">
 | 
			
		||||
        <button
 | 
			
		||||
            mat-icon-button
 | 
			
		||||
            (click)="drawer.close()">
 | 
			
		||||
            <mat-icon [svgIcon]="'heroicons_outline:x'"></mat-icon>
 | 
			
		||||
        </button>
 | 
			
		||||
        <div class="ml-2 text-lg font-medium">Contact info</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Contact avatar & info -->
 | 
			
		||||
    <div class="flex flex-col items-center mt-8">
 | 
			
		||||
        <div class="w-40 h-40 rounded-full">
 | 
			
		||||
            <ng-container *ngIf="chat.contact.avatar">
 | 
			
		||||
                <img
 | 
			
		||||
                    class="w-full h-full rounded-full object-cover"
 | 
			
		||||
                    [src]="chat.contact.avatar"
 | 
			
		||||
                    [alt]="'Contact avatar'">
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="!chat.contact.avatar">
 | 
			
		||||
                <div class="flex items-center justify-center w-full h-full rounded-full text-8xl font-semibold uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
 | 
			
		||||
                    {{chat.contact.name.charAt(0)}}
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="mt-4 text-lg font-medium">{{chat.contact.name}}</div>
 | 
			
		||||
        <div class="mt-0.5 text-md text-secondary">{{chat.contact.about}}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="py-10 px-7">
 | 
			
		||||
        <!-- Media -->
 | 
			
		||||
        <div class="text-lg font-medium">Media</div>
 | 
			
		||||
        <div class="grid grid-cols-4 gap-1 mt-4">
 | 
			
		||||
            <ng-container *ngFor="let media of chat.contact.attachments.media">
 | 
			
		||||
                <img
 | 
			
		||||
                    class="h-20 rounded object-cover"
 | 
			
		||||
                    [src]="media"/>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </div>
 | 
			
		||||
        <!-- Details -->
 | 
			
		||||
        <div class="mt-10 space-y-4">
 | 
			
		||||
            <div class="text-lg font-medium mb-3">Details</div>
 | 
			
		||||
            <ng-container *ngIf="chat.contact.details.emails.length">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="font-medium">Email</div>
 | 
			
		||||
                    <div class="">{{chat.contact.details.emails[0].email}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="chat.contact.details.phoneNumbers.length">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="font-medium">Phone number</div>
 | 
			
		||||
                    <div class="">{{chat.contact.details.phoneNumbers[0].number}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="chat.contact.details.title">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="font-medium">Title</div>
 | 
			
		||||
                    <div class="">{{chat.contact.details.title}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="chat.contact.details.company">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="font-medium">Company</div>
 | 
			
		||||
                    <div class="">{{chat.contact.details.company}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="chat.contact.details.birthday">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="font-medium">Birthday</div>
 | 
			
		||||
                    <div class="">{{chat.contact.details.birthday}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="chat.contact.details.address">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="font-medium">Address</div>
 | 
			
		||||
                    <div class="">{{chat.contact.details.address}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,22 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core';
 | 
			
		||||
import { MatDrawer } from '@angular/material/sidenav';
 | 
			
		||||
import { Chat, Contact } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector       : 'chat-contact-info',
 | 
			
		||||
    templateUrl    : './contact-info.component.html',
 | 
			
		||||
    encapsulation  : ViewEncapsulation.None,
 | 
			
		||||
    changeDetection: ChangeDetectionStrategy.OnPush
 | 
			
		||||
})
 | 
			
		||||
export class ContactInfoComponent
 | 
			
		||||
{
 | 
			
		||||
    @Input() chat: Chat;
 | 
			
		||||
    @Input() drawer: MatDrawer;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,215 @@
 | 
			
		||||
<div class="flex flex-col flex-auto overflow-y-auto lg:overflow-hidden bg-card dark:bg-default">
 | 
			
		||||
 | 
			
		||||
    <ng-container *ngIf="chat; else selectChatOrStartNew">
 | 
			
		||||
 | 
			
		||||
        <mat-drawer-container
 | 
			
		||||
            class="flex-auto h-full"
 | 
			
		||||
            [hasBackdrop]="false">
 | 
			
		||||
 | 
			
		||||
            <!-- Drawer -->
 | 
			
		||||
            <mat-drawer
 | 
			
		||||
                class="w-100 border-r shadow-none dark:bg-gray-900"
 | 
			
		||||
                [autoFocus]="false"
 | 
			
		||||
                [mode]="'side'"
 | 
			
		||||
                [position]="'end'"
 | 
			
		||||
                [(opened)]="drawerOpened"
 | 
			
		||||
                #drawer>
 | 
			
		||||
 | 
			
		||||
                <!-- Contact info -->
 | 
			
		||||
                <chat-contact-info
 | 
			
		||||
                    [drawer]="drawer"
 | 
			
		||||
                    [chat]="chat"></chat-contact-info>
 | 
			
		||||
            </mat-drawer>
 | 
			
		||||
 | 
			
		||||
            <!-- Drawer content -->
 | 
			
		||||
            <mat-drawer-content class="flex flex-col overflow-hidden">
 | 
			
		||||
 | 
			
		||||
                <!-- Header -->
 | 
			
		||||
                <div class="flex flex-0 items-center h-18 px-4 md:px-6 border-b bg-gray-50 dark:bg-transparent">
 | 
			
		||||
 | 
			
		||||
                    <!-- Back button -->
 | 
			
		||||
                    <a
 | 
			
		||||
                        class="lg:hidden md:-ml-2"
 | 
			
		||||
                        mat-icon-button
 | 
			
		||||
                        [routerLink]="['./']"
 | 
			
		||||
                        (click)="resetChat()">
 | 
			
		||||
                        <mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
 | 
			
		||||
                    </a>
 | 
			
		||||
 | 
			
		||||
                    <!-- Contact info -->
 | 
			
		||||
                    <div
 | 
			
		||||
                        class="flex items-center ml-2 lg:ml-0 mr-2 cursor-pointer"
 | 
			
		||||
                        (click)="openContactInfo()">
 | 
			
		||||
                        <div class="relative flex flex-0 items-center justify-center w-10 h-10">
 | 
			
		||||
                            <ng-container *ngIf="chat.contact.avatar">
 | 
			
		||||
                                <img
 | 
			
		||||
                                    class="w-full h-full rounded-full object-cover"
 | 
			
		||||
                                    [src]="chat.contact.avatar"
 | 
			
		||||
                                    alt="Contact avatar"/>
 | 
			
		||||
                            </ng-container>
 | 
			
		||||
                            <ng-container *ngIf="!chat.contact.avatar">
 | 
			
		||||
                                <div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
 | 
			
		||||
                                    {{chat.contact.name.charAt(0)}}
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </ng-container>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="ml-4 text-lg font-medium leading-5 truncate">{{chat.contact.name}}</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <button
 | 
			
		||||
                        class="ml-auto"
 | 
			
		||||
                        mat-icon-button
 | 
			
		||||
                        [matMenuTriggerFor]="conversationHeaderMenu">
 | 
			
		||||
                        <mat-icon [svgIcon]="'heroicons_outline:dots-vertical'"></mat-icon>
 | 
			
		||||
                        <mat-menu #conversationHeaderMenu>
 | 
			
		||||
                            <button
 | 
			
		||||
                                mat-menu-item
 | 
			
		||||
                                (click)="openContactInfo()">
 | 
			
		||||
                                <mat-icon [svgIcon]="'heroicons_outline:user-circle'"></mat-icon>
 | 
			
		||||
                                Contact info
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <button mat-menu-item>
 | 
			
		||||
                                <mat-icon [svgIcon]="'heroicons_outline:check-circle'"></mat-icon>
 | 
			
		||||
                                Select messages
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <button
 | 
			
		||||
                                mat-menu-item
 | 
			
		||||
                                (click)="toggleMuteNotifications()">
 | 
			
		||||
                                <ng-container *ngIf="!chat.muted">
 | 
			
		||||
                                    <mat-icon [svgIcon]="'heroicons_outline:volume-off'"></mat-icon>
 | 
			
		||||
                                    Mute notifications
 | 
			
		||||
                                </ng-container>
 | 
			
		||||
                                <ng-container *ngIf="chat.muted">
 | 
			
		||||
                                    <mat-icon [svgIcon]="'heroicons_outline:volume-up'"></mat-icon>
 | 
			
		||||
                                    Unmute notifications
 | 
			
		||||
                                </ng-container>
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <button mat-menu-item>
 | 
			
		||||
                                <mat-icon [svgIcon]="'heroicons_outline:backspace'"></mat-icon>
 | 
			
		||||
                                Clear messages
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <button mat-menu-item>
 | 
			
		||||
                                <mat-icon [svgIcon]="'heroicons_outline:trash'"></mat-icon>
 | 
			
		||||
                                Delete chat
 | 
			
		||||
                            </button>
 | 
			
		||||
                        </mat-menu>
 | 
			
		||||
                    </button>
 | 
			
		||||
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <!-- Conversation -->
 | 
			
		||||
                <div class="flex overflow-y-auto flex-col-reverse">
 | 
			
		||||
                    <div class="flex flex-col flex-auto flex-shrink p-6 bg-card dark:bg-transparent">
 | 
			
		||||
                        <ng-container *ngFor="let message of chat.messages; let i = index; let first = first; let last = last; trackBy: trackByFn">
 | 
			
		||||
                            <!-- Start of the day -->
 | 
			
		||||
                            <ng-container *ngIf="first || (chat.messages[i - 1].createdAt | date:'d') !== (message.createdAt | date:'d')">
 | 
			
		||||
                                <div class="flex items-center justify-center my-3 -mx-6">
 | 
			
		||||
                                    <div class="flex-auto border-b"></div>
 | 
			
		||||
                                    <div class="flex-0 mx-4 text-sm font-medium leading-5 text-secondary">
 | 
			
		||||
                                        {{message.createdAt | date: 'longDate'}}
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <div class="flex-auto border-b"></div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </ng-container>
 | 
			
		||||
                            <div
 | 
			
		||||
                                class="flex flex-col"
 | 
			
		||||
                                [ngClass]="{'items-end': message.isMine,
 | 
			
		||||
                                        'items-start': !message.isMine,
 | 
			
		||||
                                        'mt-0.5': i > 0 && chat.messages[i - 1].isMine === message.isMine,
 | 
			
		||||
                                        'mt-3': i > 0 && chat.messages[i - 1].isMine !== message.isMine}">
 | 
			
		||||
                                <!-- Bubble -->
 | 
			
		||||
                                <div
 | 
			
		||||
                                    class="relative max-w-3/4 px-3 py-2 rounded-lg"
 | 
			
		||||
                                    [ngClass]="{'bg-blue-500 text-blue-50': message.isMine,
 | 
			
		||||
                                            'bg-gray-500 text-gray-50': !message.isMine}">
 | 
			
		||||
                                    <!-- Speech bubble tail -->
 | 
			
		||||
                                    <ng-container *ngIf="last || chat.messages[i + 1].isMine !== message.isMine">
 | 
			
		||||
                                        <div
 | 
			
		||||
                                            class="absolute bottom-0 w-3 transform"
 | 
			
		||||
                                            [ngClass]="{'text-blue-500 -right-1 -mr-px mb-px': message.isMine,
 | 
			
		||||
                                                    'text-gray-500 -left-1 -ml-px mb-px -scale-x-1': !message.isMine}">
 | 
			
		||||
                                            <ng-container *ngTemplateOutlet="speechBubbleExtension"></ng-container>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </ng-container>
 | 
			
		||||
                                    <!-- Message -->
 | 
			
		||||
                                    <div
 | 
			
		||||
                                        class="min-w-4 leading-5"
 | 
			
		||||
                                        [innerHTML]="message.value">
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <!-- Time -->
 | 
			
		||||
                                <ng-container
 | 
			
		||||
                                    *ngIf="first
 | 
			
		||||
                                       || last
 | 
			
		||||
                                       || chat.messages[i + 1].isMine !== message.isMine
 | 
			
		||||
                                       || chat.messages[i + 1].createdAt !== message.createdAt">
 | 
			
		||||
                                    <div
 | 
			
		||||
                                        class="my-0.5 text-sm font-medium text-secondary"
 | 
			
		||||
                                        [ngClass]="{'mr-3': message.isMine,
 | 
			
		||||
                                                'ml-3': !message.isMine}">
 | 
			
		||||
                                        {{message.createdAt | date:'HH:mm'}}
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </ng-container>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </ng-container>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <!-- Message field -->
 | 
			
		||||
                <div class="flex items-end p-4 border-t bg-gray-50">
 | 
			
		||||
                    <div class="flex items-center h-11 my-px">
 | 
			
		||||
                        <button mat-icon-button>
 | 
			
		||||
                            <mat-icon [svgIcon]="'heroicons_outline:emoji-happy'"></mat-icon>
 | 
			
		||||
                        </button>
 | 
			
		||||
                        <button
 | 
			
		||||
                            class="ml-0.5"
 | 
			
		||||
                            mat-icon-button>
 | 
			
		||||
                            <mat-icon [svgIcon]="'heroicons_outline:paper-clip'"></mat-icon>
 | 
			
		||||
                        </button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <mat-form-field class="fuse-mat-dense fuse-mat-no-subscript fuse-mat-rounded fuse-mat-bold w-full ml-4">
 | 
			
		||||
                        <textarea
 | 
			
		||||
                            class="min-h-5 my-0 resize-none"
 | 
			
		||||
                            style="margin: 11px 0 !important; padding: 0 !important;"
 | 
			
		||||
                            [rows]="1"
 | 
			
		||||
                            matInput
 | 
			
		||||
                            #messageInput></textarea>
 | 
			
		||||
                    </mat-form-field>
 | 
			
		||||
                    <div class="flex items-center h-11 my-px ml-4">
 | 
			
		||||
                        <button
 | 
			
		||||
                            mat-icon-button>
 | 
			
		||||
                            <mat-icon
 | 
			
		||||
                                class="transform rotate-90"
 | 
			
		||||
                                [svgIcon]="'heroicons_outline:paper-airplane'"></mat-icon>
 | 
			
		||||
                        </button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
            </mat-drawer-content>
 | 
			
		||||
 | 
			
		||||
        </mat-drawer-container>
 | 
			
		||||
 | 
			
		||||
    </ng-container>
 | 
			
		||||
 | 
			
		||||
    <!-- Select chat or start new template -->
 | 
			
		||||
    <ng-template #selectChatOrStartNew>
 | 
			
		||||
        <div class="flex flex-col flex-auto items-center justify-center bg-gray-100 dark:bg-transparent">
 | 
			
		||||
            <mat-icon
 | 
			
		||||
                class="icon-size-24"
 | 
			
		||||
                [svgIcon]="'iconsmind:speach_bubble'"></mat-icon>
 | 
			
		||||
            <div class="mt-4 text-2xl font-semibold tracking-tight text-secondary">Select a conversation or start a new chat</div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
 | 
			
		||||
    <!-- Speech bubble tail SVG -->
 | 
			
		||||
    <!-- @formatter:off -->
 | 
			
		||||
    <ng-template #speechBubbleExtension>
 | 
			
		||||
        <svg width="100%" height="100%" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
            <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
 | 
			
		||||
                <path d="M1.01522827,0.516204834 C-8.83532715,54.3062744 61.7609863,70.5215302 64.8009949,64.3061218 C68.8074951,54.8859711 30.1663208,52.9997559 37.5036011,0.516204834 L1.01522827,0.516204834 Z" fill="currentColor" fill-rule="nonzero"></path>
 | 
			
		||||
            </g>
 | 
			
		||||
        </svg>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
    <!-- @formatter:on -->
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,143 @@
 | 
			
		||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { takeUntil } from 'rxjs/operators';
 | 
			
		||||
import { Chat } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
import { ChatService } from 'app/modules/admin/apps/chat/chat.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector       : 'chat-conversation',
 | 
			
		||||
    templateUrl    : './conversation.component.html',
 | 
			
		||||
    encapsulation  : ViewEncapsulation.None,
 | 
			
		||||
    changeDetection: ChangeDetectionStrategy.OnPush
 | 
			
		||||
})
 | 
			
		||||
export class ConversationComponent implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
    @ViewChild('messageInput') messageInput: ElementRef;
 | 
			
		||||
    chat: Chat;
 | 
			
		||||
    drawerOpened: boolean = false;
 | 
			
		||||
    private _unsubscribeAll: Subject<any> = new Subject<any>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(
 | 
			
		||||
        private _changeDetectorRef: ChangeDetectorRef,
 | 
			
		||||
        private _chatService: ChatService,
 | 
			
		||||
        private _ngZone: NgZone
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Lifecycle hooks
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On init
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Chat
 | 
			
		||||
        this._chatService.chat$
 | 
			
		||||
            .pipe(takeUntil(this._unsubscribeAll))
 | 
			
		||||
            .subscribe((chat: Chat) => {
 | 
			
		||||
                this.chat = chat;
 | 
			
		||||
 | 
			
		||||
                // Mark for check
 | 
			
		||||
                this._changeDetectorRef.markForCheck();
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On destroy
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Unsubscribe from all subscriptions
 | 
			
		||||
        this._unsubscribeAll.next();
 | 
			
		||||
        this._unsubscribeAll.complete();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the contact info
 | 
			
		||||
     */
 | 
			
		||||
    openContactInfo(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Open the drawer
 | 
			
		||||
        this.drawerOpened = true;
 | 
			
		||||
 | 
			
		||||
        // Mark for check
 | 
			
		||||
        this._changeDetectorRef.markForCheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset the chat
 | 
			
		||||
     */
 | 
			
		||||
    resetChat(): void
 | 
			
		||||
    {
 | 
			
		||||
        this._chatService.resetChat();
 | 
			
		||||
 | 
			
		||||
        // Mark for check
 | 
			
		||||
        this._changeDetectorRef.markForCheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Toggle mute notifications
 | 
			
		||||
     */
 | 
			
		||||
    toggleMuteNotifications(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Toggle the muted
 | 
			
		||||
        this.chat.muted = !this.chat.muted;
 | 
			
		||||
 | 
			
		||||
        // Update the chat on the server
 | 
			
		||||
        this._chatService.updateChat(this.chat.id, this.chat).subscribe();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Track by function for ngFor loops
 | 
			
		||||
     *
 | 
			
		||||
     * @param index
 | 
			
		||||
     * @param item
 | 
			
		||||
     */
 | 
			
		||||
    trackByFn(index: number, item: any): any
 | 
			
		||||
    {
 | 
			
		||||
        return item.id || index;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Private methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resize on 'input' and 'ngModelChange' events
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    @HostListener('input')
 | 
			
		||||
    @HostListener('ngModelChange')
 | 
			
		||||
    private _resizeMessageInput(): void
 | 
			
		||||
    {
 | 
			
		||||
        // This doesn't need to trigger Angular's change detection by itself
 | 
			
		||||
        this._ngZone.runOutsideAngular(() => {
 | 
			
		||||
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
 | 
			
		||||
                // Set the height to 'auto' so we can correctly read the scrollHeight
 | 
			
		||||
                this.messageInput.nativeElement.style.height = 'auto';
 | 
			
		||||
 | 
			
		||||
                // Detect the changes so the height is applied
 | 
			
		||||
                this._changeDetectorRef.detectChanges();
 | 
			
		||||
 | 
			
		||||
                // Get the scrollHeight and subtract the vertical padding
 | 
			
		||||
                this.messageInput.nativeElement.style.height = `${this.messageInput.nativeElement.scrollHeight}px`;
 | 
			
		||||
 | 
			
		||||
                // Detect the changes one more time to apply the final height
 | 
			
		||||
                this._changeDetectorRef.detectChanges();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,51 @@
 | 
			
		||||
<div class="flex flex-col flex-auto h-full overflow-hidden bg-card dark:bg-default">
 | 
			
		||||
 | 
			
		||||
    <!-- Header -->
 | 
			
		||||
    <div class="flex flex-0 items-center h-18 -mb-px px-6 bg-gray-50 dark:bg-transparent">
 | 
			
		||||
        <button
 | 
			
		||||
            mat-icon-button
 | 
			
		||||
            (click)="drawer.close()">
 | 
			
		||||
            <mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
 | 
			
		||||
        </button>
 | 
			
		||||
        <div class="ml-2 text-2xl font-semibold">New chat</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="relative overflow-y-auto">
 | 
			
		||||
        <ng-container *ngIf="contacts.length; else noContacts">
 | 
			
		||||
            <ng-container *ngFor="let contact of contacts; let i = index; trackBy: trackByFn">
 | 
			
		||||
                <!-- Group -->
 | 
			
		||||
                <ng-container *ngIf="i === 0 || contact.name.charAt(0) !== contacts[i - 1].name.charAt(0)">
 | 
			
		||||
                    <div class="z-10 sticky top-0 -mt-px px-6 py-1 md:px-8 border-t border-b font-medium uppercase text-secondary bg-gray-100 dark:bg-gray-900">
 | 
			
		||||
                        {{contact.name.charAt(0)}}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
                <!-- Contact -->
 | 
			
		||||
                <div class="z-20 flex items-center px-6 py-4 md:px-8 cursor-pointer hover:bg-hover border-b">
 | 
			
		||||
                    <div class="flex flex-0 items-center justify-center w-10 h-10 rounded-full overflow-hidden">
 | 
			
		||||
                        <ng-container *ngIf="contact.avatar">
 | 
			
		||||
                            <img
 | 
			
		||||
                                class="object-cover w-full h-full"
 | 
			
		||||
                                [src]="contact.avatar"
 | 
			
		||||
                                alt="Contact avatar"/>
 | 
			
		||||
                        </ng-container>
 | 
			
		||||
                        <ng-container *ngIf="!contact.avatar">
 | 
			
		||||
                            <div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
 | 
			
		||||
                                {{contact.name.charAt(0)}}
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </ng-container>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="min-w-0 ml-4">
 | 
			
		||||
                        <div class="font-medium leading-5 truncate">{{contact.name}}</div>
 | 
			
		||||
                        <div class="leading-5 truncate text-secondary">{{contact.about}}</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- No contacts -->
 | 
			
		||||
    <ng-template #noContacts>
 | 
			
		||||
        <div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no contacts!</div>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,68 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
 | 
			
		||||
import { MatDrawer } from '@angular/material/sidenav';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { takeUntil } from 'rxjs/operators';
 | 
			
		||||
import { Contact } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
import { ChatService } from 'app/modules/admin/apps/chat/chat.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector     : 'chat-new-chat',
 | 
			
		||||
    templateUrl  : './new-chat.component.html',
 | 
			
		||||
    encapsulation: ViewEncapsulation.None,
 | 
			
		||||
    changeDetection: ChangeDetectionStrategy.OnPush
 | 
			
		||||
})
 | 
			
		||||
export class NewChatComponent implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
    @Input() drawer: MatDrawer;
 | 
			
		||||
    contacts: Contact[] = [];
 | 
			
		||||
    private _unsubscribeAll: Subject<any> = new Subject<any>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(private _chatService: ChatService)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Lifecycle hooks
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On init
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Contacts
 | 
			
		||||
        this._chatService.contacts$
 | 
			
		||||
            .pipe(takeUntil(this._unsubscribeAll))
 | 
			
		||||
            .subscribe((contacts: Contact[]) => {
 | 
			
		||||
                this.contacts = contacts;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On destroy
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Unsubscribe from all subscriptions
 | 
			
		||||
        this._unsubscribeAll.next();
 | 
			
		||||
        this._unsubscribeAll.complete();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Public methods
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Track by function for ngFor loops
 | 
			
		||||
     *
 | 
			
		||||
     * @param index
 | 
			
		||||
     * @param item
 | 
			
		||||
     */
 | 
			
		||||
    trackByFn(index: number, item: any): any
 | 
			
		||||
    {
 | 
			
		||||
        return item.id || index;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,80 @@
 | 
			
		||||
<div class="flex flex-col flex-auto overflow-y-auto bg-card dark:bg-default">
 | 
			
		||||
 | 
			
		||||
    <!-- Header -->
 | 
			
		||||
    <div class="flex flex-0 items-center h-18 px-6 border-b bg-gray-50 dark:bg-transparent">
 | 
			
		||||
        <button
 | 
			
		||||
            mat-icon-button
 | 
			
		||||
            (click)="drawer.close()">
 | 
			
		||||
            <mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
 | 
			
		||||
        </button>
 | 
			
		||||
        <div class="ml-2 text-2xl font-semibold">Profile</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="px-6">
 | 
			
		||||
        <!-- Profile photo -->
 | 
			
		||||
        <div class="group relative flex flex-0 mt-8 mx-auto w-40 h-40 rounded-full">
 | 
			
		||||
            <div class="hidden group-hover:flex absolute inset-0 flex-col items-center justify-center backdrop-filter backdrop-blur bg-opacity-80 rounded-full cursor-pointer bg-gray-800">
 | 
			
		||||
                <mat-icon
 | 
			
		||||
                    class="text-white"
 | 
			
		||||
                    [svgIcon]="'heroicons_outline:camera'"></mat-icon>
 | 
			
		||||
                <div class="mt-2 mx-6 font-medium text-center text-white">Change Profile Photo</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <ng-container *ngIf="profile.avatar">
 | 
			
		||||
                <img
 | 
			
		||||
                    class="w-full h-full rounded-full object-cover"
 | 
			
		||||
                    [src]="profile.avatar"
 | 
			
		||||
                    [alt]="'Profile avatar'">
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="!profile.avatar">
 | 
			
		||||
                <div class="flex items-center justify-center w-full h-full rounded-full text-8xl font-semibold uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
 | 
			
		||||
                    {{profile.name.charAt(0)}}
 | 
			
		||||
                </div>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- Profile info -->
 | 
			
		||||
        <div class="flex flex-col mt-8 mx-2">
 | 
			
		||||
            <mat-form-field>
 | 
			
		||||
                <mat-label>Name</mat-label>
 | 
			
		||||
                <mat-icon
 | 
			
		||||
                    class="icon-size-5"
 | 
			
		||||
                    matPrefix
 | 
			
		||||
                    [svgIcon]="'heroicons_solid:user-circle'"></mat-icon>
 | 
			
		||||
                <input
 | 
			
		||||
                    matInput
 | 
			
		||||
                    [ngModel]="profile.name">
 | 
			
		||||
            </mat-form-field>
 | 
			
		||||
            <mat-form-field>
 | 
			
		||||
                <mat-label>Email</mat-label>
 | 
			
		||||
                <mat-icon
 | 
			
		||||
                    class="icon-size-5"
 | 
			
		||||
                    matPrefix
 | 
			
		||||
                    [svgIcon]="'heroicons_solid:mail'"></mat-icon>
 | 
			
		||||
                <input
 | 
			
		||||
                    matInput
 | 
			
		||||
                    [ngModel]="profile.email">
 | 
			
		||||
            </mat-form-field>
 | 
			
		||||
            <mat-form-field>
 | 
			
		||||
                <mat-label>About</mat-label>
 | 
			
		||||
                <mat-icon
 | 
			
		||||
                    class="icon-size-5"
 | 
			
		||||
                    matPrefix
 | 
			
		||||
                    [svgIcon]="'heroicons_solid:identification'"></mat-icon>
 | 
			
		||||
                <input
 | 
			
		||||
                    matInput
 | 
			
		||||
                    [ngModel]="profile.about">
 | 
			
		||||
            </mat-form-field>
 | 
			
		||||
            <div class="flex items-center justify-end mt-4">
 | 
			
		||||
                <button
 | 
			
		||||
                    (click)="drawer.close()"
 | 
			
		||||
                    mat-button>Cancel
 | 
			
		||||
                </button>
 | 
			
		||||
                <button
 | 
			
		||||
                    class="ml-2"
 | 
			
		||||
                    mat-flat-button
 | 
			
		||||
                    [color]="'primary'">Save
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										53
									
								
								src/app/modules/admin/apps/chat/profile/profile.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/app/modules/admin/apps/chat/profile/profile.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
 | 
			
		||||
import { MatDrawer } from '@angular/material/sidenav';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { takeUntil } from 'rxjs/operators';
 | 
			
		||||
import { Profile } from 'app/modules/admin/apps/chat/chat.types';
 | 
			
		||||
import { ChatService } from 'app/modules/admin/apps/chat/chat.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector     : 'chat-profile',
 | 
			
		||||
    templateUrl  : './profile.component.html',
 | 
			
		||||
    encapsulation: ViewEncapsulation.None,
 | 
			
		||||
    changeDetection: ChangeDetectionStrategy.OnPush
 | 
			
		||||
})
 | 
			
		||||
export class ProfileComponent implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
    @Input() drawer: MatDrawer;
 | 
			
		||||
    profile: Profile;
 | 
			
		||||
    private _unsubscribeAll: Subject<any> = new Subject<any>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
    constructor(private _chatService: ChatService)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
    // @ Lifecycle hooks
 | 
			
		||||
    // -----------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On init
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Profile
 | 
			
		||||
        this._chatService.profile$
 | 
			
		||||
            .pipe(takeUntil(this._unsubscribeAll))
 | 
			
		||||
            .subscribe((profile: Profile) => {
 | 
			
		||||
                this.profile = profile;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On destroy
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Unsubscribe from all subscriptions
 | 
			
		||||
        this._unsubscribeAll.next();
 | 
			
		||||
        this._unsubscribeAll.complete();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user