Clean up polling, make sync
(cherry picked from commit 7f75cd04c3bf81c94662785748698640a84bef51)
This commit is contained in:
+67
-47
@@ -2,6 +2,7 @@ import {AppBskyActorDefs} from '@atproto/api'
|
|||||||
import {
|
import {
|
||||||
BskyAgent,
|
BskyAgent,
|
||||||
ChatBskyConvoDefs,
|
ChatBskyConvoDefs,
|
||||||
|
ChatBskyConvoGetLog,
|
||||||
ChatBskyConvoSendMessage,
|
ChatBskyConvoSendMessage,
|
||||||
} from '@atproto-labs/api'
|
} from '@atproto-labs/api'
|
||||||
import {nanoid} from 'nanoid/non-secure'
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
@@ -181,7 +182,7 @@ export type ConvoState =
|
|||||||
fetchMessageHistory: undefined
|
fetchMessageHistory: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTIVE_POLL_INTERVAL = 2e3
|
const ACTIVE_POLL_INTERVAL = 1e3
|
||||||
const BACKGROUND_POLL_INTERVAL = 10e3
|
const BACKGROUND_POLL_INTERVAL = 10e3
|
||||||
|
|
||||||
// TODO temporary
|
// TODO temporary
|
||||||
@@ -236,13 +237,6 @@ export class Convo {
|
|||||||
private isProcessingPendingMessages = false
|
private isProcessingPendingMessages = false
|
||||||
private pendingPoll: Promise<void> | undefined
|
private pendingPoll: Promise<void> | undefined
|
||||||
private nextPoll: NodeJS.Timeout | undefined
|
private nextPoll: NodeJS.Timeout | undefined
|
||||||
private pendingConvoFetch:
|
|
||||||
| Promise<{
|
|
||||||
convo: ChatBskyConvoDefs.ConvoView
|
|
||||||
sender: AppBskyActorDefs.ProfileViewBasic | undefined
|
|
||||||
recipients: AppBskyActorDefs.ProfileViewBasic[]
|
|
||||||
}>
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
convoId: string
|
convoId: string
|
||||||
convo: ChatBskyConvoDefs.ConvoView | undefined
|
convo: ChatBskyConvoDefs.ConvoView | undefined
|
||||||
@@ -586,10 +580,17 @@ export class Convo {
|
|||||||
DEBUG_ACTIVE_CHAT = undefined
|
DEBUG_ACTIVE_CHAT = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private pendingFetchConvo:
|
||||||
|
| Promise<{
|
||||||
|
convo: ChatBskyConvoDefs.ConvoView
|
||||||
|
sender: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||||
|
recipients: AppBskyActorDefs.ProfileViewBasic[]
|
||||||
|
}>
|
||||||
|
| undefined
|
||||||
async fetchConvo() {
|
async fetchConvo() {
|
||||||
if (this.pendingConvoFetch) return this.pendingConvoFetch
|
if (this.pendingFetchConvo) return this.pendingFetchConvo
|
||||||
|
|
||||||
this.pendingConvoFetch = new Promise<{
|
this.pendingFetchConvo = new Promise<{
|
||||||
convo: ChatBskyConvoDefs.ConvoView
|
convo: ChatBskyConvoDefs.ConvoView
|
||||||
sender: AppBskyActorDefs.ProfileViewBasic | undefined
|
sender: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||||
recipients: AppBskyActorDefs.ProfileViewBasic[]
|
recipients: AppBskyActorDefs.ProfileViewBasic[]
|
||||||
@@ -618,11 +619,11 @@ export class Convo {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e)
|
reject(e)
|
||||||
} finally {
|
} finally {
|
||||||
this.pendingConvoFetch = undefined
|
this.pendingFetchConvo = undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.pendingConvoFetch
|
return this.pendingFetchConvo
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshConvo() {
|
async refreshConvo() {
|
||||||
@@ -731,16 +732,14 @@ export class Convo {
|
|||||||
|
|
||||||
private initiatePoll() {
|
private initiatePoll() {
|
||||||
this.cancelNextPoll()
|
this.cancelNextPoll()
|
||||||
this.pollEvents()
|
this.pollLatestEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
private cancelNextPoll() {
|
private cancelNextPoll() {
|
||||||
if (this.nextPoll) clearTimeout(this.nextPoll)
|
if (this.nextPoll) clearTimeout(this.nextPoll)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async pollEvents() {
|
private pollLatestEvents() {
|
||||||
if (this.pendingPoll) return
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.status === ConvoStatus.Ready ||
|
this.status === ConvoStatus.Ready ||
|
||||||
this.status === ConvoStatus.Backgrounded
|
this.status === ConvoStatus.Backgrounded
|
||||||
@@ -748,24 +747,23 @@ export class Convo {
|
|||||||
/*
|
/*
|
||||||
* Uncomment to view poll events
|
* Uncomment to view poll events
|
||||||
*/
|
*/
|
||||||
// logger.debug(
|
logger.debug(
|
||||||
// 'Convo: poll events',
|
'Convo: poll events',
|
||||||
// {id: this.id},
|
{id: this.id},
|
||||||
// logger.DebugContext.convo,
|
logger.DebugContext.convo,
|
||||||
// )
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.pendingPoll = this.ingestLatestEvents()
|
this.fetchLatestEvents().then(({events}) => {
|
||||||
await this.pendingPoll
|
this.applyLatestEvents(events)
|
||||||
this.pendingPoll = undefined
|
})
|
||||||
this.nextPoll = setTimeout(() => {
|
this.nextPoll = setTimeout(() => {
|
||||||
this.pollEvents()
|
this.pollLatestEvents()
|
||||||
}, this.pollInterval)
|
}, this.pollInterval)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error('Convo: poll events failed')
|
logger.error('Convo: poll events failed')
|
||||||
|
|
||||||
this.cancelNextPoll()
|
this.cancelNextPoll()
|
||||||
this.pendingPoll = undefined
|
|
||||||
|
|
||||||
this.footerItems.set(ConvoItemError.PollFailed, {
|
this.footerItems.set(ConvoItemError.PollFailed, {
|
||||||
type: 'error-recoverable',
|
type: 'error-recoverable',
|
||||||
@@ -774,7 +772,7 @@ export class Convo {
|
|||||||
retry: () => {
|
retry: () => {
|
||||||
this.footerItems.delete(ConvoItemError.PollFailed)
|
this.footerItems.delete(ConvoItemError.PollFailed)
|
||||||
this.commit()
|
this.commit()
|
||||||
this.pollEvents()
|
this.pollLatestEvents()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -785,7 +783,18 @@ export class Convo {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
async ingestLatestEvents() {
|
private pendingFetchLatestEvents:
|
||||||
|
| Promise<{
|
||||||
|
events: ChatBskyConvoGetLog.OutputSchema['logs']
|
||||||
|
}>
|
||||||
|
| undefined
|
||||||
|
async fetchLatestEvents() {
|
||||||
|
if (this.pendingFetchLatestEvents) return this.pendingFetchLatestEvents
|
||||||
|
|
||||||
|
this.pendingFetchLatestEvents = new Promise<{
|
||||||
|
events: ChatBskyConvoGetLog.OutputSchema['logs']
|
||||||
|
}>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
// throw new Error('UNCOMMENT TO TEST POLL FAILURE')
|
// throw new Error('UNCOMMENT TO TEST POLL FAILURE')
|
||||||
const response = await this.agent.api.chat.bsky.convo.getLog(
|
const response = await this.agent.api.chat.bsky.convo.getLog(
|
||||||
{
|
{
|
||||||
@@ -798,57 +807,68 @@ export class Convo {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
const {logs} = response.data
|
const {logs} = response.data
|
||||||
|
resolve({events: logs})
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
} finally {
|
||||||
|
this.pendingFetchLatestEvents = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.pendingFetchLatestEvents
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyLatestEvents(events: ChatBskyConvoGetLog.OutputSchema['logs']) {
|
||||||
let needsCommit = false
|
let needsCommit = false
|
||||||
|
|
||||||
for (const log of logs) {
|
for (const ev of events) {
|
||||||
/*
|
/*
|
||||||
* If there's a rev, we should handle it. If there's not a rev, we don't
|
* If there's a rev, we should handle it. If there's not a rev, we don't
|
||||||
* know what it is.
|
* know what it is.
|
||||||
*/
|
*/
|
||||||
if (typeof log.rev === 'string') {
|
if (typeof ev.rev === 'string') {
|
||||||
/*
|
/*
|
||||||
* We only care about new events
|
* We only care about new events
|
||||||
*/
|
*/
|
||||||
if (log.rev > (this.eventsCursor = this.eventsCursor || log.rev)) {
|
if (ev.rev > (this.eventsCursor = this.eventsCursor || ev.rev)) {
|
||||||
/*
|
/*
|
||||||
* Update rev regardless of if it's a log type we care about or not
|
* Update rev regardless of if it's a ev type we care about or not
|
||||||
*/
|
*/
|
||||||
this.eventsCursor = log.rev
|
this.eventsCursor = ev.rev
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is VERY important. We don't want to insert any messages from
|
* This is VERY important. We don't want to insert any messages from
|
||||||
* your other chats.
|
* your other chats.
|
||||||
*/
|
*/
|
||||||
if (log.convoId !== this.convoId) continue
|
if (ev.convoId !== this.convoId) continue
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ChatBskyConvoDefs.isLogCreateMessage(log) &&
|
ChatBskyConvoDefs.isLogCreateMessage(ev) &&
|
||||||
ChatBskyConvoDefs.isMessageView(log.message)
|
ChatBskyConvoDefs.isMessageView(ev.message)
|
||||||
) {
|
) {
|
||||||
if (this.newMessages.has(log.message.id)) {
|
if (this.newMessages.has(ev.message.id)) {
|
||||||
// Trust the log as the source of truth on ordering
|
// Trust the ev as the source of truth on ordering
|
||||||
this.newMessages.delete(log.message.id)
|
this.newMessages.delete(ev.message.id)
|
||||||
}
|
}
|
||||||
this.newMessages.set(log.message.id, log.message)
|
this.newMessages.set(ev.message.id, ev.message)
|
||||||
needsCommit = true
|
needsCommit = true
|
||||||
} else if (
|
} else if (
|
||||||
ChatBskyConvoDefs.isLogDeleteMessage(log) &&
|
ChatBskyConvoDefs.isLogDeleteMessage(ev) &&
|
||||||
ChatBskyConvoDefs.isDeletedMessageView(log.message)
|
ChatBskyConvoDefs.isDeletedMessageView(ev.message)
|
||||||
) {
|
) {
|
||||||
/*
|
/*
|
||||||
* Update if we have this in state. If we don't, don't worry about it.
|
* Update if we have this in state. If we don't, don't worry about it.
|
||||||
*/
|
*/
|
||||||
if (this.pastMessages.has(log.message.id)) {
|
if (this.pastMessages.has(ev.message.id)) {
|
||||||
/*
|
/*
|
||||||
* For now, we remove deleted messages from the thread, if we receive one.
|
* For now, we remove deleted messages from the thread, if we receive one.
|
||||||
*
|
*
|
||||||
* To support them, it'd look something like this:
|
* To support them, it'd look something like this:
|
||||||
* this.pastMessages.set(log.message.id, log.message)
|
* this.pastMessages.set(ev.message.id, ev.message)
|
||||||
*/
|
*/
|
||||||
this.pastMessages.delete(log.message.id)
|
this.pastMessages.delete(ev.message.id)
|
||||||
this.newMessages.delete(log.message.id)
|
this.newMessages.delete(ev.message.id)
|
||||||
this.deletedMessages.delete(log.message.id)
|
this.deletedMessages.delete(ev.message.id)
|
||||||
needsCommit = true
|
needsCommit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user