feat(platform): avatar mesh, DM video/screen-share, rekey prefs v0.8.635
Peer avatar image IPC, live screen-share on video calls, toggleDmVideoCall, readPeerAvatarPreview, and dismissed rekey channel id persistence. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -142,6 +142,7 @@ const {
|
||||
const { PearcordContacts } = require('pearcord-contacts')
|
||||
const { PearcordNotifications } = require('pearcord-notifications')
|
||||
const { PearcordSettings } = require('pearcord-settings')
|
||||
const { UserPrefsMirrorStore } = require('pearcord-settings/prefs-store')
|
||||
const { PearcordProfileCosmetics } = require('pearcord-profile-cosmetics')
|
||||
const {
|
||||
enrichProfileCosmeticGossip,
|
||||
@@ -331,6 +332,8 @@ class PearcordPlatform extends EventEmitter {
|
||||
this.dm = null
|
||||
this.dmVoice = null
|
||||
this._dmVoiceMedia = null
|
||||
this._dmScreenShare = null
|
||||
this._prefsMirrorStore = null
|
||||
this.attachments = null
|
||||
this.moderation = null
|
||||
this.voice = null
|
||||
@@ -921,6 +924,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
storagePath: this.storagePath,
|
||||
userId: snap.user.id
|
||||
})
|
||||
this.userSettings._prefsMirror = await this._ensurePrefsMirrorStore()
|
||||
await this.userSettings.ready()
|
||||
const sessionPrefs = await this.userSettings.getPrefs()
|
||||
this._ingestGuildPresenceCacheFromPrefs(sessionPrefs)
|
||||
@@ -1137,8 +1141,11 @@ class PearcordPlatform extends EventEmitter {
|
||||
})
|
||||
this.userSettings = new PearcordSettings({
|
||||
storagePath: this.storagePath,
|
||||
userId: user.id
|
||||
userId: user.id,
|
||||
prefsMirror: null
|
||||
})
|
||||
const prefsMirror = await this._ensurePrefsMirrorStore()
|
||||
this.userSettings._prefsMirror = prefsMirror
|
||||
this.profileCosmetics = new PearcordProfileCosmetics({
|
||||
storagePath: this.storagePath,
|
||||
userId: user.id
|
||||
@@ -1646,6 +1653,16 @@ class PearcordPlatform extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
async _ensurePrefsMirrorStore () {
|
||||
if (this._prefsMirrorStore) return this._prefsMirrorStore
|
||||
this._prefsMirrorStore = new UserPrefsMirrorStore({
|
||||
db: this.db,
|
||||
storagePath: this.storagePath
|
||||
})
|
||||
await this._prefsMirrorStore.ready()
|
||||
return this._prefsMirrorStore
|
||||
}
|
||||
|
||||
async _ensureDmMetaStore () {
|
||||
if (this._dmMetaStore) return this._dmMetaStore
|
||||
this._dmMetaStore = new DmChannelMetaStore({
|
||||
@@ -1848,7 +1865,20 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
|
||||
clearDmSessionRekeyNotice () {
|
||||
const notice = this._dmSessionRekeyNotice
|
||||
this._dmSessionRekeyNotice = null
|
||||
if (notice?.channelId && this.userSettings) {
|
||||
void this.userSettings.getPrefs().then(async (prefs) => {
|
||||
const dismissed = [...(prefs.dismissedDmRekeyChannelIds || [])]
|
||||
if (!dismissed.includes(notice.channelId)) {
|
||||
dismissed.push(notice.channelId)
|
||||
await this.userSettings.setPrefs(
|
||||
{ dismissedDmRekeyChannelIds: dismissed.slice(-48) },
|
||||
{ gossip: false }
|
||||
)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1998,6 +2028,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
channelId: ch.id,
|
||||
peerUserId,
|
||||
isGroup: !!ch.isGroup,
|
||||
participantIds: ch.isGroup ? (ch.participantIds || []) : undefined,
|
||||
voiceMediaHub: media
|
||||
})
|
||||
this.emit('dm-voice')
|
||||
@@ -2024,7 +2055,42 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async _ensureDmScreenShare () {
|
||||
const user = this.identity?.user
|
||||
if (!user?.id) throw new Error('register first')
|
||||
if (!this._dmScreenShare) {
|
||||
this._dmScreenShare = new ScreenShareHub({
|
||||
storagePath: this.storagePath,
|
||||
guildId: DM_GUILD_ID,
|
||||
userId: user.id,
|
||||
displayFeed: this.displayCapture
|
||||
})
|
||||
await this._dmScreenShare.ready()
|
||||
this._dmScreenShare.on('frame', () => this.emit('screen-share'))
|
||||
}
|
||||
return this._dmScreenShare
|
||||
}
|
||||
|
||||
async _resolvePeerAvatarHash (userId) {
|
||||
if (!userId) return null
|
||||
const presenceMap = this.contacts?.getFriendPresenceMap?.() || {}
|
||||
if (presenceMap[userId]?.avatarHash) return presenceMap[userId].avatarHash
|
||||
const user = await this.db.get(COLLECTIONS.USERS, { id: userId }).catch(() => null)
|
||||
return user?.avatarHash || null
|
||||
}
|
||||
|
||||
async readPeerAvatarPreview (userId) {
|
||||
if (!userId) throw new Error('userId required')
|
||||
const hash = await this._resolvePeerAvatarHash(userId)
|
||||
if (!hash) return null
|
||||
await this._ensureBannerAttachmentReady(hash, userId).catch(() => {})
|
||||
return this.readAttachmentPreview(hash)
|
||||
}
|
||||
|
||||
async endDmVoiceCall (reason = 'ended') {
|
||||
if (this._dmScreenShare?.active) {
|
||||
await this._dmScreenShare.stopShare().catch(() => {})
|
||||
}
|
||||
const voice = await this._ensureDmVoice()
|
||||
if (!voice || voice.state === 'idle') return { ended: false }
|
||||
const span = this.log.time('dm.voice.hangup', {
|
||||
@@ -2130,6 +2196,11 @@ class PearcordPlatform extends EventEmitter {
|
||||
peerUserId,
|
||||
voiceMediaHub: media
|
||||
})
|
||||
const screen = await this._ensureDmScreenShare().catch(() => null)
|
||||
if (screen) {
|
||||
await screen.startShare(ch.id, 'DM Video').catch(() => {})
|
||||
}
|
||||
this.emit('screen-share')
|
||||
this.emit('dm-voice')
|
||||
this.emit('voice-media')
|
||||
span.end({
|
||||
@@ -2153,6 +2224,20 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async toggleDmVideoCall () {
|
||||
const voice = await this._ensureDmVoice()
|
||||
if (voice?.snapshot?.()?.videoActive) {
|
||||
await voice.stopVideo()
|
||||
if (this._dmScreenShare?.active) {
|
||||
await this._dmScreenShare.stopShare().catch(() => {})
|
||||
}
|
||||
this.emit('dm-voice')
|
||||
this.emit('screen-share')
|
||||
return { videoActive: false, toggled: true }
|
||||
}
|
||||
return this.startDmVideoCall()
|
||||
}
|
||||
|
||||
async createGroupDM ({ peerUserIds, name }) {
|
||||
const ids = peerUserIds || []
|
||||
const span = this.log.time('dm.group.create', {
|
||||
@@ -20037,12 +20122,27 @@ class PearcordPlatform extends EventEmitter {
|
||||
peers: []
|
||||
}
|
||||
let dmMetaEngine = 'json'
|
||||
let userPrefsEngine = 'json'
|
||||
try {
|
||||
const metaStore = await this._ensureDmMetaStore()
|
||||
dmMetaEngine = metaStore.engine || 'json'
|
||||
} catch {
|
||||
dmMetaEngine = 'json'
|
||||
}
|
||||
try {
|
||||
const prefsMirror = await this._ensurePrefsMirrorStore()
|
||||
userPrefsEngine = prefsMirror.engine || 'json'
|
||||
} catch {
|
||||
userPrefsEngine = 'json'
|
||||
}
|
||||
const dismissedRekeys = new Set(
|
||||
(await this.userSettings?.getPrefs?.().catch(() => null))?.dismissedDmRekeyChannelIds || []
|
||||
)
|
||||
const dmSessionRekey =
|
||||
this._dmSessionRekeyNotice &&
|
||||
!dismissedRekeys.has(this._dmSessionRekeyNotice.channelId)
|
||||
? this._dmSessionRekeyNotice
|
||||
: null
|
||||
const activityFeed = buildActivityFeed({
|
||||
selfUserId: user?.id,
|
||||
presence: this.presence?.snapshot() || null,
|
||||
@@ -20101,6 +20201,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
permissionMeta: PERMISSION_META,
|
||||
dmConversations,
|
||||
dmMetaEngine,
|
||||
userPrefsEngine,
|
||||
globalPresence,
|
||||
dmCall: this.dmVoice?.snapshot?.() || {
|
||||
state: 'idle',
|
||||
@@ -20108,7 +20209,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
connected: false,
|
||||
peerCount: 0
|
||||
},
|
||||
dmSessionRekey: this._dmSessionRekeyNotice,
|
||||
dmSessionRekey,
|
||||
dmPeer: this._dmPeerId(),
|
||||
dmTitle: this.dm?.channel?.name || null,
|
||||
dmIsGroup: !!this.dm?.channel?.isGroup,
|
||||
|
||||
Reference in New Issue
Block a user