From 176407a613f61616c5d845dfdeeba8d55027a1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 28 Oct 2020 17:24:51 +0100 Subject: [PATCH 1/6] MOBILE-3565 core: Add empty box and icon properties --- src/app/components/components.module.ts | 3 + .../components/empty-box/core-empty-box.html | 9 +++ src/app/components/empty-box/empty-box.scss | 70 +++++++++++++++++++ src/app/components/empty-box/empty-box.ts | 43 ++++++++++++ src/app/components/icon/icon.scss | 28 -------- src/app/components/icon/icon.ts | 9 +-- src/app/core/courses/pages/home/home.html | 6 +- src/app/core/settings/pages/about/about.html | 9 ++- src/app/directives/fa-icon.ts | 2 - src/theme/app.scss | 32 +++++++++ 10 files changed, 170 insertions(+), 41 deletions(-) create mode 100644 src/app/components/empty-box/core-empty-box.html create mode 100644 src/app/components/empty-box/empty-box.scss create mode 100644 src/app/components/empty-box/empty-box.ts diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index afc95a694..b16efbbcb 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -20,6 +20,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreIconComponent } from './icon/icon'; import { CoreLoadingComponent } from './loading/loading'; import { CoreShowPasswordComponent } from './show-password/show-password'; +import { CoreEmptyBoxComponent } from './empty-box/empty-box'; import { CoreDirectivesModule } from '@app/directives/directives.module'; import { CorePipesModule } from '@app/pipes/pipes.module'; @@ -28,6 +29,7 @@ import { CorePipesModule } from '@app/pipes/pipes.module'; CoreIconComponent, CoreLoadingComponent, CoreShowPasswordComponent, + CoreEmptyBoxComponent, ], imports: [ CommonModule, @@ -40,6 +42,7 @@ import { CorePipesModule } from '@app/pipes/pipes.module'; CoreIconComponent, CoreLoadingComponent, CoreShowPasswordComponent, + CoreEmptyBoxComponent, ], }) export class CoreComponentsModule {} diff --git a/src/app/components/empty-box/core-empty-box.html b/src/app/components/empty-box/core-empty-box.html new file mode 100644 index 000000000..b15958be0 --- /dev/null +++ b/src/app/components/empty-box/core-empty-box.html @@ -0,0 +1,9 @@ +
+
+ + + +

{{ message }}

+ +
+
diff --git a/src/app/components/empty-box/empty-box.scss b/src/app/components/empty-box/empty-box.scss new file mode 100644 index 000000000..9fea08738 --- /dev/null +++ b/src/app/components/empty-box/empty-box.scss @@ -0,0 +1,70 @@ +:host { + .core-empty-box { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: table; + height: 100%; + width: 100%; + margin: 0; + clear: both; + pointer-events: none; + + .core-empty-box-content { + margin: 0; + display: table-cell; + text-align: center; + vertical-align: middle; + pointer-events: auto; + } + + &.core-empty-box-inline { + position: relative; + z-index: initial; + top: initial; + right: initial; + bottom: 0; + left: initial; + height: auto; + } + + ion-icon { + font-size: 120px; + } + img { + height: 125px; + width: 145px; + margin: 0 auto; + } + p { + font-size: 120%; + } + } + + &.core-empty-box-clickable .core-empty-box { + z-index: 0; + } + + @media (max-width: 350px) { + .core-empty-box { + position: relative; + height: auto; + margin-top: 50px; + + ion-icon { + font-size: 100px; + } + img { + height: 104px; + width: 121px; + } + } + } +} + +:host-context(core-block-course-blocks) .core-empty-box { + position: relative; +} + diff --git a/src/app/components/empty-box/empty-box.ts b/src/app/components/empty-box/empty-box.ts new file mode 100644 index 000000000..f2d5ef172 --- /dev/null +++ b/src/app/components/empty-box/empty-box.ts @@ -0,0 +1,43 @@ +// (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'; + +/** + * Component to show an empty box message. It will show an optional icon or image and a text centered on page. + * + * Use class="core-empty-box-clickable" if you want to add some clickable elements to the box. + * + * Usage: + * + */ +@Component({ + selector: 'core-empty-box', + templateUrl: 'core-empty-box.html', + styleUrls: ['empty-box.scss'], +}) +export class CoreEmptyBoxComponent { + + @Input() message = ''; // Message to display. + @Input() icon?: string; // Name of the icon to use. + @Input() image?: string; // Image source. If an icon is provided, image won't be used. + + /** + * If this has to be shown inline instead of occupying whole page. + * If image or icon is not supplied, it's true by default. + */ + @Input() inline?: boolean; + @Input() flipIconRtl?: boolean; // Whether to flip the icon in RTL. Defaults to false. + +} diff --git a/src/app/components/icon/icon.scss b/src/app/components/icon/icon.scss index 99a0a14ee..93c842fce 100644 --- a/src/app/components/icon/icon.scss +++ b/src/app/components/icon/icon.scss @@ -123,31 +123,3 @@ } } } - -// TODO ionic 5 -:host-context([dir=rtl]) ion-icon { - &.core-icon-dir-flip, - &.fa-caret-right, - &.ion-md-send, &.ion-ios-send { - -webkit-transform: scale(-1, 1); - transform: scale(-1, 1); - } -} - -:host { - &.icon-slash { - &::after { - content: " "; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background-color: var(--ion-color-danger); - -webkit-mask: url("/assets/fonts/font-awesome/solid/slash.svg") no-repeat 50% 50%; - mask: url("/assets/fonts/font-awesome/solid/slash.svg") no-repeat 50% 50%; - -webkit-transform: scale(-1, 1); - transform: scale(-1, 1); - } - } -} diff --git a/src/app/components/icon/icon.ts b/src/app/components/icon/icon.ts index c9a36fd21..4d181a0a5 100644 --- a/src/app/components/icon/icon.ts +++ b/src/app/components/icon/icon.ts @@ -20,6 +20,7 @@ import { Component, Input, OnChanges, ElementRef, SimpleChange } from '@angular/ * the component will detect it. * * Check available icons at https://fontawesome.com/icons?d=gallery&m=free + * * @deprecated since 3.9.3. Please use instead. */ @Component({ @@ -81,9 +82,9 @@ export class CoreIconComponent implements OnChanges { } if (this.isTrueProperty(this.flipRtl)) { - iconElement.classList.add('core-icon-dir-flip'); + iconElement.classList.add('icon-flip-rtl'); } else { - iconElement.classList.remove('core-icon-dir-flip'); + iconElement.classList.remove('icon-flip-rtl'); } if (this.isTrueProperty(this.fixedWidth)) { @@ -96,10 +97,10 @@ export class CoreIconComponent implements OnChanges { /** * Check if the value is true or on. * - * @param val value to be checked. + * @param val Value to be checked. * @return If has a value equivalent to true. */ - isTrueProperty(val: any): boolean { + isTrueProperty(val: unknown): boolean { if (typeof val === 'string') { val = val.toLowerCase().trim(); diff --git a/src/app/core/courses/pages/home/home.html b/src/app/core/courses/pages/home/home.html index 79b79100d..2b428a553 100644 --- a/src/app/core/courses/pages/home/home.html +++ b/src/app/core/courses/pages/home/home.html @@ -16,5 +16,7 @@ - Home page. - \ No newline at end of file + +
Home page
+
+ diff --git a/src/app/core/settings/pages/about/about.html b/src/app/core/settings/pages/about/about.html index c0d25bb80..8a47fbf42 100644 --- a/src/app/core/settings/pages/about/about.html +++ b/src/app/core/settings/pages/about/about.html @@ -1,4 +1,3 @@ - @@ -11,18 +10,18 @@ - +

{{ appName }} {{ versionName }}

- + {{ 'core.settings.opensourcelicenses' | translate }} - + {{ 'core.settings.privacypolicy' | translate }} - + {{ 'core.settings.deviceinfo' | translate }} diff --git a/src/app/directives/fa-icon.ts b/src/app/directives/fa-icon.ts index c68c7bdf5..f2f60c509 100644 --- a/src/app/directives/fa-icon.ts +++ b/src/app/directives/fa-icon.ts @@ -32,8 +32,6 @@ export class CoreFaIconDirective implements OnChanges { @Input() name = ''; - // TODO: Support slash, RTL and fixed width. - protected element: HTMLElement; protected logger: CoreLogger; diff --git a/src/theme/app.scss b/src/theme/app.scss index 033a25fa9..8808db1b7 100644 --- a/src/theme/app.scss +++ b/src/theme/app.scss @@ -4,3 +4,35 @@ ion-toolbar ion-back-button, ion-toolbar .in-toolbar.button-clear { --color: var(--ion-color-primary-contrast); } + +// Ion icon styles. +ion-icon { + &.icon-slash::after, + &.icon-backslash::after { + content: " "; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: var(--ion-color-danger); + -webkit-mask: url("/assets/fonts/font-awesome/solid/slash.svg") no-repeat 50% 50%; + mask: url("/assets/fonts/font-awesome/solid/slash.svg") no-repeat 50% 50%; + } + + &.icon-slash::after { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); + } + + &.fa-fw { + text-align: center; + width: 1.25em; + } +} + +[dir=rtl] ion-icon.icon-flip-rtl { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); +} + From 14decb9c0199e139fa9918b201ab0a81ddefb916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 28 Oct 2020 23:19:57 +0100 Subject: [PATCH 2/6] MOBILE-3565 core: Style loading and show password components --- src/app/components/loading/core-loading.html | 4 +- src/app/components/loading/loading.scss | 90 +++++++------------ .../show-password/core-show-password.html | 6 +- .../show-password/show-password.scss | 51 +++++------ .../components/show-password/show-password.ts | 5 +- src/app/lang/en.json | 1 + src/assets/lang/en.json | 1 + 7 files changed, 67 insertions(+), 91 deletions(-) diff --git a/src/app/components/loading/core-loading.html b/src/app/components/loading/core-loading.html index da4887db0..986f383f9 100644 --- a/src/app/components/loading/core-loading.html +++ b/src/app/components/loading/core-loading.html @@ -1,10 +1,10 @@
- +

{{message}}

-
\ No newline at end of file + diff --git a/src/app/components/loading/loading.scss b/src/app/components/loading/loading.scss index afe335a58..b8289607b 100644 --- a/src/app/components/loading/loading.scss +++ b/src/app/components/loading/loading.scss @@ -1,67 +1,43 @@ -ion-app.app-root { - core-loading { - // @todo @include core-transition(height, 200ms); +:host { + position: static; + -webkit-transition: height 200ms ease-in-out; + transition: height 200ms ease-in-out; - .core-loading-container { - width: 100%; + > .core-loading-container { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + display: table; + height: 100%; + width: 100%; + text-align: center; + clear: both; + z-index: 3; + margin: 0; + padding: 10px 0 0 0; + background-color: rgba(255, 255, 255, 0.26); + -webkit-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; + + .core-loading-spinner { + display: table-cell; text-align: center; - padding-top: 10px; - clear: both; - /* @todo @include darkmode() { - color: $core-dark-text-color; - } */ - } - - .core-loading-content { - display: inline; - padding-bottom: 1px; /* This makes height be real */ - } - - &.core-loading-noheight .core-loading-content { - height: auto; - } - - &.safe-area-page { - padding-left: 0 !important; - padding-right: 0 !important; - - > .core-loading-content > *:not[padding], - > .core-loading-content-loading > *:not[padding] { - // @todo @include safe-area-padding-horizontal(0px, 0px); - } + vertical-align: middle; } } - .scroll-content > core-loading, - ion-content > .scroll-content > core-loading, - core-tab core-loading, - .core-loading-center { - position: static !important; + .core-loading-content { + display: inline; + padding-bottom: 1px; /* This makes height be real */ } - .scroll-content > core-loading, - ion-content > .scroll-content > core-loading, - core-tab core-loading, - .core-loading-center, - core-loading.core-loading-loaded { + &.core-loading-noheight .core-loading-content { + height: auto; + } + + &.core-loading-loaded { position: relative; - - > .core-loading-container { - position: absolute; - // @todo @include position(0, 0, 0, 0); - display: table; - height: 100%; - width: 100%; - z-index: 1; - margin: 0; - padding: 0; - clear: both; - - .core-loading-spinner { - display: table-cell; - text-align: center; - vertical-align: middle; - } - } } } diff --git a/src/app/components/show-password/core-show-password.html b/src/app/components/show-password/core-show-password.html index d90db3f49..ef0ac0ce1 100644 --- a/src/app/components/show-password/core-show-password.html +++ b/src/app/components/show-password/core-show-password.html @@ -1,4 +1,4 @@ - - - + + + diff --git a/src/app/components/show-password/show-password.scss b/src/app/components/show-password/show-password.scss index 26188a26b..1f98c810c 100644 --- a/src/app/components/show-password/show-password.scss +++ b/src/app/components/show-password/show-password.scss @@ -1,38 +1,35 @@ -ion-app.app-root core-show-password { - padding: 0px; - width: 100%; - position: relative; +:host { + display: contents; - ion-input input.text-input { - // @todo @include padding(null, 47px, null, null); - } - - .button[icon-only] { + ion-button { background: transparent; - // @todo padding: 0 ($content-padding / 2); + padding: 0 calc(var(--padding-start) / 2); position: absolute; - // @todo @include position(null, 0, $content-padding / 2, null); + right: 0; + bottom: calc(var(--padding-bottom) / 2); margin-top: 0; margin-bottom: 0; - } - - .core-ioninput-password { - padding-top: 0; - padding-bottom: 0; + z-index: 3; } } -ion-app.app-root.md { - .item-label-stacked core-show-password .button[icon-only] { - bottom: 0; - } +::slotted(ion-input) { + --padding-end: 47px !important; } -ion-app.app-root.ios { - .item-label-stacked core-show-password .button[icon-only] { - bottom: -5px; - } - core-show-password .button[icon-only] { - bottom: 0; - } +:host-context([dir="rtl"]) ion-button { + left: 0; + right: unset; +} + +:host-context(.md.item-label.stacked) ion-button { + bottom: 0; +} + +:host-context(.iositem-label.stacked) ion-button { + bottom: -5px; +} + +:host-context(.ios) ion-button { + bottom: 0; } diff --git a/src/app/components/show-password/show-password.ts b/src/app/components/show-password/show-password.ts index 6cec76522..d90cf3615 100644 --- a/src/app/components/show-password/show-password.ts +++ b/src/app/components/show-password/show-password.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, AfterViewInit, Input, ElementRef, ContentChild } from '@angular/core'; +import { Component, OnInit, AfterViewInit, Input, ElementRef, ContentChild, ViewEncapsulation } from '@angular/core'; import { IonInput } from '@ionic/angular'; import { CoreApp } from '@services/app'; @@ -29,7 +29,7 @@ import { CoreUtils } from '@services/utils/utils'; * * Example: * - * + * * * */ @@ -37,6 +37,7 @@ import { CoreUtils } from '@services/utils/utils'; selector: 'core-show-password', templateUrl: 'core-show-password.html', styleUrls: ['show-password.scss'], + encapsulation: ViewEncapsulation.ShadowDom, }) export class CoreShowPasswordComponent implements OnInit, AfterViewInit { diff --git a/src/app/lang/en.json b/src/app/lang/en.json index 00ce91b93..c02aec243 100644 --- a/src/app/lang/en.json +++ b/src/app/lang/en.json @@ -2,6 +2,7 @@ "back": "Back", "browser": "Browser", "copiedtoclipboard": "Text copied to clipboard", + "loading": "Loading", "no": "No", "offline": "Offline", "ok": "OK", diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 7eeea14b8..2ccd08508 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -340,6 +340,7 @@ "core.courses.sendpaymentbutton": "Send payment via PayPal", "core.courses.show": "Restore to view", "core.courses.totalcoursesearchresults": "Total courses: {{$a}}", + "core.loading": "Loading", "core.login.auth_email": "Email-based self-registration", "core.login.authenticating": "Authenticating", "core.login.cancel": "Cancel", From 7008be653646b439baff398fc3c831bef8150cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 29 Oct 2020 12:39:15 +0100 Subject: [PATCH 3/6] MOBILE-3565 core: Drop desktop support --- src/app/core/login/pages/site/site.page.ts | 2 +- src/app/core/login/services/helper.ts | 19 +--- .../core/settings/pages/about/about.page.ts | 2 +- .../pages/deviceinfo/deviceinfo.page.ts | 25 ++---- src/app/services/app.ts | 90 +++---------------- src/app/services/cron.ts | 3 +- src/app/services/file.ts | 2 +- src/app/services/filepool.ts | 13 +-- src/app/services/local-notifications.ts | 5 +- src/app/services/sites.ts | 1 - src/app/services/utils/dom.ts | 5 -- src/app/services/utils/utils.ts | 17 +--- src/types/global.d.ts | 1 - 13 files changed, 37 insertions(+), 148 deletions(-) diff --git a/src/app/core/login/pages/site/site.page.ts b/src/app/core/login/pages/site/site.page.ts index 158594e0d..67aef30cb 100644 --- a/src/app/core/login/pages/site/site.page.ts +++ b/src/app/core/login/pages/site/site.page.ts @@ -79,7 +79,7 @@ export class CoreLoginSitePage implements OnInit { // Load fixed sites if they're set. if (CoreLoginHelper.instance.hasSeveralFixedSites()) { url = this.initSiteSelector(); - } else if (CoreConstants.CONFIG.enableonboarding && !CoreApp.instance.isIOS() && !CoreApp.instance.isMac()) { + } else if (CoreConstants.CONFIG.enableonboarding && !CoreApp.instance.isIOS()) { this.initOnboarding(); } diff --git a/src/app/core/login/services/helper.ts b/src/app/core/login/services/helper.ts index 5c7d9609b..1d48d43fd 100644 --- a/src/app/core/login/services/helper.ts +++ b/src/app/core/login/services/helper.ts @@ -601,11 +601,6 @@ export class CoreLoginHelperProvider { * @return True if embedded browser, false othwerise. */ isSSOEmbeddedBrowser(code: number): boolean { - if (CoreApp.instance.isLinux()) { - // In Linux desktop app, always use embedded browser. - return true; - } - return code == CoreConstants.LOGIN_SSO_INAPP_CODE; } @@ -722,16 +717,11 @@ export class CoreLoginHelperProvider { oauthsso: params.id, }); - if (CoreApp.instance.isLinux()) { - // In Linux desktop app, always use embedded browser. - CoreUtils.instance.openInApp(loginUrl); - } else { - // Always open it in browser because the user might have the session stored in there. - CoreUtils.instance.openInBrowser(loginUrl); + // Always open it in browser because the user might have the session stored in there. + CoreUtils.instance.openInBrowser(loginUrl); - const nav = window.navigator; // eslint-disable-line @typescript-eslint/no-explicit-any - nav.app?.exitApp(); - } + const nav = window.navigator; // eslint-disable-line @typescript-eslint/no-explicit-any + nav.app?.exitApp(); return true; } @@ -1071,7 +1061,6 @@ export class CoreLoginHelperProvider { */ protected showMoodleAppNoticeModal(message: string): void { const storesConfig: CoreStoreConfig = CoreConstants.CONFIG.appstores; - storesConfig.desktop = 'https://download.moodle.org/desktop/'; storesConfig.mobile = 'https://download.moodle.org/mobile/'; storesConfig.default = 'https://download.moodle.org/mobile/'; diff --git a/src/app/core/settings/pages/about/about.page.ts b/src/app/core/settings/pages/about/about.page.ts index cf00be122..bc35c3244 100644 --- a/src/app/core/settings/pages/about/about.page.ts +++ b/src/app/core/settings/pages/about/about.page.ts @@ -33,7 +33,7 @@ export class CoreSettingsAboutPage { ) { const currentSite = CoreSites.instance.getCurrentSite(); - this.appName = CoreApp.instance.isDesktop() ? CoreConstants.CONFIG.desktopappname : CoreConstants.CONFIG.appname; + this.appName = CoreConstants.CONFIG.appname; this.versionName = CoreConstants.CONFIG.versionname; // Calculate the privacy policy to use. diff --git a/src/app/core/settings/pages/deviceinfo/deviceinfo.page.ts b/src/app/core/settings/pages/deviceinfo/deviceinfo.page.ts index 322fb6585..6149b260b 100644 --- a/src/app/core/settings/pages/deviceinfo/deviceinfo.page.ts +++ b/src/app/core/settings/pages/deviceinfo/deviceinfo.page.ts @@ -117,25 +117,14 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy { } } } else { - this.deviceInfo.deviceType = appProvider.isDesktop() ? 'desktop' : 'browser'; - if (appProvider.isLinux()) { - this.deviceInfo.deviceOs = 'linux'; - this.deviceOsTranslated = 'Linux'; - } else if (appProvider.isMac()) { - this.deviceInfo.deviceOs = 'mac'; - this.deviceOsTranslated = 'MacOS'; - } else if (appProvider.isWindows()) { - this.deviceInfo.deviceOs = 'windows'; - this.deviceOsTranslated = 'Windows'; + this.deviceInfo.deviceType = 'browser'; + const matches = navigator.userAgent.match(/\(([^)]*)\)/); + if (matches && matches.length > 1) { + this.deviceInfo.deviceOs = matches[1]; + this.deviceOsTranslated = matches[1]; } else { - const matches = navigator.userAgent.match(/\(([^)]*)\)/); - if (matches && matches.length > 1) { - this.deviceInfo.deviceOs = matches[1]; - this.deviceOsTranslated = matches[1]; - } else { - this.deviceInfo.deviceOs = 'unknown'; - this.deviceOsTranslated = translate.instant('core.unknown'); - } + this.deviceInfo.deviceOs = 'unknown'; + this.deviceOsTranslated = translate.instant('core.unknown'); } } diff --git a/src/app/services/app.ts b/src/app/services/app.ts index 2862dc336..a22293047 100644 --- a/src/app/services/app.ts +++ b/src/app/services/app.ts @@ -226,22 +226,6 @@ export class CoreAppProvider { * @return Store URL. */ getAppStoreUrl(storesConfig: CoreStoreConfig): string | undefined { - if (this.isMac() && storesConfig.mac) { - return 'itms-apps://itunes.apple.com/app/' + storesConfig.mac; - } - - if (this.isWindows() && storesConfig.windows) { - return 'https://www.microsoft.com/p/' + storesConfig.windows; - } - - if (this.isLinux() && storesConfig.linux) { - return storesConfig.linux; - } - - if (this.isDesktop() && storesConfig.desktop) { - return storesConfig.desktop; - } - if (this.isIOS() && storesConfig.ios) { return 'itms-apps://itunes.apple.com/app/' + storesConfig.ios; } @@ -260,10 +244,11 @@ export class CoreAppProvider { /** * Checks if the app is running in a 64 bits desktop environment (not browser). * - * @return Whether the app is running in a 64 bits desktop environment (not browser). + * @return false. + * @deprecated Desktop support has been removed. */ is64Bits(): boolean { - return this.isDesktop() && window.process.arch == 'x64'; + return false; } /** @@ -278,10 +263,10 @@ export class CoreAppProvider { /** * Checks if the app is running in a desktop environment (not browser). * - * @return Whether the app is running in a desktop environment (not browser). + * @return false. + * @deprecated Desktop support has been removed. */ isDesktop(): boolean { - // @todo return false; } @@ -324,39 +309,21 @@ export class CoreAppProvider { /** * Check if the app is running in a Linux environment. * - * @return Whether it's running in a Linux environment. + * @return false. + * @deprecated Desktop support has been removed. */ isLinux(): boolean { - if (!this.isDesktop()) { - return false; - } - - try { - // @todo return require('os').platform().indexOf('linux') === 0; - - return false; - } catch (ex) { - return false; - } + return false; } /** * Check if the app is running in a Mac OS environment. * - * @return Whether it's running in a Mac OS environment. + * @return false. + * @deprecated Desktop support has been removed. */ isMac(): boolean { - if (!this.isDesktop()) { - return false; - } - - try { - // @todo return require('os').platform().indexOf('darwin') === 0; - - return false; - } catch (ex) { - return false; - } + return false; } /** @@ -435,20 +402,11 @@ export class CoreAppProvider { /** * Check if the app is running in a Windows environment. * - * @return Whether it's running in a Windows environment. + * @return false. + * @deprecated Desktop support has been removed. */ isWindows(): boolean { - if (!this.isDesktop()) { - return false; - } - - try { - // @todo return require('os').platform().indexOf('win') === 0; - - return false; - } catch (ex) { - return false; - } + return false; } /** @@ -724,26 +682,6 @@ export type CoreRedirectData = { * Store config data. */ export type CoreStoreConfig = { - /** - * ID of the Apple store where the desktop Mac app is uploaded. - */ - mac?: string; - - /** - * ID of the Windows store where the desktop Windows app is uploaded. - */ - windows?: string; - - /** - * Url with the desktop linux download link. - */ - linux?: string; - - /** - * Fallback URL when the desktop options is not set. - */ - desktop?: string; - /** * ID of the Apple store where the mobile iOS app is uploaded. */ diff --git a/src/app/services/cron.ts b/src/app/services/cron.ts index a4e6cdf38..fd13576ab 100644 --- a/src/app/services/cron.ts +++ b/src/app/services/cron.ts @@ -35,7 +35,6 @@ export class CoreCronDelegate { // Constants. static readonly DEFAULT_INTERVAL = 3600000; // Default interval is 1 hour. static readonly MIN_INTERVAL = 300000; // Minimum interval is 5 minutes. - static readonly DESKTOP_MIN_INTERVAL = 60000; // Minimum interval in desktop is 1 minute. static readonly MAX_TIME_PROCESS = 120000; // Max time a process can block the queue. Defaults to 2 minutes. // Variables for database. @@ -237,7 +236,7 @@ export class CoreCronDelegate { } // Don't allow intervals lower than the minimum. - const minInterval = CoreApp.instance.isDesktop() ? CoreCronDelegate.DESKTOP_MIN_INTERVAL : CoreCronDelegate.MIN_INTERVAL; + const minInterval = CoreCronDelegate.MIN_INTERVAL; const handlerInterval = this.handlers[name].getInterval!(); if (!handlerInterval) { diff --git a/src/app/services/file.ts b/src/app/services/file.ts index 6d9702625..980bc83d0 100644 --- a/src/app/services/file.ts +++ b/src/app/services/file.ts @@ -568,7 +568,7 @@ export class CoreFileProvider { // Create file (and parent folders) to prevent errors. const fileEntry = await this.createFile(path); - if (this.isHTMLAPI && !CoreApp.instance.isDesktop() && + if (this.isHTMLAPI && (typeof data == 'string' || data.toString() == '[object ArrayBuffer]')) { // We need to write Blobs. const extension = CoreMimetypeUtils.instance.getFileExtension(path); diff --git a/src/app/services/filepool.ts b/src/app/services/filepool.ts index f1b2ec9f2..6baddc81c 100644 --- a/src/app/services/filepool.ts +++ b/src/app/services/filepool.ts @@ -1756,12 +1756,8 @@ export class CoreFilepoolProvider { const path = await this.getFilePath(siteId, fileId); const fileEntry = await CoreFile.instance.getFile(path); - // This URL is usually used to launch files or put them in HTML. In desktop we need the internal URL. - if (CoreApp.instance.isDesktop()) { - return fileEntry.toInternalURL(); - } else { - return fileEntry.toURL(); - } + // This URL is usually used to launch files or put them in HTML. + return fileEntry.toURL(); } /** @@ -2937,11 +2933,6 @@ export class CoreFilepoolProvider { return; } - if (CoreApp.instance.isDesktop()) { - // In desktop always download first. - return; - } - const mimetype = await CoreUtils.instance.getMimeTypeFromUrl(url); // If the file is streaming (audio or video) we reject. if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) { diff --git a/src/app/services/local-notifications.ts b/src/app/services/local-notifications.ts index 2c38641f7..5f1c7e3d7 100644 --- a/src/app/services/local-notifications.ts +++ b/src/app/services/local-notifications.ts @@ -222,8 +222,7 @@ export class CoreLocalNotificationsProvider { */ canDisableSound(): boolean { // Only allow disabling sound in Android 7 or lower. In iOS and Android 8+ it can easily be done with system settings. - return this.isAvailable() &&!CoreApp.instance.isDesktop() && CoreApp.instance.isAndroid() && - Number(Device.instance.version?.split('.')[0]) < 8; + return this.isAvailable() && CoreApp.instance.isAndroid() && Number(Device.instance.version?.split('.')[0]) < 8; } /** @@ -363,7 +362,7 @@ export class CoreLocalNotificationsProvider { isAvailable(): boolean { const win = window; // eslint-disable-line @typescript-eslint/no-explicit-any - return CoreApp.instance.isDesktop() || !!win.cordova?.plugins?.notification?.local; + return !!win.cordova?.plugins?.notification?.local; } /** diff --git a/src/app/services/sites.ts b/src/app/services/sites.ts index 9292eacf4..71604d276 100644 --- a/src/app/services/sites.ts +++ b/src/app/services/sites.ts @@ -888,7 +888,6 @@ export class CoreSitesProvider { const storesConfig: CoreStoreConfig = { android: config.tool_mobile_androidappid, ios: config.tool_mobile_iosappid, - desktop: config.tool_mobile_setuplink || 'https://download.moodle.org/desktop/', mobile: config.tool_mobile_setuplink || 'https://download.moodle.org/mobile/', default: config.tool_mobile_setuplink, }; diff --git a/src/app/services/utils/dom.ts b/src/app/services/utils/dom.ts index 5dfa487d2..7a07f95a4 100644 --- a/src/app/services/utils/dom.ts +++ b/src/app/services/utils/dom.ts @@ -144,11 +144,6 @@ export class CoreDomUtilsProvider { const readableSize = CoreTextUtils.instance.bytesToSize(size.size, 2); const getAvailableBytes = async (): Promise => { - if (CoreApp.instance.isDesktop()) { - // Free space calculation is not supported on desktop. - return null; - } - const availableBytes = await CoreFile.instance.calculateFreeSpace(); if (CoreApp.instance.isAndroid()) { diff --git a/src/app/services/utils/utils.ts b/src/app/services/utils/utils.ts index 84f793f3e..e8eedbb87 100644 --- a/src/app/services/utils/utils.ts +++ b/src/app/services/utils/utils.ts @@ -227,15 +227,10 @@ export class CoreUtilsProvider { /** * Close the InAppBrowser window. - * - * @param closeAll Desktop only. True to close all secondary windows, false to close only the "current" one. */ - closeInAppBrowser(closeAll?: boolean): void { + closeInAppBrowser(): void { if (this.iabInstance) { this.iabInstance.close(); - if (closeAll && CoreApp.instance.isDesktop()) { - // @todo require('electron').ipcRenderer.send('closeSecondaryWindows'); - } } } @@ -959,7 +954,7 @@ export class CoreUtilsProvider { this.iabInstance = InAppBrowser.instance.create(url, '_blank', options); - if (CoreApp.instance.isDesktop() || CoreApp.instance.isMobile()) { + if (CoreApp.instance.isMobile()) { let loadStopSubscription; const loadStartUrls: string[] = []; @@ -1011,11 +1006,7 @@ export class CoreUtilsProvider { * @param url The URL to open. */ openInBrowser(url: string): void { - if (CoreApp.instance.isDesktop()) { - // @todo - } else { - window.open(url, '_system'); - } + window.open(url, '_system'); } /** @@ -1513,7 +1504,7 @@ export class CoreUtilsProvider { if (!CoreApp.instance.isMobile()) { - return Promise.reject('QRScanner isn\'t available in desktop apps.'); + return Promise.reject('QRScanner isn\'t available in browser.'); } // Ask the user for permission to use the camera. diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 1e05ea6d4..19e8234c4 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -27,7 +27,6 @@ declare global { CONFIG: { app_id: string; appname: string; - desktopappname: string; versioncode: number; versionname: string; cache_update_frequency_usually: number; From 8a816561c70ea09138dacd9b868fef1aea2dbaf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 29 Oct 2020 11:42:50 +0100 Subject: [PATCH 4/6] MOBILE-3565 core: Style error alerts --- src/app/lang/en.json | 6 +++++ src/app/services/utils/dom.ts | 45 ++++++++++++++++------------------- src/assets/lang/en.json | 6 +++++ src/theme/app.scss | 38 +++++++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 27 deletions(-) diff --git a/src/app/lang/en.json b/src/app/lang/en.json index c02aec243..bd3da88ca 100644 --- a/src/app/lang/en.json +++ b/src/app/lang/en.json @@ -1,12 +1,18 @@ { "back": "Back", "browser": "Browser", + "cannotconnect": "Cannot connect", + "cannotconnecttrouble": "We're having trouble connecting to your site.", + "cannotconnectverify": "Please check the address is correct.", "copiedtoclipboard": "Text copied to clipboard", "loading": "Loading", + "needhelp": "Need help?", + "networkerrormsg": "There was a problem connecting to the site. Please check your connection and try again.", "no": "No", "offline": "Offline", "ok": "OK", "online": "Online", + "tryagain": "Try again", "unknown": "Unknown", "yes": "Yes" } diff --git a/src/app/services/utils/dom.ts b/src/app/services/utils/dom.ts index 7a07f95a4..86b2ccf07 100644 --- a/src/app/services/utils/dom.ts +++ b/src/app/services/utils/dom.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable, SimpleChange, ElementRef, KeyValueChanges } from '@angular/core'; -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { DomSanitizer } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; import { AlertOptions, AlertButton, TextFieldTypes } from '@ionic/core'; import { Md5 } from 'ts-md5'; @@ -570,18 +570,6 @@ export class CoreDomUtilsProvider { return parseInt(style[measure], 10) || 0; } - /** - * Get the HTML code to render a connection warning icon. - * - * @return HTML Code. - */ - getConnectionWarningIconHtml(): string { - return '
' + - '' + - '' + - '
'; - } - /** * Returns width of an element. * @@ -650,18 +638,14 @@ export class CoreDomUtilsProvider { } /** - * Given an error message, return a suitable error title. + * Given a message, it deduce if it's a network error. * - * @param message The error message. - * @return Title. + * @param message Message text. + * @return True if the message error is a network error, false otherwise. */ - private getErrorTitle(message: string): SafeHtml | string { - if (message == Translate.instance.instant('core.networkerrormsg') || - message == Translate.instance.instant('core.fileuploader.errormustbeonlinetoupload')) { - return this.domSanitizer.bypassSecurityTrustHtml(this.getConnectionWarningIconHtml()); - } - - return CoreTextUtils.instance.decodeHTML(Translate.instance.instant('core.error')); + protected isNetworkError(message: string): boolean { + return message == Translate.instance.instant('core.networkerrormsg') || + message == Translate.instance.instant('core.fileuploader.errormustbeonlinetoupload'); } /** @@ -1240,7 +1224,7 @@ export class CoreDomUtilsProvider { // Store the alert and remove it when dismissed. this.displayedAlerts[alertId] = alert; - // // Set the callbacks to trigger an observable event. + // Set the callbacks to trigger an observable event. // eslint-disable-next-line promise/catch-or-return, promise/always-return alert.onDidDismiss().then(() => { delete this.displayedAlerts[alertId]; @@ -1366,7 +1350,18 @@ export class CoreDomUtilsProvider { return Promise.resolve(null); } - return this.showAlert( this.getErrorTitle(message), message, undefined, autocloseTime); + const alertOptions: AlertOptions = { + message: message, + buttons: [Translate.instance.instant('core.ok')], + }; + + if (this.isNetworkError(message)) { + alertOptions.cssClass = 'core-alert-network-error'; + } else { + alertOptions.header = Translate.instance.instant('core.error'); + } + + return this.showAlertWithOptions(alertOptions, autocloseTime); } /** diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 2ccd08508..1f1eb0b64 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -302,6 +302,9 @@ "assets.mimetypes.video": "Video file ({{$a.EXT}})", "core.back": "Back", "core.browser": "Browser", + "core.cannotconnect": "Cannot connect", + "core.cannotconnecttrouble": "We're having trouble connecting to your site.", + "core.cannotconnectverify": "Please check the address is correct.", "core.copiedtoclipboard": "Text copied to clipboard", "core.courses.addtofavourites": "Star this course", "core.courses.allowguests": "This course allows guest users to enter", @@ -467,6 +470,8 @@ "core.mainmenu.help": "Help", "core.mainmenu.logout": "Log out", "core.mainmenu.website": "Website", + "core.needhelp": "Need help?", + "core.networkerrormsg": "There was a problem connecting to the site. Please check your connection and try again.", "core.no": "No", "core.offline": "Offline", "core.ok": "OK", @@ -542,6 +547,7 @@ "core.settings.syncsettings": "Synchronisation settings", "core.settings.total": "Total", "core.settings.wificonnection": "Wi-Fi connection", + "core.tryagain": "Try again", "core.unknown": "Unknown", "core.yes": "Yes" } \ No newline at end of file diff --git a/src/theme/app.scss b/src/theme/app.scss index 8808db1b7..6a8b5c921 100644 --- a/src/theme/app.scss +++ b/src/theme/app.scss @@ -1,11 +1,11 @@ -// Add here base app styles. +// Ionic toolbar. ion-toolbar ion-back-button, ion-toolbar .in-toolbar.button-clear { --color: var(--ion-color-primary-contrast); } -// Ion icon styles. +// Ionic icon. ion-icon { &.icon-slash::after, &.icon-backslash::after { @@ -36,3 +36,37 @@ ion-icon { transform: scale(-1, 1); } +// Ionic alert. +ion-alert.core-alert-network-error .alert-head { + position: relative; + content: " "; + background: url("/assets/fonts/font-awesome/solid/wifi.svg") no-repeat 50% 50%; + margin: 25px auto; + + &::after { + content: " "; + position: absolute; + top: -20%; + right: -15%; + width: 50%; + height: 50%; + background-color: var(--ion-color-danger); + -webkit-mask: url("/assets/fonts/font-awesome/solid/exclamation-triangle.svg") no-repeat 50% 50%; + mask: url("/assets/fonts/font-awesome/solid/exclamation-triangle.svg") no-repeat 50% 50%; + } +} +[dir=rtl] ion-alert.core-alert-network-error .alert-head::after { + right: unset; + left: -15%; +} + +// Ionic item divider. +ion-item-divider { + --background: var(--gray-lighter); + border: 0; +} + +// Ionic list. +ion-list.list-md { + padding-bottom: 0; +} From c1b7b10c5d04d9933aa3b01e07aa66b966b2faf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 29 Oct 2020 12:55:11 +0100 Subject: [PATCH 5/6] MOBILE-3565 home: Move home component to mainmenu --- src/app/core/courses/courses.module.ts | 11 +---------- .../{courses => mainmenu}/handlers/mainmenu.ts | 4 ++-- src/app/core/mainmenu/lang/en.json | 3 ++- .../core/mainmenu/mainmenu-routing.module.ts | 2 +- src/app/core/mainmenu/mainmenu.module.ts | 16 +++++++++++++--- .../{courses => mainmenu}/pages/home/home.html | 0 .../pages/home/home.page.module.ts | 8 ++++---- .../pages/home/home.page.ts | 4 ++-- .../{courses => mainmenu}/pages/home/home.scss | 0 src/app/core/mainmenu/pages/more/more.html | 18 +++++++++--------- src/assets/lang/en.json | 1 + 11 files changed, 35 insertions(+), 32 deletions(-) rename src/app/core/{courses => mainmenu}/handlers/mainmenu.ts (92%) rename src/app/core/{courses => mainmenu}/pages/home/home.html (100%) rename src/app/core/{courses => mainmenu}/pages/home/home.page.module.ts (89%) rename src/app/core/{courses => mainmenu}/pages/home/home.page.ts (90%) rename src/app/core/{courses => mainmenu}/pages/home/home.scss (100%) diff --git a/src/app/core/courses/courses.module.ts b/src/app/core/courses/courses.module.ts index 38449fa0a..4dd76b82a 100644 --- a/src/app/core/courses/courses.module.ts +++ b/src/app/core/courses/courses.module.ts @@ -14,17 +14,8 @@ import { NgModule } from '@angular/core'; -import { CoreMainMenuDelegate } from '@core/mainmenu/services/delegate'; -import { CoreHomeMainMenuHandler } from './handlers/mainmenu'; - @NgModule({ imports: [], declarations: [], }) -export class CoreCoursesModule { - - constructor(mainMenuDelegate: CoreMainMenuDelegate) { - mainMenuDelegate.registerHandler(new CoreHomeMainMenuHandler()); - } - -} +export class CoreCoursesModule { } diff --git a/src/app/core/courses/handlers/mainmenu.ts b/src/app/core/mainmenu/handlers/mainmenu.ts similarity index 92% rename from src/app/core/courses/handlers/mainmenu.ts rename to src/app/core/mainmenu/handlers/mainmenu.ts index 337ddaed5..a4c2d1fad 100644 --- a/src/app/core/courses/handlers/mainmenu.ts +++ b/src/app/core/mainmenu/handlers/mainmenu.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/services/delegate'; +import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../services/delegate'; /** * Handler to add Home into main menu. @@ -51,7 +51,7 @@ export class CoreHomeMainMenuHandler implements CoreMainMenuHandler { getDisplayData(): CoreMainMenuHandlerData { return { icon: 'fa-home', - title: 'core.courses.mymoodle', + title: 'core.mainmenu.home', page: 'home', class: 'core-home-handler', }; diff --git a/src/app/core/mainmenu/lang/en.json b/src/app/core/mainmenu/lang/en.json index 4ff96fbf7..a6558e06e 100644 --- a/src/app/core/mainmenu/lang/en.json +++ b/src/app/core/mainmenu/lang/en.json @@ -1,6 +1,7 @@ { "changesite": "Change site", "help": "Help", + "home": "Home", "logout": "Log out", "website": "Website" -} \ No newline at end of file +} diff --git a/src/app/core/mainmenu/mainmenu-routing.module.ts b/src/app/core/mainmenu/mainmenu-routing.module.ts index 9633807c5..9e45e3e5e 100644 --- a/src/app/core/mainmenu/mainmenu-routing.module.ts +++ b/src/app/core/mainmenu/mainmenu-routing.module.ts @@ -25,7 +25,7 @@ const routes: Routes = [ children: [ { path: 'home', // @todo: Add this route dynamically. - loadChildren: () => import('../courses/pages/home/home.page.module').then( m => m.CoreCoursesHomePageModule), + loadChildren: () => import('./pages/home/home.page.module').then( m => m.CoreHomePageModule), }, { path: 'more', diff --git a/src/app/core/mainmenu/mainmenu.module.ts b/src/app/core/mainmenu/mainmenu.module.ts index 1cd8f561e..8e4e5fde5 100644 --- a/src/app/core/mainmenu/mainmenu.module.ts +++ b/src/app/core/mainmenu/mainmenu.module.ts @@ -18,12 +18,16 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from '@ionic/angular'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@/app/components/components.module'; -import { CoreDirectivesModule } from '@/app/directives/directives.module'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +import { CoreMainMenuDelegate } from './services/delegate'; import { CoreMainMenuRoutingModule } from './mainmenu-routing.module'; import { CoreMainMenuPage } from './pages/menu/menu.page'; import { CoreMainMenuMorePage } from './pages/more/more.page'; +import { CoreHomeMainMenuHandler } from './handlers/mainmenu'; + @NgModule({ imports: [ @@ -39,4 +43,10 @@ import { CoreMainMenuMorePage } from './pages/more/more.page'; CoreMainMenuMorePage, ], }) -export class CoreMainMenuModule {} +export class CoreMainMenuModule { + + constructor(mainMenuDelegate: CoreMainMenuDelegate) { + mainMenuDelegate.registerHandler(new CoreHomeMainMenuHandler()); + } + +} diff --git a/src/app/core/courses/pages/home/home.html b/src/app/core/mainmenu/pages/home/home.html similarity index 100% rename from src/app/core/courses/pages/home/home.html rename to src/app/core/mainmenu/pages/home/home.html diff --git a/src/app/core/courses/pages/home/home.page.module.ts b/src/app/core/mainmenu/pages/home/home.page.module.ts similarity index 89% rename from src/app/core/courses/pages/home/home.page.module.ts rename to src/app/core/mainmenu/pages/home/home.page.module.ts index 9397ce678..ef2e8f653 100644 --- a/src/app/core/courses/pages/home/home.page.module.ts +++ b/src/app/core/mainmenu/pages/home/home.page.module.ts @@ -21,12 +21,12 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCoursesHomePage } from './home.page'; +import { CoreHomePage } from './home.page'; const routes: Routes = [ { path: '', - component: CoreCoursesHomePage, + component: CoreHomePage, }, ]; @@ -40,8 +40,8 @@ const routes: Routes = [ CoreDirectivesModule, ], declarations: [ - CoreCoursesHomePage, + CoreHomePage, ], exports: [RouterModule], }) -export class CoreCoursesHomePageModule {} +export class CoreHomePageModule {} diff --git a/src/app/core/courses/pages/home/home.page.ts b/src/app/core/mainmenu/pages/home/home.page.ts similarity index 90% rename from src/app/core/courses/pages/home/home.page.ts rename to src/app/core/mainmenu/pages/home/home.page.ts index 1664c9d14..b5b2410b2 100644 --- a/src/app/core/courses/pages/home/home.page.ts +++ b/src/app/core/mainmenu/pages/home/home.page.ts @@ -18,11 +18,11 @@ import { Component, OnInit } from '@angular/core'; * Page that displays the Home. */ @Component({ - selector: 'page-core-courses-home', + selector: 'page-core-home', templateUrl: 'home.html', styleUrls: ['home.scss'], }) -export class CoreCoursesHomePage implements OnInit { +export class CoreHomePage implements OnInit { siteName = 'Hello world'; diff --git a/src/app/core/courses/pages/home/home.scss b/src/app/core/mainmenu/pages/home/home.scss similarity index 100% rename from src/app/core/courses/pages/home/home.scss rename to src/app/core/mainmenu/pages/home/home.scss diff --git a/src/app/core/mainmenu/pages/more/more.html b/src/app/core/mainmenu/pages/more/more.html index be3de959f..5edaa06e7 100644 --- a/src/app/core/mainmenu/pages/more/more.html +++ b/src/app/core/mainmenu/pages/more/more.html @@ -22,7 +22,7 @@
+ (click)="openHandler(handler)" title="{{ handler.title | translate }}" detail="true" details>

{{ handler.title | translate}}

@@ -32,47 +32,47 @@
+ [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'" class="core-moremenu-customitem" details>

{{item.label}}

+ class="core-moremenu-customitem" details>

{{item.label}}

- +

{{ 'core.scanqr' | translate }}

+ title="{{ 'core.mainmenu.website' | translate }}" details>

{{ 'core.mainmenu.website' | translate }}

+ title="{{ 'core.mainmenu.help' | translate }}" details>

{{ 'core.mainmenu.help' | translate }}

- +

{{ 'core.settings.preferences' | translate }}

- +

{{ logoutLabel | translate }}

@@ -80,7 +80,7 @@
+ title="{{ 'core.settings.appsettings' | translate }}" details>

{{ 'core.settings.appsettings' | translate }}

diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 1f1eb0b64..5b7aed812 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -468,6 +468,7 @@ "core.login.yourenteredsite": "Connect to your site", "core.mainmenu.changesite": "Change site", "core.mainmenu.help": "Help", + "core.mainmenu.home": "Home", "core.mainmenu.logout": "Log out", "core.mainmenu.website": "Website", "core.needhelp": "Need help?", From 9a3de31b63f2905b67118c2fe573ae28b0cadb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 29 Oct 2020 12:55:28 +0100 Subject: [PATCH 6/6] MOBILE-3565 resources: Add favicon --- src/assets/icon/favicon.ico | Bin 105840 -> 0 bytes src/assets/icon/favicon.png | Bin 0 -> 23531 bytes src/index.html | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/assets/icon/favicon.ico create mode 100644 src/assets/icon/favicon.png diff --git a/src/assets/icon/favicon.ico b/src/assets/icon/favicon.ico deleted file mode 100644 index 483c9647ca89f20932866fdbbd7628bb424df68e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 105840 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 literal 0 HcmV?d00001 diff --git a/src/index.html b/src/index.html index dfdd5afe6..df229b489 100644 --- a/src/index.html +++ b/src/index.html @@ -3,7 +3,7 @@ - Ionic App + Moodle App