logo

pleroma-fe

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

statuses.js (22844B)


  1. import {
  2. remove,
  3. slice,
  4. each,
  5. findIndex,
  6. find,
  7. maxBy,
  8. minBy,
  9. merge,
  10. first,
  11. last,
  12. isArray,
  13. omitBy
  14. } from 'lodash'
  15. import apiService from '../services/api/api.service.js'
  16. const emptyTl = (userId = 0) => ({
  17. statuses: [],
  18. statusesObject: {},
  19. faves: [],
  20. visibleStatuses: [],
  21. visibleStatusesObject: {},
  22. newStatusCount: 0,
  23. maxId: 0,
  24. minId: 0,
  25. minVisibleId: 0,
  26. loading: false,
  27. followers: [],
  28. friends: [],
  29. userId,
  30. flushMarker: 0
  31. })
  32. export const defaultState = () => ({
  33. allStatuses: [],
  34. allStatusesObject: {},
  35. conversationsObject: {},
  36. maxId: 0,
  37. favorites: new Set(),
  38. timelines: {
  39. mentions: emptyTl(),
  40. public: emptyTl(),
  41. user: emptyTl(),
  42. favorites: emptyTl(),
  43. media: emptyTl(),
  44. publicAndExternal: emptyTl(),
  45. friends: emptyTl(),
  46. tag: emptyTl(),
  47. dms: emptyTl(),
  48. bookmarks: emptyTl(),
  49. list: emptyTl()
  50. }
  51. })
  52. export const prepareStatus = (status) => {
  53. // Set deleted flag
  54. status.deleted = false
  55. // To make the array reactive
  56. status.attachments = status.attachments || []
  57. return status
  58. }
  59. const mergeOrAdd = (arr, obj, item) => {
  60. const oldItem = obj[item.id]
  61. if (oldItem) {
  62. // We already have this, so only merge the new info.
  63. // We ignore null values to avoid overwriting existing properties with missing data
  64. // we also skip 'user' because that is handled by users module
  65. merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user'))
  66. // Reactivity fix.
  67. oldItem.attachments.splice(oldItem.attachments.length)
  68. return { item: oldItem, new: false }
  69. } else {
  70. // This is a new item, prepare it
  71. prepareStatus(item)
  72. arr.push(item)
  73. obj[item.id] = item
  74. return { item, new: true }
  75. }
  76. }
  77. const sortById = (a, b) => {
  78. const seqA = Number(a.id)
  79. const seqB = Number(b.id)
  80. const isSeqA = !Number.isNaN(seqA)
  81. const isSeqB = !Number.isNaN(seqB)
  82. if (isSeqA && isSeqB) {
  83. return seqA > seqB ? -1 : 1
  84. } else if (isSeqA && !isSeqB) {
  85. return 1
  86. } else if (!isSeqA && isSeqB) {
  87. return -1
  88. } else {
  89. return a.id > b.id ? -1 : 1
  90. }
  91. }
  92. const sortTimeline = (timeline) => {
  93. timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)
  94. timeline.statuses = timeline.statuses.sort(sortById)
  95. timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id
  96. return timeline
  97. }
  98. // Add status to the global storages (arrays and objects maintaining statuses) except timelines
  99. const addStatusToGlobalStorage = (state, data) => {
  100. const result = mergeOrAdd(state.allStatuses, state.allStatusesObject, data)
  101. if (result.new) {
  102. // Add to conversation
  103. const status = result.item
  104. const conversationsObject = state.conversationsObject
  105. const conversationId = status.statusnet_conversation_id
  106. if (conversationsObject[conversationId]) {
  107. conversationsObject[conversationId].push(status)
  108. } else {
  109. conversationsObject[conversationId] = [status]
  110. }
  111. }
  112. return result
  113. }
  114. const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId, pagination = {} }) => {
  115. // Sanity check
  116. if (!isArray(statuses)) {
  117. return false
  118. }
  119. const allStatuses = state.allStatuses
  120. const timelineObject = state.timelines[timeline]
  121. // Mismatch between API pagination and our internal minId/maxId tracking systems:
  122. // pagination.maxId is the oldest of the returned statuses when fetching older,
  123. // and pagination.minId is the newest when fetching newer. The names come directly
  124. // from the arguments they're supposed to be passed as for the next fetch.
  125. const minNew = pagination.maxId || (statuses.length > 0 ? minBy(statuses, 'id').id : 0)
  126. const maxNew = pagination.minId || (statuses.length > 0 ? maxBy(statuses, 'id').id : 0)
  127. const newer = timeline && (maxNew > timelineObject.maxId || timelineObject.maxId === 0) && statuses.length > 0
  128. const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0
  129. if (!noIdUpdate && newer) {
  130. timelineObject.maxId = maxNew
  131. }
  132. if (!noIdUpdate && older) {
  133. timelineObject.minId = minNew
  134. }
  135. // This makes sure that user timeline won't get data meant for other
  136. // user. I.e. opening different user profiles makes request which could
  137. // return data late after user already viewing different user profile
  138. if ((timeline === 'user' || timeline === 'media') && timelineObject.userId !== userId) {
  139. return
  140. }
  141. const addStatus = (data, showImmediately, addToTimeline = true) => {
  142. const result = addStatusToGlobalStorage(state, data)
  143. const status = result.item
  144. if (result.new) {
  145. // We are mentioned in a post
  146. if (status.type === 'status' && find(status.attentions, { id: user.id })) {
  147. const mentions = state.timelines.mentions
  148. // Add the mention to the mentions timeline
  149. if (timelineObject !== mentions) {
  150. mergeOrAdd(mentions.statuses, mentions.statusesObject, status)
  151. mentions.newStatusCount += 1
  152. sortTimeline(mentions)
  153. }
  154. }
  155. if (status.visibility === 'direct') {
  156. const dms = state.timelines.dms
  157. mergeOrAdd(dms.statuses, dms.statusesObject, status)
  158. dms.newStatusCount += 1
  159. sortTimeline(dms)
  160. }
  161. }
  162. // Decide if we should treat the status as new for this timeline.
  163. let resultForCurrentTimeline
  164. // Some statuses should only be added to the global status repository.
  165. if (timeline && addToTimeline) {
  166. resultForCurrentTimeline = mergeOrAdd(timelineObject.statuses, timelineObject.statusesObject, status)
  167. }
  168. if (timeline && showImmediately) {
  169. // Add it directly to the visibleStatuses, don't change
  170. // newStatusCount
  171. mergeOrAdd(timelineObject.visibleStatuses, timelineObject.visibleStatusesObject, status)
  172. } else if (timeline && addToTimeline && resultForCurrentTimeline.new) {
  173. // Just change newStatuscount
  174. timelineObject.newStatusCount += 1
  175. }
  176. if (status.quote) {
  177. addStatus(status.quote, /* showImmediately = */ false, /* addToTimeline = */ false)
  178. }
  179. return status
  180. }
  181. const favoriteStatus = (favorite, counter) => {
  182. const status = find(allStatuses, { id: favorite.in_reply_to_status_id })
  183. if (status) {
  184. // This is our favorite, so the relevant bit.
  185. if (favorite.user.id === user.id) {
  186. status.favorited = true
  187. } else {
  188. status.fave_num += 1
  189. }
  190. }
  191. return status
  192. }
  193. const processors = {
  194. status: (status) => {
  195. addStatus(status, showImmediately)
  196. },
  197. edit: (status) => {
  198. addStatus(status, showImmediately)
  199. },
  200. retweet: (status) => {
  201. // RetweetedStatuses are never shown immediately
  202. const retweetedStatus = addStatus(status.retweeted_status, false, false)
  203. let retweet
  204. // If the retweeted status is already there, don't add the retweet
  205. // to the timeline.
  206. if (timeline && find(timelineObject.statuses, (s) => {
  207. if (s.retweeted_status) {
  208. return s.id === retweetedStatus.id || s.retweeted_status.id === retweetedStatus.id
  209. } else {
  210. return s.id === retweetedStatus.id
  211. }
  212. })) {
  213. // Already have it visible (either as the original or another RT), don't add to timeline, don't show.
  214. retweet = addStatus(status, false, false)
  215. } else {
  216. retweet = addStatus(status, showImmediately)
  217. }
  218. retweet.retweeted_status = retweetedStatus
  219. },
  220. favorite: (favorite) => {
  221. // Only update if this is a new favorite.
  222. // Ignore our own favorites because we get info about likes as response to like request
  223. if (!state.favorites.has(favorite.id)) {
  224. state.favorites.add(favorite.id)
  225. favoriteStatus(favorite)
  226. }
  227. },
  228. follow: (follow) => {
  229. // NOOP, it is known status but we don't do anything about it for now
  230. },
  231. default: (unknown) => {
  232. console.log('unknown status type')
  233. console.log(unknown)
  234. }
  235. }
  236. each(statuses, (status) => {
  237. const type = status.type
  238. const processor = processors[type] || processors.default
  239. processor(status)
  240. })
  241. // Keep the visible statuses sorted
  242. if (timeline && !(timeline === 'bookmarks')) {
  243. sortTimeline(timelineObject)
  244. }
  245. }
  246. const removeStatus = (state, { timeline, userId }) => {
  247. const timelineObject = state.timelines[timeline]
  248. if (userId) {
  249. remove(timelineObject.statuses, { user: { id: userId } })
  250. remove(timelineObject.visibleStatuses, { user: { id: userId } })
  251. timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0
  252. timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0
  253. }
  254. }
  255. export const mutations = {
  256. addNewStatuses,
  257. removeStatus,
  258. showNewStatuses (state, { timeline }) {
  259. const oldTimeline = (state.timelines[timeline])
  260. oldTimeline.newStatusCount = 0
  261. oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)
  262. oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id
  263. oldTimeline.minId = oldTimeline.minVisibleId
  264. oldTimeline.visibleStatusesObject = {}
  265. each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status })
  266. },
  267. resetStatuses (state) {
  268. const emptyState = defaultState()
  269. Object.entries(emptyState).forEach(([key, value]) => {
  270. state[key] = value
  271. })
  272. },
  273. clearTimeline (state, { timeline, excludeUserId = false }) {
  274. const userId = excludeUserId ? state.timelines[timeline].userId : undefined
  275. state.timelines[timeline] = emptyTl(userId)
  276. },
  277. setFavorited (state, { status, value }) {
  278. const newStatus = state.allStatusesObject[status.id]
  279. if (newStatus.favorited !== value) {
  280. if (value) {
  281. newStatus.fave_num++
  282. } else {
  283. newStatus.fave_num--
  284. }
  285. }
  286. newStatus.favorited = value
  287. },
  288. setFavoritedConfirm (state, { status, user }) {
  289. const newStatus = state.allStatusesObject[status.id]
  290. newStatus.favorited = status.favorited
  291. newStatus.fave_num = status.fave_num
  292. const index = findIndex(newStatus.favoritedBy, { id: user.id })
  293. if (index !== -1 && !newStatus.favorited) {
  294. newStatus.favoritedBy.splice(index, 1)
  295. } else if (index === -1 && newStatus.favorited) {
  296. newStatus.favoritedBy.push(user)
  297. }
  298. },
  299. setMutedStatus (state, status) {
  300. const newStatus = state.allStatusesObject[status.id]
  301. newStatus.thread_muted = status.thread_muted
  302. if (newStatus.thread_muted !== undefined) {
  303. state.conversationsObject[newStatus.statusnet_conversation_id].forEach(status => { status.thread_muted = newStatus.thread_muted })
  304. }
  305. },
  306. setRetweeted (state, { status, value }) {
  307. const newStatus = state.allStatusesObject[status.id]
  308. if (newStatus.repeated !== value) {
  309. if (value) {
  310. newStatus.repeat_num++
  311. } else {
  312. newStatus.repeat_num--
  313. }
  314. }
  315. newStatus.repeated = value
  316. },
  317. setRetweetedConfirm (state, { status, user }) {
  318. const newStatus = state.allStatusesObject[status.id]
  319. newStatus.repeated = status.repeated
  320. newStatus.repeat_num = status.repeat_num
  321. const index = findIndex(newStatus.rebloggedBy, { id: user.id })
  322. if (index !== -1 && !newStatus.repeated) {
  323. newStatus.rebloggedBy.splice(index, 1)
  324. } else if (index === -1 && newStatus.repeated) {
  325. newStatus.rebloggedBy.push(user)
  326. }
  327. },
  328. setBookmarked (state, { status, value }) {
  329. const newStatus = state.allStatusesObject[status.id]
  330. newStatus.bookmarked = value
  331. newStatus.bookmark_folder_id = status.bookmark_folder_id
  332. },
  333. setBookmarkedConfirm (state, { status }) {
  334. const newStatus = state.allStatusesObject[status.id]
  335. newStatus.bookmarked = status.bookmarked
  336. if (status.pleroma) newStatus.bookmark_folder_id = status.pleroma.bookmark_folder
  337. },
  338. setDeleted (state, { status }) {
  339. const newStatus = state.allStatusesObject[status.id]
  340. if (newStatus) newStatus.deleted = true
  341. },
  342. setManyDeleted (state, condition) {
  343. Object.values(state.allStatusesObject).forEach(status => {
  344. if (condition(status)) {
  345. status.deleted = true
  346. }
  347. })
  348. },
  349. setLoading (state, { timeline, value }) {
  350. state.timelines[timeline].loading = value
  351. },
  352. setNsfw (state, { id, nsfw }) {
  353. const newStatus = state.allStatusesObject[id]
  354. newStatus.nsfw = nsfw
  355. },
  356. queueFlush (state, { timeline, id }) {
  357. state.timelines[timeline].flushMarker = id
  358. },
  359. queueFlushAll (state) {
  360. Object.keys(state.timelines).forEach((timeline) => {
  361. state.timelines[timeline].flushMarker = state.timelines[timeline].maxId
  362. })
  363. },
  364. addRepeats (state, { id, rebloggedByUsers, currentUser }) {
  365. const newStatus = state.allStatusesObject[id]
  366. newStatus.rebloggedBy = rebloggedByUsers.filter(_ => _)
  367. // repeats stats can be incorrect based on polling condition, let's update them using the most recent data
  368. newStatus.repeat_num = newStatus.rebloggedBy.length
  369. newStatus.repeated = !!newStatus.rebloggedBy.find(({ id }) => currentUser.id === id)
  370. },
  371. addFavs (state, { id, favoritedByUsers, currentUser }) {
  372. const newStatus = state.allStatusesObject[id]
  373. newStatus.favoritedBy = favoritedByUsers.filter(_ => _)
  374. // favorites stats can be incorrect based on polling condition, let's update them using the most recent data
  375. newStatus.fave_num = newStatus.favoritedBy.length
  376. newStatus.favorited = !!newStatus.favoritedBy.find(({ id }) => currentUser.id === id)
  377. },
  378. addEmojiReactionsBy (state, { id, emojiReactions, currentUser }) {
  379. const status = state.allStatusesObject[id]
  380. status.emoji_reactions = emojiReactions
  381. },
  382. addOwnReaction (state, { id, emoji, currentUser }) {
  383. const status = state.allStatusesObject[id]
  384. const reactionIndex = findIndex(status.emoji_reactions, { name: emoji })
  385. const reaction = status.emoji_reactions[reactionIndex] || { name: emoji, count: 0, accounts: [] }
  386. const newReaction = {
  387. ...reaction,
  388. count: reaction.count + 1,
  389. me: true,
  390. accounts: [
  391. ...reaction.accounts,
  392. currentUser
  393. ]
  394. }
  395. // Update count of existing reaction if it exists, otherwise append at the end
  396. if (reactionIndex >= 0) {
  397. status.emoji_reactions[reactionIndex] = newReaction
  398. } else {
  399. status.emoji_reactions = [...status.emoji_reactions, newReaction]
  400. }
  401. },
  402. removeOwnReaction (state, { id, emoji, currentUser }) {
  403. const status = state.allStatusesObject[id]
  404. const reactionIndex = findIndex(status.emoji_reactions, { name: emoji })
  405. if (reactionIndex < 0) return
  406. const reaction = status.emoji_reactions[reactionIndex]
  407. const accounts = reaction.accounts || []
  408. const newReaction = {
  409. ...reaction,
  410. count: reaction.count - 1,
  411. me: false,
  412. accounts: accounts.filter(acc => acc.id !== currentUser.id)
  413. }
  414. if (newReaction.count > 0) {
  415. status.emoji_reactions[reactionIndex] = newReaction
  416. } else {
  417. status.emoji_reactions = status.emoji_reactions.filter(r => r.name !== emoji)
  418. }
  419. },
  420. updateStatusWithPoll (state, { id, poll }) {
  421. const status = state.allStatusesObject[id]
  422. status.poll = poll
  423. },
  424. setVirtualHeight (state, { statusId, height }) {
  425. state.allStatusesObject[statusId].virtualHeight = height
  426. }
  427. }
  428. const statuses = {
  429. state: defaultState(),
  430. actions: {
  431. addNewStatuses ({ rootState, commit, dispatch, state }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId, pagination }) {
  432. commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId, pagination })
  433. },
  434. fetchStatus ({ rootState, dispatch }, id) {
  435. return rootState.api.backendInteractor.fetchStatus({ id })
  436. .then((status) => dispatch('addNewStatuses', { statuses: [status] }))
  437. },
  438. fetchStatusSource ({ rootState, dispatch }, status) {
  439. return apiService.fetchStatusSource({ id: status.id, credentials: rootState.users.currentUser.credentials })
  440. },
  441. fetchStatusHistory ({ rootState, dispatch }, status) {
  442. return apiService.fetchStatusHistory({ status })
  443. },
  444. deleteStatus ({ rootState, commit, dispatch }, status) {
  445. apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
  446. .then((_) => {
  447. commit('setDeleted', { status })
  448. })
  449. .catch((e) => {
  450. dispatch('pushGlobalNotice', {
  451. level: 'error',
  452. messageKey: 'status.delete_error',
  453. messageArgs: [e.message],
  454. timeout: 5000
  455. })
  456. })
  457. },
  458. deleteStatusById ({ rootState, commit }, id) {
  459. const status = rootState.statuses.allStatusesObject[id]
  460. commit('setDeleted', { status })
  461. },
  462. markStatusesAsDeleted ({ commit }, condition) {
  463. commit('setManyDeleted', condition)
  464. },
  465. favorite ({ rootState, commit }, status) {
  466. // Optimistic favoriting...
  467. commit('setFavorited', { status, value: true })
  468. rootState.api.backendInteractor.favorite({ id: status.id })
  469. .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
  470. },
  471. unfavorite ({ rootState, commit }, status) {
  472. // Optimistic unfavoriting...
  473. commit('setFavorited', { status, value: false })
  474. rootState.api.backendInteractor.unfavorite({ id: status.id })
  475. .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
  476. },
  477. fetchPinnedStatuses ({ rootState, dispatch }, userId) {
  478. rootState.api.backendInteractor.fetchPinnedStatuses({ id: userId })
  479. .then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true }))
  480. },
  481. pinStatus ({ rootState, dispatch }, statusId) {
  482. return rootState.api.backendInteractor.pinOwnStatus({ id: statusId })
  483. .then((status) => dispatch('addNewStatuses', { statuses: [status] }))
  484. },
  485. unpinStatus ({ rootState, dispatch }, statusId) {
  486. rootState.api.backendInteractor.unpinOwnStatus({ id: statusId })
  487. .then((status) => dispatch('addNewStatuses', { statuses: [status] }))
  488. },
  489. muteConversation ({ rootState, commit }, statusId) {
  490. return rootState.api.backendInteractor.muteConversation({ id: statusId })
  491. .then((status) => commit('setMutedStatus', status))
  492. },
  493. unmuteConversation ({ rootState, commit }, statusId) {
  494. return rootState.api.backendInteractor.unmuteConversation({ id: statusId })
  495. .then((status) => commit('setMutedStatus', status))
  496. },
  497. retweet ({ rootState, commit }, status) {
  498. // Optimistic retweeting...
  499. commit('setRetweeted', { status, value: true })
  500. rootState.api.backendInteractor.retweet({ id: status.id })
  501. .then(status => commit('setRetweetedConfirm', { status: status.retweeted_status, user: rootState.users.currentUser }))
  502. },
  503. unretweet ({ rootState, commit }, status) {
  504. // Optimistic unretweeting...
  505. commit('setRetweeted', { status, value: false })
  506. rootState.api.backendInteractor.unretweet({ id: status.id })
  507. .then(status => commit('setRetweetedConfirm', { status, user: rootState.users.currentUser }))
  508. },
  509. bookmark ({ rootState, commit }, status) {
  510. commit('setBookmarked', { status, value: true })
  511. rootState.api.backendInteractor.bookmarkStatus({ id: status.id, folder_id: status.bookmark_folder_id })
  512. .then(status => {
  513. commit('setBookmarkedConfirm', { status })
  514. })
  515. },
  516. unbookmark ({ rootState, commit }, status) {
  517. commit('setBookmarked', { status, value: false })
  518. rootState.api.backendInteractor.unbookmarkStatus({ id: status.id })
  519. .then(status => {
  520. commit('setBookmarkedConfirm', { status })
  521. })
  522. },
  523. queueFlush ({ rootState, commit }, { timeline, id }) {
  524. commit('queueFlush', { timeline, id })
  525. },
  526. queueFlushAll ({ rootState, commit }) {
  527. commit('queueFlushAll')
  528. },
  529. fetchFavsAndRepeats ({ rootState, commit }, id) {
  530. Promise.all([
  531. rootState.api.backendInteractor.fetchFavoritedByUsers({ id }),
  532. rootState.api.backendInteractor.fetchRebloggedByUsers({ id })
  533. ]).then(([favoritedByUsers, rebloggedByUsers]) => {
  534. commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser })
  535. commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })
  536. })
  537. },
  538. reactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) {
  539. const currentUser = rootState.users.currentUser
  540. if (!currentUser) return
  541. commit('addOwnReaction', { id, emoji, currentUser })
  542. rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then(
  543. ok => {
  544. dispatch('fetchEmojiReactionsBy', id)
  545. }
  546. )
  547. },
  548. unreactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) {
  549. const currentUser = rootState.users.currentUser
  550. if (!currentUser) return
  551. commit('removeOwnReaction', { id, emoji, currentUser })
  552. rootState.api.backendInteractor.unreactWithEmoji({ id, emoji }).then(
  553. ok => {
  554. dispatch('fetchEmojiReactionsBy', id)
  555. }
  556. )
  557. },
  558. fetchEmojiReactionsBy ({ rootState, commit }, id) {
  559. return rootState.api.backendInteractor.fetchEmojiReactions({ id }).then(
  560. emojiReactions => {
  561. commit('addEmojiReactionsBy', { id, emojiReactions, currentUser: rootState.users.currentUser })
  562. }
  563. )
  564. },
  565. fetchFavs ({ rootState, commit }, id) {
  566. rootState.api.backendInteractor.fetchFavoritedByUsers({ id })
  567. .then(favoritedByUsers => commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser }))
  568. },
  569. fetchRepeats ({ rootState, commit }, id) {
  570. rootState.api.backendInteractor.fetchRebloggedByUsers({ id })
  571. .then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))
  572. },
  573. search (store, { q, resolve, limit, offset, following, type }) {
  574. return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following, type })
  575. .then((data) => {
  576. store.commit('addNewUsers', data.accounts)
  577. store.commit('addNewUsers', data.statuses.map(s => s.user).filter(u => u))
  578. store.commit('addNewStatuses', { statuses: data.statuses })
  579. return data
  580. })
  581. },
  582. setVirtualHeight ({ commit }, { statusId, height }) {
  583. commit('setVirtualHeight', { statusId, height })
  584. }
  585. },
  586. mutations
  587. }
  588. export default statuses