Skip to content

Commit 2c9091c

Browse files
committed
MOBILE-4266 app: Sort some app initializers
1 parent 492fa10 commit 2c9091c

File tree

8 files changed

+399
-328
lines changed

8 files changed

+399
-328
lines changed

src/app/app.component.test.ts

Lines changed: 1 addition & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -13,104 +13,16 @@
1313
// limitations under the License.
1414

1515
import { AppComponent } from '@/app/app.component';
16-
import { CoreEvents } from '@singletons/events';
17-
import { CoreLang, CoreLangProvider } from '@services/lang';
1816

19-
import { mock, mockSingleton, renderComponent, wait } from '@/testing/utils';
20-
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
21-
import { CoreSites } from '@services/sites';
22-
import { Http } from '@singletons';
23-
import { of } from 'rxjs';
24-
import { CoreSite } from '@classes/sites/site';
25-
import { CoreUtils } from '@services/utils/utils';
17+
import { renderComponent } from '@/testing/utils';
2618

2719
describe('AppComponent', () => {
2820

29-
let langProvider: CoreLangProvider;
30-
beforeEach(() => {
31-
langProvider = mockSingleton(CoreLang, mock({ getCurrentLanguage: async () => 'en' , clearCustomStrings: () => null }));
32-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
33-
mockSingleton(Http, { get: () => of(null as any) });
34-
});
35-
3621
it('should render', async () => {
3722
const fixture = await renderComponent(AppComponent);
3823

3924
expect(fixture.debugElement.componentInstance).toBeTruthy();
4025
expect(fixture.nativeElement.querySelector('ion-router-outlet')).toBeTruthy();
4126
});
4227

43-
it('cleans up on logout', async () => {
44-
const fixture = await renderComponent(AppComponent);
45-
const navigator: CoreNavigatorService = mockSingleton(CoreNavigator, ['navigate']);
46-
47-
fixture.componentInstance.ngOnInit();
48-
CoreEvents.trigger(CoreEvents.LOGOUT);
49-
50-
expect(langProvider.clearCustomStrings).toHaveBeenCalled();
51-
expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true });
52-
});
53-
54-
it('adds ionic platform and theme classes', async () => {
55-
const fixture = await renderComponent(AppComponent);
56-
const siteUrl = 'https://campus.example.edu';
57-
const themeName = 'mytheme';
58-
const themeName2 = 'anothertheme';
59-
60-
fixture.componentInstance.ngOnInit();
61-
62-
expect(document.documentElement.classList.contains('ionic7')).toBe(true);
63-
64-
const site = mock(new CoreSite('42', siteUrl, 'token', { info: {
65-
sitename: 'Example Campus',
66-
username: 'admin',
67-
firstname: 'Admin',
68-
lastname: 'User',
69-
fullname: 'Admin User',
70-
lang: 'en',
71-
userid: 1,
72-
siteurl: siteUrl,
73-
userpictureurl: '',
74-
theme: themeName,
75-
functions: [],
76-
} }));
77-
78-
mockSingleton(CoreSites, {
79-
getSite: () => Promise.resolve(site),
80-
getCurrentSiteId: () => '42',
81-
});
82-
83-
CoreEvents.trigger(CoreEvents.LOGIN, {}, '42');
84-
// Wait the event to be processed.
85-
await wait(50);
86-
87-
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(true);
88-
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
89-
90-
if (site.infos) {
91-
site.infos.theme = themeName2;
92-
}
93-
94-
CoreEvents.trigger(CoreEvents.SITE_UPDATED, site.infos , '42');
95-
96-
// Wait the event to be processed.
97-
await CoreUtils.nextTick();
98-
99-
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
100-
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
101-
102-
CoreEvents.trigger(CoreEvents.LOGOUT);
103-
104-
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
105-
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
106-
107-
CoreEvents.trigger(CoreEvents.SITE_ADDED, site.infos , '42');
108-
109-
// Wait the event to be processed.
110-
await CoreUtils.nextTick();
111-
112-
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
113-
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
114-
});
115-
11628
});

src/app/app.component.ts

Lines changed: 2 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,19 @@
1414

1515
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
1616
import { IonRouterOutlet } from '@ionic/angular';
17-
import { BackButtonEvent, ScrollDetail } from '@ionic/core';
17+
import { BackButtonEvent } from '@ionic/core';
1818

19-
import { CoreLang } from '@services/lang';
2019
import { CoreLoginHelper } from '@features/login/services/login-helper';
21-
import { CoreEvents } from '@singletons/events';
22-
import { NgZone, SplashScreen } from '@singletons';
23-
import { CoreNetwork } from '@services/network';
20+
import { SplashScreen } from '@singletons';
2421
import { CoreApp } from '@services/app';
25-
import { CoreSites } from '@services/sites';
2622
import { CoreNavigator } from '@services/navigator';
2723
import { CoreSubscriptions } from '@singletons/subscriptions';
2824
import { CoreWindow } from '@singletons/window';
2925
import { CoreUtils } from '@services/utils/utils';
30-
import { CoreConstants } from '@/core/constants';
31-
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
32-
import { CoreDomUtils } from '@services/utils/dom';
33-
import { CoreDom } from '@singletons/dom';
3426
import { CorePlatform } from '@services/platform';
35-
import { CoreUrl } from '@singletons/url';
3627
import { CoreLogger } from '@singletons/logger';
3728
import { CorePromisedValue } from '@classes/promised-value';
3829
import { register } from 'swiper/element/bundle';
39-
import { CoreSiteInfo, CoreSiteInfoResponse } from '@classes/sites/unauthenticated-site';
40-
41-
const MOODLE_SITE_URL_PREFIX = 'url-';
42-
const MOODLE_VERSION_PREFIX = 'version-';
43-
const MOODLEAPP_VERSION_PREFIX = 'moodleapp-';
44-
const MOODLE_SITE_THEME_PREFIX = 'theme-site-';
4530

4631
register();
4732

@@ -61,43 +46,6 @@ export class AppComponent implements OnInit, AfterViewInit {
6146
ngOnInit(): void {
6247
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6348
const win = <any> window;
64-
CoreDomUtils.toggleModeClass('ionic7', true, { includeLegacy: true });
65-
CoreDomUtils.toggleModeClass('development', CoreConstants.BUILD.isDevelopment);
66-
this.addVersionClass(MOODLEAPP_VERSION_PREFIX, CoreConstants.CONFIG.versionname.replace('-dev', ''));
67-
68-
CoreEvents.on(CoreEvents.LOGOUT, async () => {
69-
// Unload lang custom strings.
70-
CoreLang.clearCustomStrings();
71-
72-
// Remove version classes from body.
73-
this.removeSiteClasses();
74-
75-
// Go to sites page when user is logged out.
76-
await CoreNavigator.navigate('/login/sites', { reset: true });
77-
78-
if (CoreSitePlugins.hasSitePluginsLoaded) {
79-
// Temporary fix. Reload the page to unload all plugins.
80-
window.location.reload();
81-
}
82-
});
83-
84-
// Listen to scroll to add style when scroll is not 0.
85-
win.addEventListener('ionScroll', async ({ detail, target }: CustomEvent<ScrollDetail>) => {
86-
if ((target as HTMLElement).tagName != 'ION-CONTENT') {
87-
return;
88-
}
89-
const content = (target as HTMLIonContentElement);
90-
91-
const page = content.closest('.ion-page');
92-
if (!page) {
93-
return;
94-
}
95-
96-
page.querySelector<HTMLIonHeaderElement>('ion-header')?.classList.toggle('core-header-shadow', detail.scrollTop > 0);
97-
98-
const scrollElement = await content.getScrollElement();
99-
content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scrollElement));
100-
});
10149

10250
CorePlatform.resume.subscribe(() => {
10351
// Wait a second before setting it to false since in iOS there could be some frozen WS calls.
@@ -119,41 +67,6 @@ export class AppComponent implements OnInit, AfterViewInit {
11967
CoreWindow.open(url);
12068
};
12169

122-
CoreEvents.on(CoreEvents.LOGIN, async (data) => {
123-
if (data.siteId) {
124-
const site = await CoreSites.getSite(data.siteId);
125-
const info = site.getInfo();
126-
if (info) {
127-
this.addSiteClasses(info);
128-
}
129-
}
130-
131-
this.loadCustomStrings();
132-
});
133-
134-
// Site config is checked in login.
135-
CoreEvents.on(CoreEvents.LOGIN_SITE_CHECKED, (data) => {
136-
this.addSiteUrlClass(data.config.httpswwwroot);
137-
});
138-
139-
CoreEvents.on(CoreEvents.SITE_UPDATED, async (data) => {
140-
if (data.siteId === CoreSites.getCurrentSiteId()) {
141-
this.loadCustomStrings();
142-
143-
this.addSiteClasses(data);
144-
}
145-
});
146-
147-
CoreEvents.on(CoreEvents.SITE_ADDED, (data) => {
148-
if (data.siteId === CoreSites.getCurrentSiteId()) {
149-
this.loadCustomStrings();
150-
151-
this.addSiteClasses(data);
152-
}
153-
});
154-
155-
this.onPlatformReady();
156-
15770
// Quit app with back button.
15871
document.addEventListener('ionBackButton', (event: BackButtonEvent) => {
15972
// This callback should have the lowest priority in the app.
@@ -234,148 +147,4 @@ export class AppComponent implements OnInit, AfterViewInit {
234147
return promise;
235148
}
236149

237-
/**
238-
* Async init function on platform ready.
239-
*/
240-
protected async onPlatformReady(): Promise<void> {
241-
await CorePlatform.ready();
242-
243-
this.logger.debug('Platform is ready');
244-
245-
// Refresh online status when changes.
246-
CoreNetwork.onChange().subscribe(() => {
247-
// Execute the callback in the Angular zone, so change detection doesn't stop working.
248-
NgZone.run(() => {
249-
const isOnline = CoreNetwork.isOnline();
250-
const hadOfflineMessage = CoreDomUtils.hasModeClass('core-offline');
251-
252-
CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true });
253-
254-
if (isOnline && hadOfflineMessage) {
255-
CoreDomUtils.toggleModeClass('core-online', true, { includeLegacy: true });
256-
257-
setTimeout(() => {
258-
CoreDomUtils.toggleModeClass('core-online', false, { includeLegacy: true });
259-
}, 3000);
260-
} else if (!isOnline) {
261-
CoreDomUtils.toggleModeClass('core-online', false, { includeLegacy: true });
262-
}
263-
});
264-
});
265-
266-
const isOnline = CoreNetwork.isOnline();
267-
CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true });
268-
}
269-
270-
/**
271-
* Load custom lang strings. This cannot be done inside the lang provider because it causes circular dependencies.
272-
*/
273-
protected loadCustomStrings(): void {
274-
const currentSite = CoreSites.getCurrentSite();
275-
276-
if (currentSite) {
277-
CoreLang.loadCustomStringsFromSite(currentSite);
278-
}
279-
}
280-
281-
/**
282-
* Convenience function to add version to html classes.
283-
*
284-
* @param prefix Prefix to add to the class.
285-
* @param release Current release number of the site.
286-
*/
287-
protected addVersionClass(prefix: string, release: string): void {
288-
const parts = release.split('.', 3);
289-
290-
parts[1] = parts[1] || '0';
291-
parts[2] = parts[2] || '0';
292-
293-
CoreDomUtils.toggleModeClass(prefix + parts[0], true, { includeLegacy: true });
294-
CoreDomUtils.toggleModeClass(prefix + parts[0] + '-' + parts[1], true, { includeLegacy: true });
295-
CoreDomUtils.toggleModeClass(prefix + parts[0] + '-' + parts[1] + '-' + parts[2], true, { includeLegacy: true });
296-
}
297-
298-
/**
299-
* Convenience function to remove all mode classes form body.
300-
*
301-
* @param prefixes Prefixes of the class mode to be removed.
302-
*/
303-
protected removeModeClasses(prefixes: string[]): void {
304-
for (const modeClass of CoreDomUtils.getModeClasses()) {
305-
if (!prefixes.some((prefix) => modeClass.startsWith(prefix))) {
306-
continue;
307-
}
308-
309-
CoreDomUtils.toggleModeClass(modeClass, false, { includeLegacy: true });
310-
}
311-
}
312-
313-
/**
314-
* Convenience function to add site classes to html.
315-
*
316-
* @param siteInfo Site Info.
317-
*/
318-
protected addSiteClasses(siteInfo: CoreSiteInfo | CoreSiteInfoResponse): void {
319-
// Add version classes to html tag.
320-
this.removeSiteClasses();
321-
322-
this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(siteInfo.release || ''));
323-
this.addSiteUrlClass(siteInfo.siteurl);
324-
325-
if (siteInfo.theme) {
326-
CoreDomUtils.toggleModeClass(MOODLE_SITE_THEME_PREFIX + siteInfo.theme, true);
327-
}
328-
}
329-
330-
/**
331-
* Convenience function to remove all site mode classes form html.
332-
*/
333-
protected removeSiteClasses(): void {
334-
// Remove version classes from html tag.
335-
this.removeModeClasses(
336-
[MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX, MOODLE_SITE_THEME_PREFIX],
337-
);
338-
}
339-
340-
/**
341-
* Converts the provided URL into a CSS class that be used within the page.
342-
* This is primarily used to add the siteurl to the body tag as a CSS class.
343-
* Extracted from LMS url_to_class_name function.
344-
*
345-
* @param url Url.
346-
* @returns Class name
347-
*/
348-
protected urlToClassName(url: string): string {
349-
const parsedUrl = CoreUrl.parse(url);
350-
351-
if (!parsedUrl) {
352-
return '';
353-
}
354-
355-
let className = parsedUrl.domain?.replace(/\./g, '-') || '';
356-
357-
if (parsedUrl.port) {
358-
className += `--${parsedUrl.port}`;
359-
}
360-
if (parsedUrl.path) {
361-
const leading = new RegExp('^/+');
362-
const trailing = new RegExp('/+$');
363-
const path = parsedUrl.path.replace(leading, '').replace(trailing, '');
364-
if (path) {
365-
className += '--' + path.replace(/\//g, '-') || '';
366-
}
367-
}
368-
369-
return className;
370-
}
371-
372-
/**
373-
* Convenience function to add site url to html classes.
374-
*/
375-
protected addSiteUrlClass(siteUrl: string): void {
376-
const className = this.urlToClassName(siteUrl);
377-
378-
CoreDomUtils.toggleModeClass(MOODLE_SITE_URL_PREFIX + className, true);
379-
}
380-
381150
}

0 commit comments

Comments
 (0)