Skip to content

Copilot/display exercises logs inline#145

Open
gfauredev wants to merge 6 commits into
mainfrom
copilot/display-exercises-logs-inline
Open

Copilot/display exercises logs inline#145
gfauredev wants to merge 6 commits into
mainfrom
copilot/display-exercises-logs-inline

Conversation

@gfauredev
Copy link
Copy Markdown
Owner

@gfauredev gfauredev commented May 20, 2026

This Pull Request…

Engineering Principles

  • PR only contains changes strictly related to the requested feature or fix,
    scope is focused (no unrelated dependency updates or formatting)
  • This code totally respects README’s Engineering Principles

CI/CD Readiness

  • Branch follows Conventional Branch: feat/…, fix/…, refactor/…, …
  • Code is formatted with dx fmt; cargo fmt
  • All checks pass, nix flake checks succeeds without warnings
    • Code compiles, dx build with necessary platform flags succeeds
    • cargo clippy -- -D warnings -W clippy::all -W clippy::pedantic
      produces zero warnings
    • All unit tests pass without warnings
      cargo llvm-cov nextest --ignore-filename-regex '(src/components/|\.cargo/registry/|nix/store)'
    • End-to-end tests pass maestro test --headless maestro/web
      maestro test --headless maestro/android

Summary by CodeRabbit

  • New Features

    • Gesture-based controls for completed exercise tiles: tap to replay, swipe right to edit, swipe left + hold to delete; inline delete-progress indicator shown during hold.
    • On-screen gesture hint displayed when no exercise is active (and on coarse-pointer devices).
  • Localization

    • Added gesture hint text in English, Spanish and French.
  • Style

    • Updated exercise log layout, spacing, overflow and interaction styling.

Review Change Stack

Copilot AI and others added 3 commits May 20, 2026 08:39
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Warning

Rate limit exceeded

@Copilot has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 51 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 52b23229-73aa-4939-9430-6b5054660328

📥 Commits

Reviewing files that changed from the base of the PR and between 5799999 and fd79b27.

📒 Files selected for processing (1)
  • src/components/completed_exercise_log.rs
📝 Walkthrough

Walkthrough

Add pointer gestures to completed exercise tiles: swipe-right to edit, tap to replay (when allowed), swipe-left then hold to delete with visual progress; update tile RSX and SCSS for layout/delete-progress; show a conditional gesture hint with EN/ES/FR translations.

Changes

Gesture-based completed exercise log interactions

Layer / File(s) Summary
Gesture interaction constants and state foundation
src/components/completed_exercise_log.rs
Gesture constants (swipe thresholds, tap slop, delete-hold timing) and new pointer/drag/delete-progress state signals plus edit-input initialisation and toast/storage wiring.
Touch handlers, RSX refactor and async delete-hold
src/components/completed_exercise_log.rs
Tile RSX now applies horizontal translation and adds ontouchstart/ontouchmove/ontouchend/ontouchcancel handlers. Right-swipe arms inline edit, tap triggers replay when allowed, and left-swipe + hold runs a cancellable async delete-hold loop that updates delete_progress, emits a toast, removes the log by idx, and calls storage::save_session. The prior non-editing else branch for stats was removed.
Styling updates for gesture UI
assets/_component.scss
article.log padding/overflow and header layout updated; ul.log-stats pill-style list and .log-pointer-actions added; .log-delete-progress bar added with transition and default hidden state; p.log-gestures-hint rules and a coarse-pointer media query toggle visibility; .hold-del block closing fixed.
Documentation, gesture hints, and translations
src/components/active_session/completed_exercises.rs, assets/en.ftl, assets/es.ftl, assets/fr.ftl
Component doc comment updated to mention gesture-based replay/edit/delete; a conditional p renders the completed-exercises-gestures-hint when no exercise is active; English, Spanish and French translations for the hint were added.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • gfauredev/LogOut#142: Both PRs touch src/components/completed_exercise_log.rs’s deletion flow; this PR adds swipe/hold delete progress while the related PR adds a toast-log-deleted emission.

Poem

🐰 A tap, a swipe, a gentle hold,
The tile drifts, a progress told;
Hints in three tongues softly sing,
Swipe right to tweak, left to bring
the log to rest — then saved with a spring.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only the template with unchecked checkboxes and no actual content describing the changes, objectives, or context of the pull request. Complete the PR description by explaining the feature, summarising the changes across files, and providing context or testing notes. Mark completed checklist items accordingly.
Title check ❓ Inconclusive The title 'Copilot/display exercises logs inline' is vague and uses non-descriptive prefixes; it doesn't clearly convey the specific change (adding gesture-based interactions for touchscreens). Clarify the title to reflect the main change more specifically, e.g., 'Add gesture-based interactions for completed exercise logs on touchscreens' or similar.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch copilot/display-exercises-logs-inline

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.

@github-actions
Copy link
Copy Markdown

📊 Coverage Report

Lines: 3774/5029 (75.04474050507059%)

⏱️ Tests: 255 tests in 0.590s

FilenameFunction CoverageLine CoverageRegion CoverageBranch Coverage
main.rs
  22.00% (11/50)
  58.94% (155/263)
  60.56% (218/360)
- (0/0)
models/analytics.rs
   0.00% (0/6)
   0.00% (0/36)
   0.00% (0/48)
- (0/0)
models/enums.rs
 100.00% (28/28)
 100.00% (147/147)
 100.00% (337/337)
- (0/0)
models/exercise.rs
  92.00% (46/50)
  92.10% (548/595)
  91.01% (749/823)
- (0/0)
models/log.rs
 100.00% (12/12)
 100.00% (118/118)
 100.00% (144/144)
- (0/0)
models/mod.rs
 100.00% (11/11)
 100.00% (67/67)
 100.00% (97/97)
- (0/0)
models/session.rs
  72.22% (13/18)
  84.78% (156/184)
  83.33% (210/252)
- (0/0)
models/units.rs
 100.00% (28/28)
 100.00% (167/167)
  98.88% (353/357)
- (0/0)
services/app_state.rs
   1.89% (1/53)
   2.47% (11/445)
   2.27% (15/662)
- (0/0)
services/exercise_db.rs
  88.81% (127/143)
  90.30% (1210/1340)
  89.60% (1922/2145)
- (0/0)
services/exercise_loader.rs
   0.00% (0/14)
   0.00% (0/100)
   0.00% (0/135)
- (0/0)
services/native_queue.rs
   0.00% (0/15)
   0.00% (0/145)
   0.00% (0/197)
- (0/0)
services/notifications.rs
   0.00% (0/3)
   0.00% (0/9)
   0.00% (0/10)
- (0/0)
services/service_worker.rs
 100.00% (2/2)
 100.00% (6/6)
 100.00% (6/6)
- (0/0)
services/storage.rs
  63.77% (88/138)
  81.21% (778/958)
  83.53% (1187/1421)
- (0/0)
services/wake_lock.rs
 100.00% (2/2)
 100.00% (5/5)
 100.00% (5/5)
- (0/0)
utils.rs
  91.36% (74/81)
  91.44% (406/444)
  91.80% (649/707)
- (0/0)
Totals
  67.74% (443/654)
  75.04% (3774/5029)
  76.46% (5892/7706)
- (0/0)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/components/completed_exercise_log.rs`:
- Around line 91-207: The gesture-only handlers
(onpointerdown/onpointermove/onpointerup/onpointerleave/onpointercancel) make
replay/edit/delete unreachable to keyboard users; add accessible alternatives by
introducing visible or visually-hidden focusable controls (e.g., buttons or
links) in the tile header that call start_edit and on_replay and invoke the same
delete flow, and add keyboard event handlers (onkeydown/onkeyup or onkeypress)
on the article or those controls to trigger edit (Enter/Space), replay
(Enter/Space), and start a confirmable delete (Delete/Backspace or a focused
“Delete” button) while preserving the existing state variables
(delete_armed/delete_progress/delete_hold_gen/pointer_*); also add appropriate
ARIA attributes/roles and aria-labels to these controls so non-pointer users can
discover and perform the same actions as pointer gestures.
- Around line 101-103: Tap detection only uses horizontal slop (pointer_start_x
and drag_delta_x), allowing vertical scrolls with little horizontal drift to
trigger on_replay; add vertical tracking by introducing pointer_start_y and
drag_delta_y, set pointer_start_y to Some(evt.client_coordinates().y) and
drag_delta_y.set(0.0) at the same places where pointer_start_x and drag_delta_x
are initialized (also clear/reset them where delete_armed is cleared), and
update the tap/replay condition to require both horizontal and vertical deltas
be below the tap threshold before calling on_replay (apply same change for the
other occurrence referenced around lines 177-179).
- Around line 139-141: The async delete path currently clones session.read()
into current_session and calls current_session.exercise_logs.remove(idx), which
can panic or remove the wrong entry if logs changed; instead, validate the index
against current_session.exercise_logs.len() before calling remove, or better:
locate the log to delete by a stable identifier (e.g., log.id) using
position/find and remove_by_ident (or retain/filter to drop matching id) to
ensure identity-based removal; after safely removing the intended entry, call
storage::save_session(current_session). Ensure all references to session.read(),
current_session, exercise_logs, remove(idx), and storage::save_session are
updated accordingly.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3ac2af03-6134-4ceb-8d3d-be2293d08720

📥 Commits

Reviewing files that changed from the base of the PR and between 4d60f48 and bfd68e1.

📒 Files selected for processing (6)
  • assets/_component.scss
  • assets/en.ftl
  • assets/es.ftl
  • assets/fr.ftl
  • src/components/active_session/completed_exercises.rs
  • src/components/completed_exercise_log.rs

Comment thread src/components/completed_exercise_log.rs
Comment thread src/components/completed_exercise_log.rs Outdated
Comment thread src/components/completed_exercise_log.rs Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 3 unresolved review comments.

Files modified:

  • src/components/completed_exercise_log.rs

Commit: ec65df5e59179cca1f58d6f480c500a9696eba14

The changes have been pushed to the copilot/display-exercises-logs-inline branch.

Time taken: 7m 4s

@gfauredev
Copy link
Copy Markdown
Owner Author

@copilot Keep the previous button-based UI for pointer devices, use this new gesture UI for touchscreens only

Fixed 1 file(s) based on 3 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@github-actions
Copy link
Copy Markdown

📊 Coverage Report

Lines: / (%)

⏱️ Tests: tests in s

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (2)
src/components/completed_exercise_log.rs (2)

120-244: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate the swipe/hold logic to touch input only.

These handlers currently run for every pointer type, so desktop mouse drags can now shift the tile and trigger edit/delete as well. That conflicts with the explicit PR requirement to keep the previous button-based UI for pointer devices and reserve the new gesture flow for touchscreens.

🤖 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 `@src/components/completed_exercise_log.rs` around lines 120 - 244, The
handlers implement swipe/hold for all pointer types; restrict them to touch-only
by tracking pointer type: in onpointerdown (the move |evt| closure) check
evt.pointer_type() == "touch" and set a new flag (e.g.,
pointer_is_touch.set(true)) only for touch pointers, and early-return for
non-touch; in onpointerup/onpointerleave/onpointercancel and onpointermove,
short-circuit the logic unless pointer_is_touch.read() is true (and clear
pointer_is_touch.set(false) on up/leave/cancel). Update references in the
existing closures (onpointerdown, onpointermove, onpointerup, onpointerleave,
onpointercancel) and coordinate with pointer_down, delete_hold_gen,
delete_armed, and delete_progress to ensure state is only modified for
touch-driven gestures.

189-213: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Read dy before clearing the drag state.

Line 196 resets drag_delta_y before Line 212 reads it, so the vertical tap guard always sees 0 and a mostly vertical scroll can still replay the log. Capture dy alongside dx before resetting the gesture signals.

Proposed fix
                 let dx = *drag_delta_x.read();
+                let dy = *drag_delta_y.read();
                 let armed_delete = *delete_armed.read();
                 let completed_delete = *delete_progress.read() >= 1.0;
                 pointer_down.set(false);
                 pointer_start_x.set(None);
                 pointer_start_y.set(None);
                 drag_delta_x.set(0.0);
                 drag_delta_y.set(0.0);
@@
-                let dy = *drag_delta_y.read();
                 if show_replay && dx.abs() <= TAP_SLOP_PX && dy.abs() <= TAP_SLOP_PX {
                     on_replay.call(());
                 }
🤖 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 `@src/components/completed_exercise_log.rs` around lines 189 - 213, The code
clears drag state before reading vertical delta, so dy is always zero; change
the gesture reset sequence in completed_exercise_log.rs to read both dx and dy
from drag_delta_x.read() and drag_delta_y.read() into local variables (e.g., let
dx = *drag_delta_x.read(); let dy = *drag_delta_y.read();) before mutating
pointer_down, pointer_start_x/y, drag_delta_x/y, delete_armed, delete_progress,
and delete_hold_gen; then use those captured dx and dy in the subsequent checks
(completed_delete, armed_delete, swipe/edit guard, and the show_replay vertical
tap guard) so vertical taps are detected correctly.
🤖 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 `@src/components/completed_exercise_log.rs`:
- Line 92: The code uses a non-existent identifier on the model (ExerciseLog ->
log.id / l.id), so the delete paths fail to compile; either restore the previous
bounds-checked removal using remove(idx) (replace uses of log.id / l.id with
index-based deletion where variable names like log or l are used) or add a
stable id field to the ExerciseLog model and update every delete path that
references log.id / l.id to use that new id consistently (ensure the model
definition, any serialization/migrations, and all three delete call sites are
changed together).
- Around line 263-285: The visible button labels ("Edit", "Replay", "Delete")
are hard-coded while only their aria-labels use localization; update the button
contents to use the same translation macro calls (t!()) as the aria-labels so
the rendered text is localized. Locate the button blocks in
completed_exercise_log.rs where is_editing, start_edit, show_replay, on_replay,
toast, session, storage::save_session and log_id are used and replace the
literal strings with the appropriate t!("edit-log"), t!("log-replay-title") and
t!("delete-log") (or the correct keys) so both the visible label and aria-label
are translated.
- Around line 98-118: The current key handling matches on evt.key().as_str();
change it to match on the Key enum returned by evt.key() (use Key::Enter,
Key::Space, Key::Character(c) with c == "e" or c == "E" to trigger start_edit,
and Key::Delete | Key::Backspace to perform the delete logic that pushes the
toast, removes the log from session.exercise_logs using log_id, and calls
storage::save_session). Keep the existing calls to on_replay.call(),
start_edit(()), toast.write().push_back(...), and storage::save_session(...) but
move them into the appropriate Key variant arms to be type-safe and idiomatic.

---

Outside diff comments:
In `@src/components/completed_exercise_log.rs`:
- Around line 120-244: The handlers implement swipe/hold for all pointer types;
restrict them to touch-only by tracking pointer type: in onpointerdown (the move
|evt| closure) check evt.pointer_type() == "touch" and set a new flag (e.g.,
pointer_is_touch.set(true)) only for touch pointers, and early-return for
non-touch; in onpointerup/onpointerleave/onpointercancel and onpointermove,
short-circuit the logic unless pointer_is_touch.read() is true (and clear
pointer_is_touch.set(false) on up/leave/cancel). Update references in the
existing closures (onpointerdown, onpointermove, onpointerup, onpointerleave,
onpointercancel) and coordinate with pointer_down, delete_hold_gen,
delete_armed, and delete_progress to ensure state is only modified for
touch-driven gestures.
- Around line 189-213: The code clears drag state before reading vertical delta,
so dy is always zero; change the gesture reset sequence in
completed_exercise_log.rs to read both dx and dy from drag_delta_x.read() and
drag_delta_y.read() into local variables (e.g., let dx = *drag_delta_x.read();
let dy = *drag_delta_y.read();) before mutating pointer_down, pointer_start_x/y,
drag_delta_x/y, delete_armed, delete_progress, and delete_hold_gen; then use
those captured dx and dy in the subsequent checks (completed_delete,
armed_delete, swipe/edit guard, and the show_replay vertical tap guard) so
vertical taps are detected correctly.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 60bb0c99-4f11-44f7-877b-75750c0e0904

📥 Commits

Reviewing files that changed from the base of the PR and between bfd68e1 and ec65df5.

📒 Files selected for processing (1)
  • src/components/completed_exercise_log.rs

Comment thread src/components/completed_exercise_log.rs Outdated
Comment on lines +98 to +118
onkeydown: move |evt| {
if *is_editing.read() {
return;
}
match evt.key().as_str() {
"Enter" | " " => {
if show_replay {
on_replay.call(());
}
}
button {
class: "edit",
onclick: start_edit,
title: t!("log-edit-title"),
"✏️"
"e" | "E" => {
start_edit(());
}
HoldDeleteButton {
title: t!("log-delete-title").to_string(),
on_delete: move |()| {
consume_context::<crate::ToastSignal>()
.0
.write()
.push_back(t!("toast-log-deleted").to_string());
"Delete" | "Backspace" => {
toast.write().push_back(t!("toast-log-deleted").to_string());
let mut current_session = session.read().clone();
current_session.exercise_logs.retain(|l| l.id != log_id);
storage::save_session(current_session);
}
_ => {}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Dioxus 0.7.9, what type does KeyboardEvent::key()return, and what is the idiomatic way to handle Enter, Space, Delete, Backspace, and character keys like "e" in anonkeydown handler?

💡 Result:

In Dioxus 0.7.9, the key() method on KeyboardEvent returns a Key enum [1][2]. The idiomatic way to handle keyboard input is to use the onkeydown event handler and match on the value returned by evt.key() [3][4]. The Key type provides a structured, type-safe way to represent keyboard input, which is preferred over manual string matching [1]. Below is the idiomatic approach to handling specific keys: rsx! { div { tabindex: 0, onkeydown: move |evt| { use dioxus::prelude::Key; match evt.key { Key::Enter => println!("Enter pressed"), Key::Space => println!("Space pressed"), Key::Backspace => println!("Backspace pressed"), Key::Delete => println!("Delete pressed"), Key::Character(c) if c == "e" => println!("'e' pressed"), _ => {} } } } } Key Handling Tips: 1. Identification: Use the Key enum for named keys (e.g., Key::Enter, Key::Backspace) [1]. For character inputs, match against Key::Character(s) [1]. 2. Focus: Remember that onkeydown events only fire on focusable elements (like input, textarea, or elements with a tabindex attribute) [4]. 3. Physical Keys vs. Input: If you need to detect the physical location of a key regardless of the user's keyboard layout, you can use evt.code() instead of evt.key(), which returns a Code enum [1]. The KeyboardEvent in Dioxus is a type alias for Event<KeyboardData> [2], and the KeyboardData struct exposes both key() (for the logical character/function) and code() (for the physical key position) [1].

Citations:


🏁 Script executed:

# First, check if the file exists and view the relevant code
fd "completed_exercise_log.rs" --type f

Repository: gfauredev/LogOut

Length of output: 101


🏁 Script executed:

# Search for KeyboardEvent and key() usage patterns in the codebase
rg "evt\.key\(\)" --type rs -A 3 -B 3

Repository: gfauredev/LogOut

Length of output: 87


🏁 Script executed:

# Find and display the relevant code section from the file
cat -n src/components/completed_exercise_log.rs | sed -n '90,125p'

Repository: gfauredev/LogOut

Length of output: 1673


🏁 Script executed:

# Search for evt.key usage with correct rg syntax
rg "evt\.key" --type-add='rs:*.rs' -A 3 -B 3

Repository: gfauredev/LogOut

Length of output: 2539


🏁 Script executed:

# Check Dioxus version in Cargo.toml
cat Cargo.toml | grep -A 5 "dioxus"

Repository: gfauredev/LogOut

Length of output: 615


🏁 Script executed:

# Check imports in completed_exercise_log.rs to see if Key is imported
head -n 30 src/components/completed_exercise_log.rs

Repository: gfauredev/LogOut

Length of output: 1282


🏁 Script executed:

# Check the session_exercise_form.rs to see how Key is imported
head -n 30 src/components/session_exercise_form.rs | grep -A 20 "use"

Repository: gfauredev/LogOut

Length of output: 1187


🏁 Script executed:

# Check if Key is imported in completed_exercise_log.rs
rg "Key" src/components/completed_exercise_log.rs

Repository: gfauredev/LogOut

Length of output: 42


🏁 Script executed:

# Check entire imports section of completed_exercise_log.rs
head -n 50 src/components/completed_exercise_log.rs

Repository: gfauredev/LogOut

Length of output: 2242


🏁 Script executed:

# Check if the code actually compiles by looking for build output or test results
ls -la | grep -E "(Cargo.lock|target|build)"

Repository: gfauredev/LogOut

Length of output: 184


🏁 Script executed:

# Check if the project actually builds with dx build for wasm
cd . && cat Cargo.toml | head -20

Repository: gfauredev/LogOut

Length of output: 715


🏁 Script executed:

# Check if there are any other files using evt.key().as_str() to see if it's a widespread pattern
rg "\.as_str\(\)" src/components/ -A 2 -B 2

Repository: gfauredev/LogOut

Length of output: 3363


🏁 Script executed:

# Look for any compilation errors or warnings in target directory
find target -name "*.rmeta" -o -name "*.rlib" | head -5

Repository: gfauredev/LogOut

Length of output: 341


🏁 Script executed:

# Check if there's a Key import or alias somewhere
rg "Key" src/components/completed_exercise_log.rs -B 5 -A 5

Repository: gfauredev/LogOut

Length of output: 42


🏁 Script executed:

# Check the session_exercise_form.rs to see if Key is imported there
head -n 15 src/components/session_exercise_form.rs

Repository: gfauredev/LogOut

Length of output: 675


🏁 Script executed:

# Look at all imports in both files more carefully
grep -n "use" src/components/completed_exercise_log.rs | head -20

Repository: gfauredev/LogOut

Length of output: 1048


🏁 Script executed:

# Check if dioxus-0.7.9 docs are available locally
find . -name "*.md" -o -name "CHANGELOG*" | grep -i dioxus | head -5

Repository: gfauredev/LogOut

Length of output: 42


🏁 Script executed:

# Try to find any test files or examples that show key handling patterns
find . -type f -name "*.rs" -exec grep -l "Key::" {} \;

Repository: gfauredev/LogOut

Length of output: 102


🏁 Script executed:

# Check the session_exercise_form.rs file where Key:: is used
grep -n "Key::" src/components/session_exercise_form.rs -B 5 -A 5

Repository: gfauredev/LogOut

Length of output: 1887


🏁 Script executed:

# Also check for the exact line where Key is matched
grep -n "evt.key()" src/components/session_exercise_form.rs -B 2 -A 2

Repository: gfauredev/LogOut

Length of output: 984


🏁 Script executed:

# Find where Key is imported in session_exercise_form.rs
head -n 30 src/components/session_exercise_form.rs | grep -E "use|Key"

Repository: gfauredev/LogOut

Length of output: 250


🏁 Script executed:

# Check if there are compile errors or warnings in the project
cat Cargo.lock | grep -A 2 "name = \"dioxus\"" | head -10

Repository: gfauredev/LogOut

Length of output: 159


🏁 Script executed:

# Look at any test or example files that might show the correct pattern
find . -type f -name "*.rs" -path "*/examples/*" -o -path "*/tests/*" 2>/dev/null | head -5

Repository: gfauredev/LogOut

Length of output: 42


Match on the Key enum directly instead of calling .as_str().

In Dioxus 0.7.9, evt.key() returns a Key enum, and the idiomatic approach is to match directly on the enum variants rather than converting to a string. This is demonstrated in session_exercise_form.rs (lines 244, 317, 384) which uses if evt.key() == Key::Enter.

Update the match statement to use:

match evt.key {
    Key::Enter | Key::Space => { /* replay */ },
    Key::Character(c) if c == "e" => { /* edit */ },
    Key::Delete | Key::Backspace => { /* delete */ },
    _ => {}
}

This pattern is more type-safe and aligns with idiomatic Dioxus event handling across the codebase.

🧰 Tools
🪛 GitHub Actions: .github/workflows/ci.yml / 0_android-build.txt

[error] 102-102: Rust compiler error E0599: no method named as_str found for enum dioxus::prelude::Key in the current scope (method exists on std::string::String).


[error] 114-114: Rust compiler error E0609: no field id on type &log::ExerciseLog when calling current_session.exercise_logs.retain(|l| l.id != log_id).

🪛 GitHub Actions: .github/workflows/ci.yml / 1_coverage.txt

[error] 114-114: Rust compile error (E0609): no field id on type &log::ExerciseLog. Code: current_session.exercise_logs.retain(|l| l.id != log_id);

🪛 GitHub Actions: .github/workflows/ci.yml / android-build

[error] 102-102: Rust compile error E0599: no method named as_str found for enum dioxus::prelude::Key in the current scope.


[error] 114-114: Rust compile error E0609: no field id on type &log::ExerciseLog (unknown field).

🪛 GitHub Actions: .github/workflows/ci.yml / coverage

[error] 114-114: Rust compile error (E0609): no field id on type &log::ExerciseLog at current_session.exercise_logs.retain(|l| l.id != log_id);. Available fields include exercise_id, exercise_name, category, start_time, end_time, etc.

🤖 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 `@src/components/completed_exercise_log.rs` around lines 98 - 118, The current
key handling matches on evt.key().as_str(); change it to match on the Key enum
returned by evt.key() (use Key::Enter, Key::Space, Key::Character(c) with c ==
"e" or c == "E" to trigger start_edit, and Key::Delete | Key::Backspace to
perform the delete logic that pushes the toast, removes the log from
session.exercise_logs using log_id, and calls storage::save_session). Keep the
existing calls to on_replay.call(), start_edit(()),
toast.write().push_back(...), and storage::save_session(...) but move them into
the appropriate Key variant arms to be type-safe and idiomatic.

Comment on lines +263 to +285
if !*is_editing.read() {
div { class: "log-actions",
button {
"aria-label": t!("edit-log"),
onclick: move |_| start_edit(()),
"Edit"
}
if show_replay {
button {
"aria-label": t!("log-replay-title"),
onclick: move |_| on_replay.call(()),
"Replay"
}
}
button {
"aria-label": t!("delete-log"),
onclick: move |_| {
toast.write().push_back(t!("toast-log-deleted").to_string());
let mut current_session = session.read().clone();
current_session.exercise_logs.retain(|l| l.id != log_id);
storage::save_session(current_session);
},
"Delete"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localise the visible action labels as well.

The new buttons translate their aria-labels but still render hard-coded "Edit", "Replay", and "Delete". In non-English locales that leaves the accessible name translated whilst the visible UI regresses to English.

🧰 Tools
🪛 GitHub Actions: .github/workflows/ci.yml / 0_android-build.txt

[error] 282-282: Rust compiler error E0609: no field id on type &log::ExerciseLog when calling current_session.exercise_logs.retain(|l| l.id != log_id).

🪛 GitHub Actions: .github/workflows/ci.yml / android-build

[error] 282-282: Rust compile error E0609: no field id on type &log::ExerciseLog (unknown field).

🤖 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 `@src/components/completed_exercise_log.rs` around lines 263 - 285, The visible
button labels ("Edit", "Replay", "Delete") are hard-coded while only their
aria-labels use localization; update the button contents to use the same
translation macro calls (t!()) as the aria-labels so the rendered text is
localized. Locate the button blocks in completed_exercise_log.rs where
is_editing, start_edit, show_replay, on_replay, toast, session,
storage::save_session and log_id are used and replace the literal strings with
the appropriate t!("edit-log"), t!("log-replay-title") and t!("delete-log") (or
the correct keys) so both the visible label and aria-label are translated.

Agent-Logs-Url: https://github.com/gfauredev/LogOut/sessions/a6444465-e6d7-41c1-8865-621558c901aa

Co-authored-by: gfauredev <19304085+gfauredev@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
src/components/completed_exercise_log.rs (2)

137-142: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid index-based removal after the hold delay.

Both delete completions still remove by idx after a hold interval. If exercise_logs changes before the callback fires, this can panic or delete the wrong log. Remove by a stable identity captured from log; a bounds check only covers the panic case.

Also applies to: 286-291

🤖 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 `@src/components/completed_exercise_log.rs` around lines 137 - 142, The
callback removes an entry by the captured idx which can be invalid after the
hold delay; instead capture a stable identifier from the log (e.g., log.id or
another unique key) when setting up the callback and, inside the closure that
currently reads session_for_touch_delete and calls
current_session.exercise_logs.remove(idx), locate the log by matching that
identifier (e.g., find_position(|l| l.id == captured_id)) and remove by position
only if found, then call storage::save_session with the updated session; apply
the same change to the other delete callback that also uses idx (the similar
block mentioned in the comment).

41-43: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Track vertical travel before treating a touch as replay.

This still classifies a near-vertical scroll as a tap whenever the horizontal drift stays within TAP_SLOP_PX, so browsing the log list can fire on_replay accidentally. Record Y as well and require both axes to stay within the tap slop before replaying.

Also applies to: 89-96, 105-113, 205-206

🤖 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 `@src/components/completed_exercise_log.rs` around lines 41 - 43, The touch
handling only tracks horizontal movement (touch_start_x, drag_delta_x) so
near-vertical scrolls can still be treated as taps; add symmetric vertical
tracking (e.g., touch_start_y: use_signal(|| None::<f64>) and drag_delta_y:
use_signal(|| 0.0f64)), update touchstart/touchmove/touchend handlers that
currently read/write touch_start_x, touch_active, and drag_delta_x to also
record and compute Y deltas, and change the tap/replay check to require both
|drag_delta_x| and |drag_delta_y| to be <= TAP_SLOP_PX before firing on_replay;
apply the same changes in the other touch handler blocks that mirror the logic
(the blocks referenced around the other occurrences using
touch_start_x/drag_delta_x).
assets/_component.scss (1)

303-317: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not hide the only focusable action controls on coarse pointers.

This media query makes replay/edit/delete gesture-only again on touch devices by setting .log-pointer-actions to display: none. That drops the fallback controls for keyboard, switch, and screen-reader users on phones and tablets with external input. Keep a focusable control path available here.

🤖 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 `@assets/_component.scss` around lines 303 - 317, The rule that sets
.log-pointer-actions to display: none for coarse pointers removes the only
focusable controls on touch devices; instead of display:none, replace that with
a visual-hide technique that preserves focusability (e.g., apply your project’s
.sr-only/visually-hidden utility or the clip/absolute 1px technique) so
keyboard/switch/screen-reader users still can tab to .log-pointer-actions while
keeping it visually hidden on coarse pointers; keep .log-delete-progress and
p.log-gestures-hint behavior as-is and verify .log-pointer-actions remains in
the DOM and reachable by keyboard/screen readers.
🤖 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.

Duplicate comments:
In `@assets/_component.scss`:
- Around line 303-317: The rule that sets .log-pointer-actions to display: none
for coarse pointers removes the only focusable controls on touch devices;
instead of display:none, replace that with a visual-hide technique that
preserves focusability (e.g., apply your project’s .sr-only/visually-hidden
utility or the clip/absolute 1px technique) so keyboard/switch/screen-reader
users still can tab to .log-pointer-actions while keeping it visually hidden on
coarse pointers; keep .log-delete-progress and p.log-gestures-hint behavior
as-is and verify .log-pointer-actions remains in the DOM and reachable by
keyboard/screen readers.

In `@src/components/completed_exercise_log.rs`:
- Around line 137-142: The callback removes an entry by the captured idx which
can be invalid after the hold delay; instead capture a stable identifier from
the log (e.g., log.id or another unique key) when setting up the callback and,
inside the closure that currently reads session_for_touch_delete and calls
current_session.exercise_logs.remove(idx), locate the log by matching that
identifier (e.g., find_position(|l| l.id == captured_id)) and remove by position
only if found, then call storage::save_session with the updated session; apply
the same change to the other delete callback that also uses idx (the similar
block mentioned in the comment).
- Around line 41-43: The touch handling only tracks horizontal movement
(touch_start_x, drag_delta_x) so near-vertical scrolls can still be treated as
taps; add symmetric vertical tracking (e.g., touch_start_y: use_signal(||
None::<f64>) and drag_delta_y: use_signal(|| 0.0f64)), update
touchstart/touchmove/touchend handlers that currently read/write touch_start_x,
touch_active, and drag_delta_x to also record and compute Y deltas, and change
the tap/replay check to require both |drag_delta_x| and |drag_delta_y| to be <=
TAP_SLOP_PX before firing on_replay; apply the same changes in the other touch
handler blocks that mirror the logic (the blocks referenced around the other
occurrences using touch_start_x/drag_delta_x).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: af23e192-0544-4f0c-b365-84d6c2003e14

📥 Commits

Reviewing files that changed from the base of the PR and between ec65df5 and 5799999.

📒 Files selected for processing (2)
  • assets/_component.scss
  • src/components/completed_exercise_log.rs

Agent-Logs-Url: https://github.com/gfauredev/LogOut/sessions/a6444465-e6d7-41c1-8865-621558c901aa

Co-authored-by: gfauredev <19304085+gfauredev@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot Keep the previous button-based UI for pointer devices, use this new gesture UI for touchscreens only

Done in fd79b27: pointer/fine devices now keep the button-based controls (🔁/✏️/hold-delete), while touch/coarse devices use the gesture-only flow (tap/swipe right/swipe left+hold). Screenshot: updated completed logs UI

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.

2 participants