diff --git a/fitnesstrax/app/src/view_models/day_detail.rs b/fitnesstrax/app/src/view_models/day_detail.rs index c4223e9..07a70cb 100644 --- a/fitnesstrax/app/src/view_models/day_detail.rs +++ b/fitnesstrax/app/src/view_models/day_detail.rs @@ -30,7 +30,6 @@ enum RecordState { Original(Record), New(T), Updated(Record), - #[allow(unused)] Deleted(Record), } @@ -45,6 +44,15 @@ impl RecordState { } } + fn exists(&self) -> bool { + match self { + RecordState::Original(ref r) => true, + RecordState::New(ref r) => true, + RecordState::Updated(ref r) => true, + RecordState::Deleted(ref r) => false, + } + } + fn with_value(self, value: T) -> RecordState { match self { RecordState::Original(r) => RecordState::Updated(Record { data: value, ..r }), @@ -211,52 +219,66 @@ impl DayDetailViewModel { let id = workout.id.clone(); let data = workout.data.clone(); - self.records - .write() - .unwrap() - .entry(id) - .and_modify(|r| match **r { - TraxRecord::BikeRide(ref mut v) => *v = data, - TraxRecord::Row(ref mut v) => *v = data, - TraxRecord::Run(ref mut v) => *v = data, - TraxRecord::Swim(ref mut v) => *v = data, - TraxRecord::Walk(ref mut v) => *v = data, - _ => {} - }); + let mut record_set = self.records.write().unwrap(); + if let Some(record_state) = record_set.get(&id).clone() { + let updated_state = match **record_state { + TraxRecord::BikeRide(_) => { + Some(record_state.clone().with_value(TraxRecord::BikeRide(data))) + } + TraxRecord::Row(_) => Some(record_state.clone().with_value(TraxRecord::Row(data))), + TraxRecord::Run(_) => Some(record_state.clone().with_value(TraxRecord::Run(data))), + TraxRecord::Swim(_) => { + Some(record_state.clone().with_value(TraxRecord::Swim(data))) + } + TraxRecord::Walk(_) => { + Some(record_state.clone().with_value(TraxRecord::Walk(data))) + } + _ => None, + }; + + if let Some(updated_state) = updated_state { + record_set.insert(id, updated_state); + } + } } - pub fn time_distance_records(&self, type_: RecordType) -> Vec> { - unimplemented!("time_distance_records") - } - - pub fn time_distance_summary( + pub fn time_distance_records( &self, type_: TimeDistanceWorkoutType, - ) -> (si::Meter, si::Second) { + ) -> Vec> { self.records .read() .unwrap() .iter() + .filter(|(_, record)| record.exists()) .filter(|(_, workout_state)| workout_state.is_time_distance_type(type_)) .filter_map(|(id, record_state)| match **record_state { TraxRecord::BikeRide(ref workout) | TraxRecord::Row(ref workout) | TraxRecord::Run(ref workout) | TraxRecord::Swim(ref workout) - | TraxRecord::Walk(ref workout) => Some(workout), + | TraxRecord::Walk(ref workout) => Some(Record { + id: id.clone(), + data: workout.clone(), + }), _ => None, }) - .fold((0. * si::M, 0. * si::S), |(distance, duration), workout| { - println!("folding workout: {:?}", workout); - match (workout.distance, workout.duration) { - (Some(distance_), Some(duration_)) => { - (distance + distance_, duration + duration_) - } - (Some(distance_), None) => (distance + distance_, duration), - (None, Some(duration_)) => (distance, duration + duration_), - (None, None) => (distance, duration), - } - }) + .collect() + } + + pub fn time_distance_summary( + &self, + type_: TimeDistanceWorkoutType, + ) -> (si::Meter, si::Second) { + self.time_distance_records(type_).into_iter().fold( + (0. * si::M, 0. * si::S), + |(distance, duration), workout| match (workout.data.distance, workout.data.duration) { + (Some(distance_), Some(duration_)) => (distance + distance_, duration + duration_), + (Some(distance_), None) => (distance + distance_, duration), + (None, Some(duration_)) => (distance, duration + duration_), + (None, None) => (distance, duration), + }, + ) } fn get_record(&self, id: &RecordId) -> Option> { @@ -271,7 +293,17 @@ impl DayDetailViewModel { } pub fn remove_record(&self, id: RecordId) { - unimplemented!("remove_record") + let mut record_set = self.records.write().unwrap(); + let updated_record = match record_set.remove(&id) { + Some(RecordState::Original(r)) => Some(RecordState::Deleted(r)), + Some(RecordState::New(_)) => None, + Some(RecordState::Updated(r)) => Some(RecordState::Deleted(r)), + Some(RecordState::Deleted(r)) => Some(RecordState::Deleted(r)), + None => None, + }; + if let Some(updated_record) = updated_record { + record_set.insert(id, updated_record); + } } pub fn save(&self) { @@ -339,7 +371,9 @@ impl DayDetailViewModel { RecordState::Updated(r) => { let _ = self.provider.update_record(r.clone()).await; } - RecordState::Deleted(_) => unimplemented!(), + RecordState::Deleted(r) => { + let _ = self.provider.delete_record(r.id).await; + } } } } @@ -413,11 +447,19 @@ mod test { } async fn update_record(&self, record: Record) -> Result<(), WriteError> { - Err(WriteError::NoDatabase) + println!("updated record: {:?}", record); + self.updated_records.write().unwrap().push(record.clone()); + self.records + .write() + .unwrap() + .insert(record.id.clone(), record); + Ok(()) } async fn delete_record(&self, id: RecordId) -> Result<(), WriteError> { - Err(WriteError::NoDatabase) + self.deleted_records.write().unwrap().push(id.clone()); + let _ = self.records.write().unwrap().remove(&id); + Ok(()) } } @@ -560,15 +602,16 @@ mod test { } #[tokio::test] - #[ignore] async fn it_can_update_an_existing_record() { let (view_model, provider) = create_view_model().await; let mut workout = view_model - .time_distance_records(RecordType::BikeRide) + .time_distance_records(TimeDistanceWorkoutType::BikeRide) .first() .cloned() .unwrap(); + println!("found record: {:?}", workout); + workout.data.duration = Some(1800. * si::S); view_model.update_time_distance(workout.clone()); @@ -577,7 +620,7 @@ mod test { (15000. * si::M, 1800. * si::S) ); - view_model.save(); + view_model.async_save().await; assert_eq!(provider.put_records.read().unwrap().len(), 0); assert_eq!(provider.updated_records.read().unwrap().len(), 1); @@ -585,7 +628,6 @@ mod test { } #[tokio::test] - #[ignore] async fn it_can_remove_a_new_record() { let (view_model, provider) = create_empty_view_model().await; assert_eq!( @@ -603,11 +645,10 @@ mod test { } #[tokio::test] - #[ignore] async fn it_can_delete_an_existing_record() { let (view_model, provider) = create_view_model().await; let mut workout = view_model - .time_distance_records(RecordType::BikeRide) + .time_distance_records(TimeDistanceWorkoutType::BikeRide) .first() .cloned() .unwrap(); @@ -617,7 +658,7 @@ mod test { view_model.time_distance_summary(TimeDistanceWorkoutType::BikeRide), (0. * si::M, 0. * si::S) ); - view_model.save(); + view_model.async_save().await; assert_eq!(provider.put_records.read().unwrap().len(), 0); assert_eq!(provider.updated_records.read().unwrap().len(), 0);