@@ -38,8 +38,10 @@ pub fn CompletedExerciseLog(
3838) -> Element {
3939 let mut is_editing = use_signal ( || false ) ;
4040 let mut pointer_start_x = use_signal ( || None :: < f64 > ) ;
41+ let mut pointer_start_y = use_signal ( || None :: < f64 > ) ;
4142 let mut pointer_down = use_signal ( || false ) ;
4243 let mut drag_delta_x = use_signal ( || 0.0f64 ) ;
44+ let mut drag_delta_y = use_signal ( || 0.0f64 ) ;
4345 let mut delete_armed = use_signal ( || false ) ;
4446 let mut delete_progress = use_signal ( || 0.0f32 ) ;
4547 // Gesture generation token used to cancel in-flight hold tasks.
@@ -87,10 +89,34 @@ pub fn CompletedExerciseLog(
8789 let display_dx = drag_delta_x
8890 . read ( )
8991 . clamp ( -SWIPE_VISUAL_MAX_PX , SWIPE_VISUAL_MAX_PX ) ;
92+ let log_id = log. id ;
9093 rsx ! {
9194 article {
9295 class: "log log-tile" ,
9396 style: "transform: translateX({display_dx}px);" ,
97+ tabindex: 0 ,
98+ onkeydown: move |evt| {
99+ if * is_editing. read( ) {
100+ return ;
101+ }
102+ match evt. key( ) . as_str( ) {
103+ "Enter" | " " => {
104+ if show_replay {
105+ on_replay. call( ( ) ) ;
106+ }
107+ }
108+ "e" | "E" => {
109+ start_edit( ( ) ) ;
110+ }
111+ "Delete" | "Backspace" => {
112+ toast. write( ) . push_back( t!( "toast-log-deleted" ) . to_string( ) ) ;
113+ let mut current_session = session. read( ) . clone( ) ;
114+ current_session. exercise_logs. retain( |l| l. id != log_id) ;
115+ storage:: save_session( current_session) ;
116+ }
117+ _ => { }
118+ }
119+ } ,
94120 onpointerdown: move |evt| {
95121 if * is_editing. read( ) {
96122 return ;
@@ -99,7 +125,9 @@ pub fn CompletedExerciseLog(
99125 delete_hold_gen. set( next) ;
100126 pointer_down. set( true ) ;
101127 pointer_start_x. set( Some ( evt. client_coordinates( ) . x) ) ;
128+ pointer_start_y. set( Some ( evt. client_coordinates( ) . y) ) ;
102129 drag_delta_x. set( 0.0 ) ;
130+ drag_delta_y. set( 0.0 ) ;
103131 delete_armed. set( false ) ;
104132 delete_progress. set( 0.0 ) ;
105133 } ,
@@ -110,8 +138,13 @@ pub fn CompletedExerciseLog(
110138 let Some ( start_x) = * pointer_start_x. read( ) else {
111139 return ;
112140 } ;
141+ let Some ( start_y) = * pointer_start_y. read( ) else {
142+ return ;
143+ } ;
113144 let dx = evt. client_coordinates( ) . x - start_x;
145+ let dy = evt. client_coordinates( ) . y - start_y;
114146 drag_delta_x. set( dx) ;
147+ drag_delta_y. set( dy) ;
115148 if dx <= -SWIPE_DELETE_PX && !* delete_armed. read( ) {
116149 delete_armed. set( true ) ;
117150 let gen = delete_hold_gen. peek( ) . wrapping_add( 1 ) ;
@@ -137,7 +170,7 @@ pub fn CompletedExerciseLog(
137170 {
138171 toast. write( ) . push_back( t!( "toast-log-deleted" ) . to_string( ) ) ;
139172 let mut current_session = session. read( ) . clone( ) ;
140- current_session. exercise_logs. remove ( idx ) ;
173+ current_session. exercise_logs. retain ( |l| l . id != log_id ) ;
141174 storage:: save_session( current_session) ;
142175 }
143176 delete_progress. set( 0.0 ) ;
@@ -158,7 +191,9 @@ pub fn CompletedExerciseLog(
158191 let completed_delete = * delete_progress. read( ) >= 1.0 ;
159192 pointer_down. set( false ) ;
160193 pointer_start_x. set( None ) ;
194+ pointer_start_y. set( None ) ;
161195 drag_delta_x. set( 0.0 ) ;
196+ drag_delta_y. set( 0.0 ) ;
162197 delete_armed. set( false ) ;
163198 delete_progress. set( 0.0 ) ;
164199 let next = delete_hold_gen. peek( ) . wrapping_add( 1 ) ;
@@ -174,7 +209,8 @@ pub fn CompletedExerciseLog(
174209 start_edit( ( ) ) ;
175210 return ;
176211 }
177- if show_replay && dx. abs( ) <= TAP_SLOP_PX {
212+ let dy = * drag_delta_y. read( ) ;
213+ if show_replay && dx. abs( ) <= TAP_SLOP_PX && dy. abs( ) <= TAP_SLOP_PX {
178214 on_replay. call( ( ) ) ;
179215 }
180216 } ,
@@ -184,7 +220,9 @@ pub fn CompletedExerciseLog(
184220 }
185221 pointer_down. set( false ) ;
186222 pointer_start_x. set( None ) ;
223+ pointer_start_y. set( None ) ;
187224 drag_delta_x. set( 0.0 ) ;
225+ drag_delta_y. set( 0.0 ) ;
188226 delete_armed. set( false ) ;
189227 delete_progress. set( 0.0 ) ;
190228 let next = delete_hold_gen. peek( ) . wrapping_add( 1 ) ;
@@ -196,7 +234,9 @@ pub fn CompletedExerciseLog(
196234 }
197235 pointer_down. set( false ) ;
198236 pointer_start_x. set( None ) ;
237+ pointer_start_y. set( None ) ;
199238 drag_delta_x. set( 0.0 ) ;
239+ drag_delta_y. set( 0.0 ) ;
200240 delete_armed. set( false ) ;
201241 delete_progress. set( 0.0 ) ;
202242 let next = delete_hold_gen. peek( ) . wrapping_add( 1 ) ;
@@ -220,6 +260,32 @@ pub fn CompletedExerciseLog(
220260 li { "{crate::models::format_time(duration)}" }
221261 }
222262 }
263+ if !* is_editing. read( ) {
264+ div { class: "log-actions" ,
265+ button {
266+ "aria-label" : t!( "edit-log" ) ,
267+ onclick: move |_| start_edit( ( ) ) ,
268+ "Edit"
269+ }
270+ if show_replay {
271+ button {
272+ "aria-label" : t!( "log-replay-title" ) ,
273+ onclick: move |_| on_replay. call( ( ) ) ,
274+ "Replay"
275+ }
276+ }
277+ button {
278+ "aria-label" : t!( "delete-log" ) ,
279+ onclick: move |_| {
280+ toast. write( ) . push_back( t!( "toast-log-deleted" ) . to_string( ) ) ;
281+ let mut current_session = session. read( ) . clone( ) ;
282+ current_session. exercise_logs. retain( |l| l. id != log_id) ;
283+ storage:: save_session( current_session) ;
284+ } ,
285+ "Delete"
286+ }
287+ }
288+ }
223289 }
224290 if !* is_editing. read( ) {
225291 if * delete_armed. read( ) || * delete_progress. read( ) > 0.0 {
0 commit comments