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 (22817B)


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