diff --git a/src/app/core/settings/pages/app/app.html b/src/app/core/settings/pages/app/app.html
index 6ff4dbad7..c8150a9db 100644
--- a/src/app/core/settings/pages/app/app.html
+++ b/src/app/core/settings/pages/app/app.html
@@ -19,7 +19,7 @@
{{ 'core.settings.spaceusage' | translate }}
-
+
{{ 'core.settings.synchronization' | translate }}
+
+
+
+
+ {{ 'core.settings.synchronization' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'core.settings.syncsettings' | translate }}
+
+
+
+ {{ 'core.settings.enablesyncwifi' | translate }}
+
+
+
+
+
+ {{ 'core.settings.sites' | translate }}
+
+
+
+
+
+
+
+ {{ site.fullName }}
+ {{ site.siteUrl }}
+
+
+
+
+
+
+
+
diff --git a/src/app/core/settings/pages/synchronization/synchronization.page.module.ts b/src/app/core/settings/pages/synchronization/synchronization.page.module.ts
new file mode 100644
index 000000000..39a2121fe
--- /dev/null
+++ b/src/app/core/settings/pages/synchronization/synchronization.page.module.ts
@@ -0,0 +1,50 @@
+// (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 { TranslateModule } from '@ngx-translate/core';
+import { RouterModule, Routes } from '@angular/router';
+import { CommonModule } from '@angular/common';
+import { IonicModule } from '@ionic/angular';
+
+import { CoreComponentsModule } from '@components/components.module';
+import { CoreDirectivesModule } from '@directives/directives.module';
+
+import { CoreSettingsSynchronizationPage } from './synchronization.page';
+import { FormsModule } from '@angular/forms';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: CoreSettingsSynchronizationPage,
+ },
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(routes),
+ CommonModule,
+ IonicModule,
+ FormsModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ CoreDirectivesModule,
+ ],
+ declarations: [
+ CoreSettingsSynchronizationPage,
+ ],
+ exports: [RouterModule],
+})
+export class CoreSettingsSynchronizationPageModule {}
diff --git a/src/app/core/settings/pages/synchronization/synchronization.page.ts b/src/app/core/settings/pages/synchronization/synchronization.page.ts
new file mode 100644
index 000000000..19c6bf063
--- /dev/null
+++ b/src/app/core/settings/pages/synchronization/synchronization.page.ts
@@ -0,0 +1,130 @@
+// (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, OnDestroy, OnInit } from '@angular/core';
+
+import { CoreConstants } from '@core/constants';
+import { CoreEventObserver, CoreEvents, CoreEventSiteUpdatedData } from '@singletons/events';
+import { CoreSites, CoreSiteBasicInfo } from '@services/sites';
+import { CoreDomUtils } from '@services/utils/dom';
+import { CoreConfig } from '@services/config';
+import { CoreSettingsHelper } from '@core/settings/services/settings.helper';
+import { Translate } from '@singletons/core.singletons';
+
+/**
+ * Page that displays the synchronization settings.
+ */
+@Component({
+ selector: 'page-core-app-settings-synchronization',
+ templateUrl: 'synchronization.html',
+})
+export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
+
+ sites: CoreSiteBasicInfo[] = [];
+ sitesLoaded = false;
+ currentSiteId = '';
+ syncOnlyOnWifi = false;
+ protected isDestroyed = false;
+ protected sitesObserver: CoreEventObserver;
+
+ constructor() {
+
+ this.currentSiteId = CoreSites.instance.getCurrentSiteId();
+
+ this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, async (data: CoreEventSiteUpdatedData) => {
+ const site = await CoreSites.instance.getSite(data.siteId);
+
+ const siteEntry = this.sites.find((siteEntry) => siteEntry.id == site.id);
+ if (siteEntry) {
+ const siteInfo = site.getInfo();
+
+ siteEntry.siteName = site.getSiteName();
+
+ if (siteInfo) {
+ siteEntry.siteUrl = siteInfo.siteurl;
+ siteEntry.fullName = siteInfo.fullname;
+ }
+ }
+ });
+ }
+
+ /**
+ * View loaded.
+ */
+ async ngOnInit(): Promise {
+ try {
+ this.sites = await CoreSites.instance.getSortedSites();
+ } catch {
+ // Ignore errors.
+ }
+
+ this.sitesLoaded = true;
+
+ this.syncOnlyOnWifi = await CoreConfig.instance.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
+ }
+
+ /**
+ * Called when sync only on wifi setting is enabled or disabled.
+ */
+ syncOnlyOnWifiChanged(): void {
+ CoreConfig.instance.set(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, this.syncOnlyOnWifi ? 1 : 0);
+ }
+
+ /**
+ * Syncrhonizes a site.
+ *
+ * @param siteId Site ID.
+ */
+ async synchronize(siteId: string): Promise {
+ // Using syncOnlyOnWifi false to force manual sync.
+ try {
+ await CoreSettingsHelper.instance.synchronizeSite(false, siteId);
+ } catch (error) {
+ if (this.isDestroyed) {
+ return;
+ }
+
+ CoreDomUtils.instance.showErrorModalDefault(error, 'core.settings.errorsyncsite', true);
+ }
+ }
+
+ /**
+ * Returns true if site is beeing synchronized.
+ *
+ * @param siteId Site ID.
+ * @return True if site is beeing synchronized, false otherwise.
+ */
+ isSynchronizing(siteId: string): boolean {
+ return !!CoreSettingsHelper.instance.getSiteSyncPromise(siteId);
+ }
+
+ /**
+ * Show information about sync actions.
+ */
+ showInfo(): void {
+ CoreDomUtils.instance.showAlert(
+ Translate.instance.instant('core.help'),
+ Translate.instance.instant('core.settings.synchronizenowhelp'),
+ );
+ }
+
+ /**
+ * Page destroyed.
+ */
+ ngOnDestroy(): void {
+ this.isDestroyed = true;
+ this.sitesObserver?.off();
+ }
+
+}
diff --git a/src/app/core/settings/services/settings.helper.ts b/src/app/core/settings/services/settings.helper.ts
index 736ac57a2..dc6094397 100644
--- a/src/app/core/settings/services/settings.helper.ts
+++ b/src/app/core/settings/services/settings.helper.ts
@@ -257,7 +257,7 @@ export class CoreSettingsHelperProvider {
* @param siteId ID of the site.
* @return Sync promise or null if site is not being syncrhonized.
*/
- async getSiteSyncPromise(siteId: string): Promise {
+ getSiteSyncPromise(siteId: string): Promise | void {
if (this.syncPromises[siteId]) {
return this.syncPromises[siteId];
}
diff --git a/src/app/core/settings/settings-routing.module.ts b/src/app/core/settings/settings-routing.module.ts
index 66bb7de18..5c0508893 100644
--- a/src/app/core/settings/settings-routing.module.ts
+++ b/src/app/core/settings/settings-routing.module.ts
@@ -30,6 +30,12 @@ const routes: Routes = [
import('@core/settings/pages/space-usage/space-usage.page.module')
.then(m => m.CoreSettingsSpaceUsagePageModule),
},
+ {
+ path: 'sync',
+ loadChildren: () =>
+ import('@core/settings/pages/synchronization/synchronization.page.module')
+ .then(m => m.CoreSettingsSynchronizationPageModule),
+ },
{
path: '',
loadChildren: () => import('./pages/app/app.page.module').then( m => m.CoreSettingsAppPageModule),
diff --git a/src/theme/variables.scss b/src/theme/variables.scss
index c9e862cbd..20a65148c 100644
--- a/src/theme/variables.scss
+++ b/src/theme/variables.scss
@@ -131,6 +131,10 @@
}
}
+ ion-spinner {
+ --color: var(--core-color);
+ }
+
--selected-item-color: var(--custom-selected-item-color, var(--core-color));
--selected-item-border-width: var(--custom-selected-item-border-width, 5px);