feat: redesign GPS workout UI with Runkeeper-style map overlay
All checks were successful
CI / update (push) Successful in 2m32s
All checks were successful
CI / update (push) Successful in 2m32s
- Full-screen fixed map with controls overlaid at the bottom - Activity type selector (running/walking/cycling/hiking) with proper exercise mapping for history display - GPS starts immediately on entering workout screen for faster lock - GPS track attached to cardio exercise (like GPX upload) so history shows distance, pace, splits, and map - Add activityType field to workout state, session model, and sync - Cancel button appears when workout is paused - GPS Workout button only shown in Tauri app
This commit is contained in:
@@ -18,6 +18,8 @@ export interface IActiveWorkout {
|
||||
userId: string;
|
||||
version: number;
|
||||
name: string;
|
||||
mode: 'manual' | 'gps';
|
||||
activityType: 'running' | 'walking' | 'cycling' | 'hiking' | null;
|
||||
templateId: string | null;
|
||||
exercises: IActiveWorkoutExercise[];
|
||||
paused: boolean;
|
||||
@@ -62,6 +64,16 @@ const ActiveWorkoutSchema = new mongoose.Schema(
|
||||
trim: true,
|
||||
maxlength: 100
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
enum: ['manual', 'gps'],
|
||||
default: 'manual'
|
||||
},
|
||||
activityType: {
|
||||
type: String,
|
||||
enum: ['running', 'walking', 'cycling', 'hiking'],
|
||||
default: null
|
||||
},
|
||||
templateId: {
|
||||
type: String,
|
||||
default: null
|
||||
|
||||
@@ -41,12 +41,16 @@ export interface IWorkoutSession {
|
||||
templateId?: string; // Reference to WorkoutTemplate if based on template
|
||||
templateName?: string; // Snapshot of template name for history
|
||||
name: string;
|
||||
mode?: 'manual' | 'gps';
|
||||
activityType?: 'running' | 'walking' | 'cycling' | 'hiking';
|
||||
exercises: ICompletedExercise[];
|
||||
startTime: Date;
|
||||
endTime?: Date;
|
||||
duration?: number; // Duration in minutes
|
||||
totalVolume?: number; // Total weight × reps across all exercises
|
||||
totalDistance?: number; // Total distance across all cardio exercises
|
||||
gpsTrack?: IGpsPoint[]; // Top-level GPS track for GPS-only workouts
|
||||
gpsPreview?: number[][]; // Downsampled [[lat,lng], ...] for card preview
|
||||
prs?: IPr[];
|
||||
notes?: string;
|
||||
createdBy: string; // username/nickname of the person who performed the workout
|
||||
@@ -155,15 +159,18 @@ const WorkoutSessionSchema = new mongoose.Schema(
|
||||
trim: true,
|
||||
maxlength: 100
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
enum: ['manual', 'gps'],
|
||||
default: 'manual'
|
||||
},
|
||||
activityType: {
|
||||
type: String,
|
||||
enum: ['running', 'walking', 'cycling', 'hiking']
|
||||
},
|
||||
exercises: {
|
||||
type: [CompletedExerciseSchema],
|
||||
required: true,
|
||||
validate: {
|
||||
validator: function(exercises: ICompletedExercise[]) {
|
||||
return exercises.length > 0;
|
||||
},
|
||||
message: 'A workout session must have at least one exercise'
|
||||
}
|
||||
default: []
|
||||
},
|
||||
startTime: {
|
||||
type: Date,
|
||||
@@ -185,6 +192,14 @@ const WorkoutSessionSchema = new mongoose.Schema(
|
||||
type: Number,
|
||||
min: 0
|
||||
},
|
||||
gpsTrack: {
|
||||
type: [GpsPointSchema],
|
||||
default: undefined
|
||||
},
|
||||
gpsPreview: {
|
||||
type: [[Number]],
|
||||
default: undefined
|
||||
},
|
||||
prs: [{
|
||||
exerciseId: { type: String, required: true },
|
||||
type: { type: String, required: true },
|
||||
|
||||
Reference in New Issue
Block a user