logo

mastofe

My custom branche(s) on git.pleroma.social/pleroma/mastofe git clone https://hacktivis.me/git/mastofe.git

compose.js (11991B)


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