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.
 
 
 
 

377 lines
9.4 KiB

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