From 3bdab171d3b4cbf5e40d56741593ca5c74f9a862 Mon Sep 17 00:00:00 2001 From: LocalNewsTV <62873746+LocalNewsTV@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:27:47 -0800 Subject: [PATCH] Standardize Function names, implement BaseCacheService, strengthen typing --- .../UI/LegacyMap/helpers/recordset-layers.ts | 12 ++-- app/src/state/actions/cache/RecordCache.ts | 2 +- app/src/state/sagas/activity/offline.ts | 2 +- app/src/state/sagas/iappsite/dataAccess.ts | 2 +- app/src/state/sagas/map.ts | 8 +-- app/src/state/sagas/map/dataAccess.ts | 12 ++-- app/src/utils/record-cache/context.ts | 3 +- app/src/utils/record-cache/index.ts | 67 ++++++++++--------- .../utils/record-cache/localforage-cache.ts | 29 ++++---- app/src/utils/record-cache/sqlite-cache.ts | 34 ++++++---- app/src/utils/tile-cache/context.ts | 2 +- app/src/utils/tile-cache/index.ts | 26 +++---- app/src/utils/tile-cache/localforage-cache.ts | 8 +-- app/src/utils/tile-cache/sqlite-cache.ts | 12 +++- 14 files changed, 123 insertions(+), 96 deletions(-) diff --git a/app/src/UI/LegacyMap/helpers/recordset-layers.ts b/app/src/UI/LegacyMap/helpers/recordset-layers.ts index 5cca5adcc..8338036f7 100644 --- a/app/src/UI/LegacyMap/helpers/recordset-layers.ts +++ b/app/src/UI/LegacyMap/helpers/recordset-layers.ts @@ -24,9 +24,9 @@ export const createOfflineIappLayer = async (map: maplibregl.Map, layer: any) => return; } const service = await RecordCacheServiceFactory.getPlatformInstance(); - const repo = await service.fetchRepository(layer.recordSetID); + const repo = await service.getRepository(layer.recordSetID); - if (!repo.cachedGeoJson) { + if (!repo?.cachedGeoJson) { return; } const layerID = formatLayerID(layer.recordSetID, layer.tableFiltersHash); @@ -192,9 +192,9 @@ export const createOfflineActivityLayer = async (map: maplibregl.Map, layer: any return; } const service = await RecordCacheServiceFactory.getPlatformInstance(); - const { cachedCentroid, cachedGeoJson } = await service.fetchRepository(layer.recordSetID); + const metadata = await service.getRepository(layer.recordSetID); - if (!cachedCentroid || !cachedGeoJson) { + if (!metadata?.cachedCentroid || !metadata?.cachedGeoJson) { return; } @@ -203,8 +203,8 @@ export const createOfflineActivityLayer = async (map: maplibregl.Map, layer: any const CENTROID_ID = `${GEOJSON_ID}-centroid`; const color = getPaintBySchemeOrColor(layer); - const geoJsonSourceObj: GeoJSONSourceSpecification = cachedGeoJson; - const centroidSourceObj: GeoJSONSourceSpecification = cachedCentroid; + const geoJsonSourceObj: GeoJSONSourceSpecification = metadata.cachedGeoJson; + const centroidSourceObj: GeoJSONSourceSpecification = metadata.cachedCentroid; const circleMarkerZoomedOutLayerCentroid: CircleLayerSpecification = getCircleMarkerZoomedOutLayer(CENTROID_ID, { color, diff --git a/app/src/state/actions/cache/RecordCache.ts b/app/src/state/actions/cache/RecordCache.ts index fd9fc3148..41022046e 100644 --- a/app/src/state/actions/cache/RecordCache.ts +++ b/app/src/state/actions/cache/RecordCache.ts @@ -37,7 +37,7 @@ class RecordCache { const recordSet = state.UserSettings.recordSets[spec.setId]; const bbox = await getBoundingBoxFromRecordsetFilters(recordSet); - const downloadCompleted = await service.downloadCache({ + const downloadCompleted = await service.download({ API_BASE: state.Configuration.current.API_BASE, bbox, idsToCache, diff --git a/app/src/state/sagas/activity/offline.ts b/app/src/state/sagas/activity/offline.ts index 695f49d33..12550b135 100644 --- a/app/src/state/sagas/activity/offline.ts +++ b/app/src/state/sagas/activity/offline.ts @@ -74,7 +74,7 @@ export function* handle_ACTIVITY_GET_LOCAL_REQUEST(action: PayloadAction } } else { try { - const service: RecordCacheService = yield RecordCacheServiceFactory.getPlatformInstance(); + const service = yield RecordCacheServiceFactory.getPlatformInstance(); const result = yield service.loadActivity(activityID); const datav2 = { diff --git a/app/src/state/sagas/iappsite/dataAccess.ts b/app/src/state/sagas/iappsite/dataAccess.ts index 1ef408a19..561dc0ccb 100644 --- a/app/src/state/sagas/iappsite/dataAccess.ts +++ b/app/src/state/sagas/iappsite/dataAccess.ts @@ -15,7 +15,7 @@ export function* handle_IAPP_GET_REQUEST(iappId: PayloadAction) { try { const connected = yield select(selectNetworkConnected); if (MOBILE && !connected) { - const service: RecordCacheService = yield RecordCacheServiceFactory.getPlatformInstance(); + const service = yield RecordCacheServiceFactory.getPlatformInstance(); const result = yield service.loadIapp(iappId.payload, IappRecordMode.Record); yield put(IappActions.getSuccess(result)); } else { diff --git a/app/src/state/sagas/map.ts b/app/src/state/sagas/map.ts index 57dd1351f..4761c2dda 100644 --- a/app/src/state/sagas/map.ts +++ b/app/src/state/sagas/map.ts @@ -84,7 +84,7 @@ import { RecordCacheServiceFactory } from 'utils/record-cache/context'; import bboxToPolygon from 'utils/bboxToPolygon'; import IappActions from 'state/actions/activity/Iapp'; import IappRecord from 'interfaces/IappRecord'; -import { RecordCacheAddSpec } from 'utils/record-cache'; +import { RepositoryMetadata } from 'utils/record-cache'; import NetworkActions from 'state/actions/network/NetworkActions'; function* handle_USER_SETTINGS_GET_INITIAL_STATE_SUCCESS(action) { @@ -163,7 +163,7 @@ function* handle_WHATS_HERE_FEATURE(whatsHereFeature: PayloadAction) { const service = yield RecordCacheServiceFactory.getPlatformInstance(); const repos = yield service.listRepositories(); - const recordSetsInBoundingBox = repos.filter((repo: RecordCacheAddSpec) => { + const recordSetsInBoundingBox = repos.filter((repo: RepositoryMetadata) => { const { status, bbox } = repo; return ( status === UserRecordCacheStatus.CACHED && @@ -223,7 +223,7 @@ function* handle_WHATS_HERE_IAPP_ROWS_REQUEST() { let records: IappRecord[]; if (MOBILE && !connected) { const service = yield RecordCacheServiceFactory.getPlatformInstance(); - records = yield service.fetchPaginatedCachedIappRecords( + records = yield service.getPaginatedCachedIappRecords( whatsHere.IAPPIDs.map((id) => id.toString()), whatsHere.IAPPPage, whatsHere.IAPPLimit @@ -313,7 +313,7 @@ function* handle_WHATS_HERE_ACTIVITY_ROWS_REQUEST() { let records: UserRecord[]; if (MOBILE && !connected) { const service = yield RecordCacheServiceFactory.getPlatformInstance(); - records = yield service.fetchPaginatedCachedRecords( + records = yield service.getPaginatedCachedActivityRecords( whatsHere.ActivityIDs, whatsHere.ActivityPage, whatsHere.ActivityLimit diff --git a/app/src/state/sagas/map/dataAccess.ts b/app/src/state/sagas/map/dataAccess.ts index 09b256696..c1b19e6c3 100644 --- a/app/src/state/sagas/map/dataAccess.ts +++ b/app/src/state/sagas/map/dataAccess.ts @@ -116,7 +116,7 @@ export function* handle_ACTIVITIES_GET_IDS_FOR_RECORDSET_REQUEST(action) { const recordSet = currentState.recordSets[action.payload.recordSetID] ?? null; if (recordSet.cacheMetadataStatus === UserRecordCacheStatus.CACHED) { const service = yield RecordCacheServiceFactory.getPlatformInstance(); - const ids = yield service.fetchIdList(action.payload.recordSetID); + const ids = yield service.getIdList(action.payload.recordSetID); yield put({ type: ACTIVITIES_GET_IDS_FOR_RECORDSET_SUCCESS, @@ -160,7 +160,7 @@ export function* handle_IAPP_GET_IDS_FOR_RECORDSET_REQUEST(action) { } else { const service = yield RecordCacheServiceFactory.getPlatformInstance(); if (yield service.isCached(action.payload.recordSetID)) { - const ids = yield service.fetchIdList(action.payload.recordSetID); + const ids = yield service.getIdList(action.payload.recordSetID); yield put({ type: IAPP_GET_IDS_FOR_RECORDSET_SUCCESS, payload: { @@ -237,8 +237,8 @@ export function* handle_ACTIVITIES_TABLE_ROWS_GET_REQUEST(action) { if (userMobileOffline) { const service = yield RecordCacheServiceFactory.getPlatformInstance(); - const recordSetIdList = yield service.fetchIdList(recordSetID); - const records = yield service.fetchPaginatedCachedRecords(recordSetIdList, page, limit); + const recordSetIdList = yield service.getIdList(recordSetID); + const records = yield service.getPaginatedCachedActivityRecords(recordSetIdList, page, limit); yield put( Activity.getRowsSuccess({ recordSetID: recordSetID, @@ -289,8 +289,8 @@ export function* handle_IAPP_TABLE_ROWS_GET_REQUEST(action: PayloadAction { if (PLATFORM == Platform.IOS) { return SQLiteRecordCacheService.getInstance(); } diff --git a/app/src/utils/record-cache/index.ts b/app/src/utils/record-cache/index.ts index a2f7ee092..9b0c91332 100644 --- a/app/src/utils/record-cache/index.ts +++ b/app/src/utils/record-cache/index.ts @@ -5,6 +5,7 @@ import { RecordSetType, UserRecordCacheStatus } from 'interfaces/UserRecordSet'; import { GeoJSONSourceSpecification } from 'maplibre-gl'; import { getCurrentJWT } from 'state/sagas/auth/auth'; import { getSelectColumnsByRecordSetType } from 'state/sagas/map/dataAccess'; +import BaseCacheService from 'utils/base-classes/BaseCacheService'; import { RepositoryBoundingBoxSpec } from 'utils/tile-cache'; export enum IappRecordMode { @@ -25,7 +26,7 @@ export interface RecordCacheDownloadRequestSpec { * @property { GeoJSONSourceSpecification } cachedCentroid Cached Points for high map layers * @property { UserRecordCacheStatus } status Cache Status. */ -export interface RecordCacheAddSpec { +export interface RepositoryMetadata { setId: string; cacheTime: Date; cachedIds: string[]; @@ -58,53 +59,57 @@ export interface CacheDownloadSpec { recordSetType: RecordSetType; } -abstract class RecordCacheService { +abstract class RecordCacheService extends BaseCacheService< + RepositoryMetadata, + CacheDownloadSpec, + RecordCacheProgressCallbackParameters, + UserRecordCacheStatus +> { private readonly RECORDS_BETWEEN_PROGRESS_UPDATES = 25; - protected constructor() {} + + protected constructor() { + super(); + } static async getInstance(): Promise { throw new Error('unimplemented in abstract base class'); } + protected abstract addOrUpdateRepository(spec: RepositoryMetadata): Promise; - abstract saveActivity(id: string, data: unknown): Promise; + protected abstract deleteCachedRecordsFromIds(idsToDelete: string[], recordSetType: RecordSetType): Promise; - abstract saveIapp(id: string, iappRecord: unknown, iappTableRow: unknown): Promise; + /** */ + public abstract loadActivity(id: string): Promise; - abstract deleteCachedRecordsFromIds(idsToDelete: string[], recordSetType: RecordSetType): Promise; + public abstract loadIapp(id: string, type: IappRecordMode): Promise; - abstract loadActivity(id: string): Promise; + protected abstract saveActivity(id: string, data: unknown): Promise; - abstract loadIapp(id: string, type: IappRecordMode): Promise; + protected abstract saveIapp(id: string, iappRecord: unknown, iappTableRow: unknown): Promise; - abstract fetchPaginatedCachedIappRecords( + public abstract getPaginatedCachedActivityRecords( recordSetIdList: string[], page: number, limit: number - ): Promise; - - abstract fetchPaginatedCachedRecords(recordSetIdList: string[], page: number, limit: number): Promise; - - abstract addOrUpdateRepository(spec: RecordCacheAddSpec): Promise; - - abstract deleteRepository(repositoryId: string): Promise; + ): Promise; - abstract fetchRepository(repositoryId: string): Promise; - - abstract isCached(repositoryId: string): Promise; - - abstract fetchIdList(repositoryId: string): Promise; + public abstract getPaginatedCachedIappRecords( + recordSetIdList: string[], + page: number, + limit: number + ): Promise; - abstract listRepositories(): Promise; + public abstract isCached(repositoryId: string): Promise; - abstract loadIappRecordsetSourceMetadata(ids: string[]): Promise; + public abstract getIdList(repositoryId: string): Promise; - abstract loadRecordsetSourceMetadata(ids: string[]): Promise; + protected abstract createIappRecordsetSourceMetadata(ids: string[]): Promise; - abstract setRepositoryStatus(repositoryId: string, status: UserRecordCacheStatus): Promise; + protected abstract createActivityRecordsetSourceMetadata(ids: string[]): Promise; abstract checkForAbort(id: string): Promise; - async downloadCache(spec: CacheDownloadSpec): Promise { + public async download(spec: CacheDownloadSpec): Promise { const args = { idsToCache: spec.idsToCache, setId: spec.setId, @@ -127,9 +132,9 @@ abstract class RecordCacheService { let downloadCompleted = true; if (spec.recordSetType === RecordSetType.Activity && (await this.downloadActivity(args))) { - Object.assign(responseData, await this.loadRecordsetSourceMetadata(spec.idsToCache)); + Object.assign(responseData, await this.createActivityRecordsetSourceMetadata(spec.idsToCache)); } else if (spec.recordSetType === RecordSetType.IAPP && (await this.downloadIapp(args))) { - Object.assign(responseData, await this.loadIappRecordsetSourceMetadata(spec.idsToCache)); + Object.assign(responseData, await this.createIappRecordsetSourceMetadata(spec.idsToCache)); } else { downloadCompleted = false; this.deleteRepository(spec.setId); @@ -153,7 +158,7 @@ abstract class RecordCacheService { * Download Records for IAPP Given a list of IDs * @returns { boolean } download was successful */ - async downloadIapp( + private async downloadIapp( spec: RecordCacheDownloadRequestSpec, progressCallback?: (currentProgress: RecordCacheProgressCallbackParameters) => void ): Promise { @@ -203,7 +208,7 @@ abstract class RecordCacheService { * Download Records for Activities Given a list of IDs * @returns { boolean } download was successful */ - async downloadActivity( + private async downloadActivity( spec: RecordCacheDownloadRequestSpec, progressCallback?: (currentProgress: RecordCacheProgressCallbackParameters) => void ): Promise { @@ -224,7 +229,7 @@ abstract class RecordCacheService { } return !abort; } - async stopDownload(repositoryId: string): Promise { + public async stopDownload(repositoryId: string): Promise { const repositories = await this.listRepositories(); const foundIndex = repositories.findIndex((repo) => repo.setId === repositoryId); if (foundIndex === -1) throw Error(`Repository ${repositoryId} wasn't found`); diff --git a/app/src/utils/record-cache/localforage-cache.ts b/app/src/utils/record-cache/localforage-cache.ts index 1e6dab83e..6ba5efb5f 100644 --- a/app/src/utils/record-cache/localforage-cache.ts +++ b/app/src/utils/record-cache/localforage-cache.ts @@ -3,7 +3,7 @@ import localForage from 'localforage'; import centroid from '@turf/centroid'; import { IappRecordMode, - RecordCacheAddSpec, + RepositoryMetadata, RecordCacheService, RecordSetSourceMetadata } from 'utils/record-cache/index'; @@ -34,13 +34,13 @@ class LocalForageRecordCacheService extends RecordCacheService { async isCached(repositoryId: string): Promise { try { - return (await this.fetchRepository(repositoryId)).status === UserRecordCacheStatus.CACHED; + return (await this.getRepository(repositoryId)).status === UserRecordCacheStatus.CACHED; } catch (e) { return false; } } - async fetchRepository(repositoryId: string): Promise { + async getRepository(repositoryId: string): Promise { const repos = await this.listRepositories(); const foundIndex = repos.findIndex((p) => p.setId === repositoryId); if (foundIndex === -1) throw Error(`Repository ${repositoryId} not found`); @@ -48,8 +48,8 @@ class LocalForageRecordCacheService extends RecordCacheService { return repos[foundIndex]; } - async fetchIdList(repositoryId: string): Promise { - return (await this.fetchRepository(repositoryId)).cachedIds ?? []; + async getIdList(repositoryId: string): Promise { + return (await this.getRepository(repositoryId)).cachedIds ?? []; } async saveActivity(id: string, data: unknown): Promise { @@ -80,6 +80,7 @@ class LocalForageRecordCacheService extends RecordCacheService { } return true; } + async saveIapp(id: string, iappRecord: IappRecord, iappTableRow: IappTableRow): Promise { if (this.store == null) { throw new Error('cache not available'); @@ -99,7 +100,7 @@ class LocalForageRecordCacheService extends RecordCacheService { return data[type]; } - async fetchPaginatedCachedIappRecords( + async getPaginatedCachedIappRecords( recordSetIdList: string[], page: number, limit: number, @@ -139,7 +140,11 @@ class LocalForageRecordCacheService extends RecordCacheService { * @param limit Maximum results per page * @returns { UserRecord[] } Filter Objects */ - async fetchPaginatedCachedRecords(recordSetIdList: string[], page: number, limit: number): Promise { + async getPaginatedCachedActivityRecords( + recordSetIdList: string[], + page: number, + limit: number + ): Promise { if (recordSetIdList?.length === 0) { return []; } @@ -159,7 +164,7 @@ class LocalForageRecordCacheService extends RecordCacheService { * @param ids ids to filter * @returns { RecordSetSourceMetadata } Returns cached GeoJson, all IAPP Sites are Points. */ - async loadIappRecordsetSourceMetadata(ids: string[]): Promise { + async createIappRecordsetSourceMetadata(ids: string[]): Promise { const geoJsonArr: any[] = []; for (const id of ids) { const data: IappRecord = await this.loadIapp(id, IappRecordMode.Row); @@ -183,7 +188,7 @@ class LocalForageRecordCacheService extends RecordCacheService { * @param ids ids to filter * @returns { RecordSetSourceMetadata } Two formatted queries for High/Low zoom layers */ - async loadRecordsetSourceMetadata(ids: string[]): Promise { + async createActivityRecordsetSourceMetadata(ids: string[]): Promise { const centroidArr: any[] = []; const geoJsonArr: any[] = []; @@ -257,7 +262,7 @@ class LocalForageRecordCacheService extends RecordCacheService { * @desc Create or Update an entry in the cachedSet Repository * @param newSet Data to update */ - async addOrUpdateRepository(newSet: RecordCacheAddSpec): Promise { + async addOrUpdateRepository(newSet: RepositoryMetadata): Promise { if (this.store == null) { throw new Error('cache not available'); } @@ -273,12 +278,12 @@ class LocalForageRecordCacheService extends RecordCacheService { await this.store.setItem(LocalForageRecordCacheService.CACHED_SETS_METADATA_KEY, cachedSets); } - async listRepositories(): Promise { + async listRepositories(): Promise { if (this.store == null) { return []; } - const metadata: RecordCacheAddSpec[] = + const metadata: RepositoryMetadata[] = (await this.store.getItem(LocalForageRecordCacheService.CACHED_SETS_METADATA_KEY)) ?? []; if (metadata == null) { console.error('expected key not found'); diff --git a/app/src/utils/record-cache/sqlite-cache.ts b/app/src/utils/record-cache/sqlite-cache.ts index 2156dc81b..69b29c742 100644 --- a/app/src/utils/record-cache/sqlite-cache.ts +++ b/app/src/utils/record-cache/sqlite-cache.ts @@ -8,7 +8,7 @@ import { RecordSetType, UserRecordCacheStatus } from 'interfaces/UserRecordSet'; import { GeoJSONSourceSpecification } from 'maplibre-gl'; import { IappRecordMode, - RecordCacheAddSpec, + RepositoryMetadata, RecordCacheService, RecordSetSourceMetadata } from 'utils/record-cache/index'; @@ -76,7 +76,7 @@ class SQLiteRecordCacheService extends RecordCacheService { return SQLiteRecordCacheService._instance; } - async addOrUpdateRepository(spec: RecordCacheAddSpec): Promise { + async addOrUpdateRepository(spec: RepositoryMetadata): Promise { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); } @@ -97,7 +97,7 @@ class SQLiteRecordCacheService extends RecordCacheService { } } - async fetchRepository(repositoryId: string): Promise { + async getRepository(repositoryId: string): Promise { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); } @@ -128,11 +128,11 @@ class SQLiteRecordCacheService extends RecordCacheService { return metadata?.values?.[0]?.['STATUS'] === UserRecordCacheStatus.CACHED; } - async fetchIdList(repositoryId: string): Promise { + async getIdList(repositoryId: string): Promise { if (this.cacheDB == null) { throw Error(CACHE_UNAVAILABLE); } - return (await this.fetchRepository(repositoryId)).cachedIds ?? []; + return (await this.getRepository(repositoryId)).cachedIds ?? []; } async deleteRepository(repositoryId: string): Promise { @@ -144,7 +144,7 @@ class SQLiteRecordCacheService extends RecordCacheService { `SELECT DATA FROM CACHE_METADATA` ); - const repositoryMetadata: RecordCacheAddSpec[] = + const repositoryMetadata: RepositoryMetadata[] = rawRepositoryMetadata?.values?.map((set) => JSON.parse(set['DATA'])) ?? []; const targetIndex = repositoryMetadata.findIndex((set) => set.setId === repositoryId); @@ -170,7 +170,7 @@ class SQLiteRecordCacheService extends RecordCacheService { ); } - async listRepositories(): Promise { + async listRepositories(): Promise { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); } @@ -179,7 +179,7 @@ class SQLiteRecordCacheService extends RecordCacheService { `SELECT DATA FROM CACHE_METADATA` ); - const response = repositories?.values?.map((entry) => JSON.parse(entry['DATA']) as RecordCacheAddSpec) ?? []; + const response = repositories?.values?.map((entry) => JSON.parse(entry['DATA']) as RepositoryMetadata) ?? []; return response; } @@ -187,7 +187,7 @@ class SQLiteRecordCacheService extends RecordCacheService { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); } - const currData = await this.fetchRepository(repositoryId); + const currData = await this.getRepository(repositoryId); if (Object.keys(currData).length === 0) return; // Repo doesn't exist. currData.status = status; @@ -226,7 +226,11 @@ class SQLiteRecordCacheService extends RecordCacheService { * @param limit Maximum results per page * @returns { UserRecord[] } Filter Objects */ - async fetchPaginatedCachedRecords(recordSetIdList: string[], page: number, limit: number): Promise { + async getPaginatedCachedActivityRecords( + recordSetIdList: string[], + page: number, + limit: number + ): Promise { if (!recordSetIdList || recordSetIdList.length === 0) { return []; } @@ -259,7 +263,7 @@ class SQLiteRecordCacheService extends RecordCacheService { return response; } - async fetchPaginatedCachedIappRecords(recordSetIdList: string[], page: number, limit: number): Promise { + async getPaginatedCachedIappRecords(recordSetIdList: string[], page: number, limit: number): Promise { if (!recordSetIdList || recordSetIdList.length === 0) { return []; } @@ -381,7 +385,7 @@ class SQLiteRecordCacheService extends RecordCacheService { return JSON.parse(result.values[0][dataType]); } - async loadIappRecordsetSourceMetadata(ids: string[]): Promise { + async createIappRecordsetSourceMetadata(ids: string[]): Promise { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); } @@ -403,7 +407,8 @@ class SQLiteRecordCacheService extends RecordCacheService { }; return { cachedGeoJson }; } - async loadRecordsetSourceMetadata(ids: string[]): Promise { + + async createActivityRecordsetSourceMetadata(ids: string[]): Promise { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); } @@ -428,6 +433,7 @@ class SQLiteRecordCacheService extends RecordCacheService { console.error('Error parsing record:', e); } }); + const cachedCentroid: GeoJSONSourceSpecification = { type: 'geojson', data: { @@ -444,6 +450,7 @@ class SQLiteRecordCacheService extends RecordCacheService { }; return { cachedCentroid, cachedGeoJson }; } + async deleteCachedRecordsFromIds(idsToDelete: string[], recordSetType: RecordSetType): Promise { if (this.cacheDB == null) { throw new Error(CACHE_UNAVAILABLE); @@ -473,6 +480,7 @@ class SQLiteRecordCacheService extends RecordCacheService { throw e; } } + private async initializeRecordCache(sqlite: SQLiteConnection) { // Hold Migrations as named variable so we can use length to update the Db version automagically // Note: toVersion must be an integer. diff --git a/app/src/utils/tile-cache/context.ts b/app/src/utils/tile-cache/context.ts index 3428863e5..b5568412f 100644 --- a/app/src/utils/tile-cache/context.ts +++ b/app/src/utils/tile-cache/context.ts @@ -5,7 +5,7 @@ import { SQLiteTileCacheService } from 'utils/tile-cache/sqlite-cache'; import { LocalForageCacheService } from 'utils/tile-cache/localforage-cache'; class TileCacheServiceFactory { - static async getPlatformInstance() { + static async getPlatformInstance(): Promise { if ([Platform.IOS, Platform.ANDROID].includes(PLATFORM)) { return SQLiteTileCacheService.getInstance(); } diff --git a/app/src/utils/tile-cache/index.ts b/app/src/utils/tile-cache/index.ts index 543db6a7f..a2e9a1373 100644 --- a/app/src/utils/tile-cache/index.ts +++ b/app/src/utils/tile-cache/index.ts @@ -1,3 +1,4 @@ +import BaseCacheService from 'utils/base-classes/BaseCacheService'; import { base64toBuffer, lat2tile, long2tile } from 'utils/tile-cache/helpers'; // base64-encoded blank tile image 256x256 (opaque, light blue) @@ -42,7 +43,6 @@ enum RepositoryStatus { FAILED = 'FAILED', UNKNOWN = 'UNKNOWN' } - interface TilePromise { id: string; url: string; @@ -50,6 +50,7 @@ interface TilePromise { y: number; z: number; } + export interface TileCacheProgressCallbackParameters { repository: string; message: string; @@ -64,8 +65,15 @@ export interface RepositoryStatistics { tileCount: number; } -abstract class TileCacheService { - protected constructor() {} +abstract class TileCacheService extends BaseCacheService< + RepositoryMetadata, + RepositoryDownloadRequestSpec, + TileCacheProgressCallbackParameters, + RepositoryStatus +> { + protected constructor() { + super(); + } static generateFallbackTile(): TileData { return { @@ -106,13 +114,7 @@ abstract class TileCacheService { abstract setTile(repository: string, z: number, x: number, y: number, tileData: Uint8Array): Promise; - abstract getRepository(id: string): Promise; - - abstract listRepositories(): Promise; - - abstract deleteRepository(repository: string): Promise; - - abstract setRepositoryStatus(repository: string, status: RepositoryStatus): Promise; + protected abstract addOrUpdateRepository(spec: RepositoryMetadata): Promise; private async downloadTile(tileDetails: TilePromise): Promise { const { id, url, x, y, z } = tileDetails; @@ -151,7 +153,7 @@ abstract class TileCacheService { const executing = new Set(); try { - await this.addRepository({ + await this.addOrUpdateRepository({ id: spec.id, status: RepositoryStatus.DOWNLOADING, maxZoom: spec.maxZoom, @@ -234,8 +236,6 @@ abstract class TileCacheService { public abstract updateDescription(repository: string, newDescription: string): Promise; protected abstract cleanupOrphanTiles(): Promise; - - protected abstract addRepository(spec: RepositoryMetadata): Promise; } export { TileCacheService, FALLBACK_IMAGE, RepositoryStatus }; diff --git a/app/src/utils/tile-cache/localforage-cache.ts b/app/src/utils/tile-cache/localforage-cache.ts index 58bc3cb84..ffdac1c13 100644 --- a/app/src/utils/tile-cache/localforage-cache.ts +++ b/app/src/utils/tile-cache/localforage-cache.ts @@ -222,7 +222,7 @@ class LocalForageCacheService extends TileCacheService { } } - protected async addRepository(spec: RepositoryMetadata) { + protected async addOrUpdateRepository(spec: RepositoryMetadata) { if (this.store == null) { throw new Error('cache not available'); } @@ -230,11 +230,11 @@ class LocalForageCacheService extends TileCacheService { const repositories = await this.listRepositories(); const foundIndex = repositories.findIndex((p) => p.id == spec.id); if (foundIndex !== -1) { - throw new Error('repository already exists'); + repositories[foundIndex] = spec; + } else { + repositories.push(spec); } - repositories.push(spec); - await this.store.setItem(LocalForageCacheService.REPOSITORY_METADATA_KEY, repositories); } diff --git a/app/src/utils/tile-cache/sqlite-cache.ts b/app/src/utils/tile-cache/sqlite-cache.ts index eaf0e9569..2b9dc56bf 100644 --- a/app/src/utils/tile-cache/sqlite-cache.ts +++ b/app/src/utils/tile-cache/sqlite-cache.ts @@ -190,7 +190,7 @@ class SQLiteTileCacheService extends TileCacheService { ); } - protected async addRepository(spec: RepositoryMetadata): Promise { + protected async addOrUpdateRepository(spec: RepositoryMetadata): Promise { if (this.cacheDB == null) { throw new Error('cache not available'); } @@ -207,7 +207,15 @@ class SQLiteTileCacheService extends TileCacheService { MIN_LONGITUDE, MAX_LONGITUDE) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - `, + ON CONFLICT(TILESET) + DO UPDATE SET + DESCRIPTION = excluded.DESCRIPTION, + STATUS = excluded.STATUS, + MAX_ZOOM = excluded.MAX_ZOOM, + MIN_LATITUDE = excluded.MIN_LATITUDE, + MAX_LATITUDE = excluded.MAX_LATITUDE, + MIN_LONGITUDE = excluded.MIN_LONGITUDE, + MAX_LONGITUDE = excluded.MAX_LONGITUDE`, [ spec.id, spec.description,