MOBILE-3833 core: Improve database config updates
parent
6f222fe2ab
commit
3f71825ead
|
@ -23,6 +23,7 @@ import {
|
|||
CoreDatabaseConditions,
|
||||
GetDBRecordPrimaryKey,
|
||||
CoreDatabaseQueryOptions,
|
||||
CoreDatabaseTableConstructor,
|
||||
} from './database-table';
|
||||
import { CoreDebugDatabaseTable } from './debug-database-table';
|
||||
import { CoreEagerDatabaseTable } from './eager-database-table';
|
||||
|
@ -42,6 +43,14 @@ export class CoreDatabaseTableProxy<
|
|||
protected config: CoreDatabaseConfiguration;
|
||||
protected target = asyncInstance<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>();
|
||||
protected environmentObserver?: CoreEventObserver;
|
||||
protected targetConstructors: Record<
|
||||
CoreDatabaseCachingStrategy,
|
||||
CoreDatabaseTableConstructor<DBRecord, PrimaryKeyColumn, PrimaryKey>
|
||||
> = {
|
||||
[CoreDatabaseCachingStrategy.Eager]: CoreEagerDatabaseTable,
|
||||
[CoreDatabaseCachingStrategy.Lazy]: CoreLazyDatabaseTable,
|
||||
[CoreDatabaseCachingStrategy.None]: CoreDatabaseTable,
|
||||
};
|
||||
|
||||
constructor(
|
||||
config: Partial<CoreDatabaseConfiguration>,
|
||||
|
@ -58,7 +67,13 @@ export class CoreDatabaseTableProxy<
|
|||
* @inheritdoc
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
this.environmentObserver = CoreEvents.on(CoreConfigProvider.ENVIRONMENT_UPDATED, () => this.updateTarget());
|
||||
this.environmentObserver = CoreEvents.on(CoreConfigProvider.ENVIRONMENT_UPDATED, async () => {
|
||||
if (!(await this.shouldUpdateTarget())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateTarget();
|
||||
});
|
||||
|
||||
await this.updateTarget();
|
||||
}
|
||||
|
@ -202,6 +217,20 @@ export class CoreDatabaseTableProxy<
|
|||
this.target.setInstance(newTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the underlying target should be updated.
|
||||
*
|
||||
* @returns Whether target should be updated.
|
||||
*/
|
||||
protected async shouldUpdateTarget(): Promise<boolean> {
|
||||
const config = await this.getRuntimeConfig();
|
||||
const target = await this.target.getInstance();
|
||||
const originalTarget = target instanceof CoreDebugDatabaseTable ? target.getTarget() : target;
|
||||
|
||||
return (config.debug && target === originalTarget)
|
||||
|| originalTarget?.constructor !== this.targetConstructors[config.cachingStrategy];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create proxy target.
|
||||
*
|
||||
|
@ -221,14 +250,9 @@ export class CoreDatabaseTableProxy<
|
|||
* @returns Database table.
|
||||
*/
|
||||
protected createTable(cachingStrategy: CoreDatabaseCachingStrategy): CoreDatabaseTable<DBRecord, PrimaryKeyColumn> {
|
||||
switch (cachingStrategy) {
|
||||
case CoreDatabaseCachingStrategy.Eager:
|
||||
return new CoreEagerDatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
case CoreDatabaseCachingStrategy.Lazy:
|
||||
return new CoreLazyDatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
case CoreDatabaseCachingStrategy.None:
|
||||
return new CoreDatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
}
|
||||
const DatabaseTable = this.targetConstructors[cachingStrategy];
|
||||
|
||||
return new DatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -336,6 +336,23 @@ export class CoreDatabaseTable<
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* CoreDatabaseTable constructor.
|
||||
*/
|
||||
export type CoreDatabaseTableConstructor<
|
||||
DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues,
|
||||
PrimaryKeyColumn extends keyof DBRecord = 'id',
|
||||
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
|
||||
> = {
|
||||
|
||||
new (
|
||||
database: SQLiteDB,
|
||||
tableName: string,
|
||||
primaryKeyColumns?: PrimaryKeyColumn[]
|
||||
): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey>;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Infer primary key type from database record and primary key column types.
|
||||
*/
|
||||
|
|
|
@ -43,6 +43,13 @@ export class CoreDebugDatabaseTable<
|
|||
this.logger = CoreLogger.getInstance(`CoreDatabase[${this.tableName}]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get underlying table instance.
|
||||
*/
|
||||
getTarget(): CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -333,7 +333,14 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
|
|||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.viewLeft = true;
|
||||
CoreEvents.trigger(CoreEvents.LOGIN_SITE_UNCHECKED, { config: this.siteConfig }, this.siteId);
|
||||
CoreEvents.trigger(
|
||||
CoreEvents.LOGIN_SITE_UNCHECKED,
|
||||
{
|
||||
config: this.siteConfig,
|
||||
loginSuccessful: !!this.siteId,
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
this.valueChangeSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
|||
protected viewLeft = false;
|
||||
protected eventThrown = false;
|
||||
protected redirectData?: CoreRedirectPayload;
|
||||
protected loginSuccessful = false;
|
||||
|
||||
constructor(
|
||||
protected fb: FormBuilder,
|
||||
|
@ -117,7 +118,14 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
|||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.viewLeft = true;
|
||||
CoreEvents.trigger(CoreEvents.LOGIN_SITE_UNCHECKED, { config: this.siteConfig }, this.siteId);
|
||||
CoreEvents.trigger(
|
||||
CoreEvents.LOGIN_SITE_UNCHECKED,
|
||||
{
|
||||
config: this.siteConfig,
|
||||
loginSuccessful: this.loginSuccessful,
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,6 +222,8 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
|||
this.credForm.controls['password'].reset();
|
||||
|
||||
// Go to the site initial page.
|
||||
this.loginSuccessful = true;
|
||||
|
||||
await CoreNavigator.navigateToSiteHome({
|
||||
params: this.redirectData,
|
||||
});
|
||||
|
|
|
@ -108,19 +108,11 @@ export class CoreStylesService {
|
|||
* 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();
|
||||
|
@ -164,10 +156,10 @@ export class CoreStylesService {
|
|||
});
|
||||
|
||||
// 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.
|
||||
CoreEvents.on(CoreEvents.LOGIN_SITE_UNCHECKED, ({ loginSuccessful }) => {
|
||||
if (loginSuccessful) {
|
||||
// The tmp styles have been added for a site we've logged into, so we'll wait for the final
|
||||
// site styles to be loaded before removing the tmp styles so there is no blink effect.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,19 @@ export class CoreConfigProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an app setting directly from the database, without using any optimizations..
|
||||
*
|
||||
* @param name The config name.
|
||||
* @return Resolves upon success along with the config data. Reject on failure.
|
||||
*/
|
||||
async getFromDB<T>(name: string): Promise<T> {
|
||||
const db = CoreApp.getDB();
|
||||
const record = await db.getRecord<ConfigDBEntry>(CONFIG_TABLE_NAME, { name });
|
||||
|
||||
return record.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given app setting exists.
|
||||
*
|
||||
|
@ -151,9 +164,14 @@ export class CoreConfigProvider {
|
|||
* Update config with the given values.
|
||||
*
|
||||
* @param config Config updates.
|
||||
* @param reset Whether to reset environment before applying the patch.
|
||||
*/
|
||||
patchEnvironment(config: Partial<EnvironmentConfig>): void {
|
||||
this.defaultEnvironment = this.defaultEnvironment ?? CoreConstants.CONFIG;
|
||||
patchEnvironment(config: Partial<EnvironmentConfig>, reset: boolean = false): void {
|
||||
this.defaultEnvironment = this.defaultEnvironment ?? { ...CoreConstants.CONFIG };
|
||||
|
||||
if (reset) {
|
||||
this.resetEnvironmentSilently();
|
||||
}
|
||||
|
||||
Object.assign(CoreConstants.CONFIG, config);
|
||||
CoreEvents.trigger(CoreConfigProvider.ENVIRONMENT_UPDATED, CoreConstants.CONFIG);
|
||||
|
@ -169,8 +187,7 @@ export class CoreConfigProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
Object.keys(CoreConstants.CONFIG).forEach(key => delete CoreConstants.CONFIG[key]);
|
||||
Object.assign(CoreConstants.CONFIG, this.defaultEnvironment);
|
||||
this.resetEnvironmentSilently();
|
||||
CoreEvents.trigger(CoreConfigProvider.ENVIRONMENT_UPDATED, CoreConstants.CONFIG);
|
||||
}
|
||||
|
||||
|
@ -185,6 +202,20 @@ export class CoreConfigProvider {
|
|||
this.patchEnvironment(JSON.parse(CoreUtils.getCookie('MoodleAppConfig') ?? '{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset config values to its original state without emitting any events.
|
||||
*/
|
||||
protected resetEnvironmentSilently(): void {
|
||||
if (!this.defaultEnvironment) {
|
||||
// The environment config hasn't been modified; there's not need to reset.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(CoreConstants.CONFIG).forEach(key => delete CoreConstants.CONFIG[key]);
|
||||
Object.assign(CoreConstants.CONFIG, this.defaultEnvironment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreConfig = makeSingleton(CoreConfigProvider);
|
||||
|
|
|
@ -66,6 +66,7 @@ import { asyncInstance, AsyncInstance } from '../utils/async-instance';
|
|||
import { CoreConfig } from './config';
|
||||
|
||||
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
|
||||
export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
|
||||
|
||||
/*
|
||||
* Service to manage and interact with sites.
|
||||
|
@ -1032,13 +1033,31 @@ export class CoreSitesProvider {
|
|||
try {
|
||||
const data = await this.sitesTable.getOneByPrimaryKey({ id: siteId });
|
||||
|
||||
return this.makeSiteFromSiteListEntry(data);
|
||||
return this.addSiteFromSiteListEntry(data);
|
||||
} catch {
|
||||
throw new CoreError('SiteId not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a site directly from the database, without using any optimizations.
|
||||
*
|
||||
* @param siteId Site id.
|
||||
* @return Site.
|
||||
*/
|
||||
async getSiteFromDB(siteId: string): Promise<CoreSite> {
|
||||
const db = CoreApp.getDB();
|
||||
|
||||
try {
|
||||
const record = await db.getRecord<SiteDBEntry>(SITES_TABLE_NAME, { id: siteId });
|
||||
|
||||
return this.makeSiteFromSiteListEntry(record);
|
||||
} catch {
|
||||
throw new CoreError('SiteId not found');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a site with a certain URL. It will return the first site found.
|
||||
*
|
||||
|
@ -1052,7 +1071,7 @@ export class CoreSitesProvider {
|
|||
return this.sites[data.id];
|
||||
}
|
||||
|
||||
return this.makeSiteFromSiteListEntry(data);
|
||||
return this.addSiteFromSiteListEntry(data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1061,8 +1080,25 @@ export class CoreSitesProvider {
|
|||
* @param entry Site list entry.
|
||||
* @return Promised resolved with the created site.
|
||||
*/
|
||||
makeSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> {
|
||||
addSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> {
|
||||
// Parse info and config.
|
||||
const site = this.makeSiteFromSiteListEntry(entry);
|
||||
|
||||
return this.migrateSiteSchemas(site).then(() => {
|
||||
// Set site after migrating schemas, or a call to getSite could get the site while tables are being created.
|
||||
this.sites[entry.id] = site;
|
||||
|
||||
return site;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a site instance from a database entry.
|
||||
*
|
||||
* @param entry Site database entry.
|
||||
* @return Site.
|
||||
*/
|
||||
makeSiteFromSiteListEntry(entry: SiteDBEntry): CoreSite {
|
||||
const info = entry.info ? <CoreSiteInfo> CoreTextUtils.parseJSON(entry.info) : undefined;
|
||||
const config = entry.config ? <CoreSiteConfig> CoreTextUtils.parseJSON(entry.config) : undefined;
|
||||
|
||||
|
@ -1077,12 +1113,7 @@ export class CoreSitesProvider {
|
|||
);
|
||||
site.setOAuthId(entry.oauthId || undefined);
|
||||
|
||||
return this.migrateSiteSchemas(site).then(() => {
|
||||
// Set site after migrating schemas, or a call to getSite could get the site while tables are being created.
|
||||
this.sites[entry.id] = site;
|
||||
|
||||
return site;
|
||||
});
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1231,7 +1262,7 @@ export class CoreSitesProvider {
|
|||
* @return Promise resolved when current site is stored.
|
||||
*/
|
||||
async login(siteId: string): Promise<void> {
|
||||
await CoreConfig.set('current_site_id', siteId);
|
||||
await CoreConfig.set(CORE_SITE_CURRENT_SITE_ID_CONFIG, siteId);
|
||||
|
||||
CoreEvents.trigger(CoreEvents.LOGIN, {}, siteId);
|
||||
}
|
||||
|
@ -1477,7 +1508,7 @@ export class CoreSitesProvider {
|
|||
|
||||
siteEntries.forEach((site) => {
|
||||
if (!this.sites[site.id]) {
|
||||
promises.push(this.makeSiteFromSiteListEntry(site));
|
||||
promises.push(this.addSiteFromSiteListEntry(site));
|
||||
}
|
||||
|
||||
if (this.sites[site.id].containsUrl(url)) {
|
||||
|
@ -1504,7 +1535,7 @@ export class CoreSitesProvider {
|
|||
async getStoredCurrentSiteId(): Promise<string> {
|
||||
await this.migrateCurrentSiteLegacyTable();
|
||||
|
||||
return CoreConfig.get('current_site_id');
|
||||
return CoreConfig.get(CORE_SITE_CURRENT_SITE_ID_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1513,7 +1544,7 @@ export class CoreSitesProvider {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
async removeStoredCurrentSite(): Promise<void> {
|
||||
await CoreConfig.delete('current_site_id');
|
||||
await CoreConfig.delete(CORE_SITE_CURRENT_SITE_ID_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1789,7 +1820,7 @@ export class CoreSitesProvider {
|
|||
|
||||
const { siteId } = await db.getRecord<{ siteId: string }>('current_site');
|
||||
|
||||
await CoreConfig.set('current_site_id', siteId);
|
||||
await CoreConfig.set(CORE_SITE_CURRENT_SITE_ID_CONFIG, siteId);
|
||||
await CoreApp.deleteTableSchema('current_site');
|
||||
await db.dropTable('current_site');
|
||||
} finally {
|
||||
|
|
|
@ -56,6 +56,7 @@ export interface CoreEventsData {
|
|||
[CoreEvents.ACTIVITY_DATA_SENT]: CoreEventActivityDataSentData;
|
||||
[CoreEvents.IAB_LOAD_START]: InAppBrowserEvent;
|
||||
[CoreEvents.LOGIN_SITE_CHECKED]: CoreEventLoginSiteCheckedData;
|
||||
[CoreEvents.LOGIN_SITE_UNCHECKED]: CoreEventLoginSiteUncheckedData;
|
||||
[CoreEvents.SEND_ON_ENTER_CHANGED]: CoreEventSendOnEnterChangedData;
|
||||
[CoreEvents.COMPONENT_FILE_ACTION]: CoreFilepoolComponentFileEventData;
|
||||
[CoreEvents.FILE_SHARED]: CoreEventFileSharedData;
|
||||
|
@ -400,6 +401,14 @@ export type CoreEventLoginSiteCheckedData = {
|
|||
config: CoreSitePublicConfigResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to LOGIN_SITE_UNCHECKED event.
|
||||
*/
|
||||
export type CoreEventLoginSiteUncheckedData = {
|
||||
config?: CoreSitePublicConfigResponse;
|
||||
loginSuccessful: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to SEND_ON_ENTER_CHANGED event.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue