@stoker-platform/web-app 0.5.221 → 0.5.223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/package.json +4 -4
- package/src/Calendar.tsx +119 -45
- package/src/Collection.tsx +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @stoker-platform/web-app
|
|
2
2
|
|
|
3
|
+
## 0.5.223
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @stoker-platform/node-client@0.5.86
|
|
9
|
+
- @stoker-platform/web-client@0.5.92
|
|
10
|
+
- @stoker-platform/utils@0.5.77
|
|
11
|
+
|
|
12
|
+
## 0.5.222
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- feat: improve calendar data loading
|
|
17
|
+
|
|
3
18
|
## 0.5.221
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stoker-platform/web-app",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.223",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"scripts": {
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
52
52
|
"@react-google-maps/api": "^2.20.8",
|
|
53
53
|
"@sentry/react": "^10.56.0",
|
|
54
|
-
"@stoker-platform/node-client": "0.5.
|
|
55
|
-
"@stoker-platform/utils": "0.5.
|
|
56
|
-
"@stoker-platform/web-client": "0.5.
|
|
54
|
+
"@stoker-platform/node-client": "0.5.86",
|
|
55
|
+
"@stoker-platform/utils": "0.5.77",
|
|
56
|
+
"@stoker-platform/web-client": "0.5.92",
|
|
57
57
|
"@tanstack/react-table": "^8.21.3",
|
|
58
58
|
"@types/react": "18.3.13",
|
|
59
59
|
"@types/react-dom": "18.3.1",
|
package/src/Calendar.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
CollectionMeta,
|
|
4
4
|
CollectionSchema,
|
|
5
5
|
Filter,
|
|
6
|
+
PreloadCacheRange,
|
|
6
7
|
RangeFilter,
|
|
7
8
|
StokerCollection,
|
|
8
9
|
StokerPermissions,
|
|
@@ -37,12 +38,13 @@ import {
|
|
|
37
38
|
tryFunction,
|
|
38
39
|
} from "@stoker-platform/utils"
|
|
39
40
|
import cloneDeep from "lodash/cloneDeep.js"
|
|
40
|
-
import { useGlobalLoading } from "./providers/LoadingProvider"
|
|
41
|
+
import { useGlobalLoading, useRouteLoading } from "./providers/LoadingProvider"
|
|
41
42
|
import { useToast } from "./hooks/use-toast"
|
|
42
43
|
import { QueryConstraint, Timestamp, where } from "firebase/firestore"
|
|
43
44
|
import { Table, TableBody, TableCell, TableRow } from "./components/ui/table"
|
|
44
45
|
import { Grip } from "lucide-react"
|
|
45
46
|
import { useDrag } from "react-dnd"
|
|
47
|
+
import { useLocation } from "react-router"
|
|
46
48
|
|
|
47
49
|
import {
|
|
48
50
|
CalendarOptions,
|
|
@@ -257,6 +259,7 @@ export function Calendar({
|
|
|
257
259
|
const customization = getCollectionConfigModule(labels.collection)
|
|
258
260
|
const permissions = getCurrentUserPermissions()
|
|
259
261
|
if (!permissions?.Role) throw new Error("PERMISSION_DENIED")
|
|
262
|
+
const location = useLocation()
|
|
260
263
|
const goToRecord = useGoToRecord()
|
|
261
264
|
const { toast } = useToast()
|
|
262
265
|
const [connectionStatus] = useConnection()
|
|
@@ -280,6 +283,7 @@ export function Calendar({
|
|
|
280
283
|
|
|
281
284
|
const { optimisticUpdates, removeOptimisticUpdate, setOptimisticUpdate, removeCacheOptimistic } = useOptimistic()
|
|
282
285
|
const { isGlobalLoading, setGlobalLoading } = useGlobalLoading()
|
|
286
|
+
const { setIsRouteLoading } = useRouteLoading()
|
|
283
287
|
const [isInitialized, setIsInitialized] = useState(false)
|
|
284
288
|
const [mainCollectionLoaded, setMainCollectionLoaded] = useState(false)
|
|
285
289
|
const [additionalConfigLoaded, setAdditionalConfigLoaded] = useState(false)
|
|
@@ -307,6 +311,10 @@ export function Calendar({
|
|
|
307
311
|
const preloadRange = preloadRangeAll?.[labels.collection]
|
|
308
312
|
const isPreloadCacheEnabled = preloadCacheEnabled(collection)
|
|
309
313
|
|
|
314
|
+
const pendingPreload = useRef<{ preloadCacheRange: PreloadCacheRange; newRange: DateRange } | null>(null)
|
|
315
|
+
const isPreloading = useRef(false)
|
|
316
|
+
const isPreloadSpinnerShown = useRef(false)
|
|
317
|
+
|
|
310
318
|
const constraintsWithoutRangeFilter = useCallback(
|
|
311
319
|
(allFilters: Filter[]) => {
|
|
312
320
|
const newFilters = cloneDeep(allFilters)
|
|
@@ -1259,57 +1267,116 @@ export function Calendar({
|
|
|
1259
1267
|
}
|
|
1260
1268
|
}, [currentViewSmall])
|
|
1261
1269
|
|
|
1270
|
+
const runPreload = useCallback(
|
|
1271
|
+
async (preloadCacheRange: PreloadCacheRange, newRange: DateRange) => {
|
|
1272
|
+
const preloadedCollections: StokerCollection[] = [labels.collection]
|
|
1273
|
+
const preloads: Promise<void>[] = [preloadCollection(labels.collection, undefined, preloadCacheRange)]
|
|
1274
|
+
for (const additionalCollection of calendarConfig?.additionalCollections || []) {
|
|
1275
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
1276
|
+
const additionalSchema = schema.collections[additionalCollection]
|
|
1277
|
+
if (!additionalSchema?.preloadCache?.range || !preloadCacheEnabled(additionalSchema)) continue
|
|
1278
|
+
const preloadCacheRangeAdditional = cloneDeep(additionalSchema.preloadCache.range)
|
|
1279
|
+
preloadCacheRangeAdditional.start = preloadCacheRange.start
|
|
1280
|
+
preloadCacheRangeAdditional.end = preloadCacheRange.end
|
|
1281
|
+
preloads.push(preloadCollection(additionalCollection, undefined, preloadCacheRangeAdditional))
|
|
1282
|
+
preloadedCollections.push(additionalCollection)
|
|
1283
|
+
}
|
|
1284
|
+
try {
|
|
1285
|
+
await Promise.all(preloads)
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
console.error(error)
|
|
1288
|
+
return
|
|
1289
|
+
}
|
|
1290
|
+
setPreloadRange((prev) => {
|
|
1291
|
+
const next = { ...prev }
|
|
1292
|
+
for (const preloadedCollection of preloadedCollections) {
|
|
1293
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
1294
|
+
next[preloadedCollection] = newRange
|
|
1295
|
+
}
|
|
1296
|
+
return next
|
|
1297
|
+
})
|
|
1298
|
+
},
|
|
1299
|
+
[calendarConfig, labels.collection, schema.collections, setPreloadRange],
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
const requestPreload = useCallback(
|
|
1303
|
+
(preloadCacheRange: PreloadCacheRange, newRange: DateRange) => {
|
|
1304
|
+
pendingPreload.current = { preloadCacheRange, newRange }
|
|
1305
|
+
if (isPreloading.current) {
|
|
1306
|
+
if (!isPreloadSpinnerShown.current) {
|
|
1307
|
+
isPreloadSpinnerShown.current = true
|
|
1308
|
+
setIsRouteLoading("+", location.pathname, true)
|
|
1309
|
+
}
|
|
1310
|
+
return
|
|
1311
|
+
}
|
|
1312
|
+
isPreloading.current = true
|
|
1313
|
+
const run = async () => {
|
|
1314
|
+
try {
|
|
1315
|
+
while (pendingPreload.current) {
|
|
1316
|
+
const request = pendingPreload.current
|
|
1317
|
+
pendingPreload.current = null
|
|
1318
|
+
await runPreload(request.preloadCacheRange, request.newRange)
|
|
1319
|
+
}
|
|
1320
|
+
} finally {
|
|
1321
|
+
isPreloading.current = false
|
|
1322
|
+
if (isPreloadSpinnerShown.current) {
|
|
1323
|
+
isPreloadSpinnerShown.current = false
|
|
1324
|
+
setIsRouteLoading("-", location.pathname, true)
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
run()
|
|
1329
|
+
},
|
|
1330
|
+
[runPreload, setIsRouteLoading, location.pathname],
|
|
1331
|
+
)
|
|
1332
|
+
|
|
1333
|
+
useEffect(() => {
|
|
1334
|
+
return () => {
|
|
1335
|
+
if (isPreloadSpinnerShown.current) {
|
|
1336
|
+
isPreloadSpinnerShown.current = false
|
|
1337
|
+
setIsRouteLoading("-", location.pathname, true)
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}, [])
|
|
1341
|
+
|
|
1262
1342
|
const handleDateChange = useCallback(
|
|
1263
|
-
(date: Date) => {
|
|
1343
|
+
(date: Date, viewStart: Date, viewEnd: Date) => {
|
|
1264
1344
|
if (!isInitialized || (isPreloadCacheEnabled && !preloadRange)) return
|
|
1265
1345
|
const datetime = DateTime.fromJSDate(date)
|
|
1346
|
+
const requiredStart = DateTime.min(
|
|
1347
|
+
DateTime.fromJSDate(viewStart),
|
|
1348
|
+
datetime.minus(calendarConfig?.dataStartOffset || { months: 1 }),
|
|
1349
|
+
)
|
|
1350
|
+
const requiredEnd = DateTime.max(
|
|
1351
|
+
DateTime.fromJSDate(viewEnd),
|
|
1352
|
+
datetime.plus(calendarConfig?.dataEndOffset || { months: 1 }),
|
|
1353
|
+
)
|
|
1354
|
+
const expandStart = DateTime.min(
|
|
1355
|
+
DateTime.fromJSDate(viewStart),
|
|
1356
|
+
datetime.minus(calendarConfig?.dataStart || { months: 1 }),
|
|
1357
|
+
).toJSDate()
|
|
1358
|
+
const expandEnd = DateTime.max(
|
|
1359
|
+
DateTime.fromJSDate(viewEnd),
|
|
1360
|
+
datetime.plus(calendarConfig?.dataEnd || { months: 1 }),
|
|
1361
|
+
).toJSDate()
|
|
1266
1362
|
const newRange = {} as DateRange
|
|
1267
1363
|
if (currentField && preloadCache?.range) {
|
|
1268
1364
|
if (preloadRange?.from && preloadRange?.to) {
|
|
1269
1365
|
const preloadCacheRange = cloneDeep(preloadCache.range)
|
|
1270
|
-
if (
|
|
1271
|
-
|
|
1272
|
-
.minus(calendarConfig?.dataStartOffset || { months: 1 })
|
|
1273
|
-
.diff(DateTime.fromJSDate(preloadRange?.from)).milliseconds < 0
|
|
1274
|
-
) {
|
|
1275
|
-
preloadCacheRange.start = datetime.minus(calendarConfig?.dataStart || { months: 1 }).toJSDate()
|
|
1366
|
+
if (requiredStart.diff(DateTime.fromJSDate(preloadRange?.from)).milliseconds < 0) {
|
|
1367
|
+
preloadCacheRange.start = expandStart
|
|
1276
1368
|
} else {
|
|
1277
1369
|
preloadCacheRange.start = preloadRange?.from
|
|
1278
1370
|
}
|
|
1279
|
-
if (
|
|
1280
|
-
|
|
1281
|
-
.plus(calendarConfig?.dataEndOffset || { months: 1 })
|
|
1282
|
-
.diff(DateTime.fromJSDate(preloadRange?.to)).milliseconds > 0
|
|
1283
|
-
) {
|
|
1284
|
-
preloadCacheRange.end = datetime.plus(calendarConfig?.dataEnd || { months: 1 }).toJSDate()
|
|
1371
|
+
if (requiredEnd.diff(DateTime.fromJSDate(preloadRange?.to)).milliseconds > 0) {
|
|
1372
|
+
preloadCacheRange.end = expandEnd
|
|
1285
1373
|
} else {
|
|
1286
1374
|
preloadCacheRange.end = preloadRange?.to
|
|
1287
1375
|
}
|
|
1288
1376
|
newRange.from = preloadCacheRange.start
|
|
1289
1377
|
newRange.to = preloadCacheRange.end
|
|
1290
1378
|
if (!isEqual(newRange, preloadRange)) {
|
|
1291
|
-
|
|
1292
|
-
for (const additionalCollection of calendarConfig?.additionalCollections || []) {
|
|
1293
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
1294
|
-
const additionalSchema = schema.collections[additionalCollection]
|
|
1295
|
-
if (!additionalSchema?.preloadCache?.range || !preloadCacheEnabled(additionalSchema))
|
|
1296
|
-
continue
|
|
1297
|
-
const preloadCacheRangeAdditional = cloneDeep(additionalSchema.preloadCache.range)
|
|
1298
|
-
preloadCacheRangeAdditional.start = preloadCacheRange.start
|
|
1299
|
-
preloadCacheRangeAdditional.end = preloadCacheRange.end
|
|
1300
|
-
preloadCollection(additionalCollection, undefined, preloadCacheRangeAdditional)
|
|
1301
|
-
}
|
|
1302
|
-
setPreloadRange((prev) => {
|
|
1303
|
-
const next = {
|
|
1304
|
-
...prev,
|
|
1305
|
-
[labels.collection]: newRange,
|
|
1306
|
-
}
|
|
1307
|
-
for (const additionalCollection of calendarConfig?.additionalCollections || []) {
|
|
1308
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
1309
|
-
next[additionalCollection] = newRange
|
|
1310
|
-
}
|
|
1311
|
-
return next
|
|
1312
|
-
})
|
|
1379
|
+
requestPreload(preloadCacheRange, newRange)
|
|
1313
1380
|
}
|
|
1314
1381
|
}
|
|
1315
1382
|
} else {
|
|
@@ -1319,13 +1386,11 @@ export function Calendar({
|
|
|
1319
1386
|
if (
|
|
1320
1387
|
calendarConfig.dataStart &&
|
|
1321
1388
|
calendarConfig.dataEnd &&
|
|
1322
|
-
(
|
|
1323
|
-
.milliseconds
|
|
1324
|
-
datetime.plus(calendarConfig?.dataEndOffset || { months: 1 }).diff(DateTime.fromISO(range.to))
|
|
1325
|
-
.milliseconds > 0)
|
|
1389
|
+
(requiredStart.diff(DateTime.fromISO(range.from)).milliseconds < 0 ||
|
|
1390
|
+
requiredEnd.diff(DateTime.fromISO(range.to)).milliseconds > 0)
|
|
1326
1391
|
) {
|
|
1327
|
-
newRange.from =
|
|
1328
|
-
newRange.to =
|
|
1392
|
+
newRange.from = expandStart
|
|
1393
|
+
newRange.to = expandEnd
|
|
1329
1394
|
if (!isEqual(JSON.stringify(newRange), JSON.stringify(range))) {
|
|
1330
1395
|
const rangeFilterValue = {
|
|
1331
1396
|
type: "range" as const,
|
|
@@ -1337,7 +1402,16 @@ export function Calendar({
|
|
|
1337
1402
|
}
|
|
1338
1403
|
}
|
|
1339
1404
|
},
|
|
1340
|
-
[
|
|
1405
|
+
[
|
|
1406
|
+
calendarConfig,
|
|
1407
|
+
rangeFilter,
|
|
1408
|
+
preloadCache,
|
|
1409
|
+
currentField,
|
|
1410
|
+
preloadRange,
|
|
1411
|
+
isInitialized,
|
|
1412
|
+
isPreloadCacheEnabled,
|
|
1413
|
+
requestPreload,
|
|
1414
|
+
],
|
|
1341
1415
|
)
|
|
1342
1416
|
|
|
1343
1417
|
if (!calendarConfig || !permissions || (isPreloadCacheEnabled && preloadCache?.range && !currentField)) return null
|
|
@@ -1448,7 +1522,7 @@ export function Calendar({
|
|
|
1448
1522
|
datesSet={(arg: DatesSetArg) => {
|
|
1449
1523
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1450
1524
|
const date = (arg.view as any).getCurrentData().calendarApi.getDate()
|
|
1451
|
-
handleDateChange(date)
|
|
1525
|
+
handleDateChange(date, arg.start, arg.end)
|
|
1452
1526
|
setState(
|
|
1453
1527
|
`collection-calendar-large-date-${labels.collection.toLowerCase()}`,
|
|
1454
1528
|
"calendar-large-date",
|
|
@@ -1480,7 +1554,7 @@ export function Calendar({
|
|
|
1480
1554
|
datesSet={(arg: DatesSetArg) => {
|
|
1481
1555
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1482
1556
|
const date = (arg.view as any).getCurrentData().calendarApi.getDate()
|
|
1483
|
-
handleDateChange(date)
|
|
1557
|
+
handleDateChange(date, arg.start, arg.end)
|
|
1484
1558
|
setState(
|
|
1485
1559
|
`collection-calendar-small-date-${labels.collection.toLowerCase()}`,
|
|
1486
1560
|
"calendar-small-date",
|
package/src/Collection.tsx
CHANGED
|
@@ -875,6 +875,7 @@ function Collection({
|
|
|
875
875
|
}
|
|
876
876
|
}, [location.pathname, setIsRouteLoading])
|
|
877
877
|
const cacheLoaded = useCallback(() => {
|
|
878
|
+
if (!isCacheLoading.current) return
|
|
878
879
|
isCacheLoading.current = false
|
|
879
880
|
setIsRouteLoading("-", location.pathname, true)
|
|
880
881
|
setCacheLoadingCompleted(true)
|
|
@@ -883,14 +884,18 @@ function Collection({
|
|
|
883
884
|
const hasRunSingleton = useRef(false)
|
|
884
885
|
|
|
885
886
|
useEffect(() => {
|
|
887
|
+
// Prevent UI flicker
|
|
888
|
+
const cacheLoadedDelayed = () => setTimeout(() => cacheLoaded(), 100)
|
|
886
889
|
if (isPreloadCacheEnabled) {
|
|
887
890
|
const isPreloading = getLoadingState()[labels.collection]
|
|
888
891
|
if (!isPreloading || isPreloading === "Loading") {
|
|
889
892
|
cacheLoading()
|
|
893
|
+
} else if (isPreloading === "Loaded") {
|
|
894
|
+
cacheLoadingStarted.current = true
|
|
895
|
+
setCacheLoadingCompleted(true)
|
|
890
896
|
}
|
|
891
897
|
document.addEventListener(`stoker:loading:${labels.collection}`, cacheLoading)
|
|
892
|
-
|
|
893
|
-
document.addEventListener(`stoker:loaded:${labels.collection}`, () => setTimeout(() => cacheLoaded(), 100))
|
|
898
|
+
document.addEventListener(`stoker:loaded:${labels.collection}`, cacheLoadedDelayed)
|
|
894
899
|
}
|
|
895
900
|
|
|
896
901
|
const getSingleton = async () => {
|
|
@@ -1480,7 +1485,7 @@ function Collection({
|
|
|
1480
1485
|
unsubscribe.current = {}
|
|
1481
1486
|
if (isPreloadCacheEnabled) {
|
|
1482
1487
|
document.removeEventListener(`stoker:loading:${labels.collection}`, cacheLoading)
|
|
1483
|
-
document.removeEventListener(`stoker:loaded:${labels.collection}`,
|
|
1488
|
+
document.removeEventListener(`stoker:loaded:${labels.collection}`, cacheLoadedDelayed)
|
|
1484
1489
|
}
|
|
1485
1490
|
if (relationList?.loadAll) {
|
|
1486
1491
|
const unsubscribes = getPreloadListeners()[`${labels.collection}-${relationList.field}`]
|