You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
10 KiB

6 years ago
7 years ago
6 years ago
7 years ago
7 years ago
6 years ago
6 years ago
6 years ago
  1. import api from 'flavours/glitch/util/api';
  2. import { CancelToken } from 'axios';
  3. import { throttle } from 'lodash';
  4. import { search as emojiSearch } from 'flavours/glitch/util/emoji/emoji_mart_search_light';
  5. import { useEmoji } from './emojis';
  6. import resizeImage from 'flavours/glitch/util/resize_image';
  7. import { updateTimeline } from './timelines';
  8. let cancelFetchComposeSuggestionsAccounts;
  9. export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
  10. export const COMPOSE_CYCLE_ELEFRIEND = 'COMPOSE_CYCLE_ELEFRIEND';
  11. export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
  12. export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
  13. export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
  14. export const COMPOSE_REPLY = 'COMPOSE_REPLY';
  15. export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
  16. export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
  17. export const COMPOSE_MENTION = 'COMPOSE_MENTION';
  18. export const COMPOSE_RESET = 'COMPOSE_RESET';
  19. export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
  20. export const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
  21. export const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
  22. export const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
  23. 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_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
  27. export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
  28. export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
  29. export const COMPOSE_ADVANCED_OPTIONS_CHANGE = 'COMPOSE_ADVANCED_OPTIONS_CHANGE';
  30. export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
  31. export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
  32. export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
  33. export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
  34. export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
  35. export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
  36. export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
  37. export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
  38. export const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
  39. export const COMPOSE_DOODLE_SET = 'COMPOSE_DOODLE_SET';
  40. export function changeCompose(text) {
  41. return {
  42. type: COMPOSE_CHANGE,
  43. text: text,
  44. };
  45. };
  46. export function cycleElefriendCompose() {
  47. return {
  48. type: COMPOSE_CYCLE_ELEFRIEND,
  49. };
  50. };
  51. export function replyCompose(status, router) {
  52. return (dispatch, getState) => {
  53. dispatch({
  54. type: COMPOSE_REPLY,
  55. status: status,
  56. });
  57. if (router && !getState().getIn(['compose', 'mounted'])) {
  58. router.push('/statuses/new');
  59. }
  60. };
  61. };
  62. export function cancelReplyCompose() {
  63. return {
  64. type: COMPOSE_REPLY_CANCEL,
  65. };
  66. };
  67. export function resetCompose() {
  68. return {
  69. type: COMPOSE_RESET,
  70. };
  71. };
  72. export function mentionCompose(account, router) {
  73. return (dispatch, getState) => {
  74. dispatch({
  75. type: COMPOSE_MENTION,
  76. account: account,
  77. });
  78. if (!getState().getIn(['compose', 'mounted'])) {
  79. router.push('/statuses/new');
  80. }
  81. };
  82. };
  83. export function directCompose(account, router) {
  84. return (dispatch, getState) => {
  85. dispatch({
  86. type: COMPOSE_DIRECT,
  87. account: account,
  88. });
  89. if (!getState().getIn(['compose', 'mounted'])) {
  90. router.push('/statuses/new');
  91. }
  92. };
  93. };
  94. export function submitCompose() {
  95. return function (dispatch, getState) {
  96. let status = getState().getIn(['compose', 'text'], '');
  97. let media = getState().getIn(['compose', 'media_attachments']);
  98. if ((!status || !status.length) && media.size === 0) {
  99. return;
  100. }
  101. dispatch(submitComposeRequest());
  102. if (getState().getIn(['compose', 'advanced_options', 'do_not_federate'])) {
  103. status = status + ' 👁️';
  104. }
  105. api(getState).post('/api/v1/statuses', {
  106. status,
  107. in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
  108. media_ids: media.map(item => item.get('id')),
  109. sensitive: getState().getIn(['compose', 'sensitive']),
  110. spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
  111. visibility: getState().getIn(['compose', 'privacy']),
  112. }, {
  113. headers: {
  114. 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
  115. },
  116. }).then(function (response) {
  117. dispatch(submitComposeSuccess({ ...response.data }));
  118. // If the response has no data then we can't do anything else.
  119. if (!response.data) {
  120. return;
  121. }
  122. // To make the app more responsive, immediately get the status into the columns
  123. const insertIfOnline = (timelineId) => {
  124. if (getState().getIn(['timelines', timelineId, 'items', 0]) !== null) {
  125. dispatch(updateTimeline(timelineId, { ...response.data }));
  126. }
  127. };
  128. insertIfOnline('home');
  129. if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
  130. insertIfOnline('community');
  131. insertIfOnline('public');
  132. } else if (response.data.visibility === 'direct') {
  133. insertIfOnline('direct');
  134. }
  135. }).catch(function (error) {
  136. dispatch(submitComposeFail(error));
  137. });
  138. };
  139. };
  140. export function submitComposeRequest() {
  141. return {
  142. type: COMPOSE_SUBMIT_REQUEST,
  143. };
  144. };
  145. export function submitComposeSuccess(status) {
  146. return {
  147. type: COMPOSE_SUBMIT_SUCCESS,
  148. status: status,
  149. };
  150. };
  151. export function submitComposeFail(error) {
  152. return {
  153. type: COMPOSE_SUBMIT_FAIL,
  154. error: error,
  155. };
  156. };
  157. export function doodleSet(options) {
  158. return {
  159. type: COMPOSE_DOODLE_SET,
  160. options: options,
  161. };
  162. };
  163. export function uploadCompose(files) {
  164. return function (dispatch, getState) {
  165. if (getState().getIn(['compose', 'media_attachments']).size > 3) {
  166. return;
  167. }
  168. dispatch(uploadComposeRequest());
  169. resizeImage(files[0]).then(file => {
  170. const data = new FormData();
  171. data.append('file', file);
  172. return api(getState).post('/api/v1/media', data, {
  173. onUploadProgress: ({ loaded, total }) => dispatch(uploadComposeProgress(loaded, total)),
  174. }).then(({ data }) => dispatch(uploadComposeSuccess(data)));
  175. }).catch(error => dispatch(uploadComposeFail(error)));
  176. };
  177. };
  178. export function changeUploadCompose(id, params) {
  179. return (dispatch, getState) => {
  180. dispatch(changeUploadComposeRequest());
  181. api(getState).put(`/api/v1/media/${id}`, params).then(response => {
  182. dispatch(changeUploadComposeSuccess(response.data));
  183. }).catch(error => {
  184. dispatch(changeUploadComposeFail(id, error));
  185. });
  186. };
  187. };
  188. export function changeUploadComposeRequest() {
  189. return {
  190. type: COMPOSE_UPLOAD_CHANGE_REQUEST,
  191. skipLoading: true,
  192. };
  193. };
  194. export function changeUploadComposeSuccess(media) {
  195. return {
  196. type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
  197. media: media,
  198. skipLoading: true,
  199. };
  200. };
  201. export function changeUploadComposeFail(error) {
  202. return {
  203. type: COMPOSE_UPLOAD_CHANGE_FAIL,
  204. error: error,
  205. skipLoading: true,
  206. };
  207. };
  208. export function uploadComposeRequest() {
  209. return {
  210. type: COMPOSE_UPLOAD_REQUEST,
  211. skipLoading: true,
  212. };
  213. };
  214. export function uploadComposeProgress(loaded, total) {
  215. return {
  216. type: COMPOSE_UPLOAD_PROGRESS,
  217. loaded: loaded,
  218. total: total,
  219. };
  220. };
  221. export function uploadComposeSuccess(media) {
  222. return {
  223. type: COMPOSE_UPLOAD_SUCCESS,
  224. media: media,
  225. skipLoading: true,
  226. };
  227. };
  228. export function uploadComposeFail(error) {
  229. return {
  230. type: COMPOSE_UPLOAD_FAIL,
  231. error: error,
  232. skipLoading: true,
  233. };
  234. };
  235. export function undoUploadCompose(media_id) {
  236. return {
  237. type: COMPOSE_UPLOAD_UNDO,
  238. media_id: media_id,
  239. };
  240. };
  241. export function clearComposeSuggestions() {
  242. if (cancelFetchComposeSuggestionsAccounts) {
  243. cancelFetchComposeSuggestionsAccounts();
  244. }
  245. return {
  246. type: COMPOSE_SUGGESTIONS_CLEAR,
  247. };
  248. };
  249. const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
  250. if (cancelFetchComposeSuggestionsAccounts) {
  251. cancelFetchComposeSuggestionsAccounts();
  252. }
  253. api(getState).get('/api/v1/accounts/search', {
  254. cancelToken: new CancelToken(cancel => {
  255. cancelFetchComposeSuggestionsAccounts = cancel;
  256. }),
  257. params: {
  258. q: token.slice(1),
  259. resolve: false,
  260. limit: 4,
  261. },
  262. }).then(response => {
  263. dispatch(readyComposeSuggestionsAccounts(token, response.data));
  264. });
  265. }, 200, { leading: true, trailing: true });
  266. const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => {
  267. const results = emojiSearch(token.replace(':', ''), { maxResults: 5 });
  268. dispatch(readyComposeSuggestionsEmojis(token, results));
  269. };
  270. export function fetchComposeSuggestions(token) {
  271. return (dispatch, getState) => {
  272. if (token[0] === ':') {
  273. fetchComposeSuggestionsEmojis(dispatch, getState, token);
  274. } else {
  275. fetchComposeSuggestionsAccounts(dispatch, getState, token);
  276. }
  277. };
  278. };
  279. export function readyComposeSuggestionsEmojis(token, emojis) {
  280. return {
  281. type: COMPOSE_SUGGESTIONS_READY,
  282. token,
  283. emojis,
  284. };
  285. };
  286. export function readyComposeSuggestionsAccounts(token, accounts) {
  287. return {
  288. type: COMPOSE_SUGGESTIONS_READY,
  289. token,
  290. accounts,
  291. };
  292. };
  293. export function selectComposeSuggestion(position, token, suggestion) {
  294. return (dispatch, getState) => {
  295. const completion = typeof suggestion === 'object' && suggestion.id ? (
  296. dispatch(useEmoji(suggestion)),
  297. suggestion.native || suggestion.colons
  298. ) : '@' + getState().getIn(['accounts', suggestion, 'acct']);
  299. dispatch({
  300. type: COMPOSE_SUGGESTION_SELECT,
  301. position,
  302. token,
  303. completion,
  304. });
  305. };
  306. };
  307. export function mountCompose() {
  308. return {
  309. type: COMPOSE_MOUNT,
  310. };
  311. };
  312. export function unmountCompose() {
  313. return {
  314. type: COMPOSE_UNMOUNT,
  315. };
  316. };
  317. export function changeComposeAdvancedOption(option, value) {
  318. return {
  319. option,
  320. type: COMPOSE_ADVANCED_OPTIONS_CHANGE,
  321. value,
  322. };
  323. }
  324. export function changeComposeSensitivity() {
  325. return {
  326. type: COMPOSE_SENSITIVITY_CHANGE,
  327. };
  328. };
  329. export function changeComposeSpoilerness() {
  330. return {
  331. type: COMPOSE_SPOILERNESS_CHANGE,
  332. };
  333. };
  334. export function changeComposeSpoilerText(text) {
  335. return {
  336. type: COMPOSE_SPOILER_TEXT_CHANGE,
  337. text,
  338. };
  339. };
  340. export function changeComposeVisibility(value) {
  341. return {
  342. type: COMPOSE_VISIBILITY_CHANGE,
  343. value,
  344. };
  345. };
  346. export function insertEmojiCompose(position, emoji) {
  347. return {
  348. type: COMPOSE_EMOJI_INSERT,
  349. position,
  350. emoji,
  351. };
  352. };