diff --git a/src/app/core/login/login-routing.module.ts b/src/app/core/login/login-routing.module.ts index 775de336d..8b1ae6f9a 100644 --- a/src/app/core/login/login-routing.module.ts +++ b/src/app/core/login/login-routing.module.ts @@ -18,6 +18,7 @@ import { RouterModule, Routes } from '@angular/router'; import { CoreLoginCredentialsPage } from './pages/credentials/credentials.page'; import { CoreLoginInitPage } from './pages/init/init.page'; import { CoreLoginSitePage } from './pages/site/site.page'; +import { CoreLoginSitesPage } from './pages/sites/sites.page'; const routes: Routes = [ { @@ -32,6 +33,10 @@ const routes: Routes = [ path: 'credentials', component: CoreLoginCredentialsPage, }, + { + path: 'sites', + component: CoreLoginSitesPage, + }, ]; @NgModule({ diff --git a/src/app/core/login/login.module.ts b/src/app/core/login/login.module.ts index 0c18f5430..89a65c544 100644 --- a/src/app/core/login/login.module.ts +++ b/src/app/core/login/login.module.ts @@ -26,6 +26,7 @@ import { CoreLoginRoutingModule } from './login-routing.module'; import { CoreLoginCredentialsPage } from './pages/credentials/credentials.page'; import { CoreLoginInitPage } from './pages/init/init.page'; import { CoreLoginSitePage } from './pages/site/site.page'; +import { CoreLoginSitesPage } from './pages/sites/sites.page'; import { CoreLoginHelperProvider } from './services/helper'; @NgModule({ @@ -43,6 +44,7 @@ import { CoreLoginHelperProvider } from './services/helper'; CoreLoginCredentialsPage, CoreLoginInitPage, CoreLoginSitePage, + CoreLoginSitesPage, ], providers: [ CoreLoginHelperProvider, diff --git a/src/app/core/login/pages/credentials/credentials.page.ts b/src/app/core/login/pages/credentials/credentials.page.ts index 3bcc2f00e..6bb45942c 100644 --- a/src/app/core/login/pages/credentials/credentials.page.ts +++ b/src/app/core/login/pages/credentials/credentials.page.ts @@ -248,7 +248,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy { CoreLoginHelper.instance.treatUserTokenError(siteUrl, error, username, password); if (error.loggedout) { - // @todo Go to sites page. + this.navCtrl.navigateRoot('/login/sites'); } else if (error.errorcode == 'forcepasswordchangenotice') { // Reset password field. this.credForm.controls.password.reset(); diff --git a/src/app/core/login/pages/init/init.page.ts b/src/app/core/login/pages/init/init.page.ts index 762be4743..2391f6c98 100644 --- a/src/app/core/login/pages/init/init.page.ts +++ b/src/app/core/login/pages/init/init.page.ts @@ -90,7 +90,7 @@ export class CoreLoginInitPage implements OnInit { // return this.loginHelper.goToSiteInitialPage(); // } - await this.navCtrl.navigateRoot('/login/site'); + await this.navCtrl.navigateRoot('/login/sites'); } } diff --git a/src/app/core/login/pages/site/site.page.ts b/src/app/core/login/pages/site/site.page.ts index 432c52a12..783f77060 100644 --- a/src/app/core/login/pages/site/site.page.ts +++ b/src/app/core/login/pages/site/site.page.ts @@ -321,7 +321,7 @@ export class CoreLoginSitePage implements OnInit { CoreLoginHelper.instance.treatUserTokenError(siteData.url, error, siteData.username, siteData.password); if (error.loggedout) { - // @todo Send the user to sites page. + this.navCtrl.navigateRoot('/login/sites'); } } finally { modal.dismiss(); diff --git a/src/app/core/login/pages/sites/sites.html b/src/app/core/login/pages/sites/sites.html new file mode 100644 index 000000000..be0a4b1b5 --- /dev/null +++ b/src/app/core/login/pages/sites/sites.html @@ -0,0 +1,37 @@ + + + + + + + {{ 'core.settings.sites' | translate }} + + + + + + + + + + + + + + {{ 'core.pictureof' | translate:{$a: site.fullName} }} + +

{{site.fullName}}

+

+

{{site.siteUrl}}

+ {{site.badge}} + + + +
+
+ + + + + +
diff --git a/src/app/core/login/pages/sites/sites.page.ts b/src/app/core/login/pages/sites/sites.page.ts new file mode 100644 index 000000000..8605780a1 --- /dev/null +++ b/src/app/core/login/pages/sites/sites.page.ts @@ -0,0 +1,145 @@ +// (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 { CoreDomUtils } from '@/app/services/utils/dom'; +import { CoreUtils } from '@/app/services/utils/utils'; +import { Component, OnInit } from '@angular/core'; + +import { CoreSiteBasicInfo, CoreSites } from '@services/sites'; +import { CoreLogger } from '@singletons/logger'; +import { CoreLoginHelper } from '../../services/helper'; + +/** + * Page that displays a "splash screen" while the app is being initialized. + */ +@Component({ + selector: 'page-core-login-sites', + templateUrl: 'sites.html', + styleUrls: ['sites.scss'], +}) +export class CoreLoginSitesPage implements OnInit { + + sites: CoreSiteBasicInfo[] = []; + showDelete = false; + + protected logger: CoreLogger; + + constructor() { + this.logger = CoreLogger.getInstance('CoreLoginSitesPage'); + } + + /** + * Component being initialized. + * + * @return Promise resolved when done. + */ + async ngOnInit(): Promise { + const sites = await CoreUtils.instance.ignoreErrors(CoreSites.instance.getSortedSites()); + + if (!sites || sites.length == 0) { + CoreLoginHelper.instance.goToAddSite(true); + + return; + } + + // Remove protocol from the url to show more url text. + this.sites = sites.map((site) => { + site.siteUrl = site.siteUrl.replace(/^https?:\/\//, ''); + site.badge = 0; + // @todo: getSiteCounter. + + return site; + }); + + this.showDelete = false; + } + + /** + * Go to the page to add a site. + */ + add(): void { + CoreLoginHelper.instance.goToAddSite(false, true); + } + + /** + * Delete a site. + * + * @param e Click event. + * @param index Position of the site. + * @return Promise resolved when done. + */ + async deleteSite(e: Event, index: number): Promise { + e.stopPropagation(); + + const site = this.sites[index]; + const siteName = site.siteName || ''; + + // @todo: Format text: siteName. + + try { + await CoreDomUtils.instance.showDeleteConfirm('core.login.confirmdeletesite', { sitename: siteName }); + } catch (error) { + // User cancelled, stop. + return; + } + + try { + await CoreSites.instance.deleteSite(site.id); + + this.sites.splice(index, 1); + this.showDelete = false; + + // If there are no sites left, go to add site. + const hasSites = await CoreSites.instance.hasSites(); + + if (!hasSites) { + CoreLoginHelper.instance.goToAddSite(true, true); + } + } catch (error) { + this.logger.error('Error deleting site ' + site.id, error); + CoreDomUtils.instance.showErrorModalDefault(error, 'core.login.errordeletesite', true); + } + } + + /** + * Login in a site. + * + * @param siteId The site ID. + * @return Promise resolved when done. + */ + async login(siteId: string): Promise { + const modal = await CoreDomUtils.instance.showModalLoading(); + + try { + const loggedIn = await CoreSites.instance.loadSite(siteId); + + if (loggedIn) { + return CoreLoginHelper.instance.goToSiteInitialPage(); + } + } catch (error) { + this.logger.error('Error loading site ' + siteId, error); + CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading site.'); + } finally { + modal.dismiss(); + } + } + + /** + * Toggle delete. + */ + toggleDelete(): void { + this.showDelete = !this.showDelete; + } + +} diff --git a/src/app/core/login/pages/sites/sites.scss b/src/app/core/login/pages/sites/sites.scss new file mode 100644 index 000000000..abe0054de --- /dev/null +++ b/src/app/core/login/pages/sites/sites.scss @@ -0,0 +1,3 @@ +.item-ios .item-button[icon-only] ion-icon { + font-size: 2.1em; +} diff --git a/src/app/core/login/services/helper.ts b/src/app/core/login/services/helper.ts index cd3f3dbdf..c01d9caf8 100644 --- a/src/app/core/login/services/helper.ts +++ b/src/app/core/login/services/helper.ts @@ -55,7 +55,8 @@ export class CoreLoginHelperProvider { waitingForBrowser = false; constructor( - private location: Location, + protected location: Location, + protected navCtrl: NavController, ) { this.logger = CoreLogger.getInstance('CoreLoginHelper'); @@ -314,7 +315,7 @@ export class CoreLoginHelperProvider { */ getLogoutLabel(site?: CoreSite): string { site = site || CoreSites.instance.getCurrentSite(); - const config = site?.getStoredConfig(); + const config = site?.getStoredConfig(); return 'core.mainmenu.' + (config && config.tool_mobile_forcelogout == '1' ? 'logout' : 'changesite'); } @@ -342,7 +343,7 @@ export class CoreLoginHelperProvider { try { // Try to get the latest config, maybe the site policy was just added or has changed. - sitePolicy = await site.getConfig('sitepolicy', true); + sitePolicy = await site.getConfig('sitepolicy', true); } catch (error) { // Cannot get config, try to get the site policy using auth_email_get_signup_settings. const settings = await CoreWS.instance.callAjax( @@ -413,8 +414,30 @@ export class CoreLoginHelperProvider { * @return Promise resolved when done. */ async goToAddSite(setRoot?: boolean, showKeyboard?: boolean): Promise { - // @todo - return Promise.resolve(); + let pageRoute: string; + let params: Params; + + if (this.isFixedUrlSet()) { + // Fixed URL is set, go to credentials page. + const fixedSites = this.getFixedSites(); + const url = typeof fixedSites == 'string' ? fixedSites : fixedSites[0].url; + + pageRoute = '/login/credentials'; + params = { siteUrl: url }; + } else { + pageRoute = '/login/site'; + params = { showKeyboard: showKeyboard }; + } + + if (setRoot) { + await this.navCtrl.navigateRoot(pageRoute, { + queryParams: params, + }); + } else { + await this.navCtrl.navigateForward(pageRoute, { + queryParams: params, + }); + } } /** diff --git a/src/assets/img/user-avatar.png b/src/assets/img/user-avatar.png new file mode 100644 index 000000000..0345e26c8 Binary files /dev/null and b/src/assets/img/user-avatar.png differ