Skip to content

Commit d90a090

Browse files
committed
Removes URL hash (fragment) for part 1 of a challenge
1 parent d4fc1d1 commit d90a090

File tree

4 files changed

+40
-16
lines changed

4 files changed

+40
-16
lines changed

src/components/challenges/PartsTimeline.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import cn from 'classnames';
33

44
import * as css from './PartsTimeline.module.css';
55
import { Link } from 'gatsby';
6+
import { buildPartHash } from '../../utils';
67

78
const PartsTimeline = ({
89
className,
@@ -21,7 +22,7 @@ const PartsTimeline = ({
2122
[css.seen]: index <= currentPartIndex,
2223
[css.last]: index === currentPartIndex
2324
})}>
24-
<Link to={`#part-${index + 1}`} onClick={onSelection}>
25+
<Link to={buildPartHash(index)} onClick={onSelection}>
2526
{part.title}
2627
</Link>
2728
</li>
@@ -31,15 +32,15 @@ const PartsTimeline = ({
3132
<div className={css.navigation}>
3233
{currentPartIndex > 0 && (
3334
<Link
34-
to={`#part-${currentPartIndex}`}
35+
to={buildPartHash(currentPartIndex - 1)}
3536
className={css.navButton}
3637
onClick={onSelection}>
3738
Previous
3839
</Link>
3940
)}
4041
{currentPartIndex < parts.length - 1 && (
4142
<Link
42-
to={`#part-${currentPartIndex + 2}`}
43+
to={buildPartHash(currentPartIndex + 1)}
4344
className={css.navButton}
4445
onClick={onSelection}>
4546
Next

src/components/tracks/OverviewTimeline.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import cn from 'classnames';
33
import { Link } from 'gatsby';
44

55
import { usePersistScrollPosition } from '../../hooks';
6+
import { buildPartHash } from '../../utils';
67

78
import * as css from './OverviewTimeline.module.css';
89

@@ -29,7 +30,7 @@ const usePaths = (chapters, track, trackPosition) => {
2930
const nextVideo = flatTrack[currentIndex + 1];
3031
const computePath = (video) => {
3132
if (video) {
32-
const hash = video.isMultiPart ? `#part-${video.partIndex + 1}` : '';
33+
const hash = video.isMultiPart ? buildPartHash(video.partIndex) : '';
3334
return {
3435
...video,
3536
path: `/tracks/${track.slug}/${video.slug}${hash}`
@@ -137,7 +138,9 @@ const ChapterSection = memo(
137138
[css.last]: isLastVideo && partIndex === currentPartIndex
138139
})}>
139140
<Link
140-
to={`${trackPath}/${video.slug}#part-${partIndex + 1}`}
141+
to={`${trackPath}/${video.slug}${buildPartHash(
142+
partIndex
143+
)}`}
141144
onClick={onSelection}>
142145
{video.title} - {part.title}
143146
</Link>

src/hooks/index.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,27 @@ export const useIsFirstRender = () => {
139139
};
140140

141141
/**
142-
* If the current URL (and hash value) corresponds to a part from a multi-part
143-
* coding challenge (within a challenge page or a track page), this hook returns
144-
* the zero-based index of this part, else it returns 0.
142+
* If the current URL hash value (fragment) matches a part number (from a
143+
* multi-part coding challenge), this hook returns the zero-based index of this
144+
* part.
145145
*
146-
* The hash value should look like `#part-3`. If the part number if larger than
147-
* the `totalParts` parameter, the returned index corresponds to the last part
148-
* of the challenge (`totalParts - 1`).
146+
* If the hash value doesn't match the format `#part-{partNumber}` where
147+
* `0 <= partNumber <= partsCount`, this hook returns 0.
148+
*
149+
* @param partsCount {number} total number of parts of the challenge (1 if
150+
* the challenge is not multi-part)
149151
*
150-
* @param totalParts {number} total number of parts of the challenge (1 if the
151-
* challenge is not multi-part)
152-
*
153152
* @returns {number} challenge part index
153+
*
154+
* @example
155+
* with hash "#part-1", useChallengePartIndex(3) === 0
156+
* with hash "#part-3", useChallengePartIndex(3) === 2
157+
* with hash "#part-8", useChallengePartIndex(2) === 0;
158+
* with hash "#part-abc", useChallengePartIndex(3) === 0
154159
*/
155-
export const useChallengePartIndex = (totalParts) => {
160+
export const useChallengePartIndex = (partsCount) => {
156161
const { hash } = useLocation();
157162
const [match, partNumberStr] = hash.match(/#part-([1-9][0-9]*)/) || [false];
158-
return match ? Math.min(parseInt(partNumberStr), totalParts) - 1 : 0;
163+
const partIndex = match ? parseInt(partNumberStr) - 1 : 0;
164+
return partIndex < partsCount ? partIndex : 0;
159165
};

src/utils/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,17 @@ export const randomElement = (array) => {
7777
const index = Math.floor(Math.random() * array.length);
7878
return array[index];
7979
};
80+
81+
/**
82+
* Creates a URL hash value (fragment) refering to a specific part of a
83+
* multi-part coding challenge. Part 1 of a challenge has no hash value.
84+
*
85+
* @param partIndex {number} Zero-based part index
86+
*
87+
* @example
88+
* buildPartHash(0) === ""
89+
* buildPartHash(1) === "#part-2"
90+
*/
91+
export const buildPartHash = (partIndex) => {
92+
return partIndex > 0 ? `#part-${partIndex + 1}` : '';
93+
};

0 commit comments

Comments
 (0)