Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[INV-3742][INV-3733][INV-3747][INV-3746] IAPP Records can Be cached and Used when Offline #3762

Open
wants to merge 23 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
57b4955
Set default Recordset name as '', put old default as falsey placeholder
LocalNewsTV Dec 16, 2024
86dfad6
remove logic that would block cache button from IAPP recordsets
LocalNewsTV Dec 16, 2024
4f5a4b0
Update/add stub interfaces for Iapp and Activity
LocalNewsTV Dec 17, 2024
2827734
Stub methods, parse json parse records in their promises
LocalNewsTV Dec 17, 2024
d5fe524
Add caching functionality for IAPP
LocalNewsTV Dec 17, 2024
755a518
Correct label for cached IAPP records
LocalNewsTV Dec 17, 2024
6a02a87
Implement createOfflineIappLayer, simplify workflow
LocalNewsTV Dec 17, 2024
58fd1ea
remove stale log
LocalNewsTV Dec 17, 2024
ae07072
Force cast IAPP Ids as strings
LocalNewsTV Dec 17, 2024
8945886
disable Asc/desc buttons on RecordTable when offline IAPP
LocalNewsTV Dec 17, 2024
583d526
Offline workflow to populate IAPP Records, destructure action.payload
LocalNewsTV Dec 17, 2024
67c5a72
remove unused offline actions
LocalNewsTV Dec 17, 2024
d2eec4d
delete all actions that are not imported outside of actions.ts
LocalNewsTV Dec 17, 2024
9998d6d
Add IappActions class
LocalNewsTV Dec 18, 2024
6d36ad1
Add abstract loadIapp() to RecordCache class
LocalNewsTV Dec 18, 2024
8d166b9
Refactor Iapp 'actions' to 'actionCreators'
LocalNewsTV Dec 18, 2024
38c4b8b
Add Back Button to IAPP records
LocalNewsTV Dec 18, 2024
17c7057
Cleanup/Correct IAPP actions
LocalNewsTV Dec 18, 2024
09be7df
Modify IAPP Whats Here workflow for offline
LocalNewsTV Dec 18, 2024
8be9c80
Sqlite iapp records appear on Map while offline
LocalNewsTV Dec 24, 2024
7621889
Cast ID lists as string for consistency between Activity/IAPP. IAPP A…
LocalNewsTV Dec 24, 2024
0b80f7a
Full IAPP Records load from Offline cache.
LocalNewsTV Dec 24, 2024
87d9ab1
Merge branch 'dev' into offline_iapp_records
LocalNewsTV Dec 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 50 additions & 62 deletions app/src/UI/LegacyMap/helpers/recordset-layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,23 @@ import { MOBILE } from 'state/build-time-config';
import { RecordSetType } from 'interfaces/UserRecordSet';
import VECTOR_MAP_FONT_FACE from 'constants/vectorMapFontFace';

export const createIAPPLayer = (map: any, layer: any, mode, API_BASE) => {
const layerID = 'recordset-layer-' + layer.recordSetID + '-hash-' + layer.tableFiltersHash;
export const createOfflineIappLayer = (map: maplibregl.Map, layer: any) => {
if (!layer?.layerState?.cacheMetadata?.hasOwnProperty('cachedGeoJson')) {
return;
}
const layerID = `recordset-layer-${layer.recordSetID}-hash-${layer.tableFiltersHash}`;
const source: GeoJSONSourceSpecification = layer.layerState.cacheMetadata.cachedGeoJson;
const color: string = layer.layerState.color ?? FALLBACK_COLOR;
const labelLayer: SymbolLayerSpecification = getLabelLayer(layerID, { color, minzoom: 10, get_tag: 'name' });
const circleLayer: CircleLayerSpecification = getCircleMarkerZoomedOutLayer(layerID, { color });
map.addSource(layerID, source);
map.addLayer(circleLayer, LAYER_Z_FOREGROUND);
map.addLayer(labelLayer, LAYER_Z_FOREGROUND);
};

let source = {};
export const createOnlineIappLayer = (map: any, layer: any, mode: string, API_BASE: string) => {
const layerID = `recordset-layer-${layer.recordSetID}-hash-${layer.tableFiltersHash}`;
let source: SourceSpecification;
if (mode === 'VECTOR_ENDPOINT') {
source = {
type: 'vector',
Expand All @@ -41,41 +54,9 @@ export const createIAPPLayer = (map: any, layer: any, mode, API_BASE) => {
}

map.addSource(layerID, source).addLayer(circleLayer, LAYER_Z_MID);

map.addLayer(labelLayer, LAYER_Z_BACKGROUND);
};

export const deleteStaleIAPPLayer = (map: any, layer: any, mode) => {
const allLayersForRecordSet = map.getLayersOrder().filter((mapLayer: any) => {
return mapLayer.includes('recordset-layer-' + layer.recordSetID) || mapLayer.includes('label-' + layer.recordSetID);
});

const stale = allLayersForRecordSet.filter((mapLayer) => {
return !mapLayer.includes(layer.tableFiltersHash);
});

stale.map((staleLayer) => {
try {
map.removeLayer(staleLayer);
} catch (e) {
console.error('error removing layer' + staleLayer);
}
});

const staleSources = Object.keys(map.style.sourceCaches).filter((source) => {
return source.includes('recordset-layer-' + layer.recordSetID) && !source.includes(layer.tableFiltersHash);
});

staleSources?.map((staleSource) => {
if (map.getSource(staleSource)) {
try {
map.removeSource(staleSource);
} catch (e) {
console.error('error removing source', e);
}
}
});
};
const getPaintBySchemeOrColor = (layer: any) => {
if (layer?.layerState?.colorScheme) {
return [
Expand Down Expand Up @@ -238,7 +219,7 @@ export const createOfflineActivityLayer = (map: maplibregl.Map, layer: any) => {
map.addLayer(circleMarkerZoomedOutLayerCentroid, LAYER_Z_FOREGROUND);
};

export const createActivityLayer = (map: maplibregl.Map, layer: any, mode, API_BASE) => {
export const createOnlineActivityLayer = (map: maplibregl.Map, layer: any, mode, API_BASE) => {
const layerID = 'recordset-layer-' + layer.recordSetID + '-hash-' + layer.tableFiltersHash;

if (['1', '2'].includes(layer.recordSetID) && !layer.layerState.colorScheme) {
Expand Down Expand Up @@ -283,7 +264,7 @@ export const createActivityLayer = (map: maplibregl.Map, layer: any, mode, API_B
map.addLayer(labelLayer, LAYER_Z_FOREGROUND);
};

export const deleteStaleActivityLayer = (map: maplibregl.Map, layer: Record<PropertyKey, any>) => {
export const deleteStaleRecordsetLayer = (map: maplibregl.Map, layer: Record<PropertyKey, any>) => {
if (!map) {
return;
}
Expand All @@ -296,9 +277,7 @@ export const deleteStaleActivityLayer = (map: maplibregl.Map, layer: Record<Prop
mapLayer.includes('polygon-circle-' + layer.recordSetID)
);
});
const stale = allLayersForRecordSet.filter((mapLayer) => {
return !mapLayer.includes(layer.tableFiltersHash);
});
const stale = allLayersForRecordSet.filter((mapLayer) => !mapLayer.includes(layer.tableFiltersHash));

stale.forEach((staleLayer) => {
try {
Expand Down Expand Up @@ -390,33 +369,42 @@ export const rebuildLayersOnTableHashUpdate = (
});

// now update the layers that are in the store
storeLayers.map((layer: any) => {
storeLayers.map((layer: Record<PropertyKey, any>) => {
if ((layer.geoJSON && layer.loading === false) || (mode === 'VECTOR_ENDPOINT' && layer.filterObject)) {
if (layer.type === RecordSetType.Activity) {
deleteStaleActivityLayer(map, layer);
const existingSource = map.getSource(
'recordset-layer-' + layer.recordSetID + '-hash-' + layer.tableFiltersHash
);
if (existingSource === undefined) {
if (MOBILE_OFFLINE) {
createOfflineActivityLayer(map, layer);
} else {
createActivityLayer(map, layer, mode, API_BASE);
}
}
} else if (layer.type === RecordSetType.IAPP && !MOBILE_OFFLINE) {
deleteStaleIAPPLayer(map, layer, mode);
const existingSource = map.getSource(
'recordset-layer-' + layer.recordSetID + '-hash-' + layer.tableFiltersHash
);
if (existingSource === undefined) {
createIAPPLayer(map, layer, mode, API_BASE);
}
}
const sourceId = `recordset-layer-${layer.recordSetID}-hash-${layer.tableFiltersHash}`;
deleteStaleRecordsetLayer(map, layer);
const existingSource = map.getSource(sourceId);
if (existingSource) return;
createMapLayer(map, layer, mode, API_BASE, MOBILE_OFFLINE);
}
});
};

/**
* @desc Handler logic for creating a new layer based on Network condition and recordset type
*/
const createMapLayer = (
map: maplibregl.Map,
layer: Record<PropertyKey, any>,
mapMode: string,
apiBase: string,
isOfflineLayer: boolean
): void => {
if (layer.type === RecordSetType.Activity) {
if (isOfflineLayer) {
createOfflineActivityLayer(map, layer);
} else {
createOnlineActivityLayer(map, layer, mapMode, apiBase);
}
} else if (layer.type === RecordSetType.IAPP) {
if (isOfflineLayer) {
createOfflineIappLayer(map, layer);
} else {
createOnlineIappLayer(map, layer, mapMode, apiBase);
}
}
};

export const refreshColoursOnColourUpdate = (storeLayers, map: maplibregl.Map) => {
for (const layer of storeLayers) {
const layerSearchString = layer.recordSetID + '-hash-' + layer.tableFiltersHash;
Expand Down
25 changes: 17 additions & 8 deletions app/src/UI/Overlay/IAPP/IAPPRecord.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React, { useEffect } from 'react';
import { useEffect } from 'react';

import './IAPPRecords.css';
import { Route, useHistory, useParams, useRouteMatch } from 'react-router';
import { Route, useHistory, useParams } from 'react-router';
import { useDispatch, useSelector } from 'react-redux';
import { OverlayHeader } from '../OverlayHeader';
import { Button } from '@mui/material';
import { Summary } from './Summary';
import { Photos } from './Photos';
import { IAPP_GET_REQUEST, IAPP_PAN_AND_ZOOM } from '../../../state/actions';
import { IAPP_PAN_AND_ZOOM } from '../../../state/actions';
import IappActions from 'state/actions/activity/Iapp';

export const IAPPRecord = (props) => {
export const IAPPRecord = () => {
const history = useHistory();
const dispatch = useDispatch();

Expand All @@ -18,8 +19,9 @@ export const IAPPRecord = (props) => {
const IAPPState = useSelector((state: any) => state?.IAPPSitePage);

useEffect(() => {
if (id !== null && id !== undefined && id !== '' && id !== 'undefined')
dispatch({ type: IAPP_GET_REQUEST, payload: { iappID: id } });
if (id && id !== 'undefined') {
dispatch(IappActions.get(id));
}
}, [id]);

return (
Expand Down Expand Up @@ -53,6 +55,11 @@ export const IAPPRecord = (props) => {
</Button>
</div>
</div>
<div className="control">
<Button variant="contained" color="primary" onClick={() => history.goBack()}>
{'< Back'}
</Button>
</div>
<Route
path="/Records/IAPP/:id/summary"
render={() => {
Expand All @@ -62,9 +69,11 @@ export const IAPPRecord = (props) => {
}, 3000);
return <div>Activity does not exists, redirecting...</div>;
}
if ((IAPPState?.site as any)?.site_id && IAPPState?.loading === false)
if ((IAPPState?.site as any)?.site_id && IAPPState?.loading === false) {
return <Summary record={IAPPState?.site} />;
else return <div>loading</div>;
} else {
return <div>loading</div>;
}
}}
/>
<Route
Expand Down
8 changes: 8 additions & 0 deletions app/src/UI/Overlay/IAPP/IAPPRecords.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@
color: white;
}

.records__activity .control {
width: 100%;
max-width: 1200px;
color: red;
padding: 10pt;
display: flex;
}

.records__activity__header {
/* same height as the apps header and footer */
width: 100%;
Expand Down
4 changes: 3 additions & 1 deletion app/src/UI/Overlay/Records/RecordSet/RecordSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export const RecordSet = (props) => {
{'< Back'}
</Button>
</div>
<div className="recordSet_header_name">{recordSet?.recordSetName}</div>
<div className="recordSet_header_name">
{recordSet?.recordSetName || `New Recordset - ${recordSet?.recordSetType}`}
</div>
</div>
</div>
<div className="recordSet_filter_buttons_container">
Expand Down
5 changes: 3 additions & 2 deletions app/src/UI/Overlay/Records/RecordSet/RecordTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const RecordTable = ({ setID, userOfflineMobile }: PropTypes) => {
const unmappedRows = useSelector((state) => state.Map?.recordTables?.[setID]?.rows);
const tableType = useSelector((state) => state.UserSettings?.recordSets?.[setID].recordSetType);
const activitySortColumns = userOfflineMobile ? [] : validActivitySortColumns;
const iappSortColumns = userOfflineMobile ? [] : validIAPPSortColumns;
const dispatch = useDispatch();
const isTouch = detectTouchDevice();
const mappedRows = unmappedRows?.map((row) => {
Expand Down Expand Up @@ -80,13 +81,13 @@ export const RecordTable = ({ setID, userOfflineMobile }: PropTypes) => {
className="record_table_header_column"
key={col.key}
onClick={() => {
if (validIAPPSortColumns.includes(col.key)) {
if (iappSortColumns.includes(col.key)) {
dispatch({ type: RECORDSET_SET_SORT, payload: { setID: setID, sortColumn: col.key } });
}
}}
>
{col.name}{' '}
{validIAPPSortColumns.includes(sortColumn) &&
{iappSortColumns.includes(sortColumn) &&
sortColumn === col.key &&
(sortOrder === 'ASC' ? '▲' : '▼')}
</th>
Expand Down
5 changes: 1 addition & 4 deletions app/src/UI/Overlay/Records/RecordSetCacheButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RecordSetType, UserRecordCacheStatus, UserRecordSet } from 'interfaces/UserRecordSet';
import { UserRecordCacheStatus, UserRecordSet } from 'interfaces/UserRecordSet';
import { Button, Tooltip } from '@mui/material';
import SaveIcon from '@mui/icons-material/Save';
import { MouseEvent, useEffect, useState } from 'react';
Expand Down Expand Up @@ -80,9 +80,6 @@ const RecordSetCacheButtons = ({ recordSet, setId }: PropTypes) => {
);
}, [recordSet.cacheMetadata?.status, connected]);

if (recordSet.recordSetType !== RecordSetType.Activity) {
return;
}
return (
<Tooltip classes={{ tooltip: 'toolTip' }} title="Click to save this layer and it's records">
<span>
Expand Down
13 changes: 11 additions & 2 deletions app/src/UI/Overlay/Records/RecordSetDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Check, Edit } from '@mui/icons-material';
import { IconButton } from '@mui/material';
import { RecordSetType } from 'interfaces/UserRecordSet';
import { MouseEvent, ReactNode, useState } from 'react';

type PropTypes = {
Expand All @@ -8,8 +9,16 @@ type PropTypes = {
isDefaultRecordset: boolean;
handleNameChange: (val: string, recordsetKey: string) => void;
children?: ReactNode;
recordSetType: RecordSetType;
};
const RecordSetDetails = ({ name, isDefaultRecordset, handleNameChange, recordsetKey, children }: PropTypes) => {
const RecordSetDetails = ({
name,
isDefaultRecordset,
handleNameChange,
recordsetKey,
recordSetType,
children
}: PropTypes) => {
const [isEditing, setIsEditing] = useState<boolean>(false);
const toggleEdit = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand All @@ -34,7 +43,7 @@ const RecordSetDetails = ({ name, isDefaultRecordset, handleNameChange, recordse
onClick={(e) => e.stopPropagation()}
/>
) : (
<p>{name}</p>
<p>{name || `New Recordset - ${recordSetType}`}</p>
)}
<IconButton color="primary" onClick={toggleEdit}>
{isEditing ? <Check /> : <Edit />}
Expand Down
1 change: 1 addition & 0 deletions app/src/UI/Overlay/Records/Records.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const Records = () => {
name={recordSets[set]?.recordSetName}
isDefaultRecordset={DEFAULT_RECORD_TYPES.includes(recordSets[set]?.recordSetName)}
handleNameChange={handleNameChange}
recordSetType={recordSets[set].recordSetType}
recordsetKey={set}
></RecordSetDetails>

Expand Down
8 changes: 8 additions & 0 deletions app/src/interfaces/IappRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Stub interface to reduce later refactoring
*/
interface IappRecord {
[key: PropertyKey]: any;
}

export default IappRecord;
8 changes: 8 additions & 0 deletions app/src/interfaces/IappTableRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Stub interface to reduce later refactoring
*/
interface IappTableRow {
[key: PropertyKey]: any;
}

export default IappTableRow;
2 changes: 1 addition & 1 deletion app/src/interfaces/UserRecord.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Stub interface to reduce later refactoring
*/
interface UserRecord {
[key: string]: any;
[key: PropertyKey]: any;
}

export default UserRecord;
Loading
Loading