hevy-shared 1.0.710 → 1.0.712

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.
@@ -1,4 +1,3 @@
1
- import Fuse from 'fuse.js';
2
1
  import { EquipmentFilter, Language, LibraryExercise, MuscleGroupFilter, CustomExercise } from './index';
3
2
  export interface SearchableLibraryExercise extends LibraryExercise {
4
3
  localized_muscle_group_name: string;
@@ -21,12 +20,11 @@ interface FilterExercisesProps {
21
20
  searchText: string;
22
21
  language: Language;
23
22
  }
24
- export declare const createFuseInstance: () => Fuse<string>;
25
- export declare const newFilterExercises: ({ fuseInstance, exercises, muscleGroupFilter, equipmentType, searchText, language, muscleGroupMatchPenalty, }: FilterExercisesProps & {
26
- muscleGroupMatchPenalty: number;
27
- fuseInstance: Fuse<string>;
28
- }) => SearchableExerciseTemplateWithStringScore[];
29
- export declare const filterExercises: ({ exercises, muscleGroupFilter, equipmentType, searchText, language, }: FilterExercisesProps) => (CustomExercise | LibraryExercise)[];
23
+ export declare const getExerciseMatches: ({ exercises, muscleGroupFilter, equipmentType, searchText, language, }: FilterExercisesProps) => ExerciseSearchMatch<SearchableExerciseTemplate>[];
24
+ export type ExerciseSearchMatch<T> = {
25
+ exercise: T;
26
+ matchType: 'title' | 'muscle_group' | 'other' | 'custom';
27
+ };
30
28
  /**
31
29
  * Exercise templates that are shown at the top of the Exercise Library to
32
30
  * users who don't have any workouts logged.
@@ -1,41 +1,18 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.popularExerciseTemplateIds = exports.filterExercises = exports.newFilterExercises = exports.createFuseInstance = void 0;
7
- const fuse_js_1 = __importDefault(require("fuse.js"));
3
+ exports.popularExerciseTemplateIds = exports.getExerciseMatches = void 0;
8
4
  const utils_1 = require("./utils");
9
5
  const lodash_1 = require("lodash");
10
- const createFuseInstance = () => {
11
- const stringSimilarityFuseInstance = new fuse_js_1.default([''], {
12
- includeScore: true,
13
- threshold: 0.4,
14
- location: 0, // Prefer matches at the beginning of the string
15
- });
16
- return stringSimilarityFuseInstance;
17
- };
18
- exports.createFuseInstance = createFuseInstance;
19
- const newFilterExercises = ({ fuseInstance, exercises, muscleGroupFilter, equipmentType, searchText, language, muscleGroupMatchPenalty, }) => {
6
+ const getExerciseMatches = ({ exercises, muscleGroupFilter, equipmentType, searchText, language, }) => {
20
7
  let result = exercises;
21
8
  result = filterExercisesWithMuscleGroup(muscleGroupFilter, result);
22
9
  result = filterExercisesWithEquipment(equipmentType, result);
23
10
  const normalizedSearchText = normalizeSearchText(searchText, language);
24
- result = newFilterExercisesWithSearchText(fuseInstance, normalizedSearchText, result, language, muscleGroupMatchPenalty);
25
- result = orderExercisesWithSearchText(normalizedSearchText, result);
26
- return result;
11
+ let matches = filterExercisesWithSearchText(normalizedSearchText, result, language);
12
+ matches = orderExercisesWithSearchText(normalizedSearchText, matches);
13
+ return matches;
27
14
  };
28
- exports.newFilterExercises = newFilterExercises;
29
- const filterExercises = ({ exercises, muscleGroupFilter, equipmentType, searchText, language, }) => {
30
- let result = exercises;
31
- result = filterExercisesWithMuscleGroup(muscleGroupFilter, result);
32
- result = filterExercisesWithEquipment(equipmentType, result);
33
- const normalizedSearchText = normalizeSearchText(searchText, language);
34
- result = filterExercisesWithSearchText(normalizedSearchText, result, language);
35
- result = orderExercisesWithSearchText(normalizedSearchText, result);
36
- return result;
37
- };
38
- exports.filterExercises = filterExercises;
15
+ exports.getExerciseMatches = getExerciseMatches;
39
16
  const filterExercisesWithMuscleGroup = (muscleGroup, exercises) => {
40
17
  if (muscleGroup === 'all_muscles') {
41
18
  return exercises;
@@ -48,30 +25,13 @@ const filterExercisesWithEquipment = (equipmentType, exercises) => {
48
25
  }
49
26
  return exercises.filter((rd) => rd.equipment_category === equipmentType);
50
27
  };
51
- const fuseScoreToSimilarity = (score) => {
52
- if (score === 0)
53
- return 'exact';
54
- if (score < 0.05)
55
- return 'extremely-similar';
56
- if (score < 0.3)
57
- return 'very-similar';
58
- if (score < 0.6)
59
- return 'somewhat-similar';
60
- return 'not-similar';
61
- };
62
- const getStringSimilarityScore = (searchString, targetString, fuseInstance) => {
63
- var _a, _b;
64
- fuseInstance.setCollection([targetString]);
65
- const result = fuseInstance.search(searchString);
66
- const score = (_b = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.score) !== null && _b !== void 0 ? _b : 1.0;
67
- return score;
68
- };
69
28
  // Old filter exercise functionality, will be removed when new feature goes live
70
29
  const filterExercisesWithSearchText = (searchText, exercises, language) => {
71
30
  if (searchText.length === 0) {
72
- return exercises;
31
+ return exercises.map((e) => ({ exercise: e, matchType: 'other' }));
73
32
  }
74
- return exercises.filter((e) => {
33
+ const matches = [];
34
+ for (const e of exercises) {
75
35
  const searchableSearchText = searchable(searchText);
76
36
  const searchableEnglishTitle = searchable(e.title);
77
37
  const searchableLocalizedTitleMap = !e.is_custom
@@ -99,13 +59,18 @@ const filterExercisesWithSearchText = (searchText, exercises, language) => {
99
59
  const searchableTags = !e.is_custom && e.manual_tag ? searchable(e.manual_tag) : '';
100
60
  const searchableAka = !e.is_custom && e.aka ? searchable(e.aka) : '';
101
61
  if (searchableLocalizedTitle.includes(searchableSearchText) ||
102
- searchableEnglishTitle.includes(searchableSearchText) ||
103
- searchableLocalizedMuscleGroupName.includes(searchableSearchText) ||
62
+ searchableEnglishTitle.includes(searchableSearchText)) {
63
+ matches.push({ exercise: e, matchType: 'title' });
64
+ continue;
65
+ }
66
+ else if (searchableLocalizedMuscleGroupName.includes(searchableSearchText) ||
104
67
  searchableMuscleGroup.includes(searchableSearchText)) {
105
- return true;
68
+ matches.push({ exercise: e, matchType: 'muscle_group' });
69
+ continue;
106
70
  }
107
71
  if (e.is_custom && 'custom'.includes(searchableSearchText)) {
108
- return true;
72
+ matches.push({ exercise: e, matchType: 'custom' });
73
+ continue;
109
74
  }
110
75
  const splitSearch = searchableSearchText.split(' ');
111
76
  const splitTitle = [
@@ -121,111 +86,29 @@ const filterExercisesWithSearchText = (searchText, exercises, language) => {
121
86
  // title ['arnold', 'press', '(barbell)']
122
87
  // search ['press', 'bar']
123
88
  if ((0, lodash_1.intersectionWith)(splitSearch, splitTitle, (search, title) => title.includes(search)).length === splitSearch.length) {
124
- return true;
89
+ matches.push({ exercise: e, matchType: 'other' });
90
+ continue;
125
91
  }
126
- return false;
127
- });
128
- };
129
- const newFilterExercisesWithSearchText = (fuseInstance, searchText, exercises, language,
130
- // 0 means no penalty, 1 means don't match muscle groups (because a score > 1 means no match)
131
- muscleGroupMatchPenalty) => {
132
- if (searchText.length === 0) {
133
- return exercises;
134
92
  }
135
- return exercises.reduce((accu, e) => {
136
- const searchableSearchText = searchable(searchText);
137
- const searchableEnglishTitle = searchable(e.title);
138
- const searchableLocalizedTitleMap = !e.is_custom
139
- ? {
140
- en: searchable(e.title),
141
- es: e.es_title ? searchable(e.es_title) : undefined,
142
- de: e.de_title ? searchable(e.de_title) : undefined,
143
- fr: e.fr_title ? searchable(e.fr_title) : undefined,
144
- it: e.it_title ? searchable(e.it_title) : undefined,
145
- pt: e.pt_title ? searchable(e.pt_title) : undefined,
146
- tr: e.tr_title ? searchable(e.tr_title) : undefined,
147
- zh_CN: e.zh_cn_title ? searchable(e.zh_cn_title) : undefined,
148
- zh_TW: e.zh_tw_title ? searchable(e.zh_tw_title) : undefined,
149
- ru: e.ru_title ? searchable(e.ru_title) : undefined,
150
- ja: e.ja_title ? searchable(e.ja_title) : undefined,
151
- ko: e.ko_title ? searchable(e.ko_title) : undefined,
152
- }
153
- : {
154
- en: searchable(e.title),
155
- };
156
- const searchableLocalizedTitle = searchableLocalizedTitleMap[language] || searchableLocalizedTitleMap.en;
157
- const searchableLocalizedMuscleGroupName = searchable(e.localized_muscle_group_name);
158
- const searchableMuscleGroup = searchable(e.muscle_group);
159
- const searchableMisspelledDumbbellEquipment = e.equipment_category === 'dumbbell' ? 'dumbell' : '';
160
- const searchableTags = !e.is_custom && e.manual_tag ? searchable(e.manual_tag) : '';
161
- const searchableAka = !e.is_custom && e.aka ? searchable(e.aka) : '';
162
- const englishSimilarityScore = getStringSimilarityScore(searchableSearchText, searchableEnglishTitle, fuseInstance);
163
- const localizedSimilarityScore = getStringSimilarityScore(searchableSearchText, searchableLocalizedTitle, fuseInstance);
164
- let englishMuscleGroupSimilarityScore = 1;
165
- let muscleGroupSimilarityScore = 1;
166
- if (muscleGroupMatchPenalty < 1) {
167
- englishMuscleGroupSimilarityScore = getStringSimilarityScore(searchableSearchText, searchableMuscleGroup, fuseInstance);
168
- muscleGroupSimilarityScore = getStringSimilarityScore(searchableSearchText, searchableLocalizedMuscleGroupName, fuseInstance);
169
- }
170
- // We always return the best score, regardless of where the match happens
171
- const bestScore = Math.min(englishSimilarityScore, localizedSimilarityScore, englishMuscleGroupSimilarityScore + muscleGroupMatchPenalty, muscleGroupSimilarityScore + muscleGroupMatchPenalty);
172
- if (fuseScoreToSimilarity(englishSimilarityScore) !== 'not-similar') {
173
- accu.push(Object.assign(Object.assign({}, e), { searchStringScore: bestScore }));
174
- return accu;
175
- }
176
- if (fuseScoreToSimilarity(localizedSimilarityScore) !== 'not-similar') {
177
- accu.push(Object.assign(Object.assign({}, e), { searchStringScore: bestScore }));
178
- return accu;
179
- }
180
- if (['exact', 'extremely-similar'].includes(fuseScoreToSimilarity(englishMuscleGroupSimilarityScore))) {
181
- accu.push(Object.assign(Object.assign({}, e), { searchStringScore: bestScore }));
182
- return accu;
183
- }
184
- if (['exact', 'extremely-similar'].includes(fuseScoreToSimilarity(muscleGroupSimilarityScore))) {
185
- accu.push(Object.assign(Object.assign({}, e), { searchStringScore: bestScore }));
186
- return accu;
187
- }
188
- if (e.is_custom && 'custom'.includes(searchableSearchText)) {
189
- accu.push(e);
190
- return accu;
191
- }
192
- const splitSearch = searchableSearchText.split(' ');
193
- const splitTitle = [
194
- searchableLocalizedTitle,
195
- searchableEnglishTitle,
196
- searchableTags,
197
- searchableAka,
198
- searchableMisspelledDumbbellEquipment,
199
- ]
200
- .join(' ')
201
- .split(' ');
202
- // Find:
203
- // title ['arnold', 'press', '(barbell)']
204
- // search ['press', 'bar']
205
- if ((0, lodash_1.intersectionWith)(splitSearch, splitTitle, (search, title) => title.includes(search)).length === splitSearch.length) {
206
- accu.push(e);
207
- return accu;
208
- }
209
- return accu;
210
- }, []);
93
+ return matches;
211
94
  };
212
- const orderExercisesWithSearchText = (searchText, exercises) => {
95
+ const orderExercisesWithSearchText = (searchText, exerciseMatches) => {
213
96
  if (searchText.length === 0) {
214
- return exercises;
97
+ return exerciseMatches;
215
98
  }
216
- return exercises
217
- .reduce((accu, currentExercise) => {
218
- const lowercaseTitle = currentExercise.title.toLowerCase();
99
+ return exerciseMatches
100
+ .reduce((accu, currentMatch) => {
101
+ const lowercaseTitle = currentMatch.exercise.title.toLowerCase();
219
102
  if (lowercaseTitle.startsWith(searchText)) {
220
103
  accu.push({
221
- exercise: currentExercise,
222
- weight: currentExercise.priority * 2,
104
+ exercise: currentMatch,
105
+ weight: currentMatch.exercise.priority * 2,
223
106
  });
224
107
  return accu;
225
108
  }
226
109
  accu.push({
227
- exercise: currentExercise,
228
- weight: currentExercise.priority,
110
+ exercise: currentMatch,
111
+ weight: currentMatch.exercise.priority,
229
112
  });
230
113
  return accu;
231
114
  }, [])
package/built/index.d.ts CHANGED
@@ -1198,6 +1198,8 @@ export interface OutstandingInvitesForCoachTeamResponse {
1198
1198
  invites: CoachTeamInvite[];
1199
1199
  }
1200
1200
  export interface HevyTrainerProgram {
1201
+ created_at: string;
1202
+ updated_at: string;
1201
1203
  title: string;
1202
1204
  level: TrainingLevel;
1203
1205
  goal: TrainingGoal;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hevy-shared",
3
- "version": "1.0.710",
3
+ "version": "1.0.712",
4
4
  "description": "",
5
5
  "main": "built/index.js",
6
6
  "types": "built/index.d.ts",
@@ -35,7 +35,6 @@
35
35
  "typescript": "5.8.2"
36
36
  },
37
37
  "dependencies": {
38
- "fuse.js": "^7.0.0",
39
38
  "lodash": "^4.17.21"
40
39
  }
41
40
  }