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.
 
 
 
 

321 lines
7.9 KiB

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