fix(contacts): sync profile fields on presence gossip and USER_UPSERT
Store username/displayName in friend presence, set peerUsername on accept, and use guarded mesh broadcast. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -22,6 +22,20 @@ function contactKey (ownerId, peerUserId) {
|
||||
return { ownerId, peerUserId }
|
||||
}
|
||||
|
||||
function presenceGossipRow (payload, fallbackUserId) {
|
||||
return {
|
||||
userId: payload?.userId || fallbackUserId,
|
||||
status: payload?.status || USER_STATUS.ONLINE,
|
||||
customStatus: payload?.customStatus ?? null,
|
||||
activity: normalizeActivity(payload?.activity),
|
||||
username: payload?.username || null,
|
||||
displayName: payload?.displayName || null,
|
||||
discriminator: payload?.discriminator || null,
|
||||
avatarHash: payload?.avatarHash || null,
|
||||
at: payload?.at || now()
|
||||
}
|
||||
}
|
||||
|
||||
class PearcordContacts extends EventEmitter {
|
||||
constructor (opts = {}) {
|
||||
super()
|
||||
@@ -272,7 +286,8 @@ class PearcordContacts extends EventEmitter {
|
||||
}
|
||||
row.status = CONTACT_STATUS.ACCEPTED
|
||||
row.acceptedAt = now()
|
||||
row.peerDisplayName = payload.fromDisplayName || row.peerDisplayName
|
||||
row.peerUsername = payload.fromUsername || row.peerUsername || null
|
||||
row.peerDisplayName = payload.fromDisplayName || payload.fromUsername || row.peerDisplayName
|
||||
await this.store.insert(CONTACTS_COLLECTION, row)
|
||||
await this._syncFriendTopicLinks()
|
||||
this.emit('contact', row)
|
||||
@@ -300,27 +315,24 @@ class PearcordContacts extends EventEmitter {
|
||||
|
||||
ingestFriendPresence (payload) {
|
||||
if (!payload?.userId || payload.userId === this.userId) return null
|
||||
const row = {
|
||||
userId: payload.userId,
|
||||
status: payload.status || USER_STATUS.ONLINE,
|
||||
customStatus: payload.customStatus || null,
|
||||
activity: normalizeActivity(payload.activity),
|
||||
at: payload.at || now()
|
||||
}
|
||||
const row = presenceGossipRow(payload)
|
||||
this._friendPresence.set(payload.userId, row)
|
||||
this.emit('friend-presence', row)
|
||||
if (row.username || row.displayName) {
|
||||
this.emit('user', {
|
||||
id: row.userId,
|
||||
username: row.username,
|
||||
displayName: row.displayName,
|
||||
discriminator: row.discriminator,
|
||||
avatarHash: row.avatarHash
|
||||
})
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
async broadcastPresence (payload) {
|
||||
if (!payload?.userId) return null
|
||||
this._lastSelfPresence = {
|
||||
userId: payload.userId,
|
||||
status: payload.status || USER_STATUS.ONLINE,
|
||||
customStatus: payload.customStatus || null,
|
||||
activity: normalizeActivity(payload.activity),
|
||||
at: payload.at || now()
|
||||
}
|
||||
this._lastSelfPresence = presenceGossipRow(payload, this.userId)
|
||||
if (this._meshJoined) {
|
||||
await broadcastContactsGossip(this, RPC.PRESENCE_UPDATE, this._lastSelfPresence)
|
||||
}
|
||||
@@ -342,6 +354,10 @@ class PearcordContacts extends EventEmitter {
|
||||
}
|
||||
|
||||
_onGossip (method, payload) {
|
||||
if (method === RPC.USER_UPSERT) {
|
||||
if (payload?.id) this.emit('user', payload)
|
||||
return
|
||||
}
|
||||
if (method === RPC.PRESENCE_UPDATE) {
|
||||
this.ingestFriendPresence(payload)
|
||||
return
|
||||
@@ -374,6 +390,10 @@ class PearcordContacts extends EventEmitter {
|
||||
this.emit('dm-channel', payload)
|
||||
return payload
|
||||
}
|
||||
if (method === RPC.USER_UPSERT) {
|
||||
if (payload?.id) this.emit('user', payload)
|
||||
return payload
|
||||
}
|
||||
if (method === RPC.PRESENCE_UPDATE) return this.ingestFriendPresence(payload)
|
||||
if (method === RPC.PROFILE_COSMETIC_UPDATE) {
|
||||
this.emit('profile-cosmetic', payload)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const Protomux = require('protomux')
|
||||
const b4a = require('b4a')
|
||||
const { encodeRpc, decodeRpc, wireSwarmConnection } = require('pearcord-shared')
|
||||
const { openWireChannel, wireReady } = require('pearcord-drive/mux-wire')
|
||||
const { decodeRpc, wireSwarmConnection } = require('pearcord-shared')
|
||||
const { openWireChannel, broadcastRpcToSessions } = require('pearcord-drive/mux-wire')
|
||||
|
||||
const CONTACTS_PROTOCOL = 'pearcord-contacts-v1'
|
||||
|
||||
@@ -26,19 +26,7 @@ function attachContactsMesh (contacts, conn) {
|
||||
}
|
||||
|
||||
async function broadcastContactsGossip (contacts, method, payload) {
|
||||
const buf = encodeRpc(method, b4a.from(JSON.stringify(payload)))
|
||||
const sessions = [...contacts._channels.values()]
|
||||
await Promise.all(
|
||||
sessions.map(async (session) => {
|
||||
try {
|
||||
const ok = await wireReady(session, 6000)
|
||||
if (!ok) return
|
||||
session.send(buf)
|
||||
} catch {
|
||||
// peer gone
|
||||
}
|
||||
})
|
||||
)
|
||||
await broadcastRpcToSessions(contacts._channels, method, payload, { wireReadyMs: 6000 })
|
||||
}
|
||||
|
||||
module.exports = { attachContactsMesh, broadcastContactsGossip, CONTACTS_PROTOCOL }
|
||||
|
||||
Reference in New Issue
Block a user