hevy-shared 1.0.844 → 1.0.845
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/built/index.d.ts +3 -0
- package/built/tests/utils.test.js +60 -0
- package/built/utils.d.ts +10 -1
- package/built/utils.js +60 -1
- package/package.json +2 -1
package/built/index.d.ts
CHANGED
|
@@ -569,6 +569,9 @@ export interface UserProfile {
|
|
|
569
569
|
weekly_workout_durations: WeeklyWorkoutDuration[];
|
|
570
570
|
mutual_followers: PreviewMutualUser[];
|
|
571
571
|
}
|
|
572
|
+
export interface UserWorkoutStreakCount {
|
|
573
|
+
streak_count: number;
|
|
574
|
+
}
|
|
572
575
|
export interface PublicUserProfile {
|
|
573
576
|
private_profile: boolean;
|
|
574
577
|
username: string;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
3
7
|
const utils_1 = require("../utils");
|
|
4
8
|
describe('utils', () => {
|
|
5
9
|
describe('isValidUsername', () => {
|
|
@@ -639,4 +643,60 @@ describe('utils', () => {
|
|
|
639
643
|
expect((0, utils_1.getYoutubeVideoId)('https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstleyVEVO')).toBe('dQw4w9WgXcQ');
|
|
640
644
|
});
|
|
641
645
|
});
|
|
646
|
+
describe('calculateCurrentWeekStreak', () => {
|
|
647
|
+
it("Doesn't include gaps in the streak count", () => {
|
|
648
|
+
const workouts = [
|
|
649
|
+
{ start_time: (0, dayjs_1.default)('2025-11-05').unix() },
|
|
650
|
+
{ start_time: (0, dayjs_1.default)('2025-11-02').unix() },
|
|
651
|
+
{ start_time: (0, dayjs_1.default)('2025-10-31').unix() },
|
|
652
|
+
{ start_time: (0, dayjs_1.default)('2025-10-23').unix() },
|
|
653
|
+
{ start_time: (0, dayjs_1.default)('2025-10-10').unix() },
|
|
654
|
+
];
|
|
655
|
+
const firstWeekday = 'sunday';
|
|
656
|
+
const untilUnix = (0, dayjs_1.default)('2025-11-06').unix();
|
|
657
|
+
expect((0, utils_1.calculateCurrentWeekStreak)(workouts, firstWeekday, untilUnix)).toBe(3);
|
|
658
|
+
});
|
|
659
|
+
it("Calculates a potentially non-zero streak when the user hasn't worked out this week yet", () => {
|
|
660
|
+
const workouts = [
|
|
661
|
+
{ start_time: (0, dayjs_1.default)('2025-10-31').unix() },
|
|
662
|
+
{ start_time: (0, dayjs_1.default)('2025-10-23').unix() },
|
|
663
|
+
{ start_time: (0, dayjs_1.default)('2025-10-20').unix() },
|
|
664
|
+
{ start_time: (0, dayjs_1.default)('2025-10-14').unix() + 1 },
|
|
665
|
+
{ start_time: (0, dayjs_1.default)('2025-10-12').unix() },
|
|
666
|
+
];
|
|
667
|
+
const firstWeekday = 'monday';
|
|
668
|
+
const untilUnix = (0, dayjs_1.default)('2025-11-06').unix();
|
|
669
|
+
expect((0, utils_1.calculateCurrentWeekStreak)(workouts, firstWeekday, untilUnix)).toBe(4);
|
|
670
|
+
});
|
|
671
|
+
it('Includes this week in the streak count if the user worked out this week', () => {
|
|
672
|
+
const workouts = [
|
|
673
|
+
{ start_time: (0, dayjs_1.default)('2025-11-05').unix() },
|
|
674
|
+
{ start_time: (0, dayjs_1.default)('2025-10-31').unix() },
|
|
675
|
+
{ start_time: (0, dayjs_1.default)('2025-10-23').unix() },
|
|
676
|
+
{ start_time: (0, dayjs_1.default)('2025-10-20').unix() },
|
|
677
|
+
{ start_time: (0, dayjs_1.default)('2025-10-14').unix() },
|
|
678
|
+
{ start_time: (0, dayjs_1.default)('2025-10-07').unix() },
|
|
679
|
+
];
|
|
680
|
+
const firstWeekday = 'monday';
|
|
681
|
+
const untilUnix = (0, dayjs_1.default)('2025-11-06').unix();
|
|
682
|
+
expect((0, utils_1.calculateCurrentWeekStreak)(workouts, firstWeekday, untilUnix)).toBe(5);
|
|
683
|
+
});
|
|
684
|
+
it("Returns 0 if the user hasn't worked out", () => {
|
|
685
|
+
const workouts = [];
|
|
686
|
+
const firstWeekday = 'friday';
|
|
687
|
+
const untilUnix = (0, dayjs_1.default)('2025-10-10').unix();
|
|
688
|
+
expect((0, utils_1.calculateCurrentWeekStreak)(workouts, firstWeekday, untilUnix)).toBe(0);
|
|
689
|
+
});
|
|
690
|
+
it('Returns 0 if the user has not worked out this week and last week', () => {
|
|
691
|
+
const workouts = [
|
|
692
|
+
{ start_time: (0, dayjs_1.default)('2025-10-23').unix() },
|
|
693
|
+
{ start_time: (0, dayjs_1.default)('2025-10-20').unix() },
|
|
694
|
+
{ start_time: (0, dayjs_1.default)('2025-10-13').unix() },
|
|
695
|
+
{ start_time: (0, dayjs_1.default)('2025-10-07').unix() },
|
|
696
|
+
];
|
|
697
|
+
const firstWeekday = 'sunday';
|
|
698
|
+
const untilUnix = (0, dayjs_1.default)('2025-11-05').unix();
|
|
699
|
+
expect((0, utils_1.calculateCurrentWeekStreak)(workouts, firstWeekday, untilUnix)).toBe(0);
|
|
700
|
+
});
|
|
701
|
+
});
|
|
642
702
|
});
|
package/built/utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Dayjs } from 'dayjs';
|
|
2
|
+
import { BaseExerciseTemplate, ExerciseType, FormatAtText, Result, SetType, StrengthLevel, UserExerciseSet, UserFacingSetIndicator, Weekday } from '.';
|
|
2
3
|
/**
|
|
3
4
|
* Doesn't matter what you throw in the function it'll
|
|
4
5
|
* always return a number. Non number values will return
|
|
@@ -240,4 +241,12 @@ export declare const isVersionAGreaterOrEqualToVersionB: (versionA: string, vers
|
|
|
240
241
|
export declare const splitAtUsernamesAndLinks: (text: string) => FormatAtText[];
|
|
241
242
|
export declare const validateYoutubeUrl: (url: string) => boolean;
|
|
242
243
|
export declare const getYoutubeVideoId: (url: string) => string | undefined;
|
|
244
|
+
/**@param workouts must be sorted descending by start_time */
|
|
245
|
+
export declare const calculateCurrentWeekStreak: (workouts: {
|
|
246
|
+
start_time: number;
|
|
247
|
+
}[], firstWeekday: Weekday, untilUnix?: number) => number;
|
|
248
|
+
export declare const startOfWeek: (d: Dayjs, firstDayOfWeek: Weekday) => Dayjs;
|
|
249
|
+
export declare const weekdayNumberMap: {
|
|
250
|
+
[key in Weekday]: number;
|
|
251
|
+
};
|
|
243
252
|
export {};
|
package/built/utils.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getYoutubeVideoId = exports.validateYoutubeUrl = exports.splitAtUsernamesAndLinks = exports.isVersionAGreaterOrEqualToVersionB = exports.generateUserGroupValue = exports.generateUserGroup = exports.isBaseExerciseTemplate = exports.getStrengthLevelFromPercentile = exports.numberToLocaleString = exports.numberWithCommas = exports.setVolume = exports.oneRepMax = exports.oneRepMaxPercentageMap = exports.getEstimatedExercisesDurationSeconds = exports.ESTIMATED_REST_TIMER_DURATION = exports.ESTIMATED_SET_DURATION = exports.UserFacingIndicatorToSetIndicator = exports.workoutSetCount = exports.userExerciseSetWeight = exports.workoutDistanceMeters = exports.workoutReps = exports.workoutDurationSeconds = exports.removeAccents = exports.getClosestDataPointAroundTargetDate = exports.getClosestDataPointBeforeTargetDate = exports.findMapped = exports.stringToNumber = exports.forceStringToNumber = exports.formatDurationInput = exports.isValidFormattedTime = exports.isWholeNumber = exports.isNumber = exports.isValidUuid = exports.isValidPhoneNumber = exports.isValidWebUrl = exports.URL_REGEX = exports.isValidEmail = exports.secondsToWordFormatMinutes = exports.secondsToWordFormat = exports.secondsToClockFormat = exports.secondsToClockParts = exports.isValidUsername = exports.roundToWholeNumber = exports.roundToOneDecimal = exports.roundToTwoDecimal = exports.divide = exports.clampNumber = exports.num = void 0;
|
|
6
|
+
exports.startOfWeek = exports.calculateCurrentWeekStreak = exports.getYoutubeVideoId = exports.validateYoutubeUrl = exports.splitAtUsernamesAndLinks = exports.isVersionAGreaterOrEqualToVersionB = exports.generateUserGroupValue = exports.generateUserGroup = exports.isBaseExerciseTemplate = exports.getStrengthLevelFromPercentile = exports.numberToLocaleString = exports.numberWithCommas = exports.setVolume = exports.oneRepMax = exports.oneRepMaxPercentageMap = exports.getEstimatedExercisesDurationSeconds = exports.ESTIMATED_REST_TIMER_DURATION = exports.ESTIMATED_SET_DURATION = exports.UserFacingIndicatorToSetIndicator = exports.workoutSetCount = exports.userExerciseSetWeight = exports.workoutDistanceMeters = exports.workoutReps = exports.workoutDurationSeconds = exports.removeAccents = exports.getClosestDataPointAroundTargetDate = exports.getClosestDataPointBeforeTargetDate = exports.findMapped = exports.stringToNumber = exports.forceStringToNumber = exports.formatDurationInput = exports.isValidFormattedTime = exports.isWholeNumber = exports.isNumber = exports.isValidUuid = exports.isValidPhoneNumber = exports.isValidWebUrl = exports.URL_REGEX = exports.isValidEmail = exports.secondsToWordFormatMinutes = exports.secondsToWordFormat = exports.secondsToClockFormat = exports.secondsToClockParts = exports.isValidUsername = exports.roundToWholeNumber = exports.roundToOneDecimal = exports.roundToTwoDecimal = exports.divide = exports.clampNumber = exports.num = void 0;
|
|
7
|
+
exports.weekdayNumberMap = void 0;
|
|
8
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
4
9
|
/**
|
|
5
10
|
* Doesn't matter what you throw in the function it'll
|
|
6
11
|
* always return a number. Non number values will return
|
|
@@ -716,3 +721,57 @@ const getYoutubeVideoId = (url) => {
|
|
|
716
721
|
return match && match[1] && match[1].length === 11 ? match[1] : undefined;
|
|
717
722
|
};
|
|
718
723
|
exports.getYoutubeVideoId = getYoutubeVideoId;
|
|
724
|
+
/**@param workouts must be sorted descending by start_time */
|
|
725
|
+
const calculateCurrentWeekStreak = (workouts, firstWeekday, untilUnix = (0, dayjs_1.default)().unix()) => {
|
|
726
|
+
let streakCount = 0;
|
|
727
|
+
let weekStart = (0, exports.startOfWeek)(dayjs_1.default.unix(untilUnix), firstWeekday).subtract(1, 'week');
|
|
728
|
+
let weekEnd = weekStart.add(1, 'week');
|
|
729
|
+
let i = 0;
|
|
730
|
+
/** Iterate through the workouts from most recent to least and keep track of the week-long
|
|
731
|
+
* sliding window in which there needs to be a workout present in order for the streak to count.
|
|
732
|
+
* If there is a workout present during this window, move the window into the past by a week
|
|
733
|
+
* and keep iterating through the workouts. If there are no workouts during this window,
|
|
734
|
+
* the streak count is not incremented.
|
|
735
|
+
*/
|
|
736
|
+
while (i < workouts.length) {
|
|
737
|
+
const workoutStart = workouts[i].start_time;
|
|
738
|
+
if (workoutStart >= weekStart.unix() && workoutStart <= weekEnd.unix()) {
|
|
739
|
+
streakCount += 1;
|
|
740
|
+
weekStart = weekStart.subtract(1, 'week');
|
|
741
|
+
weekEnd = weekEnd.subtract(1, 'week');
|
|
742
|
+
}
|
|
743
|
+
else if (workoutStart < weekStart.unix()) {
|
|
744
|
+
/** This workout is before the sliding window. We don't get to this point unless
|
|
745
|
+
* the user didn't workout during the window. So that means the streak is over
|
|
746
|
+
*/
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
i += 1;
|
|
750
|
+
}
|
|
751
|
+
// check if the current week has any workouts
|
|
752
|
+
weekStart = (0, exports.startOfWeek)(dayjs_1.default.unix(untilUnix), firstWeekday);
|
|
753
|
+
weekEnd = weekStart.add(1, 'week');
|
|
754
|
+
if (workouts.find(({ start_time }) => start_time >= weekStart.unix() && start_time <= weekEnd.unix())) {
|
|
755
|
+
streakCount += 1;
|
|
756
|
+
}
|
|
757
|
+
return streakCount;
|
|
758
|
+
};
|
|
759
|
+
exports.calculateCurrentWeekStreak = calculateCurrentWeekStreak;
|
|
760
|
+
const startOfWeek = (d, firstDayOfWeek) => {
|
|
761
|
+
const firstDayOfWeekIndex = exports.weekdayNumberMap[firstDayOfWeek];
|
|
762
|
+
let testDay = d;
|
|
763
|
+
while (testDay.day() !== firstDayOfWeekIndex) {
|
|
764
|
+
testDay = testDay.subtract(1, 'day');
|
|
765
|
+
}
|
|
766
|
+
return testDay.startOf('day');
|
|
767
|
+
};
|
|
768
|
+
exports.startOfWeek = startOfWeek;
|
|
769
|
+
exports.weekdayNumberMap = {
|
|
770
|
+
sunday: 0,
|
|
771
|
+
monday: 1,
|
|
772
|
+
tuesday: 2,
|
|
773
|
+
wednesday: 3,
|
|
774
|
+
thursday: 4,
|
|
775
|
+
friday: 5,
|
|
776
|
+
saturday: 6,
|
|
777
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hevy-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.845",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "built/index.js",
|
|
6
6
|
"types": "built/index.d.ts",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"typescript": "5.8.2"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
+
"dayjs": "^1.11.19",
|
|
49
50
|
"lodash": "^4.17.21"
|
|
50
51
|
}
|
|
51
52
|
}
|