The code powering m.abunchtell.com https://m.abunchtell.com
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

444 linhas
11 KiB

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