Léo Salé пре 3 година
родитељ
комит
cc3495d538

+ 35 - 0
app/lib/challenge/daily_challenge_page.dart

@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/challenge/services/challenge_service.dart';
+import 'package:physigo/challenge/widgets/challenge_exercises.dart';
+
+class DailyChallengePage extends StatelessWidget {
+  const DailyChallengePage({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      body: Padding(
+        padding: const EdgeInsets.all(16.0),
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: const [
+            Center(
+              child: Text(
+                "Daily Challenge",
+                textAlign: TextAlign.center,
+                style: TextStyle(
+                  fontSize: 28,
+                  fontWeight: FontWeight.bold,
+                  letterSpacing: 1.8,
+                  // color: Colors.white,
+                ),
+              ),
+            ),
+            SizedBox(height: 48),
+            ChallengeExercises(getChallenge: ChallengeService.getDailyChallenge),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 52 - 0
app/lib/challenge/models/challenge.dart

@@ -0,0 +1,52 @@
+class Challenge {
+  final List<Exercise> exercises;
+  final int bonusExercise;
+
+  const Challenge({
+    required this.exercises,
+    required this.bonusExercise,
+  });
+
+  factory Challenge.fromMap(Map<String, dynamic> data) {
+    return Challenge(
+      exercises: _getExercise(data["list_exercises"]),
+      bonusExercise: data["bonus_exercise"],
+    );
+  }
+
+  static List<Exercise> _getExercise(List<Map<String, dynamic>> exercises) {
+    return exercises.map((exercise) => Exercise.fromMap(exercise)).toList();
+  }
+}
+
+class Exercise {
+  final String description;
+  final Difficulty difficulty;
+  final String name;
+  final int repetitions;
+  final int sets;
+
+  const Exercise({
+    required this.description,
+    required this.difficulty,
+    required this.name,
+    required this.repetitions,
+    required this.sets,
+  });
+
+  factory Exercise.fromMap(Map<String, dynamic> data) {
+    return Exercise(
+      description: data["description"],
+      difficulty: Difficulty.values[data["difficulty"]],
+      name: data["name"],
+      repetitions: data["repetitions"],
+      sets: data["sets"],
+    );
+  }
+}
+
+enum Difficulty {
+  easy,
+  normal,
+  hard,
+}

+ 31 - 0
app/lib/challenge/services/challenge_service.dart

@@ -0,0 +1,31 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+import '../models/challenge.dart';
+
+class ChallengeService {
+  static final _db = FirebaseFirestore.instance;
+  static final _dailyChallenges = _db.collection("DailyChallenges");
+  static final _weeklyChallenges = _db.collection("WeeklyChallenges");
+
+  static Future<Challenge> getDailyChallenge() async {
+    final challenges = await _dailyChallenges.orderBy("date", descending: true).limit(1).get();
+    final challenge = challenges.docs.first.data();
+    challenge["list_exercises"] = await _getExercisesFromChallenge(challenge);
+    return Challenge.fromMap(challenge);
+  }
+
+  static Future<Challenge> getWeeklyChallenge() async {
+    final challenges = await _weeklyChallenges.orderBy("date", descending: true).limit(1).get();
+    final challenge = challenges.docs.first.data();
+    challenge["list_exercises"] = await _getExercisesFromChallenge(challenge);
+    return Challenge.fromMap(challenge);
+  }
+
+  static Future<List<Map<String, dynamic>>> _getExercisesFromChallenge(Map<String, dynamic> challenge) async {
+    return await Future.wait(
+        List<DocumentReference<Map<String, dynamic>>>.from(challenge["list_exercises"]).map((exerciseRef) async {
+      final exercise = await exerciseRef.get();
+      return exercise.data()!;
+    }));
+  }
+}

+ 78 - 0
app/lib/challenge/weekly_challenge_page.dart

@@ -0,0 +1,78 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
+import 'package:physigo/challenge/services/challenge_service.dart';
+import 'package:physigo/challenge/widgets/challenge_exercises.dart';
+import 'package:physigo/navigation/navigation_page.dart';
+
+class WeeklyChallengePage extends StatelessWidget {
+  const WeeklyChallengePage({Key? key}) : super(key: key);
+  Future<dynamic> get weeklyPlaceFuture => AuthenticationServices.user!.weeklyPlace.get();
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<dynamic>(
+        future: weeklyPlaceFuture,
+        builder: (context, snapshot) {
+          if (snapshot.connectionState == ConnectionState.waiting) {
+            return const Center(child: CircularProgressIndicator());
+          }
+          final weeklyPlace = snapshot.data.data();
+          final weeklyPlaceName = weeklyPlace["name"];
+          final weeklyPlaceLocation = weeklyPlace["location"]["geopoint"];
+          return Scaffold(
+            body: Padding(
+              padding: const EdgeInsets.all(16.0),
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  const Center(
+                    child: Text(
+                      "Weekly Challenge",
+                      textAlign: TextAlign.center,
+                      style: TextStyle(
+                        fontSize: 28,
+                        fontWeight: FontWeight.bold,
+                        letterSpacing: 1.8,
+                        // color: Colors.white,
+                      ),
+                    ),
+                  ),
+                  const SizedBox(height: 12),
+                  Center(
+                    child: Text(
+                      "Location: $weeklyPlaceName",
+                      textAlign: TextAlign.center,
+                      maxLines: 2,
+                      overflow: TextOverflow.ellipsis,
+                      style: const TextStyle(fontSize: 18),
+                    ),
+                  ),
+                  const SizedBox(height: 48),
+                  const ChallengeExercises(getChallenge: ChallengeService.getWeeklyChallenge),
+                  const SizedBox(height: 48),
+                  ElevatedButton(
+                    style: ElevatedButton.styleFrom(primary: Colors.teal),
+                    onPressed: () => Navigator.push(
+                      context,
+                      MaterialPageRoute(
+                        builder: (_) => NavigationPage(destination: weeklyPlaceLocation),
+                      ),
+                    ),
+                    child: Padding(
+                      padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
+                      child: Row(
+                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                        children: const [
+                          Flexible(child: Text("Show me the way", style: TextStyle(fontSize: 24), maxLines: 2)),
+                          Icon(Icons.map, size: 32),
+                        ],
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          );
+        });
+  }
+}

+ 66 - 0
app/lib/challenge/widgets/challenge_exercises.dart

@@ -0,0 +1,66 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/challenge/models/challenge.dart';
+import 'package:physigo/exercises/exercises_validation/exercise_validation_page.dart';
+import 'package:physigo/exercises/exercises_validation/models/squat.dart';
+
+class ChallengeExercises extends StatelessWidget {
+  final Future<Challenge> Function() getChallenge;
+  const ChallengeExercises({required this.getChallenge, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<Challenge>(
+      future: getChallenge(),
+      builder: (context, snapshot) {
+        if (snapshot.connectionState == ConnectionState.waiting) {
+          return const CircularProgressIndicator();
+        }
+        final challenge = snapshot.data!;
+        return Column(
+          children: challenge.exercises
+              .asMap()
+              .entries
+              .map((e) => ExerciseTile(
+                    exercise: e.value,
+                    isBonusExercise: e.key == challenge.bonusExercise,
+                  ))
+              .toList(),
+        );
+      },
+    );
+  }
+}
+
+class ExerciseTile extends StatelessWidget {
+  final Exercise exercise;
+  final bool isBonusExercise;
+  const ExerciseTile({required this.exercise, required this.isBonusExercise, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+      elevation: 3,
+      color: isBonusExercise ? Colors.orange : Colors.blueGrey,
+      child: ListTile(
+        iconColor: Colors.white,
+        textColor: Colors.white,
+        title: Text(
+          exercise.name.toUpperCase(),
+          textAlign: TextAlign.center,
+          style: const TextStyle(
+            fontSize: 18,
+            fontWeight: FontWeight.bold,
+            letterSpacing: 1.8,
+          ),
+        ),
+        minLeadingWidth: 24,
+        leading: isBonusExercise ? const Icon(Icons.star, size: 24) : const SizedBox(width: 24),
+        subtitle: isBonusExercise ? const Text("Bonus exercise, earn 2x points!", textAlign: TextAlign.center, style: TextStyle(fontSize: 16),) : null,
+        trailing: const Icon(Icons.arrow_forward),
+        onTap: () {
+          Navigator.push(context, MaterialPageRoute(builder: (_) => ExerciseValidationPage(exercise: squat)));
+        },
+      ),
+    );
+  }
+}

+ 90 - 0
app/lib/home/home_page.dart

@@ -0,0 +1,90 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
+import 'package:physigo/challenge/daily_challenge_page.dart';
+import 'package:physigo/challenge/weekly_challenge_page.dart';
+
+class HomePage extends StatelessWidget {
+  const HomePage({Key? key}) : super(key: key);
+
+  String get name => AuthenticationServices.user!.name;
+  num get totalPoint => AuthenticationServices.user!.totalPoints;
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.all(16.0),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Text(
+            "Welcome back $name!",
+            textAlign: TextAlign.center,
+            style: const TextStyle(
+              fontSize: 26,
+              fontWeight: FontWeight.w600,
+            ),
+          ),
+          const SizedBox(height: 36),
+          Column(
+            children: const [
+              Align(
+                alignment: Alignment.bottomLeft,
+                child: Text(
+                  "Here are your challenges:",
+                  style: TextStyle(fontSize: 24),
+                ),
+              ),
+              SizedBox(height: 24),
+              ChallengeTile(
+                challengeType: "Daily",
+                challengePage: DailyChallengePage(),
+              ),
+              ChallengeTile(
+                challengeType: "Weekly",
+                challengePage: WeeklyChallengePage(),
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class ChallengeTile extends StatelessWidget {
+  final Widget challengePage;
+  const ChallengeTile({
+    required this.challengePage,
+    required this.challengeType,
+    Key? key,
+  }) : super(key: key);
+
+  final String challengeType;
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      width: MediaQuery.of(context).size.width * 0.6,
+      child: Card(
+        elevation: 3,
+        color: Colors.blueGrey,
+        child: ListTile(
+          title: Text(
+            challengeType.toUpperCase(),
+            textAlign: TextAlign.center,
+            style: const TextStyle(
+              fontSize: 18,
+              fontWeight: FontWeight.bold,
+              letterSpacing: 1.8,
+              color: Colors.white,
+            ),
+          ),
+          trailing: const Icon(Icons.arrow_forward, color: Colors.white),
+          onTap: () {
+            Navigator.push(context, MaterialPageRoute(builder: (_) => challengePage));
+          },
+        ),
+      ),
+    );
+  }
+}

+ 3 - 2
app/lib/navigation/navigation_page.dart

@@ -1,10 +1,11 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:latlong2/latlong.dart';
 import 'package:latlong2/latlong.dart';
 import 'package:physigo/navigation/utils/permissions_utils.dart';
 import 'package:physigo/navigation/utils/permissions_utils.dart';
 import 'package:physigo/navigation/widgets/navigation.dart';
 import 'package:physigo/navigation/widgets/navigation.dart';
 
 
 class NavigationPage extends StatelessWidget {
 class NavigationPage extends StatelessWidget {
-  final LatLng destination;
+  final GeoPoint destination;
 
 
   const NavigationPage({required this.destination, Key? key}) : super(key: key);
   const NavigationPage({required this.destination, Key? key}) : super(key: key);
 
 
@@ -22,7 +23,7 @@ class NavigationPage extends StatelessWidget {
             return Text(snapshot.error.toString());
             return Text(snapshot.error.toString());
           }
           }
 
 
-          return Navigation(destination: destination);
+          return Navigation(destination: LatLng(destination.latitude, destination.longitude));
         },
         },
       ),
       ),
     );
     );