Conversation
Coverage Report for CI Build 35886137946Coverage increased (+0.4%) to 59.91%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions607 previously-covered lines in 8 files lost coverage.
Coverage Stats
💛 - Coveralls |
📦 Build Artifacts Ready
|
Withalion
left a comment
There was a problem hiding this comment.
Great job, it looks very promising! There are some trivial things, a bit more refactoring is needed, but mainly unit tests.
UI/UX
- It would be nice to navigate to the geometry you will be recording further after you open the draft
- After user is done recording geometry and doesn't fill out anything in the form yet, application crashes. We should open the form right away and not the geometry recording
- When a draft is available and an existing feature is clicked to edit the warning drawer opens and closes right away
- When a draft is available and "add" button is clicked, the drawer opens correctly, but discarding draft doesn't start recording mode, but stays in "view" mode of map
Withalion
left a comment
There was a problem hiding this comment.
Great job, it looks very promising! There are some trivial things, a bit more refactoring is needed, but mainly unit tests.
UI/UX
- It would be nice to navigate to the geometry you will be recording further after you open the draft
- After user is done recording geometry and doesn't fill out anything in the form yet, application crashes. We should open the form right away and not the geometry recording
- When a draft is available and an existing feature is clicked to edit the warning drawer opens and closes right away
- When a draft is available and "add" button is clicked, the drawer opens correctly, but discarding draft doesn't start recording mode, but stays in "view" mode of map
b242e2a to
d02dcfb
Compare
📦 Build Artifacts Ready
|
tomasMizera
left a comment
There was a problem hiding this comment.
Nice! :)
When testing, let's try it out on some low-end devices to see if the 1 second interval is not causing too much slowness in UI. If so, we might need to move the saving part to another thread.
In general, this is going in a very good direction, I believe we will have this ready for testing soon.
| // geometry edits round-trip back into this same setter (via the live QML | ||
| // binding once the geometry-editing map tool hands the feature back) - that | ||
| // must not wipe attribute changes already tracked for this same feature | ||
| bool isSameFeature = !hasLayerChanged && mFeatureLayerPair.feature().id() == pair.feature().id(); |
There was a problem hiding this comment.
Why not? Can't we simply store draft immediately? What is the problem with it?
| { | ||
| mTouchedFieldIndices.clear(); | ||
|
|
||
| // draft immediately so a crash before the first keystroke still resumes into the form |
There was a problem hiding this comment.
We actually do that here :)
| // only touched fields are drafted - an untouched one falls back to its | ||
| // default value expression on resume rather than a stale recorded value |
There was a problem hiding this comment.
I'd prefer if we stored everything, not just selected fields. Imagine there are automatic field values that are changed when a different field changes. They would not get stored. Let's instead try storing everything and when opening the feature again, do not evaluate anything, just present what we stored.
| connect( this, &RecordingMapTool::recordedGeometryChanged, this, [ this ]() | ||
| { | ||
| mDraftSaveTimer->start(); | ||
| } ); |
There was a problem hiding this comment.
The signal sounds good to me too for now.
| mActiveLayer->endEditCommand(); | ||
|
|
||
| mRecordedGeometry = geometry; | ||
| mActiveLayer->beginEditCommand( QStringLiteral( "Resume feature" ) ); |
There was a problem hiding this comment.
I do not think we need edit command for resuming. It also does not have any end command associated.
| QTimer mDraftSaveTimer; // debounces saveDraft() | ||
|
|
||
| //! Indices of fields the user has actually changed this session, used for drafting | ||
| QSet<int> mTouchedFieldIndices; |
There was a problem hiding this comment.
We should not need this. Simply store everything whenever an attribute is changed
|
|
||
| void FeatureDraftController::checkForDraft() | ||
| { | ||
| const QString projectId = QgsProject::instance()->homePath(); |
There was a problem hiding this comment.
This one's not resolved and I am +1 to use project IDs instead of homepath.
|
|
||
| // push into the layer too, so the map shows the resumed shape right away | ||
| // instead of the stale committed one until the next vertex edit | ||
| layer->startEditing(); |
There was a problem hiding this comment.
Note that this action may fail
| setDraft( true, layer, toQmlStage( draft.stage ), draft.isExistingFeature(), featureTitle ); | ||
| } | ||
|
|
||
| FeatureLayerPair FeatureDraftController::resumeDraft() |
There was a problem hiding this comment.
Maybe loadDraft would better say what this method is doing
| featureTitle: __activeProject.featureDraftController.draftFeatureTitle | ||
| layerName: __activeProject.featureDraftController.draftLayerName | ||
|
|
||
| onResumeClicked: () => root.resumeDraft() |
There was a problem hiding this comment.
Why are we ignoring the returned feature? We need to open form of the drafted feature, the one that is currently open might not be the one..
Description
Adds automatic recovery of in-progress feature edits. If the app closes unexpectedly while you're editing a feature - a crash, an incoming call, a dead battery - your unsaved geometry and attribute changes are no longer lost. On the next launch you're offered the chance to resume exactly where you left off, or discard the changes.
Fixes: #4585
What changed
FeatureDraftStorage/FeatureDraftController(app/drafts/) persist an in-progress edit (geometry + attributes) as a JSON blob viaQSettings, debounced by ~1s so it isn't written on every keystroke/vertex.AttributeControllerandRecordingMapToolwrite a draft whenever attributes or geometry are edited, and clear it once the feature is saved, deleted, or the edit is cancelled.ActiveProjectexposes the draft controller to QML.Behaviour
Screen_Recording_20260818-082140_One.UI.Home.mp4
Screen_Recording_20260818-082018_One.UI.Home.mp4
Screen_Recording_20260818-082039.mp4
TLDR @Withalion
Feature edits are now drafted automatically so a crash, call, or dead battery doesn't cost you your unsaved work - surfaced via a notification and resume/discard drawers.