pleroma-fe/src/modules/statuses.js

317 lines
8.9 KiB
JavaScript
Raw Normal View History

import { remove, slice, sortBy, toInteger, each, find, flatten, maxBy, last, merge, max, isArray } from 'lodash'
2016-10-31 02:12:35 +11:00
import apiService from '../services/api/api.service.js'
2016-11-15 20:35:16 +11:00
// import parse from '../services/status_parser/status_parser.js'
2016-10-27 04:03:55 +11:00
export const defaultState = {
2016-10-27 04:03:55 +11:00
allStatuses: [],
maxId: 0,
notifications: [],
2016-11-26 02:56:08 +11:00
favorites: new Set(),
2016-10-27 04:03:55 +11:00
timelines: {
2016-12-03 22:43:21 +11:00
mentions: {
statuses: [],
faves: [],
visibleStatuses: [],
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
loading: false
},
2016-10-27 04:03:55 +11:00
public: {
statuses: [],
faves: [],
visibleStatuses: [],
newStatusCount: 0,
maxId: 0,
2016-11-07 03:44:05 +11:00
minVisibleId: 0,
loading: false
2016-10-27 04:03:55 +11:00
},
publicAndExternal: {
statuses: [],
faves: [],
visibleStatuses: [],
newStatusCount: 0,
maxId: 0,
2016-11-07 03:44:05 +11:00
minVisibleId: 0,
loading: false
2016-10-27 04:03:55 +11:00
},
friends: {
statuses: [],
faves: [],
visibleStatuses: [],
newStatusCount: 0,
maxId: 0,
2016-11-07 03:44:05 +11:00
minVisibleId: 0,
loading: false
2016-10-27 04:03:55 +11:00
}
}
}
2016-11-15 20:35:16 +11:00
export const prepareStatus = (status) => {
// Parse nsfw tags
if (status.nsfw === undefined) {
const nsfwRegex = /#nsfw/i
status.nsfw = !!status.text.match(nsfwRegex)
}
// Set deleted flag
status.deleted = false
2016-12-02 04:05:20 +11:00
// To make the array reactive
status.attachments = status.attachments || []
2016-11-15 20:35:16 +11:00
return status
}
2016-11-28 04:54:17 +11:00
export const statusType = (status) => {
if (status.is_post_verb) {
return 'status'
}
2016-11-14 08:09:27 +11:00
if (status.retweeted_status) {
return 'retweet'
}
2016-11-14 08:09:27 +11:00
2016-11-28 04:54:17 +11:00
if (typeof status.uri === 'string' && status.uri.match(/(fave|objectType=Favourite)/)) {
return 'favorite'
}
2016-11-14 08:40:33 +11:00
if (status.text.match(/deleted notice {{tag/)) {
return 'deletion'
}
return 'unknown'
}
2016-11-14 08:54:49 +11:00
2016-11-15 20:35:16 +11:00
export const findMaxId = (...args) => {
return (maxBy(flatten(args), 'id') || {}).id
2016-11-14 08:40:33 +11:00
}
const mergeOrAdd = (arr, item) => {
const oldItem = find(arr, {id: item.id})
if (oldItem) {
// We already have this, so only merge the new info.
merge(oldItem, item)
2016-12-02 04:05:20 +11:00
// Reactivity fix.
oldItem.attachments.splice(oldItem.attachments.length)
2016-11-19 22:39:10 +11:00
return {item: oldItem, new: false}
} else {
// This is a new item, prepare it
prepareStatus(item)
arr.push(item)
2016-11-19 22:39:10 +11:00
return {item, new: true}
}
}
2016-12-03 22:43:21 +11:00
const sortTimeline = (timeline) => {
timeline.visibleStatuses = sortBy(timeline.visibleStatuses, ({id}) => -id)
timeline.statuses = sortBy(timeline.statuses, ({id}) => -id)
timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id
2016-12-03 22:43:21 +11:00
return timeline
}
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false }) => {
// Sanity check
if (!isArray(statuses)) {
return false
}
2016-11-22 02:33:08 +11:00
const allStatuses = state.allStatuses
const timelineObject = state.timelines[timeline]
2016-11-15 20:35:16 +11:00
2016-11-22 02:33:08 +11:00
// Set the maxId to the new id if it's larger.
const updateMaxId = ({id}) => {
if (!timeline || noIdUpdate) { return false }
2016-11-22 02:33:08 +11:00
timelineObject.maxId = max([id, timelineObject.maxId])
}
2016-11-22 02:33:08 +11:00
const addStatus = (status, showImmediately, addToTimeline = true) => {
const result = mergeOrAdd(allStatuses, status)
status = result.item
2016-11-22 02:33:08 +11:00
if (result.new) {
updateMaxId(status)
2016-11-28 04:54:51 +11:00
if (statusType(status) === 'retweet' && status.retweeted_status.user.id === user.id) {
addNotification({ type: 'repeat', status: status.retweeted_status, action: status })
}
2016-11-28 05:11:05 +11:00
2016-12-03 22:43:21 +11:00
// We are mentioned in a post
2016-11-28 05:11:05 +11:00
if (statusType(status) === 'status' && find(status.attentions, { id: user.id })) {
2016-12-03 22:43:21 +11:00
const mentions = state.timelines.mentions
// Add the mention to the mentions timeline
if (timelineObject !== mentions) {
mergeOrAdd(mentions.statuses, status)
mentions.newStatusCount += 1
2016-12-03 22:43:21 +11:00
sortTimeline(mentions)
}
2016-12-03 22:43:21 +11:00
2016-11-28 05:11:05 +11:00
addNotification({ type: 'mention', status, action: status })
}
2016-11-22 02:33:08 +11:00
}
// Decide if we should treat the status as new for this timeline.
let resultForCurrentTimeline
2016-11-22 02:33:08 +11:00
// Some statuses should only be added to the global status repository.
if (timeline && addToTimeline) {
resultForCurrentTimeline = mergeOrAdd(timelineObject.statuses, status)
}
2016-11-19 02:05:04 +11:00
if (timeline && showImmediately) {
2016-11-22 02:33:08 +11:00
// Add it directly to the visibleStatuses, don't change
// newStatusCount
mergeOrAdd(timelineObject.visibleStatuses, status)
} else if (timeline && addToTimeline && resultForCurrentTimeline.new) {
2016-11-22 02:33:08 +11:00
// Just change newStatuscount
timelineObject.newStatusCount += 1
}
2016-11-22 02:33:08 +11:00
return status
}
const addNotification = ({type, status, action}) => {
state.notifications.push({type, status, action, seen: false})
2016-11-22 02:33:08 +11:00
}
const favoriteStatus = (favorite) => {
const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) })
if (status) {
status.fave_num += 1
2016-11-26 02:56:08 +11:00
// This is our favorite, so the relevant bit.
if (favorite.user.id === user.id) {
status.favorited = true
}
// Add a notification if the user's status is favorited
2016-11-22 02:33:08 +11:00
if (status.user.id === user.id) {
addNotification({type: 'favorite', status, action: favorite})
}
}
2016-11-22 02:33:08 +11:00
return status
}
2016-11-22 02:33:08 +11:00
const processors = {
'status': (status) => {
addStatus(status, showImmediately)
},
'retweet': (status) => {
// RetweetedStatuses are never shown immediately
const retweetedStatus = addStatus(status.retweeted_status, false, false)
let retweet
// If the retweeted status is already there, don't add the retweet
// to the timeline.
if (timeline && find(timelineObject.statuses, {id: retweetedStatus.id})) {
2016-11-22 02:33:08 +11:00
// Already have it visible, don't add to timeline, don't show.
retweet = addStatus(status, false, false)
} else {
retweet = addStatus(status, showImmediately)
}
2016-11-22 02:33:08 +11:00
retweet.retweeted_status = retweetedStatus
},
'favorite': (favorite) => {
2016-11-26 02:56:08 +11:00
// Only update if this is a new favorite.
if (!state.favorites.has(favorite.id)) {
state.favorites.add(favorite.id)
updateMaxId(favorite)
favoriteStatus(favorite)
}
2016-11-22 02:33:08 +11:00
},
'deletion': (deletion) => {
const uri = deletion.uri
updateMaxId(deletion)
remove(allStatuses, { uri })
if (timeline) {
remove(timelineObject.statuses, { uri })
remove(timelineObject.visibleStatuses, { uri })
}
2016-11-22 02:33:08 +11:00
},
'default': (unknown) => {
2016-11-28 04:54:51 +11:00
console.log('unknown status type')
2016-11-22 02:33:08 +11:00
console.log(unknown)
}
2016-11-22 02:33:08 +11:00
}
2016-11-22 02:33:08 +11:00
each(statuses, (status) => {
const type = statusType(status)
const processor = processors[type] || processors['default']
processor(status)
})
2016-11-22 02:33:08 +11:00
// Keep the visible statuses sorted
if (timeline) {
2016-12-03 22:43:21 +11:00
sortTimeline(timelineObject)
}
2016-11-22 02:33:08 +11:00
}
export const mutations = {
addNewStatuses,
showNewStatuses (state, { timeline }) {
const oldTimeline = (state.timelines[timeline])
oldTimeline.newStatusCount = 0
oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)
},
setFavorited (state, { status, value }) {
const newStatus = find(state.allStatuses, status)
newStatus.favorited = value
},
2016-11-14 03:09:16 +11:00
setRetweeted (state, { status, value }) {
const newStatus = find(state.allStatuses, status)
newStatus.repeated = value
},
setDeleted (state, { status }) {
const newStatus = find(state.allStatuses, status)
newStatus.deleted = true
},
setLoading (state, { timeline, value }) {
state.timelines[timeline].loading = value
},
setNsfw (state, { id, nsfw }) {
2016-11-08 04:47:38 +11:00
const newStatus = find(state.allStatuses, { id })
newStatus.nsfw = nsfw
},
markNotificationsAsSeen (state, notifications) {
each(notifications, (notification) => {
notification.seen = true
})
}
}
2016-10-27 04:03:55 +11:00
const statuses = {
state: defaultState,
2016-10-31 02:12:35 +11:00
actions: {
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false }) {
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser })
},
deleteStatus ({ rootState, commit }, status) {
commit('setDeleted', { status })
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
},
2016-10-31 02:12:35 +11:00
favorite ({ rootState, commit }, status) {
// Optimistic favoriting...
commit('setFavorited', { status, value: true })
apiService.favorite({ id: status.id, credentials: rootState.users.currentUser.credentials })
},
unfavorite ({ rootState, commit }, status) {
// Optimistic favoriting...
commit('setFavorited', { status, value: false })
apiService.unfavorite({ id: status.id, credentials: rootState.users.currentUser.credentials })
2016-11-14 03:09:16 +11:00
},
retweet ({ rootState, commit }, status) {
// Optimistic retweeting...
commit('setRetweeted', { status, value: true })
apiService.retweet({ id: status.id, credentials: rootState.users.currentUser.credentials })
2016-10-31 02:12:35 +11:00
}
},
mutations
2016-10-27 04:03:55 +11:00
}
export default statuses