diff --git a/src/addons/calendar/services/calendar-helper.ts b/src/addons/calendar/services/calendar-helper.ts index 593637898..80d70451f 100644 --- a/src/addons/calendar/services/calendar-helper.ts +++ b/src/addons/calendar/services/calendar-helper.ts @@ -546,12 +546,12 @@ export class AddonCalendarHelperProvider { const site = await CoreSites.getSite(siteId); const fetchTimestarts: number[] = []; const invalidateTimestarts: number[] = []; - const promises: Promise[] = []; + let promises: Promise[] = []; // Always fetch upcoming events. promises.push(AddonCalendar.getUpcomingEvents(undefined, undefined, true, site.id)); - promises.concat(events.map(async (eventData) => { + promises = promises.concat(events.map(async (eventData) => { if (eventData.repeated <= 1) { // Not repeated. diff --git a/src/addons/messages/services/messages.ts b/src/addons/messages/services/messages.ts index 5fd53a9c6..dd2ae0076 100644 --- a/src/addons/messages/services/messages.ts +++ b/src/addons/messages/services/messages.ts @@ -31,6 +31,7 @@ import { CoreWSExternalWarning } from '@services/ws'; import { makeSingleton } from '@singletons'; import { CoreError } from '@classes/errors/error'; import { AddonMessagesSyncEvents, AddonMessagesSyncProvider } from './messages-sync'; +import { CoreWSError } from '@classes/errors/wserror'; const ROOT_CACHE_KEY = 'mmaMessages:'; @@ -191,7 +192,14 @@ export class AddonMessagesProvider { requesteduserid: userId, }; - await site.write('core_message_create_contact_request', params); + const result = await site.write( + 'core_message_create_contact_request', + params, + ); + + if (result.warnings?.length) { + throw new CoreWSError(result.warnings[0]); + } } await this.invalidateAllMemberInfo(userId, site).finally(() => { @@ -3483,6 +3491,19 @@ type AddonMessagesConfirmContactRequestWSParams = { */ type AddonMessagesCreateContactRequestWSParams = AddonMessagesConfirmContactRequestWSParams; +/** + * Data returned by core_message_create_contact_request WS. + */ +export type AddonMessagesCreateContactRequestWSResponse = { + request?: { + id: number; // Message id. + userid: number; // User from id. + requesteduserid: number; // User to id. + timecreated: number; // Time created. + }; // Request record. + warnings?: CoreWSExternalWarning[]; +}; + /** * Params of core_message_decline_contact_request WS. */ diff --git a/src/addons/mod/forum/services/handlers/prefetch.ts b/src/addons/mod/forum/services/handlers/prefetch.ts index a9c55d2e9..79e1bf33b 100644 --- a/src/addons/mod/forum/services/handlers/prefetch.ts +++ b/src/addons/mod/forum/services/handlers/prefetch.ts @@ -48,13 +48,13 @@ export class AddonModForumPrefetchHandlerService extends CoreCourseActivityPrefe try { const forum = await AddonModForum.getForum(courseId, module.id); - const files = this.getIntroFilesFromInstance(module, forum); + let files = this.getIntroFilesFromInstance(module, forum); // Get posts. const posts = await this.getPostsForPrefetch(forum, { cmId: module.id }); // Add posts attachments and embedded files. - files.concat(this.getPostsFiles(posts)); + files = files.concat(this.getPostsFiles(posts)); return files; } catch (error) { diff --git a/src/addons/mod/glossary/components/index/index.ts b/src/addons/mod/glossary/components/index/index.ts index 03be8f63b..d79b755ea 100644 --- a/src/addons/mod/glossary/components/index/index.ts +++ b/src/addons/mod/glossary/components/index/index.ts @@ -573,7 +573,6 @@ class AddonModGlossaryEntriesManager extends CorePageItemsListManager */ setOnlineEntries(onlineEntries: AddonModGlossaryEntry[], hasMoreItems: boolean = false): void { this.setItems(( this.offlineEntries).concat(onlineEntries), hasMoreItems); - this.onlineEntries.concat(onlineEntries); } /** @@ -583,7 +582,6 @@ class AddonModGlossaryEntriesManager extends CorePageItemsListManager */ setOfflineEntries(offlineEntries: AddonModGlossaryOfflineEntry[]): void { this.setItems(( offlineEntries).concat(this.onlineEntries), this.hasMoreItems); - this.offlineEntries = offlineEntries; } /** diff --git a/src/addons/notes/components/add/add-modal.html b/src/addons/notes/components/add/add-modal.html index c3fe269d6..843df9053 100644 --- a/src/addons/notes/components/add/add-modal.html +++ b/src/addons/notes/components/add/add-modal.html @@ -19,11 +19,10 @@ - - - - + + +
diff --git a/src/addons/notes/services/notes-sync.ts b/src/addons/notes/services/notes-sync.ts index 2df96b2ce..a9dfac04d 100644 --- a/src/addons/notes/services/notes-sync.ts +++ b/src/addons/notes/services/notes-sync.ts @@ -62,12 +62,10 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider { - const courseIds = notes.map((note) => note.courseid); - - courseIds.concat(courseIds); - }, []); + courseIds = courseIds.concat(notes.map((note) => note.courseid)); + }); CoreUtils.uniqueArray(courseIds); diff --git a/src/addons/notifications/notifications.module.ts b/src/addons/notifications/notifications.module.ts index c81f9813b..86595e7c3 100644 --- a/src/addons/notifications/notifications.module.ts +++ b/src/addons/notifications/notifications.module.ts @@ -64,6 +64,8 @@ const preferencesRoutes: Routes = [ CoreCronDelegate.register(AddonNotificationsCronHandler.instance); CorePushNotificationsDelegate.registerClickHandler(AddonNotificationsPushClickHandler.instance); CoreSettingsDelegate.registerHandler(AddonNotificationsSettingsHandler.instance); + + AddonNotificationsMainMenuHandler.initialize(); }, }, ], diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bc4d2881f..7457bda23 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -132,16 +132,21 @@ export class AppComponent implements OnInit, AfterViewInit { // Check InAppBrowser closed. CoreEvents.on(CoreEvents.IAB_EXIT, () => { - CoreLoginHelper.setWaitingForBrowser(false); this.lastInAppUrl = ''; - CoreLoginHelper.checkLogout(); + + if (CoreLoginHelper.isWaitingForBrowser()) { + CoreLoginHelper.setWaitingForBrowser(false); + CoreLoginHelper.checkLogout(); + } }); Platform.resume.subscribe(() => { // Wait a second before setting it to false since in iOS there could be some frozen WS calls. setTimeout(() => { - CoreLoginHelper.setWaitingForBrowser(false); - CoreLoginHelper.checkLogout(); + if (CoreLoginHelper.isWaitingForBrowser()) { + CoreLoginHelper.setWaitingForBrowser(false); + CoreLoginHelper.checkLogout(); + } }, 1000); }); diff --git a/src/core/classes/tabs.ts b/src/core/classes/tabs.ts index 92f76b076..7182606c4 100644 --- a/src/core/classes/tabs.ts +++ b/src/core/classes/tabs.ts @@ -247,7 +247,7 @@ export class CoreTabsBaseComponent implements OnInit, Aft await this.calculateMaxSlides(); - this.updateSlides(); + await this.updateSlides(); } /** @@ -378,9 +378,14 @@ export class CoreTabsBaseComponent implements OnInit, Aft } this.maxSlides = 3; - const width = this.slidesSwiper.width; + let width = this.slidesSwiper.width; if (!width) { - return; + this.slidesSwiper.updateSize(); + width = this.slidesSwiper.width; + + if (!width) { + return; + } } const zoomLevel = await CoreSettingsHelper.getZoom(); @@ -619,7 +624,7 @@ export class CoreTabsBaseComponent implements OnInit, Aft protected windowResized(): void { setTimeout(() => { this.calculateSlides(); - }); + }, 200); } /** diff --git a/src/core/components/tabs-outlet/core-tabs-outlet.html b/src/core/components/tabs-outlet/core-tabs-outlet.html index 0c5ce4ea6..74747fbe7 100644 --- a/src/core/components/tabs-outlet/core-tabs-outlet.html +++ b/src/core/components/tabs-outlet/core-tabs-outlet.html @@ -11,7 +11,6 @@ { // Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually. - const linkDir = new CoreLinkDirective(new ElementRef(anchor), this.content); + const linkDir = new CoreLinkDirective(new ElementRef(anchor), this.content, this.sanitizer); linkDir.capture = this.captureLinks ?? true; linkDir.inApp = this.openLinksInApp; linkDir.ngOnInit(); diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 49d296352..94aae1bf7 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { Directive, Input, OnInit, ElementRef, Optional, SecurityContext } from '@angular/core'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; import { CoreFileHelper } from '@services/file-helper'; @@ -33,7 +34,7 @@ import { CoreCustomURLSchemes } from '@services/urlschemes'; }) export class CoreLinkDirective implements OnInit { - @Input() href?: string; // Link URL. + @Input() href?: string | SafeUrl; // Link URL. @Input() capture?: boolean | string; // If the link needs to be captured by the app. @Input() inApp?: boolean | string; // True to open in embedded browser, false to open in system browser. /* Whether the link should be opened with auto-login. Accepts the following values: @@ -47,6 +48,7 @@ export class CoreLinkDirective implements OnInit { constructor( element: ElementRef, @Optional() protected content: IonContent, + protected sanitizer: DomSanitizer, ) { this.element = element.nativeElement; } @@ -91,7 +93,13 @@ export class CoreLinkDirective implements OnInit { return; // Link already treated, stop. } - let href = this.href || this.element.getAttribute('href') || this.element.getAttribute('xlink:href'); + let href: string | null = null; + if (this.href) { + // Convert the URL back to string if needed. + href = typeof this.href === 'string' ? this.href : this.sanitizer.sanitize(SecurityContext.URL, this.href); + } + + href = href || this.element.getAttribute('href') || this.element.getAttribute('xlink:href'); if (!href || CoreUrlUtils.getUrlScheme(href) == 'javascript') { return; diff --git a/src/core/features/mainmenu/mainmenu-tab-routing.module.ts b/src/core/features/mainmenu/mainmenu-tab-routing.module.ts index 3a4c0ddb0..ca9c0538c 100644 --- a/src/core/features/mainmenu/mainmenu-tab-routing.module.ts +++ b/src/core/features/mainmenu/mainmenu-tab-routing.module.ts @@ -24,7 +24,7 @@ export function buildTabMainRoutes(injector: Injector, mainRoute: Route): Routes mainRoute.path = mainRoute.path || ''; mainRoute.children = mainRoute.children || []; - mainRoute.children.concat(routes.children); + mainRoute.children = mainRoute.children.concat(routes.children); return [ mainRoute, diff --git a/src/core/features/sharedfiles/components/list-modal/list-modal.html b/src/core/features/sharedfiles/components/list-modal/list-modal.html index f022309dd..602b672ac 100644 --- a/src/core/features/sharedfiles/components/list-modal/list-modal.html +++ b/src/core/features/sharedfiles/components/list-modal/list-modal.html @@ -14,6 +14,7 @@ + [path]="path" [showSitePicker]="!hideSitePicker" (onPathChanged)="calculateTitle($event)" + (onFilePicked)="filePicked($event)"> diff --git a/src/core/features/sharedfiles/components/list-modal/list-modal.ts b/src/core/features/sharedfiles/components/list-modal/list-modal.ts index f3ba2f873..9a38a9c75 100644 --- a/src/core/features/sharedfiles/components/list-modal/list-modal.ts +++ b/src/core/features/sharedfiles/components/list-modal/list-modal.ts @@ -32,7 +32,7 @@ export class CoreSharedFilesListModalComponent implements OnInit { @Input() manage?: boolean; @Input() pick?: boolean; // To pick a file you MUST use a modal. @Input() path?: string; - @Input() showSitePicker?: boolean; + @Input() hideSitePicker?: boolean; title?: string; diff --git a/src/core/features/tag/components/list/list.ts b/src/core/features/tag/components/list/list.ts index 286d8916c..561db82bf 100644 --- a/src/core/features/tag/components/list/list.ts +++ b/src/core/features/tag/components/list/list.ts @@ -40,7 +40,7 @@ export class CoreTagListComponent { fromContextId: tag.taginstancecontextid, }; - CoreNavigator.navigateToSitePath('/tag/index', { params, preferCurrentTab: false }); + CoreNavigator.navigateToSitePath('/tag/index', { params }); } } diff --git a/src/core/features/user/pages/about/about.html b/src/core/features/user/pages/about/about.html index 463fc451d..41072034b 100644 --- a/src/core/features/user/pages/about/about.html +++ b/src/core/features/user/pages/about/about.html @@ -38,21 +38,21 @@

- +

{{ 'core.user.address' | translate}}

- {{ user.address }} + {{ formattedAddress }}

- +

{{ 'core.user.city' | translate}}

{{ user.city }}

- +

{{ 'core.user.country' | translate}}

{{ user.country }}

diff --git a/src/core/features/user/pages/about/about.page.ts b/src/core/features/user/pages/about/about.page.ts index 1999862e4..2c3e5808f 100644 --- a/src/core/features/user/pages/about/about.page.ts +++ b/src/core/features/user/pages/about/about.page.ts @@ -75,7 +75,7 @@ export class CoreUserAboutPage implements OnInit { if (user.address) { this.formattedAddress = CoreUserHelper.formatAddress(user.address, user.city, user.country); - this.encodedAddress = CoreTextUtils.buildAddressURL(user.address); + this.encodedAddress = CoreTextUtils.buildAddressURL(this.formattedAddress); } this.hasContact = !!(user.email || user.phone1 || user.phone2 || user.city || user.country || user.address);