Merge pull request #4168 from dpalou/MOBILE-4359

Mobile 4359
main
Pau Ferrer Ocaña 2024-09-05 13:30:26 +02:00 committed by GitHub
commit d7c3c37b21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 191 additions and 96 deletions

48
package-lock.json generated
View File

@ -47,7 +47,6 @@
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3", "@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
"@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3", "@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3",
"@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.4", "@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.4",
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.12",
"@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5", "@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5",
"@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3", "@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3",
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1", "@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",
@ -72,6 +71,7 @@
"cordova-plugin-file": "^8.0.1", "cordova-plugin-file": "^8.0.1",
"cordova-plugin-geolocation": "^5.0.0", "cordova-plugin-geolocation": "^5.0.0",
"cordova-plugin-ionic-keyboard": "^2.2.0", "cordova-plugin-ionic-keyboard": "^2.2.0",
"cordova-plugin-local-notification": "1.0.0",
"cordova-plugin-media-capture": "^5.0.0", "cordova-plugin-media-capture": "^5.0.0",
"cordova-plugin-network-information": "^3.0.0", "cordova-plugin-network-information": "^3.0.0",
"cordova-plugin-prevent-override": "^1.0.1", "cordova-plugin-prevent-override": "^1.0.1",
@ -5408,33 +5408,6 @@
} }
} }
}, },
"node_modules/@moodlehq/cordova-plugin-local-notification": {
"version": "0.9.0-moodle.12",
"resolved": "https://registry.npmjs.org/@moodlehq/cordova-plugin-local-notification/-/cordova-plugin-local-notification-0.9.0-moodle.12.tgz",
"integrity": "sha512-gt6BhqsltCnNmk/CRUIDxTha/c1/UGTsh2d15zoUVeGaKYqE6olmIyN/HCAn+ofy7CA6DNHOdm1v0uqC3YbPZg==",
"engines": [
{
"name": "cordova",
"version": ">=3.6.0"
},
{
"name": "cordova-android",
"version": ">=6.0.0"
},
{
"name": "cordova-windows",
"version": ">=4.2.0"
},
{
"name": "android-sdk",
"version": ">=26"
},
{
"name": "apple-ios",
"version": ">=10.0.0"
}
]
},
"node_modules/@moodlehq/cordova-plugin-qrscanner": { "node_modules/@moodlehq/cordova-plugin-qrscanner": {
"version": "3.0.1-moodle.5", "version": "3.0.1-moodle.5",
"resolved": "https://registry.npmjs.org/@moodlehq/cordova-plugin-qrscanner/-/cordova-plugin-qrscanner-3.0.1-moodle.5.tgz", "resolved": "https://registry.npmjs.org/@moodlehq/cordova-plugin-qrscanner/-/cordova-plugin-qrscanner-3.0.1-moodle.5.tgz",
@ -11263,6 +11236,25 @@
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.2.0.tgz", "resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.2.0.tgz",
"integrity": "sha512-yDUG+9ieKVRitq5mGlNxjaZh/MgEhFFIgTIPhqSbUaQ8UuZbawy5mhJAVClqY97q8/rcQtL6dCDa7x2sEtCLcA==" "integrity": "sha512-yDUG+9ieKVRitq5mGlNxjaZh/MgEhFFIgTIPhqSbUaQ8UuZbawy5mhJAVClqY97q8/rcQtL6dCDa7x2sEtCLcA=="
}, },
"node_modules/cordova-plugin-local-notification": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/cordova-plugin-local-notification/-/cordova-plugin-local-notification-1.0.0.tgz",
"integrity": "sha512-dhw00d38cRo/6JlDR7i4u4u+In7wkzH5ffDnIZVzverct9XLcDyxs6WnPUGmWrpUjK6CpXrn3aGmt5kwkck+8g==",
"engines": {
"cordovaDependencies": {
"0.9.0-beta.3": {
"cordova": ">=3.6.0",
"cordova-android": ">=6.0.0",
"cordova-ios": ">=10.0.0"
},
"1.0.0": {
"cordova": ">=12.0.0",
"cordova-android": ">=13.0.0",
"cordova-ios": ">=10.0.0"
}
}
}
},
"node_modules/cordova-plugin-media-capture": { "node_modules/cordova-plugin-media-capture": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/cordova-plugin-media-capture/-/cordova-plugin-media-capture-5.0.0.tgz", "resolved": "https://registry.npmjs.org/cordova-plugin-media-capture/-/cordova-plugin-media-capture-5.0.0.tgz",

View File

@ -81,7 +81,6 @@
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3", "@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
"@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3", "@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3",
"@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.4", "@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.4",
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.12",
"@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5", "@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5",
"@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3", "@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3",
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1", "@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",
@ -106,6 +105,7 @@
"cordova-plugin-file": "^8.0.1", "cordova-plugin-file": "^8.0.1",
"cordova-plugin-geolocation": "^5.0.0", "cordova-plugin-geolocation": "^5.0.0",
"cordova-plugin-ionic-keyboard": "^2.2.0", "cordova-plugin-ionic-keyboard": "^2.2.0",
"cordova-plugin-local-notification": "1.0.0",
"cordova-plugin-media-capture": "^5.0.0", "cordova-plugin-media-capture": "^5.0.0",
"cordova-plugin-network-information": "^3.0.0", "cordova-plugin-network-information": "^3.0.0",
"cordova-plugin-prevent-override": "^1.0.1", "cordova-plugin-prevent-override": "^1.0.1",
@ -201,9 +201,6 @@
"@moodlehq/cordova-plugin-inappbrowser": {}, "@moodlehq/cordova-plugin-inappbrowser": {},
"@moodlehq/cordova-plugin-intent": {}, "@moodlehq/cordova-plugin-intent": {},
"@moodlehq/cordova-plugin-ionic-webview": {}, "@moodlehq/cordova-plugin-ionic-webview": {},
"@moodlehq/cordova-plugin-local-notification": {
"ANDROID_SUPPORT_V4_VERSION": "26.+"
},
"@moodlehq/cordova-plugin-qrscanner": {}, "@moodlehq/cordova-plugin-qrscanner": {},
"@moodlehq/cordova-plugin-statusbar": {}, "@moodlehq/cordova-plugin-statusbar": {},
"@moodlehq/cordova-plugin-zip": {}, "@moodlehq/cordova-plugin-zip": {},
@ -227,6 +224,9 @@
"GPS_REQUIRED": "false" "GPS_REQUIRED": "false"
}, },
"cordova-plugin-ionic-keyboard": {}, "cordova-plugin-ionic-keyboard": {},
"cordova-plugin-local-notification": {
"ANDROID_SUPPORT_V4_VERSION": "26.+"
},
"cordova-plugin-media-capture": {}, "cordova-plugin-media-capture": {},
"cordova-plugin-moodleapp": {}, "cordova-plugin-moodleapp": {},
"cordova-plugin-network-information": {}, "cordova-plugin-network-information": {},

View File

@ -29,7 +29,6 @@ import { AddonBlogOffline, AddonBlogOfflineEntry } from '@addons/blog/services/b
import { AddonBlogSync } from '@addons/blog/services/blog-sync'; import { AddonBlogSync } from '@addons/blog/services/blog-sync';
import { Component, computed, OnDestroy, OnInit, signal } from '@angular/core'; import { Component, computed, OnDestroy, OnInit, signal } from '@angular/core';
import { CoreComments } from '@features/comments/services/comments'; import { CoreComments } from '@features/comments/services/comments';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTag } from '@features/tag/services/tag'; import { CoreTag } from '@features/tag/services/tag';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
@ -202,8 +201,7 @@ export class AddonBlogIndexPage implements OnInit, OnDestroy {
this.commentsEnabled = CoreComments.areCommentsEnabledInSite(); this.commentsEnabled = CoreComments.areCommentsEnabledInSite();
this.tagsEnabled = CoreTag.areTagsAvailableInSite(); this.tagsEnabled = CoreTag.areTagsAvailableInSite();
const deepLinkManager = new CoreMainMenuDeepLinkManager(); CoreSites.loginNavigationFinished();
deepLinkManager.treatLink();
await this.fetchEntries(false, false, true); await this.fetchEntries(false, false, true);
this.optionsAvailable = await AddonBlog.isEditingEnabled(); this.optionsAvailable = await AddonBlog.isEditingEnabled();

View File

@ -30,7 +30,6 @@ import { AddonCalendarCalendarComponent } from '../../components/calendar/calend
import { AddonCalendarUpcomingEventsComponent } from '../../components/upcoming-events/upcoming-events'; import { AddonCalendarUpcomingEventsComponent } from '../../components/upcoming-events/upcoming-events';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreModals } from '@services/modals'; import { CoreModals } from '@services/modals';
/** /**
@ -178,8 +177,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy {
} }
}); });
const deepLinkManager = new CoreMainMenuDeepLinkManager(); CoreSites.loginNavigationFinished();
deepLinkManager.treatLink();
} }
/** /**

View File

@ -30,7 +30,6 @@ import { Subscription } from 'rxjs';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreKeyboard } from '@singletons/keyboard'; import { CoreKeyboard } from '@singletons/keyboard';
@ -145,8 +144,6 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
this.discussionUserId = CoreNavigator.getRouteNumberParam('userId', { params }) ?? this.discussionUserId; this.discussionUserId = CoreNavigator.getRouteNumberParam('userId', { params }) ?? this.discussionUserId;
}); });
const deepLinkManager = new CoreMainMenuDeepLinkManager();
await this.fetchData(); await this.fetchData();
if (!this.discussionUserId && this.discussions.length > 0 && CoreScreen.isTablet && this.discussions[0].message) { if (!this.discussionUserId && this.discussions.length > 0 && CoreScreen.isTablet && this.discussions[0].message) {
@ -154,8 +151,8 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
await this.gotoDiscussion(this.discussions[0].message.user); await this.gotoDiscussion(this.discussions[0].message.user);
} }
// Treat deep link now that the conversation route has been loaded if needed. // Mark login navigation finished now that the conversation route has been loaded if needed.
deepLinkManager.treatLink(); CoreSites.loginNavigationFinished();
} }
/** /**

View File

@ -38,7 +38,6 @@ import { ActivatedRoute, Params } from '@angular/router';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
@ -312,8 +311,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
} }
}); });
const deepLinkManager = new CoreMainMenuDeepLinkManager();
await this.fetchData(); await this.fetchData();
if (!this.selectedConversationId && !this.selectedUserId && CoreScreen.isTablet) { if (!this.selectedConversationId && !this.selectedUserId && CoreScreen.isTablet) {
@ -326,8 +323,8 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
} }
} }
// Treat deep link now that the conversation route has been loaded if needed. // Mark login navigation finished now that the conversation route has been loaded if needed.
deepLinkManager.treatLink(); CoreSites.loginNavigationFinished();
} }
/** /**

View File

@ -26,7 +26,6 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTimeUtils } from '@services/utils/time'; import { CoreTimeUtils } from '@services/utils/time';
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
import { CoreListItemsManager } from '@classes/items-management/list-items-manager'; import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
@ -130,8 +129,7 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
this.loadMarkAllAsReadButton(); this.loadMarkAllAsReadButton();
}); });
const deepLinkManager = new CoreMainMenuDeepLinkManager(); CoreSites.loginNavigationFinished();
deepLinkManager.treatLink();
} }
/** /**

View File

@ -39,6 +39,11 @@ export type CoreQueueRunnerItem<T = any> = {
* Deferred with a promise resolved/rejected with the result of the function. * Deferred with a promise resolved/rejected with the result of the function.
*/ */
deferred: CorePromisedValue<T>; deferred: CorePromisedValue<T>;
/**
* Item's priority. Only used if usePriority=true.
*/
priority: number;
}; };
/** /**
@ -49,6 +54,12 @@ export type CoreQueueRunnerAddOptions = {
* Whether to allow having multiple entries with same ID in the queue. * Whether to allow having multiple entries with same ID in the queue.
*/ */
allowRepeated?: boolean; allowRepeated?: boolean;
/**
* If usePriority=true, the priority of the item. Higher priority means it will be executed first.
* Please notice that the first item is always run immediately, so it's not affected by the priority.
*/
priority?: number;
}; };
/** /**
@ -60,7 +71,13 @@ export class CoreQueueRunner {
protected orderedQueue: CoreQueueRunnerItem[] = []; protected orderedQueue: CoreQueueRunnerItem[] = [];
protected numberRunning = 0; protected numberRunning = 0;
constructor(protected maxParallel: number = 1) { } /**
* Constructor.
*
* @param maxParallel Max number of parallel executions.
* @param usePriority If true, the queue will be ordered by priority.
*/
constructor(protected maxParallel: number = 1, protected usePriority = false) { }
/** /**
* Get unique ID. * Get unique ID.
@ -147,10 +164,14 @@ export class CoreQueueRunner {
id, id,
fn, fn,
deferred: new CorePromisedValue<T>(), deferred: new CorePromisedValue<T>(),
priority: options.priority ?? 0,
}; };
this.queue[id] = item; this.queue[id] = item;
this.orderedQueue.push(item); this.orderedQueue.push(item);
if (this.usePriority) {
this.orderedQueue.sort((a, b) => b.priority - a.priority);
}
// Process next item if we haven't reached the max yet. // Process next item if we haven't reached the max yet.
this.processNextItem(); this.processNextItem();

View File

@ -21,7 +21,6 @@ import { CoreBlockComponent } from '@features/block/components/block/block';
import { CoreBlockDelegate } from '@features/block/services/block-delegate'; import { CoreBlockDelegate } from '@features/block/services/block-delegate';
import { CoreCourseBlock } from '@features/course/services/course'; import { CoreCourseBlock } from '@features/course/services/course';
import { CoreCoursesDashboard, CoreCoursesDashboardProvider } from '@features/courses/services/dashboard'; import { CoreCoursesDashboard, CoreCoursesDashboardProvider } from '@features/courses/services/dashboard';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
@ -97,8 +96,7 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective {
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite(); this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
const deepLinkManager = new CoreMainMenuDeepLinkManager(); CoreSites.loginNavigationFinished();
deepLinkManager.treatLink();
await this.loadSiteName(); await this.loadSiteName();

View File

@ -50,18 +50,18 @@ export class CoreMainMenuDeepLinkManager {
/** /**
* Treat a deep link if there's any to treat. * Treat a deep link if there's any to treat.
*/ */
treatLink(): void { async treatLink(): Promise<void> {
if (!this.pendingRedirect) { const pendingRedirect = this.pendingRedirect;
if (!pendingRedirect) {
return; return;
} }
if (this.pendingRedirect.redirectPath) {
this.treatPath(this.pendingRedirect.redirectPath, this.pendingRedirect.redirectOptions);
} else if (this.pendingRedirect.urlToOpen) {
this.treatUrlToOpen(this.pendingRedirect.urlToOpen);
}
delete this.pendingRedirect; delete this.pendingRedirect;
if (pendingRedirect.redirectPath) {
await this.treatPath(pendingRedirect.redirectPath, pendingRedirect.redirectOptions);
} else if (pendingRedirect.urlToOpen) {
await this.treatUrlToOpen(pendingRedirect.urlToOpen);
}
} }
/** /**
@ -70,18 +70,18 @@ export class CoreMainMenuDeepLinkManager {
* @param path Path. * @param path Path.
* @param navOptions Navigation options. * @param navOptions Navigation options.
*/ */
protected treatPath(path: string, navOptions: CoreNavigationOptions = {}): void { protected async treatPath(path: string, navOptions: CoreNavigationOptions = {}): Promise<void> {
const params = navOptions.params; const params = navOptions.params;
const coursePathMatches = path.match(/^course\/(\d+)\/?$/); const coursePathMatches = path.match(/^course\/(\d+)\/?$/);
if (coursePathMatches) { if (coursePathMatches) {
if (!params?.course) { if (!params?.course) {
CoreCourseHelper.getAndOpenCourse(Number(coursePathMatches[1]), params); await CoreCourseHelper.getAndOpenCourse(Number(coursePathMatches[1]), params);
} else { } else {
CoreCourse.openCourse(params.course, navOptions); await CoreCourse.openCourse(params.course, navOptions);
} }
} else { } else {
CoreNavigator.navigateToSitePath(path, { await CoreNavigator.navigateToSitePath(path, {
...navOptions, ...navOptions,
preferCurrentTab: false, preferCurrentTab: false,
}); });
@ -96,7 +96,7 @@ export class CoreMainMenuDeepLinkManager {
protected async treatUrlToOpen(url: string): Promise<void> { protected async treatUrlToOpen(url: string): Promise<void> {
const action = await CoreContentLinksHelper.getFirstValidActionFor(url); const action = await CoreContentLinksHelper.getFirstValidActionFor(url);
if (action?.sites?.[0]) { if (action?.sites?.[0]) {
action.action(action.sites[0]); await action.action(action.sites[0]);
} }
} }

View File

@ -21,7 +21,6 @@ import { CoreTabsOutletComponent, CoreTabsOutletTab } from '@components/tabs-out
import { CoreMainMenuHomeDelegate, CoreMainMenuHomeHandlerToDisplay } from '../../services/home-delegate'; import { CoreMainMenuHomeDelegate, CoreMainMenuHomeHandlerToDisplay } from '../../services/home-delegate';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu'; import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
/** /**
* Page that displays the Home. * Page that displays the Home.
@ -40,14 +39,11 @@ export class CoreMainMenuHomePage implements OnInit {
protected subscription?: Subscription; protected subscription?: Subscription;
protected updateSiteObserver?: CoreEventObserver; protected updateSiteObserver?: CoreEventObserver;
protected deepLinkManager?: CoreMainMenuDeepLinkManager;
/** /**
* @inheritdoc * @inheritdoc
*/ */
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
this.deepLinkManager = new CoreMainMenuDeepLinkManager();
await this.loadSiteName(); await this.loadSiteName();
this.subscription = CoreMainMenuHomeDelegate.getHandlersObservable().subscribe((handlers) => { this.subscription = CoreMainMenuHomeDelegate.getHandlersObservable().subscribe((handlers) => {
@ -108,7 +104,7 @@ export class CoreMainMenuHomePage implements OnInit {
* Tab was selected. * Tab was selected.
*/ */
tabSelected(): void { tabSelected(): void {
this.deepLinkManager?.treatLink(); CoreSites.loginNavigationFinished();
} }
/** /**

View File

@ -23,7 +23,7 @@ import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../servic
import { Router } from '@singletons'; import { Router } from '@singletons';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from '@classes/aria-role-tab'; import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from '@classes/aria-role-tab';
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { filter } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { NavigationEnd } from '@angular/router'; import { NavigationEnd } from '@angular/router';
import { trigger, state, style, transition, animate } from '@angular/animations'; import { trigger, state, style, transition, animate } from '@angular/animations';
@ -32,6 +32,7 @@ import { CoreDom } from '@singletons/dom';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreWait } from '@singletons/wait'; import { CoreWait } from '@singletons/wait';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
const ANIMATION_DURATION = 500; const ANIMATION_DURATION = 500;
@ -83,9 +84,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
protected backButtonFunction: (event: BackButtonEvent) => void; protected backButtonFunction: (event: BackButtonEvent) => void;
protected selectHistory: string[] = []; protected selectHistory: string[] = [];
protected firstSelectedTab?: string; protected firstSelectedTab?: string;
protected urlToOpen?: string;
protected redirectPath?: string;
protected redirectOptions?: CoreNavigationOptions;
protected logger: CoreLogger; protected logger: CoreLogger;
@ViewChild('mainTabs') mainTabs?: IonTabs; @ViewChild('mainTabs') mainTabs?: IonTabs;
@ -111,9 +109,16 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
*/ */
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
this.showTabs = true; this.showTabs = true;
this.urlToOpen = CoreNavigator.getRouteParam('urlToOpen');
this.redirectPath = CoreNavigator.getRouteParam('redirectPath'); const deepLinkManager = new CoreMainMenuDeepLinkManager();
this.redirectOptions = CoreNavigator.getRouteParam('redirectOptions');
// Treat the deep link (if any) when the login navigation finishes.
CoreSites.runAfterLoginNavigation({
priority: 800,
callback: async () => {
await deepLinkManager.treatLink();
},
});
this.isMainScreen = !this.mainTabs?.outlet.canGoBack(); this.isMainScreen = !this.mainTabs?.outlet.canGoBack();
this.updateVisibility(); this.updateVisibility();
@ -213,12 +218,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
// Use navigate instead of mainTabs.select to be able to pass page params. // Use navigate instead of mainTabs.select to be able to pass page params.
CoreNavigator.navigateToSitePath(tabPage, { CoreNavigator.navigateToSitePath(tabPage, {
preferCurrentTab: false, preferCurrentTab: false,
params: { params: tabPageParams,
urlToOpen: this.urlToOpen,
redirectPath: this.redirectPath,
redirectOptions: this.redirectOptions,
...tabPageParams,
},
}); });
} }
} }

View File

@ -22,7 +22,6 @@ import { CoreMainMenu, CoreMainMenuCustomItem } from '../../services/mainmenu';
import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreViewer } from '@features/viewer/services/viewer'; import { CoreViewer } from '@features/viewer/services/viewer';
@ -75,8 +74,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
this.initHandlers(); this.initHandlers();
}); });
const deepLinkManager = new CoreMainMenuDeepLinkManager(); CoreSites.loginNavigationFinished();
deepLinkManager.treatLink();
} }
/** /**

View File

@ -108,7 +108,7 @@ export class CorePushNotificationsDelegateService {
handlers = handlers.sort((a, b) => (a.priority || 0) <= (b.priority || 0) ? 1 : -1); handlers = handlers.sort((a, b) => (a.priority || 0) <= (b.priority || 0) ? 1 : -1);
// Execute the first one. // Execute the first one.
handlers[0]?.handleClick(notification); await handlers[0]?.handleClick(notification);
} }
/** /**

View File

@ -54,6 +54,8 @@ import { CorePlatform } from '@services/platform';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreSiteInfo } from '@classes/sites/unauthenticated-site'; import { CoreSiteInfo } from '@classes/sites/unauthenticated-site';
import { Push } from '@features/native/plugins'; import { Push } from '@features/native/plugins';
import { CoreNavigator } from '@services/navigator';
import { CoreWait } from '@singletons/wait';
/** /**
* Service to handle push notifications. * Service to handle push notifications.
@ -442,7 +444,27 @@ export class CorePushNotificationsProvider {
async notificationClicked(data: CorePushNotificationsNotificationBasicData): Promise<void> { async notificationClicked(data: CorePushNotificationsNotificationBasicData): Promise<void> {
await ApplicationInit.donePromise; await ApplicationInit.donePromise;
CorePushNotificationsDelegate.clicked(data); // This code is also done when clicking local notifications. If it's modified, it should be modified in there too.
if (CoreSites.isLoggedIn()) {
CoreSites.runAfterLoginNavigation({
priority: 600,
callback: async () => {
await CorePushNotificationsDelegate.clicked(data);
},
});
return;
}
// User not logged in, wait for the path to be a "valid" path (not a parent path used when starting the app).
await CoreWait.waitFor(() => {
const currentPath = CoreNavigator.getCurrentPath();
return currentPath !== '/' && currentPath !== '/login';
}, { timeout: 400 });
await CorePushNotificationsDelegate.clicked(data);
} }
/** /**

View File

@ -21,10 +21,10 @@ import { CoreTagCloud, CoreTagCollection, CoreTagCloudTag, CoreTag } from '@feat
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTime } from '@singletons/time'; import { CoreTime } from '@singletons/time';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreKeyboard } from '@singletons/keyboard'; import { CoreKeyboard } from '@singletons/keyboard';
import { CoreSites } from '@services/sites';
/** /**
* Page that displays most used tags and allows searching. * Page that displays most used tags and allows searching.
@ -65,8 +65,7 @@ export class CoreTagSearchPage implements OnInit {
this.collectionId = CoreNavigator.getRouteNumberParam('collectionId') || 0; this.collectionId = CoreNavigator.getRouteNumberParam('collectionId') || 0;
this.query = CoreNavigator.getRouteParam('query') || ''; this.query = CoreNavigator.getRouteParam('query') || '';
const deepLinkManager = new CoreMainMenuDeepLinkManager(); CoreSites.loginNavigationFinished();
deepLinkManager.treatLink();
this.fetchData().finally(() => { this.fetchData().finally(() => {
this.loaded = true; this.loaded = true;

View File

@ -23,7 +23,7 @@ import { CoreText } from '@singletons/text';
import { CoreQueueRunner } from '@classes/queue-runner'; import { CoreQueueRunner } from '@classes/queue-runner';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { makeSingleton, NgZone, Translate, LocalNotifications } from '@singletons'; import { makeSingleton, NgZone, Translate, LocalNotifications, ApplicationInit } from '@singletons';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { import {
APP_SCHEMA, APP_SCHEMA,
@ -42,6 +42,9 @@ import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance';
import { CoreDatabaseTable } from '@classes/database/database-table'; import { CoreDatabaseTable } from '@classes/database/database-table';
import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy';
import { CoreDomUtils } from './utils/dom'; import { CoreDomUtils } from './utils/dom';
import { CoreSites } from './sites';
import { CoreNavigator } from './navigator';
import { CoreWait } from '@singletons/wait';
/** /**
* Service to handle local notifications. * Service to handle local notifications.
@ -87,7 +90,28 @@ export class CoreLocalNotificationsProvider {
this.handleEvent('trigger', notification); this.handleEvent('trigger', notification);
}); });
this.clickSubscription = LocalNotifications.on('click').subscribe((notification: ILocalNotification) => { this.clickSubscription = LocalNotifications.on('click').subscribe(async (notification: ILocalNotification) => {
await ApplicationInit.donePromise;
// This code is also done when clicking push notifications. If it's modified, it should be modified in there too.
if (CoreSites.isLoggedIn()) {
CoreSites.runAfterLoginNavigation({
priority: 0, // Use a low priority because the execution of this process doesn't block the next ones.
callback: async () => {
this.handleEvent('click', notification);
},
});
return;
}
// User not logged in, wait for the path to be a "valid" path (not a parent path used when starting the app).
await CoreWait.waitFor(() => {
const currentPath = CoreNavigator.getCurrentPath();
return currentPath !== '/' && currentPath !== '/login';
}, { timeout: 400 });
this.handleEvent('click', notification); this.handleEvent('click', notification);
}); });

View File

@ -67,6 +67,7 @@ import { firstValueFrom } from 'rxjs';
import { CoreHTMLClasses } from '@singletons/html-classes'; import { CoreHTMLClasses } from '@singletons/html-classes';
import { CoreSiteErrorDebug } from '@classes/errors/siteerror'; import { CoreSiteErrorDebug } from '@classes/errors/siteerror';
import { CoreErrorHelper } from './error-helper'; import { CoreErrorHelper } from './error-helper';
import { CoreQueueRunner } from '@classes/queue-runner';
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS'); export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id'; export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
@ -96,6 +97,11 @@ export class CoreSitesProvider {
protected schemasTables: Record<string, AsyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name', never>>> = {}; protected schemasTables: Record<string, AsyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name', never>>> = {};
protected sitesTable = asyncInstance<CoreDatabaseTable<SiteDBEntry>>(); protected sitesTable = asyncInstance<CoreDatabaseTable<SiteDBEntry>>();
// Variables to run code after login navigation.
protected isLoginNavigationFinished = false;
protected afterLoginNavigationQueue: CoreSitesAfterLoginNavigationProcess[] = [];
protected afterLoginNavigationQueueRunner = new CoreQueueRunner(1, true);
constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] | null) { constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] | null) {
this.logger = CoreLogger.getInstance('CoreSitesProvider'); this.logger = CoreLogger.getInstance('CoreSitesProvider');
this.siteSchemas = (siteSchemas ?? []).flat().reduce( this.siteSchemas = (siteSchemas ?? []).flat().reduce(
@ -1462,6 +1468,8 @@ export class CoreSitesProvider {
const siteId = this.currentSite.getId(); const siteId = this.currentSite.getId();
this.currentSite = undefined; this.currentSite = undefined;
this.isLoginNavigationFinished = false;
this.afterLoginNavigationQueue = [];
if (options.forceLogout || (siteConfig && siteConfig.tool_mobile_forcelogout == '1')) { if (options.forceLogout || (siteConfig && siteConfig.tool_mobile_forcelogout == '1')) {
promises.push(this.setSiteLoggedOut(siteId)); promises.push(this.setSiteLoggedOut(siteId));
@ -2168,6 +2176,47 @@ export class CoreSitesProvider {
); );
} }
/**
* Run some code when the login navigation is finished. Login navigation finishes when the proper main menu page
* has loaded.
* If not logged in or the login navigation is already finished, the callback will run immediately (waiting for currently
* running processes to finish).
* Otherwise, the process will be added to a queue and will run once the login navigation is finished.
*
* @param data Process data.
*/
runAfterLoginNavigation(data: CoreSitesAfterLoginNavigationProcess): void {
if (!this.isLoggedIn() || this.isLoginNavigationFinished) {
this.afterLoginNavigationQueueRunner.run(data.callback, { priority: data.priority });
return;
}
this.afterLoginNavigationQueue.push(data);
// Sort the list by priority. The queue runner also uses priority, but the first run is always executed immediately
// so it's important to always pass the highest priority process first.
this.afterLoginNavigationQueue.sort((a, b) => b.priority - a.priority);
}
/**
* Notify that the login navigation is finished. This function should only be used by main menu pages.
*/
loginNavigationFinished(): void {
if (this.isLoginNavigationFinished) {
// Already finished, nothing else to do.
return;
}
this.isLoginNavigationFinished = true;
// Run the processes in the queue.
this.afterLoginNavigationQueue.forEach(data => {
this.afterLoginNavigationQueueRunner.run(data.callback, { priority: data.priority });
});
this.afterLoginNavigationQueue = [];
}
} }
export const CoreSites = makeSingleton(CoreSitesProvider); export const CoreSites = makeSingleton(CoreSitesProvider);
@ -2392,3 +2441,11 @@ export type CoreSitesLogoutOptions = {
forceLogout?: boolean; // If true, site will be marked as logged out, no matter the value tool_mobile_forcelogout. forceLogout?: boolean; // If true, site will be marked as logged out, no matter the value tool_mobile_forcelogout.
removeAccount?: boolean; // If true, site will be removed too after logout. removeAccount?: boolean; // If true, site will be removed too after logout.
}; };
/**
* Process to run after login navigation finishes.
*/
export type CoreSitesAfterLoginNavigationProcess = {
priority: number;
callback: () => Promise<void>;
};