Skip to content

fix(categories): import JSON from Android SAF and persist a single save - #956

Open
TimeToBuildBob wants to merge 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/category-import-android
Open

fix(categories): import JSON from Android SAF and persist a single save#956
TimeToBuildBob wants to merge 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/category-import-android

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Problem

v0.14.0b2 user report (ActivityWatch/aw-android#247):

  • In-app category import gives no feedback after picking a file.
  • Browser-mode import appears to succeed, then save "breaks down" and the app still shows default categories.

Two independent bugs:

  1. MIME-only reject. importCategories required file.type == 'application/json'. Android SAF/WebView often reports .json as application/octet-stream, empty, or text/plain, so import silently console.error'd and returned.
  2. Save race + unsynced import. import() wrote this.classes but not category_sets. save() then fired saveCategories() and saveClasses() as two un-awaited settingsStore.update() calls. Those two saves raced: one could persist an empty/default snapshot, reload it, and the other would write defaults back. jsonEq could also throw on an unserializable key and abort the whole save with no UI error.

Fix

  • Accept JSON by MIME, .json name, or Android-ambiguous types; reject camera/gallery images with a visible alert. Parse errors are visible instead of silent.
  • File input now has accept=".json,application/json" so the Android picker from refactor: refactored event viewer (in Query/Search views) into component, changed use of (broken) aw-timeline to vis-timeline #248 prefers JSON.
  • import() creates a primary set if needed, runs createMissingParents, and syncs into that set before save.
  • save() is async, awaits a single saveCategories() (which already writes the legacy classes field), and only clears the unsaved flag after success.
  • Settings jsonEq no longer throws the whole save if one key cannot be serialized.
  • Save failures surface an alert instead of a white screen.

Testing

npx jest --selectProjects node --testPathPattern 'importFile|store/categories' --coverage=false

21 passed (including new import-file MIME cases and import→primary-set sync).

Does not close ActivityWatch/aw-android#247 on its own: the Android app still needs an aw-webui submodule bump after this merges.

Android file picks often report .json as octet-stream, so the MIME-only
import check silently no-op'd. Import also left category_sets unsynced
and save() raced two settings updates, which could write defaults back.

ActivityWatch/aw-android#247
@greptile-apps

greptile-apps Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR makes category imports compatible with Android SAF file metadata and consolidates category persistence into one awaited settings update.

  • Accepts JSON imports using MIME type, filename, or Android-ambiguous file types and surfaces parse or save errors to users.
  • Synchronizes imported categories into the primary category set before persistence.
  • Removes the competing legacy save call and clears the unsaved state only after successful persistence.
  • Prevents serialization comparison failures from aborting an entire settings save.
  • Adds focused tests for import file handling and primary-set synchronization.

Confidence Score: 5/5

The PR appears safe to merge with no concrete changed-code defects identified.

The import flow now synchronizes category data before issuing a single awaited settings update, while rejected file parsing and explicit save failures receive visible handling.

Important Files Changed

Filename Overview
src/stores/categories.ts Synchronizes imports into category sets and converts saving to one awaited persistence operation.
src/stores/settings.ts Makes JSON equality comparison fail safely when a setting cannot be serialized.
src/util/classes.ts Returns the category settings update promise so callers can await persistence.
src/util/importFile.ts Adds Android-compatible file eligibility checks and top-level category export parsing.
src/views/settings/CategorizationSettings.vue Integrates the import helpers, resets file selection, and displays import and explicit-save failures.
test/unit/importFile.test.node.ts Covers Android MIME variations and recognized category export envelopes.
test/unit/store/categories.test.node.ts Verifies that a legacy import initializes and synchronizes a primary category set.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as Categorization settings
    participant Categories as Category store
    participant Settings as Settings store
    participant Service as ActivityWatch service
    User->>UI: Select category JSON
    UI->>UI: Check file type and parse export
    UI->>Categories: Import categories
    Categories->>Categories: Add missing parents and sync primary set
    User->>UI: Save
    UI->>Categories: await save()
    Categories->>Settings: update category sets, active IDs, legacy classes
    Settings->>Service: Persist one settings snapshot
    Service-->>Settings: Save result
    Settings-->>Categories: Resolve
    Categories->>Categories: Clear unsaved flag
Loading

Reviews (1): Last reviewed commit: "fix(categories): import JSON from Androi..." | Re-trigger Greptile

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.75%. Comparing base (3eebf68) to head (850cdff).

Files with missing lines Patch % Lines
src/stores/settings.ts 0.00% 6 Missing ⚠️
src/stores/categories.ts 90.90% 1 Missing ⚠️
src/util/classes.ts 0.00% 1 Missing ⚠️
src/util/importFile.ts 94.44% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #956      +/-   ##
==========================================
+ Coverage   49.62%   49.75%   +0.12%     
==========================================
  Files          46       47       +1     
  Lines        2783     2810      +27     
  Branches      630      608      -22     
==========================================
+ Hits         1381     1398      +17     
- Misses       1322     1391      +69     
+ Partials       80       21      -59     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…atch#652

aw-server-rust#652 moved --testing on a fresh machine to
~/.cache/activitywatch-testing/log/. The old glob still pointed at
the shared activitywatch root, so the rust-master matrix cell failed
after tests already passed.

Use a nullglob over activitywatch*/log so tagged releases and named
profiles both work, and missing logs no longer fail the job.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI red was not the category import: unit tests 35/35 and e2e 7/7 passed. Test (node-20, py-3.9, aw-server-rust master) died in the post-test log glob after ActivityWatch/aw-server-rust#652 moved --testing logs on a fresh runner to ~/.cache/activitywatch-testing/log/.

Pushed 850cdff to collect ~/.cache/activitywatch*/log/*/*.log with nullglob, so tagged releases and named profiles both work and missing logs no longer fail the job.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click.

This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted.

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.

User feedback on v0.14.02b

1 participant