Skip to content

Commit 3a22bdf

Browse files
committed
feat(ActivityStream): load previous activities until container is filled
1 parent f4af0c8 commit 3a22bdf

File tree

7 files changed

+94
-2
lines changed

7 files changed

+94
-2
lines changed

src/components/WebexActivity/WebexActivity.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ $content-left-margin: 3.5rem; //avatar width + margin
22
.activity {
33
display: flex;
44
flex-direction: column;
5+
justify-content: flex-start;
6+
width: 100%;
57
margin: 0.5rem 0;
68

79
.activity-header {

src/components/WebexActivityStream/WebexActivityStream.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {ListSeparator} from '@momentum-ui/react';
44
import {format, isToday, isSameWeek, isYesterday} from 'date-fns';
55

66
import {RoomType} from '../../adapters/RoomsAdapter';
7-
import {useActivityStream, useActivityScroll, useRoom} from '../hooks';
7+
import {useActivityStream, useActivityScroll, useOverflowActivities, useRoom} from '../hooks';
88
import {PREPEND_ACTIVITIES} from '../hooks/useActivityStream';
99
import WebexActivity from '../WebexActivity/WebexActivity';
1010

@@ -188,6 +188,7 @@ export default function WebexActivityStream({roomID}) {
188188
const {title, roomType} = useRoom(roomID);
189189
const activityStreamRef = useRef(null);
190190
const showLoader = useActivityScroll(roomID, activityStreamRef, loadPreviousActivities);
191+
const lastActivityRef = useOverflowActivities(roomID, activityStreamRef, loadPreviousActivities);
191192

192193
const personName = roomType === RoomType.DIRECT ? title : '';
193194
const activities = activitiesData.map((activity) => {
@@ -205,6 +206,7 @@ export default function WebexActivityStream({roomID}) {
205206
<div className="activity-stream" ref={activityStreamRef}>
206207
{showLoader && <div className="activity-stream-loader" />}
207208
{activities.length ? <Fragment>{activities}</Fragment> : <Greeting personName={personName} />}
209+
<div className="last-activity" ref={lastActivityRef} />
208210
</div>
209211
);
210212
}

src/components/WebexActivityStream/WebexActivityStream.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
// For Storybook, so that activity stream loads content properly
2+
#root {
3+
height: 100%;
4+
}
5+
16
.activity-stream {
27
position: relative;
38
width: 100%;
4-
height: 100px;
9+
height: 100%;
510
overflow-y: auto;
611

712
&-loader {

src/components/WebexActivityStream/__snapshots__/WebexActivityStream.test.js.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ exports[`Webex Activity Stream component Webex Activity Stream snapshots matches
186186
activityID="long"
187187
key="long"
188188
/>
189+
<div
190+
className="last-activity"
191+
/>
189192
</div>
190193
`;
191194

@@ -196,6 +199,9 @@ exports[`Webex Activity Stream component Webex Activity Stream snapshots matches
196199
<Greeting
197200
personName="Webex Component User"
198201
/>
202+
<div
203+
className="last-activity"
204+
/>
199205
</div>
200206
`;
201207

@@ -206,6 +212,9 @@ exports[`Webex Activity Stream component Webex Activity Stream snapshots matches
206212
<Greeting
207213
personName=""
208214
/>
215+
<div
216+
className="last-activity"
217+
/>
209218
</div>
210219
`;
211220

@@ -237,5 +246,8 @@ exports[`Webex Activity Stream component Webex Activity Stream snapshots matches
237246
activityID="multi-line"
238247
key="multi-line"
239248
/>
249+
<div
250+
className="last-activity"
251+
/>
240252
</div>
241253
`;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function usePreviousActivities() {
2+
return {};
3+
}

src/components/hooks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export {default as useActivity} from './useActivity';
22
export {default as useActivityScroll} from './useActivityScroll';
33
export {default as useActivityStream} from './useActivityStream';
44
export {default as usePerson} from './usePerson';
5+
export {default as useOverflowActivities} from './useOverflowActivities';
56
export {default as useRoom} from './useRoom';
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {useContext, useEffect, useRef, useState} from 'react';
2+
import {Observable} from 'rxjs';
3+
import {flatMap} from 'rxjs/operators';
4+
5+
import {AdapterContext} from '..';
6+
7+
/**
8+
* Callback function to execute once data has been fetched.
9+
*
10+
* @callback previousActivitiesCallback
11+
* @param {Array.<string|ActivityDate>} previousActivities Array of previous activities fetched
12+
* @returns undefined
13+
*/
14+
15+
/**
16+
* Custom hook that returns a reference to the DOM element of the last activity.
17+
* Requests previous activity data for the given room, and executes
18+
* the given callback with that data.
19+
* It completes once there is enough data to fill the viewport of the
20+
* parent container, finishing by scrolling into view the last activity.
21+
*
22+
* @param {string} roomID ID of the room for which to load data.
23+
* @param {object} elementRef DOM reference of the parent component.
24+
* @param {previousActivitiesCallback} callback Callback to execute once data has been fetched.
25+
* @returns {object} DOM reference to the the last activity.
26+
*/
27+
export default function useOverflowActivities(roomID, elementRef, callback) {
28+
const lastElementRef = useRef(null);
29+
const {roomsAdapter} = useContext(AdapterContext);
30+
const [scrollToLastActivity, setScrollToLastActivity] = useState(false);
31+
const [lastActivityBottom, setLastActivityBottom] = useState(0);
32+
33+
useEffect(() => {
34+
const activityStreamBoundaries = elementRef.current.getBoundingClientRect();
35+
const lastActivityBoundaries = lastElementRef.current.getBoundingClientRect();
36+
37+
// If the bottom position of the last activity is less than
38+
// the bottom position of the container that means that the last
39+
// activity is visible and there is not enough content to overflow
40+
// the container
41+
const loadMoreActivities = Observable.create((observer) => {
42+
if (lastActivityBoundaries.bottom < activityStreamBoundaries.bottom) {
43+
observer.next();
44+
} else {
45+
if (!scrollToLastActivity) {
46+
lastElementRef.current.scrollIntoView();
47+
setScrollToLastActivity(true);
48+
}
49+
50+
// Once enough content has loaded, complete
51+
observer.complete();
52+
}
53+
})
54+
.pipe(flatMap(() => roomsAdapter.getPreviousRoomActivities(roomID)))
55+
.subscribe((previousActivities) => {
56+
callback(previousActivities);
57+
setLastActivityBottom(lastActivityBoundaries.bottom);
58+
});
59+
60+
return () => {
61+
loadMoreActivities.unsubscribe();
62+
};
63+
// eslint-disable-next-line react-hooks/exhaustive-deps
64+
}, [lastActivityBottom]);
65+
66+
return lastElementRef;
67+
}

0 commit comments

Comments
 (0)