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 { PearcordContacts } = require('pearcord-contacts')
|
||||||
const { PearcordNotifications } = require('pearcord-notifications')
|
const { PearcordNotifications } = require('pearcord-notifications')
|
||||||
const { PearcordSettings } = require('pearcord-settings')
|
const { PearcordSettings } = require('pearcord-settings')
|
||||||
|
const { UserPrefsMirrorStore } = require('pearcord-settings/prefs-store')
|
||||||
const { PearcordProfileCosmetics } = require('pearcord-profile-cosmetics')
|
const { PearcordProfileCosmetics } = require('pearcord-profile-cosmetics')
|
||||||
const {
|
const {
|
||||||
enrichProfileCosmeticGossip,
|
enrichProfileCosmeticGossip,
|
||||||
@@ -331,6 +332,8 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
this.dm = null
|
this.dm = null
|
||||||
this.dmVoice = null
|
this.dmVoice = null
|
||||||
this._dmVoiceMedia = null
|
this._dmVoiceMedia = null
|
||||||
|
this._dmScreenShare = null
|
||||||
|
this._prefsMirrorStore = null
|
||||||
this.attachments = null
|
this.attachments = null
|
||||||
this.moderation = null
|
this.moderation = null
|
||||||
this.voice = null
|
this.voice = null
|
||||||
@@ -921,6 +924,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
storagePath: this.storagePath,
|
storagePath: this.storagePath,
|
||||||
userId: snap.user.id
|
userId: snap.user.id
|
||||||
})
|
})
|
||||||
|
this.userSettings._prefsMirror = await this._ensurePrefsMirrorStore()
|
||||||
await this.userSettings.ready()
|
await this.userSettings.ready()
|
||||||
const sessionPrefs = await this.userSettings.getPrefs()
|
const sessionPrefs = await this.userSettings.getPrefs()
|
||||||
this._ingestGuildPresenceCacheFromPrefs(sessionPrefs)
|
this._ingestGuildPresenceCacheFromPrefs(sessionPrefs)
|
||||||
@@ -1137,8 +1141,11 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
})
|
})
|
||||||
this.userSettings = new PearcordSettings({
|
this.userSettings = new PearcordSettings({
|
||||||
storagePath: this.storagePath,
|
storagePath: this.storagePath,
|
||||||
userId: user.id
|
userId: user.id,
|
||||||
|
prefsMirror: null
|
||||||
})
|
})
|
||||||
|
const prefsMirror = await this._ensurePrefsMirrorStore()
|
||||||
|
this.userSettings._prefsMirror = prefsMirror
|
||||||
this.profileCosmetics = new PearcordProfileCosmetics({
|
this.profileCosmetics = new PearcordProfileCosmetics({
|
||||||
storagePath: this.storagePath,
|
storagePath: this.storagePath,
|
||||||
userId: user.id
|
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 () {
|
async _ensureDmMetaStore () {
|
||||||
if (this._dmMetaStore) return this._dmMetaStore
|
if (this._dmMetaStore) return this._dmMetaStore
|
||||||
this._dmMetaStore = new DmChannelMetaStore({
|
this._dmMetaStore = new DmChannelMetaStore({
|
||||||
@@ -1848,7 +1865,20 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearDmSessionRekeyNotice () {
|
clearDmSessionRekeyNotice () {
|
||||||
|
const notice = this._dmSessionRekeyNotice
|
||||||
this._dmSessionRekeyNotice = null
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1998,6 +2028,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
channelId: ch.id,
|
channelId: ch.id,
|
||||||
peerUserId,
|
peerUserId,
|
||||||
isGroup: !!ch.isGroup,
|
isGroup: !!ch.isGroup,
|
||||||
|
participantIds: ch.isGroup ? (ch.participantIds || []) : undefined,
|
||||||
voiceMediaHub: media
|
voiceMediaHub: media
|
||||||
})
|
})
|
||||||
this.emit('dm-voice')
|
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') {
|
async endDmVoiceCall (reason = 'ended') {
|
||||||
|
if (this._dmScreenShare?.active) {
|
||||||
|
await this._dmScreenShare.stopShare().catch(() => {})
|
||||||
|
}
|
||||||
const voice = await this._ensureDmVoice()
|
const voice = await this._ensureDmVoice()
|
||||||
if (!voice || voice.state === 'idle') return { ended: false }
|
if (!voice || voice.state === 'idle') return { ended: false }
|
||||||
const span = this.log.time('dm.voice.hangup', {
|
const span = this.log.time('dm.voice.hangup', {
|
||||||
@@ -2130,6 +2196,11 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
peerUserId,
|
peerUserId,
|
||||||
voiceMediaHub: media
|
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('dm-voice')
|
||||||
this.emit('voice-media')
|
this.emit('voice-media')
|
||||||
span.end({
|
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 }) {
|
async createGroupDM ({ peerUserIds, name }) {
|
||||||
const ids = peerUserIds || []
|
const ids = peerUserIds || []
|
||||||
const span = this.log.time('dm.group.create', {
|
const span = this.log.time('dm.group.create', {
|
||||||
@@ -20037,12 +20122,27 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
peers: []
|
peers: []
|
||||||
}
|
}
|
||||||
let dmMetaEngine = 'json'
|
let dmMetaEngine = 'json'
|
||||||
|
let userPrefsEngine = 'json'
|
||||||
try {
|
try {
|
||||||
const metaStore = await this._ensureDmMetaStore()
|
const metaStore = await this._ensureDmMetaStore()
|
||||||
dmMetaEngine = metaStore.engine || 'json'
|
dmMetaEngine = metaStore.engine || 'json'
|
||||||
} catch {
|
} catch {
|
||||||
dmMetaEngine = 'json'
|
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({
|
const activityFeed = buildActivityFeed({
|
||||||
selfUserId: user?.id,
|
selfUserId: user?.id,
|
||||||
presence: this.presence?.snapshot() || null,
|
presence: this.presence?.snapshot() || null,
|
||||||
@@ -20101,6 +20201,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
permissionMeta: PERMISSION_META,
|
permissionMeta: PERMISSION_META,
|
||||||
dmConversations,
|
dmConversations,
|
||||||
dmMetaEngine,
|
dmMetaEngine,
|
||||||
|
userPrefsEngine,
|
||||||
globalPresence,
|
globalPresence,
|
||||||
dmCall: this.dmVoice?.snapshot?.() || {
|
dmCall: this.dmVoice?.snapshot?.() || {
|
||||||
state: 'idle',
|
state: 'idle',
|
||||||
@@ -20108,7 +20209,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
connected: false,
|
connected: false,
|
||||||
peerCount: 0
|
peerCount: 0
|
||||||
},
|
},
|
||||||
dmSessionRekey: this._dmSessionRekeyNotice,
|
dmSessionRekey,
|
||||||
dmPeer: this._dmPeerId(),
|
dmPeer: this._dmPeerId(),
|
||||||
dmTitle: this.dm?.channel?.name || null,
|
dmTitle: this.dm?.channel?.name || null,
|
||||||
dmIsGroup: !!this.dm?.channel?.isGroup,
|
dmIsGroup: !!this.dm?.channel?.isGroup,
|
||||||
|
|||||||
Reference in New Issue
Block a user