forked from EVOgeek/Vmeda.Online
		
	MOBILE-3634 remotethemes: Support remote themes
This commit is contained in:
		
							parent
							
								
									468400b13c
								
							
						
					
					
						commit
						a3f4fa2234
					
				| @ -27,6 +27,7 @@ import { AddonModModule } from './mod/mod.module'; | |||||||
| import { AddonQbehaviourModule } from './qbehaviour/qbehaviour.module'; | import { AddonQbehaviourModule } from './qbehaviour/qbehaviour.module'; | ||||||
| import { AddonQtypeModule } from './qtype/qtype.module'; | import { AddonQtypeModule } from './qtype/qtype.module'; | ||||||
| import { AddonBlogModule } from './blog/blog.module'; | import { AddonBlogModule } from './blog/blog.module'; | ||||||
|  | import { AddonRemoteThemesModule } from './remotethemes/remotethemes.module'; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     imports: [ |     imports: [ | ||||||
| @ -43,6 +44,7 @@ import { AddonBlogModule } from './blog/blog.module'; | |||||||
|         AddonModModule, |         AddonModModule, | ||||||
|         AddonQbehaviourModule, |         AddonQbehaviourModule, | ||||||
|         AddonQtypeModule, |         AddonQtypeModule, | ||||||
|  |         AddonRemoteThemesModule, | ||||||
|     ], |     ], | ||||||
| }) | }) | ||||||
| export class AddonsModule {} | export class AddonsModule {} | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								src/addons/remotethemes/remotethemes.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/addons/remotethemes/remotethemes.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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 { APP_INITIALIZER, NgModule, Type } from '@angular/core'; | ||||||
|  | import { AddonRemoteThemes, AddonRemoteThemesProvider } from './services/remotethemes'; | ||||||
|  | 
 | ||||||
|  | // List of providers (without handlers).
 | ||||||
|  | export const ADDON_REMOTETHEMES_SERVICES: Type<unknown>[] = [ | ||||||
|  |     AddonRemoteThemesProvider, | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | @NgModule({ | ||||||
|  |     declarations: [], | ||||||
|  |     imports: [], | ||||||
|  |     providers: [ | ||||||
|  |         { | ||||||
|  |             provide: APP_INITIALIZER, | ||||||
|  |             multi: true, | ||||||
|  |             deps: [], | ||||||
|  |             useFactory: () => async () => { | ||||||
|  |                 await AddonRemoteThemes.initialize(); | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|  |     ], | ||||||
|  | }) | ||||||
|  | export class AddonRemoteThemesModule {} | ||||||
							
								
								
									
										452
									
								
								src/addons/remotethemes/services/remotethemes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										452
									
								
								src/addons/remotethemes/services/remotethemes.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,452 @@ | |||||||
|  | // (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 { Injectable } from '@angular/core'; | ||||||
|  | import { Md5 } from 'ts-md5/dist/md5'; | ||||||
|  | 
 | ||||||
|  | import { CoreConstants } from '@/core/constants'; | ||||||
|  | import { CoreError } from '@classes/errors/error'; | ||||||
|  | import { CoreSitePublicConfigResponse } from '@classes/site'; | ||||||
|  | import { CoreApp } from '@services/app'; | ||||||
|  | import { CoreFile } from '@services/file'; | ||||||
|  | import { CoreFilepool } from '@services/filepool'; | ||||||
|  | import { CoreSites } from '@services/sites'; | ||||||
|  | import { CoreUtils } from '@services/utils/utils'; | ||||||
|  | import { CoreWS } from '@services/ws'; | ||||||
|  | import { CoreLogger } from '@singletons/logger'; | ||||||
|  | import { makeSingleton } from '@singletons'; | ||||||
|  | import { CoreEvents } from '@singletons/events'; | ||||||
|  | 
 | ||||||
|  | const SEPARATOR_35 = /\/\*\*? *3\.5(\.0)? *styles? *\*\//i; // A comment like "/* 3.5 styles */".
 | ||||||
|  | const TMP_SITE_ID = 'tmpsite'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Service to handle remote themes. A remote theme is a CSS sheet stored in the site that allows customising the Mobile app. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonRemoteThemesProvider { | ||||||
|  | 
 | ||||||
|  |     static readonly COMPONENT = 'mmaRemoteStyles'; | ||||||
|  | 
 | ||||||
|  |     protected logger: CoreLogger; | ||||||
|  |     protected stylesEls: {[siteId: string]: { element: HTMLStyleElement; hash: string }} = {}; | ||||||
|  | 
 | ||||||
|  |     constructor() { | ||||||
|  |         this.logger = CoreLogger.getInstance('AddonRemoteThemesProvider'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Initialize remote themes. | ||||||
|  |      */ | ||||||
|  |     async initialize(): Promise<void> { | ||||||
|  |         this.listenEvents(); | ||||||
|  | 
 | ||||||
|  |         // Preload the current site styles first, we want this to be fast.
 | ||||||
|  |         await this.preloadCurrentSite(); | ||||||
|  | 
 | ||||||
|  |         // Preload the styles of the rest of sites.
 | ||||||
|  |         await this.preloadSites(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Listen events. | ||||||
|  |      */ | ||||||
|  |     protected listenEvents(): void { | ||||||
|  |         let addingSite: string | undefined; | ||||||
|  | 
 | ||||||
|  |         // When a new site is added to the app, add its styles.
 | ||||||
|  |         CoreEvents.on(CoreEvents.SITE_ADDED, async (data) => { | ||||||
|  |             addingSite = data.siteId; | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 await this.addSite(data.siteId); | ||||||
|  | 
 | ||||||
|  |                 if (addingSite == data.siteId) { | ||||||
|  |                     addingSite = undefined; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // User has logged in, remove tmp styles and enable loaded styles.
 | ||||||
|  |                 if (data.siteId == CoreSites.getCurrentSiteId()) { | ||||||
|  |                     this.unloadTmpStyles(); | ||||||
|  |                     this.enable(data.siteId); | ||||||
|  |                 } | ||||||
|  |             } catch (error) { | ||||||
|  |                 this.logger.error('Error adding remote styles for new site', error); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Update styles when current site is updated.
 | ||||||
|  |         CoreEvents.on(CoreEvents.SITE_UPDATED, (data) => { | ||||||
|  |             if (data.siteId === CoreSites.getCurrentSiteId()) { | ||||||
|  |                 this.load(data.siteId).catch((error) => { | ||||||
|  |                     this.logger.error('Error loading site after site update', error); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Enable styles of current site on login.
 | ||||||
|  |         CoreEvents.on(CoreEvents.LOGIN, (data) => { | ||||||
|  |             this.unloadTmpStyles(); | ||||||
|  |             this.enable(data.siteId); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Disable added styles on logout.
 | ||||||
|  |         CoreEvents.on(CoreEvents.LOGOUT, () => { | ||||||
|  |             this.clear(); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Remove site styles when a site is deleted.
 | ||||||
|  |         CoreEvents.on(CoreEvents.SITE_DELETED, (site) => { | ||||||
|  |             this.removeSite(site.getId()); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Load temporary styles when site config is checked in login.
 | ||||||
|  |         CoreEvents.on(CoreEvents.LOGIN_SITE_CHECKED, (data) => { | ||||||
|  |             this.loadTmpStylesForSiteConfig(data.config).catch((error) => { | ||||||
|  |                 this.logger.error('Error loading tmp styles', error); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Unload temporary styles when site config is "unchecked" in login.
 | ||||||
|  |         CoreEvents.on(CoreEvents.LOGIN_SITE_UNCHECKED, (data) => { | ||||||
|  |             if (data.siteId && data.siteId === addingSite) { | ||||||
|  |                 // The tmp styles are from a site that is being added permanently.
 | ||||||
|  |                 // Wait for the final site styles to be loaded before removing the tmp styles so there is no blink effect.
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // The tmp styles are from a site that wasn't added in the end. Just remove them.
 | ||||||
|  |             this.unloadTmpStyles(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Add a style element for a site and load the styles for that element. The style will be disabled. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. | ||||||
|  |      * @return Promise resolved when added and loaded. | ||||||
|  |      */ | ||||||
|  |     async addSite(siteId?: string): Promise<void> { | ||||||
|  |         if (!siteId || this.stylesEls[siteId]) { | ||||||
|  |             // Invalid site ID or style already added.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create the style and add it to the header.
 | ||||||
|  |         this.initSiteStyleElement(siteId, true); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             await this.load(siteId, true); | ||||||
|  |         } catch (error) { | ||||||
|  |             this.logger.error('Error loading site after site init', error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Clear styles added to the DOM, disabling them all. | ||||||
|  |      */ | ||||||
|  |     clear(): void { | ||||||
|  |         // Disable all the styles.
 | ||||||
|  |         this.disableElementsBySelector('style[id*=mobilecssurl]'); | ||||||
|  | 
 | ||||||
|  |         // Set StatusBar properties.
 | ||||||
|  |         CoreApp.setStatusBarColor(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create a style element. | ||||||
|  |      * | ||||||
|  |      * @param id ID to set to the element. | ||||||
|  |      * @param disabled Whether the element should be disabled. | ||||||
|  |      * @return New element. | ||||||
|  |      */ | ||||||
|  |     protected createStyleElement(id: string, disabled: boolean): HTMLStyleElement { | ||||||
|  |         const styleEl = document.createElement('style'); | ||||||
|  | 
 | ||||||
|  |         styleEl.setAttribute('id', id); | ||||||
|  |         this.disableElement(styleEl, disabled); | ||||||
|  | 
 | ||||||
|  |         return styleEl; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Enabled or disable a certain style element. | ||||||
|  |      * | ||||||
|  |      * @param element The element to enable or disable. | ||||||
|  |      * @param disable Whether to disable or enable the element. | ||||||
|  |      */ | ||||||
|  |     disableElement(element: HTMLStyleElement, disable: boolean): void { | ||||||
|  |         // Setting disabled should be enough, but we also set the attribute so it can be seen in the DOM which ones are disabled.
 | ||||||
|  |         // Cast to any because the HTMLStyleElement type doesn't define the disabled attribute.
 | ||||||
|  |         (<any> element).disabled = !!disable; // eslint-disable-line @typescript-eslint/no-explicit-any
 | ||||||
|  | 
 | ||||||
|  |         if (disable) { | ||||||
|  |             element.setAttribute('disabled', 'true'); | ||||||
|  |         } else { | ||||||
|  |             element.removeAttribute('disabled'); | ||||||
|  | 
 | ||||||
|  |             if (element.innerHTML != '') { | ||||||
|  |                 CoreApp.setStatusBarColor(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Disable all the style elements based on a query selector. | ||||||
|  |      * | ||||||
|  |      * @param selector The selector to get the style elements. | ||||||
|  |      */ | ||||||
|  |     protected disableElementsBySelector(selector: string): void { | ||||||
|  |         const styles = <HTMLStyleElement[]> Array.from(document.querySelectorAll(selector)); | ||||||
|  | 
 | ||||||
|  |         styles.forEach((style) => { | ||||||
|  |             this.disableElement(style, true); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Downloads a CSS file and remove old files if needed. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. | ||||||
|  |      * @param url File URL. | ||||||
|  |      * @return Promise resolved when the file is downloaded. | ||||||
|  |      */ | ||||||
|  |     protected async downloadFileAndRemoveOld(siteId: string, url: string): Promise<string> { | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             // Check if the file is downloaded.
 | ||||||
|  |             const state = await CoreFilepool.getFileStateByUrl(siteId, url); | ||||||
|  | 
 | ||||||
|  |             if (state == CoreConstants.NOT_DOWNLOADED) { | ||||||
|  |                 // File not downloaded, URL has changed or first time. Delete downloaded CSS files.
 | ||||||
|  |                 await CoreFilepool.removeFilesByComponent(siteId, AddonRemoteThemesProvider.COMPONENT, 1); | ||||||
|  |             } | ||||||
|  |         } catch { | ||||||
|  |             // An error occurred while getting state (shouldn't happen). Don't delete downloaded file.
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return CoreFilepool.downloadUrl(siteId, url, false, AddonRemoteThemesProvider.COMPONENT, 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Enable the styles of a certain site. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. If not defined, current site. | ||||||
|  |      */ | ||||||
|  |     enable(siteId?: string): void { | ||||||
|  |         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||||
|  | 
 | ||||||
|  |         if (this.stylesEls[siteId]) { | ||||||
|  |             this.disableElement(this.stylesEls[siteId].element, false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get remote styles of a certain site. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. If not defined, current site. | ||||||
|  |      * @return Promise resolved with the styles and the URL of the CSS file, | ||||||
|  |      *         resolved with undefined if no styles to load. | ||||||
|  |      */ | ||||||
|  |     async get(siteId?: string): Promise<{fileUrl: string; styles: string} | undefined> { | ||||||
|  |         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||||
|  | 
 | ||||||
|  |         const site = await CoreSites.getSite(siteId); | ||||||
|  |         const infos = site.getInfo(); | ||||||
|  | 
 | ||||||
|  |         if (!infos?.mobilecssurl) { | ||||||
|  |             if (infos?.mobilecssurl === '') { | ||||||
|  |                 // CSS URL is empty. Delete downloaded files (if any).
 | ||||||
|  |                 CoreFilepool.removeFilesByComponent(siteId, AddonRemoteThemesProvider.COMPONENT, 1); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let fileUrl = infos.mobilecssurl; | ||||||
|  | 
 | ||||||
|  |         if (CoreFile.isAvailable()) { | ||||||
|  |             // The file system is available. Download the file and remove old CSS files if needed.
 | ||||||
|  |             fileUrl = await this.downloadFileAndRemoveOld(siteId, fileUrl); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.logger.debug('Loading styles from: ', fileUrl); | ||||||
|  | 
 | ||||||
|  |         // Get the CSS content using HTTP because we will treat the styles before saving them in the file.
 | ||||||
|  |         const text = await CoreWS.getText(fileUrl); | ||||||
|  | 
 | ||||||
|  |         return { fileUrl, styles: this.get35Styles(text) }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if the CSS code has a separator for 3.5 styles. If it does, get only the styles after the separator. | ||||||
|  |      * | ||||||
|  |      * @param cssCode The CSS code to check. | ||||||
|  |      * @return The filtered styles. | ||||||
|  |      */ | ||||||
|  |     protected get35Styles(cssCode: string): string { | ||||||
|  |         const separatorPos = cssCode.search(SEPARATOR_35); | ||||||
|  |         if (separatorPos > -1) { | ||||||
|  |             return cssCode.substr(separatorPos).replace(SEPARATOR_35, ''); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return cssCode; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Init the style element for a site. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. | ||||||
|  |      * @param disabled Whether the element should be disabled. | ||||||
|  |      */ | ||||||
|  |     protected initSiteStyleElement(siteId: string, disabled: boolean): void { | ||||||
|  |         if (this.stylesEls[siteId]) { | ||||||
|  |             // Already initialized, ignore.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create the style and add it to the header.
 | ||||||
|  |         const styleEl = this.createStyleElement('mobilecssurl-' + siteId, disabled); | ||||||
|  | 
 | ||||||
|  |         document.head.appendChild(styleEl); | ||||||
|  |         this.stylesEls[siteId] = { | ||||||
|  |             element: styleEl, | ||||||
|  |             hash: '', | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load styles for a certain site. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. If not defined, current site. | ||||||
|  |      * @param disabled Whether loaded styles should be disabled. | ||||||
|  |      * @return Promise resolved when styles are loaded. | ||||||
|  |      */ | ||||||
|  |     async load(siteId?: string, disabled?: boolean): Promise<void> { | ||||||
|  |         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||||
|  |         disabled = !!disabled; | ||||||
|  | 
 | ||||||
|  |         if (!siteId || !this.stylesEls[siteId]) { | ||||||
|  |             throw new CoreError('Cannot load remote styles, site not found: ${siteId}'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.logger.debug('Load site', siteId, disabled); | ||||||
|  | 
 | ||||||
|  |         // Enable or disable the styles.
 | ||||||
|  |         this.disableElement(this.stylesEls[siteId].element, disabled); | ||||||
|  | 
 | ||||||
|  |         const data = await this.get(siteId); | ||||||
|  | 
 | ||||||
|  |         if (typeof data == 'undefined') { | ||||||
|  |             // Nothing to load.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const hash = <string> Md5.hashAsciiStr(data.styles); | ||||||
|  | 
 | ||||||
|  |         // Update the styles only if they have changed.
 | ||||||
|  |         if (this.stylesEls[siteId].hash !== hash) { | ||||||
|  |             this.stylesEls[siteId].element.innerHTML = data.styles; | ||||||
|  |             this.stylesEls[siteId].hash = hash; | ||||||
|  | 
 | ||||||
|  |             // Adding styles to a style element automatically enables it. Disable it again.
 | ||||||
|  |             if (disabled) { | ||||||
|  |                 this.disableElement(this.stylesEls[siteId].element, true); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Styles have been loaded, now treat the CSS.
 | ||||||
|  |         CoreUtils.ignoreErrors( | ||||||
|  |             CoreFilepool.treatCSSCode(siteId, data.fileUrl, data.styles, AddonRemoteThemesProvider.COMPONENT, 2), | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load styles for a temporary site. These styles aren't prefetched. | ||||||
|  |      * | ||||||
|  |      * @param url URL to get the styles from. | ||||||
|  |      * @return Promise resolved when loaded. | ||||||
|  |      */ | ||||||
|  |     async loadTmpStyles(url?: string): Promise<void> { | ||||||
|  |         if (!url) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let text = await CoreWS.getText(url); | ||||||
|  | 
 | ||||||
|  |         text = this.get35Styles(text); | ||||||
|  | 
 | ||||||
|  |         this.initSiteStyleElement(TMP_SITE_ID, false); | ||||||
|  |         this.stylesEls[TMP_SITE_ID].element.innerHTML = text; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load styles for a temporary site, given its public config. These styles aren't prefetched. | ||||||
|  |      * | ||||||
|  |      * @param config Site public config. | ||||||
|  |      * @return Promise resolved when loaded. | ||||||
|  |      */ | ||||||
|  |     loadTmpStylesForSiteConfig(config: CoreSitePublicConfigResponse): Promise<void> { | ||||||
|  |         return this.loadTmpStyles(config.mobilecssurl); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Preload the styles of the current site (stored in DB). | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when loaded. | ||||||
|  |      */ | ||||||
|  |     async preloadCurrentSite(): Promise<void> { | ||||||
|  |         const siteId = await CoreUtils.ignoreErrors(CoreSites.getStoredCurrentSiteId()); | ||||||
|  | 
 | ||||||
|  |         if (!siteId) { | ||||||
|  |             // No current site stored.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this.addSite(siteId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Preload the styles of all the stored sites. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when loaded. | ||||||
|  |      */ | ||||||
|  |     async preloadSites(): Promise<void> { | ||||||
|  |         const ids = await CoreSites.getSitesIds(); | ||||||
|  | 
 | ||||||
|  |         await CoreUtils.allPromises(ids.map((siteId) => this.addSite(siteId))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Remove the styles of a certain site. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site ID. | ||||||
|  |      */ | ||||||
|  |     removeSite(siteId: string): void { | ||||||
|  |         if (siteId && this.stylesEls[siteId]) { | ||||||
|  |             document.head.removeChild(this.stylesEls[siteId].element); | ||||||
|  |             delete this.stylesEls[siteId]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Unload styles for a temporary site. | ||||||
|  |      */ | ||||||
|  |     unloadTmpStyles(): void { | ||||||
|  |         return this.removeSite(TMP_SITE_ID); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const AddonRemoteThemes = makeSingleton(AddonRemoteThemesProvider); | ||||||
| @ -89,7 +89,7 @@ export class CorePushNotificationsProvider { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         CoreEvents.on(CoreEvents.SITE_DELETED, async (site: CoreSite) => { |         CoreEvents.on(CoreEvents.SITE_DELETED, async (site) => { | ||||||
|             try { |             try { | ||||||
|                 await Promise.all([ |                 await Promise.all([ | ||||||
|                     this.unregisterDeviceOnMoodle(site), |                     this.unregisterDeviceOnMoodle(site), | ||||||
|  | |||||||
| @ -22,7 +22,6 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; | |||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils } from '@services/utils/utils'; | ||||||
| import { SQLiteDB } from '@classes/sqlitedb'; | import { SQLiteDB } from '@classes/sqlitedb'; | ||||||
| import { CoreSite } from '@classes/site'; |  | ||||||
| import { CoreQueueRunner } from '@classes/queue-runner'; | import { CoreQueueRunner } from '@classes/queue-runner'; | ||||||
| import { CoreError } from '@classes/errors/error'; | import { CoreError } from '@classes/errors/error'; | ||||||
| import { CoreConstants } from '@/core/constants'; | import { CoreConstants } from '@/core/constants'; | ||||||
| @ -125,7 +124,7 @@ export class CoreLocalNotificationsProvider { | |||||||
|             this.createDefaultChannel(); |             this.createDefaultChannel(); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         CoreEvents.on(CoreEvents.SITE_DELETED, (site: CoreSite) => { |         CoreEvents.on(CoreEvents.SITE_DELETED, (site) => { | ||||||
|             if (site) { |             if (site) { | ||||||
|                 this.cancelSiteNotifications(site.id!); |                 this.cancelSiteNotifications(site.id!); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ import { Params } from '@angular/router'; | |||||||
| import { Subject } from 'rxjs'; | import { Subject } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| import { CoreLogger } from '@singletons/logger'; | import { CoreLogger } from '@singletons/logger'; | ||||||
| import { CoreSiteInfoResponse } from '@classes/site'; | import { CoreSite, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/site'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Observer instance to stop listening to an event. |  * Observer instance to stop listening to an event. | ||||||
| @ -34,6 +34,7 @@ export interface CoreEventObserver { | |||||||
| export interface CoreEventsData { | export interface CoreEventsData { | ||||||
|     [CoreEvents.SITE_UPDATED]: CoreEventSiteUpdatedData; |     [CoreEvents.SITE_UPDATED]: CoreEventSiteUpdatedData; | ||||||
|     [CoreEvents.SITE_ADDED]: CoreEventSiteAddedData; |     [CoreEvents.SITE_ADDED]: CoreEventSiteAddedData; | ||||||
|  |     [CoreEvents.SITE_DELETED]: CoreSite; | ||||||
|     [CoreEvents.SESSION_EXPIRED]: CoreEventSessionExpiredData; |     [CoreEvents.SESSION_EXPIRED]: CoreEventSessionExpiredData; | ||||||
|     [CoreEvents.CORE_LOADING_CHANGED]: CoreEventLoadingChangedData; |     [CoreEvents.CORE_LOADING_CHANGED]: CoreEventLoadingChangedData; | ||||||
|     [CoreEvents.COURSE_STATUS_CHANGED]: CoreEventCourseStatusChanged; |     [CoreEvents.COURSE_STATUS_CHANGED]: CoreEventCourseStatusChanged; | ||||||
| @ -46,6 +47,7 @@ export interface CoreEventsData { | |||||||
|     [CoreEvents.SECTION_STATUS_CHANGED]: CoreEventSectionStatusChangedData; |     [CoreEvents.SECTION_STATUS_CHANGED]: CoreEventSectionStatusChangedData; | ||||||
|     [CoreEvents.ACTIVITY_DATA_SENT]: CoreEventActivityDataSentData; |     [CoreEvents.ACTIVITY_DATA_SENT]: CoreEventActivityDataSentData; | ||||||
|     [CoreEvents.IAB_LOAD_START]: InAppBrowserEvent; |     [CoreEvents.IAB_LOAD_START]: InAppBrowserEvent; | ||||||
|  |     [CoreEvents.LOGIN_SITE_CHECKED]: CoreEventLoginSiteCheckedData; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| @ -340,3 +342,10 @@ export type CoreEventSectionStatusChangedData = CoreEventSiteData & { | |||||||
| export type CoreEventActivityDataSentData = CoreEventSiteData & { | export type CoreEventActivityDataSentData = CoreEventSiteData & { | ||||||
|     module: string; |     module: string; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Data passed to LOGIN_SITE_CHECKED event. | ||||||
|  |  */ | ||||||
|  | export type CoreEventLoginSiteCheckedData = { | ||||||
|  |     config: CoreSitePublicConfigResponse; | ||||||
|  | }; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user