diff --git a/src/addons/messages/services/handlers/mainmenu.ts b/src/addons/messages/services/handlers/mainmenu.ts index ea20c90d6..5df64592f 100644 --- a/src/addons/messages/services/handlers/mainmenu.ts +++ b/src/addons/messages/services/handlers/mainmenu.ts @@ -54,18 +54,21 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler, protected unreadCount = 0; protected contactRequestsCount = 0; protected orMore = false; + protected badgeCount?: number; constructor() { CoreEvents.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { this.unreadCount = data.favourites + data.individual + data.group + data.self; this.orMore = !!data.orMore; - this.updateBadge(data.siteId!); + + data.siteId && this.updateBadge(data.siteId); }); CoreEvents.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { this.contactRequestsCount = data.count; - this.updateBadge(data.siteId!); + + data.siteId && this.updateBadge(data.siteId); }); // Reset info on logout. @@ -123,27 +126,28 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler, * @return Resolve when done. */ async refreshBadge(siteId?: string, unreadOnly?: boolean): Promise { - siteId = siteId || CoreSites.getCurrentSiteId(); - if (!siteId) { + const badgeSiteId = siteId || CoreSites.getCurrentSiteId(); + + if (!badgeSiteId) { return; } const promises: Promise[] = []; - promises.push(AddonMessages.refreshUnreadConversationCounts(siteId).catch(() => { + promises.push(AddonMessages.refreshUnreadConversationCounts(badgeSiteId).catch(() => { this.unreadCount = 0; this.orMore = false; })); // Refresh the number of contact requests in 3.6+ sites. if (!unreadOnly && AddonMessages.isGroupMessagingEnabled()) { - promises.push(AddonMessages.refreshContactRequestsCount(siteId).catch(() => { + promises.push(AddonMessages.refreshContactRequestsCount(badgeSiteId).catch(() => { this.contactRequestsCount = 0; })); } await Promise.all(promises).finally(() => { - this.updateBadge(siteId!); + this.updateBadge(badgeSiteId); this.handler.loading = false; }); } @@ -155,6 +159,13 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler, */ updateBadge(siteId: string): void { const totalCount = this.unreadCount + (this.contactRequestsCount || 0); + + if (this.badgeCount === totalCount) { + return; + } + + this.badgeCount = totalCount; + if (totalCount > 0) { this.handler.badge = totalCount + (this.orMore ? '+' : ''); } else { diff --git a/src/addons/mod/book/services/book.ts b/src/addons/mod/book/services/book.ts index 6b6c18a8f..64d025dd6 100644 --- a/src/addons/mod/book/services/book.ts +++ b/src/addons/mod/book/services/book.ts @@ -69,6 +69,7 @@ export class AddonModBookProvider { () => CoreSites.getSiteTable(LAST_CHAPTER_VIEWED_TABLE, { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, + onDestroy: () => delete this.lastChapterViewedTables[siteId], }), ), ); diff --git a/src/core/classes/database/database-table-proxy.ts b/src/core/classes/database/database-table-proxy.ts index 7e183f95a..b1cba7e88 100644 --- a/src/core/classes/database/database-table-proxy.ts +++ b/src/core/classes/database/database-table-proxy.ts @@ -14,10 +14,11 @@ import { CoreConstants } from '@/core/constants'; import { asyncInstance } from '@/core/utils/async-instance'; -import { SQLiteDB, SQLiteDBRecordValues } from '@classes/sqlitedb'; +import { SQLiteDBRecordValues } from '@classes/sqlitedb'; import { CoreConfig, CoreConfigProvider } from '@services/config'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { + CoreDatabaseConfiguration, CoreDatabaseReducer, CoreDatabaseTable, CoreDatabaseConditions, @@ -40,7 +41,8 @@ export class CoreDatabaseTableProxy< PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey > extends CoreDatabaseTable { - protected config: CoreDatabaseConfiguration; + protected readonly DEFAULT_CACHING_STRATEGY = CoreDatabaseCachingStrategy.None; + protected target = asyncInstance>(); protected environmentObserver?: CoreEventObserver; protected targetConstructors: Record< @@ -52,21 +54,12 @@ export class CoreDatabaseTableProxy< [CoreDatabaseCachingStrategy.None]: CoreDatabaseTable, }; - constructor( - config: Partial, - database: SQLiteDB, - tableName: string, - primaryKeyColumns?: PrimaryKeyColumn[], - ) { - super(database, tableName, primaryKeyColumns); - - this.config = { ...this.getConfigDefaults(), ...config }; - } - /** * @inheritdoc */ async initialize(): Promise { + await super.initialize(); + this.environmentObserver = CoreEvents.on(CoreConfigProvider.ENVIRONMENT_UPDATED, async () => { if (!(await this.shouldUpdateTarget())) { return; @@ -82,9 +75,23 @@ export class CoreDatabaseTableProxy< * @inheritdoc */ async destroy(): Promise { + await super.destroy(); + this.environmentObserver?.off(); } + /** + * @inheritdoc + */ + matchesConfig(config: Partial): boolean { + const thisDebug = this.config.debug ?? false; + const thisCachingStrategy = this.config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY; + const otherDebug = config.debug ?? false; + const otherCachingStrategy = config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY; + + return super.matchesConfig(config) && thisDebug === otherDebug && thisCachingStrategy === otherCachingStrategy; + } + /** * @inheritdoc */ @@ -172,24 +179,12 @@ export class CoreDatabaseTableProxy< return this.target.deleteByPrimaryKey(primaryKey); } - /** - * Get default configuration values. - * - * @returns Config defaults. - */ - protected getConfigDefaults(): CoreDatabaseConfiguration { - return { - cachingStrategy: CoreDatabaseCachingStrategy.None, - debug: false, - }; - } - /** * Get database configuration to use at runtime. * * @returns Database configuration. */ - protected async getRuntimeConfig(): Promise { + protected async getRuntimeConfig(): Promise> { await CoreConfig.ready(); return { @@ -228,7 +223,8 @@ export class CoreDatabaseTableProxy< const originalTarget = target instanceof CoreDebugDatabaseTable ? target.getTarget() : target; return (config.debug && target === originalTarget) - || originalTarget?.constructor !== this.targetConstructors[config.cachingStrategy]; + || originalTarget?.constructor !== this.targetConstructors[config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY] + || !originalTarget.matchesConfig(config); } /** @@ -238,7 +234,7 @@ export class CoreDatabaseTableProxy< */ protected async createTarget(): Promise> { const config = await this.getRuntimeConfig(); - const table = this.createTable(config.cachingStrategy); + const table = this.createTable(config); return config.debug ? new CoreDebugDatabaseTable(table) : table; } @@ -246,23 +242,29 @@ export class CoreDatabaseTableProxy< /** * Create a database table using the given caching strategy. * - * @param cachingStrategy Caching strategy. + * @param config Database configuration. * @returns Database table. */ - protected createTable(cachingStrategy: CoreDatabaseCachingStrategy): CoreDatabaseTable { - const DatabaseTable = this.targetConstructors[cachingStrategy]; + protected createTable(config: Partial): CoreDatabaseTable { + const DatabaseTable = this.targetConstructors[config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY]; - return new DatabaseTable(this.database, this.tableName, this.primaryKeyColumns); + return new DatabaseTable(config, this.database, this.tableName, this.primaryKeyColumns); } } -/** - * Database proxy configuration. - */ -export interface CoreDatabaseConfiguration { - cachingStrategy: CoreDatabaseCachingStrategy; - debug: boolean; +declare module '@classes/database/database-table' { + + /** + * Augment CoreDatabaseConfiguration interface with data specific to this class. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreDatabaseConfiguration { + cachingStrategy: CoreDatabaseCachingStrategy; + debug: boolean; + } + } /** diff --git a/src/core/classes/database/database-table.ts b/src/core/classes/database/database-table.ts index f198b333e..43e0ac125 100644 --- a/src/core/classes/database/database-table.ts +++ b/src/core/classes/database/database-table.ts @@ -24,16 +24,31 @@ export class CoreDatabaseTable< PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey > { + protected config: Partial; protected database: SQLiteDB; protected tableName: string; protected primaryKeyColumns: PrimaryKeyColumn[]; + protected listeners: CoreDatabaseTableListener[] = []; - constructor(database: SQLiteDB, tableName: string, primaryKeyColumns?: PrimaryKeyColumn[]) { + constructor( + config: Partial, + database: SQLiteDB, + tableName: string, + primaryKeyColumns?: PrimaryKeyColumn[], + ) { + this.config = config; this.database = database; this.tableName = tableName; this.primaryKeyColumns = primaryKeyColumns ?? ['id'] as PrimaryKeyColumn[]; } + /** + * Get database configuration. + */ + getConfig(): Partial { + return this.config; + } + /** * Get database connection. * @@ -72,7 +87,27 @@ export class CoreDatabaseTable< * Destroy. */ async destroy(): Promise { - // Nothing to destroy by default, override this method if necessary. + this.listeners.forEach(listener => listener.onDestroy?.()); + } + + /** + * Add listener. + * + * @param listener Listener. + */ + addListener(listener: CoreDatabaseTableListener): void { + this.listeners.push(listener); + } + + /** + * Check whether the table matches the given configuration for the values that concern it. + * + * @param config Database config. + * @returns Whether the table matches the given configuration. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + matchesConfig(config: Partial): boolean { + return true; } /** @@ -336,6 +371,20 @@ export class CoreDatabaseTable< } +/** + * Database configuration. + */ +export interface CoreDatabaseConfiguration { + // This definition is augmented in subclasses. +} + +/** + * Database table listener. + */ +export interface CoreDatabaseTableListener { + onDestroy?(): void; +} + /** * CoreDatabaseTable constructor. */ @@ -346,6 +395,7 @@ export type CoreDatabaseTableConstructor< > = { new ( + config: Partial, database: SQLiteDB, tableName: string, primaryKeyColumns?: PrimaryKeyColumn[] diff --git a/src/core/classes/database/debug-database-table.ts b/src/core/classes/database/debug-database-table.ts index d7e58b56b..5ab22bb8e 100644 --- a/src/core/classes/database/debug-database-table.ts +++ b/src/core/classes/database/debug-database-table.ts @@ -37,7 +37,7 @@ export class CoreDebugDatabaseTable< protected logger: CoreLogger; constructor(target: CoreDatabaseTable) { - super(target.getDatabase(), target.getTableName(), target.getPrimaryKeyColumns()); + super(target.getConfig(), target.getDatabase(), target.getTableName(), target.getPrimaryKeyColumns()); this.target = target; this.logger = CoreLogger.getInstance(`CoreDatabase[${this.tableName}]`); @@ -53,7 +53,9 @@ export class CoreDebugDatabaseTable< /** * @inheritdoc */ - initialize(): Promise { + async initialize(): Promise { + await super.initialize(); + this.logger.log('initialize', this.target); return this.target.initialize(); @@ -62,7 +64,9 @@ export class CoreDebugDatabaseTable< /** * @inheritdoc */ - destroy(): Promise { + async destroy(): Promise { + await super.destroy(); + this.logger.log('destroy'); return this.target.destroy(); diff --git a/src/core/classes/database/eager-database-table.ts b/src/core/classes/database/eager-database-table.ts index 461485df5..97aac2f19 100644 --- a/src/core/classes/database/eager-database-table.ts +++ b/src/core/classes/database/eager-database-table.ts @@ -40,6 +40,8 @@ export class CoreEagerDatabaseTable< * @inheritdoc */ async initialize(): Promise { + await super.initialize(); + const records = await super.getMany(); this.records = records.reduce((data, record) => { diff --git a/src/core/classes/database/lazy-database-table.ts b/src/core/classes/database/lazy-database-table.ts index 489b21a54..f29bbddb7 100644 --- a/src/core/classes/database/lazy-database-table.ts +++ b/src/core/classes/database/lazy-database-table.ts @@ -14,7 +14,13 @@ import { CoreError } from '@classes/errors/error'; import { SQLiteDBRecordValues } from '@classes/sqlitedb'; -import { CoreDatabaseTable, CoreDatabaseConditions, GetDBRecordPrimaryKey, CoreDatabaseQueryOptions } from './database-table'; +import { + CoreDatabaseConfiguration, + CoreDatabaseTable, + CoreDatabaseConditions, + GetDBRecordPrimaryKey, + CoreDatabaseQueryOptions, +} from './database-table'; /** * Wrapper used to improve performance by caching records that are used often for faster read operations. @@ -28,7 +34,38 @@ export class CoreLazyDatabaseTable< PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey > extends CoreDatabaseTable { + protected readonly DEFAULT_CACHE_LIFETIME = 60000; + protected records: Record = {}; + protected interval?: number; + + /** + * @inheritdoc + */ + async initialize(): Promise { + await super.initialize(); + + this.interval = window.setInterval(() => (this.records = {}), this.config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME); + } + + /** + * @inheritdoc + */ + async destroy(): Promise { + await super.destroy(); + + this.interval && window.clearInterval(this.interval); + } + + /** + * @inheritdoc + */ + matchesConfig(config: Partial): boolean { + const thisCacheLifetime = this.config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME; + const otherCacheLifetime = config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME; + + return super.matchesConfig(config) && thisCacheLifetime === otherCacheLifetime; + } /** * @inheritdoc @@ -152,3 +189,16 @@ export class CoreLazyDatabaseTable< } } + +declare module '@classes/database/database-table' { + + /** + * Augment CoreDatabaseConfiguration interface with data specific to this table. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreDatabaseConfiguration { + lazyCacheLifetime: number; + } + +} diff --git a/src/core/classes/sqlitedb.ts b/src/core/classes/sqlitedb.ts index fc5f762e9..0d5bce2df 100644 --- a/src/core/classes/sqlitedb.ts +++ b/src/core/classes/sqlitedb.ts @@ -1170,27 +1170,54 @@ export class SQLiteDB { */ protected getDatabaseSpies(db: SQLiteObject): Partial { return { - executeSql(statement, params) { + async executeSql(statement, params) { const start = performance.now(); - return db.executeSql(statement, params).then(result => { - CoreDB.logQuery(statement, performance.now() - start, params); + try { + const result = await db.executeSql(statement, params); + + CoreDB.logQuery({ + params, + sql: statement, + duration: performance.now() - start, + }); return result; - }); + } catch (error) { + CoreDB.logQuery({ + params, + error, + sql: statement, + duration: performance.now() - start, + }); + + throw error; + } }, - sqlBatch(statements) { + async sqlBatch(statements) { const start = performance.now(); + const sql = Array.isArray(statements) + ? statements.join(' | ') + : String(statements); - return db.sqlBatch(statements).then(result => { - const sql = Array.isArray(statements) - ? statements.join(' | ') - : String(statements); + try { + const result = await db.sqlBatch(statements); - CoreDB.logQuery(sql, performance.now() - start); + CoreDB.logQuery({ + sql, + duration: performance.now() - start, + }); return result; - }); + } catch (error) { + CoreDB.logQuery({ + sql, + error, + duration: performance.now() - start, + }); + + throw error; + } }, }; } diff --git a/src/core/classes/tests/database-table.test.ts b/src/core/classes/tests/database-table.test.ts index 2cf4840b0..8e1e0f1e9 100644 --- a/src/core/classes/tests/database-table.test.ts +++ b/src/core/classes/tests/database-table.test.ts @@ -13,12 +13,8 @@ // limitations under the License. import { mock, mockSingleton } from '@/testing/utils'; -import { CoreDatabaseSorting, CoreDatabaseTable } from '@classes/database/database-table'; -import { - CoreDatabaseCachingStrategy, - CoreDatabaseConfiguration, - CoreDatabaseTableProxy, -} from '@classes/database/database-table-proxy'; +import { CoreDatabaseConfiguration, CoreDatabaseSorting, CoreDatabaseTable } from '@classes/database/database-table'; +import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreConfig } from '@services/config'; diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index 55c7fc262..bce92b409 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -148,6 +148,7 @@ export class CoreCourseProvider { () => CoreSites.getSiteTable(COURSE_STATUS_TABLE, { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, + onDestroy: () => delete this.statusTables[siteId], }), ), ); diff --git a/src/core/features/emulator/classes/sqlitedb.ts b/src/core/features/emulator/classes/sqlitedb.ts index 18ad526b6..8b7f5c682 100644 --- a/src/core/features/emulator/classes/sqlitedb.ts +++ b/src/core/features/emulator/classes/sqlitedb.ts @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* tslint:disable:no-console */ - import { SQLiteDB } from '@classes/sqlitedb'; import { DbTransaction, SQLiteObject } from '@ionic-native/sqlite/ngx'; import { CoreDB } from '@services/db'; @@ -53,7 +51,7 @@ export class SQLiteDBMock extends SQLiteDB { await this.ready(); return new Promise((resolve, reject): void => { - this.db!.transaction((tx) => { + this.db?.transaction((tx) => { // Query all tables from sqlite_master that we have created and can modify. const args = []; const query = `SELECT * FROM sqlite_master @@ -99,15 +97,13 @@ export class SQLiteDBMock extends SQLiteDB { return new Promise((resolve, reject): void => { // With WebSQL, all queries must be run in a transaction. - this.db!.transaction((tx) => { - tx.executeSql(sql, params, (tx, results) => { - resolve(results); - }, (tx, error) => { - // eslint-disable-next-line no-console - console.error(sql, params, error); - - reject(error); - }); + this.db?.transaction((tx) => { + tx.executeSql( + sql, + params, + (_, results) => resolve(results), + (_, error) => reject(new Error(`SQL failed: ${sql}, reason: ${error?.message}`)), + ); }); }); } @@ -126,7 +122,7 @@ export class SQLiteDBMock extends SQLiteDB { return new Promise((resolve, reject): void => { // Create a transaction to execute the queries. - this.db!.transaction((tx) => { + this.db?.transaction((tx) => { const promises: Promise[] = []; // Execute all the queries. Each statement can be a string or an array. @@ -143,14 +139,7 @@ export class SQLiteDBMock extends SQLiteDB { params = null; } - tx.executeSql(query, params, (tx, results) => { - resolve(results); - }, (tx, error) => { - // eslint-disable-next-line no-console - console.error(query, params, error); - - reject(error); - }); + tx.executeSql(query, params, (_, results) => resolve(results), (_, error) => reject(error)); })); }); @@ -187,13 +176,30 @@ export class SQLiteDBMock extends SQLiteDB { const transactionSpy: DbTransaction = { executeSql(sql, params, success, error) { const start = performance.now(); - const resolve = callback => (...args) => { - CoreDB.logQuery(sql, performance.now() - start, params); - return callback(...args); - }; + return transaction.executeSql( + sql, + params, + (...args) => { + CoreDB.logQuery({ + sql, + params, + duration: performance.now() - start, + }); - return transaction.executeSql(sql, params, resolve(success), resolve(error)); + return success?.(...args); + }, + (...args) => { + CoreDB.logQuery({ + sql, + params, + error: args[0], + duration: performance.now() - start, + }); + + return error?.(...args); + }, + ); }, }; diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts index be515b7c9..2477b1cfc 100644 --- a/src/core/features/pushnotifications/services/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/pushnotifications.ts @@ -74,6 +74,7 @@ export class CorePushNotificationsProvider { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.None }, primaryKeyColumns: ['appid', 'uuid'], + onDestroy: () => delete this.registeredDevicesTables[siteId], }, ), ), diff --git a/src/core/initializers/prepare-devtools.ts b/src/core/initializers/prepare-devtools.ts index ffed96e55..adc324726 100644 --- a/src/core/initializers/prepare-devtools.ts +++ b/src/core/initializers/prepare-devtools.ts @@ -13,14 +13,20 @@ // limitations under the License. import { CoreConfig, CoreConfigProvider } from '@services/config'; +import { CoreDB, CoreDbProvider } from '@services/db'; +import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/urlschemes'; import { CoreConstants } from '../constants'; type DevelopmentWindow = Window & { configProvider?: CoreConfigProvider; + dbProvider?: CoreDbProvider; + urlSchemes?: CoreCustomURLSchemesProvider; }; function initializeDevelopmentWindow(window: DevelopmentWindow) { window.configProvider = CoreConfig.instance; + window.dbProvider = CoreDB.instance; + window.urlSchemes = CoreCustomURLSchemes.instance; } export default function(): void { diff --git a/src/core/services/db.ts b/src/core/services/db.ts index 489d44072..de01fd469 100644 --- a/src/core/services/db.ts +++ b/src/core/services/db.ts @@ -18,6 +18,7 @@ import { SQLiteDB } from '@classes/sqlitedb'; import { SQLiteDBMock } from '@features/emulator/classes/sqlitedb'; import { makeSingleton, SQLite, Platform } from '@singletons'; import { CoreAppProvider } from './app'; +import { CoreUtils } from './utils/utils'; /** * This service allows interacting with the local database to store and retrieve data. @@ -35,15 +36,21 @@ export class CoreDbProvider { * @returns Whether queries should be logged. */ loggingEnabled(): boolean { - return CoreAppProvider.isAutomated(); + return CoreUtils.hasCookie('MoodleAppDBLoggingEnabled') || CoreAppProvider.isAutomated(); } /** * Print query history in console. + * + * @param format Log format, with the following substitutions: :sql, :duration, and :result. */ - printHistory(): void { - const substituteParams = ({ sql, params }: CoreDbQueryLog) => - Object.values(params ?? []).reduce((sql: string, param: string) => sql.replace('?', param), sql); + printHistory(format: string = ':sql | Duration: :duration | Result: :result'): void { + const substituteParams = ({ sql, params, duration, error }: CoreDbQueryLog) => format + .replace(':sql', Object + .values(params ?? []) + .reduce((sql: string, param: string) => sql.replace('?', param) as string, sql) as string) + .replace(':duration', `${Math.round(duration).toString().padStart(4, '0')}ms`) + .replace(':result', error?.message ?? 'Success'); // eslint-disable-next-line no-console console.log(this.queryLogs.map(substituteParams).join('\n')); @@ -52,11 +59,10 @@ export class CoreDbProvider { /** * Log a query. * - * @param sql Query SQL. - * @param params Query parameters. + * @param log Query log. */ - logQuery(sql: string, duration: number, params?: unknown[]): void { - this.queryLogs.push({ sql, duration, params }); + logQuery(log: CoreDbQueryLog): void { + this.queryLogs.push(log); } /** @@ -121,5 +127,6 @@ export const CoreDB = makeSingleton(CoreDbProvider); export interface CoreDbQueryLog { sql: string; duration: number; + error?: Error; params?: unknown[]; } diff --git a/src/core/services/filepool.ts b/src/core/services/filepool.ts index 30d857010..f41ee3cd4 100644 --- a/src/core/services/filepool.ts +++ b/src/core/services/filepool.ts @@ -113,6 +113,7 @@ export class CoreFilepoolProvider { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, primaryKeyColumns: ['fileId'], + onDestroy: () => delete this.filesTables[siteId], }), ), ); @@ -122,6 +123,7 @@ export class CoreFilepoolProvider { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, primaryKeyColumns: ['fileId', 'component', 'componentId'], + onDestroy: () => delete this.linksTables[siteId], }), ), ); @@ -130,6 +132,7 @@ export class CoreFilepoolProvider { () => CoreSites.getSiteTable(PACKAGES_TABLE_NAME, { siteId, config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy }, + onDestroy: () => delete this.packagesTables[siteId], }), ), ); @@ -149,16 +152,6 @@ export class CoreFilepoolProvider { NgZone.run(() => this.checkQueueProcessing()); }); }); - - CoreEvents.on(CoreEvents.SITE_DELETED, async ({ siteId }) => { - if (!siteId || !(siteId in this.filesTables)) { - return; - } - - await this.filesTables[siteId].destroy(); - - delete this.filesTables[siteId]; - }); } /** diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 9388baaa5..c7cc2ba8e 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -56,12 +56,8 @@ import { CoreAjaxError } from '@classes/errors/ajaxerror'; import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CorePromisedValue } from '@classes/promised-value'; -import { CoreDatabaseTable } from '@classes/database/database-table'; -import { - CoreDatabaseCachingStrategy, - CoreDatabaseConfiguration, - CoreDatabaseTableProxy, -} from '@classes/database/database-table-proxy'; +import { CoreDatabaseConfiguration, CoreDatabaseTable } from '@classes/database/database-table'; +import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { asyncInstance, AsyncInstance } from '../utils/async-instance'; import { CoreConfig } from './config'; @@ -162,6 +158,7 @@ export class CoreSitesProvider { config: Partial; database: SQLiteDB; primaryKeyColumns: PrimaryKeyColumn[]; + onDestroy(): void; }> = {}, ): Promise> { const siteId = options.siteId ?? this.getCurrentSiteId(); @@ -180,6 +177,8 @@ export class CoreSitesProvider { options.primaryKeyColumns, ); + options.onDestroy && table.addListener({ onDestroy: options.onDestroy }); + await table.initialize(); promisedTable.resolve(table as unknown as CoreDatabaseTable); @@ -1837,16 +1836,19 @@ export class CoreSitesProvider { * @returns Scehmas Table. */ protected getSiteSchemasTable(site: CoreSite): AsyncInstance> { - this.schemasTables[site.getId()] = this.schemasTables[site.getId()] ?? asyncInstance( + const siteId = site.getId(); + + this.schemasTables[siteId] = this.schemasTables[siteId] ?? asyncInstance( () => this.getSiteTable(SCHEMA_VERSIONS_TABLE_NAME, { - siteId: site.getId(), + siteId: siteId, database: site.getDb(), config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, primaryKeyColumns: ['name'], + onDestroy: () => delete this.schemasTables[siteId], }), ); - return this.schemasTables[site.getId()]; + return this.schemasTables[siteId]; } } diff --git a/src/core/singletons/object.ts b/src/core/singletons/object.ts index 9bc8fe760..5e5571451 100644 --- a/src/core/singletons/object.ts +++ b/src/core/singletons/object.ts @@ -32,7 +32,7 @@ export class CoreObject { * @param b Second object. * @return Whether objects are equal. */ - static deepEquals(a: unknown, b: unknown): boolean { + static deepEquals(a: T, b: T): boolean { return JSON.stringify(a) === JSON.stringify(b); } diff --git a/src/types/config.d.ts b/src/types/config.d.ts index c8fa468bc..ef8c23349 100644 --- a/src/types/config.d.ts +++ b/src/types/config.d.ts @@ -17,7 +17,7 @@ import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mai import { CoreSitesDemoSiteData } from '@services/sites'; import { OpenFileAction } from '@services/utils/utils'; import { CoreLoginSiteSelectorListMethod } from '@features/login/services/login-helper'; -import { CoreDatabaseConfiguration } from '@classes/database/database-table-proxy'; +import { CoreDatabaseConfiguration } from '@classes/database/database-table'; /* eslint-disable @typescript-eslint/naming-convention */