diff --git a/scripts/langindex.json b/scripts/langindex.json index db523decd..9e6a56dc5 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -2168,6 +2168,7 @@ "core.login.missingfirstname": "moodle", "core.login.missinglastname": "moodle", "core.login.mobileservicesnotenabled": "local_moodlemobileapp", + "core.login.morewaystologin": "local_moodlemobileapp", "core.login.mustconfirm": "moodle", "core.login.newaccount": "moodle", "core.login.notloggedin": "local_moodlemobileapp", diff --git a/src/core/classes/sites/site.ts b/src/core/classes/sites/site.ts index 005aad7d4..296b5d9d0 100644 --- a/src/core/classes/sites/site.ts +++ b/src/core/classes/sites/site.ts @@ -195,6 +195,7 @@ export class CoreSite extends CoreAuthenticatedSite { * Check if the user authenticated in the site using an OAuth method. * * @returns Whether the user authenticated in the site using an OAuth method. + * @deprecated since 5.0. Use getOAuthId instead. */ isOAuth(): boolean { return this.oauthId != null && this.oauthId !== undefined; @@ -268,7 +269,6 @@ export class CoreSite extends CoreAuthenticatedSite { * * @param component Component name. * @param componentId Component id. - * @returns Promise resolved when the entries are deleted. */ async deleteComponentFromCache(component: string, componentId?: number): Promise { if (!component) { @@ -284,7 +284,7 @@ export class CoreSite extends CoreAuthenticatedSite { await this.cacheTable.delete(params); } - /* + /** * Uploads a file using Cordova File API. * * @param filePath File path. @@ -366,13 +366,17 @@ export class CoreSite extends CoreAuthenticatedSite { * @param url The url to be fixed. * @returns Promise resolved with the fixed URL. */ - checkAndFixPluginfileURL(url: string): Promise { - return this.checkTokenPluginFile(url).then(() => this.fixPluginfileURL(url)); + async checkAndFixPluginfileURL(url: string): Promise { + // Resolve the checking promise to make sure it's finished. + await this.checkTokenPluginFile(url); + + // The previous promise (tokenPluginFileWorks) result will be used here. + return this.fixPluginfileURL(url); } /** * Generic function for adding the wstoken to Moodle urls and for pointing to the correct script. - * Uses CoreUtilsProvider.fixPluginfileURL, passing site's token. + * Uses CoreUrl.fixPluginfileURL, passing site's token. * * @param url The url to be fixed. * @returns Fixed URL. @@ -386,8 +390,6 @@ export class CoreSite extends CoreAuthenticatedSite { /** * Deletes site's DB. - * - * @returns Promise to be resolved when the DB is deleted. */ async deleteDB(): Promise { await CoreDB.deleteDB('Site-' + this.id); @@ -395,8 +397,6 @@ export class CoreSite extends CoreAuthenticatedSite { /** * Deletes site's folder. - * - * @returns Promise to be resolved when the DB is deleted. */ async deleteFolder(): Promise { if (!CoreFile.isAvailable() || !this.id) { @@ -466,7 +466,6 @@ export class CoreSite extends CoreAuthenticatedSite { * @param url The URL to open. * @param alertMessage If defined, an alert will be shown before opening the browser. * @param options Other options. - * @returns Promise resolved when done, rejected otherwise. */ async openInBrowserWithAutoLogin( url: string, @@ -598,8 +597,6 @@ export class CoreSite extends CoreAuthenticatedSite { /** * Invalidates config WS call. - * - * @returns Promise resolved when the data is invalidated. */ async invalidateConfig(): Promise { await this.invalidateWsCacheForKey(this.getConfigCacheKey()); @@ -728,7 +725,6 @@ export class CoreSite extends CoreAuthenticatedSite { * Deletes a site setting. * * @param name The config name. - * @returns Promise resolved when done. */ async deleteSiteConfig(name: string): Promise { await this.configTable.deleteByPrimaryKey({ name }); @@ -760,13 +756,12 @@ export class CoreSite extends CoreAuthenticatedSite { * * @param name The config name. * @param value The config value. Can only store number or strings. - * @returns Promise resolved when done. */ async setLocalSiteConfig(name: string, value: number | string): Promise { await this.configTable.insert({ name, value }); } - /* + /** * Check if tokenpluginfile script works in the site. * * @param url URL to check. @@ -802,7 +797,6 @@ export class CoreSite extends CoreAuthenticatedSite { * Deletes last viewed records based on some conditions. * * @param conditions Conditions. - * @returns Promise resolved when done. */ async deleteLastViewed(conditions?: Partial): Promise { await this.lastViewedTable.delete(conditions); @@ -853,7 +847,6 @@ export class CoreSite extends CoreAuthenticatedSite { * @param id ID. * @param value Last viewed item value. * @param options Options. - * @returns Promise resolved when done. */ async storeLastViewed( component: string, diff --git a/src/core/features/login/components/components.module.ts b/src/core/features/login/components/components.module.ts index 1d2fc3fa7..be46f2bfd 100644 --- a/src/core/features/login/components/components.module.ts +++ b/src/core/features/login/components/components.module.ts @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreLoginMethodsComponent } from './login-methods/login-methods'; import { CoreLoginExceededAttemptsComponent } from '@features/login/components/exceeded-attempts/exceeded-attempts'; +import { CoreLoginIdentityProviderComponent } from './identity-provider/identity-provider'; @NgModule({ declarations: [ @@ -24,6 +25,7 @@ import { CoreLoginExceededAttemptsComponent } from '@features/login/components/e ], imports: [ CoreSharedModule, + CoreLoginIdentityProviderComponent, ], exports: [ CoreLoginExceededAttemptsComponent, diff --git a/src/core/features/login/components/identity-provider/identity-provider.html b/src/core/features/login/components/identity-provider/identity-provider.html new file mode 100644 index 000000000..e36769e20 --- /dev/null +++ b/src/core/features/login/components/identity-provider/identity-provider.html @@ -0,0 +1,7 @@ + + @if (provider.iconurl) { + + } + {{ provider.name }} + diff --git a/src/core/features/login/components/identity-provider/identity-provider.ts b/src/core/features/login/components/identity-provider/identity-provider.ts new file mode 100644 index 000000000..350ef4c72 --- /dev/null +++ b/src/core/features/login/components/identity-provider/identity-provider.ts @@ -0,0 +1,53 @@ +// (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 } from '@angular/core'; +import { CoreSiteIdentityProvider } from '@classes/sites/unauthenticated-site'; +import { CoreLoginHelper } from '@features/login/services/login-helper'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreSharedModule } from '@/core/shared.module'; +import { CoreRedirectPayload } from '@services/navigator'; + +@Component({ + selector: 'core-identity-provider', + templateUrl: 'identity-provider.html', + standalone: true, + imports: [ + CoreSharedModule, + ], +}) +export class CoreLoginIdentityProviderComponent { + + @Input({ required: true }) provider!: CoreSiteIdentityProvider; + @Input() launchurl = ''; + @Input() siteUrl = ''; + @Input() redirectData?: CoreRedirectPayload; + + /** + * The button has been clicked. + */ + async openOAuth(): Promise { + const result = await CoreLoginHelper.openBrowserForOAuthLogin( + this.siteUrl, + this.provider, + this.launchurl, + this.redirectData, + ); + + if (!result) { + CoreDomUtils.showErrorModal('Invalid data.'); + } + } + +} diff --git a/src/core/features/login/components/login-methods/login-methods.html b/src/core/features/login/components/login-methods/login-methods.html index be93e7972..163766bb9 100644 --- a/src/core/features/login/components/login-methods/login-methods.html +++ b/src/core/features/login/components/login-methods/login-methods.html @@ -21,9 +21,6 @@ diff --git a/src/core/features/login/components/login-methods/login-methods.ts b/src/core/features/login/components/login-methods/login-methods.ts index 9b8f3b1a5..9d55d49fc 100644 --- a/src/core/features/login/components/login-methods/login-methods.ts +++ b/src/core/features/login/components/login-methods/login-methods.ts @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { toBoolean } from '@/core/transforms/boolean'; import { Component, Input, OnInit } from '@angular/core'; +import { CorePromisedValue } from '@classes/promised-value'; +import { CoreSite } from '@classes/sites/site'; import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site'; import { CoreLoginHelper, CoreLoginMethod } from '@features/login/services/login-helper'; import { CoreRedirectPayload } from '@services/navigator'; -import { CoreSites } from '@services/sites'; import { CoreSitesFactory } from '@services/sites-factory'; -import { CoreDomUtils } from '@services/utils/dom'; @Component({ selector: 'core-login-methods', @@ -28,10 +27,10 @@ import { CoreDomUtils } from '@services/utils/dom'; }) export class CoreLoginMethodsComponent implements OnInit { - @Input({ transform: toBoolean }) reconnect = false; @Input() siteUrl = ''; @Input() siteConfig?: CoreSitePublicConfigResponse; @Input() redirectData?: CoreRedirectPayload; + @Input() site?: CoreSite; // Defined when the user is reconnecting. @Input() showLoginForm = true; isBrowserSSO = false; @@ -39,16 +38,20 @@ export class CoreLoginMethodsComponent implements OnInit { loginMethods: CoreLoginMethod[] = []; identityProviders: CoreSiteIdentityProvider[] = []; + protected currentLoginProvider?: CoreSiteIdentityProvider; + protected isReady = new CorePromisedValue(); + /** * @inheritdoc */ async ngOnInit(): Promise { - if (this.reconnect) { + if (this.site) { + this.siteUrl = this.site.getURL(); + this.loginMethods = await CoreLoginHelper.getLoginMethods(); - const currentSite = CoreSites.getCurrentSite(); const defaultMethod = await CoreLoginHelper.getDefaultLoginMethod(); - if (currentSite?.isLoggedOut() && defaultMethod) { + if (this.site.isLoggedOut() && defaultMethod) { await defaultMethod.action(); } } @@ -59,25 +62,29 @@ export class CoreLoginMethodsComponent implements OnInit { // Identity providers won't be shown if login on browser. if (!this.isBrowserSSO) { this.identityProviders = await CoreLoginHelper.getValidIdentityProvidersForSite( - CoreSitesFactory.makeUnauthenticatedSite(this.siteUrl, this.siteConfig), + this.site ?? CoreSitesFactory.makeUnauthenticatedSite(this.siteUrl, this.siteConfig), ); } - if (this.reconnect) { + if (this.site) { this.showScanQR = CoreLoginHelper.displayQRInSiteScreen(); + + // The identity provider set in the site will be shown at the top. + const oAuthId = this.site.getOAuthId(); + this.currentLoginProvider = CoreLoginHelper.findIdentityProvider(this.identityProviders, oAuthId); } // If still false or credentials screen. - if (!this.reconnect || !this.showScanQR) { + if (!this.site || !this.showScanQR) { this.showScanQR = await CoreLoginHelper.displayQRInCredentialsScreen(this.siteConfig.tool_mobile_qrcodetype); } } + + this.isReady.resolve(); } /** * Show instructions and scan QR code. - * - * @returns Promise resolved when done. */ async showInstructionsAndScanQR(): Promise { try { @@ -90,21 +97,33 @@ export class CoreLoginMethodsComponent implements OnInit { } /** - * An OAuth button was clicked. + * Get the current login, removing the identity provider from the list. * - * @param provider The provider that was clicked. + * @returns Current login. */ - async oauthClicked(provider: CoreSiteIdentityProvider): Promise { - const result = await CoreLoginHelper.openBrowserForOAuthLogin( - this.siteUrl, - provider, - this.siteConfig?.launchurl, - this.redirectData, - ); + async extractCurrentLogin(): Promise { + await this.isReady; - if (!result) { - CoreDomUtils.showErrorModal('Invalid data.'); + if (!this.currentLoginProvider) { + return; } + + // Remove the identity provider from the array. + this.identityProviders = this.identityProviders.filter((provider) => + provider.url !== this.currentLoginProvider?.url); + + const showOther = !!(this.showLoginForm || this.isBrowserSSO) && + !!(this.loginMethods.length || this.identityProviders.length || this.showScanQR); + + return { + provider: this.currentLoginProvider, + showOther, + }; } } + +export type CoreLoginMethodsCurrentLogin = { + provider: CoreSiteIdentityProvider; + showOther: boolean; +}; diff --git a/src/core/features/login/lang.json b/src/core/features/login/lang.json index 015fa398a..b84f1bd53 100644 --- a/src/core/features/login/lang.json +++ b/src/core/features/login/lang.json @@ -71,6 +71,7 @@ "missingfirstname": "Missing given name", "missinglastname": "Missing last name", "mobileservicesnotenabled": "Mobile services are not enabled on the site.", + "morewaystologin": "More ways to log in", "mustconfirm": "You need to confirm your account", "newaccount": "New account", "notloggedin": "You need to be logged in.", diff --git a/src/core/features/login/login-reconnect-lazy.module.ts b/src/core/features/login/login-reconnect-lazy.module.ts index e0696584c..1d02a530e 100644 --- a/src/core/features/login/login-reconnect-lazy.module.ts +++ b/src/core/features/login/login-reconnect-lazy.module.ts @@ -19,6 +19,7 @@ import { CoreSharedModule } from '@/core/shared.module'; import { CoreLoginComponentsModule } from '@features/login/components/components.module'; import { CoreLoginReconnectPage } from '@features/login/pages/reconnect/reconnect'; import { CoreSiteLogoComponent } from '@/core/components/site-logo/site-logo'; +import { CoreLoginIdentityProviderComponent } from './components/identity-provider/identity-provider'; const routes: Routes = [ { @@ -33,6 +34,7 @@ const routes: Routes = [ CoreSharedModule, CoreLoginComponentsModule, CoreSiteLogoComponent, + CoreLoginIdentityProviderComponent, ], declarations: [ CoreLoginReconnectPage, diff --git a/src/core/features/login/pages/reconnect/reconnect.html b/src/core/features/login/pages/reconnect/reconnect.html index cda292eb9..1a682cefc 100644 --- a/src/core/features/login/pages/reconnect/reconnect.html +++ b/src/core/features/login/pages/reconnect/reconnect.html @@ -51,41 +51,62 @@ + + + + + + + + + + + + + diff --git a/src/core/features/login/pages/reconnect/reconnect.ts b/src/core/features/login/pages/reconnect/reconnect.ts index 1db1c98f7..8ad3b8a7f 100644 --- a/src/core/features/login/pages/reconnect/reconnect.ts +++ b/src/core/features/login/pages/reconnect/reconnect.ts @@ -34,6 +34,7 @@ import { CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-sit import { ALWAYS_SHOW_LOGIN_FORM_CHANGED, FORGOTTEN_PASSWORD_FEATURE_NAME } from '@features/login/constants'; import { CoreKeyboard } from '@singletons/keyboard'; import { CoreLoadings } from '@services/loadings'; +import { CoreLoginMethodsComponent, CoreLoginMethodsCurrentLogin } from '@features/login/components/login-methods/login-methods'; /** * Page to enter the user password to reconnect to a site. @@ -46,6 +47,17 @@ import { CoreLoadings } from '@services/loadings'; export class CoreLoginReconnectPage implements OnInit, OnDestroy { @ViewChild('reconnectForm') formElement?: ElementRef; + @ViewChild(CoreLoginMethodsComponent) set loginMethods(loginMethods: CoreLoginMethodsComponent) { + if (loginMethods && !this.currentLogin) { + loginMethods.extractCurrentLogin().then(login => { + this.currentLogin = login; + + return; + }).catch(() => { + // Ignore errors. + }); + } + } credForm: FormGroup; site!: CoreSite; @@ -53,6 +65,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { showForgottenPassword = true; showUserAvatar = false; isBrowserSSO = false; + currentLogin?: CoreLoginMethodsCurrentLogin; isLoggedOut: boolean; siteId!: string; siteInfo?: CoreSiteBasicInfo; @@ -252,16 +265,21 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { const modal = await CoreLoadings.show(); + const url = this.site.getURL(); + try { // Start the authentication process. - const data = await CoreSites.getUserToken(this.site.getURL(), this.username, password); + const data = await CoreSites.getUserToken(url, this.username, password); - await CoreSites.updateSiteToken(this.site.getURL(), this.username, data.token, data.privateToken); + await CoreSites.updateSiteToken(url, this.username, data.token, data.privateToken); CoreForms.triggerFormSubmittedEvent(this.formElement, true); + // Unset oAuthID if it's set. + await CoreSites.removeSiteOauthId(this.siteId); + // Update site info too. - await CoreSites.updateSiteInfoByUrl(this.site.getURL(), this.username); + await CoreSites.updateSiteInfoByUrl(url, this.username); // Reset fields so the data is not in the view anymore. this.credForm.controls['password'].reset(); @@ -271,7 +289,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { params: this.redirectData, }); } catch (error) { - CoreLoginHelper.treatUserTokenError(this.site.getURL(), error, this.username, password); + CoreLoginHelper.treatUserTokenError(url, error, this.username, password); if (error.loggedout) { this.cancel(); diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index 23ee93791..4cc29c9d0 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -151,7 +151,6 @@ export class CoreLoginHelperProvider { * @param service The service to use. If not defined, core service will be used. * @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used. * @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page. - * @returns Promise resolved when done or if user cancelled. * @deprecated since 4.3. Use openBrowserForSSOLogin instead. */ async confirmAndOpenBrowserForSSOLogin( @@ -393,7 +392,7 @@ export class CoreLoginHelperProvider { siteConfig.identityproviders.forEach((provider) => { const urlParams = CoreUrl.extractUrlParams(provider.url); - if (provider.url && (provider.url.indexOf(httpsUrl) != -1 || provider.url.indexOf(httpUrl) != -1) && + if (provider.url && (provider.url.indexOf(httpsUrl) !== -1 || provider.url.indexOf(httpUrl) !== -1) && !site.isFeatureDisabled(IDENTITY_PROVIDER_FEATURE_NAME_PREFIX + urlParams.id)) { validProviders.push(provider); } @@ -403,13 +402,27 @@ export class CoreLoginHelperProvider { return validProviders; } + /** + * Finds an identity provider from a list of providers based on the given OAuth ID. + * + * @param providers Array of identity providers. + * @param oauthId The OAuth ID to match against the providers' URLs. + * @returns The identity provider that matches the given OAuth ID, or undefined if no match is found. + */ + findIdentityProvider(providers: CoreSiteIdentityProvider[], oauthId?: number): CoreSiteIdentityProvider | undefined { + if (!oauthId) { + return; + } + + return providers.find(provider => Number(CoreUrl.extractUrlParams(provider.url).id) === oauthId); + } + /** * Go to the page to add a new site. * If a fixed URL is configured, go to credentials instead. * * @param setRoot True to set the new page as root, false to add it to the stack. * @param showKeyboard Whether to show keyboard in the new page. Only if no fixed URL set. - * @returns Promise resolved when done. */ async goToAddSite(setRoot = false, showKeyboard = false): Promise { if (CoreSites.isLoggedIn()) { @@ -462,6 +475,7 @@ export class CoreLoginHelperProvider { * @param privateToken User's private token. * @param oauthId OAuth ID. Only if the authentication was using an OAuth method. * @returns Promise resolved when the user is authenticated with the token. + * @deprecated since 5.0. This is now handled by CoreCustomURLSchemes. */ handleSSOLoginAuthentication(siteUrl: string, token: string, privateToken?: string, oauthId?: number): Promise { // Always create a new site to prevent overriding data if another user credentials were introduced. @@ -682,7 +696,6 @@ export class CoreLoginHelperProvider { * * @param siteUrl Site URL to construct change password URL. * @param error Error message. - * @returns Promise resolved when done. */ async openChangePassword(siteUrl: string, error: string): Promise { const alert = await CoreDomUtils.showAlert(Translate.instant('core.notice'), error, undefined, 3000); @@ -708,7 +721,6 @@ export class CoreLoginHelperProvider { * @param path The relative path of the URL to open. * @param alertMessage The key of the message to display before opening the in app browser. * @param invalidateCache Whether to invalidate site's cache (e.g. when the user is forced to change password). - * @returns Promise resolved when done. */ async openInAppForEdit(siteId: string, path: string, alertMessage?: string, invalidateCache?: boolean): Promise { if (!siteId || siteId !== CoreSites.getCurrentSiteId()) { @@ -840,7 +852,6 @@ export class CoreLoginHelperProvider { * Function that should be called when the session expires. Reserved for core use. * * @param data Data received by the SESSION_EXPIRED event. - * @returns Promise resolved when done. */ async sessionExpired(data: CoreEventSessionExpiredData & CoreEventSiteData): Promise { const siteId = data?.siteId; @@ -1214,8 +1225,6 @@ export class CoreLoginHelperProvider { /** * Start waiting when opening a browser/IAB. - * - * @returns Promise resolved when the app is resumed. */ async waitForBrowser(): Promise { if (!this.waitingForBrowser) { @@ -1268,8 +1277,6 @@ export class CoreLoginHelperProvider { /** * Show instructions to scan QR code. - * - * @returns Promise resolved if the user accepts to scan QR. */ async showScanQRInstructions(): Promise { const dontShowWarning = await CoreConfig.get(FAQ_QRCODE_INFO_DONE, 0); @@ -1303,8 +1310,6 @@ export class CoreLoginHelperProvider { /** * Scan a QR code and tries to authenticate the user using custom URL scheme. - * - * @returns Promise resolved when done. */ async scanQR(): Promise { // Scan for a QR code. @@ -1375,7 +1380,6 @@ export class CoreLoginHelperProvider { * * @param accountsList Account list. * @param site Site to be deleted. - * @returns Resolved when done. */ async deleteAccountFromList(accountsList: CoreAccountsList, site: CoreSiteBasicInfo): Promise { await CoreSites.deleteSite(site.id); diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index d052b9338..5706f4d30 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -1611,7 +1611,6 @@ export class CoreSitesProvider { * @param siteId Site Id. * @param token User's new token. * @param privateToken User's private token. - * @returns A promise resolved when the site is updated. */ async updateSiteTokenBySiteId(siteId: string, token: string, privateToken: string = ''): Promise { const site = await this.getSite(siteId); @@ -1633,6 +1632,23 @@ export class CoreSitesProvider { await Promise.all(promises); } + /** + * Removes the OAuth ID for a given site. + * + * @param siteId The ID of the site to update. + */ + async removeSiteOauthId(siteId: string): Promise { + const site = await this.getSite(siteId); + + site.setOAuthId(undefined); + + const newData: Partial = { + oauthId: null, + }; + + await this.sitesTable.update(newData, { id: siteId }); + } + /** * Updates a site's info. * diff --git a/src/core/singletons/opener.ts b/src/core/singletons/opener.ts index df73a3270..a7f037b56 100644 --- a/src/core/singletons/opener.ts +++ b/src/core/singletons/opener.ts @@ -82,7 +82,6 @@ export class CoreOpener { * * @param path The local path of the file to be open. * @param options Options. - * @returns Promise resolved when done. */ static async openFile(path: string, options: CoreOpenerOpenFileOptions = {}): Promise { // Convert the path to a native path if needed. diff --git a/src/core/singletons/window.ts b/src/core/singletons/window.ts index 03972fe60..d3d08ac29 100644 --- a/src/core/singletons/window.ts +++ b/src/core/singletons/window.ts @@ -49,7 +49,6 @@ export class CoreWindow { * * @param url URL to open. * @param name Name of the browsing context into which to load the URL. - * @returns Promise resolved when done. */ static async open(url: string, name?: string): Promise { if (CoreUrl.isLocalFileUrl(url)) {