hevy-shared 1.0.962 → 1.0.964
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/built/API/APIClient.d.ts +157 -0
- package/built/API/APIClient.js +381 -0
- package/built/API/index.d.ts +2 -0
- package/built/API/index.js +18 -0
- package/built/API/types.d.ts +38 -0
- package/built/API/types.js +18 -0
- package/built/adjustEventTokens.d.ts +16 -0
- package/built/adjustEventTokens.js +18 -0
- package/built/adminPermissions.d.ts +4 -0
- package/built/adminPermissions.js +22 -0
- package/built/async.d.ts +50 -0
- package/built/async.js +170 -0
- package/built/chat.d.ts +25 -23
- package/built/coachPlans.d.ts +2 -1
- package/built/coachPlans.js +2 -2
- package/built/cue.d.ts +12 -0
- package/built/cue.js +22 -0
- package/built/exerciseLocaleUtils.d.ts +17 -0
- package/built/exerciseLocaleUtils.js +62 -0
- package/built/filterExercises.d.ts +19 -3
- package/built/filterExercises.js +72 -60
- package/built/hevyTrainer.d.ts +250 -0
- package/built/hevyTrainer.js +676 -0
- package/built/index.d.ts +1217 -304
- package/built/index.js +268 -75
- package/built/muscleHeatmaps.d.ts +31 -0
- package/built/muscleHeatmaps.js +68 -0
- package/built/muscleSplits.d.ts +36 -0
- package/built/muscleSplits.js +100 -0
- package/built/normalizedWorkoutUtils.d.ts +88 -0
- package/built/normalizedWorkoutUtils.js +112 -0
- package/built/notifications.d.ts +215 -0
- package/built/notifications.js +9 -0
- package/built/routineUtils.d.ts +14 -0
- package/built/routineUtils.js +186 -0
- package/built/setIndicatorUtils.d.ts +4 -3
- package/built/setIndicatorUtils.js +15 -1
- package/built/tests/async.test.d.ts +1 -0
- package/built/tests/async.test.js +49 -0
- package/built/tests/hevyTrainer.test.d.ts +1 -0
- package/built/tests/hevyTrainer.test.js +1199 -0
- package/built/tests/muscleSplit.test.d.ts +1 -0
- package/built/tests/muscleSplit.test.js +153 -0
- package/built/tests/routineUtils.test.d.ts +1 -0
- package/built/tests/routineUtils.test.js +745 -0
- package/built/tests/testUtils.d.ts +85 -0
- package/built/tests/testUtils.js +319 -0
- package/built/tests/utils.test.js +748 -0
- package/built/tests/workoutVolume.test.js +165 -49
- package/built/translations/index.d.ts +2 -0
- package/built/translations/index.js +18 -0
- package/built/translations/translationUtils.d.ts +2 -0
- package/built/translations/translationUtils.js +61 -0
- package/built/translations/types.d.ts +8 -0
- package/built/translations/types.js +20 -0
- package/built/typeUtils.d.ts +70 -0
- package/built/typeUtils.js +55 -0
- package/built/units.d.ts +14 -7
- package/built/units.js +24 -14
- package/built/utils.d.ts +192 -5
- package/built/utils.js +598 -85
- package/built/websocket.d.ts +14 -2
- package/built/workoutVolume.d.ts +24 -5
- package/built/workoutVolume.js +25 -34
- package/package.json +30 -9
- package/.eslintignore +0 -2
- package/.eslintrc +0 -21
- package/.github/workflows/ci.yml +0 -15
- package/.github/workflows/npm-publish.yml +0 -59
- package/.github/workflows/pr-auto-assign.yml +0 -15
- package/.prettierrc.js +0 -5
- package/jest.config.js +0 -4
- package/src/chat.ts +0 -130
- package/src/coachPlans.ts +0 -57
- package/src/constants.ts +0 -14
- package/src/filterExercises.ts +0 -222
- package/src/index.ts +0 -1576
- package/src/setIndicatorUtils.ts +0 -137
- package/src/tests/utils.test.ts +0 -156
- package/src/tests/workoutVolume.test.ts +0 -93
- package/src/units.ts +0 -41
- package/src/utils.ts +0 -516
- package/src/websocket.ts +0 -36
- package/src/workoutVolume.ts +0 -175
- package/tsconfig.json +0 -70
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ExerciseType, RPE, SetType, ShareToPlatform, WorkoutBiometrics, WorkoutMedia, WorkoutVisibility } from '.';
|
|
2
|
+
/**
|
|
3
|
+
* Events are used to determine the start time, end time and duration of a
|
|
4
|
+
* `NormalizedWorkout`, in a way that can be persisted to disk.
|
|
5
|
+
*/
|
|
6
|
+
export interface NormalizedWorkoutEvent {
|
|
7
|
+
type: 'resume' | 'pause';
|
|
8
|
+
unixTimestamp: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* NormalizedWorkout describes the state of a workout. It's used when
|
|
12
|
+
* persisting an ongoing workout to disk, or calculating shareables after
|
|
13
|
+
* completing the workout.
|
|
14
|
+
*
|
|
15
|
+
* The duration of a `NormalizedWorkout` is _derived_ from the `timeEvents`
|
|
16
|
+
* array. There is no single "duration" variable, because that would require
|
|
17
|
+
* pausing to be a complex stateful operation.
|
|
18
|
+
*/
|
|
19
|
+
export interface NormalizedWorkout {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
media: WorkoutMedia[];
|
|
23
|
+
startTime: number;
|
|
24
|
+
timeEvents: NormalizedWorkoutEvent[];
|
|
25
|
+
exercises: NormalizedExercise[];
|
|
26
|
+
routineId?: string;
|
|
27
|
+
trackWorkoutAsRoutine: boolean;
|
|
28
|
+
updateRoutineFromWorkout?: boolean;
|
|
29
|
+
appleWatch: boolean;
|
|
30
|
+
wearosWatch: boolean;
|
|
31
|
+
workoutVisibility: WorkoutVisibility;
|
|
32
|
+
isWorkoutBiometricsPublic: boolean;
|
|
33
|
+
shareTo: {
|
|
34
|
+
[key in ShareToPlatform]: boolean;
|
|
35
|
+
};
|
|
36
|
+
clientId: string;
|
|
37
|
+
biometrics?: WorkoutBiometrics;
|
|
38
|
+
trainerProgramId: string | undefined;
|
|
39
|
+
}
|
|
40
|
+
export interface NormalizedSet {
|
|
41
|
+
index: number;
|
|
42
|
+
indicator: SetType;
|
|
43
|
+
weight?: number;
|
|
44
|
+
reps?: number;
|
|
45
|
+
distance?: number;
|
|
46
|
+
duration?: number;
|
|
47
|
+
customMetric?: number;
|
|
48
|
+
rpe?: RPE;
|
|
49
|
+
targetRpe?: RPE;
|
|
50
|
+
completed: boolean;
|
|
51
|
+
completedAt?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface NormalizedExercise {
|
|
54
|
+
title: string;
|
|
55
|
+
exerciseTemplateId: string;
|
|
56
|
+
supersetId?: number;
|
|
57
|
+
autoRestTimerSeconds?: number;
|
|
58
|
+
routineNotes: string;
|
|
59
|
+
notes: string;
|
|
60
|
+
volumeDoublingEnabled: boolean;
|
|
61
|
+
sets: NormalizedSet[];
|
|
62
|
+
}
|
|
63
|
+
export declare const calculateDuration: (normalizedWorkout: Pick<NormalizedWorkout, "startTime" | "timeEvents">, nowUnix: number) => number;
|
|
64
|
+
export declare const calculateEndTime: (normalizedWorkout: Pick<NormalizedWorkout, "startTime" | "timeEvents">, nowUnix: number) => number;
|
|
65
|
+
interface GenerateWorkoutTimeEventsArgs {
|
|
66
|
+
startTimeUnix: number;
|
|
67
|
+
durationSeconds: number;
|
|
68
|
+
nowUnix: number;
|
|
69
|
+
/**
|
|
70
|
+
* if this is `true`, generate a pause event at the current timestamp
|
|
71
|
+
*/
|
|
72
|
+
isPaused?: boolean;
|
|
73
|
+
}
|
|
74
|
+
export declare const generateWorkoutTimeEvents: ({ startTimeUnix, durationSeconds, nowUnix, isPaused, }: GenerateWorkoutTimeEventsArgs) => NormalizedWorkoutEvent[];
|
|
75
|
+
export declare const isWorkoutCurrentlyPaused: (normalizedWorkout: NormalizedWorkout) => boolean;
|
|
76
|
+
/**
|
|
77
|
+
* When logging a workout, we can't just use the `completed` state of a set to
|
|
78
|
+
* determine if the user meant for the set to be included in the workout. This
|
|
79
|
+
* is because a lot of users never mark sets as completed, and forcing them to
|
|
80
|
+
* do that wouldn't add anything to the app. For that reason, we count a set as
|
|
81
|
+
* completed if it contains inputted valid values based on its exercise type.
|
|
82
|
+
* This function filters out all sets in a `NormalizedWorkout` that are
|
|
83
|
+
* incomplete based on the values inputted.
|
|
84
|
+
*/
|
|
85
|
+
export declare const trimWorkout: (workout: NormalizedWorkout, exerciseTypeMap: {
|
|
86
|
+
[key: string]: ExerciseType;
|
|
87
|
+
}) => NormalizedWorkout;
|
|
88
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.trimWorkout = exports.isWorkoutCurrentlyPaused = exports.generateWorkoutTimeEvents = exports.calculateEndTime = exports.calculateDuration = void 0;
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const calculateDuration = (normalizedWorkout, nowUnix) => {
|
|
6
|
+
const { startTime, timeEvents } = normalizedWorkout;
|
|
7
|
+
if (!timeEvents.length)
|
|
8
|
+
return nowUnix - startTime;
|
|
9
|
+
const timeBeforeFirstEvent = timeEvents[0].unixTimestamp - startTime;
|
|
10
|
+
const duration = timeEvents.reduce((prev, curr, index, array) => {
|
|
11
|
+
var _a;
|
|
12
|
+
const nextEvent = (_a = array[index + 1]) !== null && _a !== void 0 ? _a : {
|
|
13
|
+
type: 'resume',
|
|
14
|
+
unixTimestamp: nowUnix,
|
|
15
|
+
};
|
|
16
|
+
if (curr.type === 'pause') {
|
|
17
|
+
return prev;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return prev + nextEvent.unixTimestamp - curr.unixTimestamp;
|
|
21
|
+
}
|
|
22
|
+
}, timeBeforeFirstEvent);
|
|
23
|
+
return duration;
|
|
24
|
+
};
|
|
25
|
+
exports.calculateDuration = calculateDuration;
|
|
26
|
+
const calculateEndTime = (normalizedWorkout, nowUnix) => {
|
|
27
|
+
const { startTime } = normalizedWorkout;
|
|
28
|
+
const durationSeconds = (0, exports.calculateDuration)(normalizedWorkout, nowUnix);
|
|
29
|
+
return startTime + durationSeconds;
|
|
30
|
+
};
|
|
31
|
+
exports.calculateEndTime = calculateEndTime;
|
|
32
|
+
const generateWorkoutTimeEvents = ({ startTimeUnix, durationSeconds, nowUnix, isPaused = false, }) => {
|
|
33
|
+
const maxPossibleDuration = nowUnix - startTimeUnix;
|
|
34
|
+
const safeRequestedDuration = (0, _1.clampNumber)(durationSeconds, {
|
|
35
|
+
min: 0,
|
|
36
|
+
max: maxPossibleDuration,
|
|
37
|
+
});
|
|
38
|
+
const pausePaddingSeconds = maxPossibleDuration - safeRequestedDuration;
|
|
39
|
+
const timeEvents = [];
|
|
40
|
+
if (pausePaddingSeconds > 0) {
|
|
41
|
+
/**
|
|
42
|
+
* The duration is shorter than (endTime - startTime). Add a pause event at
|
|
43
|
+
* the start of the workout for padding, and a resume event at the correct
|
|
44
|
+
* time to make the duration match.
|
|
45
|
+
*/
|
|
46
|
+
timeEvents.push(...[
|
|
47
|
+
{ type: 'pause', unixTimestamp: startTimeUnix },
|
|
48
|
+
{ type: 'resume', unixTimestamp: startTimeUnix + pausePaddingSeconds },
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
if (isPaused) {
|
|
52
|
+
timeEvents.push({
|
|
53
|
+
type: 'pause',
|
|
54
|
+
unixTimestamp: startTimeUnix + maxPossibleDuration,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return timeEvents;
|
|
58
|
+
};
|
|
59
|
+
exports.generateWorkoutTimeEvents = generateWorkoutTimeEvents;
|
|
60
|
+
const isWorkoutCurrentlyPaused = (normalizedWorkout) => {
|
|
61
|
+
var _a;
|
|
62
|
+
return ((_a = normalizedWorkout.timeEvents.slice(-1)[0]) === null || _a === void 0 ? void 0 : _a.type) === 'pause';
|
|
63
|
+
};
|
|
64
|
+
exports.isWorkoutCurrentlyPaused = isWorkoutCurrentlyPaused;
|
|
65
|
+
/**
|
|
66
|
+
* When logging a workout, we can't just use the `completed` state of a set to
|
|
67
|
+
* determine if the user meant for the set to be included in the workout. This
|
|
68
|
+
* is because a lot of users never mark sets as completed, and forcing them to
|
|
69
|
+
* do that wouldn't add anything to the app. For that reason, we count a set as
|
|
70
|
+
* completed if it contains inputted valid values based on its exercise type.
|
|
71
|
+
* This function filters out all sets in a `NormalizedWorkout` that are
|
|
72
|
+
* incomplete based on the values inputted.
|
|
73
|
+
*/
|
|
74
|
+
const trimWorkout = (workout, exerciseTypeMap) => {
|
|
75
|
+
// Filter out empty sets.
|
|
76
|
+
const filteredSetsWorkout = Object.assign(Object.assign({}, workout), { exercises: workout.exercises.map((e) => {
|
|
77
|
+
return Object.assign(Object.assign({}, e), { sets: e.sets.filter((s) => {
|
|
78
|
+
const exerciseType = exerciseTypeMap[e.exerciseTemplateId];
|
|
79
|
+
if (!exerciseType) {
|
|
80
|
+
/**
|
|
81
|
+
* if we don't know the exercise type, we can't tell if the set is
|
|
82
|
+
* empty or not, so we'll just assume that it's not.
|
|
83
|
+
*/
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return !isEmptySet(s, exerciseType);
|
|
87
|
+
}) });
|
|
88
|
+
}) });
|
|
89
|
+
// Filter out exercises that contain 0 sets.
|
|
90
|
+
const filteredWorkout = Object.assign(Object.assign({}, filteredSetsWorkout), { exercises: filteredSetsWorkout.exercises.filter((e) => !!e.sets.length) });
|
|
91
|
+
return filteredWorkout;
|
|
92
|
+
};
|
|
93
|
+
exports.trimWorkout = trimWorkout;
|
|
94
|
+
const isEmptySet = (set, exerciseType) => {
|
|
95
|
+
switch (exerciseType) {
|
|
96
|
+
case 'weight_reps':
|
|
97
|
+
case 'bodyweight_reps':
|
|
98
|
+
case 'bodyweight_assisted_reps':
|
|
99
|
+
case 'reps_only':
|
|
100
|
+
return !set.reps && !set.weight;
|
|
101
|
+
case 'distance_duration':
|
|
102
|
+
case 'duration':
|
|
103
|
+
return !set.duration && !set.distance;
|
|
104
|
+
case 'short_distance_weight':
|
|
105
|
+
return !set.distance && !set.weight;
|
|
106
|
+
case 'weight_duration':
|
|
107
|
+
return !set.weight && !set.duration;
|
|
108
|
+
case 'floors_duration':
|
|
109
|
+
case 'steps_duration':
|
|
110
|
+
return !set.duration;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifications and Mobile pushes
|
|
3
|
+
*
|
|
4
|
+
* Notifications are types used for the in-app notification feed
|
|
5
|
+
*
|
|
6
|
+
* Mobile pushes are payloads delivered to mobile apps
|
|
7
|
+
*/
|
|
8
|
+
import { ClientsCoachData, Program } from '.';
|
|
9
|
+
import { HevyChatUpdateMobilePushData } from './chat';
|
|
10
|
+
export type Notification = WorkoutCompleteNotification | WorkoutMentionNotification | WorkoutCommentNotification | WorkoutCommentMentionNotification | WorkoutCommentReplyNotification | WorkoutCommentDiscussionNotification | WorkoutLikeNotification | WorkoutCommentLikeNotification | FollowNotification | FollowRequestNotification | AcceptFollowRequestNotification | CoachClientInviteNotification | ContactOnHevyNotification;
|
|
11
|
+
export type NotificationType = 'workout-complete' | 'workout-mention' | 'workout-comment' | 'workout-comment-mention' | 'workout-comment-reply' | 'workout-comment-reply-v2' | 'workout-like' | 'workout-comment-like' | 'follow' | 'follow-request' | 'accept-follow-request' | 'coach-client-invite' | 'contact-on-hevy';
|
|
12
|
+
export interface BaseNotification {
|
|
13
|
+
id: number;
|
|
14
|
+
type: NotificationType;
|
|
15
|
+
from_username: string;
|
|
16
|
+
profile_pic?: string;
|
|
17
|
+
date: string;
|
|
18
|
+
}
|
|
19
|
+
export interface BaseMobilePushData {
|
|
20
|
+
type: MobilePushDataType;
|
|
21
|
+
deeplink?: string;
|
|
22
|
+
profilePic?: string;
|
|
23
|
+
cue_notification_group_identifier?: string;
|
|
24
|
+
additionalHevyData?: any;
|
|
25
|
+
}
|
|
26
|
+
export interface WorkoutCompleteNotification extends BaseNotification {
|
|
27
|
+
type: 'workout-complete';
|
|
28
|
+
workout_id: string;
|
|
29
|
+
workout_title: string;
|
|
30
|
+
}
|
|
31
|
+
export interface WorkoutCompleteMobilePushData extends BaseMobilePushData {
|
|
32
|
+
type: 'workout-complete';
|
|
33
|
+
}
|
|
34
|
+
export interface WorkoutMentionNotification extends BaseNotification {
|
|
35
|
+
type: 'workout-mention';
|
|
36
|
+
workout_id: string;
|
|
37
|
+
workout_title: string;
|
|
38
|
+
}
|
|
39
|
+
export interface WorkoutMentionMobilePushData extends BaseMobilePushData {
|
|
40
|
+
type: 'workout-mention';
|
|
41
|
+
}
|
|
42
|
+
export interface WorkoutCommentNotification extends BaseNotification {
|
|
43
|
+
type: 'workout-comment';
|
|
44
|
+
workout_id: string;
|
|
45
|
+
workout_title: string;
|
|
46
|
+
comment: string;
|
|
47
|
+
}
|
|
48
|
+
export interface WorkoutCommentMobilePushData extends BaseMobilePushData {
|
|
49
|
+
type: 'workout-comment';
|
|
50
|
+
}
|
|
51
|
+
export interface WorkoutCommentDiscussionNotification extends BaseNotification {
|
|
52
|
+
type: 'workout-comment-reply';
|
|
53
|
+
workout_id: string;
|
|
54
|
+
workout_title: string;
|
|
55
|
+
comment: string;
|
|
56
|
+
}
|
|
57
|
+
export interface WorkoutCommentDiscussionMobilePushData extends BaseMobilePushData {
|
|
58
|
+
type: 'workout-comment-reply';
|
|
59
|
+
}
|
|
60
|
+
export interface WorkoutCommentMentionNotification extends BaseNotification {
|
|
61
|
+
type: 'workout-comment-mention';
|
|
62
|
+
workout_id: string;
|
|
63
|
+
workout_title: string;
|
|
64
|
+
comment: string;
|
|
65
|
+
}
|
|
66
|
+
export interface WorkoutCommentMentionMobilePushData extends BaseMobilePushData {
|
|
67
|
+
type: 'workout-comment-mention';
|
|
68
|
+
}
|
|
69
|
+
export interface WorkoutCommentReplyNotification extends BaseNotification {
|
|
70
|
+
type: 'workout-comment-reply-v2';
|
|
71
|
+
workout_id: string;
|
|
72
|
+
workout_title: string;
|
|
73
|
+
comment: string;
|
|
74
|
+
}
|
|
75
|
+
export interface WorkoutCommentReplyMobilePushData extends BaseMobilePushData {
|
|
76
|
+
type: 'workout-comment-reply-v2';
|
|
77
|
+
}
|
|
78
|
+
export interface WorkoutLikeNotification extends BaseNotification {
|
|
79
|
+
type: 'workout-like';
|
|
80
|
+
workout_id: string;
|
|
81
|
+
workout_title: string;
|
|
82
|
+
}
|
|
83
|
+
export interface WorkoutLikeMobilePushData extends BaseMobilePushData {
|
|
84
|
+
type: 'workout-like';
|
|
85
|
+
}
|
|
86
|
+
export interface WorkoutCommentLikeNotification extends BaseNotification {
|
|
87
|
+
type: 'workout-comment-like';
|
|
88
|
+
workout_id: string;
|
|
89
|
+
workout_title: string;
|
|
90
|
+
comment_id: number;
|
|
91
|
+
comment: string;
|
|
92
|
+
}
|
|
93
|
+
export interface WorkoutCommentLikeMobilePushData extends BaseMobilePushData {
|
|
94
|
+
type: 'workout-comment-like';
|
|
95
|
+
}
|
|
96
|
+
export interface FollowNotification extends BaseNotification {
|
|
97
|
+
type: 'follow';
|
|
98
|
+
}
|
|
99
|
+
export interface FollowMobilePushData extends BaseMobilePushData {
|
|
100
|
+
type: 'follow';
|
|
101
|
+
}
|
|
102
|
+
export interface FollowRequestNotification extends BaseNotification {
|
|
103
|
+
type: 'follow-request';
|
|
104
|
+
}
|
|
105
|
+
export interface FollowRequestMobilePushData extends BaseMobilePushData {
|
|
106
|
+
type: 'follow-request';
|
|
107
|
+
}
|
|
108
|
+
export interface AcceptFollowRequestNotification extends BaseNotification {
|
|
109
|
+
type: 'accept-follow-request';
|
|
110
|
+
}
|
|
111
|
+
export interface AcceptFollowRequestMobilePushData extends BaseMobilePushData {
|
|
112
|
+
type: 'accept-follow-request';
|
|
113
|
+
}
|
|
114
|
+
export interface CoachClientInviteNotification extends BaseNotification {
|
|
115
|
+
type: 'coach-client-invite';
|
|
116
|
+
from_full_name?: string;
|
|
117
|
+
}
|
|
118
|
+
export interface CoachClientInviteMobilePushData extends BaseMobilePushData {
|
|
119
|
+
type: 'coach-client-invite';
|
|
120
|
+
}
|
|
121
|
+
export interface ContactOnHevyNotification extends BaseNotification {
|
|
122
|
+
type: 'contact-on-hevy';
|
|
123
|
+
contact_name: string;
|
|
124
|
+
}
|
|
125
|
+
export interface ContactOnHevyMobilePushData extends BaseMobilePushData {
|
|
126
|
+
type: 'contact-on-hevy';
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* "Pushes": a push is basically a remote notification, NOT to be confused with a "notification", which is
|
|
130
|
+
* a data type in the Hevy database
|
|
131
|
+
*/
|
|
132
|
+
export type MobilePushDataType = NotificationMobilePushDataType | DataOnlyMobilePushDataType;
|
|
133
|
+
/**
|
|
134
|
+
* These types will have an alert/title, and will only be delivered if the user has given the notification
|
|
135
|
+
* permission to the App
|
|
136
|
+
*/
|
|
137
|
+
export type NotificationMobilePushDataType = NotificationType | 'chat-update' | 'coach-program-finishes-next-week' | 'coach-program-starts-today' | 'coach-logged-your-workout' | 'coach-logged-your-measurements' | 'monthly-report' | 'grace-period-enter' | 'grace-period-resolve';
|
|
138
|
+
/**
|
|
139
|
+
* These types CAN NOT have an alert/title, but may be delivered even if the user has not given the notification
|
|
140
|
+
* permission and even if Hevy isn't running, although these should be treated as best effort
|
|
141
|
+
* For iOS/Apple see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
|
|
142
|
+
* For Android see https://firebase.google.com/docs/cloud-messaging/concept-options#data_messages
|
|
143
|
+
*/
|
|
144
|
+
export type DataOnlyMobilePushDataType = 'coach-data-changed' | 'coach-program-changed' | 'sync-workouts' | 'sync-routines' | 'sync-routine-folders' | 'sync-exercise-templates-and-customizations' | 'sync-account' | 'hevy-pro-status-changed' | 'hevy-pro-subscription-renewed' | 'new-notification' | never;
|
|
145
|
+
export interface CoachDataChangedMobilePushData extends BaseMobilePushData {
|
|
146
|
+
type: 'coach-data-changed';
|
|
147
|
+
additionalHevyData: ClientsCoachData;
|
|
148
|
+
}
|
|
149
|
+
export interface CoachProgramChangedMobilePushData extends BaseMobilePushData {
|
|
150
|
+
type: 'coach-program-changed';
|
|
151
|
+
additionalHevyData: {
|
|
152
|
+
program: Program | undefined;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
export interface HevyProSubscriptionRenewedMobilePushData extends BaseMobilePushData {
|
|
156
|
+
type: 'hevy-pro-subscription-renewed';
|
|
157
|
+
}
|
|
158
|
+
export interface ProStatusChangedPushMobilePushData extends BaseMobilePushData {
|
|
159
|
+
type: 'hevy-pro-status-changed';
|
|
160
|
+
}
|
|
161
|
+
export interface SyncWorkoutsMobilePushData extends BaseMobilePushData {
|
|
162
|
+
type: 'sync-workouts';
|
|
163
|
+
}
|
|
164
|
+
export interface SyncRoutinesMobilePushData extends BaseMobilePushData {
|
|
165
|
+
type: 'sync-routines';
|
|
166
|
+
}
|
|
167
|
+
export interface SyncRoutineFoldersMobilePushData extends BaseMobilePushData {
|
|
168
|
+
type: 'sync-routine-folders';
|
|
169
|
+
}
|
|
170
|
+
export interface SyncCustomExercisesAndCoachCustomizationsMobilePushData extends BaseMobilePushData {
|
|
171
|
+
type: 'sync-exercise-templates-and-customizations';
|
|
172
|
+
}
|
|
173
|
+
export interface SyncAccountMobilePushData extends BaseMobilePushData {
|
|
174
|
+
type: 'sync-account';
|
|
175
|
+
}
|
|
176
|
+
export interface CoachProgramFinishesNextWeekMobilePushData extends BaseMobilePushData {
|
|
177
|
+
type: 'coach-program-finishes-next-week';
|
|
178
|
+
}
|
|
179
|
+
export interface CoachProgramStartsTodayMobilePushData extends BaseMobilePushData {
|
|
180
|
+
type: 'coach-program-starts-today';
|
|
181
|
+
}
|
|
182
|
+
export interface ChatUpdateMobilePushData extends BaseMobilePushData {
|
|
183
|
+
type: 'chat-update';
|
|
184
|
+
additionalHevyData: HevyChatUpdateMobilePushData;
|
|
185
|
+
}
|
|
186
|
+
export interface CoachLoggedYourWorkoutMobilePushData extends BaseMobilePushData {
|
|
187
|
+
type: 'coach-logged-your-workout';
|
|
188
|
+
additionalHevyData: {
|
|
189
|
+
workoutId: string;
|
|
190
|
+
};
|
|
191
|
+
deeplink: string;
|
|
192
|
+
}
|
|
193
|
+
export interface CoachLoggedYourMeasurementsMobilePushData extends BaseMobilePushData {
|
|
194
|
+
type: 'coach-logged-your-measurements';
|
|
195
|
+
deeplink: string;
|
|
196
|
+
}
|
|
197
|
+
export interface MonthlyReportMobilePushData extends BaseMobilePushData {
|
|
198
|
+
type: 'monthly-report';
|
|
199
|
+
deeplink: string;
|
|
200
|
+
}
|
|
201
|
+
export interface GracePeriodEnterMobilePushData extends BaseMobilePushData {
|
|
202
|
+
type: 'grace-period-enter';
|
|
203
|
+
deeplink: string;
|
|
204
|
+
}
|
|
205
|
+
export interface GracePeriodResolveMobilePushData extends BaseMobilePushData {
|
|
206
|
+
type: 'grace-period-resolve';
|
|
207
|
+
deeplink: string;
|
|
208
|
+
}
|
|
209
|
+
export interface NewNotificationMobilePushData extends BaseMobilePushData {
|
|
210
|
+
type: 'new-notification';
|
|
211
|
+
additionalHevyData: {
|
|
212
|
+
notificationDateISO: string;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
export type MobilePushData = ChatUpdateMobilePushData | SyncCustomExercisesAndCoachCustomizationsMobilePushData | SyncWorkoutsMobilePushData | SyncRoutineFoldersMobilePushData | SyncRoutinesMobilePushData | SyncAccountMobilePushData | ProStatusChangedPushMobilePushData | HevyProSubscriptionRenewedMobilePushData | CoachProgramChangedMobilePushData | CoachDataChangedMobilePushData | ContactOnHevyMobilePushData | CoachClientInviteMobilePushData | AcceptFollowRequestMobilePushData | FollowRequestMobilePushData | FollowMobilePushData | WorkoutLikeMobilePushData | WorkoutCommentLikeMobilePushData | WorkoutCommentMentionMobilePushData | WorkoutCommentDiscussionMobilePushData | WorkoutCommentReplyMobilePushData | WorkoutCommentMobilePushData | WorkoutMentionMobilePushData | WorkoutCompleteMobilePushData | CoachProgramFinishesNextWeekMobilePushData | CoachProgramStartsTodayMobilePushData | CoachLoggedYourWorkoutMobilePushData | CoachLoggedYourMeasurementsMobilePushData | MonthlyReportMobilePushData | GracePeriodEnterMobilePushData | GracePeriodResolveMobilePushData | NewNotificationMobilePushData;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseRoutine, HevyTrainerRoutine, RoutineUpdate } from '.';
|
|
2
|
+
import { NormalizedWorkout } from './normalizedWorkoutUtils';
|
|
3
|
+
export declare const majorRoutineUpdate: (routine: BaseRoutine, workout: NormalizedWorkout) => RoutineUpdate;
|
|
4
|
+
export declare const minorRoutineUpdate: (routine: BaseRoutine, workout: NormalizedWorkout) => RoutineUpdate;
|
|
5
|
+
/**
|
|
6
|
+
* Updates a Hevy Trainer routine with the workout data.
|
|
7
|
+
* @param routine - The Hevy Trainer routine to update.
|
|
8
|
+
* @param workout - The workout data to update the routine with.
|
|
9
|
+
* @returns The updated routine.
|
|
10
|
+
*
|
|
11
|
+
* Only the rest timer seconds are updated for now.
|
|
12
|
+
*/
|
|
13
|
+
export declare const trainerRoutineUpdate: (routine: HevyTrainerRoutine, workout: NormalizedWorkout) => RoutineUpdate;
|
|
14
|
+
export type ViewRoutineSetValueType = 'weight' | 'reps' | 'rpe' | 'repRangeStart' | 'repRangeEnd' | 'distance' | 'duration' | 'short_distance' | 'custom_metric';
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.trainerRoutineUpdate = exports.minorRoutineUpdate = exports.majorRoutineUpdate = void 0;
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const majorRoutineUpdate = (routine, workout) => {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
const exercises = workout.exercises.map((e) => {
|
|
8
|
+
const occurrenceInWorkout = workout.exercises
|
|
9
|
+
.filter((it) => it.exerciseTemplateId === e.exerciseTemplateId)
|
|
10
|
+
.indexOf(e);
|
|
11
|
+
const routineExercise = routine.exercises.filter((it) => it.exercise_template_id === e.exerciseTemplateId)[occurrenceInWorkout];
|
|
12
|
+
if (!!routineExercise) {
|
|
13
|
+
const exercise = {
|
|
14
|
+
exercise_template_id: e.exerciseTemplateId,
|
|
15
|
+
superset_id: e.supersetId,
|
|
16
|
+
rest_seconds: e.autoRestTimerSeconds,
|
|
17
|
+
sets: e.sets.map((set, setIndex) => {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
const newSet = set;
|
|
20
|
+
const oldSet = routineExercise.sets[setIndex];
|
|
21
|
+
return {
|
|
22
|
+
index: setIndex,
|
|
23
|
+
indicator: newSet.indicator,
|
|
24
|
+
weight_kg: (newSet === null || newSet === void 0 ? void 0 : newSet.weight) || (oldSet === null || oldSet === void 0 ? void 0 : oldSet.weight_kg),
|
|
25
|
+
reps: routineExercise.input_modifier === 'rep-range'
|
|
26
|
+
? undefined
|
|
27
|
+
: (newSet === null || newSet === void 0 ? void 0 : newSet.reps) || (oldSet === null || oldSet === void 0 ? void 0 : oldSet.reps),
|
|
28
|
+
distance_meters: (newSet === null || newSet === void 0 ? void 0 : newSet.distance) || (oldSet === null || oldSet === void 0 ? void 0 : oldSet.distance_meters),
|
|
29
|
+
duration_seconds: (newSet === null || newSet === void 0 ? void 0 : newSet.duration) || (oldSet === null || oldSet === void 0 ? void 0 : oldSet.duration_seconds),
|
|
30
|
+
custom_metric: (newSet === null || newSet === void 0 ? void 0 : newSet.customMetric) || (oldSet === null || oldSet === void 0 ? void 0 : oldSet.custom_metric),
|
|
31
|
+
rpe: oldSet === null || oldSet === void 0 ? void 0 : oldSet.rpe,
|
|
32
|
+
rep_range: routineExercise.input_modifier === 'rep-range'
|
|
33
|
+
? (((_a = oldSet === null || oldSet === void 0 ? void 0 : oldSet.rep_range) === null || _a === void 0 ? void 0 : _a.start) || undefined) !== undefined ||
|
|
34
|
+
(((_b = oldSet === null || oldSet === void 0 ? void 0 : oldSet.rep_range) === null || _b === void 0 ? void 0 : _b.end) || undefined) !== undefined
|
|
35
|
+
? oldSet === null || oldSet === void 0 ? void 0 : oldSet.rep_range
|
|
36
|
+
: { start: (newSet === null || newSet === void 0 ? void 0 : newSet.reps) || null, end: null }
|
|
37
|
+
: undefined,
|
|
38
|
+
};
|
|
39
|
+
}),
|
|
40
|
+
notes: routineExercise.notes,
|
|
41
|
+
input_modifier: routineExercise.input_modifier,
|
|
42
|
+
};
|
|
43
|
+
return exercise;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const exercise = {
|
|
47
|
+
exercise_template_id: e.exerciseTemplateId,
|
|
48
|
+
superset_id: e.supersetId,
|
|
49
|
+
rest_seconds: e.autoRestTimerSeconds,
|
|
50
|
+
sets: e.sets.map((set, setIndex) => {
|
|
51
|
+
var _a;
|
|
52
|
+
const newSet = set;
|
|
53
|
+
return {
|
|
54
|
+
index: setIndex,
|
|
55
|
+
indicator: (_a = newSet === null || newSet === void 0 ? void 0 : newSet.indicator) !== null && _a !== void 0 ? _a : set.indicator,
|
|
56
|
+
weight_kg: newSet === null || newSet === void 0 ? void 0 : newSet.weight,
|
|
57
|
+
reps: newSet === null || newSet === void 0 ? void 0 : newSet.reps,
|
|
58
|
+
distance_meters: newSet === null || newSet === void 0 ? void 0 : newSet.distance,
|
|
59
|
+
duration_seconds: newSet === null || newSet === void 0 ? void 0 : newSet.duration,
|
|
60
|
+
custom_metric: newSet === null || newSet === void 0 ? void 0 : newSet.customMetric,
|
|
61
|
+
rpe: undefined,
|
|
62
|
+
rep_range: undefined,
|
|
63
|
+
};
|
|
64
|
+
}),
|
|
65
|
+
notes: '',
|
|
66
|
+
input_modifier: undefined,
|
|
67
|
+
};
|
|
68
|
+
return exercise;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
title: routine.title,
|
|
73
|
+
exercises,
|
|
74
|
+
parent_routine_id: (_a = routine.parent_routine_id) !== null && _a !== void 0 ? _a : undefined,
|
|
75
|
+
folder_id: routine.folder_id,
|
|
76
|
+
index: (_b = routine.index) !== null && _b !== void 0 ? _b : 0,
|
|
77
|
+
program_id: routine.program_id,
|
|
78
|
+
notes: routine.notes,
|
|
79
|
+
coach_force_rpe_enabled: routine.coach_force_rpe_enabled,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
exports.majorRoutineUpdate = majorRoutineUpdate;
|
|
83
|
+
const minorRoutineUpdate = (routine, workout) => {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
const exercises = routine.exercises.map((e) => {
|
|
86
|
+
const occurrenceInRoutine = routine.exercises
|
|
87
|
+
.filter((it) => it.exercise_template_id === e.exercise_template_id)
|
|
88
|
+
.indexOf(e);
|
|
89
|
+
const workoutExercise = workout.exercises.filter((it) => it.exerciseTemplateId === e.exercise_template_id)[occurrenceInRoutine];
|
|
90
|
+
if (!!workoutExercise) {
|
|
91
|
+
const exercise = {
|
|
92
|
+
exercise_template_id: workoutExercise.exerciseTemplateId,
|
|
93
|
+
superset_id: workoutExercise.supersetId,
|
|
94
|
+
rest_seconds: workoutExercise.autoRestTimerSeconds,
|
|
95
|
+
sets: e.sets.map((set, setIndex) => {
|
|
96
|
+
var _a, _b;
|
|
97
|
+
const setOccurrenceInRoutineExercise = e.sets
|
|
98
|
+
.filter((it) => it.indicator === set.indicator ||
|
|
99
|
+
(it.indicator === 'normal' && set.indicator === 'failure') ||
|
|
100
|
+
(it.indicator === 'failure' && set.indicator === 'normal'))
|
|
101
|
+
.indexOf(set);
|
|
102
|
+
const workoutSet = workoutExercise.sets.filter((it) => it.indicator === set.indicator ||
|
|
103
|
+
(it.indicator === 'normal' && set.indicator === 'failure') ||
|
|
104
|
+
(it.indicator === 'failure' && set.indicator === 'normal'))[setOccurrenceInRoutineExercise];
|
|
105
|
+
return {
|
|
106
|
+
index: setIndex,
|
|
107
|
+
indicator: set.indicator,
|
|
108
|
+
weight_kg: (workoutSet === null || workoutSet === void 0 ? void 0 : workoutSet.weight) || set.weight_kg,
|
|
109
|
+
reps: e.input_modifier === 'rep-range'
|
|
110
|
+
? undefined
|
|
111
|
+
: (workoutSet === null || workoutSet === void 0 ? void 0 : workoutSet.reps) || set.reps,
|
|
112
|
+
distance_meters: (workoutSet === null || workoutSet === void 0 ? void 0 : workoutSet.distance) || set.distance_meters,
|
|
113
|
+
duration_seconds: (workoutSet === null || workoutSet === void 0 ? void 0 : workoutSet.duration) || set.duration_seconds,
|
|
114
|
+
custom_metric: (workoutSet === null || workoutSet === void 0 ? void 0 : workoutSet.customMetric) || set.custom_metric,
|
|
115
|
+
rpe: set.rpe,
|
|
116
|
+
rep_range: e.input_modifier === 'rep-range'
|
|
117
|
+
? (((_a = set.rep_range) === null || _a === void 0 ? void 0 : _a.start) || undefined) !== undefined ||
|
|
118
|
+
(((_b = set.rep_range) === null || _b === void 0 ? void 0 : _b.end) || undefined) !== undefined
|
|
119
|
+
? set.rep_range
|
|
120
|
+
: { start: (workoutSet === null || workoutSet === void 0 ? void 0 : workoutSet.reps) || set.reps || null, end: null }
|
|
121
|
+
: undefined,
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
124
|
+
notes: e.notes,
|
|
125
|
+
input_modifier: e.input_modifier,
|
|
126
|
+
};
|
|
127
|
+
return exercise;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const exercise = {
|
|
131
|
+
exercise_template_id: e.exercise_template_id,
|
|
132
|
+
superset_id: e.superset_id,
|
|
133
|
+
rest_seconds: e.rest_seconds,
|
|
134
|
+
sets: e.sets.map((set, setIndex) => ({
|
|
135
|
+
index: setIndex,
|
|
136
|
+
indicator: set.indicator,
|
|
137
|
+
weight_kg: set.weight_kg,
|
|
138
|
+
reps: set.reps,
|
|
139
|
+
distance_meters: set.distance_meters,
|
|
140
|
+
duration_seconds: set.duration_seconds,
|
|
141
|
+
custom_metric: set.custom_metric,
|
|
142
|
+
rpe: set.rpe,
|
|
143
|
+
rep_range: set.rep_range,
|
|
144
|
+
})),
|
|
145
|
+
notes: e.notes,
|
|
146
|
+
input_modifier: e.input_modifier,
|
|
147
|
+
};
|
|
148
|
+
return exercise;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
title: routine.title,
|
|
153
|
+
exercises,
|
|
154
|
+
parent_routine_id: (_a = routine.parent_routine_id) !== null && _a !== void 0 ? _a : undefined,
|
|
155
|
+
folder_id: routine.folder_id,
|
|
156
|
+
index: (_b = routine.index) !== null && _b !== void 0 ? _b : 0,
|
|
157
|
+
program_id: routine.program_id,
|
|
158
|
+
notes: routine.notes,
|
|
159
|
+
coach_force_rpe_enabled: routine.coach_force_rpe_enabled,
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
exports.minorRoutineUpdate = minorRoutineUpdate;
|
|
163
|
+
/**
|
|
164
|
+
* Updates a Hevy Trainer routine with the workout data.
|
|
165
|
+
* @param routine - The Hevy Trainer routine to update.
|
|
166
|
+
* @param workout - The workout data to update the routine with.
|
|
167
|
+
* @returns The updated routine.
|
|
168
|
+
*
|
|
169
|
+
* Only the rest timer seconds are updated for now.
|
|
170
|
+
*/
|
|
171
|
+
const trainerRoutineUpdate = (routine, workout) => {
|
|
172
|
+
var _a, _b;
|
|
173
|
+
return Object.assign(Object.assign({}, routine), { parent_routine_id: (_a = routine.parent_routine_id) !== null && _a !== void 0 ? _a : undefined, index: (_b = routine.index) !== null && _b !== void 0 ? _b : 0, exercises: routine.exercises.map((e) => {
|
|
174
|
+
const occurrenceInRoutine = routine.exercises
|
|
175
|
+
.filter((it) => it.exercise_template_id === e.exercise_template_id)
|
|
176
|
+
.indexOf(e);
|
|
177
|
+
const workoutExercise = (0, _1.typeSafeIndex)(workout.exercises.filter((it) => it.exerciseTemplateId === e.exercise_template_id), occurrenceInRoutine);
|
|
178
|
+
const shouldUpdateExercise = !!workoutExercise;
|
|
179
|
+
if (shouldUpdateExercise) {
|
|
180
|
+
const exercise = Object.assign(Object.assign({}, e), { rest_seconds: workoutExercise.autoRestTimerSeconds });
|
|
181
|
+
return exercise;
|
|
182
|
+
}
|
|
183
|
+
return e;
|
|
184
|
+
}) });
|
|
185
|
+
};
|
|
186
|
+
exports.trainerRoutineUpdate = trainerRoutineUpdate;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface SetIndicatored {
|
|
2
|
-
indicator: 'warmup' | 'dropset' | 'failure' | 'normal' |
|
|
2
|
+
indicator: 'warmup' | 'dropset' | 'failure' | 'normal' | number;
|
|
3
3
|
}
|
|
4
4
|
interface Indexed {
|
|
5
5
|
index: number;
|
|
@@ -30,7 +30,7 @@ interface Indexed {
|
|
|
30
30
|
* ]
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
export declare const indexSetObjectsForUI: <T>(indexedSet: SetIndicatored[]) =>
|
|
33
|
+
export declare const indexSetObjectsForUI: <T>(indexedSet: SetIndicatored[]) => Array<SetIndicatored & Indexed & T>;
|
|
34
34
|
/**
|
|
35
35
|
* This function is almost the exact same as `indexSetObjectsForUI`
|
|
36
36
|
* but this function will change failure sets to normal. Because when
|
|
@@ -63,7 +63,8 @@ export declare const indexSetObjectsForUI: <T>(indexedSet: SetIndicatored[]) =>
|
|
|
63
63
|
* ]
|
|
64
64
|
* ```
|
|
65
65
|
*/
|
|
66
|
-
export declare const indexSetObjectsForType: <T>(indexedSet: SetIndicatored[]) =>
|
|
66
|
+
export declare const indexSetObjectsForType: <T>(indexedSet: SetIndicatored[]) => Array<SetIndicatored & Indexed & T>;
|
|
67
67
|
export type UserFacingSetIndicator = 'dropset' | 'warmup' | 'failure' | number;
|
|
68
68
|
export declare const setObjectToUIFacingSetIndicator: (set: SetIndicatored & Indexed) => UserFacingSetIndicator;
|
|
69
|
+
export declare const compareUserFacingSetIndicator: (indicatorA: UserFacingSetIndicator, indicatorB: UserFacingSetIndicator) => boolean;
|
|
69
70
|
export {};
|