forked from EVOgeek/Vmeda.Online
		
	MOBILE-3631 messages: Fix split view on pages with tabs
This commit is contained in:
		
							parent
							
								
									210518d549
								
							
						
					
					
						commit
						44552cfe3b
					
				| @ -16,8 +16,6 @@ import { Injector, NgModule } from '@angular/core'; | |||||||
| import { Route, RouterModule, ROUTES, Routes } from '@angular/router'; | import { Route, RouterModule, ROUTES, Routes } from '@angular/router'; | ||||||
| 
 | 
 | ||||||
| import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; | import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; | ||||||
| import { AddonMessagesContactsRoutingModule } from './pages/contacts/messages-contacts-routing.module'; |  | ||||||
| import { AddonMessagesIndexRoutingModule } from './pages/index-35/messages-index-routing.module'; |  | ||||||
| import { AddonMessagesSettingsHandlerService } from './services/handlers/settings'; | import { AddonMessagesSettingsHandlerService } from './services/handlers/settings'; | ||||||
| 
 | 
 | ||||||
| export const discussionRoute: Route = { | export const discussionRoute: Route = { | ||||||
| @ -30,7 +28,12 @@ function buildRoutes(injector: Injector): Routes { | |||||||
|     return [ |     return [ | ||||||
|         { |         { | ||||||
|             path: 'index', // 3.5 or lower.
 |             path: 'index', // 3.5 or lower.
 | ||||||
|             loadChildren: () => import('./pages/index-35/index.module').then( m => m.AddonMessagesIndex35PageModule), |             loadChildren: () => | ||||||
|  |                 import('./pages/discussions-35/discussions.module').then(m => m.AddonMessagesDiscussions35PageModule), | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             path: 'contacts-35', // 3.5 or lower.
 | ||||||
|  |             loadChildren: () => import('./pages/contacts-35/contacts.module').then(m => m.AddonMessagesContacts35PageModule), | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             path: 'group-conversations', // 3.6 or greater.
 |             path: 'group-conversations', // 3.6 or greater.
 | ||||||
| @ -60,37 +63,7 @@ function buildRoutes(injector: Injector): Routes { | |||||||
|     ]; |     ]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 3.5 or lower.
 |  | ||||||
| const indexTabRoutes: Routes = [ |  | ||||||
|     { |  | ||||||
|         path: 'discussions', |  | ||||||
|         loadChildren: () => import('./pages/discussions-35/discussions.module').then(m => m.AddonMessagesDiscussions35PageModule), |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         path: 'contacts', |  | ||||||
|         loadChildren: () => import('./pages/contacts-35/contacts.module').then(m => m.AddonMessagesContacts35PageModule), |  | ||||||
|     }, |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| // 3.6 or greater.
 |  | ||||||
| const contactsTabRoutes: Routes = [ |  | ||||||
|     { |  | ||||||
|         path: 'confirmed', |  | ||||||
|         loadChildren: () => import('./pages/contacts-confirmed/contacts-confirmed.module') |  | ||||||
|             .then(m => m.AddonMessagesContactsConfirmedPageModule), |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         path: 'requests', |  | ||||||
|         loadChildren: () => import('./pages/contacts-requests/contacts-requests.module') |  | ||||||
|             .then(m => m.AddonMessagesContactsRequestsPageModule), |  | ||||||
|     }, |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     imports: [ |  | ||||||
|         AddonMessagesIndexRoutingModule.forChild({ children: indexTabRoutes }), |  | ||||||
|         AddonMessagesContactsRoutingModule.forChild({ children: contactsTabRoutes }), |  | ||||||
|     ], |  | ||||||
|     exports: [RouterModule], |     exports: [RouterModule], | ||||||
|     providers: [ |     providers: [ | ||||||
|         { |         { | ||||||
|  | |||||||
| @ -1,4 +1,17 @@ | |||||||
|  | <ion-header> | ||||||
|  |     <ion-toolbar> | ||||||
|  |         <ion-buttons slot="start"> | ||||||
|  |             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||||
|  |         </ion-buttons> | ||||||
|  |         <ion-title>{{ 'addon.messages.contacts' | translate }}</ion-title> | ||||||
|  |         <ion-buttons slot="end"> | ||||||
|  |             <!-- Add an empty context menu so discussion page can add items in split view, otherwise the menu disappears in some cases. --> | ||||||
|  |             <core-context-menu></core-context-menu> | ||||||
|  |         </ion-buttons> | ||||||
|  |     </ion-toolbar> | ||||||
|  | </ion-header> | ||||||
| <ion-content> | <ion-content> | ||||||
|  |     <core-split-view> | ||||||
|         <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)"> |         <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)"> | ||||||
|             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|         </ion-refresher> |         </ion-refresher> | ||||||
| @ -32,4 +45,5 @@ | |||||||
|                 </ng-container> |                 </ng-container> | ||||||
|             </ion-list> |             </ion-list> | ||||||
|         </core-loading> |         </core-loading> | ||||||
|  |     </core-split-view> | ||||||
| </ion-content> | </ion-content> | ||||||
|  | |||||||
| @ -17,6 +17,9 @@ import { IonicModule } from '@ionic/angular'; | |||||||
| import { TranslateModule } from '@ngx-translate/core'; | import { TranslateModule } from '@ngx-translate/core'; | ||||||
| import { RouterModule, Routes } from '@angular/router'; | import { RouterModule, Routes } from '@angular/router'; | ||||||
| import { CommonModule } from '@angular/common'; | import { CommonModule } from '@angular/common'; | ||||||
|  | import { conditionalRoutes } from '@/app/app-routing.module'; | ||||||
|  | import { discussionRoute } from '@addons/messages/messages-lazy.module'; | ||||||
|  | import { CoreScreen } from '@services/screen'; | ||||||
| 
 | 
 | ||||||
| import { CoreSharedModule } from '@/core/shared.module'; | import { CoreSharedModule } from '@/core/shared.module'; | ||||||
| import { CoreSearchComponentsModule } from '@features/search/components/components.module'; | import { CoreSearchComponentsModule } from '@features/search/components/components.module'; | ||||||
| @ -25,9 +28,21 @@ import { AddonMessagesContacts35Page } from './contacts.page'; | |||||||
| 
 | 
 | ||||||
| const routes: Routes = [ | const routes: Routes = [ | ||||||
|     { |     { | ||||||
|         path: '', |         matcher: segments => { | ||||||
|         component: AddonMessagesContacts35Page, |             const matches = CoreScreen.instance.isMobile ? segments.length === 0 : true; | ||||||
|  | 
 | ||||||
|  |             return matches ? { consumed: [] } : null; | ||||||
|         }, |         }, | ||||||
|  |         component: AddonMessagesContacts35Page, | ||||||
|  |         children: conditionalRoutes([ | ||||||
|  |             { | ||||||
|  |                 path: '', | ||||||
|  |                 pathMatch: 'full', | ||||||
|  |             }, | ||||||
|  |             discussionRoute, | ||||||
|  |         ], () => CoreScreen.instance.isTablet), | ||||||
|  |     }, | ||||||
|  |     ...conditionalRoutes([discussionRoute], () => CoreScreen.instance.isMobile), | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|  | |||||||
| @ -21,14 +21,15 @@ import { | |||||||
|     AddonMessagesSearchContactsContact, |     AddonMessagesSearchContactsContact, | ||||||
|     AddonMessagesGetContactsContact, |     AddonMessagesGetContactsContact, | ||||||
|     AddonMessages, |     AddonMessages, | ||||||
|     AddonMessagesSplitViewLoadIndexEventData, |  | ||||||
|     AddonMessagesMemberInfoChangedEventData, |     AddonMessagesMemberInfoChangedEventData, | ||||||
| } from '../../services/messages'; | } from '../../services/messages'; | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreApp } from '@services/app'; | import { CoreApp } from '@services/app'; | ||||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; | import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||||
| import { ActivatedRoute } from '@angular/router'; | import { ActivatedRoute, Params } from '@angular/router'; | ||||||
| import { Translate } from '@singletons'; | import { Translate } from '@singletons'; | ||||||
|  | import { CoreScreen } from '@services/screen'; | ||||||
|  | import { CoreNavigator } from '@services/navigator'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that displays the list of contacts. |  * Page that displays the list of contacts. | ||||||
| @ -89,7 +90,15 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy { | |||||||
|      */ |      */ | ||||||
|     ngOnInit(): void { |     ngOnInit(): void { | ||||||
|         this.route.queryParams.subscribe(async params => { |         this.route.queryParams.subscribe(async params => { | ||||||
|             this.discussionUserId = params['discussionUserId'] || undefined; |             const discussionUserId = params['discussionUserId'] | ||||||
|  |                 ? parseInt(params['discussionUserId'], 10) | ||||||
|  |                 : (params['userId'] ? parseInt(params['userId'], 10) : undefined); | ||||||
|  | 
 | ||||||
|  |             if (this.loaded && this.discussionUserId == discussionUserId) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.discussionUserId = discussionUserId; | ||||||
| 
 | 
 | ||||||
|             if (this.discussionUserId) { |             if (this.discussionUserId) { | ||||||
|                 // There is a discussion to load, open the discussion in a new state.
 |                 // There is a discussion to load, open the discussion in a new state.
 | ||||||
| @ -98,7 +107,7 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|             try { |             try { | ||||||
|                 await this.fetchData(); |                 await this.fetchData(); | ||||||
|                 if (!this.discussionUserId && this.hasContacts) { |                 if (!this.discussionUserId && this.hasContacts && CoreScreen.instance.isTablet) { | ||||||
|                     let contact: AddonMessagesGetContactsContact | undefined; |                     let contact: AddonMessagesGetContactsContact | undefined; | ||||||
|                     for (const x in this.contacts) { |                     for (const x in this.contacts) { | ||||||
|                         if (this.contacts[x].length > 0) { |                         if (this.contacts[x].length > 0) { | ||||||
| @ -109,7 +118,7 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|                     if (contact) { |                     if (contact) { | ||||||
|                         // Take first and load it.
 |                         // Take first and load it.
 | ||||||
|                         this.gotoDiscussion(contact.id, true); |                         this.gotoDiscussion(contact.id); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } finally { |             } finally { | ||||||
| @ -235,16 +244,18 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy { | |||||||
|      * Navigate to a particular discussion. |      * Navigate to a particular discussion. | ||||||
|      * |      * | ||||||
|      * @param discussionUserId Discussion Id to load. |      * @param discussionUserId Discussion Id to load. | ||||||
|      * @param onlyWithSplitView Only go to Discussion if split view is on. |  | ||||||
|      */ |      */ | ||||||
|     gotoDiscussion(discussionUserId: number, onlyWithSplitView: boolean = false): void { |     gotoDiscussion(discussionUserId: number): void { | ||||||
|         this.discussionUserId = discussionUserId; |         this.discussionUserId = discussionUserId; | ||||||
| 
 | 
 | ||||||
|         const params: AddonMessagesSplitViewLoadIndexEventData = { |         const params: Params = { | ||||||
|             discussion: discussionUserId, |             userId: discussionUserId, | ||||||
|             onlyWithSplitView: onlyWithSplitView, |  | ||||||
|         }; |         }; | ||||||
|         CoreEvents.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_INDEX_EVENT, params, this.siteId); | 
 | ||||||
|  |         const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/contacts-35/discussion'); | ||||||
|  |         const path = (splitViewLoaded ? '../' : '') + 'discussion'; | ||||||
|  | 
 | ||||||
|  |         CoreNavigator.instance.navigate(path, { params }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -1,30 +0,0 @@ | |||||||
| <ion-content> |  | ||||||
|     <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)"> |  | ||||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |  | ||||||
|     </ion-refresher> |  | ||||||
|     <core-loading [hideUntil]="loaded" class="core-loading-center"> |  | ||||||
|         <ion-list  class="ion-no-margin"> |  | ||||||
|             <ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let contact of contacts" |  | ||||||
|                 [title]="contact.fullname" (click)="selectUser(contact.id)" detail |  | ||||||
|                 [class.core-selected-item]="contact.id == selectedUserId"> |  | ||||||
|                 <core-user-avatar slot="start" core-user-avatar [user]="contact" [checkOnline]="contact.showonlinestatus" |  | ||||||
|                     [linkProfile]="false"></core-user-avatar> |  | ||||||
|                 <ion-label> |  | ||||||
|                     <h2> |  | ||||||
|                         <core-format-text [text]="contact.fullname" contextLevel="system" [contextInstanceId]="0"></core-format-text> |  | ||||||
|                         <ion-icon *ngIf="contact.isblocked" name="fas-user-slash" slot="end"> |  | ||||||
|                         </ion-icon> |  | ||||||
|                     </h2> |  | ||||||
|                 </ion-label> |  | ||||||
|             </ion-item> |  | ||||||
|         </ion-list> |  | ||||||
| 
 |  | ||||||
|         <core-empty-box *ngIf="!contacts.length" icon="far-address-book" |  | ||||||
|             [message]="'addon.messages.nocontactsgetstarted' | translate"> |  | ||||||
|         </core-empty-box> |  | ||||||
| 
 |  | ||||||
|         <core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError" |  | ||||||
|             position="bottom"> |  | ||||||
|         </core-infinite-loading> |  | ||||||
|     </core-loading> |  | ||||||
| </ion-content> |  | ||||||
| @ -1,45 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { NgModule } from '@angular/core'; |  | ||||||
| import { IonicModule } from '@ionic/angular'; |  | ||||||
| import { TranslateModule } from '@ngx-translate/core'; |  | ||||||
| import { RouterModule, Routes } from '@angular/router'; |  | ||||||
| import { CommonModule } from '@angular/common'; |  | ||||||
| 
 |  | ||||||
| import { CoreSharedModule } from '@/core/shared.module'; |  | ||||||
| 
 |  | ||||||
| import { AddonMessagesContactsConfirmedPage } from './contacts-confirmed.page'; |  | ||||||
| 
 |  | ||||||
| const routes: Routes = [ |  | ||||||
|     { |  | ||||||
|         path: '', |  | ||||||
|         component: AddonMessagesContactsConfirmedPage, |  | ||||||
|     }, |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| @NgModule({ |  | ||||||
|     imports: [ |  | ||||||
|         RouterModule.forChild(routes), |  | ||||||
|         CommonModule, |  | ||||||
|         IonicModule, |  | ||||||
|         TranslateModule.forChild(), |  | ||||||
|         CoreSharedModule, |  | ||||||
|     ], |  | ||||||
|     declarations: [ |  | ||||||
|         AddonMessagesContactsConfirmedPage, |  | ||||||
|     ], |  | ||||||
|     exports: [RouterModule], |  | ||||||
| }) |  | ||||||
| export class AddonMessagesContactsConfirmedPageModule {} |  | ||||||
| @ -1,162 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; |  | ||||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; |  | ||||||
| import { CoreSites } from '@services/sites'; |  | ||||||
| import { |  | ||||||
|     AddonMessagesProvider, |  | ||||||
|     AddonMessagesConversationMember, |  | ||||||
|     AddonMessages, |  | ||||||
|     AddonMessagesMemberInfoChangedEventData, |  | ||||||
|     AddonMessagesSplitViewLoadContactsEventData, |  | ||||||
| } from '../../services/messages'; |  | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; |  | ||||||
| import { IonRefresher } from '@ionic/angular'; |  | ||||||
| import { CoreScreen } from '@services/screen'; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Component that displays the list of confirmed contacts. |  | ||||||
|  */ |  | ||||||
| @Component({ |  | ||||||
|     selector: 'addon-messages-confirmed-contacts', |  | ||||||
|     templateUrl: 'contacts-confirmed.html', |  | ||||||
|     styleUrls: ['../../messages-common.scss'], |  | ||||||
| }) |  | ||||||
| export class AddonMessagesContactsConfirmedPage implements OnInit, OnDestroy { |  | ||||||
| 
 |  | ||||||
|     loaded = false; |  | ||||||
|     canLoadMore = false; |  | ||||||
|     loadMoreError = false; |  | ||||||
|     contacts: AddonMessagesConversationMember[] = []; |  | ||||||
|     selectedUserId?: number; |  | ||||||
| 
 |  | ||||||
|     protected memberInfoObserver: CoreEventObserver; |  | ||||||
| 
 |  | ||||||
|     constructor() { |  | ||||||
|         // Update block status of a user.
 |  | ||||||
|         this.memberInfoObserver = CoreEvents.on<AddonMessagesMemberInfoChangedEventData>( |  | ||||||
|             AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, |  | ||||||
|             (data) => { |  | ||||||
|                 if (data.userBlocked || data.userUnblocked) { |  | ||||||
|                     const user = this.contacts.find((user) => user.id == data.userId); |  | ||||||
|                     if (user) { |  | ||||||
|                         user.isblocked = !!data.userBlocked; |  | ||||||
|                     } |  | ||||||
|                 } else if (data.contactRemoved) { |  | ||||||
|                     const index = this.contacts.findIndex((contact) => contact.id == data.userId); |  | ||||||
|                     if (index >= 0) { |  | ||||||
|                         this.contacts.splice(index, 1); |  | ||||||
|                     } |  | ||||||
|                 } else if (data.contactRequestConfirmed) { |  | ||||||
|                     this.refreshData(); |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             CoreSites.instance.getCurrentSiteId(), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Component loaded. |  | ||||||
|      */ |  | ||||||
|     async ngOnInit(): Promise<void> { |  | ||||||
|         try { |  | ||||||
|             await this.fetchData(); |  | ||||||
|             if (this.contacts.length && CoreScreen.instance.isTablet) { |  | ||||||
|                 this.selectUser(this.contacts[0].id, true); |  | ||||||
|             } |  | ||||||
|         } finally { |  | ||||||
|             this.loaded = true; |  | ||||||
|         } |  | ||||||
|         // Workaround for infinite scrolling.
 |  | ||||||
|         // @todo this.content.resize();
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Fetch contacts. |  | ||||||
|      * |  | ||||||
|      * @param refresh True if we are refreshing contacts, false if we are loading more. |  | ||||||
|      * @return Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     async fetchData(refresh: boolean = false): Promise<void> { |  | ||||||
|         this.loadMoreError = false; |  | ||||||
| 
 |  | ||||||
|         const limitFrom = refresh ? 0 : this.contacts.length; |  | ||||||
| 
 |  | ||||||
|         if (limitFrom === 0) { |  | ||||||
|             // Always try to get latest data from server.
 |  | ||||||
|             await AddonMessages.instance.invalidateUserContacts(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             const result = await AddonMessages.instance.getUserContacts(limitFrom); |  | ||||||
|             this.contacts = refresh ? result.contacts : this.contacts.concat(result.contacts); |  | ||||||
|             this.canLoadMore = result.canLoadMore; |  | ||||||
|         } catch (error) { |  | ||||||
|             this.loadMoreError = true; |  | ||||||
|             CoreDomUtils.instance.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Refresh contacts. |  | ||||||
|      * |  | ||||||
|      * @param refresher Refresher. |  | ||||||
|      * @return Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> { |  | ||||||
|         // No need to invalidate contacts, we always try to get the latest.
 |  | ||||||
|         await this.fetchData(true).finally(() => { |  | ||||||
|             refresher?.detail.complete(); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Load more contacts. |  | ||||||
|      * |  | ||||||
|      * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. |  | ||||||
|      * @return Resolved when done. |  | ||||||
|      */ |  | ||||||
|     async loadMore(infiniteComplete?: () => void): Promise<void> { |  | ||||||
|         await this.fetchData().finally(() => { |  | ||||||
|             infiniteComplete && infiniteComplete(); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Notify that a contact has been selected. |  | ||||||
|      * |  | ||||||
|      * @param userId User id. |  | ||||||
|      * @param onInit Whether the contact is selected on initial load. |  | ||||||
|      */ |  | ||||||
|     selectUser(userId: number, onInit: boolean = false): void { |  | ||||||
|         this.selectedUserId = userId; |  | ||||||
| 
 |  | ||||||
|         CoreEvents.trigger<AddonMessagesSplitViewLoadContactsEventData>( |  | ||||||
|             AddonMessagesProvider.SPLIT_VIEW_LOAD_CONTACTS_EVENT, |  | ||||||
|             { |  | ||||||
|                 userId, |  | ||||||
|                 onInit, |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Component destroyed. |  | ||||||
|      */ |  | ||||||
|     ngOnDestroy(): void { |  | ||||||
|         this.memberInfoObserver?.off(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -1,24 +0,0 @@ | |||||||
| <ion-content> |  | ||||||
|     <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)"> |  | ||||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |  | ||||||
|     </ion-refresher> |  | ||||||
|     <core-loading [hideUntil]="loaded" class="core-loading-center"> |  | ||||||
|         <ion-list  class="ion-no-margin"> |  | ||||||
|             <ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let request of requests" |  | ||||||
|                 [title]="request.fullname" (click)="selectUser(request.id)" |  | ||||||
|                 [class.core-selected-item]="request.id == selectedUserId" detail> |  | ||||||
|                 <core-user-avatar slot="start" [user]="request" [linkProfile]="false"></core-user-avatar> |  | ||||||
|                 <ion-label> |  | ||||||
|                     <core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0"></core-format-text> |  | ||||||
|                     <p *ngIf="!request.iscontact"> |  | ||||||
|                         {{ 'addon.messages.wouldliketocontactyou' | translate }} |  | ||||||
|                     </p> |  | ||||||
|                 </ion-label> |  | ||||||
|             </ion-item> |  | ||||||
|         </ion-list> |  | ||||||
|         <core-empty-box *ngIf="!requests.length" icon="far-address-book" [message]="'addon.messages.nocontactrequests' | translate"> |  | ||||||
|         </core-empty-box> |  | ||||||
|         <core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError" position="bottom"> |  | ||||||
|         </core-infinite-loading> |  | ||||||
|     </core-loading> |  | ||||||
| </ion-content> |  | ||||||
| @ -1,45 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { NgModule } from '@angular/core'; |  | ||||||
| import { IonicModule } from '@ionic/angular'; |  | ||||||
| import { TranslateModule } from '@ngx-translate/core'; |  | ||||||
| import { RouterModule, Routes } from '@angular/router'; |  | ||||||
| import { CommonModule } from '@angular/common'; |  | ||||||
| 
 |  | ||||||
| import { CoreSharedModule } from '@/core/shared.module'; |  | ||||||
| 
 |  | ||||||
| import { AddonMessagesContactsRequestsPage } from './contacts-requests.page'; |  | ||||||
| 
 |  | ||||||
| const routes: Routes = [ |  | ||||||
|     { |  | ||||||
|         path: '', |  | ||||||
|         component: AddonMessagesContactsRequestsPage, |  | ||||||
|     }, |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| @NgModule({ |  | ||||||
|     imports: [ |  | ||||||
|         RouterModule.forChild(routes), |  | ||||||
|         CommonModule, |  | ||||||
|         IonicModule, |  | ||||||
|         TranslateModule.forChild(), |  | ||||||
|         CoreSharedModule, |  | ||||||
|     ], |  | ||||||
|     declarations: [ |  | ||||||
|         AddonMessagesContactsRequestsPage, |  | ||||||
|     ], |  | ||||||
|     exports: [RouterModule], |  | ||||||
| }) |  | ||||||
| export class AddonMessagesContactsRequestsPageModule {} |  | ||||||
| @ -1,160 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; |  | ||||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; |  | ||||||
| import { CoreSites } from '@services/sites'; |  | ||||||
| import { |  | ||||||
|     AddonMessagesProvider, |  | ||||||
|     AddonMessagesConversationMember, |  | ||||||
|     AddonMessagesSplitViewLoadContactsEventData, |  | ||||||
|     AddonMessages, |  | ||||||
|     AddonMessagesMemberInfoChangedEventData, |  | ||||||
| } from '../../services/messages'; |  | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; |  | ||||||
| import { CoreNavigator } from '@services/navigator'; |  | ||||||
| import { IonRefresher } from '@ionic/angular'; |  | ||||||
| import { CoreScreen } from '@services/screen'; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Component that displays the list of contact requests. |  | ||||||
|  */ |  | ||||||
| @Component({ |  | ||||||
|     selector: 'addon-messages-contact-requests', |  | ||||||
|     templateUrl: 'contacts-requests.html', |  | ||||||
|     styleUrls: ['../../messages-common.scss'], |  | ||||||
| }) |  | ||||||
| export class AddonMessagesContactsRequestsPage implements OnInit, OnDestroy { |  | ||||||
| 
 |  | ||||||
|     loaded = false; |  | ||||||
|     canLoadMore = false; |  | ||||||
|     loadMoreError = false; |  | ||||||
|     requests: AddonMessagesConversationMember[] = []; |  | ||||||
|     selectedUserId?: number; |  | ||||||
| 
 |  | ||||||
|     protected memberInfoObserver: CoreEventObserver; |  | ||||||
| 
 |  | ||||||
|     constructor() { |  | ||||||
| 
 |  | ||||||
|         // Hide the "Would like to contact you" message when a contact request is confirmed.
 |  | ||||||
|         this.memberInfoObserver = CoreEvents.on<AddonMessagesMemberInfoChangedEventData>( |  | ||||||
|             AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, |  | ||||||
|             (data) => { |  | ||||||
|                 if (data.contactRequestConfirmed || data.contactRequestDeclined) { |  | ||||||
|                     const index = this.requests.findIndex((request) => request.id == data.userId); |  | ||||||
|                     if (index >= 0) { |  | ||||||
|                         this.requests.splice(index, 1); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
| 
 |  | ||||||
|             CoreSites.instance.getCurrentSiteId(), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Component loaded. |  | ||||||
|      */ |  | ||||||
|     async ngOnInit(): Promise<void> { |  | ||||||
|         try { |  | ||||||
|             await this.fetchData(); |  | ||||||
|             if (this.requests.length && CoreScreen.instance.isTablet) { |  | ||||||
|                 this.selectUser(this.requests[0].id, true); |  | ||||||
|             } |  | ||||||
|         } finally { |  | ||||||
|             this.loaded = true; |  | ||||||
|         } |  | ||||||
|         // Workaround for infinite scrolling.
 |  | ||||||
|         // @todo this.content.resize();
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Fetch contact requests. |  | ||||||
|      * |  | ||||||
|      * @param refresh True if we are refreshing contact requests, false if we are loading more. |  | ||||||
|      * @return Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     async fetchData(refresh: boolean = false): Promise<void> { |  | ||||||
|         this.loadMoreError = false; |  | ||||||
| 
 |  | ||||||
|         const limitFrom = refresh ? 0 : this.requests.length; |  | ||||||
| 
 |  | ||||||
|         if (limitFrom === 0) { |  | ||||||
|             // Always try to get latest data from server.
 |  | ||||||
|             await AddonMessages.instance.invalidateContactRequestsCache(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             const result = await AddonMessages.instance.getContactRequests(limitFrom); |  | ||||||
|             this.requests = refresh ? result.requests : this.requests.concat(result.requests); |  | ||||||
|             this.canLoadMore = result.canLoadMore; |  | ||||||
|         } catch (error) { |  | ||||||
|             this.loadMoreError = true; |  | ||||||
|             CoreDomUtils.instance.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Refresh contact requests. |  | ||||||
|      * |  | ||||||
|      * @param refresher Refresher. |  | ||||||
|      * @return Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> { |  | ||||||
|         // Refresh the number of contacts requests to update badges.
 |  | ||||||
|         AddonMessages.instance.refreshContactRequestsCount(); |  | ||||||
| 
 |  | ||||||
|         // No need to invalidate contact requests, we always try to get the latest.
 |  | ||||||
|         await this.fetchData(true).finally(() => { |  | ||||||
|             refresher?.detail.complete(); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Load more contact requests. |  | ||||||
|      * |  | ||||||
|      * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. |  | ||||||
|      * @return Resolved when done. |  | ||||||
|      */ |  | ||||||
|     async loadMore(infiniteComplete?: () => void): Promise<void> { |  | ||||||
|         await this.fetchData().finally(() => { |  | ||||||
|             infiniteComplete && infiniteComplete(); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Notify that a contact has been selected. |  | ||||||
|      * |  | ||||||
|      * @param userId User id. |  | ||||||
|      * @param onInit Whether the contact is selected on initial load. |  | ||||||
|      */ |  | ||||||
|     selectUser(userId: number, onInit: boolean = false): void { |  | ||||||
|         this.selectedUserId = userId; |  | ||||||
| 
 |  | ||||||
|         const data: AddonMessagesSplitViewLoadContactsEventData = { |  | ||||||
|             userId, |  | ||||||
|             onInit, |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         CoreEvents.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_CONTACTS_EVENT, data); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Component destroyed. |  | ||||||
|      */ |  | ||||||
|     ngOnDestroy(): void { |  | ||||||
|         this.memberInfoObserver?.off(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -15,6 +15,77 @@ | |||||||
| </ion-header> | </ion-header> | ||||||
| <ion-content> | <ion-content> | ||||||
|     <core-split-view> |     <core-split-view> | ||||||
|         <core-tabs [tabs]="tabs" hideUntil="true"></core-tabs> |         <ion-tab-bar class="core-tabs-bar"> | ||||||
|  |             <ion-row> | ||||||
|  |                 <ion-col class="tab-slide" [attr.aria-selected]="selected == 'confirmed'" (click)="selectTab('confirmed', $event)"> | ||||||
|  |                     <ion-label>{{ 'addon.messages.contacts' | translate}}</ion-label> | ||||||
|  |                 </ion-col> | ||||||
|  |                 <ion-col class="tab-slide" [attr.aria-selected]="selected != 'confirmed'" (click)="selectTab('requests', $event)"> | ||||||
|  |                     <ion-label> | ||||||
|  |                         {{ 'addon.messages.requests' | translate}} | ||||||
|  |                         <ion-badge *ngIf="requestsBadge">{{ requestsBadge }}</ion-badge> | ||||||
|  |                     </ion-label> | ||||||
|  |                 </ion-col> | ||||||
|  |             </ion-row> | ||||||
|  |         </ion-tab-bar> | ||||||
|  |         <div *ngIf="selected == 'confirmed'"> | ||||||
|  |             <ion-refresher slot="fixed" [disabled]="!confirmedLoaded" (ionRefresh)="refreshData($event)"> | ||||||
|  |                 <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|  |             </ion-refresher> | ||||||
|  |             <core-loading [hideUntil]="confirmedLoaded" class="core-loading-center"> | ||||||
|  |                 <ion-list  class="ion-no-margin"> | ||||||
|  |                     <ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let contact of confirmedContacts" | ||||||
|  |                         [title]="contact.fullname" (click)="selectUser(contact.id)" detail | ||||||
|  |                         [class.core-selected-item]="contact.id == selectedUserId"> | ||||||
|  |                         <core-user-avatar slot="start" core-user-avatar [user]="contact" [checkOnline]="contact.showonlinestatus" | ||||||
|  |                             [linkProfile]="false"></core-user-avatar> | ||||||
|  |                         <ion-label> | ||||||
|  |                             <h2> | ||||||
|  |                                 <core-format-text [text]="contact.fullname" contextLevel="system" [contextInstanceId]="0"> | ||||||
|  |                                 </core-format-text> | ||||||
|  |                                 <ion-icon *ngIf="contact.isblocked" name="fas-user-slash" slot="end"> | ||||||
|  |                                 </ion-icon> | ||||||
|  |                             </h2> | ||||||
|  |                         </ion-label> | ||||||
|  |                     </ion-item> | ||||||
|  |                 </ion-list> | ||||||
|  | 
 | ||||||
|  |                 <core-empty-box *ngIf="!confirmedContacts.length" icon="far-address-book" | ||||||
|  |                     [message]="'addon.messages.nocontactsgetstarted' | translate"> | ||||||
|  |                 </core-empty-box> | ||||||
|  | 
 | ||||||
|  |                 <core-infinite-loading [enabled]="confirmedCanLoadMore" (action)="loadMore($event)" [error]="confirmedLoadMoreError" | ||||||
|  |                     position="bottom"> | ||||||
|  |                 </core-infinite-loading> | ||||||
|  |             </core-loading> | ||||||
|  |         </div> | ||||||
|  |         <div  *ngIf="selected != 'confirmed'"> | ||||||
|  |             <ion-refresher slot="fixed" [disabled]="!requestsLoaded" (ionRefresh)="refreshData($event)"> | ||||||
|  |                 <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|  |             </ion-refresher> | ||||||
|  |             <core-loading [hideUntil]="requestsLoaded" class="core-loading-center"> | ||||||
|  |                 <ion-list  class="ion-no-margin"> | ||||||
|  |                     <ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let request of requests" | ||||||
|  |                         [title]="request.fullname" (click)="selectUser(request.id)" | ||||||
|  |                         [class.core-selected-item]="request.id == selectedUserId" detail> | ||||||
|  |                         <core-user-avatar slot="start" [user]="request" [linkProfile]="false"></core-user-avatar> | ||||||
|  |                         <ion-label> | ||||||
|  |                             <core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0"> | ||||||
|  |                             </core-format-text> | ||||||
|  |                             <p *ngIf="!request.iscontact"> | ||||||
|  |                                 {{ 'addon.messages.wouldliketocontactyou' | translate }} | ||||||
|  |                             </p> | ||||||
|  |                         </ion-label> | ||||||
|  |                     </ion-item> | ||||||
|  |                 </ion-list> | ||||||
|  |                 <core-empty-box *ngIf="!requests.length" icon="far-address-book" | ||||||
|  |                     [message]="'addon.messages.nocontactrequests' | translate"> | ||||||
|  |                 </core-empty-box> | ||||||
|  |                 <core-infinite-loading [enabled]="requestsCanLoadMore" (action)="loadMore($event)" [error]="requestsLoadMoreError" | ||||||
|  |                     position="bottom"> | ||||||
|  |                 </core-infinite-loading> | ||||||
|  |             </core-loading> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|     </core-split-view> |     </core-split-view> | ||||||
| </ion-content> | </ion-content> | ||||||
|  | |||||||
| @ -12,22 +12,19 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injector, NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||||
| import { IonicModule } from '@ionic/angular'; | import { IonicModule } from '@ionic/angular'; | ||||||
| import { TranslateModule } from '@ngx-translate/core'; | import { TranslateModule } from '@ngx-translate/core'; | ||||||
| import { RouterModule, ROUTES, Routes } from '@angular/router'; | import { RouterModule, Routes } from '@angular/router'; | ||||||
| import { CommonModule } from '@angular/common'; | import { CommonModule } from '@angular/common'; | ||||||
| import { conditionalRoutes, resolveModuleRoutes } from '@/app/app-routing.module'; | import { conditionalRoutes } from '@/app/app-routing.module'; | ||||||
| import { discussionRoute } from '@addons/messages/messages-lazy.module'; | import { discussionRoute } from '@addons/messages/messages-lazy.module'; | ||||||
| import { CoreScreen } from '@services/screen'; | import { CoreScreen } from '@services/screen'; | ||||||
| 
 | 
 | ||||||
| import { CoreSharedModule } from '@/core/shared.module'; | import { CoreSharedModule } from '@/core/shared.module'; | ||||||
| 
 | 
 | ||||||
| import { ADDON_MESSAGES_CONTACTS_ROUTES } from './messages-contacts-routing.module'; |  | ||||||
| import { AddonMessagesContactsPage } from './contacts.page'; | import { AddonMessagesContactsPage } from './contacts.page'; | ||||||
| import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; |  | ||||||
| 
 | 
 | ||||||
| // @todo mix both routes to get messages/contacts/requests/discussion and messages/contacts/confirmed/discussion working
 |  | ||||||
| const routes: Routes = [ | const routes: Routes = [ | ||||||
|     { |     { | ||||||
|         matcher: segments => { |         matcher: segments => { | ||||||
| @ -47,19 +44,6 @@ const routes: Routes = [ | |||||||
|     ...conditionalRoutes([discussionRoute], () => CoreScreen.instance.isMobile), |     ...conditionalRoutes([discussionRoute], () => CoreScreen.instance.isMobile), | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| function buildRoutes(injector: Injector): Routes { |  | ||||||
|     const routes = resolveModuleRoutes(injector, ADDON_MESSAGES_CONTACTS_ROUTES); |  | ||||||
| 
 |  | ||||||
|     return [ |  | ||||||
|         ...buildTabMainRoutes(injector, { |  | ||||||
|             path: '', |  | ||||||
|             component: AddonMessagesContactsPage, |  | ||||||
|             children: routes.children, |  | ||||||
|         }), |  | ||||||
|         ...routes.siblings, |  | ||||||
|     ]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     imports: [ |     imports: [ | ||||||
|         RouterModule.forChild(routes), |         RouterModule.forChild(routes), | ||||||
| @ -68,9 +52,6 @@ function buildRoutes(injector: Injector): Routes { | |||||||
|         TranslateModule.forChild(), |         TranslateModule.forChild(), | ||||||
|         CoreSharedModule, |         CoreSharedModule, | ||||||
|     ], |     ], | ||||||
|     providers: [ |  | ||||||
|         { provide: ROUTES, multi: true, useFactory: buildRoutes, deps: [Injector] }, |  | ||||||
|     ], |  | ||||||
|     declarations: [ |     declarations: [ | ||||||
|         AddonMessagesContactsPage, |         AddonMessagesContactsPage, | ||||||
|     ], |     ], | ||||||
|  | |||||||
| @ -18,12 +18,14 @@ import { CoreSites } from '@services/sites'; | |||||||
| import { | import { | ||||||
|     AddonMessages, |     AddonMessages, | ||||||
|     AddonMessagesContactRequestCountEventData, |     AddonMessagesContactRequestCountEventData, | ||||||
|  |     AddonMessagesConversationMember, | ||||||
|  |     AddonMessagesMemberInfoChangedEventData, | ||||||
|     AddonMessagesProvider, |     AddonMessagesProvider, | ||||||
|     AddonMessagesSplitViewLoadContactsEventData, |  | ||||||
| } from '../../services/messages'; | } from '../../services/messages'; | ||||||
| import { CoreTab } from '@components/tabs/tabs'; |  | ||||||
| import { CoreNavigator } from '@services/navigator'; | import { CoreNavigator } from '@services/navigator'; | ||||||
| import { CoreScreen } from '@services/screen'; | import { CoreScreen } from '@services/screen'; | ||||||
|  | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
|  | import { IonRefresher } from '@ionic/angular'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that displays contacts and contact requests. |  * Page that displays contacts and contact requests. | ||||||
| @ -31,56 +33,71 @@ import { CoreScreen } from '@services/screen'; | |||||||
| @Component({ | @Component({ | ||||||
|     selector: 'page-addon-messages-contacts', |     selector: 'page-addon-messages-contacts', | ||||||
|     templateUrl: 'contacts.html', |     templateUrl: 'contacts.html', | ||||||
|  |     styleUrls: [ | ||||||
|  |         'tabs.scss', | ||||||
|  |         '../../messages-common.scss', | ||||||
|  |     ], | ||||||
| }) | }) | ||||||
| export class AddonMessagesContactsPage implements OnInit, OnDestroy { | export class AddonMessagesContactsPage implements OnInit, OnDestroy { | ||||||
| 
 | 
 | ||||||
|     protected contactsTab: CoreTab =         { |     selected = 'confirmed'; | ||||||
|         id: 'contacts-confirmed', |     requestsBadge = ''; | ||||||
|         class: '', |     selectedUserId?: number; // User id of the conversation opened in the split view.
 | ||||||
|         title: 'addon.messages.contacts', |  | ||||||
|         icon: 'fas-address-book', |  | ||||||
|         enabled: true, |  | ||||||
|         page: '/main/messages/contacts/confirmed', |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     protected requestsTab: CoreTab = { |     confirmedLoaded = false; | ||||||
|         id: 'contact-requests', |     confirmedCanLoadMore = false; | ||||||
|         class: '', |     confirmedLoadMoreError = false; | ||||||
|         title: 'addon.messages.requests', |     confirmedContacts: AddonMessagesConversationMember[] = []; | ||||||
|         icon: 'fas-user-plus', |  | ||||||
|         enabled: true, |  | ||||||
|         page: '/main/messages/contacts/requests', |  | ||||||
|         badge: '', |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     tabs: CoreTab[] = []; |     requestsLoaded = false; | ||||||
|  |     requestsCanLoadMore = false; | ||||||
|  |     requestsLoadMoreError = false; | ||||||
|  |     requests: AddonMessagesConversationMember[] = []; | ||||||
| 
 | 
 | ||||||
|     protected siteId: string; |     protected siteId: string; | ||||||
|     protected contactRequestsCountObserver: CoreEventObserver; |     protected contactRequestsCountObserver: CoreEventObserver; | ||||||
|     protected splitViewObserver: CoreEventObserver; |     protected memberInfoObserver: CoreEventObserver; | ||||||
|     protected selectedUserId?: number; // User id of the conversation opened in the split view.
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
| 
 | 
 | ||||||
|         this.tabs = [this.contactsTab, this.requestsTab]; |  | ||||||
| 
 |  | ||||||
|         this.siteId = CoreSites.instance.getCurrentSiteId(); |         this.siteId = CoreSites.instance.getCurrentSiteId(); | ||||||
| 
 | 
 | ||||||
|         // Update the contact requests badge.
 |         // Update the contact requests badge.
 | ||||||
|         this.contactRequestsCountObserver = CoreEvents.on<AddonMessagesContactRequestCountEventData>( |         this.contactRequestsCountObserver = CoreEvents.on<AddonMessagesContactRequestCountEventData>( | ||||||
|             AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, |             AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, | ||||||
|             (data) => { |             (data) => { | ||||||
|                 this.requestsTab.badge = data.count > 0 ? String(data.count) : ''; |                 this.requestsBadge = data.count > 0 ? String(data.count) : ''; | ||||||
|             }, |             }, | ||||||
|             this.siteId, |             this.siteId, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         // Update the contact requests badge.
 |         // Update block status of a user.
 | ||||||
|         this.splitViewObserver = CoreEvents.on<AddonMessagesSplitViewLoadContactsEventData>( |         this.memberInfoObserver = CoreEvents.on<AddonMessagesMemberInfoChangedEventData>( | ||||||
|             AddonMessagesProvider.SPLIT_VIEW_LOAD_CONTACTS_EVENT, |             AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, | ||||||
|             (data) => { |             (data) => { | ||||||
|                 this.selectUser(data.userId, data.onInit); |                 if (data.userBlocked || data.userUnblocked) { | ||||||
|  |                     const user = this.confirmedContacts.find((user) => user.id == data.userId); | ||||||
|  |                     if (user) { | ||||||
|  |                         user.isblocked = !!data.userBlocked; | ||||||
|  |                     } | ||||||
|  |                 } else if (data.contactRemoved) { | ||||||
|  |                     const index = this.confirmedContacts.findIndex((contact) => contact.id == data.userId); | ||||||
|  |                     if (index >= 0) { | ||||||
|  |                         this.confirmedContacts.splice(index, 1); | ||||||
|  |                     } | ||||||
|  |                 } else if (data.contactRequestConfirmed) { | ||||||
|  |                     this.refreshData(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (data.contactRequestConfirmed || data.contactRequestDeclined) { | ||||||
|  |                     const index = this.requests.findIndex((request) => request.id == data.userId); | ||||||
|  |                     if (index >= 0) { | ||||||
|  |                         this.requests.splice(index, 1); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             }, |             }, | ||||||
|  |             CoreSites.instance.getCurrentSiteId(), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| @ -88,8 +105,123 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy { | |||||||
|     /** |     /** | ||||||
|      * Page being initialized. |      * Page being initialized. | ||||||
|      */ |      */ | ||||||
|     ngOnInit(): void { |     async ngOnInit(): Promise<void> { | ||||||
|         AddonMessages.instance.getContactRequestsCount(this.siteId); // Badge already updated by the observer.
 |         AddonMessages.instance.getContactRequestsCount(this.siteId); // Badge already updated by the observer.
 | ||||||
|  | 
 | ||||||
|  |         if (this.selected == 'confirmed') { | ||||||
|  |             try { | ||||||
|  | 
 | ||||||
|  |                 await this.confirmedFetchData(); | ||||||
|  |                 if (this.confirmedContacts.length && CoreScreen.instance.isTablet) { | ||||||
|  |                     this.selectUser(this.confirmedContacts[0].id, true); | ||||||
|  |                 } | ||||||
|  |             } finally { | ||||||
|  |                 this.confirmedLoaded = true; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             try { | ||||||
|  |                 await this.requestsFetchData(); | ||||||
|  |                 if (this.requests.length && CoreScreen.instance.isTablet) { | ||||||
|  |                     this.selectUser(this.requests[0].id, true); | ||||||
|  |                 } | ||||||
|  |             } finally { | ||||||
|  |                 this.requestsLoaded = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetch contacts. | ||||||
|  |      * | ||||||
|  |      * @param refresh True if we are refreshing contacts, false if we are loading more. | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     async confirmedFetchData(refresh: boolean = false): Promise<void> { | ||||||
|  |         this.confirmedLoadMoreError = false; | ||||||
|  | 
 | ||||||
|  |         const limitFrom = refresh ? 0 : this.confirmedContacts.length; | ||||||
|  | 
 | ||||||
|  |         if (limitFrom === 0) { | ||||||
|  |             // Always try to get latest data from server.
 | ||||||
|  |             await AddonMessages.instance.invalidateUserContacts(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             const result = await AddonMessages.instance.getUserContacts(limitFrom); | ||||||
|  |             this.confirmedContacts = refresh ? result.contacts : this.confirmedContacts.concat(result.contacts); | ||||||
|  |             this.confirmedCanLoadMore = result.canLoadMore; | ||||||
|  |         } catch (error) { | ||||||
|  |             this.confirmedLoadMoreError = true; | ||||||
|  |             CoreDomUtils.instance.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetch contact requests. | ||||||
|  |      * | ||||||
|  |      * @param refresh True if we are refreshing contact requests, false if we are loading more. | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     async requestsFetchData(refresh: boolean = false): Promise<void> { | ||||||
|  |         this.requestsLoadMoreError = false; | ||||||
|  | 
 | ||||||
|  |         const limitFrom = refresh ? 0 : this.requests.length; | ||||||
|  | 
 | ||||||
|  |         if (limitFrom === 0) { | ||||||
|  |             // Always try to get latest data from server.
 | ||||||
|  |             await AddonMessages.instance.invalidateContactRequestsCache(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             const result = await AddonMessages.instance.getContactRequests(limitFrom); | ||||||
|  |             this.requests = refresh ? result.requests : this.requests.concat(result.requests); | ||||||
|  |             this.requestsCanLoadMore = result.canLoadMore; | ||||||
|  |         } catch (error) { | ||||||
|  |             this.requestsLoadMoreError = true; | ||||||
|  |             CoreDomUtils.instance.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh contacts or requests. | ||||||
|  |      * | ||||||
|  |      * @param refresher Refresher. | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> { | ||||||
|  |         try { | ||||||
|  |             if (this.selected == 'confirmed') { | ||||||
|  |                 // No need to invalidate contacts, we always try to get the latest.
 | ||||||
|  |                 await this.confirmedFetchData(true); | ||||||
|  |             } else { | ||||||
|  |                 // Refresh the number of contacts requests to update badges.
 | ||||||
|  |                 AddonMessages.instance.refreshContactRequestsCount(); | ||||||
|  | 
 | ||||||
|  |                 // No need to invalidate contact requests, we always try to get the latest.
 | ||||||
|  |                 await this.requestsFetchData(true); | ||||||
|  |             } | ||||||
|  |         } finally { | ||||||
|  |             refresher?.detail.complete(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load more contacts or requests. | ||||||
|  |      * | ||||||
|  |      * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. | ||||||
|  |      * @return Resolved when done. | ||||||
|  |      */ | ||||||
|  |     async loadMore(infiniteComplete?: () => void): Promise<void> { | ||||||
|  |         try { | ||||||
|  |             if (this.selected == 'confirmed') { | ||||||
|  |                 // No need to invalidate contacts, we always try to get the latest.
 | ||||||
|  |                 await this.confirmedFetchData(); | ||||||
|  |             } else { | ||||||
|  |                 await this.requestsFetchData(); | ||||||
|  |             } | ||||||
|  |         } finally { | ||||||
|  |             infiniteComplete && infiniteComplete(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -99,6 +231,14 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy { | |||||||
|         CoreNavigator.instance.navigateToSitePath('search'); |         CoreNavigator.instance.navigateToSitePath('search'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     selectTab(selected: string): void { | ||||||
|  |         this.selected = selected; | ||||||
|  | 
 | ||||||
|  |         if ((this.selected == 'confirmed' && !this.confirmedLoaded) || (this.selected != 'confirmed' && !this.requestsLoaded)) { | ||||||
|  |             this.ngOnInit(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Set the selected user and open the conversation in the split view if needed. |      * Set the selected user and open the conversation in the split view if needed. | ||||||
|      * |      * | ||||||
| @ -118,14 +258,8 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         this.selectedUserId = userId; |         this.selectedUserId = userId; | ||||||
| 
 | 
 | ||||||
|         // @todo it does not seem to work load anything.
 |         const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/contacts/discussion'); | ||||||
|         let path = 'discussion'; |         const path = (splitViewLoaded ? '../' : '') + 'discussion'; | ||||||
|         if (CoreScreen.instance.isMobile) { |  | ||||||
|             path = '../../' + path; |  | ||||||
|         } else { |  | ||||||
|             const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/contacts/**/discussion'); |  | ||||||
|             path = (splitViewLoaded ? '../' : '') + path; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         CoreNavigator.instance.navigate(path, { params : { userId } }); |         CoreNavigator.instance.navigate(path, { params : { userId } }); | ||||||
|     } |     } | ||||||
| @ -135,7 +269,6 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy { | |||||||
|      */ |      */ | ||||||
|     ngOnDestroy(): void { |     ngOnDestroy(): void { | ||||||
|         this.contactRequestsCountObserver?.off(); |         this.contactRequestsCountObserver?.off(); | ||||||
|         this.splitViewObserver?.off(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,33 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { InjectionToken, ModuleWithProviders, NgModule } from '@angular/core'; |  | ||||||
| 
 |  | ||||||
| import { ModuleRoutesConfig } from '@/app/app-routing.module'; |  | ||||||
| 
 |  | ||||||
| export const ADDON_MESSAGES_CONTACTS_ROUTES = new InjectionToken('ADDON_MESSAGES_CONTACTS_ROUTES'); |  | ||||||
| 
 |  | ||||||
| @NgModule() |  | ||||||
| export class AddonMessagesContactsRoutingModule { |  | ||||||
| 
 |  | ||||||
|     static forChild(routes: ModuleRoutesConfig): ModuleWithProviders<AddonMessagesContactsRoutingModule> { |  | ||||||
|         return { |  | ||||||
|             ngModule: AddonMessagesContactsRoutingModule, |  | ||||||
|             providers: [ |  | ||||||
|                 { provide: ADDON_MESSAGES_CONTACTS_ROUTES, multi: true, useValue: routes }, |  | ||||||
|             ], |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
							
								
								
									
										48
									
								
								src/addons/messages/pages/contacts/tabs.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/addons/messages/pages/contacts/tabs.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | :host { | ||||||
|  |     ion-tab-bar.core-tabs-bar { | ||||||
|  |         position: relative; | ||||||
|  |         width: 100%; | ||||||
|  |         background: var(--core-tabs-background); | ||||||
|  |         color: var(--core-tab-color); | ||||||
|  |         -webkit-filter: drop-shadow(0px 3px 3px rgba(var(--drop-shadow))); | ||||||
|  |         filter: drop-shadow(0px 3px 3px rgba(var(--drop-shadow))); | ||||||
|  |         border: 0; | ||||||
|  | 
 | ||||||
|  |         ion-row { | ||||||
|  |             width: 100%; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .tab-slide { | ||||||
|  |             border-bottom: 2px solid transparent; | ||||||
|  |             min-width: 100px; | ||||||
|  |             min-height: 56px; | ||||||
|  |             cursor: pointer; | ||||||
|  |             overflow: hidden; | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             justify-content: center; | ||||||
|  |             padding: 0; | ||||||
|  |             margin-bottom: 1px; | ||||||
|  | 
 | ||||||
|  |             ion-label { | ||||||
|  |                 font-size: 16px; | ||||||
|  |                 font-weight: 400; | ||||||
|  |                 text-overflow: ellipsis; | ||||||
|  |                 white-space: nowrap; | ||||||
|  |                 overflow: hidden; | ||||||
|  |                 word-wrap: break-word; | ||||||
|  |                 max-width: 100%; | ||||||
|  |                 line-height: 1.2em; | ||||||
|  |                 margin: 16px auto; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             &[aria-selected=true] { | ||||||
|  |                 color: var(--core-tab-border-color-active); | ||||||
|  |                 border-bottom-color: var(--core-tab-color-active); | ||||||
|  |                 ion-tab-button { | ||||||
|  |                     color: var(--core-tab-border-color-active); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,4 +1,17 @@ | |||||||
|  | <ion-header> | ||||||
|  |     <ion-toolbar> | ||||||
|  |         <ion-buttons slot="start"> | ||||||
|  |             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||||
|  |         </ion-buttons> | ||||||
|  |         <ion-title>{{ 'addon.messages.messages' | translate }}</ion-title> | ||||||
|  |         <ion-buttons slot="end"> | ||||||
|  |             <!-- Add an empty context menu so discussion page can add items in split view, otherwise the menu disappears in some cases. --> | ||||||
|  |             <core-context-menu></core-context-menu> | ||||||
|  |         </ion-buttons> | ||||||
|  |     </ion-toolbar> | ||||||
|  | </ion-header> | ||||||
| <ion-content> | <ion-content> | ||||||
|  |     <core-split-view> | ||||||
|         <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)"> |         <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)"> | ||||||
|             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|         </ion-refresher> |         </ion-refresher> | ||||||
| @ -9,7 +22,15 @@ | |||||||
| 
 | 
 | ||||||
|         <core-loading [hideUntil]="loaded" [message]="loadingMessage"> |         <core-loading [hideUntil]="loaded" [message]="loadingMessage"> | ||||||
| 
 | 
 | ||||||
|         <ion-list *ngIf="search.showResults"  class="ion-no-margin"> |             <ion-list   class="ion-no-margin"> | ||||||
|  | 
 | ||||||
|  |                 <ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts($event)" | ||||||
|  |                     [attr.aria-label]="'addon.messages.contacts' | translate" detail> | ||||||
|  |                     <ion-icon name="fas-address-book" slot="start"></ion-icon> | ||||||
|  |                     <ion-label><h2>{{ 'addon.messages.contacts' | translate }}</h2></ion-label> | ||||||
|  |                 </ion-item> | ||||||
|  | 
 | ||||||
|  |                 <ng-container *ngIf="search.showResults"> | ||||||
|                     <ion-item-divider> |                     <ion-item-divider> | ||||||
|                         <ion-label> |                         <ion-label> | ||||||
|                             <h2>{{ 'core.searchresults' | translate }}</h2> |                             <h2>{{ 'core.searchresults' | translate }}</h2> | ||||||
| @ -26,9 +47,8 @@ | |||||||
|                                 [contextInstanceId]="0"></core-format-text></p> |                                 [contextInstanceId]="0"></core-format-text></p> | ||||||
|                         </ion-label> |                         </ion-label> | ||||||
|                     </ion-item> |                     </ion-item> | ||||||
|         </ion-list> |                 </ng-container> | ||||||
| 
 |                 <ng-container *ngIf="!search.showResults"> | ||||||
|         <ion-list *ngIf="!search.showResults"  class="ion-no-margin"> |  | ||||||
|                     <ion-item class="ion-text-wrap" *ngFor="let discussion of discussions" [title]="discussion.fullname" |                     <ion-item class="ion-text-wrap" *ngFor="let discussion of discussions" [title]="discussion.fullname" | ||||||
|                     (click)="gotoDiscussion(discussion.message!.user)" |                     (click)="gotoDiscussion(discussion.message!.user)" | ||||||
|                     [class.core-selected-item]="discussion.message!.user == discussionUserId" class="addon-message-discussion"> |                     [class.core-selected-item]="discussion.message!.user == discussionUserId" class="addon-message-discussion"> | ||||||
| @ -45,6 +65,7 @@ | |||||||
|                             </p> |                             </p> | ||||||
|                         </ion-label> |                         </ion-label> | ||||||
|                     </ion-item> |                     </ion-item> | ||||||
|  |                 </ng-container> | ||||||
|             </ion-list> |             </ion-list> | ||||||
| 
 | 
 | ||||||
|             <core-empty-box *ngIf="(!discussions || discussions.length <= 0) && !search.showResults" icon="far-comments" |             <core-empty-box *ngIf="(!discussions || discussions.length <= 0) && !search.showResults" icon="far-comments" | ||||||
| @ -53,4 +74,5 @@ | |||||||
|             <core-empty-box *ngIf="(!search.results || search.results.length <= 0) && search.showResults" icon="search" |             <core-empty-box *ngIf="(!search.results || search.results.length <= 0) && search.showResults" icon="search" | ||||||
|             [message]="'core.noresults' | translate"></core-empty-box> |             [message]="'core.noresults' | translate"></core-empty-box> | ||||||
|         </core-loading> |         </core-loading> | ||||||
|  |     </core-split-view> | ||||||
| </ion-content> | </ion-content> | ||||||
|  | |||||||
| @ -17,6 +17,9 @@ import { IonicModule } from '@ionic/angular'; | |||||||
| import { TranslateModule } from '@ngx-translate/core'; | import { TranslateModule } from '@ngx-translate/core'; | ||||||
| import { RouterModule, Routes } from '@angular/router'; | import { RouterModule, Routes } from '@angular/router'; | ||||||
| import { CommonModule } from '@angular/common'; | import { CommonModule } from '@angular/common'; | ||||||
|  | import { CoreScreen } from '@services/screen'; | ||||||
|  | import { conditionalRoutes } from '@/app/app-routing.module'; | ||||||
|  | import { discussionRoute } from '@addons/messages/messages-lazy.module'; | ||||||
| 
 | 
 | ||||||
| import { CoreSharedModule } from '@/core/shared.module'; | import { CoreSharedModule } from '@/core/shared.module'; | ||||||
| import { CoreSearchComponentsModule } from '@features/search/components/components.module'; | import { CoreSearchComponentsModule } from '@features/search/components/components.module'; | ||||||
| @ -25,9 +28,21 @@ import { AddonMessagesDiscussions35Page } from './discussions.page'; | |||||||
| 
 | 
 | ||||||
| const routes: Routes = [ | const routes: Routes = [ | ||||||
|     { |     { | ||||||
|         path: '', |         matcher: segments => { | ||||||
|         component: AddonMessagesDiscussions35Page, |             const matches = CoreScreen.instance.isMobile ? segments.length === 0 : true; | ||||||
|  | 
 | ||||||
|  |             return matches ? { consumed: [] } : null; | ||||||
|         }, |         }, | ||||||
|  |         component: AddonMessagesDiscussions35Page, | ||||||
|  |         children: conditionalRoutes([ | ||||||
|  |             { | ||||||
|  |                 path: '', | ||||||
|  |                 pathMatch: 'full', | ||||||
|  |             }, | ||||||
|  |             discussionRoute, | ||||||
|  |         ], () => CoreScreen.instance.isTablet), | ||||||
|  |     }, | ||||||
|  |     ...conditionalRoutes([discussionRoute], () => CoreScreen.instance.isMobile), | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|  | |||||||
| @ -22,17 +22,18 @@ import { | |||||||
|     AddonMessagesNewMessagedEventData, |     AddonMessagesNewMessagedEventData, | ||||||
|     AddonMessagesProvider, |     AddonMessagesProvider, | ||||||
|     AddonMessagesReadChangedEventData, |     AddonMessagesReadChangedEventData, | ||||||
|     AddonMessagesSplitViewLoadIndexEventData, |  | ||||||
| } from '../../services/messages'; | } from '../../services/messages'; | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils } from '@services/utils/utils'; | ||||||
| import { CoreApp } from '@services/app'; | import { CoreApp } from '@services/app'; | ||||||
| import { ActivatedRoute } from '@angular/router'; | import { ActivatedRoute, Params } from '@angular/router'; | ||||||
| import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; | import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; | ||||||
| import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; | import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; | ||||||
| import { Subscription } from 'rxjs'; | import { Subscription } from 'rxjs'; | ||||||
| import { Translate, Platform } from '@singletons'; | import { Translate, Platform } from '@singletons'; | ||||||
| import { IonRefresher } from '@ionic/angular'; | import { IonRefresher } from '@ionic/angular'; | ||||||
|  | import { CoreNavigator } from '@services/navigator'; | ||||||
|  | import { CoreScreen } from '@services/screen'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that displays the list of discussions. |  * Page that displays the list of discussions. | ||||||
| @ -46,7 +47,6 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     protected newMessagesObserver: CoreEventObserver; |     protected newMessagesObserver: CoreEventObserver; | ||||||
|     protected readChangedObserver: CoreEventObserver; |     protected readChangedObserver: CoreEventObserver; | ||||||
|     protected cronObserver: CoreEventObserver; |  | ||||||
|     protected appResumeSubscription: Subscription; |     protected appResumeSubscription: Subscription; | ||||||
|     protected pushObserver: Subscription; |     protected pushObserver: Subscription; | ||||||
|     protected loadingMessages: string; |     protected loadingMessages: string; | ||||||
| @ -121,11 +121,6 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { | |||||||
|             this.siteId, |             this.siteId, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         // Update unread conversation counts.
 |  | ||||||
|         this.cronObserver = CoreEvents.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, () => { |  | ||||||
|             AddonMessages.instance.refreshUnreadConversationCounts(this.siteId); |  | ||||||
|         }, this.siteId); |  | ||||||
| 
 |  | ||||||
|         // Refresh the view when the app is resumed.
 |         // Refresh the view when the app is resumed.
 | ||||||
|         this.appResumeSubscription = Platform.instance.resume.subscribe(() => { |         this.appResumeSubscription = Platform.instance.resume.subscribe(() => { | ||||||
|             if (!this.loaded) { |             if (!this.loaded) { | ||||||
| @ -152,7 +147,15 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { | |||||||
|      */ |      */ | ||||||
|     ngOnInit(): void { |     ngOnInit(): void { | ||||||
|         this.route.queryParams.subscribe(async params => { |         this.route.queryParams.subscribe(async params => { | ||||||
|             this.discussionUserId = params['discussionUserId'] || undefined; |             const discussionUserId = params['discussionUserId'] | ||||||
|  |                 ? parseInt(params['discussionUserId'], 10) | ||||||
|  |                 : (params['userId'] ? parseInt(params['userId'], 10) : undefined); | ||||||
|  | 
 | ||||||
|  |             if (this.loaded && this.discussionUserId == discussionUserId) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.discussionUserId = discussionUserId; | ||||||
| 
 | 
 | ||||||
|             if (this.discussionUserId) { |             if (this.discussionUserId) { | ||||||
|                 // There is a discussion to load, open the discussion in a new state.
 |                 // There is a discussion to load, open the discussion in a new state.
 | ||||||
| @ -161,9 +164,9 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|             await this.fetchData(); |             await this.fetchData(); | ||||||
| 
 | 
 | ||||||
|             if (!this.discussionUserId && this.discussions.length > 0) { |             if (!this.discussionUserId && this.discussions.length > 0 && CoreScreen.instance.isTablet) { | ||||||
|                 // Take first and load it.
 |                 // Take first and load it.
 | ||||||
|                 this.gotoDiscussion(this.discussions[0].message!.user, undefined, true); |                 this.gotoDiscussion(this.discussions[0].message!.user); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -267,17 +270,34 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { | |||||||
|      * @param messageId Message to scroll after loading the discussion. Used when searching. |      * @param messageId Message to scroll after loading the discussion. Used when searching. | ||||||
|      * @param onlyWithSplitView Only go to Discussion if split view is on. |      * @param onlyWithSplitView Only go to Discussion if split view is on. | ||||||
|      */ |      */ | ||||||
|     gotoDiscussion(discussionUserId: number, messageId?: number, onlyWithSplitView: boolean = false): void { |     gotoDiscussion(discussionUserId: number, messageId?: number): void { | ||||||
|         this.discussionUserId = discussionUserId; |         this.discussionUserId = discussionUserId; | ||||||
| 
 | 
 | ||||||
|         const params: AddonMessagesSplitViewLoadIndexEventData = { |         const params: Params = { | ||||||
|             discussion: discussionUserId, |             userId: discussionUserId, | ||||||
|             onlyWithSplitView: onlyWithSplitView, |  | ||||||
|         }; |         }; | ||||||
|  | 
 | ||||||
|         if (messageId) { |         if (messageId) { | ||||||
|             params.message = messageId; |             params.message = messageId; | ||||||
|         } |         } | ||||||
|         CoreEvents.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_INDEX_EVENT, params, this.siteId); | 
 | ||||||
|  |         const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/index/discussion'); | ||||||
|  |         const path = (splitViewLoaded ? '../' : '') + 'discussion'; | ||||||
|  | 
 | ||||||
|  |         CoreNavigator.instance.navigate(path, { params }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Navigate to contacts view. | ||||||
|  |      */ | ||||||
|  |     gotoContacts(): void { | ||||||
|  |         const params: Params = {}; | ||||||
|  | 
 | ||||||
|  |         if (CoreScreen.instance.isTablet && this.discussionUserId) { | ||||||
|  |             params.discussionUserId = this.discussionUserId; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         CoreNavigator.instance.navigateToSitePath('contacts-35', { params }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -286,7 +306,6 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { | |||||||
|     ngOnDestroy(): void { |     ngOnDestroy(): void { | ||||||
|         this.newMessagesObserver?.off(); |         this.newMessagesObserver?.off(); | ||||||
|         this.readChangedObserver?.off(); |         this.readChangedObserver?.off(); | ||||||
|         this.cronObserver?.off(); |  | ||||||
|         this.appResumeSubscription?.unsubscribe(); |         this.appResumeSubscription?.unsubscribe(); | ||||||
|         this.pushObserver?.unsubscribe(); |         this.pushObserver?.unsubscribe(); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -25,8 +25,8 @@ | |||||||
| 
 | 
 | ||||||
|         <core-loading [hideUntil]="loaded" [message]="loadingMessage"> |         <core-loading [hideUntil]="loaded" [message]="loadingMessage"> | ||||||
|             <ion-list> |             <ion-list> | ||||||
|                 <ion-item class="ion-text-wrap" (click)="gotoContacts($event)" [attr.aria-label]="'addon.messages.contacts' | translate" |                 <ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts($event)" | ||||||
|                     class="addon-message-discussion"> |                     [attr.aria-label]="'addon.messages.contacts' | translate" detail> | ||||||
|                     <ion-icon name="fas-address-book" slot="start"></ion-icon> |                     <ion-icon name="fas-address-book" slot="start"></ion-icon> | ||||||
|                     <ion-label><h2>{{ 'addon.messages.contacts' | translate }}</h2></ion-label> |                     <ion-label><h2>{{ 'addon.messages.contacts' | translate }}</h2></ion-label> | ||||||
|                     <ion-badge *ngIf="contactRequestsCount > 0" slot="end">{{contactRequestsCount}}</ion-badge> |                     <ion-badge *ngIf="contactRequestsCount > 0" slot="end">{{contactRequestsCount}}</ion-badge> | ||||||
|  | |||||||
| @ -511,7 +511,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { | |||||||
|      */ |      */ | ||||||
|     gotoContacts(): void { |     gotoContacts(): void { | ||||||
|         CoreNavigator.instance.navigateToSitePath('contacts'); |         CoreNavigator.instance.navigateToSitePath('contacts'); | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -1,17 +0,0 @@ | |||||||
| <ion-header> |  | ||||||
|     <ion-toolbar> |  | ||||||
|         <ion-buttons slot="start"> |  | ||||||
|             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> |  | ||||||
|         </ion-buttons> |  | ||||||
|         <ion-title>{{ 'addon.messages.messages' | translate }}</ion-title> |  | ||||||
|         <ion-buttons slot="end"> |  | ||||||
|             <!-- Add an empty context menu so discussion page can add items in split view, otherwise the menu disappears in some cases. --> |  | ||||||
|             <core-context-menu></core-context-menu> |  | ||||||
|         </ion-buttons> |  | ||||||
|     </ion-toolbar> |  | ||||||
| </ion-header> |  | ||||||
| <ion-content> |  | ||||||
|     <core-split-view> |  | ||||||
|         <core-tabs [tabs]="tabs" hideUntil="true"></core-tabs> |  | ||||||
|     </core-split-view> |  | ||||||
| </ion-content> |  | ||||||
| @ -1,79 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { Injector, NgModule } from '@angular/core'; |  | ||||||
| import { IonicModule } from '@ionic/angular'; |  | ||||||
| import { TranslateModule } from '@ngx-translate/core'; |  | ||||||
| import { RouterModule, ROUTES, Routes } from '@angular/router'; |  | ||||||
| import { CommonModule } from '@angular/common'; |  | ||||||
| import { conditionalRoutes, resolveModuleRoutes } from '@/app/app-routing.module'; |  | ||||||
| import { discussionRoute } from '@addons/messages/messages-lazy.module'; |  | ||||||
| import { CoreScreen } from '@services/screen'; |  | ||||||
| 
 |  | ||||||
| import { CoreSharedModule } from '@/core/shared.module'; |  | ||||||
| 
 |  | ||||||
| import { AddonMessagesIndex35Page } from './index.page'; |  | ||||||
| import { ADDON_MESSAGES_INDEX_ROUTES } from './messages-index-routing.module'; |  | ||||||
| import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; |  | ||||||
| 
 |  | ||||||
| // @todo mix both routes to get messages/index/discussions/discussion and messages/index/contacts/discussion working
 |  | ||||||
| const routes: Routes = [ |  | ||||||
|     { |  | ||||||
|         matcher: segments => { |  | ||||||
|             const matches = CoreScreen.instance.isMobile ? segments.length === 0 : true; |  | ||||||
| 
 |  | ||||||
|             return matches ? { consumed: [] } : null; |  | ||||||
|         }, |  | ||||||
|         component: AddonMessagesIndex35Page, |  | ||||||
|         children: conditionalRoutes([ |  | ||||||
|             { |  | ||||||
|                 path: '', |  | ||||||
|                 pathMatch: 'full', |  | ||||||
|             }, |  | ||||||
|             discussionRoute, |  | ||||||
|         ], () => CoreScreen.instance.isTablet), |  | ||||||
|     }, |  | ||||||
|     ...conditionalRoutes([discussionRoute], () => CoreScreen.instance.isMobile), |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| function buildRoutes(injector: Injector): Routes { |  | ||||||
|     const routes = resolveModuleRoutes(injector, ADDON_MESSAGES_INDEX_ROUTES); |  | ||||||
| 
 |  | ||||||
|     return [ |  | ||||||
|         ...buildTabMainRoutes(injector, { |  | ||||||
|             path: '', |  | ||||||
|             component: AddonMessagesIndex35Page, |  | ||||||
|             children: routes.children, |  | ||||||
|         }), |  | ||||||
|         ...routes.siblings, |  | ||||||
|     ]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @NgModule({ |  | ||||||
|     imports: [ |  | ||||||
|         RouterModule.forChild(routes), |  | ||||||
|         CommonModule, |  | ||||||
|         IonicModule, |  | ||||||
|         TranslateModule.forChild(), |  | ||||||
|         CoreSharedModule, |  | ||||||
|     ], |  | ||||||
|     providers: [ |  | ||||||
|         { provide: ROUTES, multi: true, useFactory: buildRoutes, deps: [Injector] }, |  | ||||||
|     ], |  | ||||||
|     declarations: [ |  | ||||||
|         AddonMessagesIndex35Page, |  | ||||||
|     ], |  | ||||||
|     exports: [RouterModule], |  | ||||||
| }) |  | ||||||
| export class AddonMessagesIndex35PageModule {} |  | ||||||
| @ -1,104 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { Component, OnDestroy } from '@angular/core'; |  | ||||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; |  | ||||||
| import { CoreSites } from '@services/sites'; |  | ||||||
| import { CoreTab } from '@components/tabs/tabs'; |  | ||||||
| import { Params } from '@angular/router'; |  | ||||||
| import { AddonMessagesProvider, AddonMessagesSplitViewLoadIndexEventData } from '../../services/messages'; |  | ||||||
| import { CoreNavigator } from '@services/navigator'; |  | ||||||
| import { CoreScreen } from '@services/screen'; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Page that displays the messages index page. |  | ||||||
|  */ |  | ||||||
| @Component({ |  | ||||||
|     selector: 'page-addon-messages-index', |  | ||||||
|     templateUrl: 'index.html', |  | ||||||
| }) |  | ||||||
| export class AddonMessagesIndex35Page implements OnDestroy { |  | ||||||
| 
 |  | ||||||
|     tabs: CoreTab[] = [ |  | ||||||
|         { |  | ||||||
|             id: 'discussions-35', |  | ||||||
|             class: '', |  | ||||||
|             title: 'addon.messages.messages', |  | ||||||
|             icon: 'fas-comments', |  | ||||||
|             enabled: true, |  | ||||||
|             page: 'main/messages/index/discussions', |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             id: 'contacts-35', |  | ||||||
|             class: '', |  | ||||||
|             title: 'addon.messages.contacts', |  | ||||||
|             icon: 'fas-address-book', |  | ||||||
|             enabled: true, |  | ||||||
|             page: 'main/messages/index/contacts', |  | ||||||
|         }, |  | ||||||
|     ]; |  | ||||||
| 
 |  | ||||||
|     protected loadSplitViewObserver?: CoreEventObserver; |  | ||||||
|     protected siteId: string; |  | ||||||
| 
 |  | ||||||
|     constructor() { |  | ||||||
|         this.siteId = CoreSites.instance.getCurrentSiteId(); |  | ||||||
| 
 |  | ||||||
|         // Update split view or navigate.
 |  | ||||||
|         this.loadSplitViewObserver = CoreEvents.on<AddonMessagesSplitViewLoadIndexEventData>( |  | ||||||
|             AddonMessagesProvider.SPLIT_VIEW_LOAD_INDEX_EVENT, |  | ||||||
|             (data) => { |  | ||||||
|                 if (data.discussion && (CoreScreen.instance.isTablet || !data.onlyWithSplitView)) { |  | ||||||
|                     this.gotoDiscussion(data.discussion, data.message); |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Navigate to a particular discussion. |  | ||||||
|      * |  | ||||||
|      * @param discussionUserId Discussion Id to load. |  | ||||||
|      * @param messageId Message to scroll after loading the discussion. Used when searching. |  | ||||||
|      */ |  | ||||||
|     gotoDiscussion(discussionUserId: number, messageId?: number): void { |  | ||||||
|         const params: Params = { |  | ||||||
|             userId: discussionUserId, |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         if (messageId) { |  | ||||||
|             params.message = messageId; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let path = 'discussion'; |  | ||||||
|         if (CoreScreen.instance.isMobile) { |  | ||||||
|             path = '../../' + path; |  | ||||||
|         } else { |  | ||||||
|             const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/index/**/discussion'); |  | ||||||
|             path = (splitViewLoaded ? '../' : '') + path; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         CoreNavigator.instance.navigate(path, { params }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Page destroyed. |  | ||||||
|      */ |  | ||||||
|     ngOnDestroy(): void { |  | ||||||
|         this.loadSplitViewObserver?.off(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 |  | ||||||
| // you may not use this file except in compliance with the License.
 |  | ||||||
| // You may obtain a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  | ||||||
| // See the License for the specific language governing permissions and
 |  | ||||||
| // limitations under the License.
 |  | ||||||
| 
 |  | ||||||
| import { InjectionToken, ModuleWithProviders, NgModule } from '@angular/core'; |  | ||||||
| 
 |  | ||||||
| import { ModuleRoutesConfig } from '@/app/app-routing.module'; |  | ||||||
| 
 |  | ||||||
| export const ADDON_MESSAGES_INDEX_ROUTES = new InjectionToken('ADDON_MESSAGES_INDEX_ROUTES'); |  | ||||||
| 
 |  | ||||||
| @NgModule() |  | ||||||
| export class AddonMessagesIndexRoutingModule { |  | ||||||
| 
 |  | ||||||
|     static forChild(routes: ModuleRoutesConfig): ModuleWithProviders<AddonMessagesIndexRoutingModule> { |  | ||||||
|         return { |  | ||||||
|             ngModule: AddonMessagesIndexRoutingModule, |  | ||||||
|             providers: [ |  | ||||||
|                 { provide: ADDON_MESSAGES_INDEX_ROUTES, multi: true, useValue: routes }, |  | ||||||
|             ], |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -42,8 +42,6 @@ export class AddonMessagesProvider { | |||||||
|     static readonly NEW_MESSAGE_EVENT = 'addon_messages_new_message_event'; |     static readonly NEW_MESSAGE_EVENT = 'addon_messages_new_message_event'; | ||||||
|     static readonly READ_CHANGED_EVENT = 'addon_messages_read_changed_event'; |     static readonly READ_CHANGED_EVENT = 'addon_messages_read_changed_event'; | ||||||
|     static readonly OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify a conversation should be opened.
 |     static readonly OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify a conversation should be opened.
 | ||||||
|     static readonly SPLIT_VIEW_LOAD_INDEX_EVENT = 'addon_messages_split_view_load_index_event'; // Used on 3.5 or lower.
 |  | ||||||
|     static readonly SPLIT_VIEW_LOAD_CONTACTS_EVENT = 'addon_messages_split_view_load_contacts_event'; // Used on 3.6 or greater.
 |  | ||||||
|     static readonly UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event'; |     static readonly UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event'; | ||||||
|     static readonly MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event'; |     static readonly MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event'; | ||||||
|     static readonly UNREAD_CONVERSATION_COUNTS_EVENT = 'addon_messages_unread_conversation_counts_event'; |     static readonly UNREAD_CONVERSATION_COUNTS_EVENT = 'addon_messages_unread_conversation_counts_event'; | ||||||
| @ -3696,23 +3694,6 @@ export type AddonMessagesMemberInfoChangedEventData = { | |||||||
|     contactRemoved?: boolean; |     contactRemoved?: boolean; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Data sent by SPLIT_VIEW_LOAD_INDEX_EVENT event. Used on 3.5 or lower. |  | ||||||
|  */ |  | ||||||
| export type AddonMessagesSplitViewLoadIndexEventData = { |  | ||||||
|     discussion: number; |  | ||||||
|     onlyWithSplitView?: boolean; |  | ||||||
|     message?: number; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Data sent by SPLIT_VIEW_LOAD_CONTACTS_EVENT event. Used on 3.6 or greater. |  | ||||||
|  */ |  | ||||||
| export type AddonMessagesSplitViewLoadContactsEventData = { |  | ||||||
|     userId: number; |  | ||||||
|     onInit: boolean; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * Data sent by READ_CHANGED_EVENT event. |  * Data sent by READ_CHANGED_EVENT event. | ||||||
|  */ |  */ | ||||||
|  | |||||||
| @ -135,14 +135,21 @@ | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     --core-tabs-background: var(--custom-tabs-background, var(--white)); | ||||||
|  |     --core-tab-background: var(--custom-tab-background, var(--core-tabs-background)); | ||||||
|  |     --core-tab-color: var(--custom-tab-color, var(--gray-dark)); | ||||||
|  |     --core-tab-border-color: var(--custom-tab-border-color, var(--gray)); | ||||||
|  |     --core-tab-color-active: var(--custom-tab-color-active, var(--core-color)); | ||||||
|  |     --core-tab-border-color-active: var(--custom-tab-border-color-active, var(--core-color)); | ||||||
|  | 
 | ||||||
|     core-tabs { |     core-tabs { | ||||||
|         --background: var(--custom-tabs-background, var(--white)); |         --background: var(--core-tabs-background); | ||||||
|         ion-slide { |         ion-slide { | ||||||
|             --background: var(--custom-tab-background, var(--white)); |             --background: var(--core-tab-background); | ||||||
|             --color: var(--custom-tab-background, var(--gray-dark)); |             --color: var(--core-tab-color); | ||||||
|             --border-color: var(--custom-tab-border-color, var(--gray)); |             --border-color: var(--core-tab-border-colo); | ||||||
|             --color-active: var(--custom-tab-color-active, var(--color)); |             --color-active: var(--core-tab-color-active); | ||||||
|             --border-color-active: var(--custom-tab-border-color-active, var(--core-color)); |             --border-color-active: var(--core-tab-border-color-active); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -276,14 +283,10 @@ | |||||||
|         --contrast-background: var(--ion-background-color); |         --contrast-background: var(--ion-background-color); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     core-tabs { |     --core-tabs-background: var(--custom-tabs-background, #3a3a3a); | ||||||
|         --background: var(--custom-tabs-background, #3a3a3a); |     --core-tab-background: var(--custom-tab-background, #3a3a3a); | ||||||
|         ion-slide { |     --core-tab-color: var(--custom-tab-color, var(--white)); | ||||||
|             --background: var(--custom-tab-background, #3a3a3a); |     --core-tab-border-color: var(--custom-tab-border-color, var(--gray-light)); | ||||||
|             --color: var(--custom-tab-background, var(--white)); |  | ||||||
|             --border-color: var(--custom-tab-border-color, var(--gray-light)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     core-progress-bar { |     core-progress-bar { | ||||||
|         --text-color: var(--custom-progress-text-color, var(--gray-lighter)); |         --text-color: var(--custom-progress-text-color, var(--gray-lighter)); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user