:Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into develop
This commit is contained in:
commit
7e1b1ec990
50 changed files with 1054 additions and 621 deletions
|
@ -130,7 +130,11 @@ const promisedRequest = ({ method, url, params, payload, credentials, headers =
|
|||
return reject(new StatusCodeError(response.status, json, { url, options }, response))
|
||||
}
|
||||
return resolve(json)
|
||||
}))
|
||||
})
|
||||
.catch((error) => {
|
||||
return reject(new StatusCodeError(response.status, error, { url, options }, response))
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1225,7 +1229,7 @@ const chatMessages = ({ id, credentials, maxId, sinceId, limit = 20 }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const sendChatMessage = ({ id, content, mediaId = null, credentials }) => {
|
||||
const sendChatMessage = ({ id, content, mediaId = null, idempotencyKey, credentials }) => {
|
||||
const payload = {
|
||||
'content': content
|
||||
}
|
||||
|
@ -1234,11 +1238,18 @@ const sendChatMessage = ({ id, content, mediaId = null, credentials }) => {
|
|||
payload['media_id'] = mediaId
|
||||
}
|
||||
|
||||
const headers = {}
|
||||
|
||||
if (idempotencyKey) {
|
||||
headers['idempotency-key'] = idempotencyKey
|
||||
}
|
||||
|
||||
return promisedRequest({
|
||||
url: PLEROMA_CHAT_MESSAGES_URL(id),
|
||||
method: 'POST',
|
||||
payload: payload,
|
||||
credentials
|
||||
credentials,
|
||||
headers
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@ import _ from 'lodash'
|
|||
const empty = (chatId) => {
|
||||
return {
|
||||
idIndex: {},
|
||||
idempotencyKeyIndex: {},
|
||||
messages: [],
|
||||
newMessageCount: 0,
|
||||
lastSeenTimestamp: 0,
|
||||
lastSeenMessageId: '0',
|
||||
chatId: chatId,
|
||||
minId: undefined,
|
||||
maxId: undefined
|
||||
|
@ -13,10 +14,20 @@ const empty = (chatId) => {
|
|||
}
|
||||
|
||||
const clear = (storage) => {
|
||||
storage.idIndex = {}
|
||||
storage.messages.splice(0, storage.messages.length)
|
||||
const failedMessageIds = []
|
||||
|
||||
for (const message of storage.messages) {
|
||||
if (message.error) {
|
||||
failedMessageIds.push(message.id)
|
||||
} else {
|
||||
delete storage.idIndex[message.id]
|
||||
delete storage.idempotencyKeyIndex[message.id]
|
||||
}
|
||||
}
|
||||
|
||||
storage.messages = storage.messages.filter(m => failedMessageIds.includes(m.id))
|
||||
storage.newMessageCount = 0
|
||||
storage.lastSeenTimestamp = 0
|
||||
storage.lastSeenMessageId = '0'
|
||||
storage.minId = undefined
|
||||
storage.maxId = undefined
|
||||
}
|
||||
|
@ -37,6 +48,25 @@ const deleteMessage = (storage, messageId) => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleMessageError = (storage, fakeId, isRetry) => {
|
||||
if (!storage) { return }
|
||||
const fakeMessage = storage.idIndex[fakeId]
|
||||
if (fakeMessage) {
|
||||
fakeMessage.error = true
|
||||
fakeMessage.pending = false
|
||||
if (!isRetry) {
|
||||
// Ensure the failed message doesn't stay at the bottom of the list.
|
||||
const lastPersistedMessage = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'desc'])[0]
|
||||
if (lastPersistedMessage) {
|
||||
const oldId = fakeMessage.id
|
||||
fakeMessage.id = `${lastPersistedMessage.id}-${new Date().getTime()}`
|
||||
storage.idIndex[fakeMessage.id] = fakeMessage
|
||||
delete storage.idIndex[oldId]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const add = (storage, { messages: newMessages, updateMaxId = true }) => {
|
||||
if (!storage) { return }
|
||||
for (let i = 0; i < newMessages.length; i++) {
|
||||
|
@ -45,7 +75,25 @@ const add = (storage, { messages: newMessages, updateMaxId = true }) => {
|
|||
// sanity check
|
||||
if (message.chat_id !== storage.chatId) { return }
|
||||
|
||||
if (!storage.minId || message.id < storage.minId) {
|
||||
if (message.fakeId) {
|
||||
const fakeMessage = storage.idIndex[message.fakeId]
|
||||
if (fakeMessage) {
|
||||
// In case the same id exists (chat update before POST response)
|
||||
// make sure to remove the older duplicate message.
|
||||
if (storage.idIndex[message.id]) {
|
||||
delete storage.idIndex[message.id]
|
||||
storage.messages = storage.messages.filter(msg => msg.id !== message.id)
|
||||
}
|
||||
Object.assign(fakeMessage, message, { error: false })
|
||||
delete fakeMessage['fakeId']
|
||||
storage.idIndex[fakeMessage.id] = fakeMessage
|
||||
delete storage.idIndex[message.fakeId]
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!storage.minId || (!message.pending && message.id < storage.minId)) {
|
||||
storage.minId = message.id
|
||||
}
|
||||
|
||||
|
@ -55,20 +103,26 @@ const add = (storage, { messages: newMessages, updateMaxId = true }) => {
|
|||
}
|
||||
}
|
||||
|
||||
if (!storage.idIndex[message.id]) {
|
||||
if (storage.lastSeenTimestamp < message.created_at) {
|
||||
if (!storage.idIndex[message.id] && !isConfirmation(storage, message)) {
|
||||
if (storage.lastSeenMessageId < message.id) {
|
||||
storage.newMessageCount++
|
||||
}
|
||||
storage.messages.push(message)
|
||||
storage.idIndex[message.id] = message
|
||||
storage.messages.push(storage.idIndex[message.id])
|
||||
storage.idempotencyKeyIndex[message.idempotency_key] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isConfirmation = (storage, message) => {
|
||||
if (!message.idempotency_key) return
|
||||
return storage.idempotencyKeyIndex[message.idempotency_key]
|
||||
}
|
||||
|
||||
const resetNewMessageCount = (storage) => {
|
||||
if (!storage) { return }
|
||||
storage.newMessageCount = 0
|
||||
storage.lastSeenTimestamp = new Date()
|
||||
storage.lastSeenMessageId = storage.maxId
|
||||
}
|
||||
|
||||
// Inserts date separators and marks the head and tail if it's the chain of messages made by the same user
|
||||
|
@ -76,7 +130,7 @@ const getView = (storage) => {
|
|||
if (!storage) { return [] }
|
||||
|
||||
const result = []
|
||||
const messages = _.sortBy(storage.messages, ['id', 'desc'])
|
||||
const messages = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'asc'])
|
||||
const firstMessage = messages[0]
|
||||
let previousMessage = messages[messages.length - 1]
|
||||
let currentMessageChainId
|
||||
|
@ -148,7 +202,8 @@ const ChatService = {
|
|||
getView,
|
||||
deleteMessage,
|
||||
resetNewMessageCount,
|
||||
clear
|
||||
clear,
|
||||
handleMessageError
|
||||
}
|
||||
|
||||
export default ChatService
|
||||
|
|
|
@ -3,7 +3,7 @@ import { showDesktopNotification } from '../desktop_notification_utils/desktop_n
|
|||
export const maybeShowChatNotification = (store, chat) => {
|
||||
if (!chat.lastMessage) return
|
||||
if (store.rootState.chats.currentChatId === chat.id && !document.hidden) return
|
||||
if (store.rootState.users.currentUser.id === chat.lastMessage.account.id) return
|
||||
if (store.rootState.users.currentUser.id === chat.lastMessage.account_id) return
|
||||
|
||||
const opts = {
|
||||
tag: chat.lastMessage.id,
|
||||
|
@ -18,3 +18,24 @@ export const maybeShowChatNotification = (store, chat) => {
|
|||
|
||||
showDesktopNotification(store.rootState, opts)
|
||||
}
|
||||
|
||||
export const buildFakeMessage = ({ content, chatId, attachments, userId, idempotencyKey }) => {
|
||||
const fakeMessage = {
|
||||
content,
|
||||
chat_id: chatId,
|
||||
created_at: new Date(),
|
||||
id: `${new Date().getTime()}`,
|
||||
attachments: attachments,
|
||||
account_id: userId,
|
||||
idempotency_key: idempotencyKey,
|
||||
emojis: [],
|
||||
pending: true,
|
||||
isNormalized: true
|
||||
}
|
||||
|
||||
if (attachments[0]) {
|
||||
fakeMessage.attachment = attachments[0]
|
||||
}
|
||||
|
||||
return fakeMessage
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export const parseUser = (data) => {
|
|||
output.fields = data.fields
|
||||
output.fields_html = data.fields.map(field => {
|
||||
return {
|
||||
name: addEmojis(field.name, data.emojis),
|
||||
name: addEmojis(escape(field.name), data.emojis),
|
||||
value: addEmojis(field.value, data.emojis)
|
||||
}
|
||||
})
|
||||
|
@ -429,6 +429,9 @@ export const parseChatMessage = (message) => {
|
|||
} else {
|
||||
output.attachments = []
|
||||
}
|
||||
output.pending = !!message.pending
|
||||
output.error = false
|
||||
output.idempotency_key = message.idempotency_key
|
||||
output.isNormalized = true
|
||||
return output
|
||||
}
|
||||
|
|
|
@ -10,7 +10,14 @@ export const promiseInterval = (promiseCall, interval) => {
|
|||
let timeout = null
|
||||
|
||||
const func = () => {
|
||||
promiseCall().finally(() => {
|
||||
const promise = promiseCall()
|
||||
// something unexpected happened and promiseCall did not
|
||||
// return a promise, abort the loop.
|
||||
if (!(promise && promise.finally)) {
|
||||
console.warn('promiseInterval: promise call did not return a promise, stopping interval.')
|
||||
return
|
||||
}
|
||||
promise.finally(() => {
|
||||
if (stopped) return
|
||||
timeout = window.setTimeout(func, interval)
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue