hevy-shared 1.0.960 → 1.0.962

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.
Files changed (88) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc +21 -0
  3. package/.github/workflows/ci.yml +15 -0
  4. package/.github/workflows/npm-publish.yml +59 -0
  5. package/.github/workflows/pr-auto-assign.yml +15 -0
  6. package/.prettierrc.js +5 -0
  7. package/README.md +2 -17
  8. package/built/chat.d.ts +23 -25
  9. package/built/coachPlans.d.ts +1 -2
  10. package/built/coachPlans.js +2 -2
  11. package/built/filterExercises.d.ts +3 -19
  12. package/built/filterExercises.js +60 -72
  13. package/built/index.d.ts +304 -1140
  14. package/built/index.js +75 -269
  15. package/built/setIndicatorUtils.d.ts +3 -4
  16. package/built/setIndicatorUtils.js +1 -15
  17. package/built/tests/utils.test.js +0 -748
  18. package/built/tests/workoutVolume.test.js +49 -165
  19. package/built/units.d.ts +7 -14
  20. package/built/units.js +14 -24
  21. package/built/utils.d.ts +5 -192
  22. package/built/utils.js +85 -598
  23. package/built/websocket.d.ts +2 -14
  24. package/built/workoutVolume.d.ts +5 -24
  25. package/built/workoutVolume.js +34 -25
  26. package/jest.config.js +4 -0
  27. package/package.json +10 -32
  28. package/src/chat.ts +130 -0
  29. package/src/coachPlans.ts +57 -0
  30. package/src/constants.ts +14 -0
  31. package/src/filterExercises.ts +222 -0
  32. package/src/index.ts +1576 -0
  33. package/src/setIndicatorUtils.ts +137 -0
  34. package/src/tests/utils.test.ts +156 -0
  35. package/src/tests/workoutVolume.test.ts +93 -0
  36. package/src/units.ts +41 -0
  37. package/src/utils.ts +516 -0
  38. package/src/websocket.ts +36 -0
  39. package/src/workoutVolume.ts +175 -0
  40. package/tsconfig.json +70 -0
  41. package/built/API/APIClient.d.ts +0 -157
  42. package/built/API/APIClient.js +0 -381
  43. package/built/API/index.d.ts +0 -2
  44. package/built/API/index.js +0 -18
  45. package/built/API/types.d.ts +0 -38
  46. package/built/API/types.js +0 -18
  47. package/built/adjustEventTokens.d.ts +0 -16
  48. package/built/adjustEventTokens.js +0 -18
  49. package/built/adminPermissions.d.ts +0 -4
  50. package/built/adminPermissions.js +0 -22
  51. package/built/async.d.ts +0 -50
  52. package/built/async.js +0 -170
  53. package/built/cue.d.ts +0 -12
  54. package/built/cue.js +0 -22
  55. package/built/exerciseLocaleUtils.d.ts +0 -17
  56. package/built/exerciseLocaleUtils.js +0 -62
  57. package/built/hevyTrainer.d.ts +0 -250
  58. package/built/hevyTrainer.js +0 -676
  59. package/built/muscleHeatmaps.d.ts +0 -31
  60. package/built/muscleHeatmaps.js +0 -68
  61. package/built/muscleSplits.d.ts +0 -36
  62. package/built/muscleSplits.js +0 -100
  63. package/built/normalizedWorkoutUtils.d.ts +0 -88
  64. package/built/normalizedWorkoutUtils.js +0 -112
  65. package/built/notifications.d.ts +0 -215
  66. package/built/notifications.js +0 -9
  67. package/built/routineUtils.d.ts +0 -14
  68. package/built/routineUtils.js +0 -186
  69. package/built/schemas.d.ts +0 -6
  70. package/built/schemas.js +0 -12
  71. package/built/tests/async.test.d.ts +0 -1
  72. package/built/tests/async.test.js +0 -49
  73. package/built/tests/hevyTrainer.test.d.ts +0 -1
  74. package/built/tests/hevyTrainer.test.js +0 -1199
  75. package/built/tests/muscleSplit.test.d.ts +0 -1
  76. package/built/tests/muscleSplit.test.js +0 -153
  77. package/built/tests/routineUtils.test.d.ts +0 -1
  78. package/built/tests/routineUtils.test.js +0 -745
  79. package/built/tests/testUtils.d.ts +0 -85
  80. package/built/tests/testUtils.js +0 -319
  81. package/built/translations/index.d.ts +0 -2
  82. package/built/translations/index.js +0 -18
  83. package/built/translations/translationUtils.d.ts +0 -2
  84. package/built/translations/translationUtils.js +0 -61
  85. package/built/translations/types.d.ts +0 -8
  86. package/built/translations/types.js +0 -20
  87. package/built/typeUtils.d.ts +0 -70
  88. package/built/typeUtils.js +0 -55
@@ -0,0 +1,222 @@
1
+ import { intersectionWith } from 'lodash';
2
+ import {
3
+ EquipmentFilter,
4
+ ExerciseTemplate,
5
+ Language,
6
+ MuscleGroupFilter,
7
+ } from './index';
8
+ import { removeAccents } from './utils';
9
+
10
+ export interface SearchableExerciseTemplate extends ExerciseTemplate {
11
+ localized_muscle_group_name: string;
12
+ }
13
+
14
+ interface FilterExercisesProps {
15
+ exercises: SearchableExerciseTemplate[];
16
+ muscleGroupFilter: MuscleGroupFilter;
17
+ equipmentType: EquipmentFilter;
18
+ searchText: string;
19
+ language: Language;
20
+ }
21
+
22
+ export const filterExercises = ({
23
+ exercises,
24
+ muscleGroupFilter,
25
+ equipmentType,
26
+ searchText,
27
+ language,
28
+ }: FilterExercisesProps): ExerciseTemplate[] => {
29
+ let result = exercises;
30
+
31
+ result = filterExercisesWithMuscleGroup(muscleGroupFilter, result);
32
+ result = filterExercisesWithEquipment(equipmentType, result);
33
+
34
+ const normalizedSearchText = normalizeSearchText(searchText, language);
35
+
36
+ result = filterExercisesWithSearchText(
37
+ normalizedSearchText,
38
+ result,
39
+ language,
40
+ );
41
+ result = orderExercisesWithSearchText(normalizedSearchText, result);
42
+
43
+ return result;
44
+ };
45
+
46
+ const filterExercisesWithMuscleGroup = (
47
+ muscleGroup: MuscleGroupFilter,
48
+ exercises: SearchableExerciseTemplate[],
49
+ ): SearchableExerciseTemplate[] => {
50
+ if (muscleGroup === 'all_muscles') {
51
+ return exercises;
52
+ }
53
+
54
+ return exercises.filter((rd) => rd.muscle_group === muscleGroup);
55
+ };
56
+
57
+ const filterExercisesWithEquipment = (
58
+ equipmentType: EquipmentFilter,
59
+ exercises: SearchableExerciseTemplate[],
60
+ ): SearchableExerciseTemplate[] => {
61
+ if (equipmentType === 'all_equipment') {
62
+ return exercises;
63
+ }
64
+
65
+ return exercises.filter((rd) => rd.equipment_category === equipmentType);
66
+ };
67
+
68
+ const filterExercisesWithSearchText = (
69
+ searchText: string,
70
+ exercises: SearchableExerciseTemplate[],
71
+ language: Language,
72
+ ): SearchableExerciseTemplate[] => {
73
+ if (searchText.length === 0) {
74
+ return exercises;
75
+ }
76
+
77
+ return exercises.filter((e) => {
78
+ const searchableSearchText = searchable(searchText);
79
+ const searchableEnglishTitle = searchable(e.title);
80
+
81
+ const searchableLocalizedTitleMap = {
82
+ en: searchable(e.title),
83
+ es: e.es_title ? searchable(e.es_title) : undefined,
84
+ de: e.de_title ? searchable(e.de_title) : undefined,
85
+ fr: e.fr_title ? searchable(e.fr_title) : undefined,
86
+ it: e.it_title ? searchable(e.it_title) : undefined,
87
+ pt: e.pt_title ? searchable(e.pt_title) : undefined,
88
+ tr: e.tr_title ? searchable(e.tr_title) : undefined,
89
+ zh_CN: e.zh_cn_title ? searchable(e.zh_cn_title) : undefined,
90
+ zh_TW: e.zh_tw_title ? searchable(e.zh_tw_title) : undefined,
91
+ ru: e.ru_title ? searchable(e.ru_title) : undefined,
92
+ ja: e.ja_title ? searchable(e.ja_title) : undefined,
93
+ ko: e.ko_title ? searchable(e.ko_title) : undefined,
94
+ };
95
+
96
+ const searchableLocalizedTitle =
97
+ searchableLocalizedTitleMap[language] || searchableLocalizedTitleMap.en;
98
+
99
+ const searchableLocalizedMuscleGroupName = searchable(
100
+ e.localized_muscle_group_name,
101
+ );
102
+ const searchableMuscleGroup = searchable(e.muscle_group);
103
+ const searchableMisspelledDumbbellEquipment =
104
+ e.equipment_category === 'dumbbell' ? 'dumbell' : '';
105
+ const searchableTags = e.manual_tag ? searchable(e.manual_tag) : '';
106
+ const searchableAka = e.aka ? searchable(e.aka) : '';
107
+
108
+ if (
109
+ searchableLocalizedTitle.includes(searchableSearchText) ||
110
+ searchableEnglishTitle.includes(searchableSearchText) ||
111
+ searchableLocalizedMuscleGroupName.includes(searchableSearchText) ||
112
+ searchableMuscleGroup.includes(searchableSearchText)
113
+ ) {
114
+ return true;
115
+ }
116
+
117
+ if (e.is_custom && 'custom'.includes(searchableSearchText)) {
118
+ return true;
119
+ }
120
+
121
+ const splitSearch = searchableSearchText.split(' ');
122
+ const splitTitle = [
123
+ searchableLocalizedTitle,
124
+ searchableEnglishTitle,
125
+ searchableTags,
126
+ searchableAka,
127
+ searchableMisspelledDumbbellEquipment,
128
+ ]
129
+ .join(' ')
130
+ .split(' ');
131
+
132
+ // Find:
133
+ // title ['arnold', 'press', '(barbell)']
134
+ // search ['press', 'bar']
135
+ if (
136
+ intersectionWith(splitSearch, splitTitle, (search, title) =>
137
+ title.includes(search),
138
+ ).length === splitSearch.length
139
+ ) {
140
+ return true;
141
+ }
142
+
143
+ return false;
144
+ });
145
+ };
146
+
147
+ const orderExercisesWithSearchText = (
148
+ searchText: string,
149
+ exercises: SearchableExerciseTemplate[],
150
+ ): SearchableExerciseTemplate[] => {
151
+ if (searchText.length === 0) {
152
+ return exercises;
153
+ }
154
+
155
+ return exercises
156
+ .reduce((accu: any[], currentExercise) => {
157
+ const lowercaseTitle = currentExercise.title.toLowerCase();
158
+
159
+ if (lowercaseTitle.startsWith(searchText)) {
160
+ accu.push({
161
+ exercise: currentExercise,
162
+ weight: currentExercise.priority * 2,
163
+ });
164
+ return accu;
165
+ }
166
+
167
+ accu.push({
168
+ exercise: currentExercise,
169
+ weight: currentExercise.priority,
170
+ });
171
+ return accu;
172
+ }, [])
173
+ .sort((a, b) => b.weight - a.weight)
174
+ .map((a) => a.exercise);
175
+ };
176
+
177
+ /**
178
+ * trims, lowercases and removes trailing "s" character to
179
+ * simplify english searches
180
+ */
181
+ const normalizeSearchText = (searchText: string, language: Language) => {
182
+ const searchableSearchText = searchable(searchText).replace('-', ' ').trim();
183
+
184
+ if (language !== 'en') return searchableSearchText;
185
+ if (searchableSearchText.length < 3) return searchableSearchText;
186
+
187
+ const lastCharacter = searchableSearchText.slice(-1);
188
+ if (lastCharacter !== 's') return searchableSearchText;
189
+
190
+ return searchableSearchText.slice(0, -1);
191
+ };
192
+
193
+ const searchable = (str: string): string => {
194
+ return removeAccents(str.toLowerCase());
195
+ };
196
+
197
+ /**
198
+ * Exercise templates that are shown at the top of the Exercise Library to
199
+ * users who don't have any workouts logged.
200
+ */
201
+ export const popularExerciseTemplateIds = [
202
+ 'B8127AD1', // Lying Leg Curl (Machine)"
203
+ '11A123F3', // Seated Leg Curl (Machine)"
204
+ 'D04AC939', // Squat (Barbell)"
205
+ '75A4F6C4', // Leg Extension (Machine)"
206
+ '422B08F1', // Lateral Raise (Dumbbell)"
207
+ 'C7973E0E', // Leg Press (Machine)"
208
+ '07B38369', // Incline Bench Press (Dumbbell)"
209
+ '3601968B', // Bench Press (Dumbbell)"
210
+ '6A6C31A5', // Lat Pulldown (Cable)"
211
+ '0393F233', // Seated Cable Row - V Grip (Cable)"
212
+ '37FCC2BB', // Bicep Curl (Dumbbell)"
213
+ '55E6546F', // Bent Over Row (Barbell)"
214
+ '94B7239B', // Triceps Rope Pushdown"
215
+ 'C6272009', // Deadlift (Barbell)"
216
+ '878CD1D0', // Shoulder Press (Dumbbell)"
217
+ '651F844C', // Cable Fly Crossovers"
218
+ '79D0BB3A', // Bench Press (Barbell)"
219
+ '93A552C6', // Triceps Pushdown"
220
+ '7E3BC8B6', // Hammer Curl (Dumbbell)"
221
+ 'BE640BA0', // Face Pull
222
+ ] as const;