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/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/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/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/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 79% rename from src/app/core/courses/pages/home/home.html rename to src/app/core/mainmenu/pages/home/home.html index 79b79100d..2b428a553 100644 --- a/src/app/core/courses/pages/home/home.html +++ b/src/app/core/mainmenu/pages/home/home.html @@ -16,5 +16,7 @@ - Home page. - \ No newline at end of file + +
Home page
+
+ 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/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/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/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/app/lang/en.json b/src/app/lang/en.json index 00ce91b93..bd3da88ca 100644 --- a/src/app/lang/en.json +++ b/src/app/lang/en.json @@ -1,11 +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/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..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'; @@ -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()) { @@ -575,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. * @@ -655,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'); } /** @@ -1245,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]; @@ -1371,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/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/assets/icon/favicon.ico b/src/assets/icon/favicon.ico deleted file mode 100644 index 483c9647c..000000000 Binary files a/src/assets/icon/favicon.ico and /dev/null differ diff --git a/src/assets/icon/favicon.png b/src/assets/icon/favicon.png new file mode 100644 index 000000000..7abfc0a4f Binary files /dev/null and b/src/assets/icon/favicon.png differ diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 7eeea14b8..5b7aed812 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", @@ -340,6 +343,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", @@ -464,8 +468,11 @@ "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?", + "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", @@ -541,6 +548,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/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 diff --git a/src/theme/app.scss b/src/theme/app.scss index 033a25fa9..6a8b5c921 100644 --- a/src/theme/app.scss +++ b/src/theme/app.scss @@ -1,6 +1,72 @@ -// Add here base app styles. +// Ionic toolbar. ion-toolbar ion-back-button, ion-toolbar .in-toolbar.button-clear { --color: var(--ion-color-primary-contrast); } + +// Ionic icon. +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); +} + +// 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; +} 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;