diff --git a/package-lock.json b/package-lock.json index 14d051271..8753828fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,7 @@ "hammerjs": "2.0.8", "jszip": "3.7.1", "mathjax": "2.7.7", - "moment": "2.29.0", + "moment": "2.29.2", "nl.kingsquare.cordova.background-audio": "1.0.1", "rxjs": "6.5.5", "ts-md5": "1.2.7", @@ -20984,9 +20984,9 @@ "optional": true }, "node_modules/moment": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz", - "integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA==", + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==", "engines": { "node": "*" } @@ -47321,9 +47321,9 @@ "optional": true }, "moment": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz", - "integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA==" + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==" }, "move-concurrently": { "version": "1.0.1", diff --git a/package.json b/package.json index 712a8cdc3..3db385576 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "hammerjs": "2.0.8", "jszip": "3.7.1", "mathjax": "2.7.7", - "moment": "2.29.0", + "moment": "2.29.2", "nl.kingsquare.cordova.background-audio": "1.0.1", "rxjs": "6.5.5", "ts-md5": "1.2.7", diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html index 1dbc553e9..34c075f39 100644 --- a/src/addons/mod/forum/components/index/index.html +++ b/src/addons/mod/forum/components/index/index.html @@ -56,6 +56,8 @@ +

diff --git a/src/addons/mod/forum/components/index/index.scss b/src/addons/mod/forum/components/index/index.scss index d07dae27d..4fb44d094 100644 --- a/src/addons/mod/forum/components/index/index.scss +++ b/src/addons/mod/forum/components/index/index.scss @@ -18,6 +18,11 @@ ion-icon { @include margin(0, 6px, 0, 0); + + &.addon-mod-forum-locked-icon { + @include margin-horizontal(4px, 0px); + color: var(--gray-500); + } } } diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.html b/src/addons/mod/forum/components/post-options-menu/post-options-menu.html index ee0955932..2ff947373 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.html +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.html @@ -12,11 +12,6 @@

{{ 'core.discard' | translate }}

- - -

{{ 'core.numwords' | translate: {'$a': wordCount} }}

-
-
diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts index ad13a1e84..257d8ec78 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts @@ -34,7 +34,6 @@ export class AddonModForumPostOptionsMenuComponent implements OnInit, OnDestroy @Input() cmId!: number; @Input() forumId!: number; // The forum Id. - wordCount?: number | null; // Number of words when available. canEdit = false; canDelete = false; loaded = false; @@ -89,7 +88,6 @@ export class AddonModForumPostOptionsMenuComponent implements OnInit, OnDestroy this.canDelete = !!this.post.capabilities.delete && AddonModForum.isDeletePostAvailable(); this.canEdit = !!this.post.capabilities.edit && AddonModForum.isUpdatePostAvailable(); - this.wordCount = (this.post.haswordcount && this.post.wordcount) || null; this.loaded = true; } diff --git a/src/addons/mod/forum/components/post/post.html b/src/addons/mod/forum/components/post/post.html index 9812ff26f..ab1f83cfe 100644 --- a/src/addons/mod/forum/components/post/post.html +++ b/src/addons/mod/forum/components/post/post.html @@ -63,6 +63,9 @@ +

+ {{ 'core.numwords' | translate: {'$a': post.wordcount} }} +

diff --git a/src/core/features/mainmenu/mainmenu.module.ts b/src/core/features/mainmenu/mainmenu.module.ts index ae4136604..3ff9bf494 100644 --- a/src/core/features/mainmenu/mainmenu.module.ts +++ b/src/core/features/mainmenu/mainmenu.module.ts @@ -41,6 +41,10 @@ const appRoutes: Routes = [ canActivate: [CoreMainMenuAuthGuard], canLoad: [CoreMainMenuAuthGuard], }, + { + path: 'reload', + loadChildren: () => import('./pages/reload/reload.module').then( m => m.CoreMainMenuReloadPageModule), + }, ]; @NgModule({ diff --git a/src/core/features/mainmenu/pages/menu/menu.ts b/src/core/features/mainmenu/pages/menu/menu.ts index 35b0edd55..bf6b92219 100644 --- a/src/core/features/mainmenu/pages/menu/menu.ts +++ b/src/core/features/mainmenu/pages/menu/menu.ts @@ -30,6 +30,7 @@ import { NavigationEnd } from '@angular/router'; import { trigger, state, style, transition, animate } from '@angular/animations'; import { CoreSites } from '@services/sites'; import { CoreDom } from '@singletons/dom'; +import { CoreLogger } from '@singletons/logger'; const ANIMATION_DURATION = 500; @@ -84,6 +85,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { protected urlToOpen?: string; protected redirectPath?: string; protected redirectOptions?: CoreNavigationOptions; + protected logger: CoreLogger; @ViewChild('mainTabs') mainTabs?: IonTabs; @@ -92,6 +94,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { constructor() { this.backButtonFunction = this.backButtonClicked.bind(this); this.tabAction = new CoreMainMenuRoleTab(this); + this.logger = CoreLogger.getInstance('CoreMainMenuPage'); // Listen navigation events to show or hide tabs. this.navSubscription = Router.events @@ -193,6 +196,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { const tabPage = this.tabs[0] ? this.tabs[0].page : this.morePageName; const tabPageParams = this.tabs[0] ? this.tabs[0].pageParams : {}; + this.logger.debug(`Select first tab: ${tabPage}.`, this.tabs); // Use navigate instead of mainTabs.select to be able to pass page params. CoreNavigator.navigate(tabPage, { diff --git a/src/core/features/mainmenu/pages/reload/reload.html b/src/core/features/mainmenu/pages/reload/reload.html new file mode 100644 index 000000000..5e5ea6d14 --- /dev/null +++ b/src/core/features/mainmenu/pages/reload/reload.html @@ -0,0 +1,3 @@ + + + diff --git a/src/core/features/mainmenu/pages/reload/reload.module.ts b/src/core/features/mainmenu/pages/reload/reload.module.ts new file mode 100644 index 000000000..74a1fb7aa --- /dev/null +++ b/src/core/features/mainmenu/pages/reload/reload.module.ts @@ -0,0 +1,37 @@ +// (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 { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { CoreSharedModule } from '@/core/shared.module'; +import { CoreMainMenuReloadPage } from './reload'; + +const routes: Routes = [ + { + path: '', + component: CoreMainMenuReloadPage, + }, +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes), + CoreSharedModule, + ], + declarations: [ + CoreMainMenuReloadPage, + ], + exports: [RouterModule], +}) +export class CoreMainMenuReloadPageModule {} diff --git a/src/core/features/mainmenu/pages/reload/reload.ts b/src/core/features/mainmenu/pages/reload/reload.ts new file mode 100644 index 000000000..9a9b52a2f --- /dev/null +++ b/src/core/features/mainmenu/pages/reload/reload.ts @@ -0,0 +1,36 @@ +// (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, OnInit } from '@angular/core'; +import { CoreNavigator } from '@services/navigator'; + +/** + * Page that displays a loading and then opens the main menu again. + */ +@Component({ + selector: 'page-core-mainmenu-reload', + templateUrl: 'reload.html', +}) +export class CoreMainMenuReloadPage implements OnInit { + + /** + * @inheritdoc + */ + ngOnInit(): void { + CoreNavigator.navigate('/main', { + reset: true, + }); + } + +} diff --git a/src/core/features/settings/pages/general/general.ts b/src/core/features/settings/pages/general/general.ts index a1487f255..9db912420 100644 --- a/src/core/features/settings/pages/general/general.ts +++ b/src/core/features/settings/pages/general/general.ts @@ -26,6 +26,7 @@ import { Diagnostic, Translate } from '@singletons'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { AlertButton } from '@ionic/angular'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays the general settings. @@ -168,7 +169,10 @@ export class CoreSettingsGeneralPage { await CoreUtils.ignoreErrors(Promise.all(sites.map((site) => site.invalidateWsCache()))); CoreEvents.trigger(CoreEvents.LANGUAGE_CHANGED, this.selectedLanguage); - window.location.reload(); + + CoreNavigator.navigate('/reload', { + reset: true, + }); } /** diff --git a/src/core/services/utils/time.ts b/src/core/services/utils/time.ts index a771a6a21..446181a79 100644 --- a/src/core/services/utils/time.ts +++ b/src/core/services/utils/time.ts @@ -76,7 +76,7 @@ export class CoreTimeUtilsProvider { moment.relativeTimeThreshold('s', 60); moment.relativeTimeThreshold('m', 60); moment.relativeTimeThreshold('h', 24); - moment.relativeTimeThreshold('d', 31); + moment.relativeTimeThreshold('d', 30); moment.relativeTimeThreshold('M', 12); moment.relativeTimeThreshold('y', 365); moment.relativeTimeThreshold('ss', 0); // To display exact number of seconds instead of just "a few seconds". diff --git a/src/core/singletons/time.ts b/src/core/singletons/time.ts index e3a0f0435..6b222937b 100644 --- a/src/core/singletons/time.ts +++ b/src/core/singletons/time.ts @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import moment from 'moment'; +import { Translate } from '@singletons'; +import { CoreConstants } from '../constants'; /** * Singleton with helper functions for time operations. @@ -27,37 +28,54 @@ export class CoreTime { * @return Seconds in a human readable format. */ static formatTime(seconds: number, precision = 2): string { - precision = precision || 6; // Use max precision if 0 is passed. + precision = precision || 5; // Use max precision if 0 is passed. - const eventDuration = moment.duration(Math.abs(seconds), 'seconds'); - let durationString = ''; + const totalSecs = Math.abs(seconds); + if (!totalSecs) { + return Translate.instant('core.now'); + } - if (precision && eventDuration.years() > 0) { - durationString += ' ' + moment.duration(eventDuration.years(), 'years').humanize(); + const years = Math.floor(totalSecs / CoreConstants.SECONDS_YEAR); + let remainder = totalSecs - (years * CoreConstants.SECONDS_YEAR); + const days = Math.floor(remainder / CoreConstants.SECONDS_DAY); + + remainder = totalSecs - (days * CoreConstants.SECONDS_DAY); + + const hours = Math.floor(remainder / CoreConstants.SECONDS_HOUR); + remainder = remainder - (hours * CoreConstants.SECONDS_HOUR); + + const mins = Math.floor(remainder / CoreConstants.SECONDS_MINUTE); + const secs = remainder - (mins * CoreConstants.SECONDS_MINUTE); + + const secondsUnit = Translate.instant('core.' + (secs === 1 ? 'sec' : 'secs')); + const minutesUnit = Translate.instant('core.' + (mins === 1 ? 'min' : 'mins')); + const hoursUnit = Translate.instant('core.' + (hours === 1 ? 'hour' : 'hours')); + const daysUnit = Translate.instant('core.' + (days === 1 ? 'day' : 'days')); + const yearsUnit = Translate.instant('core.' + (years === 1 ? 'year' : 'years')); + const parts: string[] = []; + + if (precision && years) { + parts.push(`${years} ${yearsUnit}`); precision--; } - if (precision && eventDuration.months() > 0) { - durationString += ' ' + moment.duration(eventDuration.months(), 'months').humanize(); + if (precision && days) { + parts.push(`${days} ${daysUnit}`); precision--; } - if (precision && eventDuration.days() > 0) { - durationString += ' ' + moment.duration(eventDuration.days(), 'days').humanize(); + if (precision && hours) { + parts.push(`${hours} ${hoursUnit}`); precision--; } - if (precision && eventDuration.hours() > 0) { - durationString += ' ' + moment.duration(eventDuration.hours(), 'hours').humanize(); + if (precision && mins) { + parts.push(`${mins} ${minutesUnit}`); precision--; } - if (precision && eventDuration.minutes() > 0) { - durationString += ' ' + moment.duration(eventDuration.minutes(), 'minutes').humanize(); - precision--; - } - if (precision && (eventDuration.seconds() > 0 || !durationString)) { - durationString += ' ' + moment.duration(eventDuration.seconds(), 'seconds').humanize(); + if (precision && secs) { + parts.push(`${secs} ${secondsUnit}`); precision--; } - return durationString.trim(); + return parts.join(' '); } /**