Skip to content

Commit

Permalink
Determine most recent highlighted annotation by its updated date
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya committed Apr 17, 2024
1 parent 962f69e commit 8230c26
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 15 deletions.
31 changes: 22 additions & 9 deletions src/sidebar/components/ThreadList.tsx
Expand Up @@ -187,6 +187,7 @@ export default function ThreadList({ threads }: ThreadListProps) {
const store = useSidebarStore();
const editing = store.countDrafts() > 0;
const highlightedAnnotations = store.highlightedAnnotations();
const allAnnotations = store.allAnnotations();

// Get the `$tag` of the most recently created unsaved annotation.
const newAnnotationTag = (() => {
Expand All @@ -211,20 +212,32 @@ export default function ThreadList({ threads }: ThreadListProps) {
}
}, [store, newAnnotationTag]);

const mostRecentlyHighlightedAnnotationId = useMemo(
// If multiple highlighted annotations exist, assume that the last one in
// the list is the most recent.
() => highlightedAnnotations[highlightedAnnotations.length - 1],
[highlightedAnnotations],
);
const mostRecentHighlightedAnnotationId = useMemo(() => {
const highlightedAnnos = allAnnotations.filter(
anno => anno.id && highlightedAnnotations.includes(anno.id),
);
// Get the annotation with most recent updated field, which contains a
// date in ISO format. This means their alphabetical and chronological
// orders match.
const mostRecentHighlightedAnnotation =
highlightedAnnos.reduce<Annotation | null>(
(mostRecent, current) =>
!mostRecent || mostRecent.updated < current.updated
? current
: mostRecent,
null,
);

return mostRecentHighlightedAnnotation?.id;
}, [allAnnotations, highlightedAnnotations]);

// Scroll to the most recently highlighted annotation, unless creating/editing
// another annotation
useEffect(() => {
if (!editing && mostRecentlyHighlightedAnnotationId) {
setScrollToId(mostRecentlyHighlightedAnnotationId);
if (!editing && mostRecentHighlightedAnnotationId) {
setScrollToId(mostRecentHighlightedAnnotationId);
}
}, [editing, mostRecentlyHighlightedAnnotationId]);
}, [editing, mostRecentHighlightedAnnotationId]);

// Effect to scroll a particular thread into view. This is mainly used to
// scroll a newly created annotation into view.
Expand Down
65 changes: 59 additions & 6 deletions src/sidebar/components/test/ThreadList-test.js
Expand Up @@ -57,6 +57,7 @@ describe('ThreadList', () => {
unsavedAnnotations: sinon.stub().returns([]),
countDrafts: sinon.stub().returns(0),
highlightedAnnotations: sinon.stub().returns([]),
allAnnotations: sinon.stub().returns([]),
};

fakeTopThread = {
Expand Down Expand Up @@ -193,13 +194,65 @@ describe('ThreadList', () => {
assert.notCalled(fakeScrollTop);
});

it('should set the scroll container `scrollTop` to first highlighted annotation', () => {
fakeStore.highlightedAnnotations.returns(['t2', 't3']);
createComponent();
[
{
annotationUpdates: {
t2: '2024-01-01T10:40:00',
t3: '2024-01-01T10:41:00', // Most recent
t4: '2024-01-01T10:39:00',
},
// The most recent highlighted annotation is the third.
// At default height (200) should be at 400px.
expectedScrollTop: 400,
},
{
annotationUpdates: {
t1: '2024-01-01T10:42:00', // Most recent
t3: '2024-01-01T10:39:00',
t4: '2024-01-01T10:39:00',
},
// The most recent highlighted annotation is the first.
// At default height (200) should be at 0px.
expectedScrollTop: 0,
},
{
annotationUpdates: {
t1: '2024-01-01T10:42:00',
t3: '2024-01-01T10:39:00',
t4: '2024-01-01T10:51:00', // Most recent
},
// The most recent highlighted annotation is the fourth.
// At default height (200) should be at 600px.
expectedScrollTop: 600,
},
{
annotationUpdates: {
t1: '2024-01-01T10:42:00',
t3: '2024-01-01T10:39:00',
t2: '2024-01-01T10:51:00', // Most recent
},
// The most recent highlighted annotation is the second.
// At default height (200) should be at 200px.
expectedScrollTop: 200,
},
].forEach(({ annotationUpdates, expectedScrollTop }) => {
it('should set the scroll container `scrollTop` to most recent highlighted annotation', () => {
fakeStore.highlightedAnnotations.returns(
Object.keys(annotationUpdates),
);
fakeStore.allAnnotations.returns([
{}, // Discarded
{ id: 't1', updated: annotationUpdates.t1 },
{ id: 't2', updated: annotationUpdates.t2 },
{ id: 't3', updated: annotationUpdates.t3 },
{ id: 't4', updated: annotationUpdates.t4 },
{ id: 't5', updated: annotationUpdates.t5 },
]);

createComponent();

// The last highlighted annotation is the third in the collection of
// threads. At default height (200) should be at 400px.
assert.calledWith(fakeScrollTop, 400);
assert.calledWith(fakeScrollTop, expectedScrollTop);
});
});
});

Expand Down

0 comments on commit 8230c26

Please sign in to comment.