1 import { importFetchedStatus
, importFetchedStatuses
} from './importer';
2 import api
, { getLinks
} from 'mastodon/api';
3 import { Map as ImmutableMap
, List as ImmutableList
} from 'immutable';
4 import compareId
from 'mastodon/compare_id';
5 import { usePendingItems as preferPendingItems
} from 'mastodon/initial_state';
7 export const TIMELINE_UPDATE
= 'TIMELINE_UPDATE';
8 export const TIMELINE_DELETE
= 'TIMELINE_DELETE';
9 export const TIMELINE_CLEAR
= 'TIMELINE_CLEAR';
11 export const TIMELINE_EXPAND_REQUEST
= 'TIMELINE_EXPAND_REQUEST';
12 export const TIMELINE_EXPAND_SUCCESS
= 'TIMELINE_EXPAND_SUCCESS';
13 export const TIMELINE_EXPAND_FAIL
= 'TIMELINE_EXPAND_FAIL';
15 export const TIMELINE_SCROLL_TOP
= 'TIMELINE_SCROLL_TOP';
16 export const TIMELINE_LOAD_PENDING
= 'TIMELINE_LOAD_PENDING';
17 export const TIMELINE_DISCONNECT
= 'TIMELINE_DISCONNECT';
18 export const TIMELINE_CONNECT
= 'TIMELINE_CONNECT';
20 export const CURRENTLY_VIEWING
= 'CURRENTLY_VIEWING';
22 export const updateCurrentlyViewing
= (timeline
, id
) => ({
23 type: CURRENTLY_VIEWING
,
28 export const loadPending
= timeline
=> ({
29 type: TIMELINE_LOAD_PENDING
,
33 export function updateTimeline(timeline
, status
, accept
) {
35 if (typeof accept
=== 'function' && !accept(status
)) {
39 dispatch(importFetchedStatus(status
));
42 type: TIMELINE_UPDATE
,
45 usePendingItems: preferPendingItems
,
50 export function deleteFromTimelines(id
) {
51 return (dispatch
, getState
) => {
52 const accountId
= getState().getIn(['statuses', id
, 'account']);
53 const references
= getState().get('statuses').filter(status
=> status
.get('reblog') === id
).map(status
=> [status
.get('id'), status
.get('account')]);
54 const reblogOf
= getState().getIn(['statuses', id
, 'reblog'], null);
57 type: TIMELINE_DELETE
,
66 export function clearTimeline(timeline
) {
67 return (dispatch
) => {
68 dispatch({ type: TIMELINE_CLEAR
, timeline
});
72 const noOp
= () => {};
74 const parseTags
= (tags
= {}, mode
) => {
75 return (tags
[mode
] || []).map((tag
) => {
80 export function expandTimeline(timelineId
, path
, params
= {}, done
= noOp
) {
81 return (dispatch
, getState
) => {
82 const timeline
= getState().getIn(['timelines', timelineId
], ImmutableMap());
83 const isLoadingMore
= !!params
.max_id
;
85 if (timeline
.get('isLoading')) {
90 if (!params
.max_id
&& !params
.pinned
&& (timeline
.get('items', ImmutableList()).size
+ timeline
.get('pendingItems', ImmutableList()).size
) > 0) {
91 const a
= timeline
.getIn(['pendingItems', 0]);
92 const b
= timeline
.getIn(['items', 0]);
94 if (a
&& b
&& compareId(a
, b
) > 0) {
97 params
.since_id
= b
|| a
;
101 const isLoadingRecent
= !!params
.since_id
;
103 dispatch(expandTimelineRequest(timelineId
, isLoadingMore
));
105 api(getState
).get(path
, { params
}).then(response
=> {
106 const next
= getLinks(response
).refs
.find(link
=> link
.rel
=== 'next');
107 dispatch(importFetchedStatuses(response
.data
));
108 dispatch(expandTimelineSuccess(timelineId
, response
.data
, next
? next
.uri : null, response
.status
=== 206, isLoadingRecent
, isLoadingMore
, isLoadingRecent
&& preferPendingItems
));
111 dispatch(expandTimelineFail(timelineId
, error
, isLoadingMore
));
117 export const expandHomeTimeline
= ({ maxId
} = {}, done
= noOp
) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId
}, done
);
118 export const expandPublicTimeline
= ({ maxId
, onlyMedia
} = {}, done
= noOp
) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId
, only_media: !!onlyMedia
}, done
);
119 export const expandCommunityTimeline
= ({ maxId
, onlyMedia
} = {}, done
= noOp
) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId
, only_media: !!onlyMedia
}, done
);
120 export const expandAccountTimeline
= (accountId
, { maxId
, withReplies
} = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies
, max_id: maxId
});
121 export const expandAccountFeaturedTimeline
= accountId
=> expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
122 export const expandAccountMediaTimeline
= (accountId
, { maxId
} = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId
, only_media: true, limit: 40 });
123 export const expandListTimeline
= (id
, { maxId
} = {}, done
= noOp
) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId
}, done
);
124 export const expandHashtagTimeline
= (hashtag
, { maxId
, tags
} = {}, done
= noOp
) => {
125 return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
127 any: parseTags(tags
, 'any'),
128 all: parseTags(tags
, 'all'),
129 none: parseTags(tags
, 'none'),
133 export function expandTimelineRequest(timeline
, isLoadingMore
) {
135 type: TIMELINE_EXPAND_REQUEST
,
137 skipLoading: !isLoadingMore
,
141 export function expandTimelineSuccess(timeline
, statuses
, next
, partial
, isLoadingRecent
, isLoadingMore
, usePendingItems
) {
143 type: TIMELINE_EXPAND_SUCCESS
,
150 skipLoading: !isLoadingMore
,
154 export function expandTimelineFail(timeline
, error
, isLoadingMore
) {
156 type: TIMELINE_EXPAND_FAIL
,
159 skipLoading: !isLoadingMore
,
163 export function scrollTopTimeline(timeline
, top
) {
165 type: TIMELINE_SCROLL_TOP
,
171 export function connectTimeline(timeline
) {
173 type: TIMELINE_CONNECT
,
178 export const disconnectTimeline
= timeline
=> ({
179 type: TIMELINE_DISCONNECT
,
181 usePendingItems: preferPendingItems
,