@archiva/archiva-nextjs 0.2.91 → 0.2.93

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.
@@ -370,23 +370,45 @@ function Timeline({
370
370
  }) {
371
371
  const { apiBaseUrl, getToken, forceRefreshToken } = useArchiva();
372
372
  const [allEvents, setAllEvents] = React2.useState([]);
373
+ const [previousEvents, setPreviousEvents] = React2.useState([]);
373
374
  const [cursor, setCursor] = React2.useState(void 0);
374
375
  const [loading, setLoading] = React2.useState(false);
375
376
  const [error, setError] = React2.useState(null);
376
377
  const [hasMore, setHasMore] = React2.useState(false);
377
378
  const [searchQuery, setSearchQuery] = React2.useState("");
379
+ const queryParamsRef = React2.useRef({});
380
+ const queryParamsKeyRef = React2.useRef("");
381
+ const queryParams = React2.useMemo(() => {
382
+ const params = {
383
+ entityId,
384
+ actorId,
385
+ entityType,
386
+ // Filter by actorType on API side
387
+ actorType: showSystemAndServices ? void 0 : "user",
388
+ limit: initialLimit
389
+ };
390
+ const key = JSON.stringify(params);
391
+ if (key !== queryParamsKeyRef.current) {
392
+ queryParamsKeyRef.current = key;
393
+ queryParamsRef.current = params;
394
+ return params;
395
+ }
396
+ return queryParamsRef.current;
397
+ }, [entityId, actorId, entityType, initialLimit, showSystemAndServices]);
398
+ const allEventsRef = React2.useRef([]);
399
+ React2.useEffect(() => {
400
+ allEventsRef.current = allEvents;
401
+ }, [allEvents]);
378
402
  const load = React2.useCallback(
379
403
  async (options) => {
380
404
  setLoading(true);
381
405
  setError(null);
406
+ if (options?.reset && allEventsRef.current.length > 0) {
407
+ setPreviousEvents(allEventsRef.current);
408
+ }
382
409
  try {
383
410
  const params = {
384
- entityId,
385
- actorId,
386
- entityType,
387
- // Filter by actorType on API side
388
- actorType: showSystemAndServices ? void 0 : "user",
389
- limit: initialLimit,
411
+ ...queryParams,
390
412
  cursor: options?.reset ? void 0 : options?.currentCursor ?? cursor
391
413
  };
392
414
  const response = await fetchEventsWithRetry(
@@ -395,34 +417,54 @@ function Timeline({
395
417
  forceRefreshToken,
396
418
  params
397
419
  );
398
- setAllEvents((prev) => options?.reset ? response.items : [...prev, ...response.items]);
420
+ setAllEvents((prev) => {
421
+ const newEvents = options?.reset ? response.items : [...prev, ...response.items];
422
+ if (options?.reset) {
423
+ setPreviousEvents([]);
424
+ }
425
+ return newEvents;
426
+ });
399
427
  setCursor(response.nextCursor);
400
428
  setHasMore(Boolean(response.nextCursor));
401
429
  } catch (err) {
402
430
  setError(err.message);
431
+ setPreviousEvents((prev) => {
432
+ if (options?.reset && prev.length > 0) {
433
+ setAllEvents(prev);
434
+ return [];
435
+ }
436
+ return prev;
437
+ });
403
438
  } finally {
404
439
  setLoading(false);
405
440
  }
406
441
  },
407
- [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor, showSystemAndServices]
442
+ [queryParams, apiBaseUrl, getToken, forceRefreshToken, cursor]
408
443
  );
409
444
  React2.useEffect(() => {
410
445
  setCursor(void 0);
411
446
  setAllEvents([]);
447
+ setPreviousEvents([]);
412
448
  void load({ reset: true });
413
- }, [entityId, actorId, entityType, showSystemAndServices]);
449
+ }, [entityId, actorId, entityType, showSystemAndServices, initialLimit]);
450
+ const eventsToFilter = React2.useMemo(() => {
451
+ if (loading && allEvents.length === 0 && previousEvents.length > 0) {
452
+ return previousEvents;
453
+ }
454
+ return allEvents;
455
+ }, [allEvents, loading, previousEvents]);
414
456
  const filteredEvents = React2.useMemo(() => {
415
- return applyClientSideFilters(allEvents, searchQuery, showSystemAndServices);
416
- }, [allEvents, searchQuery, showSystemAndServices]);
457
+ return applyClientSideFilters(eventsToFilter, searchQuery, showSystemAndServices);
458
+ }, [eventsToFilter, searchQuery, showSystemAndServices]);
417
459
  const timelineItems = React2.useMemo(() => {
418
460
  return filteredEvents.slice(0, 10).map(
419
461
  (event) => eventToTimelineItem(event, getActorAvatar)
420
462
  );
421
463
  }, [filteredEvents, getActorAvatar]);
422
- if (loading && allEvents.length === 0) {
464
+ if (loading && allEvents.length === 0 && previousEvents.length === 0) {
423
465
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: className || "", style: { width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: "3rem", textAlign: "center", color: "#6b7280" }, children: "Loading events..." }) });
424
466
  }
425
- if (error) {
467
+ if (error && allEvents.length === 0) {
426
468
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: className || "", style: { width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "1rem", color: "#dc2626", backgroundColor: "#fef2f2", borderRadius: "0.375rem" }, children: [
427
469
  "Error: ",
428
470
  error
@@ -438,13 +480,17 @@ function Timeline({
438
480
  placeholder: "Search actions, entities, IDs, actors, sources..."
439
481
  }
440
482
  ) }),
441
- searchQuery && filteredEvents.length !== allEvents.length && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "0.5rem", fontSize: "0.875rem", color: "#6b7280" }, children: [
483
+ (searchQuery || filteredEvents.length !== eventsToFilter.length) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "0.5rem", fontSize: "0.875rem", color: "#6b7280" }, children: [
442
484
  "Showing ",
443
485
  filteredEvents.length,
444
486
  " of ",
445
- allEvents.length,
487
+ eventsToFilter.length,
446
488
  " events"
447
489
  ] }),
490
+ error && allEvents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "1rem", padding: "0.75rem", color: "#dc2626", backgroundColor: "#fef2f2", borderRadius: "0.375rem", fontSize: "0.875rem" }, children: [
491
+ "Error: ",
492
+ error
493
+ ] }),
448
494
  timelineItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: "3rem", textAlign: "center", color: "#6b7280" }, children: searchQuery ? "No events match your search." : emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "relative", width: "100%" }, children: timelineItems.map((item, index) => {
449
495
  const useActivityLayout = !!(item.actorDisplay || item.userName || item.userHandle);
450
496
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
@@ -325,23 +325,45 @@ function Timeline({
325
325
  }) {
326
326
  const { apiBaseUrl, getToken, forceRefreshToken } = useArchiva();
327
327
  const [allEvents, setAllEvents] = React.useState([]);
328
+ const [previousEvents, setPreviousEvents] = React.useState([]);
328
329
  const [cursor, setCursor] = React.useState(void 0);
329
330
  const [loading, setLoading] = React.useState(false);
330
331
  const [error, setError] = React.useState(null);
331
332
  const [hasMore, setHasMore] = React.useState(false);
332
333
  const [searchQuery, setSearchQuery] = React.useState("");
334
+ const queryParamsRef = React.useRef({});
335
+ const queryParamsKeyRef = React.useRef("");
336
+ const queryParams = React.useMemo(() => {
337
+ const params = {
338
+ entityId,
339
+ actorId,
340
+ entityType,
341
+ // Filter by actorType on API side
342
+ actorType: showSystemAndServices ? void 0 : "user",
343
+ limit: initialLimit
344
+ };
345
+ const key = JSON.stringify(params);
346
+ if (key !== queryParamsKeyRef.current) {
347
+ queryParamsKeyRef.current = key;
348
+ queryParamsRef.current = params;
349
+ return params;
350
+ }
351
+ return queryParamsRef.current;
352
+ }, [entityId, actorId, entityType, initialLimit, showSystemAndServices]);
353
+ const allEventsRef = React.useRef([]);
354
+ React.useEffect(() => {
355
+ allEventsRef.current = allEvents;
356
+ }, [allEvents]);
333
357
  const load = React.useCallback(
334
358
  async (options) => {
335
359
  setLoading(true);
336
360
  setError(null);
361
+ if (options?.reset && allEventsRef.current.length > 0) {
362
+ setPreviousEvents(allEventsRef.current);
363
+ }
337
364
  try {
338
365
  const params = {
339
- entityId,
340
- actorId,
341
- entityType,
342
- // Filter by actorType on API side
343
- actorType: showSystemAndServices ? void 0 : "user",
344
- limit: initialLimit,
366
+ ...queryParams,
345
367
  cursor: options?.reset ? void 0 : options?.currentCursor ?? cursor
346
368
  };
347
369
  const response = await fetchEventsWithRetry(
@@ -350,34 +372,54 @@ function Timeline({
350
372
  forceRefreshToken,
351
373
  params
352
374
  );
353
- setAllEvents((prev) => options?.reset ? response.items : [...prev, ...response.items]);
375
+ setAllEvents((prev) => {
376
+ const newEvents = options?.reset ? response.items : [...prev, ...response.items];
377
+ if (options?.reset) {
378
+ setPreviousEvents([]);
379
+ }
380
+ return newEvents;
381
+ });
354
382
  setCursor(response.nextCursor);
355
383
  setHasMore(Boolean(response.nextCursor));
356
384
  } catch (err) {
357
385
  setError(err.message);
386
+ setPreviousEvents((prev) => {
387
+ if (options?.reset && prev.length > 0) {
388
+ setAllEvents(prev);
389
+ return [];
390
+ }
391
+ return prev;
392
+ });
358
393
  } finally {
359
394
  setLoading(false);
360
395
  }
361
396
  },
362
- [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor, showSystemAndServices]
397
+ [queryParams, apiBaseUrl, getToken, forceRefreshToken, cursor]
363
398
  );
364
399
  React.useEffect(() => {
365
400
  setCursor(void 0);
366
401
  setAllEvents([]);
402
+ setPreviousEvents([]);
367
403
  void load({ reset: true });
368
- }, [entityId, actorId, entityType, showSystemAndServices]);
404
+ }, [entityId, actorId, entityType, showSystemAndServices, initialLimit]);
405
+ const eventsToFilter = React.useMemo(() => {
406
+ if (loading && allEvents.length === 0 && previousEvents.length > 0) {
407
+ return previousEvents;
408
+ }
409
+ return allEvents;
410
+ }, [allEvents, loading, previousEvents]);
369
411
  const filteredEvents = React.useMemo(() => {
370
- return applyClientSideFilters(allEvents, searchQuery, showSystemAndServices);
371
- }, [allEvents, searchQuery, showSystemAndServices]);
412
+ return applyClientSideFilters(eventsToFilter, searchQuery, showSystemAndServices);
413
+ }, [eventsToFilter, searchQuery, showSystemAndServices]);
372
414
  const timelineItems = React.useMemo(() => {
373
415
  return filteredEvents.slice(0, 10).map(
374
416
  (event) => eventToTimelineItem(event, getActorAvatar)
375
417
  );
376
418
  }, [filteredEvents, getActorAvatar]);
377
- if (loading && allEvents.length === 0) {
419
+ if (loading && allEvents.length === 0 && previousEvents.length === 0) {
378
420
  return /* @__PURE__ */ jsx("div", { className: className || "", style: { width: "100%" }, children: /* @__PURE__ */ jsx("div", { style: { padding: "3rem", textAlign: "center", color: "#6b7280" }, children: "Loading events..." }) });
379
421
  }
380
- if (error) {
422
+ if (error && allEvents.length === 0) {
381
423
  return /* @__PURE__ */ jsx("div", { className: className || "", style: { width: "100%" }, children: /* @__PURE__ */ jsxs("div", { style: { padding: "1rem", color: "#dc2626", backgroundColor: "#fef2f2", borderRadius: "0.375rem" }, children: [
382
424
  "Error: ",
383
425
  error
@@ -393,13 +435,17 @@ function Timeline({
393
435
  placeholder: "Search actions, entities, IDs, actors, sources..."
394
436
  }
395
437
  ) }),
396
- searchQuery && filteredEvents.length !== allEvents.length && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.5rem", fontSize: "0.875rem", color: "#6b7280" }, children: [
438
+ (searchQuery || filteredEvents.length !== eventsToFilter.length) && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.5rem", fontSize: "0.875rem", color: "#6b7280" }, children: [
397
439
  "Showing ",
398
440
  filteredEvents.length,
399
441
  " of ",
400
- allEvents.length,
442
+ eventsToFilter.length,
401
443
  " events"
402
444
  ] }),
445
+ error && allEvents.length > 0 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "1rem", padding: "0.75rem", color: "#dc2626", backgroundColor: "#fef2f2", borderRadius: "0.375rem", fontSize: "0.875rem" }, children: [
446
+ "Error: ",
447
+ error
448
+ ] }),
403
449
  timelineItems.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: "3rem", textAlign: "center", color: "#6b7280" }, children: searchQuery ? "No events match your search." : emptyMessage }) : /* @__PURE__ */ jsx("div", { style: { position: "relative", width: "100%" }, children: timelineItems.map((item, index) => {
404
450
  const useActivityLayout = !!(item.actorDisplay || item.userName || item.userHandle);
405
451
  return /* @__PURE__ */ jsxs(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archiva/archiva-nextjs",
3
- "version": "0.2.91",
3
+ "version": "0.2.93",
4
4
  "description": "Archiva Next.js SDK - Server Actions and Timeline Component",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",