Skip to content

Commit

Permalink
refactor(comments): allow for variable comment depth
Browse files Browse the repository at this point in the history
  • Loading branch information
Zweihander-Main committed Nov 8, 2023
1 parent d17aabf commit f2ce5b8
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 63 deletions.
54 changes: 26 additions & 28 deletions src/__tests__/comments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,32 @@ const storyCommentsDataWithTwoKids: HNApiResponse = {
id: 5,
};

const mockC1L1Comment: HNApiResponse = {
const mockP1D1Comment: HNApiResponse = {
kids: [12],
id: 11,
};

const mockC1L2Comment: HNApiResponse = {
const mockP1D2Comment: HNApiResponse = {
kids: [13],
id: 12,
};

const mockC2L1Comment: HNApiResponse = {
const mockP2D1Comment: HNApiResponse = {
kids: [22],
id: 21,
};

const mockC2L2Comment: HNApiResponse = {
const mockP2D2Comment: HNApiResponse = {
kids: [],
id: 22,
};

const mockC1L1CommentWithNoKids: HNApiResponse = {
const mockP1D1CommentWithNoKids: HNApiResponse = {
kids: [],
id: 41,
};

const mockC2L1CommentWithNoKids: HNApiResponse = {
const mockP2D1CommentWithNoKids: HNApiResponse = {
kids: [],
id: 51,
};
Expand All @@ -71,22 +71,22 @@ describe('fetchCommentData', () => {
fetchHNApiObjectData: jest.fn(),
}));
fetchHNApiObjectData.mockImplementation((input) => {
let result: HNApiResponse | null = null;
let result: HNApiResponse | undefined;
switch (input) {
case '1':
result = storyCommentsData;
break;
case '11':
result = mockC1L1Comment;
result = mockP1D1Comment;
break;
case '12':
result = mockC1L2Comment;
result = mockP1D2Comment;
break;
case '21':
result = mockC2L1Comment;
result = mockP2D1Comment;
break;
case '22':
result = mockC2L2Comment;
result = mockP2D2Comment;
break;
case '3':
result = storyCommentsDataWithNoKids;
Expand All @@ -95,16 +95,14 @@ describe('fetchCommentData', () => {
result = storyCommentsDataWithOneKids;
break;
case '41':
result = mockC1L1CommentWithNoKids;
result = mockP1D1CommentWithNoKids;
break;
case '5':
result = storyCommentsDataWithTwoKids;
break;
case '51':
result = mockC2L1CommentWithNoKids;
result = mockP2D1CommentWithNoKids;
break;
default:
result = null;
}
return Promise.resolve(result);
});
Expand All @@ -125,36 +123,36 @@ describe('fetchCommentData', () => {
const result = await fetch.addCommentsToStory([sampleDataHit]);

expect(fetch.fetchHNApiObjectData).toHaveBeenCalled();
expect(result && result[0].c1L1).toEqual(mockC1L1Comment);
expect(result && result[0].c1L2).toEqual(mockC1L2Comment);
expect(result && result[0].c2L1).toEqual(mockC2L1Comment);
expect(result && result[0].c2L2).toEqual(mockC2L2Comment);
expect(result && result[0].p1D1).toEqual(mockP1D1Comment);
expect(result && result[0].p1D2).toEqual(mockP1D2Comment);
expect(result && result[0].p2D1).toEqual(mockP2D1Comment);
expect(result && result[0].p2D2).toEqual(mockP2D2Comment);
});

it('should fetch expected comments with no children', async () => {
const result = await fetch.addCommentsToStory([
{ ...sampleDataHit, objectID: '3' },
]);
expect(result && result[0].c1L1).toEqual(undefined);
expect(result && result[0].c2L1).toEqual(undefined);
expect(result && result[0].p1D1).toEqual(undefined);
expect(result && result[0].p2D1).toEqual(undefined);
});

it('should fetch expected comments with one child', async () => {
const result = await fetch.addCommentsToStory([
{ ...sampleDataHit, objectID: '4' },
]);
expect(result && result[0].c1L1).toEqual(mockC1L1CommentWithNoKids);
expect(result && result[0].c1L2).toEqual(undefined);
expect(result && result[0].c2L1).toEqual(undefined);
expect(result && result[0].p1D1).toEqual(mockP1D1CommentWithNoKids);
expect(result && result[0].p1D2).toEqual(undefined);
expect(result && result[0].p2D1).toEqual(undefined);
});

it('should fetch expected comments with two first level children', async () => {
const result = await fetch.addCommentsToStory([
{ ...sampleDataHit, objectID: '5' },
]);
expect(result && result[0].c1L1).toEqual(mockC1L1CommentWithNoKids);
expect(result && result[0].c1L2).toEqual(undefined);
expect(result && result[0].c2L1).toEqual(mockC2L1CommentWithNoKids);
expect(result && result[0].c2L2).toEqual(undefined);
expect(result && result[0].p1D1).toEqual(mockP1D1CommentWithNoKids);
expect(result && result[0].p1D2).toEqual(undefined);
expect(result && result[0].p2D1).toEqual(mockP2D1CommentWithNoKids);
expect(result && result[0].p2D2).toEqual(undefined);
});
});
49 changes: 26 additions & 23 deletions src/comments.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from 'axios';
import { THROTTLE_TIME } from './constants';
import { COMMENT_DEPTH, COMMENT_PARENTS, THROTTLE_TIME } from './constants';
import {
AlgoliaSearchHit,
StoryWithComments,
Expand All @@ -24,35 +24,38 @@ export const fetchHNApiObjectData = async (objectID: string) => {
return response.data;
} catch (error) {
console.error(`Error fetching HN API data for ${objectID}:`, error);
return null;
return;
}
};

/**
* @param id id of the root story
*
* Note that returned object is in format p#d# where p is the parent comment
* and d is the depth of the comment. For example, p1d1 is the first comment
* on the root story, p2d1 is the first comment on the first comment on the
* root story, and so on.
*/
const fetchCommentDataForStory = async (id: string) => {
const comments: Comments = {};
const rootStory = await fetchHNApiObjectData(id);
if (!rootStory || !rootStory?.kids?.[0]) return {} as Comments;
if (!rootStory || !rootStory?.kids?.[0]) return comments;

const [comment1Level1, comment2Level1] = await Promise.all(
rootStory.kids
.slice(0, 2)
.map((kid) => fetchHNApiObjectData(kid.toString())) || []
);

const comment1Level2 =
comment1Level1 &&
comment1Level1?.kids?.[0] &&
(await fetchHNApiObjectData(comment1Level1.kids[0].toString()));
const comment2Level2 =
comment1Level1 &&
comment2Level1?.kids?.[0] &&
(await fetchHNApiObjectData(comment2Level1.kids[0].toString()));
for (let p = 1, len = COMMENT_PARENTS; p <= len; p++) {
const parentIdToFetch = rootStory.kids[p - 1]?.toString();
if (parentIdToFetch) {
comments[`p${p}D1`] = await fetchHNApiObjectData(parentIdToFetch);
for (let d = 2, len = COMMENT_DEPTH; d <= len; d++) {
const childIdToFetch =
comments[`p${p}D${d - 1}`]?.kids?.[0]?.toString();
if (childIdToFetch) {
comments[`p${p}D${d}`] =
await fetchHNApiObjectData(childIdToFetch);
}
}
}
}

const comments: Comments = {
c1L1: comment1Level1 || undefined,
c1L2: comment1Level2 || undefined,
c2L1: comment2Level1 || undefined,
c2L2: comment2Level2 || undefined,
};
return comments;
};

Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const COMMENT_PARENTS = 2;
export const COMMENT_DEPTH = 2;

export const TIMESTAMP_HOUR = 10;
export const TIMESTAMP_MINUTE = 0;

Expand Down
16 changes: 8 additions & 8 deletions src/templates/rss.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@
</span>
</div>
{% if num_comments > 0 %}
{% if c1L1 %}
{% if p1D1 %}
<div style=" color: #ffffff; padding: 10px; width: auto; background-color: #333333; max-width: 100%; margin: 0 10px 0 10px; border-left: 2px solid red;">
{{c1L1.text}}&nbsp;-&nbsp;<span style="color: red; font-weight: bold;">{{c1L1.by}}</span>
{{p1D1.text}}&nbsp;-&nbsp;<span style="color: red; font-weight: bold;">{{p1D1.by}}</span>
</div>
{% endif %}
{% if c1L2 %}
{% if p1D2 %}
<div style=" color: #ffffff; padding: 10px; width: auto; background-color:#333333; max-width: 100%; margin: 0 10px 0 20px; border-left: 2px solid purple; ">
{{c1L2.text}} - <span style="color: purple; font-weight: bold;">{{c1L2.by}}</span>
{{p1D2.text}} - <span style="color: purple; font-weight: bold;">{{p1D2.by}}</span>
</div>
{% endif %}
{% if c2L1 %}
{% if p2D1 %}
<div style=" color: #ffffff; padding: 10px; width: auto; background-color: #333333; max-width:100%; margin: 0 10px 0 10px; border-left: 2px solid red; ">
{{c2L1.text}} - <span style="color: red;font-weight: bold;">{{c2L1.by}}</span>
{{p2D1.text}} - <span style="color: red;font-weight: bold;">{{p2D1.by}}</span>
</div>
{% endif %}
{% if c2L2 %}
{% if p2D2 %}
<div style=" color: #ffffff; padding: 10px; width: auto; background-color: #333333; max-width: 100%; margin: 0 10px 0 20px; border-left: 2px solid purple; ">
{{c2L2.text}} - <span style="color: purple;font-weight: bold;">{{c2L2.by}}</span>
{{p2D2.text}} - <span style="color: purple;font-weight: bold;">{{p2D2.by}}</span>
</div>
{% endif %}
{% endif %}
Expand Down
5 changes: 1 addition & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ export interface HNApiResponse {
}

export interface Comments {
c1L1?: HNApiResponse;
c2L1?: HNApiResponse;
c1L2?: HNApiResponse;
c2L2?: HNApiResponse;
[key: `p${number}D${number}`]: HNApiResponse | undefined;
}

export type StoryWithComments = AlgoliaSearchHit & Comments;
Expand Down

0 comments on commit f2ce5b8

Please sign in to comment.