Merge pull request #3777 from alfonso-salces/MOBILE-4405
Mobile 4405 - Implement auto logout
This commit is contained in:
		
						commit
						038bcfaa30
					
				
							
								
								
									
										274
									
								
								src/core/features/autologout/services/autologout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								src/core/features/autologout/services/autologout.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,274 @@
 | 
				
			|||||||
 | 
					// (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 { CoreSite } from '@classes/site';
 | 
				
			||||||
 | 
					import { CorePlatform } from '@services/platform';
 | 
				
			||||||
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreStorage } from '@services/storage';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreEvents } from '@singletons/events';
 | 
				
			||||||
 | 
					import { Subscription } from 'rxjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Auto logout service
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class CoreAutoLogoutService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Timestamp indicating the last time the application was in the foreground.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected static readonly TIMESTAMP_DB_KEY = 'CoreAutoLogoutTimestamp';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * How often we will store a timestamp (in miliseconds).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected static readonly DEFAULT_TIMESTAMP_STORE_TIME = 10000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Grace period if you return to the application too soon (in miliseconds).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected static readonly GRACE_PERIOD = 30000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected platformResumeSubscription?: Subscription;
 | 
				
			||||||
 | 
					    protected platformPauseSubscription?: Subscription;
 | 
				
			||||||
 | 
					    protected interval?: ReturnType<typeof setInterval>;
 | 
				
			||||||
 | 
					    protected backgroundTimestamp?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initialize.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    initialize(): void {
 | 
				
			||||||
 | 
					        CoreEvents.on(CoreEvents.LOGIN, async() => await this.refreshListeners());
 | 
				
			||||||
 | 
					        CoreEvents.on(CoreEvents.LOGOUT, async() => {
 | 
				
			||||||
 | 
					            this.cancelListeners();
 | 
				
			||||||
 | 
					            const storage = CoreStorage.forCurrentSite();
 | 
				
			||||||
 | 
					            await storage.remove(CoreAutoLogoutService.TIMESTAMP_DB_KEY);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Refresh listeners for auto logout.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async refreshListeners(): Promise<void> {
 | 
				
			||||||
 | 
					        if (!CoreSites.isLoggedIn()) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const site = CoreSites.getCurrentSite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!site) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
 | 
				
			||||||
 | 
					        this.cancelListeners();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!autoLogoutType || autoLogoutType === CoreAutoLogoutType.NEVER) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (autoLogoutType === CoreAutoLogoutType.CUSTOM) {
 | 
				
			||||||
 | 
					            await this.setTimestamp();
 | 
				
			||||||
 | 
					            this.setInterval();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.platformPauseSubscription = CorePlatform.pause.subscribe(async () => {
 | 
				
			||||||
 | 
					            this.backgroundTimestamp = new Date().getTime();
 | 
				
			||||||
 | 
					            this.clearInterval();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.platformResumeSubscription = CorePlatform.resume.subscribe(async () => {
 | 
				
			||||||
 | 
					            if (autoLogoutType !== CoreAutoLogoutType.CUSTOM) {
 | 
				
			||||||
 | 
					                await this.handleAppClosed(site);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const autoLogoutTime = Number(site.getStoredConfig('tool_mobile_autologouttime'));
 | 
				
			||||||
 | 
					            const loggedOut = await this.handleSessionClosed(autoLogoutTime, site);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!loggedOut) {
 | 
				
			||||||
 | 
					                await this.setTimestamp();
 | 
				
			||||||
 | 
					                this.setInterval();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set site logged out.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteId site id.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async logout(siteId: string): Promise<void> {
 | 
				
			||||||
 | 
					        await CoreSites.setSiteLoggedOut(siteId, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Saves stored timestamp.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async setTimestamp(): Promise<void> {
 | 
				
			||||||
 | 
					        const date = new Date().getTime();
 | 
				
			||||||
 | 
					        const storage = CoreStorage.forCurrentSite();
 | 
				
			||||||
 | 
					        await storage.set(CoreAutoLogoutService.TIMESTAMP_DB_KEY, date);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Gives if auto logout can be displayed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @returns true if can display, false if not.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async canShowPreference(): Promise<boolean> {
 | 
				
			||||||
 | 
					        const site = CoreSites.getCurrentSite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!site) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return autoLogoutType !== CoreAutoLogoutType.NEVER;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Cancel uncompleted listeners.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected cancelListeners(): void {
 | 
				
			||||||
 | 
					        this.clearInterval();
 | 
				
			||||||
 | 
					        this.platformResumeSubscription?.unsubscribe();
 | 
				
			||||||
 | 
					        this.platformPauseSubscription?.unsubscribe();
 | 
				
			||||||
 | 
					        delete this.platformPauseSubscription;
 | 
				
			||||||
 | 
					        delete this.platformResumeSubscription;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set interval.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected setInterval(): void {
 | 
				
			||||||
 | 
					        this.interval = setInterval(async () => await this.setTimestamp(), CoreAutoLogoutService.DEFAULT_TIMESTAMP_STORE_TIME);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Clear interval.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected clearInterval(): void {
 | 
				
			||||||
 | 
					        if (!this.interval) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        clearInterval(this.interval);
 | 
				
			||||||
 | 
					        delete this.interval;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Logout user if his session is expired.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sessionDuration Session duration.
 | 
				
			||||||
 | 
					     * @param site Current site.
 | 
				
			||||||
 | 
					     * @returns Whether site has been logged out.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async handleSessionClosed(sessionDuration: number, site: CoreSite): Promise<boolean> {
 | 
				
			||||||
 | 
					        if (!site.id) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const storage = CoreStorage.forSite(site);
 | 
				
			||||||
 | 
					        const savedTimestamp = await storage.get<number>(CoreAutoLogoutService.TIMESTAMP_DB_KEY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!savedTimestamp) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get expiration time from site preferences as miliseconds.
 | 
				
			||||||
 | 
					        const expirationDate = savedTimestamp + ((sessionDuration || 0) * 1000);
 | 
				
			||||||
 | 
					        await storage.remove(CoreAutoLogoutService.TIMESTAMP_DB_KEY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (new Date().getTime() < expirationDate) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.logout(site.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Logout if user closed the app.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param site Current site.
 | 
				
			||||||
 | 
					     * @returns Whether site has been logged out.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async handleAppClosed(site: CoreSite): Promise<boolean> {
 | 
				
			||||||
 | 
					        if (!site.id) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            this.backgroundTimestamp &&
 | 
				
			||||||
 | 
					            (this.backgroundTimestamp + CoreAutoLogoutService.GRACE_PERIOD) > new Date().getTime()
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            delete this.backgroundTimestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.logout(site.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getConfig(): { autoLogoutType: CoreAutoLogoutType; autoLogoutTime: number } {
 | 
				
			||||||
 | 
					        const site = CoreSites.getRequiredCurrentSite();
 | 
				
			||||||
 | 
					        const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
 | 
				
			||||||
 | 
					        const autoLogoutTime = Number(site.getStoredConfig('tool_mobile_autologouttime'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return { autoLogoutType, autoLogoutTime };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CoreAutoLogoutSessionConfig = {
 | 
				
			||||||
 | 
					    type: CoreAutoLogoutType.CUSTOM;
 | 
				
			||||||
 | 
					    sessionDuration: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CoreAutoLogoutOtherConfig = {
 | 
				
			||||||
 | 
					    type: Exclude<CoreAutoLogoutType, CoreAutoLogoutType.CUSTOM>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Possible automatic logout cases.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export enum CoreAutoLogoutType {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disabled automatic logout.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    NEVER = 0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * When the user closes the app, in next login he need to login again.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    INMEDIATE = 1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This applies when session time is set. If the user closes the app more time than the specified,
 | 
				
			||||||
 | 
					     * then, the user must login again.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    CUSTOM = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CoreAutoLogoutConfig = CoreAutoLogoutSessionConfig | CoreAutoLogoutOtherConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CoreAutoLogout = makeSingleton(CoreAutoLogoutService);
 | 
				
			||||||
@ -158,6 +158,7 @@ import { ADDON_PRIVATEFILES_SERVICES } from '@addons/privatefiles/privatefiles.m
 | 
				
			|||||||
import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module';
 | 
					import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module';
 | 
				
			||||||
import { CorePromisedValue } from '@classes/promised-value';
 | 
					import { CorePromisedValue } from '@classes/promised-value';
 | 
				
			||||||
import { CorePlatform } from '@services/platform';
 | 
					import { CorePlatform } from '@services/platform';
 | 
				
			||||||
 | 
					import { CoreAutoLogoutService } from '@features/autologout/services/autologout';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Service to provide functionalities regarding compiling dynamic HTML and Javascript.
 | 
					 * Service to provide functionalities regarding compiling dynamic HTML and Javascript.
 | 
				
			||||||
@ -268,6 +269,7 @@ export class CoreCompileProvider {
 | 
				
			|||||||
    injectLibraries(instance: any, extraProviders: Type<unknown>[] = []): void {
 | 
					    injectLibraries(instance: any, extraProviders: Type<unknown>[] = []): void {
 | 
				
			||||||
        const providers = [
 | 
					        const providers = [
 | 
				
			||||||
            ...CORE_SERVICES,
 | 
					            ...CORE_SERVICES,
 | 
				
			||||||
 | 
					            CoreAutoLogoutService,
 | 
				
			||||||
            ...CORE_BLOCK_SERVICES,
 | 
					            ...CORE_BLOCK_SERVICES,
 | 
				
			||||||
            ...CORE_COMMENTS_SERVICES,
 | 
					            ...CORE_COMMENTS_SERVICES,
 | 
				
			||||||
            ...CORE_CONTENTLINKS_SERVICES,
 | 
					            ...CORE_CONTENTLINKS_SERVICES,
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,11 @@ import { CoreCronDelegate } from '@services/cron';
 | 
				
			|||||||
import { CoreFilepool } from '@services/filepool';
 | 
					import { CoreFilepool } from '@services/filepool';
 | 
				
			||||||
import { CoreLocalNotifications } from '@services/local-notifications';
 | 
					import { CoreLocalNotifications } from '@services/local-notifications';
 | 
				
			||||||
import { CoreSites } from '@services/sites';
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreStorage } from '@services/storage';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Init databases instances.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
export default async function(): Promise<void> {
 | 
					export default async function(): Promise<void> {
 | 
				
			||||||
    await Promise.all([
 | 
					    await Promise.all([
 | 
				
			||||||
        CoreApp.initializeDatabase(),
 | 
					        CoreApp.initializeDatabase(),
 | 
				
			||||||
@ -27,5 +31,6 @@ export default async function(): Promise<void> {
 | 
				
			|||||||
        CoreFilepool.initializeDatabase(),
 | 
					        CoreFilepool.initializeDatabase(),
 | 
				
			||||||
        CoreLocalNotifications.initializeDatabase(),
 | 
					        CoreLocalNotifications.initializeDatabase(),
 | 
				
			||||||
        CoreSites.initializeDatabase(),
 | 
					        CoreSites.initializeDatabase(),
 | 
				
			||||||
 | 
					        CoreStorage.initializeDatabase(),
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
// See the License for the specific language governing permissions and
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { CoreAutoLogout } from '@features/autologout/services/autologout';
 | 
				
			||||||
import { CoreConfig } from '@services/config';
 | 
					import { CoreConfig } from '@services/config';
 | 
				
			||||||
import { CoreFilepool } from '@services/filepool';
 | 
					import { CoreFilepool } from '@services/filepool';
 | 
				
			||||||
import { CoreLang } from '@services/lang';
 | 
					import { CoreLang } from '@services/lang';
 | 
				
			||||||
@ -31,5 +32,6 @@ export default async function(): Promise<void> {
 | 
				
			|||||||
        CoreNetwork.initialize(),
 | 
					        CoreNetwork.initialize(),
 | 
				
			||||||
        CoreUpdateManager.initialize(),
 | 
					        CoreUpdateManager.initialize(),
 | 
				
			||||||
        CoreTimeUtils.initialize(),
 | 
					        CoreTimeUtils.initialize(),
 | 
				
			||||||
 | 
					        CoreAutoLogout.initialize(),
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,13 @@ import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
				
			|||||||
import { SITE_SCHEMA as FILEPOOL_SITE_SCHEMA } from './filepool';
 | 
					import { SITE_SCHEMA as FILEPOOL_SITE_SCHEMA } from './filepool';
 | 
				
			||||||
import { SITE_SCHEMA as SITES_SITE_SCHEMA } from './sites';
 | 
					import { SITE_SCHEMA as SITES_SITE_SCHEMA } from './sites';
 | 
				
			||||||
import { SITE_SCHEMA as SYNC_SITE_SCHEMA } from './sync';
 | 
					import { SITE_SCHEMA as SYNC_SITE_SCHEMA } from './sync';
 | 
				
			||||||
 | 
					import { SITE_SCHEMA as STORAGE_SITE_SCHEMA } from './storage';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Give database providers.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @returns database providers
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
export function getDatabaseProviders(): Provider[] {
 | 
					export function getDatabaseProviders(): Provider[] {
 | 
				
			||||||
    return [{
 | 
					    return [{
 | 
				
			||||||
        provide: CORE_SITE_SCHEMAS,
 | 
					        provide: CORE_SITE_SCHEMAS,
 | 
				
			||||||
@ -26,6 +32,7 @@ export function getDatabaseProviders(): Provider[] {
 | 
				
			|||||||
            FILEPOOL_SITE_SCHEMA,
 | 
					            FILEPOOL_SITE_SCHEMA,
 | 
				
			||||||
            SITES_SITE_SCHEMA,
 | 
					            SITES_SITE_SCHEMA,
 | 
				
			||||||
            SYNC_SITE_SCHEMA,
 | 
					            SYNC_SITE_SCHEMA,
 | 
				
			||||||
 | 
					            STORAGE_SITE_SCHEMA,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        multi: true,
 | 
					        multi: true,
 | 
				
			||||||
    }];
 | 
					    }];
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										55
									
								
								src/core/services/database/storage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/core/services/database/storage.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					// (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 { SQLiteDBTableSchema } from '@classes/sqlitedb';
 | 
				
			||||||
 | 
					import { CoreAppSchema } from '@services/app';
 | 
				
			||||||
 | 
					import { CoreSiteSchema } from '@services/sites';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TABLE_NAME = 'core_storage';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TABLE_SCHEMA: SQLiteDBTableSchema = {
 | 
				
			||||||
 | 
					    name: TABLE_NAME,
 | 
				
			||||||
 | 
					    columns: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: 'key',
 | 
				
			||||||
 | 
					            type: 'TEXT',
 | 
				
			||||||
 | 
					            primaryKey: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: 'value',
 | 
				
			||||||
 | 
					            type: 'TEXT',
 | 
				
			||||||
 | 
					            notNull: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const APP_SCHEMA: CoreAppSchema = {
 | 
				
			||||||
 | 
					    name: 'CoreStorageService',
 | 
				
			||||||
 | 
					    version: 1,
 | 
				
			||||||
 | 
					    tables: [TABLE_SCHEMA],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SITE_SCHEMA: CoreSiteSchema = {
 | 
				
			||||||
 | 
					    name: 'CoreStorageService',
 | 
				
			||||||
 | 
					    version: 1,
 | 
				
			||||||
 | 
					    tables: [TABLE_SCHEMA],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Storage table record type.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type CoreStorageRecord = {
 | 
				
			||||||
 | 
					    key: string;
 | 
				
			||||||
 | 
					    value: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -65,6 +65,7 @@ import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest
 | 
				
			|||||||
import { CoreLang, CoreLangFormat } from '@services/lang';
 | 
					import { CoreLang, CoreLangFormat } from '@services/lang';
 | 
				
			||||||
import { CoreNative } from '@features/native/services/native';
 | 
					import { CoreNative } from '@features/native/services/native';
 | 
				
			||||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
 | 
					import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
 | 
				
			||||||
 | 
					import { CoreAutoLogoutType, CoreAutoLogout } from '@features/autologout/services/autologout';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
 | 
					export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
 | 
				
			||||||
export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
 | 
					export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
 | 
				
			||||||
@ -1461,6 +1462,8 @@ export class CoreSitesProvider {
 | 
				
			|||||||
     * @returns Promise resolved if a session is restored.
 | 
					     * @returns Promise resolved if a session is restored.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async restoreSession(): Promise<void> {
 | 
					    async restoreSession(): Promise<void> {
 | 
				
			||||||
 | 
					        await this.handleAutoLogout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.sessionRestored) {
 | 
					        if (this.sessionRestored) {
 | 
				
			||||||
            return Promise.reject(new CoreError('Session already restored.'));
 | 
					            return Promise.reject(new CoreError('Session already restored.'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1477,6 +1480,30 @@ export class CoreSitesProvider {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle auto logout by checking autologout type and time if its required.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async handleAutoLogout(): Promise<void> {
 | 
				
			||||||
 | 
					        await CoreUtils.ignoreErrors(( async () => {
 | 
				
			||||||
 | 
					            const siteId = await this.getStoredCurrentSiteId();
 | 
				
			||||||
 | 
					            const site = await this.getSite(siteId);
 | 
				
			||||||
 | 
					            const autoLogoutType = Number(site.getStoredConfig('tool_mobile_autologout'));
 | 
				
			||||||
 | 
					            const autoLogoutTime = Number(site.getStoredConfig('tool_mobile_autologouttime'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (autoLogoutType === CoreAutoLogoutType.NEVER || !site.id) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (autoLogoutType === CoreAutoLogoutType.CUSTOM) {
 | 
				
			||||||
 | 
					                await CoreAutoLogout.handleSessionClosed(autoLogoutTime, site);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await CoreAutoLogout.handleAppClosed(site);
 | 
				
			||||||
 | 
					        })());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Mark a site as logged out so the user needs to authenticate again.
 | 
					     * Mark a site as logged out so the user needs to authenticate again.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										222
									
								
								src/core/services/storage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								src/core/services/storage.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,222 @@
 | 
				
			|||||||
 | 
					// (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 { Inject, Injectable, Optional } from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance';
 | 
				
			||||||
 | 
					import { CoreApp } from '@services/app';
 | 
				
			||||||
 | 
					import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy';
 | 
				
			||||||
 | 
					import { CoreDatabaseTable } from '@classes/database/database-table';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { SQLiteDB } from '@classes/sqlitedb';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { APP_SCHEMA, CoreStorageRecord, TABLE_NAME } from './database/storage';
 | 
				
			||||||
 | 
					import { CoreSites } from './sites';
 | 
				
			||||||
 | 
					import { CoreSite } from '@classes/site';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Service to store data using key-value pairs.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The data can be scoped to a single site using CoreStorage.forSite(site), and it will be automatically cleared
 | 
				
			||||||
 | 
					 * when the site is deleted.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * For tabular data, use CoreAppProvider.getDB() or CoreSite.getDb().
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class CoreStorageService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    table: AsyncInstance<CoreStorageTable>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(@Optional() @Inject(null) lazyTableConstructor?: () => Promise<CoreStorageTable>) {
 | 
				
			||||||
 | 
					        this.table = asyncInstance(lazyTableConstructor);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initialize database.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async initializeDatabase(): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await CoreApp.createTablesFromSchema(APP_SCHEMA);
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            // Ignore errors.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.initializeTable(CoreApp.getDB());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initialize table.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param database Database.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async initializeTable(database: SQLiteDB): Promise<void> {
 | 
				
			||||||
 | 
					        const table = await getStorageTable(database);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.table.setInstance(table);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Data key.
 | 
				
			||||||
 | 
					     * @param defaultValue Value to return if the key wasn't found.
 | 
				
			||||||
 | 
					     * @returns Data value.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async get<T=unknown>(key: string): Promise<T | null>;
 | 
				
			||||||
 | 
					    async get<T>(key: string, defaultValue: T): Promise<T>;
 | 
				
			||||||
 | 
					    async get<T=unknown>(key: string, defaultValue: T | null = null): Promise<T | null> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const { value } = await this.table.getOneByPrimaryKey({ key });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return JSON.parse(value);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            return defaultValue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get value directly from the database, without using any optimizations..
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Data key.
 | 
				
			||||||
 | 
					     * @param defaultValue Value to return if the key wasn't found.
 | 
				
			||||||
 | 
					     * @returns Data value.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getFromDB<T=unknown>(key: string): Promise<T | null>;
 | 
				
			||||||
 | 
					    async getFromDB<T>(key: string, defaultValue: T): Promise<T>;
 | 
				
			||||||
 | 
					    async getFromDB<T=unknown>(key: string, defaultValue: T | null = null): Promise<T | null> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const db = CoreApp.getDB();
 | 
				
			||||||
 | 
					            const { value } = await db.getRecord<CoreStorageRecord>(TABLE_NAME, { key });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return JSON.parse(value);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            return defaultValue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Data key.
 | 
				
			||||||
 | 
					     * @param value Data value.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async set(key: string, value: unknown): Promise<void> {
 | 
				
			||||||
 | 
					        await this.table.insert({ key, value: JSON.stringify(value) });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if value exists.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Data key.
 | 
				
			||||||
 | 
					     * @returns Whether key exists or not.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async has(key: string): Promise<boolean> {
 | 
				
			||||||
 | 
					        return this.table.hasAny({ key });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Remove value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param key Data key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async remove(key: string): Promise<void> {
 | 
				
			||||||
 | 
					        await this.table.deleteByPrimaryKey({ key });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the core_storage table of the current site.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @returns CoreStorageService instance with the core_storage table.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    forCurrentSite(): AsyncInstance<Omit<CoreStorageService, 'forSite' | 'forCurrentSite'>> {
 | 
				
			||||||
 | 
					        return asyncInstance(async () => {
 | 
				
			||||||
 | 
					            const siteId = await CoreSites.getStoredCurrentSiteId();
 | 
				
			||||||
 | 
					            const site = await CoreSites.getSite(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!(siteId in SERVICE_INSTANCES)) {
 | 
				
			||||||
 | 
					                SERVICE_INSTANCES[siteId] = asyncInstance(async () => {
 | 
				
			||||||
 | 
					                    const instance = new CoreStorageService();
 | 
				
			||||||
 | 
					                    await instance.initializeTable(site.getDb());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return instance;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await SERVICE_INSTANCES[siteId].getInstance();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the core_storage table for the provided site.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param site Site from which we will obtain the storage.
 | 
				
			||||||
 | 
					     * @returns CoreStorageService instance with the core_storage table.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    forSite(site: CoreSite): AsyncInstance<Omit<CoreStorageService, 'forSite' | 'forCurrentSite'>> {
 | 
				
			||||||
 | 
					        const siteId = site.getId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return asyncInstance(async () => {
 | 
				
			||||||
 | 
					            if (!(siteId in SERVICE_INSTANCES)) {
 | 
				
			||||||
 | 
					                const instance = new CoreStorageService();
 | 
				
			||||||
 | 
					                await instance.initializeTable(site.getDb());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                SERVICE_INSTANCES[siteId] = asyncInstance(() => instance);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return await SERVICE_INSTANCES[siteId].getInstance();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CoreStorage = makeSingleton(CoreStorageService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SERVICE_INSTANCES: Record<string, AsyncInstance<CoreStorageService>> = {};
 | 
				
			||||||
 | 
					const TABLE_INSTANCES: WeakMap<SQLiteDB, Promise<CoreStorageTable>> = new WeakMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Helper function to get a storage table for the given database.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param database Database.
 | 
				
			||||||
 | 
					 * @returns Storage table.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getStorageTable(database: SQLiteDB): Promise<CoreStorageTable> {
 | 
				
			||||||
 | 
					    const existingTable = TABLE_INSTANCES.get(database);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (existingTable) {
 | 
				
			||||||
 | 
					        return existingTable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const table = new Promise<CoreStorageTable>((resolve, reject) => {
 | 
				
			||||||
 | 
					        const tableProxy = new CoreDatabaseTableProxy<CoreStorageRecord, 'key'>(
 | 
				
			||||||
 | 
					            { cachingStrategy: CoreDatabaseCachingStrategy.Eager },
 | 
				
			||||||
 | 
					            database,
 | 
				
			||||||
 | 
					            TABLE_NAME,
 | 
				
			||||||
 | 
					            ['key'],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tableProxy.initialize()
 | 
				
			||||||
 | 
					            .then(() => resolve(tableProxy))
 | 
				
			||||||
 | 
					            .catch(reject);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TABLE_INSTANCES.set(database, table);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return table;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Storage table.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					type CoreStorageTable = CoreDatabaseTable<CoreStorageRecord, 'key'>;
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user