Added redux functionality for queueing/dequeueing timelines

using streaming.js, when a status comes in to the current page, it queues up using updateTimelineQueue action, it then goes to the reducer to add "queuedItems" to state (up to max:40) and to tally up all count in that timeilne state "totalQueuedItemsCount".

the dequeueTimeline action takes in a "timelineId", "expandFunc", and "optionalExpandArgs". when clicking on the "click to load more" it passes in the timelineId (e.g. "home", "community", etc.) and the "handleLoadMore" function from the timeline component. if within the range of the max: 40, it pushes them to the dom, if over the max: 40 it clears the timeline and refreshes the page/timeline to show the most recent 20 statuses. Then, it resets the "queuedItems" and "totalQueuedItemsCount" in timeline state.

if no expandFunc is added, and timeline is "home" or "community" it expands those timelines with "optionalExpandArgs". Otherwise, it queues up to any other timeline (e.g. "hashtags", etc.)
This commit is contained in:
mgabdev 2019-07-11 12:09:41 -04:00
parent c8e8618f64
commit 0356e21747
3 changed files with 99 additions and 3 deletions

View File

@ -1,10 +1,10 @@
import { connectStream } from '../stream';
import {
updateTimeline,
deleteFromTimelines,
expandHomeTimeline,
connectTimeline,
disconnectTimeline,
updateTimelineQueue,
} from './timelines';
import { updateNotificationsQueue, expandNotifications } from './notifications';
import { updateConversations } from './conversations';
@ -30,7 +30,7 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
onReceive (data) {
switch(data.event) {
case 'update':
dispatch(updateTimeline(timelineId, JSON.parse(data.payload), accept));
dispatch(updateTimelineQueue(timelineId, JSON.parse(data.payload), accept));
break;
case 'delete':
dispatch(deleteFromTimelines(data.payload));

View File

@ -1,10 +1,12 @@
import { importFetchedStatus, importFetchedStatuses } from './importer';
import api, { getLinks } from '../api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
@ -13,6 +15,8 @@ export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const MAX_QUEUED_ITEMS = 40;
export function updateTimeline(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
@ -29,6 +33,64 @@ export function updateTimeline(timeline, status, accept) {
};
};
export function updateTimelineQueue(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
status,
});
}
};
export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
return (dispatch, getState) => {
const queuedItems = getState().getIn(['timelines', timeline, 'queuedItems'], ImmutableList());
const totalQueuedItemsCount = getState().getIn(['timelines', timeline, 'totalQueuedItemsCount'], 0);
let shouldDispatchDequeue = true;
if (totalQueuedItemsCount == 0) {
return;
}
else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
queuedItems.forEach(status => {
dispatch(updateTimeline(timeline, status.toJS(), null));
});
}
else {
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timeline));
expandFunc();
}
else {
if (timeline === 'home') {
dispatch(clearTimeline(timeline));
dispatch(expandHomeTimeline(optionalExpandArgs));
}
else if (timeline === 'community') {
dispatch(clearTimeline(timeline));
dispatch(expandCommunityTimeline(optionalExpandArgs));
}
else {
shouldDispatchDequeue = false;
}
}
}
if (!shouldDispatchDequeue) return;
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
});
}
};
export function deleteFromTimelines(id) {
return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']);

View File

@ -7,6 +7,9 @@ import {
TIMELINE_EXPAND_FAIL,
TIMELINE_CONNECT,
TIMELINE_DISCONNECT,
TIMELINE_UPDATE_QUEUE,
TIMELINE_DEQUEUE,
MAX_QUEUED_ITEMS,
} from '../actions/timelines';
import {
ACCOUNT_BLOCK_SUCCESS,
@ -25,6 +28,8 @@ const initialTimeline = ImmutableMap({
isLoading: false,
hasMore: true,
items: ImmutableList(),
queuedItems: ImmutableList(), //max= MAX_QUEUED_ITEMS
totalQueuedItemsCount: 0, //used for queuedItems overflow for MAX_QUEUED_ITEMS+
});
const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => {
@ -77,6 +82,28 @@ const updateTimeline = (state, timeline, status) => {
}));
};
const updateTimelineQueue = (state, timeline, status) => {
const queuedStatuses = state.getIn([timeline, 'queuedItems'], ImmutableList());
const listedStatuses = state.getIn([timeline, 'items'], ImmutableList());
const totalQueuedItemsCount = state.getIn([timeline, 'totalQueuedItemsCount'], 0);
let alreadyExists = queuedStatuses.find(existingQueuedStatus => existingQueuedStatus.get('id') === status.get('id'));
if (!alreadyExists) alreadyExists = listedStatuses.find(existingListedStatusId => existingListedStatusId === status.get('id'));
if (alreadyExists) {
return state;
}
let newQueuedStatuses = queuedStatuses;
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
if (totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
mMap.set('queuedItems', newQueuedStatuses.push(status));
}
mMap.set('totalQueuedItemsCount', totalQueuedItemsCount + 1);
}));
};
const deleteStatus = (state, id, accountId, references, exclude_account = null) => {
state.keySeq().forEach(timeline => {
if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`)))
@ -126,6 +153,13 @@ export default function timelines(state = initialState, action) {
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
case TIMELINE_UPDATE:
return updateTimeline(state, action.timeline, fromJS(action.status));
case TIMELINE_UPDATE_QUEUE:
return updateTimelineQueue(state, action.timeline, fromJS(action.status));
case TIMELINE_DEQUEUE:
return state.update(action.timeline, initialTimeline, map => map.withMutations(mMap => {
mMap.set('queuedItems', ImmutableList())
mMap.set('totalQueuedItemsCount', 0)
}));
case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
case TIMELINE_CLEAR: