Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Determine most recent highlighted annotation by its updated date #6341

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 22 additions & 9 deletions src/sidebar/components/ThreadList.tsx
Original file line number Diff line number Diff line change
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),
);
acelaya marked this conversation as resolved.
Show resolved Hide resolved
// 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
Original file line number Diff line number Diff line change
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