diff --git a/assets/img/local_save.png b/assets/img/local_save.png new file mode 100644 index 0000000..aec5b70 Binary files /dev/null and b/assets/img/local_save.png differ diff --git a/assets/img/mapIcon.svg b/assets/img/mapIcon.svg new file mode 100644 index 0000000..576fc90 --- /dev/null +++ b/assets/img/mapIcon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/img/mapIcon_selected.svg b/assets/img/mapIcon_selected.svg new file mode 100644 index 0000000..6ab505c --- /dev/null +++ b/assets/img/mapIcon_selected.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/common_widget/container/list/list_activity_widget.dart b/lib/common_widget/container/list/list_activity_widget.dart index 9c6c82d..e815b60 100644 --- a/lib/common_widget/container/list/list_activity_widget.dart +++ b/lib/common_widget/container/list/list_activity_widget.dart @@ -1,7 +1,11 @@ +import 'package:smartfit_app_mobile/modele/activity_saver.dart'; +import 'package:smartfit_app_mobile/modele/helper.dart'; +import 'package:smartfit_app_mobile/main.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:smartfit_app_mobile/common_widget/container/workout_row.dart'; import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart'; +import 'package:smartfit_app_mobile/common_widget/container/workout_row/workout_row_generic.dart'; +import 'package:smartfit_app_mobile/common_widget/container/workout_row/workout_row_walking.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/manager_file.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; @@ -24,73 +28,112 @@ class _ListActivityWidget extends State { @override Widget build(BuildContext context) { - return Material( - color: Colors.transparent, - child: ListView.builder( - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: Provider.of(context, listen: true).listActivity.length, - itemBuilder: (context, index) { - ActivityOfUser activityObj = - Provider.of(context, listen: true).listActivity[index]; - Map activityMap; - // -- Si categorie == marche - if (activityObj.category == managerFile.marche) { - activityMap = activityObj.toMapWalking(); - } else { - // -- Default -- // - activityMap = activityObj.toMapGeneric(); - } + Future onClick(ActivityOfUser activityObj) async { + if (!Provider.of(context, listen: false) + .managerSelectedActivity + .fileNotSelected(activityObj.fileUuid)) { + Provider.of(context, listen: false) + .managerSelectedActivity + .removeSelectedActivity(activityObj.fileUuid); + setState(() {}); + return; + } + + Tuple2 result = + await _utile.getContentActivity(context, activityObj, infoManager); + if (!result.item1) { + return; + } - return InkWell( - onTap: () {}, - child: WorkoutRow( - wObj: activityMap, - onDelete: () async { - if (await api.deleteFile( - Provider.of(context, listen: false).token, - activityObj.fileUuid, - infoManager)) { - if (!Provider.of(context, listen: false) - .managerSelectedActivity - .fileNotSelected(activityObj.fileUuid)) { - Provider.of(context, listen: false) - .managerSelectedActivity - .removeSelectedActivity(activityObj.fileUuid); - } - Provider.of(context, listen: false) - .removeActivity(activityObj); - } - }, - onClick: () async { - if (!Provider.of(context, listen: false) - .managerSelectedActivity - .fileNotSelected(activityObj.fileUuid)) { - Provider.of(context, listen: false) - .managerSelectedActivity - .removeSelectedActivity(activityObj.fileUuid); - setState(() {}); - return; - } + // TODO: Hein? + Provider.of(context, listen: false).removeActivity(activityObj); + Provider.of(context, listen: false).insertActivity(0, activityObj); + } - Tuple2 result = - await _utile.getContentActivity(context, activityObj); - if (!result.item1) { - return; - } + // TODO: Understand :( + Future onDelete(ActivityOfUser activityObj) async { + if (await api.deleteFile(Provider.of(context, listen: false).token, + activityObj.fileUuid, infoManager)) { + if (!Provider.of(context, listen: false) + .managerSelectedActivity + .fileNotSelected(activityObj.fileUuid)) { + Provider.of(context, listen: false) + .managerSelectedActivity + .removeSelectedActivity(activityObj.fileUuid); + } + if (!Helper.isPlatformWeb()) { + ActivitySaver actSaver = await ActivitySaver.create(); + actSaver.deleteActivity(activityObj.fileUuid); + localDB.removeActivity(activityObj.fileUuid); + } + Provider.of(context, listen: false).removeActivity(activityObj); + } + } - Provider.of(context, listen: false) - .removeActivity(activityObj); - Provider.of(context, listen: false) - .insertActivity(0, activityObj); - }, - isSelected: !Provider.of(context) - .managerSelectedActivity - .fileNotSelected(activityObj.fileUuid), - ), - ); - }, + bool isSelected(ActivityOfUser activityObj) { + return !Provider.of(context) + .managerSelectedActivity + .fileNotSelected(activityObj.fileUuid); + } + + return Material( + color: Colors.transparent, + child: Column( + children: [ + Visibility( + visible: infoManager.isVisible, + child: Text(infoManager.message, + style: TextStyle(color: infoManager.messageColor))), + ListView.builder( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: + Provider.of(context, listen: true).listActivity.length, + itemBuilder: (context, index) { + ActivityOfUser activityObj = + Provider.of(context, listen: true).listActivity[index]; + Map activityMap; + // -- Si categorie == marche + if (activityObj.category == managerFile.marche) { + activityMap = activityObj.toMapWalking(); + return InkWell( + onTap: () {}, + child: WorkoutRowWalking( + wObj: activityMap, + onDelete: () async { + await onDelete(activityObj); + setState(() {}); + }, + onClick: () async { + await onClick(activityObj); + setState(() {}); + }, + isSelected: isSelected(activityObj), + ), + ); + } else { + // -- Default -- // + activityMap = activityObj.toMapGeneric(); + return InkWell( + onTap: () {}, + child: WorkoutRowGeneric( + wObj: activityMap, + onDelete: () async { + await onDelete(activityObj); + setState(() {}); + }, + onClick: () async { + await onClick(activityObj); + setState(() {}); + }, + isSelected: isSelected(activityObj), + ), + ); + } + }, + ), + ], ), ); } diff --git a/lib/common_widget/container/profile/profile_entete.dart b/lib/common_widget/container/profile/profile_entete.dart index b95d21e..7a73c6b 100644 --- a/lib/common_widget/container/profile/profile_entete.dart +++ b/lib/common_widget/container/profile/profile_entete.dart @@ -45,17 +45,7 @@ class ProfileEntete extends StatelessWidget { ], ), ), - SizedBox( - width: 70, - height: 25, - child: RoundButton( - title: "Editer", - type: RoundButtonType.bgGradient, - fontSize: 12, - fontWeight: FontWeight.w400, - onPressed: () {}, - ), - ) + ], ); } diff --git a/lib/common_widget/container/profile/profile_info_user.dart b/lib/common_widget/container/profile/profile_info_user.dart index b404b30..9f48af8 100644 --- a/lib/common_widget/container/profile/profile_info_user.dart +++ b/lib/common_widget/container/profile/profile_info_user.dart @@ -21,7 +21,7 @@ class ProfileInfoUser extends StatelessWidget { ), Expanded( child: TitleSubtitleCell( - title: context.watch().getTotalTimeAllActivity().toString(), + title: context.watch().getTotalTimeAllActivity().toStringAsFixed(2), subtitle: "Temps en activité", ), ), @@ -31,7 +31,7 @@ class ProfileInfoUser extends StatelessWidget { Expanded( child: TitleSubtitleCell( title: - "${context.watch().getTotalDenivelePositif().toString()} + m", + "${context.watch().getTotalDenivelePositifAllActivity().toStringAsFixed(2)} + m", subtitle: "Total dénivelé positif", ), ), @@ -41,7 +41,7 @@ class ProfileInfoUser extends StatelessWidget { Expanded( child: TitleSubtitleCell( title: - "${context.watch().getTotalDeniveleNegatif().toString()} - m", + "${context.watch().getTotalDeniveleNegatifAllActivity().toStringAsFixed(2)} - m", subtitle: "Total dénivelé négatif", ), ), diff --git a/lib/common_widget/container/profile/profile_switch.dart b/lib/common_widget/container/profile/profile_switch.dart new file mode 100644 index 0000000..ccdbdff --- /dev/null +++ b/lib/common_widget/container/profile/profile_switch.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/main.dart'; + +class ProfileSwitch extends StatefulWidget { + final String title; + final String description; + final String iconFilename; + + const ProfileSwitch(this.title, this.description, this.iconFilename, + {super.key}); + + @override + State createState() => _ProfileSwitchState(); +} + +class _ProfileSwitchState extends State { + bool switchValue = localDB.getSaveLocally(); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + decoration: BoxDecoration( + color: TColor.white, + borderRadius: BorderRadius.circular(15), + boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 2)]), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + widget.title, + style: TextStyle( + color: TColor.black, + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + height: 30, + child: + Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ + Image.asset("assets/img/${widget.iconFilename}", + height: 28, width: 28, fit: BoxFit.contain), + const SizedBox( + width: 15, + ), + Expanded( + child: Text( + widget.description, + style: TextStyle( + color: TColor.black, + fontSize: 12, + ), + ), + ), + Switch( + value: switchValue, + activeColor: Colors.orange, + onChanged: (bool value) { + setState(() { + switchValue = value; + localDB.setSaveLocally(switchValue); + }); + }), + ])) + ])); + } +} diff --git a/lib/common_widget/container/workout_row/workout_row_generic.dart b/lib/common_widget/container/workout_row/workout_row_generic.dart new file mode 100644 index 0000000..fa733a9 --- /dev/null +++ b/lib/common_widget/container/workout_row/workout_row_generic.dart @@ -0,0 +1,118 @@ +import 'package:flutter_svg/svg.dart'; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/modele/convertisseur.dart'; + +class WorkoutRowGeneric extends StatelessWidget { + final Map wObj; + final bool isSelected; + final VoidCallback onDelete; + final VoidCallback onClick; + + const WorkoutRowGeneric({ + Key? key, + required this.wObj, + required this.onDelete, + required this.onClick, + required this.isSelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onClick, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 2), + decoration: BoxDecoration( + border: Border.all( + color: isSelected + ? const Color.fromARGB(255, 144, 252, 148) + : Colors.transparent, + width: 2.0, + ), + borderRadius: BorderRadius.circular(10), + ), + child: Material( + color: isSelected + ? const Color.fromARGB(255, 240, 255, 240) + : Colors.transparent, + child: InkWell( + borderRadius: + BorderRadius.circular(10), // Utiliser le même borderRadius + splashColor: const Color.fromARGB(255, 42, 94, 44) + .withOpacity(0.3), // Couleur du fond au survol + onTap: onClick, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: SvgPicture.asset( + wObj["image"].toString(), + width: 60, + height: 60, + fit: BoxFit.cover, + ), + ), + const SizedBox(width: 15), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Type : ${wObj["categorie"].toString()}", + style: TextStyle( + color: TColor.black, + fontSize: 12, + ), + ), + Text( + "Date : ${wObj["date"].toString()}", + style: TextStyle( + color: TColor.black, + fontSize: 12, + ), + ), + Text( + "Temps : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(2)} m", + style: TextStyle( + color: TColor.black, + fontSize: 12, + ), + ) + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + IconButton( + onPressed: onClick, + icon: Image.asset( + "assets/img/next_icon.png", + width: 30, + height: 30, + fit: BoxFit.contain, + ), + ), + IconButton( + onPressed: onDelete, + icon: Image.asset( + "assets/img/corbeille.png", + width: 30, + height: 30, + fit: BoxFit.contain, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/common_widget/container/workout_row.dart b/lib/common_widget/container/workout_row/workout_row_walking.dart similarity index 91% rename from lib/common_widget/container/workout_row.dart rename to lib/common_widget/container/workout_row/workout_row_walking.dart index d4d499a..1f6b5fd 100644 --- a/lib/common_widget/container/workout_row.dart +++ b/lib/common_widget/container/workout_row/workout_row_walking.dart @@ -1,14 +1,15 @@ import 'package:flutter_svg/svg.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/modele/convertisseur.dart'; -class WorkoutRow extends StatelessWidget { +class WorkoutRowWalking extends StatelessWidget { final Map wObj; final bool isSelected; final VoidCallback onDelete; final VoidCallback onClick; - const WorkoutRow({ + const WorkoutRowWalking({ Key? key, required this.wObj, required this.onDelete, @@ -74,14 +75,14 @@ class WorkoutRow extends StatelessWidget { ), ), Text( - "Temps : ${wObj["time"].toString()}", + "Temps : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(2)} m", style: TextStyle( color: TColor.black, fontSize: 12, ), ), Text( - "Dénivelé positif : ${wObj["DenivelePositif"].toString()}", + "Vitesse moyenne : ${Convertisseur.msIntoKmh(wObj["VitesseAvg"]).toStringAsFixed(2)} km/h", style: TextStyle( color: TColor.black, fontSize: 12, diff --git a/lib/common_widget/graph/altitude_by_time.dart b/lib/common_widget/graph/altitude_by_time.dart index 076c86b..c8e8f76 100644 --- a/lib/common_widget/graph/altitude_by_time.dart +++ b/lib/common_widget/graph/altitude_by_time.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:responsive_builder/responsive_builder.dart'; +import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart'; import 'package:smartfit_app_mobile/common_widget/graph/mobile/mobile_altitude_by_time.dart'; import 'package:smartfit_app_mobile/common_widget/graph/web/web_altitude_by_time.dart'; import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; @@ -18,9 +19,10 @@ class GraphAltitudeByTime extends StatefulWidget { class _GraphAltitudeByTime extends State { @override Widget build(BuildContext context) { + final FuncBpmByTime funcBpm = FuncBpmByTime(widget.data); return ScreenTypeLayout.builder( - mobile: (_) => MobileGraphAltitudeByTime(widget.media, widget.data), - desktop: (_) => WebGraphAltitudeByTime(widget.media, widget.data), + mobile: (_) => MobileGraphAltitudeByTime(widget.media, widget.data, funcBpm), + desktop: (_) => WebGraphAltitudeByTime(widget.media, widget.data, funcBpm), ); } } diff --git a/lib/common_widget/graph/bpm_by_time.dart b/lib/common_widget/graph/bpm_by_time.dart index bf0a09f..e51f4ba 100644 --- a/lib/common_widget/graph/bpm_by_time.dart +++ b/lib/common_widget/graph/bpm_by_time.dart @@ -21,7 +21,7 @@ class _BpmByTime extends State { final FuncBpmByTime funcBpm = FuncBpmByTime(widget.data); return ScreenTypeLayout.builder( - mobile: (_) => MobileBpmByTime(widget.media, widget.data), + mobile: (_) => MobileBpmByTime(widget.media, widget.data, funcBpm), desktop: (_) => WebBpmByTime(widget.media, widget.data, funcBpm), ); } diff --git a/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart b/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart index 92fafdb..32388d6 100644 --- a/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart +++ b/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart @@ -14,14 +14,14 @@ class FuncBpmAndSpeedByTime { getTitlesWidget: rightTitleWidgets, showTitles: true, interval: 20, - reservedSize: 40, + reservedSize: 42, ); SideTitles get leftTitles => SideTitles( getTitlesWidget: leftTitleWidgets, showTitles: true, interval: 20, - reservedSize: 40, + reservedSize: 42, ); SideTitles get bottomTitles => SideTitles( getTitlesWidget: bottomTitleWidgets, @@ -79,7 +79,7 @@ class FuncBpmAndSpeedByTime { return Text(text, style: TextStyle( color: TColor.gray, - fontSize: 12, + fontSize: 8, ), textAlign: TextAlign.center); } @@ -113,7 +113,7 @@ class FuncBpmAndSpeedByTime { return Text(text, style: TextStyle( color: TColor.gray, - fontSize: 12, + fontSize: 8, ), textAlign: TextAlign.center); } @@ -148,7 +148,7 @@ class FuncBpmAndSpeedByTime { return Text(text, style: TextStyle( color: TColor.gray, - fontSize: 12, + fontSize: 8, ), textAlign: TextAlign.center); } diff --git a/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart b/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart index afbed1b..a8aea75 100644 --- a/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart +++ b/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart @@ -67,19 +67,19 @@ class FuncBpmByTime { text = '0 s'; break; case 20: - text = "${(interval).toStringAsFixed(2)} s"; + text = "${(interval).toStringAsFixed(0)} s"; break; case 40: - text = "${(interval * 2).toStringAsFixed(2)} s"; + text = "${(interval * 2).toStringAsFixed(0)} s"; break; case 60: - text = "${(interval * 3).toStringAsFixed(2)} s"; + text = "${(interval * 3).toStringAsFixed(0)} s"; break; case 80: - text = "${(interval * 4).toStringAsFixed(2)} s"; + text = "${(interval * 4).toStringAsFixed(0)} s"; break; case 100: - text = "${(interval * 5).toStringAsFixed(2)} s"; + text = "${(interval * 5).toStringAsFixed(0)} s"; break; default: return Container(); diff --git a/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart b/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart index e37bee9..1846fb1 100644 --- a/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart +++ b/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart @@ -2,14 +2,16 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; class MobileGraphAltitudeByTime extends StatefulWidget { final Size media; final DataHomeView data; + final FuncBpmByTime func; - const MobileGraphAltitudeByTime(this.media, this.data, {Key? key}) + const MobileGraphAltitudeByTime(this.media, this.data, this.func, {Key? key}) : super(key: key); @override @@ -70,20 +72,19 @@ class _MobileGraphAltitudeByTime extends State { leftTitles: const AxisTitles(), topTitles: const AxisTitles(), bottomTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 20, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "${double.parse((value / 10).toStringAsFixed(2))}s"); - }, - )), + sideTitles: widget.func.bottomTitles, + ), rightTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 60, showTitles: true, getTitlesWidget: (value, meta) { - return Text("${double.parse(value.toStringAsFixed(2))} m"); + return Text("${double.parse(value.toStringAsFixed(2))} m", + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); }, )), )))); diff --git a/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart b/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart index 3dc8afc..60516e5 100644 --- a/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart +++ b/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart @@ -111,27 +111,16 @@ class _MobileGraphBpmAndSpeedByTime titlesData: FlTitlesData( show: true, leftTitles: AxisTitles( - sideTitles: widget.func.rightTitles, + sideTitles: widget.func.leftTitles, ), topTitles: const AxisTitles(), bottomTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 20, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "${double.parse((value / 10).toStringAsFixed(2))}s"); - }, - )), + sideTitles: widget.func.bottomTitles, + ), rightTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 70, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "${double.parse(value.toStringAsFixed(2))} BPM"); - }, - ))), + sideTitles: widget.func.rightTitles, + + ),), gridData: FlGridData( drawVerticalLine: true, drawHorizontalLine: true, diff --git a/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart b/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart index 9190259..fe420bf 100644 --- a/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart +++ b/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart @@ -1,13 +1,15 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart'; import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; class MobileBpmByTime extends StatefulWidget { final Size media; final DataHomeView data; + final FuncBpmByTime func; - const MobileBpmByTime(this.media, this.data, {Key? key}) : super(key: key); + const MobileBpmByTime(this.media, this.data,this.func, {Key? key}) : super(key: key); @override State createState() => _MobileBpmByTime(); @@ -57,19 +59,19 @@ class _MobileBpmByTime extends State { leftTitles: const AxisTitles(), topTitles: const AxisTitles(), bottomTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 20, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text("${double.parse((value/10).toStringAsFixed(2))}s"); - }, - )), + sideTitles: widget.func.bottomTitles, + ), rightTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 70, showTitles: true, getTitlesWidget: (value, meta) { - return Text("${double.parse(value.toStringAsFixed(2))} BPM"); + return Text("${double.parse(value.toStringAsFixed(2))} BPM", + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); }, )), )))); diff --git a/lib/common_widget/graph/web/web_altitude_by_time.dart b/lib/common_widget/graph/web/web_altitude_by_time.dart index 8868894..a9e108c 100644 --- a/lib/common_widget/graph/web/web_altitude_by_time.dart +++ b/lib/common_widget/graph/web/web_altitude_by_time.dart @@ -2,14 +2,16 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; class WebGraphAltitudeByTime extends StatefulWidget { final Size media; final DataHomeView data; + final FuncBpmByTime func; - const WebGraphAltitudeByTime(this.media, this.data, {Key? key}) + const WebGraphAltitudeByTime(this.media, this.data, this.func, {Key? key}) : super(key: key); @override @@ -69,20 +71,19 @@ class _WebGraphAltitudeByTime extends State { leftTitles: const AxisTitles(), topTitles: const AxisTitles(), bottomTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 20, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "${double.parse((value / 10).toStringAsFixed(2))}s"); - }, - )), + sideTitles: widget.func.bottomTitles, + ), rightTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 60, showTitles: true, getTitlesWidget: (value, meta) { - return Text("${double.parse(value.toStringAsFixed(2))} m"); + return Text("${double.parse(value.toStringAsFixed(2))} m", + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); }, )), )))); diff --git a/lib/common_widget/steps.dart b/lib/common_widget/steps.dart index 03acc68..5354730 100644 --- a/lib/common_widget/steps.dart +++ b/lib/common_widget/steps.dart @@ -9,7 +9,7 @@ class Steps extends StatelessWidget { Widget build(BuildContext context) { String steps = Provider.of(context, listen: false) .managerSelectedActivity - .getTimeAllActivitySelected() + .getStepsAllActivitySelected() .toString(); return Padding( diff --git a/lib/main.dart b/lib/main.dart index 233c2bb..e46d6e8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -39,8 +39,8 @@ class MyApp extends StatelessWidget { stdout.write("===== USER =====\n"); stdout.write("Username: ${user.username}\n"); - stdout.write("Username: ${user.email}\n"); - stdout.write("Username: ${user.token}\n"); + stdout.write("Email: ${user.email}\n"); + stdout.write("Token: ${user.token}\n"); viewToDisplay = const MainTabView(); } diff --git a/lib/modele/activity.dart b/lib/modele/activity.dart index 57b36f1..5637d1c 100644 --- a/lib/modele/activity.dart +++ b/lib/modele/activity.dart @@ -3,7 +3,7 @@ import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; class ActivityOfUser { final ActivityInfo _activityInfo; // A afficher - final String _categorie; + final String _category; final String _fileUuid; final String _nameFile; // ------------ // @@ -14,7 +14,7 @@ class ActivityOfUser { String get fileUuid => _fileUuid; String get nameFile => _nameFile; - String get category => _categorie; + String get category => _category; ActivityInfo get activityInfo => _activityInfo; Map get enteteCSV => _enteteCSV; @@ -29,9 +29,9 @@ class ActivityOfUser { } ActivityOfUser( - this._activityInfo, this._categorie, this._fileUuid, this._nameFile) { + this._activityInfo, this._category, this._fileUuid, this._nameFile) { // Mettre dans une fonction appart - if (_categorie == "Walking") { + if (_category == "Walking") { _imageName = "assets/img/workout1.svg"; } else { // Mettre des conditions pour d'autre type d'activité @@ -43,7 +43,7 @@ class ActivityOfUser { Map toMapGeneric() { Map map = { - 'categorie': _categorie, + 'categorie': _category, 'image': _imageName, 'date': _activityInfo.startTime, 'time': _activityInfo.timeOfActivity, diff --git a/lib/modele/activity_info/activity_info.dart b/lib/modele/activity_info/activity_info.dart index 2617846..92ba698 100644 --- a/lib/modele/activity_info/activity_info.dart +++ b/lib/modele/activity_info/activity_info.dart @@ -16,6 +16,7 @@ class ActivityInfo { int bpmMax = 0; int bpmMin = 300; int bpmAvg = 0; + bool bpmNotZero = false; // ----------- Denivelé ------------ // double denivelePositif = 0.0; double deniveleNegatif = 0.0; @@ -23,19 +24,22 @@ class ActivityInfo { double altitudeMax = 0.0; double altitudeMin = 30000.0; double altitudeAvg = 0.0; + bool altitudeNotZero = false; // ----------- Température --------- // int temperatureMax = 0; int temperatureMin = 3000; int temperatureAvg = 0; + bool temperatureNotZero = false; // ----------- Vitesse ------------- // double vitesseMax = 0.0; double vitesseMin = 999999.0; double vitesseAvg = 0.0; + bool vitesseNotZero = false; // ---------------------------------------------------------------------- // // -- Fonction pour lire le csv et remplir la classe -- // - ActivityInfo getData(List> csv) { + ActivityInfo getDataWalking(List> csv) { // - Entete - // Map enteteCSV = getEntete(csv.first); // ------------- Var tmp ---------- // @@ -63,6 +67,7 @@ class ActivityInfo { int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]); bpmSomme += value; bpmNb += 1; + bpmNotZero = true; if (value > bpmMax) { bpmMax = value; } @@ -91,6 +96,7 @@ class ActivityInfo { } altitudeSomme += value; alititudeNb += 1; + altitudeNotZero = true; } // ------------------------ Température ----------------------- // @@ -100,6 +106,7 @@ class ActivityInfo { csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]); temperatureSomme += value; temperatureNb += 1; + temperatureNotZero = true; if (value > temperatureMax) { temperatureMax = value; } @@ -114,6 +121,7 @@ class ActivityInfo { double.parse(csv[i][enteteCSV["Value_${managerFile.fieldSpeed}"]!]); vitesseSomme += value; vitesseNb += 1; + vitesseNotZero = true; if (value > vitesseMax) { vitesseMax = value; } @@ -124,13 +132,168 @@ class ActivityInfo { } // -- BPM -- // - bpmAvg = bpmSomme ~/ bpmNb; + if (bpmNotZero) { + bpmAvg = bpmSomme ~/ bpmNb; + } + // -- Atitude -- // + if (altitudeNotZero) { + altitudeAvg = altitudeSomme / alititudeNb; + } + // -- Température -- // + if (temperatureNotZero) { + temperatureAvg = temperatureSomme ~/ temperatureNb; + } + // -- Vitesse -- // + if (vitesseNotZero) { + vitesseAvg = vitesseSomme / vitesseNb; + } + return this; + } + + // -- Fonction pour lire le csv et remplir la classe -- // + ActivityInfo getDataCycling(List> csv) { + // - Entete - // + Map enteteCSV = getEntete(csv.first); + // ------------- Var tmp ---------- // + // -- BPM -- // + int bpmSomme = 0; + int bpmNb = 0; + // -- Denivelé -- // + double lastDenivele = 0.0; + // -- Altitude -- // + double altitudeSomme = 0; + int alititudeNb = 0; + // -- Température -- // + int temperatureSomme = 0; + int temperatureNb = 0; + // -- Vitesse -- // + double vitesseSomme = 0.0; + int vitesseNb = 0; + + // --- Boucle -- // + for (int i = 1; i < csv.length; i++) { + // + // ---------------------- BPM ---------------------- // + if (!isNull(enteteCSV["Value_${managerFile.fielBPM}"]!, csv[i])) { + int value = + int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]); + bpmSomme += value; + bpmNb += 1; + bpmNotZero = true; + if (value > bpmMax) { + bpmMax = value; + } + if (value < bpmMin) { + bpmMin = value; + } + } + + /// ------------------ Denivele et Altitude --------------- // + if (!isNull(enteteCSV["Value_${managerFile.fieldAltitude}"]!, csv[i])) { + double value = double.parse( + csv[i][enteteCSV["Value_${managerFile.fieldAltitude}"]!]); + // -- Denivelé -- // + if (value > lastDenivele) { + denivelePositif += value - lastDenivele; + } else { + deniveleNegatif += (value - lastDenivele) * -1; + } + lastDenivele = value; + // -- Altitude -- // + if (value > altitudeMax) { + altitudeMax = value; + } + if (value < altitudeMin) { + altitudeMin = value; + } + altitudeSomme += value; + alititudeNb += 1; + altitudeNotZero = true; + } + + // ------------------------ Température ----------------------- // + if (!isNull( + enteteCSV["Value_${managerFile.fieldTemperature}"]!, csv[i])) { + int value = int.parse( + csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]); + temperatureSomme += value; + temperatureNb += 1; + vitesseNotZero = true; + if (value > temperatureMax) { + temperatureMax = value; + } + if (value < temperatureMin) { + temperatureMin = value; + } + } + + // ------------------------ Vitesse -----------------------------// + if (!isNull(enteteCSV["Value_${managerFile.fieldSpeed}"]!, csv[i])) { + double value = + double.parse(csv[i][enteteCSV["Value_${managerFile.fieldSpeed}"]!]); + vitesseSomme += value; + vitesseNb += 1; + vitesseNotZero = true; + if (value > vitesseMax) { + vitesseMax = value; + } + if (value < vitesseMin) { + vitesseMin = value; + } + } + } + + // -- BPM -- // + if (bpmNotZero) { + bpmAvg = bpmSomme ~/ bpmNb; + } // -- Atitude -- // - altitudeAvg = altitudeSomme / alititudeNb; + if (altitudeNotZero) { + altitudeAvg = altitudeSomme / alititudeNb; + } // -- Température -- // - temperatureAvg = temperatureSomme ~/ temperatureNb; + if (temperatureNotZero) { + temperatureAvg = temperatureSomme ~/ temperatureNb; + } // -- Vitesse -- // - vitesseAvg = vitesseSomme / vitesseNb; + if (vitesseNotZero) { + vitesseAvg = vitesseSomme / vitesseNb; + } + return this; + } + +// -- Fonction pour lire le csv et remplir la classe -- // + ActivityInfo getDataGeneric(List> csv) { + // - Entete - // + Map enteteCSV = getEntete(csv.first); + // ------------- Var tmp ---------- // + // -- BPM -- // + int bpmSomme = 0; + int bpmNb = 0; + bool bpmNotZero = false; + // --- Boucle -- // + for (int i = 1; i < csv.length; i++) { + // + // ---------------------- BPM ---------------------- // + if (!isNull(enteteCSV["Value_${managerFile.fielBPM}"]!, csv[i])) { + int value = + int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]); + bpmSomme += value; + bpmNb += 1; + bpmNotZero = true; + if (value > bpmMax) { + bpmMax = value; + } + if (value < bpmMin) { + bpmMin = value; + } + } + } + + // -- BPM -- // + if (bpmNotZero) { + bpmAvg = bpmSomme ~/ bpmNb; + } return this; } @@ -156,7 +319,9 @@ class ActivityInfo { // -- Altitude -- // "AltitudeMax": altitudeMax, "AltitudeMin": altitudeMin, - "AltitudeAvg": altitudeAvg + "AltitudeAvg": altitudeAvg, + // -- Vitesse -- // + "VitesseAvg": vitesseAvg }; } @@ -167,30 +332,106 @@ class ActivityInfo { return; } // -- Ligne session -- // - startTime = DateTime.parse(map["startTime"]); - timeOfActivity = map["timeOfActivity"].toDouble(); - distance = map["distance"].toDouble(); - calories = map["calories"].toInt(); - steps = map["steps"].toInt(); + try { + startTime = DateTime.parse(map["startTime"]); + } catch (e) { + print("Impossible de recup -> startTime"); + } + try { + timeOfActivity = map["timeOfActivity"]; + } catch (e) { + print("Impossible de recup -> timeOfActivity"); + } + try { + distance = map["distance"].toDouble(); + } catch (e) { + print("Impossible de recup -> distance"); + } + try { + calories = map["calories"]; + } catch (e) { + print("Impossible de recup -> calories"); + } + try { + steps = map["steps"]; + } catch (e) { + print("Impossible de recup -> steps"); + } // -- BPM -- // - bpmAvg = map["bpmAvg"]; - bpmMax = map["bpmMax"]; - bpmMin = map["bpmMin"]; + try { + bpmAvg = map["bpmAvg"]; + } catch (e) { + print("Impossible de recup -> "); + } + try { + bpmMax = map["bpmMax"]; + } catch (e) { + print("Impossible de recup -> "); + } + try { + bpmMin = map["bpmMin"]; + } catch (e) { + print("Impossible de recup -> "); + } // -- Denivelé -- // - deniveleNegatif = map["deniveleNegatif"].toDouble(); - denivelePositif = map["denivelePositif"].toDouble(); + try { + deniveleNegatif = map["deniveleNegatif"]; + } catch (e) { + print("Impossible de recup -> deniveleNegatif"); + } + try { + denivelePositif = map["denivelePositif"]; + } catch (e) { + print("Impossible de recup -> denivelePositif"); + } // -- Altitude -- // - altitudeMax = map["altitudeMax"].toDouble(); - altitudeMin = map["altitudeMin"].toDouble(); - altitudeAvg = map["altitudeAvg"].toDouble(); + try { + altitudeMax = map["altitudeMax"]; + } catch (e) { + print("Impossible de recup -> altitudeMax"); + } + try { + altitudeMin = map["altitudeMin"]; + } catch (e) { + print("Impossible de recup -> altitudeMin"); + } + try { + altitudeAvg = map["altitudeAvg"]; + } catch (e) { + print("Impossible de recup -> altitudeAvg"); + } // -- Température -- // - temperatureMax = map["temperatureMax"].toInt(); - temperatureMin = map["temperatureMin"].toInt(); - temperatureAvg = map["temperatureAvg"].toInt(); + try { + temperatureMax = map["temperatureMax"]; + } catch (e) { + print("Impossible de recup -> temperatureMax"); + } + try { + temperatureMin = map["temperatureMin"]; + } catch (e) { + print("Impossible de recup -> temperatureMin"); + } + try { + temperatureAvg = map["temperatureAvg"]; + } catch (e) { + print("Impossible de recup -> temperatureAvg"); + } // -- Vitesse -- // - vitesseMax = map["vitesseMax"].toDouble(); - vitesseMin = map["vitesseMin"].toDouble(); - vitesseAvg = map["vitesseAvg"].toDouble(); + try { + vitesseMax = map["vitesseMax"].toDouble(); + } catch (e) { + print("Impossible de recup -> vitesseMax"); + } + try { + vitesseMin = map["vitesseMin"].toDouble(); + } catch (e) { + print("Impossible de recup -> vitesseMin"); + } + try { + vitesseAvg = map["vitesseAvg"].toDouble(); + } catch (e) { + print("Impossible de recup -> vitesseAvg"); + } } // -- Ecriture -- // @@ -216,7 +457,7 @@ class ActivityInfo { 'vitesseMin': vitesseMin, 'vitesseAvg': vitesseAvg, // Ligne session - 'startTime': startTime.toString(), + 'startTime': startTime.toIso8601String(), 'timeOfActivity': timeOfActivity, 'distance': distance, 'calories': calories, diff --git a/lib/modele/activity_saver.dart b/lib/modele/activity_saver.dart index 6f3ab68..529e476 100644 --- a/lib/modele/activity_saver.dart +++ b/lib/modele/activity_saver.dart @@ -1,10 +1,9 @@ import 'dart:io'; import 'dart:typed_data'; -import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as p; - -import 'package:smartfit_app_mobile/modele/helper.dart'; +import "package:path_provider/path_provider.dart"; +import 'package:smartfit_app_mobile/main.dart'; class ActivitySaver { String saveDirectory = "activities"; @@ -12,21 +11,33 @@ class ActivitySaver { ActivitySaver._create(this.applicationDocumentsDir); - static Future create() async { - final appDir = await getApplicationDocumentsDirectory(); - return ActivitySaver._create(appDir); + Uint8List getActivity(String uuid) { + String filename = localDB.getActivityFilenameByUuid(uuid); + + final file = + File(p.join(applicationDocumentsDir.path, saveDirectory, filename)); + return file.readAsBytesSync(); } Future saveActivity(Uint8List activityFile, String filename) async { + stdout.write("Creating activity file...\n"); final file = await File( p.join(applicationDocumentsDir.path, saveDirectory, filename)) .create(recursive: true); // To create dir if not exists file.writeAsBytesSync(activityFile); + stdout.write("Activity file created\n"); } - File getActivity(String filename) { + void deleteActivity(String uuid) { + String filename = localDB.getActivityFilenameByUuid(uuid); final file = File(p.join(applicationDocumentsDir.path, saveDirectory, filename)); - return file; + file.deleteSync(); + } + + static Future create() async { + stdout.write("Activity Saver: Created\n"); + final appDir = await getApplicationDocumentsDirectory(); + return ActivitySaver._create(appDir); } } diff --git a/lib/modele/api/api_wrapper.dart b/lib/modele/api/api_wrapper.dart index fc3ddd0..2070a59 100644 --- a/lib/modele/api/api_wrapper.dart +++ b/lib/modele/api/api_wrapper.dart @@ -9,6 +9,7 @@ import 'package:email_validator/email_validator.dart'; import 'package:tuple/tuple.dart'; import 'dart:convert'; import 'package:crypto/crypto.dart'; +import 'package:smartfit_app_mobile/main.dart'; import 'dart:io'; class ApiWrapper { @@ -34,9 +35,12 @@ class ApiWrapper { if (await isOnline()) { stdout.write("(API) "); api = RequestApi(); - } else { + } else if (localDB.getSaveLocally()) { stdout.write("(LOCAL) "); api = RequestLocal(); + } else { + stdout.write("(API OFFLINE) "); + api = RequestApi(); } } @@ -57,18 +61,27 @@ class ApiWrapper { return res; } - Future getFile(String token, String fileUuid) async { + Future getFile( + String token, String fileUuid, InfoMessage infoManager) async { await init(); Tuple2 res = await api.getFile(token, fileUuid); + if (!res.item1) { + infoManager.displayMessage(noConnectionMessage, true); + } + stdout.write("getFile: ${res.item1}\n"); return res; } - Future getFiles(String token) async { + Future getFiles(String token, InfoMessage infoManager) async { await init(); Tuple2 res = await api.getFiles(token); + if (!res.item1) { + infoManager.displayMessage(noConnectionMessage, true); + } + stdout.write("getFiles: ${res.item1}\n"); return res; } @@ -110,7 +123,7 @@ class ApiWrapper { String hash = sha256.convert(utf8.encode(password)).toString(); Tuple2 res = await api.connexion(email, hash); - stdout.write("login: ${res.item1}"); + stdout.write("login: ${res.item1}\n"); if (res.item1) { return Tuple2(true, res.item2); } else { @@ -128,7 +141,7 @@ class ApiWrapper { Tuple2 res = await api.deleteUser(token); - stdout.write("deleteUser: ${res.item1}"); + stdout.write("deleteUser: ${res.item1}\n"); return res; } @@ -139,7 +152,7 @@ class ApiWrapper { Tuple2 res = await api.postUser(email, hash, username); - stdout.write("createUser: ${res.item1}"); + stdout.write("createUser: ${res.item1}\n"); return res; } @@ -149,7 +162,7 @@ class ApiWrapper { if (handleOffline(infoManager)) return const Tuple2(false, "offline"); Tuple2 res = await api.uploadFile(token, file); - stdout.write("uploadFile: ${res.item1}"); + stdout.write("uploadFile: ${res.item1}\n"); return res; } @@ -166,7 +179,9 @@ class ApiWrapper { Tuple2 res = await api.uploadFileByte( token, contentFile, filename, category, date, activityInfo); - stdout.write("uploadFileByte: ${res.item1}"); + if (!res.item1) infoManager.displayMessage(noConnectionMessage, true); + + stdout.write("uploadFileByte: ${res.item1}\n"); return res; } @@ -176,8 +191,9 @@ class ApiWrapper { if (handleOffline(infoManager)) return false; bool res = await api.deleteFile(token, fileUuid); + if (!res) infoManager.displayMessage(noConnectionMessage, true); - stdout.write("deleteFile: ${res}"); + stdout.write("deleteFile: $res\n"); return res; } } diff --git a/lib/modele/api/request_api.dart b/lib/modele/api/request_api.dart index 03af1c8..bf525af 100644 --- a/lib/modele/api/request_api.dart +++ b/lib/modele/api/request_api.dart @@ -19,36 +19,44 @@ class RequestApi implements IDataStrategy { var request = http.Request('GET', url); request.headers.addAll({'Authorization': token}); - var streamedResponse = await request.send(); - final response = await http.Response.fromStream(streamedResponse); - // !! Crée un fichier comme ca avec les bytes !! - //File("//").writeAsBytes(response.bodyBytes); + try { + var streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); - if (response.statusCode == 200) { - return Tuple2(true, response.bodyBytes); - } - if ((response.statusCode == 401)) { - return const Tuple2(false, "401 - UNAUTHORIZED"); - } - if ((response.statusCode == 404)) { - return const Tuple2(false, "404 - NOT FOUND"); + if (response.statusCode == 200) { + return Tuple2(true, response.bodyBytes); + } + if ((response.statusCode == 401)) { + return const Tuple2(false, "401 - UNAUTHORIZED"); + } + if ((response.statusCode == 404)) { + return const Tuple2(false, "404 - NOT FOUND"); + } + // When Network Off + } on SocketException { + return const Tuple2(false, "No connection"); } + return const Tuple2(false, "Fail"); } @override Future deleteFile(String token, String fileUuid) async { - final response = await http.delete( - Uri.parse('$urlApi/user/files/$fileUuid'), - headers: {'Authorization': token}); + try { + final response = await http.delete( + Uri.parse('$urlApi/user/files/$fileUuid'), + headers: {'Authorization': token}); - if (response.statusCode == 200) { - return true; - } - if (response.statusCode == 401) { - return false; - } - if (response.statusCode == 404) { + if (response.statusCode == 200) { + return true; + } + if (response.statusCode == 401) { + return false; + } + if (response.statusCode == 404) { + return false; + } + } on SocketException catch (_) { return false; } return false; @@ -56,48 +64,60 @@ class RequestApi implements IDataStrategy { @override Future> deleteUser(String token) async { - final response = await http.delete(Uri.parse('$urlApi/user'), - headers: {'Authorization': token}); - if (response.statusCode == 200) { - return const Tuple2(true, "Successful"); - } else if (response.statusCode == 401) { - return const Tuple2( - false, "401 UNAUTHORIZED - Mauvais ou pas de token"); - } else if (response.statusCode == 404) { - return const Tuple2( - false, "404 NOT FOUND - Pas de compte lié"); + try { + final response = await http.delete(Uri.parse('$urlApi/user'), + headers: {'Authorization': token}); + if (response.statusCode == 200) { + return const Tuple2(true, "Successful"); + } else if (response.statusCode == 401) { + return const Tuple2( + false, "401 UNAUTHORIZED - Mauvais ou pas de token"); + } else if (response.statusCode == 404) { + return const Tuple2( + false, "404 NOT FOUND - Pas de compte lié"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail"); } @override Future getFiles(String token) async { - final response = await http.get(Uri.parse('$urlApi/user/files'), - headers: {'Authorization': token}); + try { + final response = await http.get(Uri.parse('$urlApi/user/files'), + headers: {'Authorization': token}); - if (response.statusCode == 200) { - return Tuple2(true, - (json.decode(response.body) as List).cast>()); - } - if (response.statusCode == 401) { - return const Tuple2(false, "401 - UNAUTHORIZED"); + if (response.statusCode == 200) { + return Tuple2(true, + (json.decode(response.body) as List).cast>()); + } + if (response.statusCode == 401) { + return const Tuple2(false, "401 - UNAUTHORIZED"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail"); } @override Future> connexion(String email, String hash) async { - final response = - await http.get(Uri.parse('$urlApi/user/login/$email/$hash')); - if (response.statusCode == 200) { - Map json = jsonDecode(response.body); - return Tuple2(true, json['token'].toString()); - } - if (response.statusCode == 401) { - return const Tuple2(false, "UNAUTHORIZED"); - } - if (response.statusCode == 404) { - return const Tuple2(false, "Not found the email"); + try { + final response = + await http.get(Uri.parse('$urlApi/user/login/$email/$hash')); + if (response.statusCode == 200) { + Map json = jsonDecode(response.body); + return Tuple2(true, json['token'].toString()); + } + if (response.statusCode == 401) { + return const Tuple2(false, "UNAUTHORIZED"); + } + if (response.statusCode == 404) { + return const Tuple2(false, "Not found the email"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail"); } @@ -107,19 +127,23 @@ class RequestApi implements IDataStrategy { String email, String hash, String username) async { var body = {"email": email, "hash": hash, "username": username}; var header = {"Content-Type": "application/json"}; - final response = await http.post(Uri.parse('$urlApi/user'), - headers: header, body: jsonEncode(body)); + try { + final response = await http.post(Uri.parse('$urlApi/user'), + headers: header, body: jsonEncode(body)); - if (response.statusCode == 200) { - Map json = jsonDecode(response.body); - return Tuple2(true, json['token'].toString()); - } - if (response.statusCode == 400) { - return const Tuple2(false, "400 BAD REQUEST - Json mal formaté"); - } - if (response.statusCode == 409) { - return const Tuple2( - false, "409 CONFLICT - Déja un compte avec cet email"); + if (response.statusCode == 200) { + Map json = jsonDecode(response.body); + return Tuple2(true, json['token'].toString()); + } + if (response.statusCode == 400) { + return const Tuple2(false, "400 BAD REQUEST - Json mal formaté"); + } + if (response.statusCode == 409) { + return const Tuple2( + false, "409 CONFLICT - Déja un compte avec cet email"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail"); } @@ -127,25 +151,28 @@ class RequestApi implements IDataStrategy { @override Future> modifAttribut( String token, String nameAttribut, String newValue) async { - final response = await http.put(Uri.parse('$urlApi/user/$nameAttribut'), - headers: { - 'Authorization': token, - "Content-Type": "application/json" - }, - body: jsonEncode({nameAttribut: newValue})); - - if (response.statusCode == 200) { - //Map json = jsonDecode(response.body); - return const Tuple2(true, "200 - OK"); - } - if (response.statusCode == 400) { - return const Tuple2(false, "400 - BAD REQUEST"); - } - if (response.statusCode == 401) { - return const Tuple2(false, "400 - UNAUTHORIZED"); - } else { - return const Tuple2(false, "Fail"); + try { + final response = await http.put(Uri.parse('$urlApi/user/$nameAttribut'), + headers: { + 'Authorization': token, + "Content-Type": "application/json" + }, + body: jsonEncode({nameAttribut: newValue})); + + if (response.statusCode == 200) { + //Map json = jsonDecode(response.body); + return const Tuple2(true, "200 - OK"); + } + if (response.statusCode == 400) { + return const Tuple2(false, "400 - BAD REQUEST"); + } + if (response.statusCode == 401) { + return const Tuple2(false, "400 - UNAUTHORIZED"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } + return const Tuple2(false, "Fail"); } // -- Priviligié uploadFileByte -- // @@ -169,19 +196,23 @@ class RequestApi implements IDataStrategy { request.fields["SmartFit_Category"] = categoryActivity; request.fields["SmartFit_Date"] = dateActivity; - final response = await request.send(); + try { + final response = await request.send(); - if (response.statusCode == 200) { - return const Tuple2(true, "Successful"); - } - if (response.statusCode == 400) { - return const Tuple2(false, "400 - BAD REQUEST"); - } - if (response.statusCode == 401) { - return const Tuple2(false, "401 - UNAUTHORIZED"); - } - if (response.statusCode == 409) { - return const Tuple2(false, "409 - CONFLICT"); + if (response.statusCode == 200) { + return const Tuple2(true, "Successful"); + } + if (response.statusCode == 400) { + return const Tuple2(false, "400 - BAD REQUEST"); + } + if (response.statusCode == 401) { + return const Tuple2(false, "401 - UNAUTHORIZED"); + } + if (response.statusCode == 409) { + return const Tuple2(false, "409 - CONFLICT"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail "); } @@ -209,36 +240,44 @@ class RequestApi implements IDataStrategy { request.fields["SmartFit_Date"] = date.toString(); request.fields["info"] = activityInfo.toJson(); - final response = await request.send(); + try { + final response = await request.send(); - if (response.statusCode == 200) { - return const Tuple2(true, "Successful"); - } - if (response.statusCode == 400) { - return const Tuple2(false, "400 - BAD REQUEST"); - } - if (response.statusCode == 401) { - return const Tuple2(false, "401 - UNAUTHORIZED"); - } - if (response.statusCode == 409) { - return const Tuple2(false, "409 - CONFLICT"); + if (response.statusCode == 200) { + return const Tuple2(true, "Successful"); + } + if (response.statusCode == 400) { + return const Tuple2(false, "400 - BAD REQUEST"); + } + if (response.statusCode == 401) { + return const Tuple2(false, "401 - UNAUTHORIZED"); + } + if (response.statusCode == 409) { + return const Tuple2(false, "409 - CONFLICT"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail "); } @override Future getInfoUser(String token) async { - final response = await http.get(Uri.parse('$urlApi/user/info'), - headers: {'Authorization': token}); - if (response.statusCode == 200) { - Map json = jsonDecode(response.body); - return Tuple2(true, json); - } - if (response.statusCode == 400) { - return const Tuple2(false, "400 - BAD REQUEST"); - } - if (response.statusCode == 401) { - return const Tuple2(false, "401 - UNAUTHORIZED"); + try { + final response = await http.get(Uri.parse('$urlApi/user/info'), + headers: {'Authorization': token}); + if (response.statusCode == 200) { + Map json = jsonDecode(response.body); + return Tuple2(true, json); + } + if (response.statusCode == 400) { + return const Tuple2(false, "400 - BAD REQUEST"); + } + if (response.statusCode == 401) { + return const Tuple2(false, "401 - UNAUTHORIZED"); + } + } on SocketException catch (_) { + return const Tuple2(false, "No connection"); } return const Tuple2(false, "Fail"); } diff --git a/lib/modele/convertisseur.dart b/lib/modele/convertisseur.dart new file mode 100644 index 0000000..89e3ebc --- /dev/null +++ b/lib/modele/convertisseur.dart @@ -0,0 +1,11 @@ +class Convertisseur { + // Mettre que des trucs static + + static double secondeIntoMinute(double seconde) { + return seconde / 60; + } + + static double msIntoKmh(double metreSeconde) { + return metreSeconde * 3.6; + } +} diff --git a/lib/modele/local_db/model.dart b/lib/modele/local_db/model.dart index a9f06dc..38dc72d 100644 --- a/lib/modele/local_db/model.dart +++ b/lib/modele/local_db/model.dart @@ -14,7 +14,6 @@ class User { @Entity() class Activity { int id; - @Unique() String uuid; String filename; @@ -23,3 +22,12 @@ class Activity { Activity(this.id, this.uuid, this.filename, this.category, this.info); } + +@Entity() +class Config { + @Id() + int id = 0; + bool saveLocally; + + Config(this.id, this.saveLocally); +} diff --git a/lib/modele/local_db/objectbox.dart b/lib/modele/local_db/objectbox.dart index 7b64eca..587d966 100644 --- a/lib/modele/local_db/objectbox.dart +++ b/lib/modele/local_db/objectbox.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:io'; -import 'dart:typed_data'; -import 'package:csv/csv.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; import 'package:smartfit_app_mobile/objectbox.g.dart'; @@ -14,7 +12,7 @@ class ObjectBox { late final Store store; late final Box userBox; late final Box activityBox; - late final String activitiesSavePath = "activities"; + late final Box configBox; late final Directory applicationDocumentDir; ObjectBox._create(this.store); @@ -29,6 +27,7 @@ class ObjectBox { applicationDocumentDir = await getApplicationDocumentsDirectory(); userBox = store.box(); activityBox = store.box(); + configBox = store.box(); } // ===== USER ===== @@ -73,8 +72,19 @@ class ObjectBox { } } - void removeActivity(int uuid) { - activityBox.remove(uuid); + // TODO: try catch + void removeActivity(String uuid) { + final Query query = activityBox.query(Activity_.uuid.equals(uuid)).build(); + final Activity act = query.findFirst(); + + activityBox.remove(act.id); + } + + String getActivityFilenameByUuid(String uuid) { + final Query query = activityBox.query(Activity_.uuid.equals(uuid)).build(); + final Activity act = query.findFirst(); + + return act.filename; } List getAllActivities() { @@ -87,21 +97,6 @@ class ObjectBox { } // ===== FIT Files ===== - Future saveActivityFile(List> activityFile) async { - String csv = const ListToCsvConverter().convert(activityFile); - Uint8List csvAsBytes = Uint8List.fromList(utf8.encode(csv)); - final file = - await File(p.join(applicationDocumentDir.path, activitiesSavePath)) - .create(); - file.writeAsBytesSync(csvAsBytes); - } - - File getActivityFile(String filename) { - final file = - File(p.join(applicationDocumentDir.path, activitiesSavePath, filename)); - return file; - } - List loadActivities() { List activityDBList = activityBox.getAll(); List userActivityList = List.empty(growable: true); @@ -114,4 +109,18 @@ class ObjectBox { return userActivityList; } + + // ===== Config ===== + void setSaveLocally(bool saveLocally) { + Config config = configBox.get(1); + config.saveLocally = saveLocally; + configBox.put(config); + stdout.write("(Config) setSaveLocally: $saveLocally\n"); + } + + bool getSaveLocally() { + Config config = configBox.get(1); + stdout.write("(Config) getSaveLocally: ${config.saveLocally}\n"); + return config.saveLocally; + } } diff --git a/lib/modele/local_db/request_local.dart b/lib/modele/local_db/request_local.dart index 4179244..4e9bad3 100644 --- a/lib/modele/local_db/request_local.dart +++ b/lib/modele/local_db/request_local.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; +import 'package:smartfit_app_mobile/modele/activity_saver.dart'; import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; import 'package:smartfit_app_mobile/modele/local_db/model.dart'; import 'package:tuple/tuple.dart'; @@ -18,7 +19,10 @@ class RequestLocal implements IDataStrategy { // need to save file on request_api.upload() beforehand. @override Future getFile(String token, String fileUuid) async { - return const Tuple2(true, "to implement"); + ActivitySaver actSaver = await ActivitySaver.create(); + Uint8List fileBytes = actSaver.getActivity(fileUuid); + + return Tuple2(true, fileBytes); } @override diff --git a/lib/modele/manager_file.dart b/lib/modele/manager_file.dart index ede5e0f..efb9959 100644 --- a/lib/modele/manager_file.dart +++ b/lib/modele/manager_file.dart @@ -113,7 +113,7 @@ class ManagerFile { categorie = _getCategoryById(int.parse(_getXfromListe(_sport, ligneSession))); - // -- Si la catégorie est pas prévu est est généric -- // + // -- Si la catégorie est pas prévu == généric -- // switch (categorie) { case (_marche): fieldAllowed = allowedFieldWalking; @@ -137,15 +137,24 @@ class ManagerFile { // ------ Remplir info avec la ligne session --------- // info.startTime = DateTime.fromMillisecondsSinceEpoch( int.parse(_getXfromListe(_startTime, ligneSession))); - info.timeOfActivity = double.parse(_getXfromListe(_timeActivity, ligneSession)); info.distance = double.parse(_getXfromListe(_totalDistance, ligneSession)); info.calories = int.parse(_getXfromListe(_totalCalories, ligneSession)); info.steps = int.parse(_getXfromListe(_totalStep, ligneSession)); // ----------------------------------------------------- // + // -- Extraire les données en fonction de la catégorie -- // + switch (categorie) { + case (_marche): + info.getDataWalking(csvData); + case (_velo): + info.getDataCycling(csvData); + default: + info.getDataGeneric(csvData); + } - return Tuple4(true, csvData, info.getData(csvData), categorie); + //print("Fin : ManagerFile -> convertBytesFitFileIntoCSVListAndGetInfo "); + return Tuple4(true, csvData, info, categorie); } List _getLigneSession(List listRecord) { @@ -164,7 +173,7 @@ class ManagerFile { return liste[i + 1].toString(); } } - return "null"; + return "0"; } List>> getDataOfListeOfRecord( diff --git a/lib/modele/user.dart b/lib/modele/user.dart index 5320f97..59a4b72 100644 --- a/lib/modele/user.dart +++ b/lib/modele/user.dart @@ -37,7 +37,7 @@ class User extends ChangeNotifier { // ------------ Walking -------------- // // ---- Denivelé ---- // - double getTotalDenivelePositif() { + double getTotalDenivelePositifAllActivity() { double totalDevPos = 0.0; for (ActivityOfUser activity in listActivity) { totalDevPos += activity.activityInfo.denivelePositif; @@ -45,7 +45,7 @@ class User extends ChangeNotifier { return totalDevPos; } - double getTotalDeniveleNegatif() { + double getTotalDeniveleNegatifAllActivity() { double totalDevNeg = 0.0; for (ActivityOfUser activity in listActivity) { totalDevNeg += activity.activityInfo.deniveleNegatif; diff --git a/lib/modele/utile/home_view/home_view_util.dart b/lib/modele/utile/home_view/home_view_util.dart index 36bec08..4cc56ad 100644 --- a/lib/modele/utile/home_view/home_view_util.dart +++ b/lib/modele/utile/home_view/home_view_util.dart @@ -22,7 +22,7 @@ class HomeViewUtil { List bpmSecondes2 = List.from(bpmSecondes); return DataHomeView(normaliserPremierElement(bpmSecondes), normaliserPremierElement(normaliserDeuxiemeElement(bpmSecondes2)), - normaliserPremierElement(normaliserDeuxiemeElement(vitesseSecondes)), altitudeSeconde); + normaliserPremierElement(normaliserDeuxiemeElement(vitesseSecondes)), normaliserPremierElement(altitudeSeconde)); } List normaliserDeuxiemeElement(List liste) { diff --git a/lib/modele/utile/list_activity/list_activity_utile.dart b/lib/modele/utile/list_activity/list_activity_utile.dart index 976f06c..6517f3a 100644 --- a/lib/modele/utile/list_activity/list_activity_utile.dart +++ b/lib/modele/utile/list_activity/list_activity_utile.dart @@ -11,8 +11,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; -import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; -import 'package:smartfit_app_mobile/modele/api/request_api.dart'; import 'package:smartfit_app_mobile/modele/manager_file.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/modele/utile/info_message.dart'; @@ -22,11 +20,12 @@ class ListActivityUtile { final ApiWrapper api = ApiWrapper(); final ManagerFile _managerFile = ManagerFile(); - Future> getContentActivity( - BuildContext context, ActivityOfUser activityOfUser) async { + Future> getContentActivity(BuildContext context, + ActivityOfUser activityOfUser, InfoMessage infoManager) async { Tuple2 result = await api.getFile( Provider.of(context, listen: false).token, - activityOfUser.fileUuid); + activityOfUser.fileUuid, + infoManager); if (result.item1 == false) { return Tuple2(result.item1, result.item2); } @@ -37,6 +36,14 @@ class ListActivityUtile { // TODO: Not sure this line as an utility // localDB.saveActivityFile(activityOfUser.contentActivity); + // TODO: Check if file exists, right now it overwrites each time + // TODO: Make ActivitySaver member of the class + if (!Helper.isPlatformWeb() && localDB.getSaveLocally()) { + ActivitySaver actSaver = await ActivitySaver.create(); + actSaver.saveActivity(result.item2, + localDB.getActivityFilenameByUuid(activityOfUser.fileUuid)); + } + if (!Provider.of(context, listen: false) .managerSelectedActivity .addSelectedActivity(activityOfUser)) { @@ -46,10 +53,10 @@ class ListActivityUtile { } Future> getFiles( - String token, BuildContext context) async { + String token, BuildContext context, InfoMessage infoManager) async { bool notZero = false; - Tuple2 result = - await api.getFiles(Provider.of(context, listen: false).token); + Tuple2 result = await api.getFiles( + Provider.of(context, listen: false).token, infoManager); if (result.item1 == false) { return Tuple2(result.item1, result.item2); } @@ -68,18 +75,13 @@ class ListActivityUtile { element["filename"].toString())); // Save to local db - localDB.addActivity(db.Activity( - 0, - element["uuid"], - element["filename"], - element["category"], - element["info"] - .toString())); // TODO: Do not remove toString(), it do not work w/o it, idk why + localDB.addActivity(db.Activity(0, element["uuid"], element["filename"], + element["category"], jsonEncode(element["info"]))); } return const Tuple2(true, "Yeah"); } - Future> _addFile(Uint8List bytes, String filename, + Future> addFile(Uint8List bytes, String filename, String token, InfoMessage infoManager) async { // -- Transormer le fit en CSV Tuple4>, ActivityInfo, String> resultData = @@ -88,55 +90,58 @@ class ListActivityUtile { String csvString = const ListToCsvConverter().convert(resultData.item2); Uint8List byteCSV = Uint8List.fromList(utf8.encode(csvString)); - // Save on local storage if plateform not browser - if (!Helper.isPlatformWeb()) { - ActivitySaver actSaver = await ActivitySaver.create(); - actSaver.saveActivity(byteCSV, filename); - } - Tuple2 result = await api.uploadFileByte( token, byteCSV, filename, - resultData.item4, - resultData.item3.startTime, + resultData.item4, // category + resultData.item3.startTime, // activityInfo resultData.item3, infoManager); if (result.item1 == false) { return Tuple2(false, result.item2); } + + // Save on local storage if plateform not browser + if (!Helper.isPlatformWeb() && localDB.getSaveLocally()) { + ActivitySaver actSaver = await ActivitySaver.create(); + actSaver.saveActivity(byteCSV, filename); + } + return const Tuple2(true, "Yeah"); } - // --- Ne marche pas sous window !! Jsp linux (mettre en format mobile) -- // - void addFileWeb(Uint8List? bytes, String token, String filename, + // --- Ne marche pas sous windows !! Jsp linux (mettre en format mobile) -- // + Future addFileWeb(Uint8List? bytes, String token, String filename, BuildContext context, InfoMessage infoManager) async { if (bytes == null) { return; } Tuple2 resultAdd = - await _addFile(bytes, filename, token, infoManager); + await addFile(bytes, filename, token, infoManager); if (!resultAdd.item1) { //print("Message error"); return; } - Tuple2 resultGet = await getFiles(token, context); + Tuple2 resultGet = + await getFiles(token, context, infoManager); if (!resultGet.item1) { //print("Message error"); return; } } - void addFileMobile(String path, String token, String filename, + Future addFileMobile(String path, String token, String filename, BuildContext context, InfoMessage infoManager) async { - Tuple2 resultAdd = await _addFile( + Tuple2 resultAdd = await addFile( await File(path).readAsBytes(), filename, token, infoManager); if (!resultAdd.item1) { //print("Message error"); return; } // TODO: What is that ? - Tuple2 resultGet = await getFiles(token, context); + Tuple2 resultGet = + await getFiles(token, context, infoManager); if (!resultGet.item1) { //print("Message error"); return; diff --git a/lib/view/activity/mobile/mobile_list_activity.dart b/lib/view/activity/mobile/mobile_list_activity.dart index 3a5d3be..bea9c2d 100644 --- a/lib/view/activity/mobile/mobile_list_activity.dart +++ b/lib/view/activity/mobile/mobile_list_activity.dart @@ -29,7 +29,7 @@ class _MobileListActivity extends State { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 20), Row( @@ -43,9 +43,13 @@ class _MobileListActivity extends State { fontWeight: FontWeight.w700), ), TextButton( - onPressed: () => _utile.getFiles( - Provider.of(context, listen: false).token, - context), + onPressed: () async { + await _utile.getFiles( + Provider.of(context, listen: false).token, + context, + infoManager); + setState(() {}); + }, child: Text("Get activity", style: TextStyle( color: TColor.gray, @@ -56,17 +60,18 @@ class _MobileListActivity extends State { FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null && result.files.isNotEmpty) { - // ignore: use_build_context_synchronously - _utile.addFileMobile( + await _utile.addFileMobile( result.files.single.path!, Provider.of(context, listen: false).token, result.files.first.name, context, infoManager); + setState(() {}); } else { // msg d'erreur // User canceled the picker } + setState(() {}); }, child: Text( "Ajouter", diff --git a/lib/view/activity/web/web_list_activity.dart b/lib/view/activity/web/web_list_activity.dart index b8a9f47..d5a333d 100644 --- a/lib/view/activity/web/web_list_activity.dart +++ b/lib/view/activity/web/web_list_activity.dart @@ -29,7 +29,7 @@ class _WebListActivityState extends State { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 20), Row( @@ -43,9 +43,13 @@ class _WebListActivityState extends State { fontWeight: FontWeight.w700), ), TextButton( - onPressed: () => _utile.getFiles( - Provider.of(context, listen: false).token, - context), + onPressed: () async { + await _utile.getFiles( + Provider.of(context, listen: false).token, + context, + infoManager); + setState(() {}); + }, child: Text("Get activity", style: TextStyle( color: TColor.gray, @@ -56,7 +60,7 @@ class _WebListActivityState extends State { FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null && result.files.isNotEmpty) { - _utile.addFileWeb( + await _utile.addFileWeb( result.files.first.bytes, Provider.of(context, listen: false).token, result.files.first.name, @@ -78,6 +82,10 @@ class _WebListActivityState extends State { ) ], ), + Visibility( + visible: infoManager.isVisible, + child: Text(infoManager.message, + style: TextStyle(color: infoManager.messageColor))), Provider.of(context, listen: true).listActivity.isEmpty ? Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/view/home/mobile/mobile_homeview.dart b/lib/view/home/mobile/mobile_homeview.dart index 9794461..5aba44d 100644 --- a/lib/view/home/mobile/mobile_homeview.dart +++ b/lib/view/home/mobile/mobile_homeview.dart @@ -47,7 +47,8 @@ class _MobileHomeView extends State { // -- Speed -- // double maxSpeed = managerSelectedActivity.getMaxSpeedAllActivitySelected(); double avgSpeed = managerSelectedActivity.getAvgSpeedAllActivitySelected(); - + data.maxSpeed = maxSpeed; + data.time = context.watch().managerSelectedActivity.getTimeAllActivitySelected(); return Scaffold( backgroundColor: TColor.white, body: SingleChildScrollView( diff --git a/lib/view/login/web/web_login_view.dart b/lib/view/login/web/web_login_view.dart index 3fc0d0e..e57fee1 100644 --- a/lib/view/login/web/web_login_view.dart +++ b/lib/view/login/web/web_login_view.dart @@ -172,6 +172,7 @@ class _WebLoginView extends State { util.fillUser(context, infoUser.item2, result.item2); localDB.userBox.put(User(0, infoUser.item2["username"], infoUser.item2["email"], result.item2)); + localDB.configBox.put(Config(0, false)); Navigator.push( context, MaterialPageRoute( diff --git a/lib/view/main_tab/mobile/mobile_main_tab_view.dart b/lib/view/main_tab/mobile/mobile_main_tab_view.dart index 7b0c1c5..5fbd638 100644 --- a/lib/view/main_tab/mobile/mobile_main_tab_view.dart +++ b/lib/view/main_tab/mobile/mobile_main_tab_view.dart @@ -92,8 +92,8 @@ class _MobileMainTabViewState extends State { width: 40, ), TabButton( - icon: "assets/img/Camera_tab.svg", - selectIcon: "assets/img/Camera_tab_select.svg", + icon: "assets/img/mapIcon.svg", + selectIcon: "assets/img/mapIcon_selected.svg", isActive: selectTab == 2, onTap: () { selectTab = 2; diff --git a/lib/view/main_tab/web/web_main_tab_view.dart b/lib/view/main_tab/web/web_main_tab_view.dart index 3a077d1..759558e 100644 --- a/lib/view/main_tab/web/web_main_tab_view.dart +++ b/lib/view/main_tab/web/web_main_tab_view.dart @@ -76,8 +76,8 @@ class _WebMainTabViewState extends State { ), ), sideBarButton( - icon: "assets/img/icon_map.svg", - selectIcon: "assets/img/icon_map.svg", + icon: "assets/img/mapIcon.svg", + selectIcon: "assets/img/mapIcon_selected.svg", index: 2, onTap: () => updateTab(2, const MyMap()), ), diff --git a/lib/view/map/chose_map.dart b/lib/view/map/chose_map.dart index 1ddd3bd..6788c11 100644 --- a/lib/view/map/chose_map.dart +++ b/lib/view/map/chose_map.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/common_widget/button/round_button.dart'; import 'package:smartfit_app_mobile/view/map/mobile/mobile_my_map.dart'; import 'package:smartfit_app_mobile/view/map/my_map_osm.dart'; @@ -13,6 +15,58 @@ class ChoseMap extends StatefulWidget { class _ChoseMap extends State { @override Widget build(BuildContext context) { + var media = MediaQuery.of(context).size; + return Scaffold( + backgroundColor: TColor.white, + body: SafeArea( + child: Center( + // Utilisation du widget Center pour centrer verticalement + child: Container( + width: media.width, + padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize + .min, // Utilisation de MainAxisSize.min pour que la colonne prenne la hauteur minimale nécessaire + children: [ + SizedBox( + height: media.height * 0.1, + ), + SvgPicture.asset( + "assets/img/group.svg", + width: media.width * 0.75, + height: media.height * 0.4, + fit: BoxFit.fitWidth, + ), + SizedBox( + height: media.height * 0.1, + ), + RoundButton( + title: "Use map with google map", + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const MobileMyMaps())); + }, + ), + SizedBox( + height: media.height * 0.03, + ), + RoundButton( + title : "Use map with Open Street Map", + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => const MyMapOSM())); + }, + ), + Spacer(), + ], + ), + ), + ), + ), + ); return Scaffold( backgroundColor: TColor.white, body: SafeArea( @@ -20,20 +74,7 @@ class _ChoseMap extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - TextButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const MobileMyMaps())); - }, - child: const Text("Use map with google map")), - TextButton( - onPressed: () { - Navigator.push(context, - MaterialPageRoute(builder: (context) => const MyMapOSM())); - }, - child: const Text("Use map with Open Street Map")), + const Text( "Mettre une image la en mode une personne avec des jumelles") ], diff --git a/lib/view/map/mobile/mobile_my_map.dart b/lib/view/map/mobile/mobile_my_map.dart index 4657afe..98171c3 100644 --- a/lib/view/map/mobile/mobile_my_map.dart +++ b/lib/view/map/mobile/mobile_my_map.dart @@ -25,7 +25,7 @@ class _MobileMyMaps extends State { return Scaffold( appBar: AppBar( title: const Text("Carte Google Map "), - backgroundColor: TColor.primaryColor1, + backgroundColor: TColor.secondaryColor1, ), body: _getMap()); } diff --git a/lib/view/map/my_map_osm.dart b/lib/view/map/my_map_osm.dart index 3d5a074..02cae4f 100644 --- a/lib/view/map/my_map_osm.dart +++ b/lib/view/map/my_map_osm.dart @@ -23,7 +23,7 @@ class _MyMapOSM extends State { return Scaffold( appBar: AppBar( title: const Text("Carte Open Street Map "), - backgroundColor: TColor.primaryColor1, + backgroundColor: TColor.secondaryColor1, ), body: FlutterMap( options: MapOptions(center: listPolynines.first), diff --git a/lib/view/profile/all_platforme/profile_view_allplatforme.dart b/lib/view/profile/all_platforme/profile_view_allplatforme.dart index 6f0df24..d6e6d6c 100644 --- a/lib/view/profile/all_platforme/profile_view_allplatforme.dart +++ b/lib/view/profile/all_platforme/profile_view_allplatforme.dart @@ -1,19 +1,19 @@ +import 'package:smartfit_app_mobile/common_widget/container/profile/profile_switch.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; import 'package:smartfit_app_mobile/common_widget/container/profile/profile_compte.dart'; import 'package:smartfit_app_mobile/common_widget/container/profile/profile_entete.dart'; import 'package:smartfit_app_mobile/common_widget/container/profile/profile_info_user.dart'; -import 'package:smartfit_app_mobile/common_widget/container/profile/profile_notification.dart'; import 'package:smartfit_app_mobile/common_widget/container/profile/profile_other.dart'; -import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; class ProfileViewAllPlatforme extends StatefulWidget { - final bool positive; + final bool offlineSave; final List accountArr; final List otherArr; - const ProfileViewAllPlatforme(this.positive, this.accountArr, this.otherArr, + const ProfileViewAllPlatforme( + this.offlineSave, this.accountArr, this.otherArr, {super.key}); @override @@ -36,26 +36,6 @@ class _ProfileViewAllPlatforme extends State { style: TextStyle( color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700), ), - actions: [ - InkWell( - onTap: () {}, - child: Container( - margin: const EdgeInsets.all(8), - height: 20, - width: 20, - alignment: Alignment.center, - decoration: BoxDecoration( - color: TColor.lightGray, - borderRadius: BorderRadius.circular(10)), - child: Image.asset( - "assets/img/more_btn.png", - width: 15, - height: 15, - fit: BoxFit.contain, - ), - ), - ) - ], ), backgroundColor: TColor.white, body: SingleChildScrollView( @@ -76,7 +56,10 @@ class _ProfileViewAllPlatforme extends State { const SizedBox( height: 25, ), - ProfileNotification(widget.positive), + // TODO: Download/Delete (local) all users files on toggle ? + // TODO: Display size of download in Mo + const ProfileSwitch( + "Offline mode", "Save your files locally", "local_save.png"), const SizedBox( height: 25, ), diff --git a/pubspec.yaml b/pubspec.yaml index 275c447..ed415a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: path: ^1.8.3 flutter_map: ^5.0.0 latlong2: ^0.9.0 + units_converter: ^2.1.1 dev_dependencies: flutter_test: