The code powering m.abunchtell.com https://m.abunchtell.com
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.
 
 
 
 

374 lines
9.3 KiB

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