Skip to content

feat: Add sync_at property for summary_data documents - #266

Merged
miguelccodev merged 2 commits into
developfrom
feature/mr-86--add-sync_up-property
Jun 10, 2026
Merged

feat: Add sync_at property for summary_data documents#266
miguelccodev merged 2 commits into
developfrom
feature/mr-86--add-sync_up-property

Conversation

@miguelccodev

@miguelccodev miguelccodev commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Changes

  • passed user id to handler
  • handler now monitors changes summary data's pending commits
  • monitor summary data on initialization

How to test

  • Start emulator
  • disable wifi and data
  • Complete an ftm puzzle or level
  • enable wifi and data to trigger sync

Notes:
You will need to add android sdk to your environment variables when working on windows.

adb shell svc wifi disable
adb shell svc data disable

Ref: MR-86

Summary by CodeRabbit

Release Notes

  • New Features

    • Documents now include a synchronization timestamp indicating when changes are successfully confirmed on the server, improving transparency into data sync status.
  • Refactor

    • Optimized initialization logic for internal components.

@miguelccodev
miguelccodev requested a review from janfb-codev June 9, 2026 17:24
@miguelccodev miguelccodev self-assigned this Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Handler initialization is refactored to accept pseudoId, enabling a new Firestore metadata snapshot listener infrastructure in DefaultAppEventPayloadHandler. After successful writes, per-document metadata listeners are attached to detect when pending writes are server-confirmed, then a synced_at timestamp is stamped into the document.

Changes

Server-confirmed sync stamping via metadata listeners

Layer / File(s) Summary
Handler initialization with pseudoId parameter
app/src/main/java/org/curiouslearning/container/WebApp.java
WebAppInterface handler field initialization is moved from inline constructor invocation to constructor-based assignment with pseudoId parameter.
Sync listener state and pre-initialization
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
Firestore metadata snapshot imports (ListenerRegistration, MetadataChanges) are added; syncListeners map tracks active listeners per document id; constructor immediately pre-attaches listeners to existing summary_data documents for the given cr_user_id via validation and async query.
Sync listener attachment in write completion handlers
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
After successful user_sessions_data, summary_data existing, and summary_data new writes, attachSyncListener is called with the document reference.
Metadata listener implementation and synced_at stamping
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
attachSyncListener registers one metadata listener per document; when pending writes clear and data is server-confirmed, the listener detaches and merges synced_at timestamp into the document.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • curiouslearning/CRcontainer#260: Both PRs modify DefaultAppEventPayloadHandler's Firestore write/update flow for summary_data (retrieved adds created_at/updated_at and merge behavior, main adds server-confirmed synced_at via per-document metadata snapshot listeners), so the changes are code-level related.

Suggested reviewers

  • janfb-codev
  • dz4va
  • ashwinnair-chimple

Poem

🐰 A handler born anew with a pseudoId heart,
Watches metadata whispers from Firestore's part,
When writes settle down and the server confirms,
A timestamp appears—synced_at gently turns,
One listener per doc, no duplicates here,
The synchronized dance brings the data so clear! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Tests & Lint & Coverage ⚠️ Warning PR adds 86+ lines to DefaultAppEventPayloadHandler without corresponding unit tests; no tests added for modified classes; no linting configured or run; coverage verification impossible. Add unit tests for DefaultAppEventPayloadHandler (sync listener logic) and WebApp modifications; configure and run linting; run coverage checks to meet 70% requirement.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a synced_at property to summary_data documents, which aligns with the primary objective and the substantial changes in DefaultAppEventPayloadHandler.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mr-86--add-sync_up-property

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (2)

350-365: ⚡ Quick win

Consider logging listener errors for observability.

When error != null, the listener silently returns without logging. This could make debugging difficult if listeners fail unexpectedly (e.g., permission errors, network issues).

♻️ Suggested improvement
         holder[0] = docRef.addSnapshotListener(MetadataChanges.INCLUDE, (snapshot, error) -> {
-            if (snapshot == null || error != null) return;
+            if (error != null) {
+                Log.w(TAG, "Sync listener error for docId=" + docId, error);
+                return;
+            }
+            if (snapshot == null) return;
             if (!snapshot.getMetadata().hasPendingWrites() && !snapshot.getMetadata().isFromCache()) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`
around lines 350 - 365, The snapshot listener currently returns silently when
error != null; update the lambda passed to docRef.addSnapshotListener (the
ListenerRegistration holder callback in DefaultAppEventPayloadHandler) to log
the error for observability—use Log.e(TAG, "Snapshot listener error for docId="
+ docId, error) (or similar) before returning, and ensure you still guard for
snapshot == null; also keep existing removal of the listener and syncListeners
logic unchanged.

31-37: ⚡ Quick win

Consider adding listener cleanup to prevent potential memory leaks.

The syncListeners map holds active ListenerRegistration instances, but there's no method to clean them up when the handler is no longer needed (e.g., when the WebApp activity is destroyed). If the device remains offline and listeners never fire, they could leak.

Consider adding a cleanup() method that unregisters all active listeners and clearing the map, then calling it from WebApp.onDestroy().

♻️ Suggested cleanup method
public void cleanup() {
    for (ListenerRegistration registration : syncListeners.values()) {
        if (registration != null) {
            registration.remove();
        }
    }
    syncListeners.clear();
    Log.d(TAG, "Cleaned up " + syncListeners.size() + " sync listeners");
}

Then in WebApp.java, store a reference to the handler and call cleanup in onDestroy().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`
around lines 31 - 37, The syncListeners map holds ListenerRegistration instances
but lacks teardown, so add a public cleanup() method on
DefaultAppEventPayloadHandler that iterates over syncListeners.values(), calls
remove() on each non-null ListenerRegistration, clears the map, and logs the
number cleaned; then ensure the WebApp that creates/holds the
DefaultAppEventPayloadHandler instance calls handler.cleanup() from its
onDestroy() so listeners are unregistered and memory leaks avoided.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 350-365: The snapshot listener currently returns silently when
error != null; update the lambda passed to docRef.addSnapshotListener (the
ListenerRegistration holder callback in DefaultAppEventPayloadHandler) to log
the error for observability—use Log.e(TAG, "Snapshot listener error for docId="
+ docId, error) (or similar) before returning, and ensure you still guard for
snapshot == null; also keep existing removal of the listener and syncListeners
logic unchanged.
- Around line 31-37: The syncListeners map holds ListenerRegistration instances
but lacks teardown, so add a public cleanup() method on
DefaultAppEventPayloadHandler that iterates over syncListeners.values(), calls
remove() on each non-null ListenerRegistration, clears the map, and logs the
number cleaned; then ensure the WebApp that creates/holds the
DefaultAppEventPayloadHandler instance calls handler.cleanup() from its
onDestroy() so listeners are unregistered and memory leaks avoided.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d458729-5f2f-4c99-b34c-c9af19088b4e

📥 Commits

Reviewing files that changed from the base of the PR and between 59aef4b and 3827ef0.

📒 Files selected for processing (2)
  • app/src/main/java/org/curiouslearning/container/WebApp.java
  • app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java

@miguelccodev
miguelccodev merged commit 5ad36d0 into develop Jun 10, 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.

3 participants