MOBILE-3634 remotethemes: Support remote themes
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 {}
|
||||||
|
|
|
@ -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 {}
|
|
@ -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…
Reference in New Issue