forked from CIT/Vmeda.Online
		
	
						commit
						4b45af2e0c
					
				@ -14,63 +14,25 @@
 | 
			
		||||
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { AddonBlockActivityResultsModule } from './block/activityresults/activityresults.module';
 | 
			
		||||
import { AddonBlockBadgesModule } from './block/badges/badges.module';
 | 
			
		||||
import { AddonBlockBlogMenuModule } from './block/blogmenu/blogmenu.module';
 | 
			
		||||
import { AddonBlockBlogRecentModule } from './block/blogrecent/blogrecent.module';
 | 
			
		||||
import { AddonBlockBlogTagsModule } from './block/blogtags/blogtags.module';
 | 
			
		||||
import { AddonBlockCalendarMonthModule } from './block/calendarmonth/calendarmonth.module';
 | 
			
		||||
import { AddonBlockCalendarUpcomingModule } from './block/calendarupcoming/calendarupcoming.module';
 | 
			
		||||
import { AddonBlockCommentsModule } from './block/comments/comments.module';
 | 
			
		||||
import { AddonBlockCompletionStatusModule } from './block/completionstatus/completionstatus.module';
 | 
			
		||||
import { AddonBlockGlossaryRandomModule } from './block/glossaryrandom/glossaryrandom.module';
 | 
			
		||||
import { AddonBlockHtmlModule } from './block/html/html.module';
 | 
			
		||||
import { AddonBlockLearningPlansModule } from './block/learningplans/learningplans.module';
 | 
			
		||||
import { AddonBlockMyOverviewModule } from './block/myoverview/myoverview.module';
 | 
			
		||||
import { AddonBlockNewsItemsModule } from './block/newsitems/newsitems.module';
 | 
			
		||||
import { AddonBlockOnlineUsersModule } from './block/onlineusers/onlineusers.module';
 | 
			
		||||
import { AddonBlockPrivateFilesModule } from './block/privatefiles/privatefiles.module';
 | 
			
		||||
import { AddonBlockRecentlyAccessedCoursesModule } from './block/recentlyaccessedcourses/recentlyaccessedcourses.module';
 | 
			
		||||
import { AddonBlockRssClientModule } from './block/rssclient/rssclient.module';
 | 
			
		||||
import { AddonBlockSelfCompletionModule } from './block/selfcompletion/selfcompletion.module';
 | 
			
		||||
import { AddonBlockSiteMainMenuModule } from './block/sitemainmenu/sitemainmenu.module';
 | 
			
		||||
import { AddonBlockStarredCoursesModule } from './block/starredcourses/starredcourses.module';
 | 
			
		||||
import { AddonBlockTagsModule } from './block/tags/tags.module';
 | 
			
		||||
import { AddonBlockModule } from './block/block.module';
 | 
			
		||||
import { AddonPrivateFilesModule } from './privatefiles/privatefiles.module';
 | 
			
		||||
import { AddonFilterModule } from './filter/filter.module';
 | 
			
		||||
import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield.module';
 | 
			
		||||
import { AddonBadgesModule } from './badges/badges.module';
 | 
			
		||||
import { AddonCalendarModule } from './calendar/calendar.module';
 | 
			
		||||
import { AddonNotificationsModule } from './notifications/notifications.module';
 | 
			
		||||
import { AddonMessageOutputModule } from './messageoutput/messageoutput.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        AddonBlockModule,
 | 
			
		||||
        AddonBadgesModule,
 | 
			
		||||
        AddonCalendarModule,
 | 
			
		||||
        AddonPrivateFilesModule,
 | 
			
		||||
        AddonFilterModule,
 | 
			
		||||
        AddonBlockActivityResultsModule,
 | 
			
		||||
        AddonBlockBadgesModule,
 | 
			
		||||
        AddonBlockBlogMenuModule,
 | 
			
		||||
        AddonBlockBlogRecentModule,
 | 
			
		||||
        AddonBlockBlogTagsModule,
 | 
			
		||||
        AddonBlockCalendarMonthModule,
 | 
			
		||||
        AddonBlockCalendarUpcomingModule,
 | 
			
		||||
        AddonBlockCommentsModule,
 | 
			
		||||
        AddonBlockCompletionStatusModule,
 | 
			
		||||
        AddonBlockGlossaryRandomModule,
 | 
			
		||||
        AddonBlockHtmlModule,
 | 
			
		||||
        AddonBlockMyOverviewModule,
 | 
			
		||||
        AddonBlockLearningPlansModule,
 | 
			
		||||
        AddonBlockNewsItemsModule,
 | 
			
		||||
        AddonBlockOnlineUsersModule,
 | 
			
		||||
        AddonBlockPrivateFilesModule,
 | 
			
		||||
        AddonBlockRecentlyAccessedCoursesModule,
 | 
			
		||||
        AddonBlockRssClientModule,
 | 
			
		||||
        AddonBlockSelfCompletionModule,
 | 
			
		||||
        AddonBlockSiteMainMenuModule,
 | 
			
		||||
        AddonBlockStarredCoursesModule,
 | 
			
		||||
        AddonBlockTagsModule,
 | 
			
		||||
        AddonUserProfileFieldModule,
 | 
			
		||||
        AddonNotificationsModule,
 | 
			
		||||
        AddonMessageOutputModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonsModule {}
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBadgesIssuedBadgePage } from './issued-badge.page';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -37,9 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonBadgesIssuedBadgePage,
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBadgesUserBadgesPage } from './user-badges.page';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -37,9 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonBadgesUserBadgesPage,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockActivityResultsComponent } from './activityresults/activityresults';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockActivityResultsComponent } from './activityresults/activityre
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockActivityResultsComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockBadgesComponent } from './badges/badges';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockBadgesComponent } from './badges/badges';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockBadgesComponent,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								src/addons/block/block.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/addons/block/block.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
// (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 { AddonBlockActivityResultsModule } from './activityresults/activityresults.module';
 | 
			
		||||
import { AddonBlockBadgesModule } from './badges/badges.module';
 | 
			
		||||
import { AddonBlockBlogMenuModule } from './blogmenu/blogmenu.module';
 | 
			
		||||
import { AddonBlockBlogRecentModule } from './blogrecent/blogrecent.module';
 | 
			
		||||
import { AddonBlockBlogTagsModule } from './blogtags/blogtags.module';
 | 
			
		||||
import { AddonBlockCalendarMonthModule } from './calendarmonth/calendarmonth.module';
 | 
			
		||||
import { AddonBlockCalendarUpcomingModule } from './calendarupcoming/calendarupcoming.module';
 | 
			
		||||
import { AddonBlockCommentsModule } from './comments/comments.module';
 | 
			
		||||
import { AddonBlockCompletionStatusModule } from './completionstatus/completionstatus.module';
 | 
			
		||||
import { AddonBlockGlossaryRandomModule } from './glossaryrandom/glossaryrandom.module';
 | 
			
		||||
import { AddonBlockHtmlModule } from './html/html.module';
 | 
			
		||||
import { AddonBlockLearningPlansModule } from './learningplans/learningplans.module';
 | 
			
		||||
import { AddonBlockMyOverviewModule } from './myoverview/myoverview.module';
 | 
			
		||||
import { AddonBlockNewsItemsModule } from './newsitems/newsitems.module';
 | 
			
		||||
import { AddonBlockOnlineUsersModule } from './onlineusers/onlineusers.module';
 | 
			
		||||
import { AddonBlockPrivateFilesModule } from './privatefiles/privatefiles.module';
 | 
			
		||||
import { AddonBlockRecentlyAccessedCoursesModule } from './recentlyaccessedcourses/recentlyaccessedcourses.module';
 | 
			
		||||
import { AddonBlockRssClientModule } from './rssclient/rssclient.module';
 | 
			
		||||
import { AddonBlockSelfCompletionModule } from './selfcompletion/selfcompletion.module';
 | 
			
		||||
import { AddonBlockSiteMainMenuModule } from './sitemainmenu/sitemainmenu.module';
 | 
			
		||||
import { AddonBlockStarredCoursesModule } from './starredcourses/starredcourses.module';
 | 
			
		||||
import { AddonBlockTagsModule } from './tags/tags.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [],
 | 
			
		||||
    imports: [
 | 
			
		||||
        AddonBlockActivityResultsModule,
 | 
			
		||||
        AddonBlockBadgesModule,
 | 
			
		||||
        AddonBlockBlogMenuModule,
 | 
			
		||||
        AddonBlockBlogRecentModule,
 | 
			
		||||
        AddonBlockBlogTagsModule,
 | 
			
		||||
        AddonBlockCalendarMonthModule,
 | 
			
		||||
        AddonBlockCalendarUpcomingModule,
 | 
			
		||||
        AddonBlockCommentsModule,
 | 
			
		||||
        AddonBlockCompletionStatusModule,
 | 
			
		||||
        AddonBlockGlossaryRandomModule,
 | 
			
		||||
        AddonBlockHtmlModule,
 | 
			
		||||
        AddonBlockMyOverviewModule,
 | 
			
		||||
        AddonBlockLearningPlansModule,
 | 
			
		||||
        AddonBlockNewsItemsModule,
 | 
			
		||||
        AddonBlockOnlineUsersModule,
 | 
			
		||||
        AddonBlockPrivateFilesModule,
 | 
			
		||||
        AddonBlockRecentlyAccessedCoursesModule,
 | 
			
		||||
        AddonBlockRssClientModule,
 | 
			
		||||
        AddonBlockSelfCompletionModule,
 | 
			
		||||
        AddonBlockSiteMainMenuModule,
 | 
			
		||||
        AddonBlockStarredCoursesModule,
 | 
			
		||||
        AddonBlockTagsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [],
 | 
			
		||||
    exports: [],
 | 
			
		||||
})
 | 
			
		||||
export class AddonBlockModule { }
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockBlogMenuComponent } from './blogmenu/blogmenu';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockBlogMenuComponent } from './blogmenu/blogmenu';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockBlogMenuComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockBlogRecentComponent } from './blogrecent/blogrecent';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockBlogRecentComponent } from './blogrecent/blogrecent';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockBlogRecentComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockBlogTagsComponent } from './blogtags/blogtags';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockBlogTagsComponent } from './blogtags/blogtags';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockBlogTagsComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,8 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '@features/courses/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonBlockMyOverviewComponent } from './myoverview/myoverview';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -33,8 +31,7 @@ import { AddonBlockMyOverviewComponent } from './myoverview/myoverview';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockNewsItemsComponent } from './newsitems/newsitems';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockNewsItemsComponent } from './newsitems/newsitems';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockNewsItemsComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockOnlineUsersComponent } from './onlineusers/onlineusers';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockOnlineUsersComponent } from './onlineusers/onlineusers';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockOnlineUsersComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockRecentActivityComponent } from './recentactivity/recentactivity';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockRecentActivityComponent } from './recentactivity/recentactivi
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockRecentActivityComponent,
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,7 @@ import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '@features/courses/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonBlockRecentlyAccessedCoursesComponent } from './recentlyaccessedcourses/recentlyaccessedcourses';
 | 
			
		||||
@ -31,8 +30,7 @@ import { AddonBlockRecentlyAccessedCoursesComponent } from './recentlyaccessedco
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
 | 
			
		||||
import { AddonBlockRecentlyAccessedCoursesComponentsModule } from './components/components.module';
 | 
			
		||||
import { AddonBlockRecentlyAccessedCoursesHandler } from './services/block-handler';
 | 
			
		||||
@ -23,7 +23,7 @@ import { AddonBlockRecentlyAccessedCoursesHandler } from './services/block-handl
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonBlockRecentlyAccessedCoursesComponentsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockRssClientComponent } from './rssclient/rssclient';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockRssClientComponent } from './rssclient/rssclient';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockRssClientComponent,
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,7 @@ import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
// import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonBlockSiteMainMenuComponent } from './sitemainmenu/sitemainmenu';
 | 
			
		||||
@ -32,8 +31,7 @@ import { AddonBlockSiteMainMenuComponent } from './sitemainmenu/sitemainmenu';
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        // CoreCourseComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
 | 
			
		||||
@ -15,8 +15,7 @@
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockSiteMainMenuComponentsModule } from './components/components.module';
 | 
			
		||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
 | 
			
		||||
import { AddonBlockSiteMainMenuHandler } from './services/block-handler';
 | 
			
		||||
@ -24,8 +23,7 @@ import { AddonBlockSiteMainMenuHandler } from './services/block-handler';
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonBlockSiteMainMenuComponentsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,7 @@ import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '@features/courses/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonBlockStarredCoursesComponent } from './starredcourses/starredcourses';
 | 
			
		||||
@ -31,8 +30,7 @@ import { AddonBlockStarredCoursesComponent } from './starredcourses/starredcours
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlockTagsComponent } from './tags/tags';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -32,8 +30,7 @@ import { AddonBlockTagsComponent } from './tags/tags';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonBlockTagsComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarCalendarComponent } from './calendar/calendar';
 | 
			
		||||
import { AddonCalendarUpcomingEventsComponent } from './upcoming-events/upcoming-events';
 | 
			
		||||
@ -37,9 +35,7 @@ import { AddonCalendarFilterPopoverComponent } from './filter/filter';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonCalendarComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarDayPage } from './day.page';
 | 
			
		||||
@ -38,9 +36,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonCalendarComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarEditEventPage } from './edit-event.page';
 | 
			
		||||
@ -40,8 +39,7 @@ const routes: Routes = [
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonCalendarComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarEventPage } from './event.page';
 | 
			
		||||
@ -40,9 +38,7 @@ const routes: Routes = [
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonCalendarComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonCalendarComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarIndexPage } from './index.page';
 | 
			
		||||
@ -38,9 +36,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonCalendarComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarListPage } from './list.page';
 | 
			
		||||
 | 
			
		||||
@ -37,9 +35,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonCalendarListPage,
 | 
			
		||||
 | 
			
		||||
@ -211,15 +211,15 @@ export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
                oldTable = 'addon_calendar_events';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await db.tableExists(oldTable);
 | 
			
		||||
 | 
			
		||||
            // Move the records from the old table.
 | 
			
		||||
            const events = await db.getAllRecords<AddonCalendarEventDBRecord>(oldTable);
 | 
			
		||||
            const promises = events.map((event) => db.insertRecord(newTable, event));
 | 
			
		||||
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await db.tableExists(oldTable);
 | 
			
		||||
 | 
			
		||||
                // Move the records from the old table.
 | 
			
		||||
                const events = await db.getAllRecords<AddonCalendarEventDBRecord>(oldTable);
 | 
			
		||||
                const promises = events.map((event) => db.insertRecord(newTable, event));
 | 
			
		||||
 | 
			
		||||
                await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
                db.dropTable(oldTable);
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Old table does not exist, ignore.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								src/addons/messageoutput/airnotifier/airnotifier.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/addons/messageoutput/airnotifier/airnotifier.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
// (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 { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate';
 | 
			
		||||
import {
 | 
			
		||||
    AddonMessageOutputAirnotifierHandler,
 | 
			
		||||
    AddonMessageOutputAirnotifierHandlerService,
 | 
			
		||||
} from './services/handlers/messageoutput';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: AddonMessageOutputAirnotifierHandlerService.PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('./pages/devices/devices.module').then( m => m.AddonMessageOutputAirnotifierDevicesPageModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                AddonMessageOutputDelegate.instance.registerHandler(AddonMessageOutputAirnotifierHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonMessageOutputAirnotifierModule {}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/addons/messageoutput/airnotifier/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/addons/messageoutput/airnotifier/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "processorsettingsdesc": "Configure devices"
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
<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.messageoutput_airnotifier.processorsettingsdesc' | translate }}</ion-title>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-refresher slot="fixed" [disabled]="!devicesLoaded" (ionRefresh)="refreshDevices($event)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
    <core-loading [hideUntil]="devicesLoaded">
 | 
			
		||||
        <ion-list>
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngFor="let device of devices">
 | 
			
		||||
                <ion-label [class.core-bold]="device.current">
 | 
			
		||||
                    {{ device.name }} {{ device.model }} {{ device.platform }} {{ device.version }}
 | 
			
		||||
                    <span *ngIf="device.current">({{ 'core.currentdevice' | translate }})</span>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-spinner *ngIf="device.updating" slot="end"></ion-spinner>
 | 
			
		||||
                <ion-toggle [disabled]="device.updating" [(ngModel)]="device.enable"
 | 
			
		||||
                    (ngModelChange)="enableDevice(device, device.enable)">
 | 
			
		||||
                </ion-toggle>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
@ -0,0 +1,46 @@
 | 
			
		||||
// (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 { CommonModule } from '@angular/common';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonMessageOutputAirnotifierDevicesPage } from './devices';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: '',
 | 
			
		||||
        component: AddonMessageOutputAirnotifierDevicesPage,
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonMessageOutputAirnotifierDevicesPage,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [RouterModule],
 | 
			
		||||
})
 | 
			
		||||
export class AddonMessageOutputAirnotifierDevicesPageModule {}
 | 
			
		||||
							
								
								
									
										161
									
								
								src/addons/messageoutput/airnotifier/pages/devices/devices.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/addons/messageoutput/airnotifier/pages/devices/devices.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
			
		||||
// (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 { IonRefresher } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
 | 
			
		||||
import { AddonMessageOutputAirnotifier, AddonMessageOutputAirnotifierDevice } from '../../services/airnotifier';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the list of devices.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-message-output-airnotifier-devices',
 | 
			
		||||
    templateUrl: 'devices.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonMessageOutputAirnotifierDevicesPage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    devices?: AddonMessageOutputAirnotifierDeviceFormatted[] = [];
 | 
			
		||||
    devicesLoaded = false;
 | 
			
		||||
 | 
			
		||||
    protected updateTimeout?: number;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.fetchDevices();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetches the list of devices.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchDevices(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            const devices = await AddonMessageOutputAirnotifier.instance.getUserDevices();
 | 
			
		||||
 | 
			
		||||
            this.devices = this.formatDevices(devices);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.instance.showErrorModal(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.devicesLoaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add some calculated data for devices.
 | 
			
		||||
     *
 | 
			
		||||
     * @param devices Devices to format.
 | 
			
		||||
     * @return Formatted devices.
 | 
			
		||||
     */
 | 
			
		||||
    protected formatDevices(devices: AddonMessageOutputAirnotifierDevice[]): AddonMessageOutputAirnotifierDeviceFormatted[] {
 | 
			
		||||
        const formattedDevices: AddonMessageOutputAirnotifierDeviceFormatted[] = devices;
 | 
			
		||||
        const pushId = CorePushNotifications.instance.getPushId();
 | 
			
		||||
 | 
			
		||||
        // Convert enabled to boolean and search current device.
 | 
			
		||||
        formattedDevices.forEach((device) => {
 | 
			
		||||
            device.enable = !!device.enable;
 | 
			
		||||
            device.current = !!(pushId && pushId == device.pushid);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return formattedDevices;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update list of devices after a certain time. The purpose is to store the updated data, it won't be reflected in the view.
 | 
			
		||||
     */
 | 
			
		||||
    protected updateDevicesAfterDelay(): void {
 | 
			
		||||
        // Cancel pending updates.
 | 
			
		||||
        if (this.updateTimeout) {
 | 
			
		||||
            clearTimeout(this.updateTimeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.updateTimeout = window.setTimeout(() => {
 | 
			
		||||
            this.updateTimeout = undefined;
 | 
			
		||||
            this.updateDevices();
 | 
			
		||||
        }, 5000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch devices. The purpose is to store the updated data, it won't be reflected in the view.
 | 
			
		||||
     */
 | 
			
		||||
    protected async updateDevices(): Promise<void> {
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(AddonMessageOutputAirnotifier.instance.invalidateUserDevices());
 | 
			
		||||
 | 
			
		||||
        await AddonMessageOutputAirnotifier.instance.getUserDevices();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the list of devices.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresher Refresher.
 | 
			
		||||
     */
 | 
			
		||||
    async refreshDevices(refresher: CustomEvent<IonRefresher>): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreUtils.instance.ignoreErrors(AddonMessageOutputAirnotifier.instance.invalidateUserDevices());
 | 
			
		||||
 | 
			
		||||
            await this.fetchDevices();
 | 
			
		||||
        } finally {
 | 
			
		||||
            refresher?.detail.complete();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enable or disable a certain device.
 | 
			
		||||
     *
 | 
			
		||||
     * @param device The device object.
 | 
			
		||||
     * @param enable True to enable the device, false to disable it.
 | 
			
		||||
     */
 | 
			
		||||
    async enableDevice(device: AddonMessageOutputAirnotifierDeviceFormatted, enable: boolean): Promise<void> {
 | 
			
		||||
        device.updating = true;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await AddonMessageOutputAirnotifier.instance.enableDevice(device.id, enable);
 | 
			
		||||
 | 
			
		||||
            // Update the list of devices since it was modified.
 | 
			
		||||
            this.updateDevicesAfterDelay();
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Show error and revert change.
 | 
			
		||||
            CoreDomUtils.instance.showErrorModal(error);
 | 
			
		||||
            device.enable = !device.enable;
 | 
			
		||||
        } finally {
 | 
			
		||||
            device.updating = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Page destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        // If there is a pending action to update devices, execute it right now.
 | 
			
		||||
        if (this.updateTimeout) {
 | 
			
		||||
            clearTimeout(this.updateTimeout);
 | 
			
		||||
            this.updateDevices();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * User device with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
type AddonMessageOutputAirnotifierDeviceFormatted = AddonMessageOutputAirnotifierDevice & {
 | 
			
		||||
    current?: boolean; // Calculated in the app. Whether it's the current device.
 | 
			
		||||
    updating?: boolean; // Calculated in the app. Whether the device enable is being updated right now.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										190
									
								
								src/addons/messageoutput/airnotifier/services/airnotifier.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/addons/messageoutput/airnotifier/services/airnotifier.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,190 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreWSExternalWarning } from '@services/ws';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreWSError } from '@classes/errors/wserror';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents, CoreEventSiteData } from '@singletons/events';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'mmaMessageOutputAirnotifier:';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle Airnotifier message output.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonMessageOutputAirnotifierProvider {
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        CoreEvents.on(CoreEvents.DEVICE_REGISTERED_IN_MOODLE, async (data: CoreEventSiteData) => {
 | 
			
		||||
            // Get user devices to make Moodle send the devices data to Airnotifier.
 | 
			
		||||
            this.getUserDevices(true, data.siteId);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enables or disables a device.
 | 
			
		||||
     *
 | 
			
		||||
     * @param deviceId Device ID.
 | 
			
		||||
     * @param enable True to enable, false to disable.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if success.
 | 
			
		||||
     */
 | 
			
		||||
    async enableDevice(deviceId: number, enable: boolean, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const data: AddonMessageOutputAirnotifierEnableDeviceWSParams = {
 | 
			
		||||
            deviceid: deviceId,
 | 
			
		||||
            enable: !!enable,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const result = await site.write<AddonMessageOutputAirnotifierEnableDeviceWSResponse>(
 | 
			
		||||
            'message_airnotifier_enable_device',
 | 
			
		||||
            data,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (result.success) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Fail. Reject with warning message if any.
 | 
			
		||||
        if (result.warnings?.length) {
 | 
			
		||||
            throw new CoreWSError(result.warnings[0]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw new CoreError('Error enabling device');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the cache key for the get user devices call.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getUserDevicesCacheKey(): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + 'userDevices';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get user devices.
 | 
			
		||||
     *
 | 
			
		||||
     * @param ignoreCache Whether to ignore cache.
 | 
			
		||||
     * @param siteId Site ID. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved with the devices.
 | 
			
		||||
     */
 | 
			
		||||
    async getUserDevices(ignoreCache?: boolean, siteId?: string): Promise<AddonMessageOutputAirnotifierDevice[]> {
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const data: AddonMessageOutputAirnotifierGetUserDevicesWSParams = {
 | 
			
		||||
            appid: CoreConstants.CONFIG.app_id,
 | 
			
		||||
        };
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getUserDevicesCacheKey(),
 | 
			
		||||
            updateFrequency: CoreSite.FREQUENCY_RARELY,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (ignoreCache) {
 | 
			
		||||
            preSets.getFromCache = false;
 | 
			
		||||
            preSets.emergencyCache = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const result = await site.read<AddonMessageOutputAirnotifierGetUserDevicesWSResponse>(
 | 
			
		||||
            'message_airnotifier_get_user_devices',
 | 
			
		||||
            data,
 | 
			
		||||
            preSets,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return result.devices;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate get user devices.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateUserDevices(siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.invalidateWsCacheForKey(this.getUserDevicesCacheKey());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether or not the plugin is enabled for the current site.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True if enabled, false otherwise.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): boolean {
 | 
			
		||||
        return CoreSites.instance.wsAvailableInCurrentSite('message_airnotifier_enable_device') &&
 | 
			
		||||
                CoreSites.instance.wsAvailableInCurrentSite('message_airnotifier_get_user_devices');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonMessageOutputAirnotifier extends makeSingleton(AddonMessageOutputAirnotifierProvider) {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Device data returned by WS message_airnotifier_get_user_devices.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonMessageOutputAirnotifierDevice = {
 | 
			
		||||
    id: number; // Device id (in the message_airnotifier table).
 | 
			
		||||
    appid: string; // The app id, something like com.moodle.moodlemobile.
 | 
			
		||||
    name: string; // The device name, 'occam' or 'iPhone' etc.
 | 
			
		||||
    model: string; // The device model 'Nexus4' or 'iPad1,1' etc.
 | 
			
		||||
    platform: string; // The device platform 'iOS' or 'Android' etc.
 | 
			
		||||
    version: string; // The device version '6.1.2' or '4.2.2' etc.
 | 
			
		||||
    pushid: string; // The device PUSH token/key/identifier/registration id.
 | 
			
		||||
    uuid: string; // The device UUID.
 | 
			
		||||
    enable: number | boolean; // Whether the device is enabled or not.
 | 
			
		||||
    timecreated: number; // Time created.
 | 
			
		||||
    timemodified: number; // Time modified.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of message_airnotifier_enable_device WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonMessageOutputAirnotifierEnableDeviceWSParams = {
 | 
			
		||||
    deviceid: number; // The device id.
 | 
			
		||||
    enable: boolean; // True for enable the device, false otherwise.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Result of WS message_airnotifier_enable_device.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonMessageOutputAirnotifierEnableDeviceWSResponse = {
 | 
			
		||||
    success: boolean; // True if success.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of message_airnotifier_get_user_devices WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonMessageOutputAirnotifierGetUserDevicesWSParams = {
 | 
			
		||||
    appid: string; // App unique id (usually a reversed domain).
 | 
			
		||||
    userid?: number; // User id, 0 for current user.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Result of WS message_airnotifier_get_user_devices.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonMessageOutputAirnotifierGetUserDevicesWSResponse = {
 | 
			
		||||
    devices: AddonMessageOutputAirnotifierDevice[]; // List of devices.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,61 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { AddonMessageOutputHandler, AddonMessageOutputHandlerData } from '@addons/messageoutput/services/messageoutput-delegate';
 | 
			
		||||
import { AddonMessageOutputAirnotifierProvider } from '../airnotifier';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Airnotifier message output handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonMessageOutputAirnotifierHandlerService implements AddonMessageOutputHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'messageoutput-airnotifier';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonMessageOutputAirnotifier';
 | 
			
		||||
    processorName = 'airnotifier';
 | 
			
		||||
 | 
			
		||||
    constructor(private airnotifierProvider: AddonMessageOutputAirnotifierProvider) {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the module is enabled for the site.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True if enabled, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return this.airnotifierProvider.isEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the data needed to render the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @param processor The processor object.
 | 
			
		||||
     * @return Data.
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
    getDisplayData(processor: Record<string, unknown>): AddonMessageOutputHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            priority: 600,
 | 
			
		||||
            label: 'addon.messageoutput_airnotifier.processorsettingsdesc',
 | 
			
		||||
            icon: 'fas-cog',
 | 
			
		||||
            page: AddonMessageOutputAirnotifierHandlerService.PAGE_NAME,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonMessageOutputAirnotifierHandler extends makeSingleton(AddonMessageOutputAirnotifierHandlerService) {}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/addons/messageoutput/messageoutput.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/addons/messageoutput/messageoutput.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
// (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 { AddonMessageOutputAirnotifierModule } from './airnotifier/airnotifier.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        AddonMessageOutputAirnotifierModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonMessageOutputModule {}
 | 
			
		||||
							
								
								
									
										93
									
								
								src/addons/messageoutput/services/messageoutput-delegate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/addons/messageoutput/services/messageoutput-delegate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
import { Params } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Interface that all message output handlers must implement.
 | 
			
		||||
 */
 | 
			
		||||
export interface AddonMessageOutputHandler extends CoreDelegateHandler {
 | 
			
		||||
    /**
 | 
			
		||||
     * The name of the processor. E.g. 'airnotifier'.
 | 
			
		||||
     */
 | 
			
		||||
    processorName: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the data needed to render the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @param processor The processor object.
 | 
			
		||||
     * @return Data.
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(processor: Record<string, unknown>): AddonMessageOutputHandlerData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data needed to render a message output handler. It's returned by the handler.
 | 
			
		||||
 */
 | 
			
		||||
export interface AddonMessageOutputHandlerData {
 | 
			
		||||
    /**
 | 
			
		||||
     * Handler's priority.
 | 
			
		||||
     */
 | 
			
		||||
    priority: number;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Name of the page to load for the handler.
 | 
			
		||||
     */
 | 
			
		||||
    page: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Label to display for the handler.
 | 
			
		||||
     */
 | 
			
		||||
    label: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Name of the icon to display for the handler.
 | 
			
		||||
     */
 | 
			
		||||
    icon: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Params to pass to the page.
 | 
			
		||||
     */
 | 
			
		||||
    pageParams?: Params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Delegate to register processors (message/output) to be used in places like notification preferences.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonMessageOutputDelegateService extends CoreDelegate<AddonMessageOutputHandler> {
 | 
			
		||||
 | 
			
		||||
    protected handlerNameProperty = 'processorName';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonMessageOutputDelegate', true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the display data of the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @param processor The processor object.
 | 
			
		||||
     * @return Data.
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(processor: Record<string, unknown>): AddonMessageOutputHandlerData | undefined {
 | 
			
		||||
        return this.executeFunctionOnEnabled(<string> processor.name, 'getDisplayData', [processor]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonMessageOutputDelegate extends makeSingleton(AddonMessageOutputDelegateService) {}
 | 
			
		||||
							
								
								
									
										91
									
								
								src/addons/notifications/components/actions/actions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/addons/notifications/components/actions/actions.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
			
		||||
// (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, Input, OnInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreContentLinksDelegate, CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays the actions for a notification.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-notifications-actions',
 | 
			
		||||
    templateUrl: 'addon-notifications-actions.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsActionsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() contextUrl?: string;
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Input() data?: Record<string, unknown>; // Extra data to handle the URL.
 | 
			
		||||
 | 
			
		||||
    actions: CoreContentLinksAction[] = [];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        if (!this.contextUrl && (!this.data || !this.data.appurl)) {
 | 
			
		||||
            // No URL, nothing to do.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let actions: CoreContentLinksAction[] = [];
 | 
			
		||||
 | 
			
		||||
        // Treat appurl first if any.
 | 
			
		||||
        if (this.data?.appurl) {
 | 
			
		||||
            actions = await CoreContentLinksDelegate.instance.getActionsFor(
 | 
			
		||||
                <string> this.data.appurl,
 | 
			
		||||
                this.courseId,
 | 
			
		||||
                undefined,
 | 
			
		||||
                this.data,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!actions.length && this.contextUrl) {
 | 
			
		||||
            // No appurl or cannot handle it. Try with contextUrl.
 | 
			
		||||
            actions = await CoreContentLinksDelegate.instance.getActionsFor(this.contextUrl, this.courseId, undefined, this.data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!actions.length) {
 | 
			
		||||
            // URL is not supported. Add an action to open it in browser.
 | 
			
		||||
            actions.push({
 | 
			
		||||
                message: 'core.view',
 | 
			
		||||
                icon: 'fas-eye',
 | 
			
		||||
                action: this.openInBrowser.bind(this),
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actions = actions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default action. Open in browser.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID to use.
 | 
			
		||||
     * @param navCtrl NavController.
 | 
			
		||||
     */
 | 
			
		||||
    protected async openInBrowser(siteId?: string): Promise<void> {
 | 
			
		||||
        const url = <string> this.data?.appurl || this.contextUrl;
 | 
			
		||||
 | 
			
		||||
        if (!url) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        site.openInBrowserWithAutoLogin(url);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<ion-row *ngIf="actions && actions.length > 0" class="justify-content-around">
 | 
			
		||||
    <ion-col *ngFor="let action of actions">
 | 
			
		||||
        <ion-button fill="clear" expand="block" (click)="action.action()">
 | 
			
		||||
            <ion-icon slot="start" name="{{action.icon}}"></ion-icon>
 | 
			
		||||
            {{ action.message | translate }}
 | 
			
		||||
        </ion-button>
 | 
			
		||||
    </ion-col>
 | 
			
		||||
</ion-row>
 | 
			
		||||
							
								
								
									
										35
									
								
								src/addons/notifications/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/addons/notifications/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
// (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 { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { AddonNotificationsActionsComponent } from './actions/actions';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonNotificationsActionsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonNotificationsActionsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsComponentsModule {}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/addons/notifications/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/addons/notifications/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
    "errorgetnotifications": "Error getting notifications.",
 | 
			
		||||
    "markallread": "Mark all as read",
 | 
			
		||||
    "notificationpreferences": "Notification preferences",
 | 
			
		||||
    "notifications": "Notifications",
 | 
			
		||||
    "playsound": "Play sound",
 | 
			
		||||
    "therearentnotificationsyet": "There are no notifications."
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								src/addons/notifications/notifications-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/addons/notifications/notifications-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
// (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 { RouterModule, ROUTES, Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
import { AddonNotificationsSettingsHandlerService } from './services/handlers/settings';
 | 
			
		||||
 | 
			
		||||
function buildRoutes(injector: Injector): Routes {
 | 
			
		||||
    return [
 | 
			
		||||
        {
 | 
			
		||||
            path: 'list',
 | 
			
		||||
            loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule),
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            path: AddonNotificationsSettingsHandlerService.PAGE_NAME,
 | 
			
		||||
            loadChildren: () => import('./pages/settings/settings.module').then(m => m.AddonNotificationsSettingsPageModule),
 | 
			
		||||
        },
 | 
			
		||||
        ...buildTabMainRoutes(injector, {
 | 
			
		||||
            redirectTo: 'list',
 | 
			
		||||
            pathMatch: 'full',
 | 
			
		||||
        }),
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    exports: [RouterModule],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: ROUTES,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [Injector],
 | 
			
		||||
            useFactory: buildRoutes,
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsLazyModule {}
 | 
			
		||||
							
								
								
									
										56
									
								
								src/addons/notifications/notifications.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/addons/notifications/notifications.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
// (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 { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
 | 
			
		||||
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
 | 
			
		||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
 | 
			
		||||
import { CoreSettingsDelegate } from '@features/settings/services/settings-delegate';
 | 
			
		||||
import { AddonNotificationsMainMenuHandler, AddonNotificationsMainMenuHandlerService } from './services/handlers/mainmenu';
 | 
			
		||||
import { AddonNotificationsCronHandler } from './services/handlers/cron';
 | 
			
		||||
import { AddonNotificationsPushClickHandler } from './services/handlers/push-click';
 | 
			
		||||
import { AddonNotificationsSettingsHandler } from './services/handlers/settings';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: AddonNotificationsMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('@/addons/notifications/notifications-lazy.module').then(m => m.AddonNotificationsLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuRoutingModule.forChild({ children: routes }),
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [CoreMainMenuRoutingModule],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                CoreMainMenuDelegate.instance.registerHandler(AddonNotificationsMainMenuHandler.instance);
 | 
			
		||||
                CoreCronDelegate.instance.register(AddonNotificationsCronHandler.instance);
 | 
			
		||||
                CorePushNotificationsDelegate.instance.registerClickHandler(AddonNotificationsPushClickHandler.instance);
 | 
			
		||||
                CoreSettingsDelegate.instance.registerHandler(AddonNotificationsSettingsHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsModule {}
 | 
			
		||||
							
								
								
									
										65
									
								
								src/addons/notifications/pages/list/list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/addons/notifications/pages/list/list.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
<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.notifications.notifications' | translate }}</ion-title>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-refresher slot="fixed" [disabled]="!notificationsLoaded" (ionRefresh)="refreshNotifications($event)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
    <core-loading [hideUntil]="notificationsLoaded">
 | 
			
		||||
        <div class="ion-padding" *ngIf="canMarkAllNotificationsAsRead">
 | 
			
		||||
            <ion-button *ngIf="!loadingMarkAllNotificationsAsRead" expand="block" (click)="markAllNotificationsAsRead()"
 | 
			
		||||
                color="light">
 | 
			
		||||
                <ion-icon slot="start" name="fas-check"></ion-icon>
 | 
			
		||||
                {{ 'addon.notifications.markallread' | translate }}
 | 
			
		||||
            </ion-button>
 | 
			
		||||
            <ion-button *ngIf="loadingMarkAllNotificationsAsRead" expand="block" color="light">
 | 
			
		||||
                <ion-spinner></ion-spinner>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <ion-card *ngFor="let notification of notifications">
 | 
			
		||||
            <ion-item class="ion-text-wrap" lines="none">
 | 
			
		||||
                <core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start"
 | 
			
		||||
                    [profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname"
 | 
			
		||||
                    [userId]="notification.useridfrom" [extraIcon]="notification.iconurl"></core-user-avatar>
 | 
			
		||||
 | 
			
		||||
                <img *ngIf="notification.useridfrom <= 0 && notification.iconurl" [src]="notification.iconurl" alt=""
 | 
			
		||||
                    role="presentation" class="core-notification-icon" slot="start">
 | 
			
		||||
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{ notification.subject }}</h2>
 | 
			
		||||
                    <p *ngIf="notification.userfromfullname">{{ notification.userfromfullname }}</p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-note slot="end" class="ion-float-end ion-padding-left ion-text-end">
 | 
			
		||||
                    {{ notification.timecreated | coreDateDayOrTime }}
 | 
			
		||||
                    <span *ngIf="!notification.timeread">
 | 
			
		||||
                        <ion-icon name="fas-circle" color="primary">
 | 
			
		||||
                        </ion-icon>
 | 
			
		||||
                    </span>
 | 
			
		||||
                </ion-note>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
            <ion-item class="ion-text-wrap">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <core-format-text [text]="notification.mobiletext | coreCreateLinks" contextLevel="system"
 | 
			
		||||
                    [contextInstanceId]="0" [maxHeight]="notification.displayfullhtml ? 120 : null">
 | 
			
		||||
                    </core-format-text>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
            <addon-notifications-actions [contextUrl]="notification.contexturl" [courseId]="notification.courseid"
 | 
			
		||||
                [data]="notification.customdata">
 | 
			
		||||
            </addon-notifications-actions>
 | 
			
		||||
        </ion-card>
 | 
			
		||||
 | 
			
		||||
        <core-empty-box *ngIf="!notifications || notifications.length <= 0" icon="notifications"
 | 
			
		||||
            [message]="'addon.notifications.therearentnotificationsyet' | translate">
 | 
			
		||||
        </core-empty-box>
 | 
			
		||||
 | 
			
		||||
        <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreNotifications($event)" [error]="loadMoreError">
 | 
			
		||||
        </core-infinite-loading>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										46
									
								
								src/addons/notifications/pages/list/list.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/addons/notifications/pages/list/list.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
// (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 { CommonModule } from '@angular/common';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonNotificationsComponentsModule } from '../../components/components.module';
 | 
			
		||||
import { AddonNotificationsListPage } from './list';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: '',
 | 
			
		||||
        component: AddonNotificationsListPage,
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonNotificationsComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonNotificationsListPage,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [RouterModule],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsListPageModule {}
 | 
			
		||||
							
								
								
									
										61
									
								
								src/addons/notifications/pages/list/list.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/addons/notifications/pages/list/list.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
:host {
 | 
			
		||||
    .core-notification-icon {
 | 
			
		||||
        width: 34px;
 | 
			
		||||
        height: 34px;
 | 
			
		||||
        margin: 10px !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .item core-format-text ::ng-deep {
 | 
			
		||||
        .forumpost {
 | 
			
		||||
            border: 1px solid var(--gray-light);
 | 
			
		||||
            width: 100%;
 | 
			
		||||
            margin: 0 0 1em 0;
 | 
			
		||||
 | 
			
		||||
            td {
 | 
			
		||||
                padding: 10px;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .header {
 | 
			
		||||
                background-color: var(--gray-lighter);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .picture {
 | 
			
		||||
                width: auto;
 | 
			
		||||
                text-align: center;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .subject {
 | 
			
		||||
                font-weight: 700;
 | 
			
		||||
                margin-bottom: 1rem;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
            text-decoration: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .userpicture {
 | 
			
		||||
            border-radius: 50%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .mdl-right {
 | 
			
		||||
            text-align: end;
 | 
			
		||||
            a {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
            font {
 | 
			
		||||
                font-size: 0.9em;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .commands {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hr {
 | 
			
		||||
            margin-top: 1.5rem;
 | 
			
		||||
            margin-bottom: 1.5rem;
 | 
			
		||||
            background-color: var(--gray-light);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										263
									
								
								src/addons/notifications/pages/list/list.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/addons/notifications/pages/list/list.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,263 @@
 | 
			
		||||
// (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 { IonRefresher } from '@ionic/angular';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreEvents, CoreEventObserver } from '@singletons/events';
 | 
			
		||||
import { AddonNotifications, AddonNotificationsAnyNotification, AddonNotificationsProvider } from '../../services/notifications';
 | 
			
		||||
import { AddonNotificationsHelper } from '../../services/notifications-helper';
 | 
			
		||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the list of notifications.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-notifications-list',
 | 
			
		||||
    templateUrl: 'list.html',
 | 
			
		||||
    styleUrls: ['list.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsListPage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    notifications: FormattedNotification[] = [];
 | 
			
		||||
    notificationsLoaded = false;
 | 
			
		||||
    canLoadMore = false;
 | 
			
		||||
    loadMoreError = false;
 | 
			
		||||
    canMarkAllNotificationsAsRead = false;
 | 
			
		||||
    loadingMarkAllNotificationsAsRead = false;
 | 
			
		||||
 | 
			
		||||
    protected isCurrentView?: boolean;
 | 
			
		||||
    protected cronObserver?: CoreEventObserver;
 | 
			
		||||
    protected pushObserver?: Subscription;
 | 
			
		||||
    protected pendingRefresh = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.fetchNotifications();
 | 
			
		||||
 | 
			
		||||
        this.cronObserver = CoreEvents.on(AddonNotificationsProvider.READ_CRON_EVENT, () => {
 | 
			
		||||
            if (!this.isCurrentView) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.notificationsLoaded = false;
 | 
			
		||||
            this.refreshNotifications();
 | 
			
		||||
        }, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        this.pushObserver = CorePushNotificationsDelegate.instance.on('receive').subscribe((notification) => {
 | 
			
		||||
            // New notification received. If it's from current site, refresh the data.
 | 
			
		||||
            if (!this.isCurrentView) {
 | 
			
		||||
                this.pendingRefresh = true;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!CoreUtils.instance.isTrueOrOne(notification.notif) || !CoreSites.instance.isCurrentSite(notification.site)) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.notificationsLoaded = false;
 | 
			
		||||
            this.refreshNotifications();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to get notifications. Gets unread notifications first.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refreh Whether we're refreshing data.
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchNotifications(refresh?: boolean): Promise<void> {
 | 
			
		||||
        this.loadMoreError = false;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const result = await AddonNotificationsHelper.instance.getNotifications(refresh ? [] : this.notifications);
 | 
			
		||||
 | 
			
		||||
            const notifications = result.notifications.map((notification) => this.formatText(notification));
 | 
			
		||||
 | 
			
		||||
            if (refresh) {
 | 
			
		||||
                this.notifications = notifications;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.notifications = this.notifications.concat(notifications);
 | 
			
		||||
            }
 | 
			
		||||
            this.canLoadMore = result.canLoadMore;
 | 
			
		||||
 | 
			
		||||
            this.markNotificationsAsRead(notifications);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.instance.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true);
 | 
			
		||||
            this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.notificationsLoaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark all notifications as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async markAllNotificationsAsRead(): Promise<void> {
 | 
			
		||||
        this.loadingMarkAllNotificationsAsRead = true;
 | 
			
		||||
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.markAllNotificationsAsRead());
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, {}, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        // All marked as read, refresh the list.
 | 
			
		||||
        this.notificationsLoaded = false;
 | 
			
		||||
 | 
			
		||||
        await this.refreshNotifications();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark notifications as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notifications Array of notification objects.
 | 
			
		||||
     */
 | 
			
		||||
    protected async markNotificationsAsRead(notifications: FormattedNotification[]): Promise<void> {
 | 
			
		||||
        if (notifications.length > 0) {
 | 
			
		||||
            const promises = notifications.map(async (notification) => {
 | 
			
		||||
                if (notification.read) {
 | 
			
		||||
                    // Already read, don't mark it.
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await AddonNotifications.instance.markNotificationRead(notification.id);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            await CoreUtils.instance.ignoreErrors(Promise.all(promises));
 | 
			
		||||
 | 
			
		||||
            await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationsList());
 | 
			
		||||
 | 
			
		||||
            CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, {}, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if mark all notifications as read is enabled and there are some to read.
 | 
			
		||||
        if (!AddonNotifications.instance.isMarkAllNotificationsAsReadEnabled()) {
 | 
			
		||||
            this.canMarkAllNotificationsAsRead = false;
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.loadingMarkAllNotificationsAsRead = true;
 | 
			
		||||
 | 
			
		||||
            const unread = await AddonNotifications.instance.getUnreadNotificationsCount();
 | 
			
		||||
 | 
			
		||||
            this.canMarkAllNotificationsAsRead = unread > 0;
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loadingMarkAllNotificationsAsRead = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh notifications.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresher Refresher.
 | 
			
		||||
     * @return Promise<any> Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async refreshNotifications(refresher?: CustomEvent<IonRefresher>): Promise<void> {
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationsList());
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.fetchNotifications(true);
 | 
			
		||||
        } finally {
 | 
			
		||||
            refresher?.detail.complete();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load more results.
 | 
			
		||||
     *
 | 
			
		||||
     * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
 | 
			
		||||
     */
 | 
			
		||||
    async loadMoreNotifications(infiniteComplete?: () => void): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await this.fetchNotifications();
 | 
			
		||||
        } finally {
 | 
			
		||||
            infiniteComplete?.();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Formats the text of a notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification The notification object.
 | 
			
		||||
     */
 | 
			
		||||
    protected formatText(notification: AddonNotificationsAnyNotification): FormattedNotification {
 | 
			
		||||
        const formattedNotification: FormattedNotification = notification;
 | 
			
		||||
        formattedNotification.displayfullhtml = this.shouldDisplayFullHtml(notification);
 | 
			
		||||
        formattedNotification.iconurl = formattedNotification.iconurl || undefined; // Make sure the property exists.
 | 
			
		||||
 | 
			
		||||
        formattedNotification.mobiletext = formattedNotification.displayfullhtml ?
 | 
			
		||||
            notification.fullmessagehtml :
 | 
			
		||||
            CoreTextUtils.instance.replaceNewLines(formattedNotification.mobiletext!.replace(/-{4,}/ig, ''), '<br>');
 | 
			
		||||
 | 
			
		||||
        return formattedNotification;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether we should display full HTML of the notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification Notification.
 | 
			
		||||
     * @return Whether to display full HTML.
 | 
			
		||||
     */
 | 
			
		||||
    protected shouldDisplayFullHtml(notification: FormattedNotification): boolean {
 | 
			
		||||
        return notification.component == 'mod_forum' && notification.eventtype == 'digests';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * User entered the page.
 | 
			
		||||
     */
 | 
			
		||||
    ionViewDidEnter(): void {
 | 
			
		||||
        this.isCurrentView = true;
 | 
			
		||||
 | 
			
		||||
        if (!this.pendingRefresh) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.pendingRefresh = false;
 | 
			
		||||
        this.notificationsLoaded = false;
 | 
			
		||||
 | 
			
		||||
        this.refreshNotifications();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * User left the page.
 | 
			
		||||
     */
 | 
			
		||||
    ionViewDidLeave(): void {
 | 
			
		||||
        this.isCurrentView = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Page destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.cronObserver?.off();
 | 
			
		||||
        this.pushObserver?.unsubscribe();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FormattedNotification = AddonNotificationsAnyNotification & {
 | 
			
		||||
    displayfullhtml?: boolean; // Whether to display the full HTML of the notification.
 | 
			
		||||
    iconurl?: string;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										115
									
								
								src/addons/notifications/pages/settings/settings.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/addons/notifications/pages/settings/settings.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,115 @@
 | 
			
		||||
<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.notifications.notifications' | translate }}</ion-title>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<core-navbar-buttons slot="end">
 | 
			
		||||
    <core-context-menu>
 | 
			
		||||
        <core-context-menu-item *ngFor="let handler of processorHandlers" [priority]="handler.priority"
 | 
			
		||||
            [content]="handler.label | translate" (action)="openExtraPreferences(handler)" [iconAction]="handler.icon">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
    </core-context-menu>
 | 
			
		||||
</core-navbar-buttons>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-refresher slot="fixed" [disabled]="!preferencesLoaded || !notifPrefsEnabled" (ionRefresh)="refreshPreferences($event)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
    <core-loading [hideUntil]="preferencesLoaded">
 | 
			
		||||
        <!-- If notification preferences aren't enabled, show only the notification sound setting. -->
 | 
			
		||||
        <ion-item *ngIf="canChangeSound && !notifPrefsEnabled">
 | 
			
		||||
            <ion-label>{{ 'addon.notifications.playsound' | translate }}</ion-label>
 | 
			
		||||
            <ion-toggle [(ngModel)]="notificationSound" (ngModelChange)="changeNotificationSound(notificationSound)"></ion-toggle>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngIf="notifPrefsEnabled">
 | 
			
		||||
            <ion-card>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="preferences">
 | 
			
		||||
                    <ion-label>{{ 'addon.notifications.notifications' | translate }}</ion-label>
 | 
			
		||||
                    <ion-toggle [(ngModel)]="preferences!.enableall" (ngModelChange)="enableAll(preferences!.enableall)"></ion-toggle>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="canChangeSound">
 | 
			
		||||
                    <ion-label>{{ 'addon.notifications.playsound' | translate }}</ion-label>
 | 
			
		||||
                    <ion-toggle [(ngModel)]="notificationSound" (ngModelChange)="changeNotificationSound(notificationSound)">
 | 
			
		||||
                    </ion-toggle>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
 | 
			
		||||
            <!-- Show processor selector. -->
 | 
			
		||||
            <ion-select *ngIf="preferences && preferences.processors && preferences.processors.length > 0"
 | 
			
		||||
                [ngModel]="currentProcessor!.name" (ngModelChange)="changeProcessor($event)" interface="action-sheet"
 | 
			
		||||
                class="core-button-select">
 | 
			
		||||
                <ion-select-option *ngFor="let processor of preferences.processors" [value]="processor.name">
 | 
			
		||||
                    {{ processor.displayname }}
 | 
			
		||||
                </ion-select-option>
 | 
			
		||||
            </ion-select>
 | 
			
		||||
 | 
			
		||||
            <ion-card list *ngFor="let component of components" class="ion-margin-top">
 | 
			
		||||
                <ion-item-divider class="ion-text-wrap">
 | 
			
		||||
                    <ion-grid class="ion-no-padding">
 | 
			
		||||
                        <ion-row class="ion-no-padding">
 | 
			
		||||
                            <ion-col class="ion-no-padding">{{ component.displayname }}</ion-col>
 | 
			
		||||
                            <ion-col size="2" class="ion-text-center ion-no-padding ion-hide-md-down">
 | 
			
		||||
                                {{ 'core.settings.loggedin' | translate }}
 | 
			
		||||
                            </ion-col>
 | 
			
		||||
                            <ion-col size="2" class="ion-text-center ion-no-padding ion-hide-md-down">
 | 
			
		||||
                                {{ 'core.settings.loggedoff' | translate }}
 | 
			
		||||
                            </ion-col>
 | 
			
		||||
                        </ion-row>
 | 
			
		||||
                    </ion-grid>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <ng-container *ngFor="let notification of component.notifications">
 | 
			
		||||
                    <!-- Tablet view -->
 | 
			
		||||
                    <ion-grid class="ion-text-wrap ion-hide-md-down addon-notifications-table-content">
 | 
			
		||||
                        <ion-row class="ion-align-items-center">
 | 
			
		||||
                            <ion-col class="ion-margin-horizontal">{{ notification.displayname }}</ion-col>
 | 
			
		||||
                            <ion-col size="2" class="ion-text-center" *ngFor="let state of ['loggedin', 'loggedoff']">
 | 
			
		||||
                                <!-- If notifications enabled, show toggle. -->
 | 
			
		||||
                                <ion-spinner [hidden]="!preferences!.enableall ||
 | 
			
		||||
                                    !(notification.processorsByName[currentProcessor!.name][state] &&
 | 
			
		||||
                                    notification.processorsByName[currentProcessor!.name][state].updating)">
 | 
			
		||||
                                </ion-spinner>
 | 
			
		||||
                                <ion-toggle *ngIf="preferences!.enableall && !notification.processorsByName[currentProcessor!.name].locked"
 | 
			
		||||
                                    [(ngModel)]="notification.processorsByName[currentProcessor!.name][state].checked"
 | 
			
		||||
                                    (ngModelChange)="changePreference(notification, state)"
 | 
			
		||||
                                    [disabled]="notification.processorsByName[currentProcessor!.name][state].updating">
 | 
			
		||||
                                </ion-toggle>
 | 
			
		||||
                                <span class="text-gray"
 | 
			
		||||
                                    *ngIf="preferences!.enableall && notification.processorsByName[currentProcessor!.name].locked">
 | 
			
		||||
                                    {{'core.settings.locked' | translate }}
 | 
			
		||||
                                </span>
 | 
			
		||||
                                <!-- If notifications are disabled, show "Disabled" instead of toggle. -->
 | 
			
		||||
                                <span *ngIf="!preferences!.enableall">{{ 'core.settings.disabled' | translate }}</span>
 | 
			
		||||
                            </ion-col>
 | 
			
		||||
                        </ion-row>
 | 
			
		||||
                    </ion-grid>
 | 
			
		||||
 | 
			
		||||
                    <!-- Phone view -->
 | 
			
		||||
                    <ion-list-header class="ion-text-wrap ion-no-margin ion-hide-md-up">
 | 
			
		||||
                        {{ notification.displayname }}
 | 
			
		||||
                    </ion-list-header>
 | 
			
		||||
                    <!-- If notifications enabled, show toggles. If disabled, show "Disabled" instead of toggle. -->
 | 
			
		||||
                    <ion-item *ngFor="let state of ['loggedin', 'loggedoff']" class="ion-text-wrap ion-hide-md-up" lines="none">
 | 
			
		||||
                        <ion-label>{{ 'core.settings.' + state | translate }}</ion-label>
 | 
			
		||||
                        <ion-spinner slot="end" *ngIf="preferences!.enableall && (notification.processorsByName[currentProcessor!.name][state] &&
 | 
			
		||||
                            notification.processorsByName[currentProcessor!.name][state].updating)">
 | 
			
		||||
                        </ion-spinner>
 | 
			
		||||
                        <ion-toggle slot="end" *ngIf="preferences!.enableall && !notification.processorsByName[currentProcessor!.name].locked"
 | 
			
		||||
                            [(ngModel)]="notification.processorsByName[currentProcessor!.name][state].checked"
 | 
			
		||||
                            (ngModelChange)="changePreference(notification, state)"
 | 
			
		||||
                            [disabled]="notification.processorsByName[currentProcessor!.name][state].updating">
 | 
			
		||||
                        </ion-toggle>
 | 
			
		||||
                        <span slot="end" *ngIf="preferences!.enableall && notification.processorsByName[currentProcessor!.name].locked" class="text-gray">
 | 
			
		||||
                            {{'core.settings.locked' | translate }}
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <ion-note slot="end" *ngIf="!preferences!.enableall">{{ 'core.settings.disabled' | translate }}</ion-note>
 | 
			
		||||
                    </ion-item>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										46
									
								
								src/addons/notifications/pages/settings/settings.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/addons/notifications/pages/settings/settings.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
// (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 { CommonModule } from '@angular/common';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonNotificationsSettingsPage } from './settings';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: '',
 | 
			
		||||
        component: AddonNotificationsSettingsPage,
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonNotificationsSettingsPage,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [RouterModule],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsSettingsPageModule {}
 | 
			
		||||
							
								
								
									
										5
									
								
								src/addons/notifications/pages/settings/settings.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/addons/notifications/pages/settings/settings.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
:host {
 | 
			
		||||
    .addon-notifications-table-content ion-row {
 | 
			
		||||
        min-height: 35px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										308
									
								
								src/addons/notifications/pages/settings/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								src/addons/notifications/pages/settings/settings.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,308 @@
 | 
			
		||||
// (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, OnInit, OnDestroy } from '@angular/core';
 | 
			
		||||
import { IonRefresher, NavController } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreConfig } from '@services/config';
 | 
			
		||||
import { CoreLocalNotifications } from '@services/local-notifications';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { AddonMessageOutputDelegate, AddonMessageOutputHandlerData } from '@addons/messageoutput/services/messageoutput-delegate';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreEventNotificationSoundChangedData, CoreEvents } from '@singletons/events';
 | 
			
		||||
import {
 | 
			
		||||
    AddonNotifications,
 | 
			
		||||
    AddonNotificationsPreferencesProcessor,
 | 
			
		||||
    AddonNotificationsPreferencesNotificationProcessorState,
 | 
			
		||||
} from '../../services/notifications';
 | 
			
		||||
import {
 | 
			
		||||
    AddonNotificationsHelper,
 | 
			
		||||
    AddonNotificationsPreferencesComponentFormatted,
 | 
			
		||||
    AddonNotificationsPreferencesFormatted,
 | 
			
		||||
    AddonNotificationsPreferencesNotificationFormatted,
 | 
			
		||||
    AddonNotificationsPreferencesProcessorFormatted,
 | 
			
		||||
} from '@addons/notifications/services/notifications-helper';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
// import { CoreSplitViewComponent } from '@components/split-view/split-view';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays notifications settings.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-notifications-settings',
 | 
			
		||||
    templateUrl: 'settings.html',
 | 
			
		||||
    styleUrls: ['settings.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class AddonNotificationsSettingsPage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    preferences?: AddonNotificationsPreferencesFormatted;
 | 
			
		||||
    components?: AddonNotificationsPreferencesComponentFormatted[];
 | 
			
		||||
    currentProcessor?: AddonNotificationsPreferencesProcessor;
 | 
			
		||||
    preferencesLoaded = false;
 | 
			
		||||
    notificationSound = false;
 | 
			
		||||
    notifPrefsEnabled: boolean;
 | 
			
		||||
    canChangeSound: boolean;
 | 
			
		||||
    processorHandlers: AddonMessageOutputHandlerData[] = [];
 | 
			
		||||
 | 
			
		||||
    protected updateTimeout?: number;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        protected navCtrl: NavController,
 | 
			
		||||
        // @Optional() protected svComponent: CoreSplitViewComponent,
 | 
			
		||||
    ) {
 | 
			
		||||
        this.notifPrefsEnabled = AddonNotifications.instance.isNotificationPreferencesEnabled();
 | 
			
		||||
        this.canChangeSound = CoreLocalNotifications.instance.canDisableSound();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        if (this.canChangeSound) {
 | 
			
		||||
            this.notificationSound = await CoreConfig.instance.get<boolean>(CoreConstants.SETTINGS_NOTIFICATION_SOUND, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.notifPrefsEnabled) {
 | 
			
		||||
            this.fetchPreferences();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.preferencesLoaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetches preferences data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchPreferences(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            const preferences = await AddonNotifications.instance.getNotificationPreferences();
 | 
			
		||||
 | 
			
		||||
            if (!this.currentProcessor) {
 | 
			
		||||
                // Initialize current processor. Load "Mobile" (airnotifier) if available.
 | 
			
		||||
                this.currentProcessor = AddonNotificationsHelper.instance.getProcessor(preferences.processors, 'airnotifier');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.currentProcessor) {
 | 
			
		||||
                // Shouldn't happen.
 | 
			
		||||
                throw new CoreError('No processor found');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            preferences.enableall = !preferences.disableall;
 | 
			
		||||
            this.preferences = AddonNotificationsHelper.instance.formatPreferences(preferences);
 | 
			
		||||
            this.loadProcessor(this.currentProcessor);
 | 
			
		||||
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.instance.showErrorModal(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.preferencesLoaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load a processor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param processor Processor object.
 | 
			
		||||
     */
 | 
			
		||||
    protected loadProcessor(processor: AddonNotificationsPreferencesProcessorFormatted): void {
 | 
			
		||||
        if (!processor) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.currentProcessor = processor;
 | 
			
		||||
        this.processorHandlers = [];
 | 
			
		||||
        this.components = AddonNotificationsHelper.instance.getProcessorComponents(
 | 
			
		||||
            processor.name,
 | 
			
		||||
            this.preferences?.components || [],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!processor.hassettings || !processor.supported) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const handlerData = AddonMessageOutputDelegate.instance.getDisplayData(processor);
 | 
			
		||||
        if (handlerData) {
 | 
			
		||||
            this.processorHandlers.push(handlerData);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update preferences after a certain time. The purpose is to store the updated data, it won't be reflected in the view.
 | 
			
		||||
     */
 | 
			
		||||
    protected updatePreferencesAfterDelay(): void {
 | 
			
		||||
        // Cancel pending updates.
 | 
			
		||||
        clearTimeout(this.updateTimeout);
 | 
			
		||||
 | 
			
		||||
        this.updateTimeout = window.setTimeout(() => {
 | 
			
		||||
            this.updateTimeout = undefined;
 | 
			
		||||
            this.updatePreferences();
 | 
			
		||||
        }, 5000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update preferences. The purpose is to store the updated data, it won't be reflected in the view.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async updatePreferences(): Promise<void> {
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationPreferences());
 | 
			
		||||
 | 
			
		||||
        await AddonNotifications.instance.getNotificationPreferences();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The selected processor was changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name Name of the selected processor.
 | 
			
		||||
     */
 | 
			
		||||
    changeProcessor(name: string): void {
 | 
			
		||||
        const processor = this.preferences!.processors.find((processor) => processor.name == name);
 | 
			
		||||
 | 
			
		||||
        if (processor) {
 | 
			
		||||
            this.loadProcessor(processor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the list of preferences.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresher Refresher.
 | 
			
		||||
     */
 | 
			
		||||
    async refreshPreferences(refresher?: CustomEvent<IonRefresher>): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationPreferences());
 | 
			
		||||
 | 
			
		||||
            await this.fetchPreferences();
 | 
			
		||||
        } finally {
 | 
			
		||||
            refresher?.detail.complete();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open extra preferences.
 | 
			
		||||
     *
 | 
			
		||||
     * @param handlerData
 | 
			
		||||
     */
 | 
			
		||||
    openExtraPreferences(handlerData: AddonMessageOutputHandlerData): void {
 | 
			
		||||
        // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav.
 | 
			
		||||
        // @todo const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
 | 
			
		||||
        CoreNavigator.instance.navigateToSitePath(handlerData.page, { params: handlerData.pageParams });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Change the value of a certain preference.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification Notification object.
 | 
			
		||||
     * @param state State name, ['loggedin', 'loggedoff'].
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async changePreference(notification: AddonNotificationsPreferencesNotificationFormatted, state: string): Promise<void> {
 | 
			
		||||
        const processor = notification.processorsByName?.[this.currentProcessor?.name || ''];
 | 
			
		||||
        if (!processor) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const processorState: ProcessorStateFormatted = processor[state];
 | 
			
		||||
        const preferenceName = notification.preferencekey + '_' + processorState.name;
 | 
			
		||||
        let value: string | undefined;
 | 
			
		||||
 | 
			
		||||
        notification.processors.forEach((processor) => {
 | 
			
		||||
            if (processor[state].checked) {
 | 
			
		||||
                if (!value) {
 | 
			
		||||
                    value = processor.name;
 | 
			
		||||
                } else {
 | 
			
		||||
                    value += ',' + processor.name;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!value) {
 | 
			
		||||
            value = 'none';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        processorState.updating = true;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreUser.instance.updateUserPreference(preferenceName, value);
 | 
			
		||||
 | 
			
		||||
            // Update the preferences since they were modified.
 | 
			
		||||
            this.updatePreferencesAfterDelay();
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Show error and revert change.
 | 
			
		||||
            CoreDomUtils.instance.showErrorModal(error);
 | 
			
		||||
            processor[state].checked = !processor[state].checked;
 | 
			
		||||
        } finally {
 | 
			
		||||
            processorState.updating = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enable all notifications changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param enable Whether to enable or disable.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async enableAll(enable?: boolean): Promise<void> {
 | 
			
		||||
        const modal = await CoreDomUtils.instance.showModalLoading('core.sending', true);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            CoreUser.instance.updateUserPreferences([], !enable);
 | 
			
		||||
 | 
			
		||||
            // Update the preferences since they were modified.
 | 
			
		||||
            this.updatePreferencesAfterDelay();
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Show error and revert change.
 | 
			
		||||
            CoreDomUtils.instance.showErrorModal(error);
 | 
			
		||||
            this.preferences!.enableall = !this.preferences!.enableall;
 | 
			
		||||
        } finally {
 | 
			
		||||
            modal.dismiss();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Change the notification sound setting.
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled True to enable the notification sound, false to disable it.
 | 
			
		||||
     */
 | 
			
		||||
    async changeNotificationSound(enabled: boolean): Promise<void> {
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(CoreConfig.instance.set(CoreConstants.SETTINGS_NOTIFICATION_SOUND, enabled ? 1 : 0));
 | 
			
		||||
 | 
			
		||||
        const siteId = CoreSites.instance.getCurrentSiteId();
 | 
			
		||||
        CoreEvents.trigger<CoreEventNotificationSoundChangedData>(CoreEvents.NOTIFICATION_SOUND_CHANGED, { enabled }, siteId);
 | 
			
		||||
        CoreLocalNotifications.instance.rescheduleAll();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Page destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        // If there is a pending action to update preferences, execute it right now.
 | 
			
		||||
        if (this.updateTimeout) {
 | 
			
		||||
            clearTimeout(this.updateTimeout);
 | 
			
		||||
            this.updatePreferences();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * State in notification processor in notification preferences component with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
type ProcessorStateFormatted = AddonNotificationsPreferencesNotificationProcessorState & {
 | 
			
		||||
    updating?: boolean; // Calculated in the app. Whether the state is being updated.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										80
									
								
								src/addons/notifications/services/handlers/cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/addons/notifications/services/handlers/cron.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreCronHandler } from '@services/cron';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { AddonNotifications, AddonNotificationsProvider } from '../notifications';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Notifications cron handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonNotificationsCronHandlerService implements CoreCronHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonNotificationsCronHandler';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the time between consecutive executions.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Time between consecutive executions (in ms).
 | 
			
		||||
     */
 | 
			
		||||
    getInterval(): number {
 | 
			
		||||
        return CoreApp.instance.isMobile() ? 600000 : 60000; // 1 or 10 minutes.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether it's a synchronization process or not. True if not defined.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether it's a synchronization process or not.
 | 
			
		||||
     */
 | 
			
		||||
    isSync(): boolean {
 | 
			
		||||
        // This is done to use only wifi if using the fallback function.
 | 
			
		||||
        return !AddonNotifications.instance.isPreciseNotificationCountEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether the sync can be executed manually. Call isSync if not defined.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether the sync can be executed manually.
 | 
			
		||||
     */
 | 
			
		||||
    canManualSync(): boolean {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the process.
 | 
			
		||||
     * Receives the ID of the site affected, undefined for all sites.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId ID of the site affected, undefined for all sites.
 | 
			
		||||
     * @param force Wether the execution is forced (manual sync).
 | 
			
		||||
     * @return Promise resolved when done, rejected if failure. If the promise is rejected, this function
 | 
			
		||||
     *         will be called again often, it shouldn't be abused.
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
    async execute(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        if (!CoreSites.instance.isCurrentSite(siteId)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(AddonNotificationsProvider.READ_CRON_EVENT, {}, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonNotificationsCronHandler extends makeSingleton(AddonNotificationsCronHandlerService) {}
 | 
			
		||||
							
								
								
									
										125
									
								
								src/addons/notifications/services/handlers/mainmenu.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/addons/notifications/services/handlers/mainmenu.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,125 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents, CoreEventSiteData } from '@singletons/events';
 | 
			
		||||
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate';
 | 
			
		||||
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
 | 
			
		||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
 | 
			
		||||
import { AddonNotifications, AddonNotificationsProvider } from '../notifications';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to inject an option into main menu.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonNotificationsMainMenuHandlerService implements CoreMainMenuHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'notifications';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonNotifications';
 | 
			
		||||
    priority = 700;
 | 
			
		||||
 | 
			
		||||
    protected handlerData: CoreMainMenuHandlerData = {
 | 
			
		||||
        icon: 'fas-bell',
 | 
			
		||||
        title: 'addon.notifications.notifications',
 | 
			
		||||
        page: AddonNotificationsMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
        class: 'addon-notifications-handler',
 | 
			
		||||
        showBadge: true,
 | 
			
		||||
        badge: '',
 | 
			
		||||
        loading: true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize the handler.
 | 
			
		||||
     */
 | 
			
		||||
    initialize(): void {
 | 
			
		||||
        CoreEvents.on(AddonNotificationsProvider.READ_CHANGED_EVENT, (data: CoreEventSiteData) => {
 | 
			
		||||
            this.updateBadge(data.siteId);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(AddonNotificationsProvider.READ_CRON_EVENT, (data: CoreEventSiteData) => {
 | 
			
		||||
            this.updateBadge(data.siteId);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Reset info on logout.
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGOUT, () => {
 | 
			
		||||
            this.handlerData.badge = '';
 | 
			
		||||
            this.handlerData.loading = true;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // If a push notification is received, refresh the count.
 | 
			
		||||
        CorePushNotificationsDelegate.instance.on('receive').subscribe((notification) => {
 | 
			
		||||
            // New notification received. If it's from current site, refresh the data.
 | 
			
		||||
            if (CoreUtils.instance.isTrueOrOne(notification.notif) && CoreSites.instance.isCurrentSite(notification.site)) {
 | 
			
		||||
                this.updateBadge(notification.site);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Register Badge counter.
 | 
			
		||||
        CorePushNotificationsDelegate.instance.registerCounterHandler('AddonNotifications');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the data needed to render the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Data needed to render the handler.
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(): CoreMainMenuHandlerData {
 | 
			
		||||
        if (this.handlerData.loading) {
 | 
			
		||||
            this.updateBadge();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.handlerData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID or current Site if undefined.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async updateBadge(siteId?: string): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.instance.getCurrentSiteId();
 | 
			
		||||
        if (!siteId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const unreadCount = await AddonNotifications.instance.getUnreadNotificationsCount(undefined, siteId);
 | 
			
		||||
 | 
			
		||||
            this.handlerData.badge = unreadCount > 0 ? String(unreadCount) : '';
 | 
			
		||||
            CorePushNotifications.instance.updateAddonCounter('AddonNotifications', unreadCount, siteId);
 | 
			
		||||
        } catch {
 | 
			
		||||
            this.handlerData.badge = '';
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.handlerData.loading = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonNotificationsMainMenuHandler extends makeSingleton(AddonNotificationsMainMenuHandlerService) {}
 | 
			
		||||
							
								
								
									
										140
									
								
								src/addons/notifications/services/handlers/push-click.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/addons/notifications/services/handlers/push-click.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,140 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate';
 | 
			
		||||
import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications';
 | 
			
		||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
 | 
			
		||||
import { AddonNotifications, AddonNotificationsProvider } from '../notifications';
 | 
			
		||||
import { AddonNotificationsMainMenuHandlerService } from './mainmenu';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler for non-messaging push notifications clicks.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonNotificationsPushClickHandlerService implements CorePushNotificationsClickHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonNotificationsPushClickHandler';
 | 
			
		||||
    priority = 0; // Low priority so it's used as a fallback if no other handler treats the notification.
 | 
			
		||||
    featureName = 'CoreMainMenuDelegate_AddonNotifications';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a notification click is handled by this handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification The notification to check.
 | 
			
		||||
     * @return Whether the notification click is handled by this handler
 | 
			
		||||
     */
 | 
			
		||||
    async handles(notification: NotificationData): Promise<boolean> {
 | 
			
		||||
        if (!notification.moodlecomponent) {
 | 
			
		||||
            // The notification doesn't come from Moodle. Handle it.
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (CoreUtils.instance.isTrueOrOne(notification.notif)) {
 | 
			
		||||
            // Notification clicked, mark as read. Don't block for this.
 | 
			
		||||
            this.markAsRead(notification);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark the notification as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification Notification to mark.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async markAsRead(notification: NotificationData): Promise<void> {
 | 
			
		||||
        const notifId = notification.savedmessageid || notification.id;
 | 
			
		||||
 | 
			
		||||
        if (!notifId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.markNotificationRead(notifId, notification.site));
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, {}, notification.site);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle the notification click.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification The notification to check.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async handleClick(notification: NotificationData): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        if (notification.customdata?.extendedtext) {
 | 
			
		||||
            // Display the text in a modal.
 | 
			
		||||
            return CoreTextUtils.instance.viewText(notification.title || '', <string> notification.customdata.extendedtext, {
 | 
			
		||||
                displayCopyButton: true,
 | 
			
		||||
                modalOptions: { cssClass: 'core-modal-fullscreen' },
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Try to handle the appurl.
 | 
			
		||||
        if (notification.customdata?.appurl) {
 | 
			
		||||
            const url = <string> notification.customdata.appurl;
 | 
			
		||||
 | 
			
		||||
            switch (notification.customdata.appurlopenin) {
 | 
			
		||||
                case 'inapp':
 | 
			
		||||
                    CoreUtils.instance.openInApp(url);
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                case 'browser':
 | 
			
		||||
                    return CoreUtils.instance.openInBrowser(url);
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    if (CoreContentLinksHelper.instance.handleLink(url, undefined, undefined, true)) {
 | 
			
		||||
                        // Link treated, stop.
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // No appurl or cannot be handled by the app. Try to handle the contexturl now.
 | 
			
		||||
        if (notification.contexturl) {
 | 
			
		||||
            if (CoreContentLinksHelper.instance.handleLink(notification.contexturl)) {
 | 
			
		||||
                // Link treated, stop.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // No contexturl or cannot be handled by the app. Open the notifications page.
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationsList(notification.site));
 | 
			
		||||
 | 
			
		||||
        await CoreNavigator.instance.navigateToSitePath(
 | 
			
		||||
            AddonNotificationsMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
            { siteId: notification.site },
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonNotificationsPushClickHandler extends makeSingleton(AddonNotificationsPushClickHandlerService) {}
 | 
			
		||||
 | 
			
		||||
type NotificationData = CorePushNotificationsNotificationBasicData & {
 | 
			
		||||
    contexturl?: string; // URL related to the notification.
 | 
			
		||||
    savedmessageid?: number; // Notification ID (optional).
 | 
			
		||||
    id?: number; // Notification ID (optional).
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										60
									
								
								src/addons/notifications/services/handlers/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/addons/notifications/services/handlers/settings.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreLocalNotifications } from '@services/local-notifications';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreSettingsHandler, CoreSettingsHandlerData } from '@features/settings/services/settings-delegate';
 | 
			
		||||
import { AddonNotifications } from '../notifications';
 | 
			
		||||
import { AddonNotificationsMainMenuHandlerService } from './mainmenu';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Notifications settings handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonNotificationsSettingsHandlerService implements CoreSettingsHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'settings';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonNotifications';
 | 
			
		||||
    priority = 500;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        // Preferences or notification sound setting available.
 | 
			
		||||
        return CoreLocalNotifications.instance.isAvailable() || AddonNotifications.instance.isNotificationPreferencesEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the data needed to render the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Data needed to render the handler.
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(): CoreSettingsHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: 'fas-bell',
 | 
			
		||||
            title: 'addon.notifications.notifications',
 | 
			
		||||
            page: AddonNotificationsMainMenuHandlerService.PAGE_NAME + '/' + AddonNotificationsSettingsHandlerService.PAGE_NAME,
 | 
			
		||||
            class: 'addon-notifications-settings-handler',
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonNotificationsSettingsHandler extends makeSingleton(AddonNotificationsSettingsHandlerService) {}
 | 
			
		||||
							
								
								
									
										211
									
								
								src/addons/notifications/services/notifications-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/addons/notifications/services/notifications-helper.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,211 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate';
 | 
			
		||||
import {
 | 
			
		||||
    AddonNotifications,
 | 
			
		||||
    AddonNotificationsAnyNotification,
 | 
			
		||||
    AddonNotificationsGetNotificationsOptions,
 | 
			
		||||
    AddonNotificationsPreferences,
 | 
			
		||||
    AddonNotificationsPreferencesComponent,
 | 
			
		||||
    AddonNotificationsPreferencesNotification,
 | 
			
		||||
    AddonNotificationsPreferencesNotificationProcessor,
 | 
			
		||||
    AddonNotificationsPreferencesProcessor,
 | 
			
		||||
    AddonNotificationsProvider,
 | 
			
		||||
} from './notifications';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service that provides some helper functions for notifications.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonNotificationsHelperProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Format preferences data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param preferences Preferences to format.
 | 
			
		||||
     * @return Formatted preferences.
 | 
			
		||||
     */
 | 
			
		||||
    formatPreferences(preferences: AddonNotificationsPreferences): AddonNotificationsPreferencesFormatted {
 | 
			
		||||
        const formattedPreferences: AddonNotificationsPreferencesFormatted = preferences;
 | 
			
		||||
 | 
			
		||||
        formattedPreferences.processors.forEach((processor) => {
 | 
			
		||||
            processor.supported = AddonMessageOutputDelegate.instance.hasHandler(processor.name, true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        formattedPreferences.components.forEach((component) => {
 | 
			
		||||
            component.notifications.forEach((notification) => {
 | 
			
		||||
                notification.processorsByName = CoreUtils.instance.arrayToObject(notification.processors, 'name');
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return formattedPreferences;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get some notifications. It will try to use the new WS if available.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notifications Current list of loaded notifications. It's used to calculate the offset.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with notifications and if can load more.
 | 
			
		||||
     */
 | 
			
		||||
    async getNotifications(
 | 
			
		||||
        notifications: AddonNotificationsAnyNotification[],
 | 
			
		||||
        options?: AddonNotificationsGetNotificationsOptions,
 | 
			
		||||
    ): Promise<{notifications: AddonNotificationsAnyNotification[]; canLoadMore: boolean}> {
 | 
			
		||||
 | 
			
		||||
        notifications = notifications || [];
 | 
			
		||||
        options = options || {};
 | 
			
		||||
        options.limit = options.limit || AddonNotificationsProvider.LIST_LIMIT;
 | 
			
		||||
        options.siteId = options.siteId || CoreSites.instance.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const available = await AddonNotifications.instance.isPopupAvailable(options.siteId);
 | 
			
		||||
 | 
			
		||||
        if (available) {
 | 
			
		||||
            return AddonNotifications.instance.getPopupNotifications(notifications.length, options);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Fallback to get_messages. We need 2 calls, one for read and the other one for unread.
 | 
			
		||||
        const unreadFrom = notifications.reduce((total, current) => total + (current.read ? 0 : 1), 0);
 | 
			
		||||
 | 
			
		||||
        const unread = await AddonNotifications.instance.getUnreadNotifications(unreadFrom, options);
 | 
			
		||||
 | 
			
		||||
        let newNotifications = unread;
 | 
			
		||||
 | 
			
		||||
        if (unread.length < options.limit) {
 | 
			
		||||
            // Limit not reached. Get read notifications until reach the limit.
 | 
			
		||||
            const readLimit = options.limit - unread.length;
 | 
			
		||||
            const readFrom = notifications.length - unreadFrom;
 | 
			
		||||
            const readOptions = Object.assign({}, options, { limit: readLimit });
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                const read = await AddonNotifications.instance.getReadNotifications(readFrom, readOptions);
 | 
			
		||||
 | 
			
		||||
                newNotifications = unread.concat(read);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                if (unread.length <= 0) {
 | 
			
		||||
                    throw error;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            notifications: newNotifications,
 | 
			
		||||
            canLoadMore: notifications.length >= options.limit,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a certain processor from a list of processors.
 | 
			
		||||
     *
 | 
			
		||||
     * @param processors List of processors.
 | 
			
		||||
     * @param name Name of the processor to get.
 | 
			
		||||
     * @param fallback True to return first processor if not found, false to not return any. Defaults to true.
 | 
			
		||||
     * @return Processor.
 | 
			
		||||
     */
 | 
			
		||||
    getProcessor(
 | 
			
		||||
        processors: AddonNotificationsPreferencesProcessor[],
 | 
			
		||||
        name: string,
 | 
			
		||||
        fallback: boolean = true,
 | 
			
		||||
    ): AddonNotificationsPreferencesProcessor | undefined {
 | 
			
		||||
        if (!processors || !processors.length) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const processor = processors.find((processor) => processor.name == name);
 | 
			
		||||
        if (processor) {
 | 
			
		||||
            return processor;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Processor not found, return first if requested.
 | 
			
		||||
        if (fallback) {
 | 
			
		||||
            return processors[0];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the components and notifications that have a certain processor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param processorName Name of the processor to filter.
 | 
			
		||||
     * @param components Array of components.
 | 
			
		||||
     * @return Filtered components.
 | 
			
		||||
     */
 | 
			
		||||
    getProcessorComponents(
 | 
			
		||||
        processorName: string,
 | 
			
		||||
        components: AddonNotificationsPreferencesComponentFormatted[],
 | 
			
		||||
    ): AddonNotificationsPreferencesComponentFormatted[] {
 | 
			
		||||
        const result: AddonNotificationsPreferencesComponentFormatted[] = [];
 | 
			
		||||
 | 
			
		||||
        components.forEach((component) => {
 | 
			
		||||
            // Check if the component has any notification with this processor.
 | 
			
		||||
            const notifications: AddonNotificationsPreferencesNotificationFormatted[] = [];
 | 
			
		||||
 | 
			
		||||
            component.notifications.forEach((notification) => {
 | 
			
		||||
                const processor = notification.processorsByName?.[processorName];
 | 
			
		||||
 | 
			
		||||
                if (processor) {
 | 
			
		||||
                    // Add the notification.
 | 
			
		||||
                    notifications.push(notification);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (notifications.length) {
 | 
			
		||||
                // At least 1 notification added, add the component to the result.
 | 
			
		||||
                result.push({
 | 
			
		||||
                    displayname: component.displayname,
 | 
			
		||||
                    notifications,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonNotificationsHelper extends makeSingleton(AddonNotificationsHelperProvider) {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Preferences with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesFormatted = Omit<AddonNotificationsPreferences, 'processors'|'components'> & {
 | 
			
		||||
    processors: AddonNotificationsPreferencesProcessorFormatted[]; // Config form values.
 | 
			
		||||
    components: AddonNotificationsPreferencesComponentFormatted[]; // Available components.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Preferences component with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesComponentFormatted = Omit<AddonNotificationsPreferencesComponent, 'notifications'> & {
 | 
			
		||||
    notifications: AddonNotificationsPreferencesNotificationFormatted[]; // List of notificaitons for the component.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Preferences notification with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesNotificationFormatted = AddonNotificationsPreferencesNotification & {
 | 
			
		||||
    processorsByName?: Record<string, AddonNotificationsPreferencesNotificationProcessor>; // Calculated in the app.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Preferences processor with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesProcessorFormatted = AddonNotificationsPreferencesProcessor & {
 | 
			
		||||
    supported?: boolean; // Calculated in the app. Whether the processor is supported in the app.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										658
									
								
								src/addons/notifications/services/notifications.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										658
									
								
								src/addons/notifications/services/notifications.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,658 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
 | 
			
		||||
import { CoreWSExternalWarning } from '@services/ws';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
// @todo import { AddonMessages, AddonMessagesMarkMessageReadResult } from '@addon/messages/services/messages';
 | 
			
		||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'mmaNotifications:';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle notifications.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonNotificationsProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly READ_CHANGED_EVENT = 'addon_notifications_read_changed_event';
 | 
			
		||||
    static readonly READ_CRON_EVENT = 'addon_notifications_read_cron_event';
 | 
			
		||||
    static readonly PUSH_SIMULATION_COMPONENT = 'AddonNotificationsPushSimulation';
 | 
			
		||||
    static readonly LIST_LIMIT = 20;
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('AddonNotificationsProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function to format notification data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notifications List of notifications.
 | 
			
		||||
     * @param read Whether the notifications are read or unread.
 | 
			
		||||
     * @return Promise resolved with notifications.
 | 
			
		||||
     */
 | 
			
		||||
    protected async formatNotificationsData(
 | 
			
		||||
        notifications: AddonNotificationsGetMessagesMessage[],
 | 
			
		||||
        read?: boolean,
 | 
			
		||||
    ): Promise<AddonNotificationsGetMessagesMessageFormatted[]>;
 | 
			
		||||
    protected async formatNotificationsData(
 | 
			
		||||
        notifications: AddonNotificationsPopupNotification[],
 | 
			
		||||
        read?: boolean,
 | 
			
		||||
    ): Promise<AddonNotificationsPopupNotificationFormatted[]>;
 | 
			
		||||
    protected async formatNotificationsData(
 | 
			
		||||
        notifications: (AddonNotificationsGetMessagesMessage | AddonNotificationsPopupNotification)[],
 | 
			
		||||
        read?: boolean,
 | 
			
		||||
    ): Promise<AddonNotificationsAnyNotification[]> {
 | 
			
		||||
 | 
			
		||||
        const promises = notifications.map(async (notificationRaw) => {
 | 
			
		||||
            const notification = <AddonNotificationsAnyNotification> notificationRaw;
 | 
			
		||||
 | 
			
		||||
            // Set message to show.
 | 
			
		||||
            if (notification.component && notification.component == 'mod_forum') {
 | 
			
		||||
                notification.mobiletext = notification.smallmessage;
 | 
			
		||||
            } else {
 | 
			
		||||
                notification.mobiletext = notification.fullmessage;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            notification.moodlecomponent = notification.component;
 | 
			
		||||
            notification.notification = 1;
 | 
			
		||||
            notification.notif = 1;
 | 
			
		||||
            if (typeof read != 'undefined') {
 | 
			
		||||
                notification.read = read;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (typeof notification.customdata == 'string') {
 | 
			
		||||
                notification.customdata = CoreTextUtils.instance.parseJSON<Record<string, unknown>>(notification.customdata, {});
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Try to set courseid the notification belongs to.
 | 
			
		||||
            if (notification.customdata?.courseid) {
 | 
			
		||||
                notification.courseid = <number> notification.customdata.courseid;
 | 
			
		||||
            } else if (!notification.courseid) {
 | 
			
		||||
                const courseIdMatch = notification.fullmessagehtml.match(/course\/view\.php\?id=([^"]*)/);
 | 
			
		||||
                if (courseIdMatch?.[1]) {
 | 
			
		||||
                    notification.courseid = parseInt(courseIdMatch[1], 10);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (notification.useridfrom > 0) {
 | 
			
		||||
                // Try to get the profile picture of the user.
 | 
			
		||||
                try {
 | 
			
		||||
                    const user = await CoreUser.instance.getProfile(notification.useridfrom, notification.courseid, true);
 | 
			
		||||
 | 
			
		||||
                    notification.profileimageurlfrom = user.profileimageurl;
 | 
			
		||||
                    notification.userfromfullname = user.fullname;
 | 
			
		||||
                } catch {
 | 
			
		||||
                    // Error getting user. This can happen if device is offline or the user is deleted.
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return notification;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the cache key for the get notification preferences call.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getNotificationPreferencesCacheKey(): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + 'notificationPreferences';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get notification preferences.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved with the notification preferences.
 | 
			
		||||
     */
 | 
			
		||||
    async getNotificationPreferences(siteId?: string): Promise<AddonNotificationsPreferences> {
 | 
			
		||||
        this.logger.debug('Get notification preferences');
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getNotificationPreferencesCacheKey(),
 | 
			
		||||
            updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const data = await site.read<AddonNotificationsGetUserNotificationPreferencesResult>(
 | 
			
		||||
            'core_message_get_user_notification_preferences',
 | 
			
		||||
            {},
 | 
			
		||||
            preSets,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return data.preferences;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache key for notification list WS calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getNotificationsCacheKey(): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + 'list';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get notifications from site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param read True if should get read notifications, false otherwise.
 | 
			
		||||
     * @param offset Position of the first notification to get.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with notifications.
 | 
			
		||||
     */
 | 
			
		||||
    async getNotifications(
 | 
			
		||||
        read: boolean,
 | 
			
		||||
        offset: number,
 | 
			
		||||
        options?: AddonNotificationsGetNotificationsOptions,
 | 
			
		||||
    ): Promise<AddonNotificationsGetMessagesMessageFormatted[]> {
 | 
			
		||||
        options = options || {};
 | 
			
		||||
        options.limit = options.limit || AddonNotificationsProvider.LIST_LIMIT;
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Get ${(read ? 'read' : 'unread')} notifications from ${offset}. Limit: ${options.limit}`);
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.instance.getSite(options.siteId);
 | 
			
		||||
        const data: AddonNotificationsGetMessagesWSParams = {
 | 
			
		||||
            useridto: site.getUserId(),
 | 
			
		||||
            useridfrom: 0,
 | 
			
		||||
            type: 'notifications',
 | 
			
		||||
            read: !!read,
 | 
			
		||||
            newestfirst: true,
 | 
			
		||||
            limitfrom: offset,
 | 
			
		||||
            limitnum: options.limit,
 | 
			
		||||
        };
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getNotificationsCacheKey(),
 | 
			
		||||
            ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Get unread notifications.
 | 
			
		||||
        const response = await site.read<AddonNotificationsGetMessagesWSResponse>('core_message_get_messages', data, preSets);
 | 
			
		||||
 | 
			
		||||
        const notifications = response.messages;
 | 
			
		||||
 | 
			
		||||
        return this.formatNotificationsData(notifications, read);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get notifications from site using the new WebService.
 | 
			
		||||
     *
 | 
			
		||||
     * @param offset Position of the first notification to get.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with notifications and if can load more.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    async getPopupNotifications(
 | 
			
		||||
        offset: number,
 | 
			
		||||
        options?: AddonNotificationsGetNotificationsOptions,
 | 
			
		||||
    ): Promise<{notifications: AddonNotificationsPopupNotificationFormatted[]; canLoadMore: boolean}> {
 | 
			
		||||
        options = options || {};
 | 
			
		||||
        options.limit = options.limit || AddonNotificationsProvider.LIST_LIMIT;
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Get popup notifications from ${offset}. Limit: ${options.limit}`);
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.instance.getSite(options.siteId);
 | 
			
		||||
        const data: AddonNotificationsPopupGetPopupNotificationsWSParams = {
 | 
			
		||||
            useridto: site.getUserId(),
 | 
			
		||||
            newestfirst: true,
 | 
			
		||||
            offset,
 | 
			
		||||
            limit: options.limit + 1, // Get one more to calculate canLoadMore.
 | 
			
		||||
        };
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getNotificationsCacheKey(),
 | 
			
		||||
            ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Get notifications.
 | 
			
		||||
        const response = await site.read<AddonNotificationsGetPopupNotificationsResult>(
 | 
			
		||||
            'message_popup_get_popup_notifications',
 | 
			
		||||
            data,
 | 
			
		||||
            preSets,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const notifications = await this.formatNotificationsData(response.notifications.slice(0, options.limit));
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            canLoadMore: response.notifications.length > options.limit,
 | 
			
		||||
            notifications,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get read notifications from site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param offset Position of the first notification to get.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with notifications.
 | 
			
		||||
     */
 | 
			
		||||
    getReadNotifications(
 | 
			
		||||
        offset: number,
 | 
			
		||||
        options?: AddonNotificationsGetNotificationsOptions,
 | 
			
		||||
    ): Promise<AddonNotificationsGetMessagesMessageFormatted[]> {
 | 
			
		||||
        return this.getNotifications(true, offset, options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get unread notifications from site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param offset Position of the first notification to get.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with notifications.
 | 
			
		||||
     */
 | 
			
		||||
    getUnreadNotifications(
 | 
			
		||||
        offset: number,
 | 
			
		||||
        options?: AddonNotificationsGetNotificationsOptions,
 | 
			
		||||
    ): Promise<AddonNotificationsGetMessagesMessageFormatted[]> {
 | 
			
		||||
        return this.getNotifications(false, offset, options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get unread notifications count. Do not cache calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId The user id who received the notification. If not defined, use current user.
 | 
			
		||||
     * @param siteId Site ID. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved with the message notifications count.
 | 
			
		||||
     */
 | 
			
		||||
    async getUnreadNotificationsCount(userId?: number, siteId?: string): Promise<number> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        // @since 3.2
 | 
			
		||||
        if (site.wsAvailable('message_popup_get_unread_popup_notification_count')) {
 | 
			
		||||
            userId = userId || site.getUserId();
 | 
			
		||||
            const params: AddonNotificationsPopupGetUnreadPopupNotificationCountWSParams = {
 | 
			
		||||
                useridto: userId,
 | 
			
		||||
            };
 | 
			
		||||
            const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
                getFromCache: false,
 | 
			
		||||
                emergencyCache: false,
 | 
			
		||||
                saveToCache: false,
 | 
			
		||||
                typeExpected: 'number',
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                return await site.read<number>('message_popup_get_unread_popup_notification_count', params, preSets);
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Return no messages if the call fails.
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Fallback call
 | 
			
		||||
        try {
 | 
			
		||||
            const unread = await this.getUnreadNotifications(0, { limit: AddonNotificationsProvider.LIST_LIMIT, siteId });
 | 
			
		||||
 | 
			
		||||
            // The app used to add a + sign if needed, but 3.1 will be dropped soon so it's easier to always return a number.
 | 
			
		||||
            return unread.length;
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Return no messages if the call fails.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether or not popup WS is available for a certain site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with true if available, resolved with false or rejected otherwise.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    async isPopupAvailable(siteId?: string): Promise<boolean> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.wsAvailable('message_popup_get_popup_notifications');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark all message notification as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    async markAllNotificationsAsRead(siteId?: string): Promise<boolean> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const params: CoreMessageMarkAllNotificationsAsReadWSParams = {
 | 
			
		||||
            useridto: CoreSites.instance.getCurrentSiteUserId(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return site.write<boolean>('core_message_mark_all_notifications_as_read', params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark a single notification as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notificationId ID of notification to mark as read
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     * @since 3.5
 | 
			
		||||
     */
 | 
			
		||||
    async markNotificationRead(
 | 
			
		||||
        notificationId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreMessageMarkNotificationReadWSResponse | undefined> { // @todo | AddonMessagesMarkMessageReadResult
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        if (site.wsAvailable('core_message_mark_notification_read')) {
 | 
			
		||||
            const params: CoreMessageMarkNotificationReadWSParams = {
 | 
			
		||||
                notificationid: notificationId,
 | 
			
		||||
                timeread: CoreTimeUtils.instance.timestamp(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return site.write<CoreMessageMarkNotificationReadWSResponse>('core_message_mark_notification_read', params);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Fallback for versions prior to 3.5.
 | 
			
		||||
            // @todo return AddonMessageProvider.instance.markMessageRead(notificationId, site.id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate get notification preferences.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateNotificationPreferences(siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.invalidateWsCacheForKey(this.getNotificationPreferencesCacheKey());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidates notifications list WS calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the list is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateNotificationsList(siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.instance.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.invalidateWsCacheForKey(this.getNotificationsCacheKey());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether or not we can mark all notifications as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True if enabled, false otherwise.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    isMarkAllNotificationsAsReadEnabled(): boolean {
 | 
			
		||||
        return CoreSites.instance.wsAvailableInCurrentSite('core_message_mark_all_notifications_as_read');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether or not we can count unread notifications precisely.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True if enabled, false otherwise.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    isPreciseNotificationCountEnabled(): boolean {
 | 
			
		||||
        return CoreSites.instance.wsAvailableInCurrentSite('message_popup_get_unread_popup_notification_count');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether or not the notification preferences are enabled for the current site.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True if enabled, false otherwise.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    isNotificationPreferencesEnabled(): boolean {
 | 
			
		||||
        return CoreSites.instance.wsAvailableInCurrentSite('core_message_get_user_notification_preferences');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonNotifications extends makeSingleton(AddonNotificationsProvider) {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Preferences returned by core_message_get_user_notification_preferences.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferences = {
 | 
			
		||||
    userid: number; // User id.
 | 
			
		||||
    disableall: number | boolean; // Whether all the preferences are disabled.
 | 
			
		||||
    processors: AddonNotificationsPreferencesProcessor[]; // Config form values.
 | 
			
		||||
    components: AddonNotificationsPreferencesComponent[]; // Available components.
 | 
			
		||||
    enableall?: boolean; // Calculated in the app. Whether all the preferences are enabled.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Processor in notification preferences.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesProcessor = {
 | 
			
		||||
    displayname: string; // Display name.
 | 
			
		||||
    name: string; // Processor name.
 | 
			
		||||
    hassettings: boolean; // Whether has settings.
 | 
			
		||||
    contextid: number; // Context id.
 | 
			
		||||
    userconfigured: number; // Whether is configured by the user.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component in notification preferences.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesComponent = {
 | 
			
		||||
    displayname: string; // Display name.
 | 
			
		||||
    notifications: AddonNotificationsPreferencesNotification[]; // List of notificaitons for the component.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Notification processor in notification preferences component.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesNotification = {
 | 
			
		||||
    displayname: string; // Display name.
 | 
			
		||||
    preferencekey: string; // Preference key.
 | 
			
		||||
    processors: AddonNotificationsPreferencesNotificationProcessor[]; // Processors values for this notification.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Notification processor in notification preferences component.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesNotificationProcessor = {
 | 
			
		||||
    displayname: string; // Display name.
 | 
			
		||||
    name: string; // Processor name.
 | 
			
		||||
    locked: boolean; // Is locked by admin?.
 | 
			
		||||
    lockedmessage?: string; // @since 3.6. Text to display if locked.
 | 
			
		||||
    userconfigured: number; // Is configured?.
 | 
			
		||||
    loggedin: AddonNotificationsPreferencesNotificationProcessorState;
 | 
			
		||||
    loggedoff: AddonNotificationsPreferencesNotificationProcessorState;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * State in notification processor in notification preferences component.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPreferencesNotificationProcessorState = {
 | 
			
		||||
    name: string; // Name.
 | 
			
		||||
    displayname: string; // Display name.
 | 
			
		||||
    checked: boolean; // Is checked?.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_message_get_messages WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetMessagesWSParams = {
 | 
			
		||||
    useridto: number; // The user id who received the message, 0 for any user.
 | 
			
		||||
    useridfrom?: number; // The user id who send the message, 0 for any user. -10 or -20 for no-reply or support user.
 | 
			
		||||
    type?: string; // Type of message to return, expected values are: notifications, conversations and both.
 | 
			
		||||
    read?: boolean; // True for getting read messages, false for unread.
 | 
			
		||||
    newestfirst?: boolean; // True for ordering by newest first, false for oldest first.
 | 
			
		||||
    limitfrom?: number; // Limit from.
 | 
			
		||||
    limitnum?: number; // Limit number.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by core_message_get_messages WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetMessagesWSResponse = {
 | 
			
		||||
    messages: AddonNotificationsGetMessagesMessage[];
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Message data returned by core_message_get_messages.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetMessagesMessage = {
 | 
			
		||||
    id: number; // Message id.
 | 
			
		||||
    useridfrom: number; // User from id.
 | 
			
		||||
    useridto: number; // User to id.
 | 
			
		||||
    subject: string; // The message subject.
 | 
			
		||||
    text: string; // The message text formated.
 | 
			
		||||
    fullmessage: string; // The message.
 | 
			
		||||
    fullmessageformat: number; // Fullmessage format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | 
			
		||||
    fullmessagehtml: string; // The message in html.
 | 
			
		||||
    smallmessage: string; // The shorten message.
 | 
			
		||||
    notification: number; // Is a notification?.
 | 
			
		||||
    contexturl: string; // Context URL.
 | 
			
		||||
    contexturlname: string; // Context URL link name.
 | 
			
		||||
    timecreated: number; // Time created.
 | 
			
		||||
    timeread: number; // Time read.
 | 
			
		||||
    usertofullname: string; // User to full name.
 | 
			
		||||
    userfromfullname: string; // User from full name.
 | 
			
		||||
    component?: string; // @since 3.7. The component that generated the notification.
 | 
			
		||||
    eventtype?: string; // @since 3.7. The type of notification.
 | 
			
		||||
    customdata?: string; // @since 3.7. Custom data to be passed to the message processor.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Message data returned by core_message_get_messages with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetMessagesMessageFormatted =
 | 
			
		||||
        Omit<AddonNotificationsGetMessagesMessage, 'customdata'> & AddonNotificationsNotificationCalculatedData;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of message_popup_get_popup_notifications WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPopupGetPopupNotificationsWSParams = {
 | 
			
		||||
    useridto: number; // The user id who received the message, 0 for current user.
 | 
			
		||||
    newestfirst?: boolean; // True for ordering by newest first, false for oldest first.
 | 
			
		||||
    limit?: number; // The number of results to return.
 | 
			
		||||
    offset?: number; // Offset the result set by a given amount.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Result of WS message_popup_get_popup_notifications.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetPopupNotificationsResult = {
 | 
			
		||||
    notifications: AddonNotificationsPopupNotification[];
 | 
			
		||||
    unreadcount: number; // The number of unread message for the given user.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Notification returned by message_popup_get_popup_notifications.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPopupNotification = {
 | 
			
		||||
    id: number; // Notification id (this is not guaranteed to be unique within this result set).
 | 
			
		||||
    useridfrom: number; // User from id.
 | 
			
		||||
    useridto: number; // User to id.
 | 
			
		||||
    subject: string; // The notification subject.
 | 
			
		||||
    shortenedsubject: string; // The notification subject shortened with ellipsis.
 | 
			
		||||
    text: string; // The message text formated.
 | 
			
		||||
    fullmessage: string; // The message.
 | 
			
		||||
    fullmessageformat: number; // Fullmessage format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | 
			
		||||
    fullmessagehtml: string; // The message in html.
 | 
			
		||||
    smallmessage: string; // The shorten message.
 | 
			
		||||
    contexturl: string; // Context URL.
 | 
			
		||||
    contexturlname: string; // Context URL link name.
 | 
			
		||||
    timecreated: number; // Time created.
 | 
			
		||||
    timecreatedpretty: string; // Time created in a pretty format.
 | 
			
		||||
    timeread: number; // Time read.
 | 
			
		||||
    read: boolean; // Notification read status.
 | 
			
		||||
    deleted: boolean; // Notification deletion status.
 | 
			
		||||
    iconurl: string; // URL for notification icon.
 | 
			
		||||
    component?: string; // The component that generated the notification.
 | 
			
		||||
    eventtype?: string; // The type of notification.
 | 
			
		||||
    customdata?: string; // @since 3.7. Custom data to be passed to the message processor.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Notification returned by message_popup_get_popup_notifications.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPopupNotificationFormatted =
 | 
			
		||||
        Omit<AddonNotificationsPopupNotification, 'customdata'> & AddonNotificationsNotificationCalculatedData;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Any kind of notification that can be retrieved.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsAnyNotification =
 | 
			
		||||
        AddonNotificationsPopupNotificationFormatted | AddonNotificationsGetMessagesMessageFormatted;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Result of WS core_message_get_user_notification_preferences.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetUserNotificationPreferencesResult = {
 | 
			
		||||
    preferences: AddonNotificationsPreferences;
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculated data for messages returned by core_message_get_messages.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsNotificationCalculatedData = {
 | 
			
		||||
    mobiletext?: string; // Calculated in the app. Text to display for the notification.
 | 
			
		||||
    moodlecomponent?: string; // Calculated in the app. Moodle's component.
 | 
			
		||||
    notif?: number; // Calculated in the app. Whether it's a notification.
 | 
			
		||||
    notification?: number; // Calculated in the app in some cases. Whether it's a notification.
 | 
			
		||||
    read?: boolean; // Calculated in the app. Whether the notifications is read.
 | 
			
		||||
    courseid?: number; // Calculated in the app. Course the notification belongs to.
 | 
			
		||||
    profileimageurlfrom?: string; // Calculated in the app. Avatar of user that sent the notification.
 | 
			
		||||
    userfromfullname?: string; // Calculated in the app in some cases. User from full name.
 | 
			
		||||
    customdata?: Record<string, unknown>; // Parsed custom data.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of message_popup_get_unread_popup_notification_count WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsPopupGetUnreadPopupNotificationCountWSParams = {
 | 
			
		||||
    useridto: number; // The user id who received the message, 0 for any user.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_message_mark_all_notifications_as_read WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreMessageMarkAllNotificationsAsReadWSParams = {
 | 
			
		||||
    useridto: number; // The user id who received the message, 0 for any user.
 | 
			
		||||
    useridfrom?: number; // The user id who send the message, 0 for any user. -10 or -20 for no-reply or support user.
 | 
			
		||||
    timecreatedto?: number; // Mark messages created before this time as read, 0 for all messages.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_message_mark_notification_read WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreMessageMarkNotificationReadWSParams = {
 | 
			
		||||
    notificationid: number; // Id of the notification.
 | 
			
		||||
    timeread?: number; // Timestamp for when the notification should be marked read.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by core_message_mark_notification_read WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreMessageMarkNotificationReadWSResponse = {
 | 
			
		||||
    notificationid: number; // Id of the notification.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Options to pass to getNotifications and getPopupNotifications.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonNotificationsGetNotificationsOptions = CoreSitesCommonWSOptions & {
 | 
			
		||||
    limit?: number; // Number of notifications to get. Defaults to LIST_LIMIT.
 | 
			
		||||
};
 | 
			
		||||
@ -19,9 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonPrivateFilesIndexPage } from '.';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -38,8 +36,7 @@ const routes: Routes = [
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonPrivateFilesIndexPage,
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { AddonUserProfileFieldCheckboxHandler } from './services/handlers/checkbox';
 | 
			
		||||
import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate';
 | 
			
		||||
import { AddonUserProfileFieldCheckboxComponent } from './component/checkbox';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -33,7 +33,7 @@ import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -21,8 +21,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { AddonUserProfileFieldDatetimeHandler } from './services/handlers/datetime';
 | 
			
		||||
import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate';
 | 
			
		||||
import { AddonUserProfileFieldDatetimeComponent } from './component/datetime';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -34,8 +33,7 @@ import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -21,8 +21,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { AddonUserProfileFieldMenuHandler } from './services/handlers/menu';
 | 
			
		||||
import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate';
 | 
			
		||||
import { AddonUserProfileFieldMenuComponent } from './component/menu';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -34,8 +33,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -21,8 +21,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { AddonUserProfileFieldTextHandler } from './services/handlers/text';
 | 
			
		||||
import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate';
 | 
			
		||||
import { AddonUserProfileFieldTextComponent } from './component/text';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -34,8 +33,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -21,8 +21,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { AddonUserProfileFieldTextareaHandler } from './services/handlers/textarea';
 | 
			
		||||
import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate';
 | 
			
		||||
import { AddonUserProfileFieldTextareaComponent } from './component/textarea';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -35,8 +34,7 @@ import { CoreEditorComponentsModule } from '@features/editor/components/componen
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
 | 
			
		||||
@ -16,12 +16,11 @@ import { NgModule } from '@angular/core';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreBlockComponent } from './block/block';
 | 
			
		||||
import { CoreBlockOnlyTitleComponent } from './only-title-block/only-title-block';
 | 
			
		||||
import { CoreBlockPreRenderedComponent } from './pre-rendered-block/pre-rendered-block';
 | 
			
		||||
import { CoreBlockCourseBlocksComponent } from './course-blocks/course-blocks';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -33,9 +32,8 @@ import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
    imports: [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        CoreBlockComponent,
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreContentLinksChooseSitePage } from './choose-site';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -40,8 +38,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreContentLinksChooseSitePageModule {}
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesCourseListItemComponent } from './course-list-item/course-list-item';
 | 
			
		||||
import { CoreCoursesCourseProgressComponent } from './course-progress/course-progress';
 | 
			
		||||
import { CoreCoursesCourseOptionsMenuComponent } from './course-options-menu/course-options-menu';
 | 
			
		||||
@ -39,9 +36,7 @@ import { CoreCoursesSelfEnrolPasswordComponent } from './self-enrol-password/sel
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        CoreCoursesCourseListItemComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
import { CoreCoursesAvailableCoursesPage } from './available-courses';
 | 
			
		||||
@ -38,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
import { CoreCoursesCategoriesPage } from './categories';
 | 
			
		||||
@ -38,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesCoursePreviewPage } from './course-preview';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
@ -38,9 +35,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreBlockComponentsModule } from '@features/block/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { CoreCoursesDashboardPage } from './dashboard';
 | 
			
		||||
@ -37,8 +36,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreBlockComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesMyCoursesPage } from './my-courses';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
 | 
			
		||||
 | 
			
		||||
@ -39,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
 | 
			
		||||
import { CoreSearchComponentsModule } from '@features/search/components/components.module';
 | 
			
		||||
 | 
			
		||||
@ -38,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCoursesComponentsModule,
 | 
			
		||||
        CoreSearchComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreEditorRichTextEditorComponent } from './rich-text-editor/rich-text-editor';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -28,7 +28,7 @@ import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -17,9 +17,7 @@ import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreEmulatorCaptureMediaComponent } from './capture-media/capture-media';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -30,9 +28,7 @@ import { CoreEmulatorCaptureMediaComponent } from './capture-media/capture-media
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule.forRoot(),
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        CoreEmulatorCaptureMediaComponent,
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import { CoreTagModule } from './tag/tag.module';
 | 
			
		||||
import { CoreUserModule } from './user/user.module';
 | 
			
		||||
import { CorePushNotificationsModule } from './pushnotifications/pushnotifications.module';
 | 
			
		||||
import { CoreXAPIModule } from './xapi/xapi.module';
 | 
			
		||||
import { CoreViewerModule } from './viewer/viewer.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -43,6 +44,7 @@ import { CoreXAPIModule } from './xapi/xapi.module';
 | 
			
		||||
        CorePushNotificationsModule,
 | 
			
		||||
        CoreXAPIModule,
 | 
			
		||||
        CoreH5PModule,
 | 
			
		||||
        CoreViewerModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreFeaturesModule {}
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,7 @@ import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreH5PPlayerComponent } from './h5p-player/h5p-player';
 | 
			
		||||
import { CoreH5PIframeComponent } from './h5p-iframe/h5p-iframe';
 | 
			
		||||
 | 
			
		||||
@ -30,9 +29,8 @@ import { CoreH5PIframeComponent } from './h5p-iframe/h5p-iframe';
 | 
			
		||||
    imports: [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginSiteHelpComponent } from './components/site-help/site-help';
 | 
			
		||||
import { CoreLoginSiteOnboardingComponent } from './components/site-onboarding/site-onboarding';
 | 
			
		||||
 | 
			
		||||
@ -74,8 +73,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginChangePasswordPage } from './change-password';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -34,7 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginChangePasswordPage,
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginCredentialsPage } from './credentials';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -39,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginCredentialsPage,
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreUserComponentsModule } from '@features/user/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { CoreLoginEmailSignupPage } from './email-signup';
 | 
			
		||||
@ -40,8 +39,7 @@ const routes: Routes = [
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreUserComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginForgottenPasswordPage } from './forgotten-password';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -37,7 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginForgottenPasswordPage,
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginReconnectPage } from './reconnect';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -39,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginReconnectPage,
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginSitePolicyPage } from './site-policy';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -35,8 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginSitePolicyPage,
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginSitePage } from './site';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -39,8 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginSitePage,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreLoginSitesPage } from './sites';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -36,8 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreLoginSitesPage,
 | 
			
		||||
 | 
			
		||||
@ -15,17 +15,16 @@
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { Injector, NgModule } from '@angular/core';
 | 
			
		||||
import { ROUTES, Routes } from '@angular/router';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
import { resolveModuleRoutes } from '@/app/app-routing.module';
 | 
			
		||||
 | 
			
		||||
import { MAIN_MENU_ROUTES } from './mainmenu-routing.module';
 | 
			
		||||
import { CoreMainMenuPage } from './pages/menu/menu';
 | 
			
		||||
import { CoreMainMenuHomeHandlerService } from './services/handlers/mainmenu';
 | 
			
		||||
import { CoreMainMenuProvider } from './services/mainmenu';
 | 
			
		||||
 | 
			
		||||
function buildRoutes(injector: Injector): Routes {
 | 
			
		||||
    const routes = resolveModuleRoutes(injector, MAIN_MENU_ROUTES);
 | 
			
		||||
@ -45,7 +44,7 @@ function buildRoutes(injector: Injector): Routes {
 | 
			
		||||
                    loadChildren: () => import('./pages/home/home.module').then(m => m.CoreMainMenuHomePageModule),
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    path: 'more',
 | 
			
		||||
                    path: CoreMainMenuProvider.MORE_PAGE_NAME,
 | 
			
		||||
                    loadChildren: () => import('./pages/more/more.module').then(m => m.CoreMainMenuMorePageModule),
 | 
			
		||||
                },
 | 
			
		||||
                ...routes.children,
 | 
			
		||||
@ -60,9 +59,7 @@ function buildRoutes(injector: Injector): Routes {
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreMainMenuPage,
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { resolveModuleRoutes } from '@/app/app-routing.module';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
 | 
			
		||||
import { CoreMainMenuHomePage } from './home';
 | 
			
		||||
import { MAIN_MENU_HOME_ROUTES } from './home-routing.module';
 | 
			
		||||
@ -44,8 +43,7 @@ function buildRoutes(injector: Injector): Routes {
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        { provide: ROUTES, multi: true, useFactory: buildRoutes, deps: [Injector] },
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
            <ion-badge *ngIf="tab.badge">{{ tab.badge }}</ion-badge>
 | 
			
		||||
        </ion-tab-button>
 | 
			
		||||
 | 
			
		||||
        <ion-tab-button (ionTabButtonClick)="tabClicked($event, 'more')" [hidden]="!loaded" tab="more" layout="label-hide">
 | 
			
		||||
        <ion-tab-button (ionTabButtonClick)="tabClicked($event, morePageName)" [hidden]="!loaded" [tab]="morePageName" layout="label-hide">
 | 
			
		||||
            <ion-icon name="fas-bars"></ion-icon>
 | 
			
		||||
            <ion-label>{{ 'core.more' | translate }}</ion-label>
 | 
			
		||||
        </ion-tab-button>
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreEvents, CoreEventObserver } from '@singletons/events';
 | 
			
		||||
import { CoreMainMenu } from '../../services/mainmenu';
 | 
			
		||||
import { CoreMainMenu, CoreMainMenuProvider } from '../../services/mainmenu';
 | 
			
		||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/mainmenu-delegate';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
@ -43,6 +43,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
 | 
			
		||||
    showTabs = false;
 | 
			
		||||
    tabsPlacement = 'bottom';
 | 
			
		||||
    hidden = false;
 | 
			
		||||
    morePageName = CoreMainMenuProvider.MORE_PAGE_NAME;
 | 
			
		||||
 | 
			
		||||
    protected subscription?: Subscription;
 | 
			
		||||
    protected redirectObs?: CoreEventObserver;
 | 
			
		||||
@ -152,7 +153,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
 | 
			
		||||
            if (this.loaded && this.mainTabs && !this.mainTabs.getSelected()) {
 | 
			
		||||
                // Select the first tab.
 | 
			
		||||
                setTimeout(() => {
 | 
			
		||||
                    this.mainTabs!.select(this.tabs[0]?.page || 'more');
 | 
			
		||||
                    this.mainTabs!.select(this.tabs[0]?.page || this.morePageName);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-list>
 | 
			
		||||
        <ion-item button *ngIf="siteInfo" class="ion-text-wrap" core-user-link [userId]="siteInfo.userid">
 | 
			
		||||
                <core-user-avatar [user]="siteInfo" slot="start"></core-user-avatar>
 | 
			
		||||
            <core-user-avatar [user]="siteInfo" slot="start"></core-user-avatar>
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2>{{siteInfo.fullname}}</h2>
 | 
			
		||||
                <p>
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ import { RouterModule, ROUTES } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreMainMenuMorePage } from './more';
 | 
			
		||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
 | 
			
		||||
@ -28,8 +27,7 @@ import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.modu
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ export class CoreMainMenuProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly NUM_MAIN_HANDLERS = 4;
 | 
			
		||||
    static readonly ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen.
 | 
			
		||||
    static readonly MORE_PAGE_NAME = 'more';
 | 
			
		||||
 | 
			
		||||
    protected tablet = false;
 | 
			
		||||
 | 
			
		||||
@ -191,11 +192,24 @@ export class CoreMainMenuProvider {
 | 
			
		||||
        return tablet ? 'side' : 'bottom';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a certain page is the root of a main menu tab.
 | 
			
		||||
     *
 | 
			
		||||
     * @param page Name of the page.
 | 
			
		||||
     * @return Promise resolved with boolean: whether it's the root of a main menu tab.
 | 
			
		||||
     */
 | 
			
		||||
    async isMainMenuTab(pageName: string): Promise<boolean> {
 | 
			
		||||
        if (pageName == CoreMainMenuProvider.MORE_PAGE_NAME) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.isCurrentMainMenuHandler(pageName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a certain page is the root of a main menu handler currently displayed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param page Name of the page.
 | 
			
		||||
     * @param pageParams Page params.
 | 
			
		||||
     * @return Promise resolved with boolean: whether it's the root of a main menu handler.
 | 
			
		||||
     */
 | 
			
		||||
    async isCurrentMainMenuHandler(pageName: string): Promise<boolean> {
 | 
			
		||||
 | 
			
		||||
@ -149,7 +149,7 @@ export class CorePushNotificationsDelegateService {
 | 
			
		||||
     * @param eventName Only receive is permitted.
 | 
			
		||||
     * @return Observer to subscribe.
 | 
			
		||||
     */
 | 
			
		||||
    on<T = unknown>(eventName: string): Subject<T> {
 | 
			
		||||
    on<T = CorePushNotificationsNotificationBasicData>(eventName: string): Subject<T> {
 | 
			
		||||
        if (typeof this.observables[eventName] == 'undefined') {
 | 
			
		||||
            const eventNames = Object.keys(this.observables).join(', ');
 | 
			
		||||
            this.logger.warn(`'${eventName}' event name is not allowed. Use one of the following: '${eventNames}'.`);
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSearchBoxComponent } from './search-box/search-box';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,8 +31,7 @@ import { CoreSearchBoxComponent } from './search-box/search-box';
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        CoreSearchBoxComponent,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSettingsAboutPage } from './about';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -48,8 +46,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSettingsAboutPage,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSettingsAppPage } from './app';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -36,8 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSettingsAppPage,
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSettingsDeviceInfoPage } from './deviceinfo';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -37,9 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSettingsDeviceInfoPage,
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,7 @@ import { IonicModule } from '@ionic/angular';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { FormsModule } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSettingsGeneralPage } from './general';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -38,8 +36,7 @@ const routes: Routes = [
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSettingsGeneralPage,
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSettingsLicensesPage } from './licenses';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -36,8 +34,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSettingsLicensesPage,
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { IonicModule } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreComponentsModule } from '@components/components.module';
 | 
			
		||||
import { CoreDirectivesModule } from '@directives/directives.module';
 | 
			
		||||
import { CorePipesModule } from '@pipes/pipes.module';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSitePreferencesPage } from './site';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@ -40,9 +37,7 @@ const routes: Routes = [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        IonicModule,
 | 
			
		||||
        TranslateModule.forChild(),
 | 
			
		||||
        CoreComponentsModule,
 | 
			
		||||
        CoreDirectivesModule,
 | 
			
		||||
        CorePipesModule,
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreSitePreferencesPageModule {}
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../services/settings-
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSiteInfo } from '@classes/site';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the list of site settings pages.
 | 
			
		||||
@ -182,7 +183,7 @@ export class CoreSitePreferencesPage implements OnInit, OnDestroy {
 | 
			
		||||
    openHandler(page: string, params?: Params): void {
 | 
			
		||||
        this.selectedPage = page;
 | 
			
		||||
        // this.splitviewCtrl.push(page, params);
 | 
			
		||||
        this.router.navigate([page], { relativeTo: this.route, queryParams: params });
 | 
			
		||||
        CoreNavigator.instance.navigateToSitePath(page, { params });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user