kopia lustrzana https://github.com/nolanlawson/pinafore
fix: handle status edit events (#2325)
rodzic
3edfed971f
commit
02f1dad098
|
@ -2,8 +2,9 @@ import { mark, stop } from '../../_utils/marks.js'
|
|||
import { deleteStatus } from '../deleteStatuses.js'
|
||||
import { addStatusOrNotification } from '../addStatusOrNotification.js'
|
||||
import { emit } from '../../_utils/eventBus.js'
|
||||
import { updateStatus } from '../updateStatus.js'
|
||||
|
||||
const KNOWN_EVENTS = ['update', 'delete', 'notification', 'conversation', 'filters_changed']
|
||||
const KNOWN_EVENTS = ['update', 'delete', 'notification', 'conversation', 'filters_changed', 'status.update']
|
||||
|
||||
export function processMessage (instanceName, timelineName, message) {
|
||||
let { event, payload } = (message || {})
|
||||
|
@ -12,7 +13,7 @@ export function processMessage (instanceName, timelineName, message) {
|
|||
return
|
||||
}
|
||||
mark('processMessage')
|
||||
if (['update', 'notification', 'conversation'].includes(event)) {
|
||||
if (['update', 'notification', 'conversation', 'status.update'].includes(event)) {
|
||||
payload = JSON.parse(payload) // only these payloads are JSON-encoded for some reason
|
||||
}
|
||||
|
||||
|
@ -43,6 +44,9 @@ export function processMessage (instanceName, timelineName, message) {
|
|||
case 'filters_changed':
|
||||
emit('wordFiltersChanged', instanceName)
|
||||
break
|
||||
case 'status.update':
|
||||
updateStatus(instanceName, payload)
|
||||
break
|
||||
}
|
||||
stop('processMessage')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { database } from '../_database/database.js'
|
||||
import { scheduleIdleTask } from '../_utils/scheduleIdleTask.js'
|
||||
|
||||
async function doUpdateStatus (instanceName, newStatus) {
|
||||
console.log('updating status', newStatus)
|
||||
await database.updateStatus(instanceName, newStatus)
|
||||
}
|
||||
|
||||
export function updateStatus (instanceName, newStatus) {
|
||||
scheduleIdleTask(() => {
|
||||
/* no await */ doUpdateStatus(instanceName, newStatus)
|
||||
})
|
||||
}
|
|
@ -1,18 +1,20 @@
|
|||
import { auth, basename } from './utils.js'
|
||||
import { DEFAULT_TIMEOUT, get, post, WRITE_TIMEOUT } from '../_utils/ajax.js'
|
||||
import { DEFAULT_TIMEOUT, get, post, put, WRITE_TIMEOUT } from '../_utils/ajax.js'
|
||||
|
||||
export async function postStatus (instanceName, accessToken, text, inReplyToId, mediaIds,
|
||||
// post is create, put is edit
|
||||
async function postOrPutStatus (url, accessToken, method, text, inReplyToId, mediaIds,
|
||||
sensitive, spoilerText, visibility, poll) {
|
||||
const url = `${basename(instanceName)}/api/v1/statuses`
|
||||
|
||||
const body = {
|
||||
status: text,
|
||||
in_reply_to_id: inReplyToId,
|
||||
media_ids: mediaIds,
|
||||
sensitive,
|
||||
spoiler_text: spoilerText,
|
||||
visibility,
|
||||
poll
|
||||
poll,
|
||||
...(method === 'post' && {
|
||||
// you can't change these properties when editing
|
||||
in_reply_to_id: inReplyToId,
|
||||
visibility
|
||||
})
|
||||
}
|
||||
|
||||
for (const key of Object.keys(body)) {
|
||||
|
@ -23,7 +25,23 @@ export async function postStatus (instanceName, accessToken, text, inReplyToId,
|
|||
}
|
||||
}
|
||||
|
||||
return post(url, body, auth(accessToken), { timeout: WRITE_TIMEOUT })
|
||||
const func = method === 'post' ? post : put
|
||||
|
||||
return func(url, body, auth(accessToken), { timeout: WRITE_TIMEOUT })
|
||||
}
|
||||
|
||||
export async function postStatus (instanceName, accessToken, text, inReplyToId, mediaIds,
|
||||
sensitive, spoilerText, visibility, poll) {
|
||||
const url = `${basename(instanceName)}/api/v1/statuses`
|
||||
return postOrPutStatus(url, accessToken, 'post', text, inReplyToId, mediaIds,
|
||||
sensitive, spoilerText, visibility, poll)
|
||||
}
|
||||
|
||||
export async function putStatus (instanceName, accessToken, id, text, inReplyToId, mediaIds,
|
||||
sensitive, spoilerText, visibility, poll) {
|
||||
const url = `${basename(instanceName)}/api/v1/statuses/${id}`
|
||||
return postOrPutStatus(url, accessToken, 'put', text, inReplyToId, mediaIds,
|
||||
sensitive, spoilerText, visibility, poll)
|
||||
}
|
||||
|
||||
export async function getStatusContext (instanceName, accessToken, statusId) {
|
||||
|
|
|
@ -3,12 +3,13 @@ import { getInCache, hasInCache, statusesCache } from '../cache.js'
|
|||
import { STATUSES_STORE } from '../constants.js'
|
||||
import { cacheStatus } from './cacheStatus.js'
|
||||
import { putStatus } from './insertion.js'
|
||||
import { cloneForStorage } from '../helpers.js'
|
||||
|
||||
//
|
||||
// update statuses
|
||||
//
|
||||
|
||||
async function updateStatus (instanceName, statusId, updateFunc) {
|
||||
async function doUpdateStatus (instanceName, statusId, updateFunc) {
|
||||
const db = await getDatabase(instanceName)
|
||||
if (hasInCache(statusesCache, instanceName, statusId)) {
|
||||
const status = getInCache(statusesCache, instanceName, statusId)
|
||||
|
@ -25,7 +26,7 @@ async function updateStatus (instanceName, statusId, updateFunc) {
|
|||
}
|
||||
|
||||
export async function setStatusFavorited (instanceName, statusId, favorited) {
|
||||
return updateStatus(instanceName, statusId, status => {
|
||||
return doUpdateStatus(instanceName, statusId, status => {
|
||||
const delta = (favorited ? 1 : 0) - (status.favourited ? 1 : 0)
|
||||
status.favourited = favorited
|
||||
status.favourites_count = (status.favourites_count || 0) + delta
|
||||
|
@ -33,7 +34,7 @@ export async function setStatusFavorited (instanceName, statusId, favorited) {
|
|||
}
|
||||
|
||||
export async function setStatusReblogged (instanceName, statusId, reblogged) {
|
||||
return updateStatus(instanceName, statusId, status => {
|
||||
return doUpdateStatus(instanceName, statusId, status => {
|
||||
const delta = (reblogged ? 1 : 0) - (status.reblogged ? 1 : 0)
|
||||
status.reblogged = reblogged
|
||||
status.reblogs_count = (status.reblogs_count || 0) + delta
|
||||
|
@ -41,19 +42,36 @@ export async function setStatusReblogged (instanceName, statusId, reblogged) {
|
|||
}
|
||||
|
||||
export async function setStatusPinned (instanceName, statusId, pinned) {
|
||||
return updateStatus(instanceName, statusId, status => {
|
||||
return doUpdateStatus(instanceName, statusId, status => {
|
||||
status.pinned = pinned
|
||||
})
|
||||
}
|
||||
|
||||
export async function setStatusMuted (instanceName, statusId, muted) {
|
||||
return updateStatus(instanceName, statusId, status => {
|
||||
return doUpdateStatus(instanceName, statusId, status => {
|
||||
status.muted = muted
|
||||
})
|
||||
}
|
||||
|
||||
export async function setStatusBookmarked (instanceName, statusId, bookmarked) {
|
||||
return updateStatus(instanceName, statusId, status => {
|
||||
return doUpdateStatus(instanceName, statusId, status => {
|
||||
status.bookmarked = bookmarked
|
||||
})
|
||||
}
|
||||
|
||||
// For the full list, see https://docs.joinmastodon.org/methods/statuses/#edit
|
||||
const PROPS_THAT_CAN_BE_EDITED = ['content', 'spoiler_text', 'sensitive', 'language', 'media_ids', 'poll']
|
||||
|
||||
export async function updateStatus (instanceName, newStatus) {
|
||||
const clonedNewStatus = cloneForStorage(newStatus)
|
||||
return doUpdateStatus(instanceName, newStatus.id, status => {
|
||||
// We can't use a simple Object.assign() to merge because a prop might have been deleted
|
||||
for (const prop of PROPS_THAT_CAN_BE_EDITED) {
|
||||
if (!(prop in clonedNewStatus)) {
|
||||
delete status[prop]
|
||||
} else {
|
||||
status[prop] = clonedNewStatus[prop]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { favoriteStatus } from '../src/routes/_api/favorite.js'
|
|||
import fetch from 'node-fetch'
|
||||
import FileApi from 'file-api'
|
||||
import { users } from './users.js'
|
||||
import { postStatus } from '../src/routes/_api/statuses.js'
|
||||
import { postStatus, putStatus } from '../src/routes/_api/statuses.js'
|
||||
import { deleteStatus } from '../src/routes/_api/delete.js'
|
||||
import { authorizeFollowRequest, getFollowRequests } from '../src/routes/_api/followRequests.js'
|
||||
import { followAccount, unfollowAccount } from '../src/routes/_api/follow.js'
|
||||
|
@ -33,6 +33,11 @@ export async function postAs (username, text) {
|
|||
null, null, false, null, 'public')
|
||||
}
|
||||
|
||||
export async function putAs (username, text, statusId) {
|
||||
return putStatus(instanceName, users[username].accessToken, statusId, text,
|
||||
null, null, false, null, 'public')
|
||||
}
|
||||
|
||||
export async function postWithSpoilerAndPrivacyAs (username, text, spoiler, privacy) {
|
||||
return postStatus(instanceName, users[username].accessToken, text,
|
||||
null, null, true, spoiler, privacy)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import {
|
||||
getNthStatus, getUrl, goBack,
|
||||
sleep
|
||||
} from '../utils'
|
||||
import { loginAsFoobar } from '../roles'
|
||||
import { postAs, putAs } from '../serverActions'
|
||||
|
||||
fixture`140-editing.js`
|
||||
.page`http://localhost:4002`
|
||||
|
||||
test('Edited toots are updated in the UI', async t => {
|
||||
const { id: statusId } = await postAs('admin', 'yolo')
|
||||
await sleep(500)
|
||||
|
||||
await loginAsFoobar(t)
|
||||
await t.expect(getNthStatus(1).innerText).contains('yolo', { timeout: 20000 })
|
||||
|
||||
await putAs('admin', 'wait I mean YOLO', statusId)
|
||||
await sleep(500)
|
||||
|
||||
await t.click(getNthStatus(1))
|
||||
.expect(getUrl()).contains('/statuses')
|
||||
.expect(getNthStatus(1).innerText).contains('wait I mean YOLO', { timeout: 20000 })
|
||||
await goBack()
|
||||
await t
|
||||
.expect(getUrl()).eql('http://localhost:4002/')
|
||||
.expect(getNthStatus(1).innerText).contains('wait I mean YOLO', { timeout: 20000 })
|
||||
})
|
Ładowanie…
Reference in New Issue