Skip to content

Fix timeline dedup over-collapse, empty-state icon, and tenant-scoped reads#30

Merged
AsmitNepali merged 2 commits into
1.xfrom
fix-activity-log-dedup
Jun 14, 2026
Merged

Fix timeline dedup over-collapse, empty-state icon, and tenant-scoped reads#30
AsmitNepali merged 2 commits into
1.xfrom
fix-activity-log-dedup

Conversation

@AsmitNepali

Copy link
Copy Markdown
Collaborator

Summary

Three fixes to the activity-log timeline, each with regression tests.

BR233-1 — multi-change save collapses to one entry (high)

ActivityLogSource keyed dedup as class:subjectId:second with no activity id, so several distinct activities written for the same subject in the same second (e.g. one save touching a core field + a custom field) collapsed into a single rendered entry while the DB held N.

Dedup key for the own-activity source now includes the activity id (dedupKeyForActivity()), so distinct rows stay separate. RelatedActivityLogSource keeps the second-precision key on purpose — that is what merges it against the synthetic RelatedModelSource events (covered by BuilderDedupTest).

BR233-3 — empty Activity log tab renders a blank card (low)

The empty-state branch in timeline.blade.php still referenced the Remix icon ri-history-line, missed by the Heroicons migration (#24) because it only renders when the timeline is empty. The missing icon set produced a blank card with no message. Swapped to heroicon-o-clock. The empty-state string was already passed non-empty from both call sites.

F3 — timeline read path not tenant-scoped (medium)

The read path resolved its model from config('activity-log.activity_model'), while hosts configure their (tenant-scoped) Activity subclass via Spatie's canonical activitylog.activity_model, which governs writes. The plugin config also defaulted to the base Spatie Activity, so reads ignored any subclass and ran without the team scope.

Reads now resolve activity-log.activity_modelactivitylog.activity_model → base Activity via a shared activityModelClass(). The plugin config default is null so it no longer shadows the Spatie key. A host can configure the scoped subclass once and have both writes and timeline reads honor it, even without publishing config/activity-log.php.

Tests

  • BuilderDedupTest — multi-change save yields N entries; existing cross-source dedup tests still pass.
  • ActivityModelResolutionTest (+ ScopedActivity fixture) — plugin key wins, falls back to Spatie key, then base model.
  • Full suite: 50 passed.

Out of scope

BR233-2 and BR233-4 target the host app's app/Observers/CustomFieldValueObserver.php, which is not part of this package.

… reads

- ActivityLogSource dedup key now includes the activity id so distinct
  activities in the same second (multi-field save) no longer collapse to one.
- Replace leftover Remix ri-history-line icon in the empty-state with
  heroicon-o-clock; the missing icon set rendered a blank card.
- Resolve the read-path Activity model from the plugin key, then Spatie's
  canonical activitylog.activity_model, then the base model, so a
  tenant-scoped Activity subclass applies to reads as well as writes.
@AsmitNepali AsmitNepali merged commit 27c9635 into 1.x Jun 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant