Skip to content

Prepare Mora v0.3.2 for signed release - #14

Merged
bjcdeshu merged 7 commits into
mainfrom
release/v0.3.2-polish
Jul 25, 2026
Merged

Prepare Mora v0.3.2 for signed release#14
bjcdeshu merged 7 commits into
mainfrom
release/v0.3.2-polish

Conversation

@bjcdeshu

@bjcdeshu bjcdeshu commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • prepare v0.3.2 / versionCode 6
  • make Reader Appearance scrollable in landscape and compact-height windows
  • serialize saves per document session, preserve revision-aware dirty state, and prevent stale Save As results from rebinding a newer document
  • replace the generic outlined-book launcher artwork with Mora's deep ink-blue / warm-white folded-page mark across adaptive, round, legacy, monochrome, and all three aliases
  • enable Release R8/resource shrinking, validate minified Release in normal CI with an ephemeral non-release signer, and retain the official R8 mapping privately in the signed workflow
  • refresh current screenshots, bilingual README, release/security/privacy/status/roadmap docs, issue intake, and changelog

Scope and non-goals

App Settings remains a scrollable Material bottom sheet. Predictive Back transforms the already laid-out sheet; an off-screen English row is not forced to reflow into view. Cancellation and completion are verified, so this platform behavior is intentionally accepted without gesture-driven auto-scroll or a custom sheet.

This PR does not add a vault, backlinks, graph views, accounts, cloud sync, plugins, syntax highlighting, math, or Mermaid. It does not change signing material, stable tag rules, v0.3.0's blocked Draft, or the public v0.3.1 Release.

Local validation

  • ./gradlew testDebugUnitTest lintDebug assembleDebug: passed
  • JVM tests: 63 passed, 0 failed/skipped
  • Debug Lint: passed (0 errors)
  • final Debug APK: 18,495,707 bytes
  • launcher resources: one shared vector geometry; all 20 legacy PNG dimensions/RGBA verified; adaptive/round/v33 monochrome/aliases resolve correctly
  • workflow and Issue-template YAML parsed; git diff --check passed

A local protected Release build was not substituted for CI: this Windows host's TLS path could not fetch one Release-only Compose mapping dependency. The PR job now performs the authoritative minified Release lint/build with a disposable non-release key; the tag workflow remains the only v0.3.2 Draft candidate path.

API 36 emulator smoke

On a dedicated Android 16/API 36 gestural-navigation AVD:

  • final Debug reinstall/launch and recent-document retention passed
  • open/read/scroll, TOC navigation, search, Home/Recent reading-position restoration passed
  • landscape Reader Appearance exposed and operated all three sliders; scrolling reached Restore defaults
  • rapid double Save wrote the same current file once; reopen preserved the exact edit
  • predictive Settings Back cancel and complete passed without crash or state loss
  • Indigo, Pine, and Night aliases each appeared in App Drawer and launched MainActivity
  • post-test FATAL/ANR scan: 0

The required physical-device check is deliberately after the tag workflow: the exact signed Draft APK must pass one current-phone smoke and the public v0.3.1 → v0.3.2 in-place upgrade before publication.

APK size

  • public v0.3.1 Release APK: 12,407,177 bytes
  • v0.3.2 signed/minified Release APK: pending the exact tag-generated Draft (to be recorded before publication)

The Debug size above is not compared with the signed Release because the build types are not equivalent.

Screenshots

Mora Home Mora Reader Mora table of contents Mora editor Mora launcher icon

Release plan

  1. Require green PR and final main CI, including the ephemeral minified-Release validation.
  2. Squash merge this one PR.
  3. Create annotated v0.3.2 on the exact final main commit.
  4. Let the protected tag workflow create the signed hidden Draft Pre-release and private R8 mapping artifact.
  5. Download and independently verify that exact Draft APK, sidecar, package/version/SDK metadata, and registered signer.
  6. Run one current Android phone smoke and v0.3.1 in-place upgrade using that exact APK.
  7. Only then turn the same Draft into stable/latest, redownload the public asset to confirm SHA-256 is unchanged, and open the bilingual feedback Issue.

Summary by CodeRabbit

  • New Features
    • Released Mora v0.3.2 with refreshed launcher artwork and updated appearance.
    • Release APKs now include SHA-256 verification information.
  • Bug Fixes
    • Improved saving reliability to prevent overlapping saves and lost edits.
    • Added clear saving progress feedback.
    • Save As now appears only when needed.
    • Improved Reader Appearance accessibility in landscape and compact windows.
    • Made App Settings content scrollable.
  • Documentation
    • Updated download, privacy, security, contribution, and release guidance.
    • Refined bug-report and feature-request forms.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@bjcdeshu, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 42abfb7c-4ada-4e90-abb5-2d99743a97b8

📥 Commits

Reviewing files that changed from the base of the PR and between e43f602 and 2d6af0c.

📒 Files selected for processing (2)
  • app/src/main/java/de/unbow/mora/ui/MoraApp.kt
  • app/src/test/java/de/unbow/mora/ui/DocumentRecreationStateTest.kt
📝 Walkthrough

Walkthrough

Version 0.3.2 updates revision-aware document saving, release build validation, signing and publication procedures, application visuals, issue intake, and product documentation.

Changes

v0.3.2 application behavior

Layer / File(s) Summary
Revision-aware save flow
app/src/main/java/de/unbow/mora/model/MarkdownViewModel.kt, app/src/main/java/de/unbow/mora/ui/MoraApp.kt, app/src/main/java/de/unbow/mora/ui/DocumentScreen.kt, app/src/test/...
Document saves use per-session snapshots and revisions, preserve dirty state for intervening edits, reject stale results, and expose saving progress in the UI with tests for coordination and routing.
Appearance and launcher presentation
app/src/main/java/.../AppearanceSheet.kt, app/src/main/res/drawable/*, app/src/main/res/values/*
The appearance sheet becomes vertically scrollable, launcher vector paths and colors are updated, and localized saving labels are added.

Release engineering

Layer / File(s) Summary
Minified release validation
.github/workflows/android.yml, app/build.gradle.kts, app/proguard-rules.pro
Release builds enable R8 and resource shrinking, preserve source mapping attributes, and validate APKs with ephemeral signing credentials and certificate checks.
Signed artifact handling
.github/workflows/release.yml, docs/RELEASING.md, PROJECT_STATUS.md, SECURITY.md
The signed release workflow uploads a private R8 mapping, while release guidance documents Draft verification, immutable tags, checksum checks, smoke testing, and publication constraints.

Product and repository documentation

Layer / File(s) Summary
Release and product documentation
CHANGELOG.md, PRIVACY.md, README.md, README.zh-CN.md, ROADMAP.md
Documentation describes v0.3.2 changes, local-first boundaries, backup behavior, build commands, roadmap priorities, and signed release downloads.
Issue intake updates
.github/ISSUE_TEMPLATE/*
Bug reports collect build source and device navigation details, feature requests add an appearance category, and security reports use a private advisory link.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Editor
  participant MoraApp
  participant MarkdownViewModel
  participant DocumentRepository
  Editor->>MoraApp: select Save
  MoraApp->>MarkdownViewModel: start save
  MarkdownViewModel->>DocumentRepository: write revisioned snapshot
  DocumentRepository-->>MarkdownViewModel: return write result
  MarkdownViewModel-->>Editor: update saving and dirty indicators
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: preparing Mora v0.3.2 for a signed release.
Description check ✅ Passed The description is detailed and covers changes, validation, scope, screenshots, and release planning, though it doesn't match the template headings exactly.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v0.3.2-polish

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.

@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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/de/unbow/mora/ui/MoraApp.kt (1)

290-297: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Bind the Save As result to the launching document session.

The callback calls saveAs against whichever document is current when it returns. If the document session changes while the picker is open, an old picker result can write the newer document to—and rebind it to—the old destination. Capture the launching sessionId and ignore results unless it still matches before persisting permission or calling saveAs.

Proposed fix
-    var saveAsPending by rememberSaveable(state.sessionId) { mutableStateOf(false) }
+    var saveAsRequestSessionId by rememberSaveable { mutableStateOf<Long?>(null) }
+    val saveAsPending = saveAsRequestSessionId == state.sessionId

     val createDocument = rememberLauncherForActivityResult(
         contract = ActivityResultContracts.CreateDocument("text/markdown"),
     ) { uri ->
-        saveAsPending = false
-        uri ?: return@rememberLauncherForActivityResult
+        val requestSessionId = saveAsRequestSessionId
+        saveAsRequestSessionId = null
+        if (uri == null || requestSessionId != markdownViewModel.uiState.sessionId) {
+            return@rememberLauncherForActivityResult
+        }
         DocumentRepository.persistPermission(context, uri)
         markdownViewModel.saveAs(context, uri, notifySave)
     }

-                                saveAsPending = true
+                                saveAsRequestSessionId = state.sessionId
                                 createDocument.launch(

Also applies to: 494-510

🤖 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/de/unbow/mora/ui/MoraApp.kt` around lines 290 - 297, Update
the CreateDocument launcher around createDocument to capture the current
document sessionId when launching Save As, then in its callback ignore results
whose sessionId no longer matches. Perform the session check before
DocumentRepository.persistPermission and markdownViewModel.saveAs, while
preserving saveAsPending cleanup and null-URI handling.
🤖 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.

Inline comments:
In @.github/workflows/android.yml:
- Around line 55-60: Update the RELEASE_MAPPING artifact path in
.github/workflows/android.yml (lines 55-60) and .github/workflows/release.yml
(lines 218-224) to use AGP’s actual release mapping filename, mapping.txt,
instead of mapping.prt; keep all checks and upload steps referencing the
corrected RELEASE_MAPPING value.

In `@app/src/main/java/de/unbow/mora/model/MarkdownViewModel.kt`:
- Around line 383-385: Update the failure branch in the save flow around
stateAfterSaveFailure and DocumentSaveResult.Failed to recompute isDirty after
isSaving is cleared, comparing the current content with persistedContent so
edits reverted during the active save are marked clean.

---

Outside diff comments:
In `@app/src/main/java/de/unbow/mora/ui/MoraApp.kt`:
- Around line 290-297: Update the CreateDocument launcher around createDocument
to capture the current document sessionId when launching Save As, then in its
callback ignore results whose sessionId no longer matches. Perform the session
check before DocumentRepository.persistPermission and markdownViewModel.saveAs,
while preserving saveAsPending cleanup and null-URI handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 02eb2559-418c-4da5-85bd-5f43ddffca2b

📥 Commits

Reviewing files that changed from the base of the PR and between 280f0ce and e43f602.

⛔ Files ignored due to path filters (26)
  • app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • app/src/main/res/mipmap-hdpi/ic_launcher_night.png is excluded by !**/*.png
  • app/src/main/res/mipmap-hdpi/ic_launcher_pine.png is excluded by !**/*.png
  • app/src/main/res/mipmap-hdpi/ic_launcher_round.png is excluded by !**/*.png
  • app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • app/src/main/res/mipmap-mdpi/ic_launcher_night.png is excluded by !**/*.png
  • app/src/main/res/mipmap-mdpi/ic_launcher_pine.png is excluded by !**/*.png
  • app/src/main/res/mipmap-mdpi/ic_launcher_round.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xhdpi/ic_launcher_night.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xhdpi/ic_launcher_pine.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xhdpi/ic_launcher_round.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxhdpi/ic_launcher_night.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxhdpi/ic_launcher_pine.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher_night.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher_pine.png is excluded by !**/*.png
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • docs/assets/social-preview.png is excluded by !**/*.png
  • docs/screenshots/v0.3.2/editor-or-settings.png is excluded by !**/*.png
  • docs/screenshots/v0.3.2/home.png is excluded by !**/*.png
  • docs/screenshots/v0.3.2/launcher.png is excluded by !**/*.png
  • docs/screenshots/v0.3.2/reader.png is excluded by !**/*.png
  • docs/screenshots/v0.3.2/toc-or-search.png is excluded by !**/*.png
📒 Files selected for processing (27)
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/workflows/android.yml
  • .github/workflows/release.yml
  • CHANGELOG.md
  • PRIVACY.md
  • PROJECT_STATUS.md
  • README.md
  • README.zh-CN.md
  • ROADMAP.md
  • SECURITY.md
  • app/build.gradle.kts
  • app/proguard-rules.pro
  • app/src/main/java/de/unbow/mora/model/MarkdownViewModel.kt
  • app/src/main/java/de/unbow/mora/ui/AppearanceSheet.kt
  • app/src/main/java/de/unbow/mora/ui/DocumentScreen.kt
  • app/src/main/java/de/unbow/mora/ui/MoraApp.kt
  • app/src/main/res/drawable/ic_launcher_foreground.xml
  • app/src/main/res/drawable/ic_launcher_foreground_night.xml
  • app/src/main/res/drawable/ic_launcher_foreground_pine.xml
  • app/src/main/res/drawable/ic_launcher_monochrome.xml
  • app/src/main/res/values-zh-rCN/strings.xml
  • app/src/main/res/values/colors.xml
  • app/src/main/res/values/strings.xml
  • app/src/test/java/de/unbow/mora/model/MarkdownViewModelSaveCoordinatorTest.kt
  • docs/RELEASING.md

Comment on lines +55 to +60
CI_KEYSTORE="$RUNNER_TEMP/mora-ci-validation.jks"
CI_PASSWORD="$(openssl rand -hex 24)"
CI_ALIAS="mora-ci-validation"
RELEASE_APK="app/build/outputs/apk/release/app-release.apk"
RELEASE_MAPPING="app/build/outputs/mapping/release/mapping.prt"
APKSIGNER="$ANDROID_HOME/build-tools/36.0.0/apksigner"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Android Gradle Plugin R8 release mapping output file mapping.txt build/outputs/mapping/release

💡 Result:

When you enable code shrinking and obfuscation (minifyEnabled true) in your Android Gradle project, the R8 compiler automatically generates a mapping file that maps the obfuscated class, method, and field names back to their original names [1][2]. The mapping file is consistently output to a variant-specific directory within your module's build folder [1][3]. For a standard release build, the location is: app/build/outputs/mapping/release/mapping.txt [1][4][2] If you are using build flavors or custom build types, the path adjusts to include those names, following this pattern: [module-name]/build/outputs/mapping/[flavor-name]/[build-type]/mapping.txt [1][4] Key details regarding this file: - Generation: The file is only produced if code shrinking (R8) is enabled for the specific build variant [1]. - Purpose: This file is essential for deobfuscating stack traces from crash reports [2][5]. It should be uploaded to the Google Play Console or third-party crash reporting services (such as Sentry, New Relic, or Shake) whenever you publish a release [6][2][5][7][8]. - Consistency: While many developers still refer to it as a ProGuard mapping file, it is generated by R8, which has replaced ProGuard as the default shrinker in the Android Gradle Plugin [1][2]. - Configuration: You generally do not need to manually specify the output path using -printmapping in your ProGuard rules, as the Android Gradle Plugin handles the output automatically [3]. If you use -printmapping, note that it may be overwritten if multiple variants share the same output path [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow references:"
rg -n "RELEASE_MAPPING|mapping\.prt|mapping\.txt|Release mapping|mapping/release" .github/workflows android build.gradle *build.gradle* 2>/dev/null || true

echo
echo "Workflow snippets:"
for f in .github/workflows/android.yml .github/workflows/release.yml; do
  if [ -f "$f" ]; then
    echo "--- $f"
    nl -ba "$f" | sed -n '45,70p'
    echo "--- $f"
    nl -ba "$f" | sed -n '208,232p'
  fi
done

Repository: bjcdeshu/mora-markdown

Length of output: 555


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow snippet android.yml:"
awk 'NR>=45 && NR<=100 {printf "%6d\t%s\n", NR, $0}' .github/workflows/android.yml

echo
echo "Workflow snippet release.yml:"
awk 'NR>=212 && NR<=228 {printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml

echo
echo "Project build/config references:"
rg -n "minifyEnabled|enableR8|proguard|android{[[:space:]]*$|namespace|buildTypes|release" build.gradle settings.gradle app/build.gradle app/src -g '*.gradle' -g 'settings.gradle' -g '*.java' -g '*.kt' 2>/dev/null || true

Repository: bjcdeshu/mora-markdown

Length of output: 3239


Use AGP’s actual R8 mapping filename.

AGP’s release variant mapping artifact is app/build/outputs/mapping/release/mapping.txt; the workflow checks for and uploads mapping.prt, so CI will fail the mapping assertion and the signed release step will miss the intended artifact.

Proposed fix
- RELEASE_MAPPING="app/build/outputs/mapping/release/mapping.prt"
+ RELEASE_MAPPING="app/build/outputs/mapping/release/mapping.txt"
- path: app/build/outputs/mapping/release/mapping.prt
+ path: app/build/outputs/mapping/release/mapping.txt
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CI_KEYSTORE="$RUNNER_TEMP/mora-ci-validation.jks"
CI_PASSWORD="$(openssl rand -hex 24)"
CI_ALIAS="mora-ci-validation"
RELEASE_APK="app/build/outputs/apk/release/app-release.apk"
RELEASE_MAPPING="app/build/outputs/mapping/release/mapping.prt"
APKSIGNER="$ANDROID_HOME/build-tools/36.0.0/apksigner"
CI_KEYSTORE="$RUNNER_TEMP/mora-ci-validation.jks"
CI_PASSWORD="$(openssl rand -hex 24)"
CI_ALIAS="mora-ci-validation"
RELEASE_APK="app/build/outputs/apk/release/app-release.apk"
RELEASE_MAPPING="app/build/outputs/mapping/release/mapping.txt"
APKSIGNER="$ANDROID_HOME/build-tools/36.0.0/apksigner"
📍 Affects 2 files
  • .github/workflows/android.yml#L55-L60 (this comment)
  • .github/workflows/release.yml#L218-L224
🤖 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 @.github/workflows/android.yml around lines 55 - 60, Update the
RELEASE_MAPPING artifact path in .github/workflows/android.yml (lines 55-60) and
.github/workflows/release.yml (lines 218-224) to use AGP’s actual release
mapping filename, mapping.txt, instead of mapping.prt; keep all checks and
upload steps referencing the corrected RELEASE_MAPPING value.

Comment on lines 383 to 385
if (failure != null) {
uiState = stateAfterSaveFailure(uiState)
onResult(DocumentSaveResult.Failed(failure))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Recompute dirty state after a failed write.

If the user edits back to persistedContent while a save is active, updateContent keeps isDirty true because isSaving is true. A write failure only clears isSaving, leaving the document falsely marked unsaved.

Proposed fix
-internal fun stateAfterSaveFailure(state: DocumentUiState): DocumentUiState =
-    state.copy(isSaving = false)
+internal fun stateAfterSaveFailure(
+    state: DocumentUiState,
+    persistedContent: String,
+): DocumentUiState = state.copy(
+    isSaving = false,
+    isDirty = state.hasDocument && (
+        state.uri == null || state.content != persistedContent
+    ),
+)
🤖 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/de/unbow/mora/model/MarkdownViewModel.kt` around lines 383
- 385, Update the failure branch in the save flow around stateAfterSaveFailure
and DocumentSaveResult.Failed to recompute isDirty after isSaving is cleared,
comparing the current content with persistedContent so edits reverted during the
active save are marked clean.

Copy link
Copy Markdown
Owner Author

Review follow-up:

  • Fixed the stale ActivityResult boundary in 2d6af0c: Save As now records the launching document session, clears the pending request on callback, and ignores a null or stale result before persisting URI permission or calling saveAs. Added a JVM regression test for matching, stale, and missing sessions.
  • Keeping mapping.prt is intentional for AGP 9.3. The official output on this build is app/build/outputs/mapping/release/mapping.prt; mapping.txt exists only under an intermediate task directory. Android CI run 30146941921 passed the explicit non-empty mapping.prt gate and the signed workflow uses the same official output path.
  • Keeping dirty state after a failed write is also intentional and follows the release requirement: a failure must retain both edited content and dirty state. It avoids presenting a failed save as safely persisted.

The full local gate now passes with 64 JVM tests, and the updated head is waiting for a fresh Android CI run.

@bjcdeshu
bjcdeshu merged commit 7ac08ed into main Jul 25, 2026
2 checks passed
@bjcdeshu
bjcdeshu deleted the release/v0.3.2-polish branch July 25, 2026 06:25
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