diff options
Diffstat (limited to 'app/javascript/mastodon/reducers/timelines.js')
-rw-r--r-- | app/javascript/mastodon/reducers/timelines.js | 314 |
1 files changed, 58 insertions, 256 deletions
diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js index ab756b854..2bc1c8050 100644 --- a/app/javascript/mastodon/reducers/timelines.js +++ b/app/javascript/mastodon/reducers/timelines.js @@ -18,228 +18,79 @@ import { UNFAVOURITE_SUCCESS, } from '../actions/interactions'; import { - ACCOUNT_TIMELINE_FETCH_REQUEST, - ACCOUNT_TIMELINE_FETCH_SUCCESS, - ACCOUNT_TIMELINE_FETCH_FAIL, - ACCOUNT_TIMELINE_EXPAND_REQUEST, - ACCOUNT_TIMELINE_EXPAND_SUCCESS, - ACCOUNT_TIMELINE_EXPAND_FAIL, - ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST, - ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS, - ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL, - ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST, - ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS, - ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL, ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS, } from '../actions/accounts'; -import { - CONTEXT_FETCH_SUCCESS, -} from '../actions/statuses'; import Immutable from 'immutable'; -const initialState = Immutable.Map({ - home: Immutable.Map({ - path: () => '/api/v1/timelines/home', - next: null, - isLoading: false, - online: false, - loaded: false, - top: true, - unread: 0, - items: Immutable.List(), - }), - - public: Immutable.Map({ - path: () => '/api/v1/timelines/public', - next: null, - isLoading: false, - online: false, - loaded: false, - top: true, - unread: 0, - items: Immutable.List(), - }), - - community: Immutable.Map({ - path: () => '/api/v1/timelines/public', - next: null, - params: { local: true }, - isLoading: false, - online: false, - loaded: false, - top: true, - unread: 0, - items: Immutable.List(), - }), +const initialState = Immutable.Map(); - tag: Immutable.Map({ - path: (id) => `/api/v1/timelines/tag/${id}`, - next: null, - isLoading: false, - id: null, - loaded: false, - top: true, - unread: 0, - items: Immutable.List(), - }), - - accounts_timelines: Immutable.Map(), - accounts_media_timelines: Immutable.Map(), - ancestors: Immutable.Map(), - descendants: Immutable.Map(), +const initialTimeline = Immutable.Map({ + unread: 0, + online: false, + top: true, + loaded: false, + isLoading: false, + next: false, + items: Immutable.List(), }); -const normalizeStatus = (state, status) => { - return state; -}; - const normalizeTimeline = (state, timeline, statuses, next) => { - let ids = Immutable.List(); - const loaded = state.getIn([timeline, 'loaded']); - - statuses.forEach((status, i) => { - state = normalizeStatus(state, status); - ids = ids.set(i, status.get('id')); - }); - - state = state.setIn([timeline, 'loaded'], true); - state = state.setIn([timeline, 'isLoading'], false); - - if (state.getIn([timeline, 'next']) === null) { - state = state.setIn([timeline, 'next'], next); - } - - return state.updateIn([timeline, 'items'], Immutable.List(), list => (loaded ? ids.concat(list) : ids)); + const ids = Immutable.List(statuses.map(status => status.get('id'))); + const wasLoaded = state.getIn([timeline, 'loaded']); + const hadNext = state.getIn([timeline, 'next']); + const oldIds = state.getIn([timeline, 'items'], Immutable.List()); + + return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { + mMap.set('loaded', true); + mMap.set('isLoading', false); + if (!hadNext) mMap.set('next', next); + mMap.set('items', wasLoaded ? ids.concat(oldIds) : ids); + })); }; const appendNormalizedTimeline = (state, timeline, statuses, next) => { - let moreIds = Immutable.List(); - - statuses.forEach((status, i) => { - state = normalizeStatus(state, status); - moreIds = moreIds.set(i, status.get('id')); - }); - - state = state.setIn([timeline, 'isLoading'], false); - state = state.setIn([timeline, 'next'], next); - - return state.updateIn([timeline, 'items'], Immutable.List(), list => list.concat(moreIds)); -}; - -const normalizeAccountTimeline = (state, accountId, statuses, replace, next) => { - let ids = Immutable.List(); - - statuses.forEach((status, i) => { - state = normalizeStatus(state, status); - ids = ids.set(i, status.get('id')); - }); - - return state.updateIn(['accounts_timelines', accountId], Immutable.Map(), map => map - .set('isLoading', false) - .set('loaded', true) - .update('next', null, v => replace ? next : v) - .update('items', Immutable.List(), list => (replace ? ids : ids.concat(list)))); -}; - -const normalizeAccountMediaTimeline = (state, accountId, statuses, replace, next) => { - let ids = Immutable.List(); - - statuses.forEach((status, i) => { - state = normalizeStatus(state, status); - ids = ids.set(i, status.get('id')); - }); - - return state.updateIn(['accounts_media_timelines', accountId], Immutable.Map(), map => map - .set('isLoading', false) - .update('next', null, v => replace ? next : v) - .update('items', Immutable.List(), list => (replace ? ids : ids.concat(list)))); -}; - -const appendNormalizedAccountTimeline = (state, accountId, statuses, next) => { - let moreIds = Immutable.List([]); - - statuses.forEach((status, i) => { - state = normalizeStatus(state, status); - moreIds = moreIds.set(i, status.get('id')); - }); - - return state.updateIn(['accounts_timelines', accountId], Immutable.Map(), map => map - .set('isLoading', false) - .set('next', next) - .update('items', list => list.concat(moreIds))); -}; - -const appendNormalizedAccountMediaTimeline = (state, accountId, statuses, next) => { - let moreIds = Immutable.List([]); - - statuses.forEach((status, i) => { - state = normalizeStatus(state, status); - moreIds = moreIds.set(i, status.get('id')); - }); - - return state.updateIn(['accounts_media_timelines', accountId], Immutable.Map(), map => map - .set('isLoading', false) - .set('next', next) - .update('items', list => list.concat(moreIds))); + const ids = Immutable.List(statuses.map(status => status.get('id'))); + const oldIds = state.getIn([timeline, 'items'], Immutable.List()); + + return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { + mMap.set('isLoading', false); + mMap.set('next', next); + mMap.set('items', oldIds.concat(ids)); + })); }; const updateTimeline = (state, timeline, status, references) => { - const top = state.getIn([timeline, 'top']); + const top = state.getIn([timeline, 'top']); + const ids = state.getIn([timeline, 'items'], Immutable.List()); + const includesId = ids.includes(status.get('id')); + const unread = state.getIn([timeline, 'unread'], 0); - state = normalizeStatus(state, status); - - if (!top) { - state = state.updateIn([timeline, 'unread'], unread => unread + 1); + if (includesId) { + return state; } - state = state.updateIn([timeline, 'items'], Immutable.List(), list => { - if (top && list.size > 40) { - list = list.take(20); - } - - if (list.includes(status.get('id'))) { - return list; - } - - const reblogOfId = status.getIn(['reblog', 'id'], null); + let newIds = ids; - if (reblogOfId !== null) { - list = list.filterNot(itemId => references.includes(itemId)); - } - - return list.unshift(status.get('id')); - }); - - return state; + return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { + if (!top) mMap.set('unread', unread + 1); + if (top && ids.size > 40) newIds = newIds.take(20); + if (status.getIn(['reblog', 'id'], null) !== null) newIds = newIds.filterNot(item => references.includes(item)); + mMap.set('items', newIds.unshift(status.get('id'))); + })); }; const deleteStatus = (state, id, accountId, references, reblogOf) => { - if (reblogOf) { - // If we are deleting a reblog, just replace reblog with its original - return state.updateIn(['home', 'items'], list => list.map(item => item === id ? reblogOf : item)); - } - - // Remove references from timelines - ['home', 'public', 'community', 'tag'].forEach(function (timeline) { - state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id)); + state.keySeq().forEach(timeline => { + state = state.updateIn([timeline, 'items'], list => { + if (reblogOf && !list.includes(reblogOf)) { + return list.map(item => item === id ? reblogOf : item); + } else { + return list.filterNot(item => item === id); + } + }); }); - // Remove references from account timelines - state = state.updateIn(['accounts_timelines', accountId, 'items'], Immutable.List([]), list => list.filterNot(item => item === id)); - state = state.updateIn(['accounts_media_timelines', accountId, 'items'], Immutable.List([]), list => list.filterNot(item => item === id)); - - // Remove references from context - state.getIn(['descendants', id], Immutable.List()).forEach(descendantId => { - state = state.updateIn(['ancestors', descendantId], Immutable.List(), list => list.filterNot(itemId => itemId === id)); - }); - - state.getIn(['ancestors', id], Immutable.List()).forEach(ancestorId => { - state = state.updateIn(['descendants', ancestorId], Immutable.List(), list => list.filterNot(itemId => itemId === id)); - }); - - state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]); - // Remove reblogs of deleted status references.forEach(ref => { state = deleteStatus(state, ref[0], ref[1], []); @@ -257,54 +108,27 @@ const filterTimelines = (state, relationship, statuses) => { } references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]); - state = deleteStatus(state, status.get('id'), status.get('account'), references); - }); - - return state; -}; - -const normalizeContext = (state, id, ancestors, descendants) => { - const ancestorsIds = ancestors.map(ancestor => ancestor.get('id')); - const descendantsIds = descendants.map(descendant => descendant.get('id')); - - return state.withMutations(map => { - map.setIn(['ancestors', id], ancestorsIds); - map.setIn(['descendants', id], descendantsIds); + state = deleteStatus(state, status.get('id'), status.get('account'), references); }); -}; - -const resetTimeline = (state, timeline, id) => { - if (timeline === 'tag' && typeof id !== 'undefined' && state.getIn([timeline, 'id']) !== id) { - state = state.update(timeline, map => map - .set('id', id) - .set('isLoading', true) - .set('loaded', false) - .set('next', null) - .set('top', true) - .update('items', list => list.clear())); - } else { - state = state.setIn([timeline, 'isLoading'], true); - } return state; }; const updateTop = (state, timeline, top) => { - if (top) { - state = state.setIn([timeline, 'unread'], 0); - } - - return state.setIn([timeline, 'top'], top); + return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { + if (top) mMap.set('unread', 0); + mMap.set('top', top); + })); }; export default function timelines(state = initialState, action) { switch(action.type) { case TIMELINE_REFRESH_REQUEST: case TIMELINE_EXPAND_REQUEST: - return resetTimeline(state, action.timeline, action.id); + return state.update(action.timeline, initialTimeline, map => map.set('isLoading', true)); case TIMELINE_REFRESH_FAIL: case TIMELINE_EXPAND_FAIL: - return state.setIn([action.timeline, 'isLoading'], false); + return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false)); case TIMELINE_REFRESH_SUCCESS: return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next); case TIMELINE_EXPAND_SUCCESS: @@ -313,37 +137,15 @@ export default function timelines(state = initialState, action) { return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); - case CONTEXT_FETCH_SUCCESS: - return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants)); - case ACCOUNT_TIMELINE_FETCH_REQUEST: - case ACCOUNT_TIMELINE_EXPAND_REQUEST: - return state.updateIn(['accounts_timelines', action.id], Immutable.Map(), map => map.set('isLoading', true)); - case ACCOUNT_TIMELINE_FETCH_FAIL: - case ACCOUNT_TIMELINE_EXPAND_FAIL: - return state.updateIn(['accounts_timelines', action.id], Immutable.Map(), map => map.set('isLoading', false)); - case ACCOUNT_TIMELINE_FETCH_SUCCESS: - return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace, action.next); - case ACCOUNT_TIMELINE_EXPAND_SUCCESS: - return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.next); - case ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST: - case ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST: - return state.updateIn(['accounts_media_timelines', action.id], Immutable.Map(), map => map.set('isLoading', true)); - case ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL: - case ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL: - return state.updateIn(['accounts_media_timelines', action.id], Immutable.Map(), map => map.set('isLoading', false)); - case ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS: - return normalizeAccountMediaTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace, action.next); - case ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS: - return appendNormalizedAccountMediaTimeline(state, action.id, Immutable.fromJS(action.statuses), action.next); case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_MUTE_SUCCESS: return filterTimelines(state, action.relationship, action.statuses); case TIMELINE_SCROLL_TOP: return updateTop(state, action.timeline, action.top); case TIMELINE_CONNECT: - return state.setIn([action.timeline, 'online'], true); + return state.update(action.timeline, initialTimeline, map => map.set('online', true)); case TIMELINE_DISCONNECT: - return state.setIn([action.timeline, 'online'], false); + return state.update(action.timeline, initialTimeline, map => map.set('online', false)); default: return state; } |