Create Duration and Distance structures to handle rendering

These structures handle parsing and rendering of a Duration and a Distance, allowing that knowledge to be centralized and reused. Then I'm using those structures in a variety of places in order to ensure that the information gets rendered consistently.
This commit is contained in:
Savanni D'Gerinel 2024-01-29 08:26:41 -05:00
parent 798fbff320
commit 2d476f266c
4 changed files with 25 additions and 24 deletions

View File

@ -418,7 +418,7 @@ where
biking_button.connect_clicked({ biking_button.connect_clicked({
let view_model = view_model.clone(); let view_model = view_model.clone();
move |_| { move |_| {
let workout = view_model.new_record(RecordType::Walk); let workout = view_model.new_record(RecordType::BikeRide);
add_row(workout); add_row(workout);
} }
}); });

View File

@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License along with Fit
use crate::types::{ use crate::types::{
DistanceFormatter, DurationFormatter, FormatOption, ParseError, TimeFormatter, WeightFormatter, DistanceFormatter, DurationFormatter, FormatOption, ParseError, TimeFormatter, WeightFormatter,
}; };
use dimensioned::si;
use gtk::prelude::*; use gtk::prelude::*;
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
pub type Renderer<T> = dyn Fn(&T) -> String;
pub type Parser<T> = dyn Fn(&str) -> Result<T, ParseError>; pub type Parser<T> = dyn Fn(&str) -> Result<T, ParseError>;
pub type OnUpdate<T> = dyn Fn(Option<T>); pub type OnUpdate<T> = dyn Fn(Option<T>);

View File

@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with Fit
// use dimensioned::si; // use dimensioned::si;
use crate::{ use crate::{
components::{distance_field, duration_field, time_field}, components::{distance_field, duration_field, time_field},
types::{DistanceFormatter, DurationFormatter, TimeFormatter}, types::{DistanceFormatter, DurationFormatter, FormatOption, TimeFormatter},
}; };
use dimensioned::si; use dimensioned::si;
use ft_core::{RecordType, TimeDistance}; use ft_core::{RecordType, TimeDistance};
@ -28,20 +28,17 @@ use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
pub fn time_distance_summary( pub fn time_distance_summary(
distance: si::Meter<f64>, distance: DistanceFormatter,
duration: si::Second<f64>, duration: DurationFormatter,
) -> Option<gtk::Label> { ) -> Option<gtk::Label> {
let text = match (distance > si::M, duration > si::S) { let text = match (*distance > si::M, *duration > si::S) {
(true, true) => Some(format!( (true, true) => Some(format!(
"{} kilometers of biking in {} minutes", "{} of biking in {}",
distance.value_unsafe / 1000., distance.format(FormatOption::Full),
duration.value_unsafe / 60. duration.format(FormatOption::Full)
)), )),
(true, false) => Some(format!( (true, false) => Some(format!("{} of biking", distance.format(FormatOption::Full))),
"{} kilometers of biking", (false, true) => Some(format!("{} of biking", duration.format(FormatOption::Full))),
distance.value_unsafe / 1000.
)),
(false, true) => Some(format!("{} seconds of biking", duration.value_unsafe / 60.)),
(false, false) => None, (false, false) => None,
}; };
@ -75,7 +72,7 @@ pub fn time_distance_detail(type_: ft_core::RecordType, record: ft_core::TimeDis
.label( .label(
record record
.distance .distance
.map(|dist| format!("{}", dist)) .map(|dist| DistanceFormatter::from(dist).format(FormatOption::Abbreviated))
.unwrap_or("".to_owned()), .unwrap_or("".to_owned()),
) )
.build(), .build(),
@ -87,7 +84,9 @@ pub fn time_distance_detail(type_: ft_core::RecordType, record: ft_core::TimeDis
.label( .label(
record record
.duration .duration
.map(|duration| format!("{}", duration)) .map(|duration| {
DurationFormatter::from(duration).format(FormatOption::Abbreviated)
})
.unwrap_or("".to_owned()), .unwrap_or("".to_owned()),
) )
.build(), .build(),

View File

@ -14,8 +14,10 @@ General Public License for more details.
You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see <https://www.gnu.org/licenses/>. You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::{app::App, types::WeightFormatter}; use crate::{
use dimensioned::si; app::App,
types::{DistanceFormatter, DurationFormatter, WeightFormatter},
};
use emseries::{Record, RecordId, Recordable}; use emseries::{Record, RecordId, Recordable};
use ft_core::{RecordType, TimeDistance, TraxRecord}; use ft_core::{RecordType, TimeDistance, TraxRecord};
use std::{ use std::{
@ -166,9 +168,9 @@ impl DayDetailViewModel {
*record = Some(new_record); *record = Some(new_record);
} }
pub fn biking_summary(&self) -> (si::Meter<f64>, si::Second<f64>) { pub fn biking_summary(&self) -> (DistanceFormatter, DurationFormatter) {
self.records.read().unwrap().iter().fold( self.records.read().unwrap().iter().fold(
(0. * si::M, 0. * si::S), (DistanceFormatter::default(), DurationFormatter::default()),
|(acc_distance, acc_duration), (_, record)| match record.data() { |(acc_distance, acc_duration), (_, record)| match record.data() {
Some(Record { Some(Record {
data: data:
@ -178,11 +180,11 @@ impl DayDetailViewModel {
.. ..
}) => ( }) => (
distance distance
.map(|distance| acc_distance + distance) .map(|distance| acc_distance + DistanceFormatter::from(distance))
.unwrap_or(acc_distance), .unwrap_or(acc_distance),
(duration duration
.map(|duration| acc_duration + duration) .map(|duration| acc_duration + DurationFormatter::from(duration))
.unwrap_or(acc_duration)), .unwrap_or(acc_duration),
), ),
_ => (acc_distance, acc_duration), _ => (acc_distance, acc_duration),