diff --git a/src/assets/storybook/sites/companylisa.ts b/src/assets/storybook/sites/companylisa.ts
new file mode 100644
index 000000000..3689523d7
--- /dev/null
+++ b/src/assets/storybook/sites/companylisa.ts
@@ -0,0 +1,32 @@
+// (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 { CoreSiteFixture } from '@/storybook/stubs/classes/site';
+
+export const companyLisaSite: CoreSiteFixture = {
+ id: 'companylisasite',
+ info: {
+ version: '2022041900',
+ sitename: 'Company',
+ username: 'lisa',
+ firstname: 'Lisa',
+ lastname: 'Díaz',
+ fullname: 'Lisa Díaz',
+ lang: 'en',
+ userid: 1,
+ siteurl: 'https://company.example.edu',
+ userpictureurl: 'https://i.pravatar.cc/300?user=companylisa',
+ functions: [],
+ },
+};
diff --git a/src/assets/storybook/sites/school.json b/src/assets/storybook/sites/school.json
deleted file mode 100644
index 1260527ba..000000000
--- a/src/assets/storybook/sites/school.json
+++ /dev/null
@@ -1 +0,0 @@
-{"id":"123456","info":{"version":"2022041900","sitename":"School","username":"barbara","firstname":"Barbara","lastname":"Gardner","fullname":"Barbara Gardner","lang":"en","userid":1,"siteurl":"https://campus.example.edu","userpictureurl":"","functions":[]}}
diff --git a/src/assets/storybook/sites/schoolbarbara.ts b/src/assets/storybook/sites/schoolbarbara.ts
new file mode 100644
index 000000000..c4fa3eda3
--- /dev/null
+++ b/src/assets/storybook/sites/schoolbarbara.ts
@@ -0,0 +1,32 @@
+// (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 { CoreSiteFixture } from '@/storybook/stubs/classes/site';
+
+export const schoolBarbaraSite: CoreSiteFixture = {
+ id: 'schoolbarbarasite',
+ info: {
+ version: '2022041900',
+ sitename: 'School',
+ username: 'barbara',
+ firstname: 'Barbara',
+ lastname: 'Gardner',
+ fullname: 'Barbara Gardner',
+ lang: 'en',
+ userid: 1,
+ siteurl: 'https://campus.example.edu',
+ userpictureurl: 'https://i.pravatar.cc/300?user=schoolbarbara',
+ functions: [],
+ },
+};
diff --git a/src/assets/storybook/sites/schooljeffery.ts b/src/assets/storybook/sites/schooljeffery.ts
new file mode 100644
index 000000000..c7823f2b0
--- /dev/null
+++ b/src/assets/storybook/sites/schooljeffery.ts
@@ -0,0 +1,32 @@
+// (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 { CoreSiteFixture } from '@/storybook/stubs/classes/site';
+
+export const schoolJefferySite: CoreSiteFixture = {
+ id: 'schooljefferysite',
+ info: {
+ version: '2022041900',
+ sitename: 'School',
+ username: 'jeffery',
+ firstname: 'Jeffery',
+ lastname: 'Sanders',
+ fullname: 'Jeffery Sanders',
+ lang: 'en',
+ userid: 2,
+ siteurl: 'https://campus.example.edu',
+ userpictureurl: 'https://i.pravatar.cc/300?user=schooljeffery',
+ functions: [],
+ },
+};
diff --git a/src/core/components/stories/components/components.module.ts b/src/core/components/stories/components/components.module.ts
index e70834d30..d0648467e 100644
--- a/src/core/components/stories/components/components.module.ts
+++ b/src/core/components/stories/components/components.module.ts
@@ -21,6 +21,8 @@ import { CoreComponentsModule } from '@components/components.module';
import { CommonModule } from '@angular/common';
import { CoreCourseImageCardsPageComponent } from '@components/stories/components/course-image-cards-page/course-image-cards-page';
import { CoreCourseImageListPageComponent } from '@components/stories/components/course-image-list-page/course-image-list-page';
+import { CoreSitesListWrapperComponent } from './sites-list-wrapper/sites-list-wrapper';
+import { CoreDirectivesModule } from '@directives/directives.module';
@NgModule({
declarations: [
@@ -28,10 +30,12 @@ import { CoreCourseImageListPageComponent } from '@components/stories/components
CoreCourseImageListPageComponent,
CoreEmptyBoxPageComponent,
CoreEmptyBoxWrapperComponent,
+ CoreSitesListWrapperComponent,
],
imports: [
CommonModule,
StorybookModule,
+ CoreDirectivesModule,
CoreComponentsModule,
CoreSearchComponentsModule,
],
diff --git a/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html b/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html
new file mode 100644
index 000000000..c51b145ec
--- /dev/null
+++ b/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
Extra text for user {{ site.fullname }}
+ {{ site.badge }} MB
+
+
+
+
+
+
+
+
+ {{site.badge}}
+
+
+
+
+
+
diff --git a/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts b/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts
new file mode 100644
index 000000000..06dc02b22
--- /dev/null
+++ b/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts
@@ -0,0 +1,60 @@
+// (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, OnChanges, OnInit, SimpleChanges } from '@angular/core';
+import { CoreAccountsList, CoreLoginHelper } from '@features/login/services/login-helper';
+import { CoreSiteBasicInfo } from '@services/sites';
+
+@Component({
+ selector: 'core-sites-list-wrapper',
+ templateUrl: 'sites-list-wrapper.html',
+})
+export class CoreSitesListWrapperComponent implements OnInit, OnChanges {
+
+ @Input() sitesClickable = false;
+ @Input() currentSiteClickableSelect = 'undefined';
+ @Input() extraText: 'text' | 'badge' | 'none' = 'none';
+ @Input() extraDetails: 'delete-button' | 'badge' | 'none' = 'none';
+
+ accountsList?: CoreAccountsList;
+ currentSiteClickable?: boolean;
+
+ /**
+ * @inheritdoc
+ */
+ async ngOnInit(): Promise {
+ this.accountsList = await CoreLoginHelper.getAccountsList();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ async ngOnChanges(changes: SimpleChanges): Promise {
+ if (changes.currentSiteClickableSelect) {
+ this.currentSiteClickable = this.currentSiteClickableSelect === 'undefined' ?
+ undefined :
+ this.currentSiteClickableSelect === 'true';
+ }
+ }
+
+ /**
+ * Site clicked.
+ *
+ * @param site Site.
+ */
+ siteClicked(site: CoreSiteBasicInfo): void {
+ alert(`clicked on ${site.id} - ${site.fullname}`);
+ }
+
+}
diff --git a/src/core/components/stories/sites-list.stories.ts b/src/core/components/stories/sites-list.stories.ts
new file mode 100644
index 000000000..0fa75f7e5
--- /dev/null
+++ b/src/core/components/stories/sites-list.stories.ts
@@ -0,0 +1,78 @@
+// (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 { Meta, moduleMetadata } from '@storybook/angular';
+
+import { story } from '@/storybook/utils/helpers';
+import { CoreSitesListComponent } from '@components/sites-list/sites-list';
+import { CoreSitesListWrapperComponent } from './components/sites-list-wrapper/sites-list-wrapper';
+import { CoreComponentsStorybookModule } from './components/components.module';
+
+interface Args {
+ sitesClickable: boolean;
+ currentSiteClickable: 'true' | 'false' | 'undefined';
+ extraText: 'text' | 'badge' | 'none';
+ extraDetails: 'delete-button' | 'badge' | 'none';
+}
+
+export default > {
+ title: 'Core/Sites List',
+ component: CoreSitesListComponent,
+ decorators: [
+ moduleMetadata({ imports: [CoreComponentsStorybookModule] }),
+ ],
+ argTypes: {
+ sitesClickable: {
+ control: {
+ type: 'boolean',
+ },
+ },
+ currentSiteClickable: {
+ control: {
+ type: 'select',
+ options: ['true', 'false', 'undefined'],
+ },
+ },
+ extraText: {
+ control: {
+ type: 'select',
+ options: ['text', 'badge', 'none'],
+ },
+ },
+ extraDetails: {
+ control: {
+ type: 'select',
+ options: ['delete-button', 'badge', 'none'],
+ },
+ },
+ },
+ args: {
+ sitesClickable: false,
+ currentSiteClickable: 'undefined',
+ extraText: 'none',
+ extraDetails: 'none',
+ },
+};
+
+const Template = story(({ sitesClickable, currentSiteClickable, extraText, extraDetails }) => ({
+ component: CoreSitesListWrapperComponent,
+ props: {
+ sitesClickable,
+ currentSiteClickableSelect: currentSiteClickable,
+ extraText,
+ extraDetails,
+ },
+}));
+
+export const Primary = story(Template);
diff --git a/src/core/features/login/components/sites-modal/sites-modal.ts b/src/core/features/login/components/sites-modal/sites-modal.ts
index ca3ed546d..e938c4f7e 100644
--- a/src/core/features/login/components/sites-modal/sites-modal.ts
+++ b/src/core/features/login/components/sites-modal/sites-modal.ts
@@ -50,7 +50,7 @@ export class CoreLoginSitesModalComponent implements OnInit {
* @inheritdoc
*/
async ngOnInit(): Promise {
- this.accountsList = await CoreLoginHelper.getAccountsList(this.currentSiteId);
+ this.accountsList = await CoreLoginHelper.getAccountsList();
this.loaded = true;
}
diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts
index d2b1abf64..7d003b67a 100644
--- a/src/core/features/login/services/login-helper.ts
+++ b/src/core/features/login/services/login-helper.ts
@@ -1301,10 +1301,9 @@ export class CoreLoginHelperProvider {
/**
* Get the accounts list classified per site.
*
- * @param currentSiteId If loggedin, current Site Id.
* @returns Promise resolved with account list.
*/
- async getAccountsList(currentSiteId?: string): Promise {
+ async getAccountsList(): Promise {
const sites = await CoreUtils.ignoreErrors(CoreSites.getSortedSites(), [] as CoreSiteBasicInfo[]);
const accountsList: CoreAccountsList = {
@@ -1312,14 +1311,11 @@ export class CoreLoginHelperProvider {
otherSites: [],
count: sites.length,
};
-
+ const currentSiteId = CoreSites.getCurrentSiteId();
let siteUrl = '';
if (currentSiteId) {
- const index = sites.findIndex((site) => site.id == currentSiteId);
-
- accountsList.currentSite = sites.splice(index, 1)[0];
- siteUrl = accountsList.currentSite.siteUrlWithoutProtocol;
+ siteUrl = sites.find((site) => site.id == currentSiteId)?.siteUrlWithoutProtocol ?? '';
}
const otherSites: Record = {};
@@ -1328,7 +1324,9 @@ export class CoreLoginHelperProvider {
await Promise.all(sites.map(async (site) => {
site.badge = await CoreUtils.ignoreErrors(CorePushNotifications.getSiteCounter(site.id)) || 0;
- if (site.siteUrlWithoutProtocol == siteUrl) {
+ if (site.id === currentSiteId) {
+ accountsList.currentSite = site;
+ } else if (site.siteUrlWithoutProtocol == siteUrl) {
accountsList.sameSite.push(site);
} else {
if (!otherSites[site.siteUrlWithoutProtocol]) {
diff --git a/src/core/features/settings/pages/synchronization/synchronization.ts b/src/core/features/settings/pages/synchronization/synchronization.ts
index 513885173..ad04dc355 100644
--- a/src/core/features/settings/pages/synchronization/synchronization.ts
+++ b/src/core/features/settings/pages/synchronization/synchronization.ts
@@ -104,10 +104,8 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
* @inheritdoc
*/
async ngOnInit(): Promise {
- const currentSiteId = CoreSites.getCurrentSiteId();
-
try {
- this.accountsList = await CoreLoginHelper.getAccountsList(currentSiteId);
+ this.accountsList = await CoreLoginHelper.getAccountsList();
} catch {
// Ignore errors.
}
diff --git a/src/core/features/sharedfiles/pages/choose-site/choose-site.ts b/src/core/features/sharedfiles/pages/choose-site/choose-site.ts
index 115e190de..84407e809 100644
--- a/src/core/features/sharedfiles/pages/choose-site/choose-site.ts
+++ b/src/core/features/sharedfiles/pages/choose-site/choose-site.ts
@@ -18,7 +18,7 @@ import { CoreSharedFilesHelper } from '@features/sharedfiles/services/sharedfile
import { FileEntry } from '@ionic-native/file/ngx';
import { CoreFile } from '@services/file';
import { CoreNavigator } from '@services/navigator';
-import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
+import { CoreSiteBasicInfo } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
/**
@@ -94,7 +94,7 @@ export class CoreSharedFilesChooseSitePage implements OnInit {
* @returns Promise resolved when done.
*/
protected async loadSites(): Promise {
- this.accountsList = await CoreLoginHelper.getAccountsList(CoreSites.getCurrentSiteId());
+ this.accountsList = await CoreLoginHelper.getAccountsList();
}
/**
diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts
index 559069759..e9fc6888b 100644
--- a/src/core/services/sites.ts
+++ b/src/core/services/sites.ts
@@ -1300,7 +1300,19 @@ export class CoreSitesProvider {
async getSites(ids?: string[]): Promise {
const sites = await this.sitesTable.getMany();
+ return this.siteDBRecordsToBasicInfo(sites, ids);
+ }
+
+ /**
+ * Convert sites DB records to site basic info.
+ *
+ * @param sites DB records.
+ * @param ids IDs of sites to return, undefined to return them all.
+ * @returns Sites basic info.
+ */
+ protected siteDBRecordsToBasicInfo(sites: SiteDBEntry[], ids?: string[]): CoreSiteBasicInfo[] {
const formattedSites: CoreSiteBasicInfo[] = [];
+
sites.forEach((site) => {
if (!ids || ids.indexOf(site.id) > -1) {
const isDemoModeSite = CoreLoginHelper.isDemoModeSite(site.siteUrl);
diff --git a/src/storybook/storybook.module.ts b/src/storybook/storybook.module.ts
index f4bf199c6..b8aa614d7 100644
--- a/src/storybook/storybook.module.ts
+++ b/src/storybook/storybook.module.ts
@@ -28,6 +28,8 @@ import { CoreFilepoolProviderStub } from '@/storybook/stubs/services/filepool';
import { CoreFilepoolProvider } from '@services/filepool';
import { HttpClientStub } from '@/storybook/stubs/services/http';
import { HttpClient } from '@angular/common/http';
+import { CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications';
+import { CorePushNotificationsProviderStub } from './stubs/services/pushnotifications';
// For translate loader. AoT requires an exported function for factories.
export class StaticTranslateLoader extends TranslateLoader {
@@ -56,6 +58,7 @@ export class StaticTranslateLoader extends TranslateLoader {
{ provide: CoreSitesProvider, useClass: CoreSitesProviderStub },
{ provide: CoreDbProvider, useClass: CoreDbProviderStub },
{ provide: CoreFilepoolProvider, useClass: CoreFilepoolProviderStub },
+ { provide: CorePushNotificationsProvider, useClass: CorePushNotificationsProviderStub },
{ provide: HttpClient, useClass: HttpClientStub },
{
provide: APP_INITIALIZER,
diff --git a/src/storybook/stubs/services/pushnotifications.ts b/src/storybook/stubs/services/pushnotifications.ts
new file mode 100644
index 000000000..9d380c32c
--- /dev/null
+++ b/src/storybook/stubs/services/pushnotifications.ts
@@ -0,0 +1,32 @@
+// (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 { CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications';
+import { makeSingleton } from '@singletons';
+
+/**
+ * Sites provider stub.
+ */
+export class CorePushNotificationsProviderStub extends CorePushNotificationsProvider {
+
+ /**
+ * @inheritdoc
+ */
+ async getSiteCounter(): Promise {
+ return Math.round(Math.random() * 100);
+ }
+
+}
+
+export const CorePushNotificationsStub = makeSingleton(CorePushNotificationsProvider);
diff --git a/src/storybook/stubs/services/sites.ts b/src/storybook/stubs/services/sites.ts
index 25f32541a..8fa901a92 100644
--- a/src/storybook/stubs/services/sites.ts
+++ b/src/storybook/stubs/services/sites.ts
@@ -12,9 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import school from '@/assets/storybook/sites/school.json';
+import { companyLisaSite } from '@/assets/storybook/sites/companylisa';
+import { schoolBarbaraSite } from '@/assets/storybook/sites/schoolbarbara';
+import { schoolJefferySite } from '@/assets/storybook/sites/schooljeffery';
import { CoreSiteFixture, CoreSiteStub } from '@/storybook/stubs/classes/site';
-import { CoreSitesProvider } from '@services/sites';
+import { CoreError } from '@classes/errors/error';
+import { CoreSite } from '@classes/site';
+import { SiteDBEntry } from '@services/database/sites';
+import { CoreSiteBasicInfo, CoreSitesProvider } from '@services/sites';
import { makeSingleton } from '@singletons';
/**
@@ -22,17 +27,55 @@ import { makeSingleton } from '@singletons';
*/
export class CoreSitesProviderStub extends CoreSitesProvider {
+ protected static readonly SITES_FIXTURES = [schoolBarbaraSite, schoolJefferySite, companyLisaSite];
+
/**
* @inheritdoc
*/
getRequiredCurrentSite!: () => CoreSiteStub;
+ /**
+ * @inheritdoc
+ */
+ async getSites(ids?: string[]): Promise {
+ const sites = CoreSitesProviderStub.SITES_FIXTURES.map(site => ( {
+ id: site.id,
+ siteUrl: site.info.siteurl,
+ info: JSON.stringify(site.info),
+ token: '',
+ privateToken: '',
+ loggedOut: 0,
+ }));
+
+ return this.siteDBRecordsToBasicInfo(sites, ids);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ async getSite(siteId?: string): Promise {
+ if (!siteId) {
+ if (this.currentSite) {
+ return this.currentSite;
+ }
+
+ throw new CoreError('No current site found.');
+ }
+
+ const siteFixture = CoreSitesProviderStub.SITES_FIXTURES.find(site => site.id === siteId);
+ if (!siteFixture) {
+ throw new CoreError('SiteId not found.');
+ }
+
+ return new CoreSiteStub(siteFixture);
+ }
+
/**
* @inheritdoc
*/
stubCurrentSite(fixture?: CoreSiteFixture): CoreSiteStub {
if (!this.currentSite) {
- this.currentSite = new CoreSiteStub(fixture ?? school);
+ this.currentSite = new CoreSiteStub(fixture ?? schoolBarbaraSite);
}
return this.getRequiredCurrentSite();