Comment: rotation not persisted across reload #40
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_whiteboard#40
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Rotating a comment marker (via the transformer, now that rubber-band selection includes comments) visually rotates the pin, but on reload it comes back at rotation 0.
Root cause:
dragendhandler inaddCommentMarkeronly sends{x, y}incomment.update.transformendgoes throughWhiteboardObjects.applyTransformwhich returns early for comment markers (they are not in theobjectsstore), so the new rotation is never sent either.commentstable has norotationcolumn; thecomment.updateRPC doesn't acceptrotation.Fix: add a
rotation REAL NOT NULL DEFAULT 0column via a new migration, accept the field incomment.update, include it in the rendered row, send it from the client on dragend and on transformend (detect comment markers in the transformer path), and apply it inaddCommentMarkerwhen loading.Consolidated Spec for Issues #36-#41
Objective
Make comments feel like first-class board objects: erasable, deletable from the sidebar, toggleable between active/resolved, non-reactive to the right mouse button, and with their rotation and scale surviving a reload.
Scope at a glance
tools.jsproperties.jsserver/*,comments.jscomments.jscomments.jscomments.jsFiles to Modify / Create
crates/hero_whiteboard_server/src/migrations/005_comment_transform.sql(NEW) — addsrotation REAL NOT NULL DEFAULT 0andscale REAL NOT NULL DEFAULT 1columns tocomments.crates/hero_whiteboard_server/src/db/models.rs— extend theCommentstruct withrotationandscale.crates/hero_whiteboard_server/src/db/queries.rs— include both columns in SELECT / INSERT / UPDATE for comments.crates/hero_whiteboard_server/src/handlers/comment.rs—createaccepts optionalrotation/scale;updateacceptsrotation/scale; newset_resolvedaccepts aresolvedbool (reusescomment.resolvemethod name with the new field to stay backwards-compatible).crates/hero_whiteboard_server/src/db/queries.rs— renameresolve_commenttoset_comment_resolved(conn, id, resolved, now)settingresolved = ?.crates/hero_whiteboard_server/openrpc.json— document the new fields oncomment.create/comment.update/comment.resolve.crates/hero_whiteboard_ui/static/web/js/whiteboard/tools.js— eraser walks.comment-marker; transformertransformendroutes comment markers toWhiteboardComments.onTransformEnd.crates/hero_whiteboard_ui/static/web/js/whiteboard/comments.js— right-click guard, Unresolve support, load/save rotation + scale,onTransformEndhelper,unresolveCommentexport.crates/hero_whiteboard_ui/static/web/js/whiteboard/properties.js— detectcomment-markerinshow()and render a small pane (Resolved badge, Resolve/Unresolve button, Delete button).Implementation Plan
Step 1: Schema + server (rotation, scale, unresolve)
Files: migration, models, queries, handler, openrpc.json
005_comment_transform.sqladdingrotation REAL NOT NULL DEFAULT 0andscale REAL NOT NULL DEFAULT 1to thecommentstable.Commentstruct; SELECT/INSERT/UPDATE inqueries.rsinclude both columns.comment.updatehandler reads optionalrotationandscalefrom params and passes them through. Clamp scale server-side to[0.2, 5.0]as a safety net.resolve_comment→set_comment_resolved(conn, id, resolved, now);comment.resolvehandler readsparams["resolved"].as_bool().unwrap_or(true)and calls the new helper.resolvedparam oncomment.resolve.Dependencies: none.
Step 2: Client-side rotation + scale persistence
Files:
comments.js,tools.jsaddCommentMarker, applycomment.rotation || 0andcomment.scale || 1to the group before adding to the layer.dragendhandler to includerotationandscalein thecomment.updatepayload (they may not have changed, but sending them is idempotent).WhiteboardComments.onTransformEnd(group)that readsgroup.rotation()and averagesscaleX/scaleY, clamps to[0.2, 5], bakes the uniform scale back onto the group (so the next transform is clean), updates_comments[id].data, sends the RPC, and broadcasts.handleSyncMessage, add acomment.updatedbranch that also readsrotationandscaleand applies them to the marker.tools.jstransformend, iterate nodes; for eachnode.hasName('comment-marker')callWhiteboardComments.onTransformEnd(node)instead ofWhiteboardObjects.applyTransform(node).Dependencies: Step 1 (needs the new RPC fields).
Step 3: Right-click guard
Files:
comments.jsaddCommentMarker, theclick taphandler short-circuits whene.evt && e.evt.button !== 0. The nativecontextmenuhandler incontextmenu.jsalready shows the canvas menu (comment markers are not.objectancestors), so no further change is needed there.Dependencies: none.
Step 4: Unresolve UI
Files:
comments.jsunresolveComment(id)which callsrpcCall('comment.resolve', { id: id, resolved: false }), flipsentry.data.resolved = false, updates marker appearance, broadcastscomment.unresolved, and re-renders the popover.showCommentPopover, render an Unresolve button (classcomment-btn unresolve) whencomment.resolved, wired toWhiteboardComments.unresolveComment.comment.unresolvedinhandleSyncMessage(mirror of the resolved branch).Dependencies: Step 1 (needs server to accept
resolved: false).Step 5: Eraser deletes comments
Files:
tools.jseraseAtPosition, after the object + connector loops, walklayer.find('.comment-marker'); for each that intersects the eraser box, extractnumericId = parseInt(node.id().replace('comment-', ''), 10)and callWhiteboardComments.deleteComment(numericId).Dependencies: none.
Step 6: Properties panel for comments
Files:
properties.jsshow(node), before the regular type dispatch, detectnode.hasName && node.hasName('comment-marker')and render a minimal pane:WhiteboardComments.resolveComment/unresolveComment/deleteComment.Dependencies: Step 4 (
unresolveCommentexport).Acceptance Criteria
[0.2, 5].Notes
scalecolumn) because the pin is a circle; non-uniform deformation has no useful semantics.comment.resolvewith aresolvedparam (instead of addingcomment.unresolve) keeps the method count flat and matches how most systems model boolean flips.Pull request opened: #42
This PR implements the consolidated spec (posted above) covering all six comment fixes.
CI
cargo check --workspace: passcargo clippy --workspace -- -D warnings: passcargo fmt --check: pass