1 import api
from '../api';
2 import emojione
from 'emojione';
7 refreshCommunityTimeline
,
11 export const COMPOSE_CHANGE
= 'COMPOSE_CHANGE';
12 export const COMPOSE_SUBMIT_REQUEST
= 'COMPOSE_SUBMIT_REQUEST';
13 export const COMPOSE_SUBMIT_SUCCESS
= 'COMPOSE_SUBMIT_SUCCESS';
14 export const COMPOSE_SUBMIT_FAIL
= 'COMPOSE_SUBMIT_FAIL';
15 export const COMPOSE_REPLY
= 'COMPOSE_REPLY';
16 export const COMPOSE_REPLY_CANCEL
= 'COMPOSE_REPLY_CANCEL';
17 export const COMPOSE_MENTION
= 'COMPOSE_MENTION';
18 export const COMPOSE_UPLOAD_REQUEST
= 'COMPOSE_UPLOAD_REQUEST';
19 export const COMPOSE_UPLOAD_SUCCESS
= 'COMPOSE_UPLOAD_SUCCESS';
20 export const COMPOSE_UPLOAD_FAIL
= 'COMPOSE_UPLOAD_FAIL';
21 export const COMPOSE_UPLOAD_PROGRESS
= 'COMPOSE_UPLOAD_PROGRESS';
22 export const COMPOSE_UPLOAD_UNDO
= 'COMPOSE_UPLOAD_UNDO';
24 export const COMPOSE_SUGGESTIONS_CLEAR
= 'COMPOSE_SUGGESTIONS_CLEAR';
25 export const COMPOSE_SUGGESTIONS_READY
= 'COMPOSE_SUGGESTIONS_READY';
26 export const COMPOSE_SUGGESTIONS_READY_TXT
= 'COMPOSE_SUGGESTIONS_READY_TXT';
27 export const COMPOSE_SUGGESTION_SELECT
= 'COMPOSE_SUGGESTION_SELECT';
29 export const COMPOSE_MOUNT
= 'COMPOSE_MOUNT';
30 export const COMPOSE_UNMOUNT
= 'COMPOSE_UNMOUNT';
32 export const COMPOSE_ADVANCED_OPTIONS_CHANGE
= 'COMPOSE_ADVANCED_OPTIONS_CHANGE';
33 export const COMPOSE_SENSITIVITY_CHANGE
= 'COMPOSE_SENSITIVITY_CHANGE';
34 export const COMPOSE_SPOILERNESS_CHANGE
= 'COMPOSE_SPOILERNESS_CHANGE';
35 export const COMPOSE_SPOILER_TEXT_CHANGE
= 'COMPOSE_SPOILER_TEXT_CHANGE';
36 export const COMPOSE_VISIBILITY_CHANGE
= 'COMPOSE_VISIBILITY_CHANGE';
37 export const COMPOSE_LISTABILITY_CHANGE
= 'COMPOSE_LISTABILITY_CHANGE';
38 export const COMPOSE_COMPOSING_CHANGE
= 'COMPOSE_COMPOSING_CHANGE';
40 export const COMPOSE_EMOJI_INSERT
= 'COMPOSE_EMOJI_INSERT';
42 export function changeCompose(text
) {
49 export function replyCompose(status
, router
) {
50 return (dispatch
, getState
) => {
56 if (!getState().getIn(['compose', 'mounted'])) {
57 router
.push('/statuses/new');
62 export function cancelReplyCompose() {
64 type: COMPOSE_REPLY_CANCEL
,
68 export function mentionCompose(account
, router
) {
69 return (dispatch
, getState
) => {
71 type: COMPOSE_MENTION
,
75 if (!getState().getIn(['compose', 'mounted'])) {
76 router
.push('/statuses/new');
81 export function submitCompose() {
82 return function (dispatch
, getState
) {
83 let status
= getState().getIn(['compose', 'text'], '');
85 if (!status
|| !status
.length
) {
89 dispatch(submitComposeRequest());
90 if (getState().getIn(['compose', 'advanced_options', 'do_not_federate'])) {
91 status
= status
+ ' 👁️';
93 api(getState
).post('/api/v1/statuses', {
95 in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
96 media_ids: getState().getIn(['compose', 'media_attachments']).map(item
=> item
.get('id')),
97 sensitive: getState().getIn(['compose', 'sensitive']),
98 spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
99 visibility: getState().getIn(['compose', 'privacy']),
102 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
104 }).then(function (response
) {
105 dispatch(submitComposeSuccess({ ...response
.data
}));
107 // To make the app more responsive, immediately get the status into the columns
109 const insertOrRefresh
= (timelineId
, refreshAction
) => {
110 if (getState().getIn(['timelines', timelineId
, 'online'])) {
111 dispatch(updateTimeline(timelineId
, { ...response
.data
}));
112 } else if (getState().getIn(['timelines', timelineId
, 'loaded'])) {
113 dispatch(refreshAction());
117 insertOrRefresh('home', refreshHomeTimeline
);
119 if (response
.data
.in_reply_to_id
=== null && response
.data
.visibility
=== 'public') {
120 insertOrRefresh('community', refreshCommunityTimeline
);
121 insertOrRefresh('public', refreshPublicTimeline
);
123 }).catch(function (error
) {
124 dispatch(submitComposeFail(error
));
129 export function submitComposeRequest() {
131 type: COMPOSE_SUBMIT_REQUEST
,
135 export function submitComposeSuccess(status
) {
137 type: COMPOSE_SUBMIT_SUCCESS
,
142 export function submitComposeFail(error
) {
144 type: COMPOSE_SUBMIT_FAIL
,
149 export function uploadCompose(files
) {
150 return function (dispatch
, getState
) {
151 if (getState().getIn(['compose', 'media_attachments']).size
> 3) {
155 dispatch(uploadComposeRequest());
157 let data
= new FormData();
158 data
.append('file', files
[0]);
160 api(getState
).post('/api/v1/media', data
, {
161 onUploadProgress: function (e
) {
162 dispatch(uploadComposeProgress(e
.loaded
, e
.total
));
164 }).then(function (response
) {
165 dispatch(uploadComposeSuccess(response
.data
));
166 }).catch(function (error
) {
167 dispatch(uploadComposeFail(error
));
172 export function uploadComposeRequest() {
174 type: COMPOSE_UPLOAD_REQUEST
,
179 export function uploadComposeProgress(loaded
, total
) {
181 type: COMPOSE_UPLOAD_PROGRESS
,
187 export function uploadComposeSuccess(media
) {
189 type: COMPOSE_UPLOAD_SUCCESS
,
195 export function uploadComposeFail(error
) {
197 type: COMPOSE_UPLOAD_FAIL
,
203 export function undoUploadCompose(media_id
) {
205 type: COMPOSE_UPLOAD_UNDO
,
210 export function clearComposeSuggestions() {
212 type: COMPOSE_SUGGESTIONS_CLEAR
,
216 export function fetchComposeSuggestions(token
) {
217 let leading
= token
[0];
219 if (leading
=== '@') {
221 return (dispatch
, getState
) => {
222 api(getState
).get('/api/v1/accounts/search', {
224 q: token
.slice(1), // remove the '@'
228 }).then(response
=> {
229 dispatch(readyComposeSuggestions(token
, response
.data
));
232 } else if (leading
=== ':') {
234 let allShortcodes
= Object
.keys(emojione
.emojioneList
);
235 // TODO when we have custom emojons merged, add theme to this shortcode list
236 return (dispatch
) => {
237 dispatch(readyComposeSuggestionsTxt(token
, allShortcodes
.filter((sc
) => {
238 return sc
.indexOf(token
) === 0;
243 return (dispatch
, getState
) => {
244 api(getState
).get('/api/v1/search', {
249 }).then(response
=> {
250 dispatch(readyComposeSuggestionsTxt(token
, response
.data
.hashtags
.map((ht
) => `#${ht}`)));
256 export function readyComposeSuggestions(token
, accounts
) {
258 type: COMPOSE_SUGGESTIONS_READY
,
264 export function readyComposeSuggestionsTxt(token
, items
) {
266 type: COMPOSE_SUGGESTIONS_READY_TXT
,
272 export function selectComposeSuggestion(position
, token
, accountId
) {
273 return (dispatch
, getState
) => {
274 const completion
= (typeof accountId
=== 'string') ?
275 accountId
.slice(1) : // text suggestion: discard the leading : or # - the replacing code replaces only what follows
276 getState().getIn(['accounts', accountId
, 'acct']);
279 type: COMPOSE_SUGGESTION_SELECT
,
287 export function mountCompose() {
293 export function unmountCompose() {
295 type: COMPOSE_UNMOUNT
,
299 export function toggleComposeAdvancedOption(option
) {
301 type: COMPOSE_ADVANCED_OPTIONS_CHANGE
,
306 export function changeComposeSensitivity() {
308 type: COMPOSE_SENSITIVITY_CHANGE
,
312 export function changeComposeSpoilerness() {
314 type: COMPOSE_SPOILERNESS_CHANGE
,
318 export function changeComposeSpoilerText(text
) {
320 type: COMPOSE_SPOILER_TEXT_CHANGE
,
325 export function changeComposeVisibility(value
) {
327 type: COMPOSE_VISIBILITY_CHANGE
,
332 export function insertEmojiCompose(position
, emoji
) {
334 type: COMPOSE_EMOJI_INSERT
,
340 export function changeComposing(value
) {
342 type: COMPOSE_COMPOSING_CHANGE
,