chore: publish pearcord-ui-flow from pearcord workspace
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# pearcord-ui-flow
|
||||||
|
|
||||||
|
Single source of truth for **desktop UI → sidecar IPC** (`dispatchUiMessage`) plus Bare smoke scenarios.
|
||||||
|
|
||||||
|
See [UI_FLOW.md](../../docs/UI_FLOW.md).
|
||||||
@@ -0,0 +1,566 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const b4a = require('b4a')
|
||||||
|
const { USER_STATUS } = require('pearcord-platform')
|
||||||
|
|
||||||
|
function fromBase64 (str) {
|
||||||
|
if (typeof Buffer !== 'undefined') return Buffer.from(str || '', 'base64')
|
||||||
|
return b4a.from(str || '', 'base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeEmojiImage (image) {
|
||||||
|
if (!image?.dataBase64) return image
|
||||||
|
return {
|
||||||
|
data: fromBase64(image.dataBase64),
|
||||||
|
filename: image.filename,
|
||||||
|
mimeType: image.mimeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch one UI → sidecar IPC message (same contract as apps/pearcord/index.js).
|
||||||
|
* @param {import('pearcord-platform').PearcordPlatform} platform
|
||||||
|
* @param {object} msg parsed JSON line from pear-pipe
|
||||||
|
* @param {{ send?: (obj: object) => void, pushState?: () => void }} hooks
|
||||||
|
* @returns {Promise<boolean>} true when type was handled
|
||||||
|
*/
|
||||||
|
async function dispatchUiMessage (platform, msg, hooks = {}) {
|
||||||
|
const send = hooks.send || (() => {})
|
||||||
|
const pushState = hooks.pushState || (() => {})
|
||||||
|
const t = msg?.type
|
||||||
|
if (!t) return false
|
||||||
|
|
||||||
|
if (t === 'register') {
|
||||||
|
await platform.registerUser({
|
||||||
|
username: msg.username,
|
||||||
|
displayName: msg.displayName || msg.username
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-guild') {
|
||||||
|
await platform.createGuild({ name: msg.name || 'My Server' })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'join-invite') {
|
||||||
|
await platform.joinInvite(msg.code)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'join-public-discovery') {
|
||||||
|
await platform.joinPublicDiscovery(msg.inviteCode)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-discovery-listing') {
|
||||||
|
await platform.setGuildDiscoveryListing(msg.guildId, msg.listed !== false)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-discovery-filter') {
|
||||||
|
platform.setDiscoveryFilter({
|
||||||
|
query: msg.query,
|
||||||
|
sort: msg.sort,
|
||||||
|
minMembers: msg.minMembers,
|
||||||
|
tag: msg.tag
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-discovery-tags') {
|
||||||
|
await platform.setGuildDiscoveryTags(msg.guildId, msg.tags)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-automod-config') {
|
||||||
|
await platform.setAutomodConfig(msg.config || {})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-announcement-crosspost') {
|
||||||
|
await platform.setAnnouncementCrosspostTargets(
|
||||||
|
msg.channelId,
|
||||||
|
msg.targetChannelIds || []
|
||||||
|
)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-announcement-hub-publish') {
|
||||||
|
await platform.setAnnouncementHubPublisher(msg.channelId, !!msg.enabled)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'subscribe-remote-announcement-hub') {
|
||||||
|
await platform.subscribeRemoteAnnouncementHub({
|
||||||
|
sourceGuildId: msg.sourceGuildId,
|
||||||
|
sourceChannelId: msg.sourceChannelId,
|
||||||
|
targetChannelId: msg.targetChannelId
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'follow-announcement') {
|
||||||
|
await platform.followAnnouncementChannel(msg.channelId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'unfollow-announcement') {
|
||||||
|
await platform.unfollowAnnouncementChannel(msg.channelId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'add-guild-boost') {
|
||||||
|
await platform.addGuildBoost(msg.amount ?? 1)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'contribute-guild-boost') {
|
||||||
|
await platform.contributeGuildBoost(msg.amount ?? 1)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-guild-boost-banner') {
|
||||||
|
await platform.setGuildBoostBannerColor(msg.bannerColor)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'voice-capture-chunk') {
|
||||||
|
platform.ingestVoiceCapture(msg.data)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'display-capture-frame') {
|
||||||
|
platform.ingestDisplayCapture({
|
||||||
|
data: msg.data,
|
||||||
|
mimeType: msg.mimeType,
|
||||||
|
width: msg.width,
|
||||||
|
height: msg.height
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-invite') {
|
||||||
|
const inv = await platform.createInvite()
|
||||||
|
send({ type: 'invite', invite: inv })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'select-guild') {
|
||||||
|
await platform.loadGuild(msg.guildId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'select-channel') {
|
||||||
|
await platform.selectChannel(msg.channelId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'join-voice') {
|
||||||
|
await platform.joinVoiceChannel(msg.channelId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'leave-voice') {
|
||||||
|
await platform.leaveVoiceChannel()
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'start-screen-share') {
|
||||||
|
await platform.startScreenShare(msg.label)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'stop-screen-share') {
|
||||||
|
await platform.stopScreenShare()
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-voice-mute') {
|
||||||
|
await platform.setVoiceMute({ muted: msg.muted, deafened: msg.deafened })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'register-slash-commands') {
|
||||||
|
await platform.registerSlashCommands(msg.commands || [])
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-bot-install-token') {
|
||||||
|
const out = await platform.createBotInstallToken({
|
||||||
|
name: msg.name,
|
||||||
|
intents: msg.intents,
|
||||||
|
permissions: msg.permissions,
|
||||||
|
botPublicKey: msg.botPublicKey,
|
||||||
|
ttlMs: msg.ttlMs
|
||||||
|
})
|
||||||
|
send({ type: 'bot-install-token', token: out.token, bot: out.bot, expiresAt: out.expiresAt })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'install-bot-token') {
|
||||||
|
await platform.installBotFromToken(msg.token)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'send-message') {
|
||||||
|
await platform.sendMessage(msg.content, {
|
||||||
|
replyToId: msg.replyToId,
|
||||||
|
attachmentIds: msg.attachmentIds,
|
||||||
|
stickerNames: msg.stickerNames
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'stage-attachment') {
|
||||||
|
const att = await platform.stageAttachment({
|
||||||
|
data: fromBase64(msg.dataBase64),
|
||||||
|
filename: msg.filename,
|
||||||
|
mimeType: msg.mimeType
|
||||||
|
})
|
||||||
|
send({ type: 'attachment-staged', attachment: att })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-guild-settings') {
|
||||||
|
await platform.updateGuildSettings({ name: msg.name, iconHash: msg.iconHash })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-presence') {
|
||||||
|
const custom = msg.customStatus !== undefined ? msg.customStatus : undefined
|
||||||
|
platform.setPresence(msg.status || USER_STATUS.ONLINE, custom)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'edit-message') {
|
||||||
|
await platform.editMessage(msg.messageId, msg.content)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'delete-message') {
|
||||||
|
await platform.deleteMessage(msg.messageId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-thread') {
|
||||||
|
await platform.createThread({ messageId: msg.messageId, name: msg.name })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-forum-post') {
|
||||||
|
await platform.createForumPost({
|
||||||
|
title: msg.title,
|
||||||
|
content: msg.content,
|
||||||
|
tags: msg.tags
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-forum-channel-palette') {
|
||||||
|
await platform.setForumChannelPalette(msg.channelId, msg.tags)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-forum-tag-filter') {
|
||||||
|
platform.setForumTagFilter(msg.tag)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'archive-thread') {
|
||||||
|
await platform.archiveThread(msg.threadId, msg.archived !== false)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-category') {
|
||||||
|
await platform.createCategory({ name: msg.name })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-channel') {
|
||||||
|
await platform.createChannel({
|
||||||
|
name: msg.name,
|
||||||
|
type: msg.channelType,
|
||||||
|
parentId: msg.parentId
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'toggle-reaction') {
|
||||||
|
await platform.toggleReaction(msg.messageId, msg.emoji)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'select-home') {
|
||||||
|
await platform.openHome()
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'send-friend-request') {
|
||||||
|
await platform.sendFriendRequest({
|
||||||
|
peerUserId: msg.peerUserId,
|
||||||
|
peerDisplayName: msg.peerDisplayName
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'accept-friend-request') {
|
||||||
|
await platform.acceptFriendRequest(msg.peerUserId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'decline-friend-request') {
|
||||||
|
await platform.declineFriendRequest(msg.peerUserId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'remove-contact') {
|
||||||
|
await platform.removeContact(msg.peerUserId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'block-contact') {
|
||||||
|
await platform.blockContact(msg.peerUserId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'unblock-contact') {
|
||||||
|
await platform.unblockContact(msg.peerUserId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'request-attachment-preview') {
|
||||||
|
const preview = await platform.readAttachmentPreview(msg.attachmentId)
|
||||||
|
send({ type: 'attachment-preview', preview })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'upsert-guild-emoji') {
|
||||||
|
await platform.upsertGuildEmoji({
|
||||||
|
name: msg.name,
|
||||||
|
glyph: msg.glyph,
|
||||||
|
description: msg.description,
|
||||||
|
attachmentId: msg.attachmentId,
|
||||||
|
packName: msg.packName,
|
||||||
|
image: normalizeEmojiImage(msg.image)
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'upsert-emoji-pack') {
|
||||||
|
await platform.upsertEmojiPack({
|
||||||
|
name: msg.name,
|
||||||
|
displayName: msg.displayName,
|
||||||
|
description: msg.description,
|
||||||
|
sortOrder: msg.sortOrder,
|
||||||
|
coverEmojiName: msg.coverEmojiName,
|
||||||
|
emojiNames: msg.emojiNames
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'remove-emoji-pack') {
|
||||||
|
await platform.removeEmojiPack(msg.name)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'upsert-guild-sticker') {
|
||||||
|
await platform.upsertGuildSticker({
|
||||||
|
name: msg.name,
|
||||||
|
description: msg.description,
|
||||||
|
attachmentId: msg.attachmentId,
|
||||||
|
packName: msg.packName,
|
||||||
|
image: normalizeEmojiImage(msg.image)
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'upsert-sticker-pack') {
|
||||||
|
await platform.upsertStickerPack({
|
||||||
|
name: msg.name,
|
||||||
|
displayName: msg.displayName,
|
||||||
|
description: msg.description,
|
||||||
|
sortOrder: msg.sortOrder,
|
||||||
|
coverStickerName: msg.coverStickerName,
|
||||||
|
stickerNames: msg.stickerNames
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'remove-sticker-pack') {
|
||||||
|
await platform.removeStickerPack(msg.name)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'request-emoji-image') {
|
||||||
|
const preview = await platform.readEmojiImage(msg.name || msg.attachmentId)
|
||||||
|
send({ type: 'emoji-image', preview, emojiName: msg.name || null })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'stage-guild-emoji-image') {
|
||||||
|
const att = await platform.stageGuildEmojiImage({
|
||||||
|
data: fromBase64(msg.dataBase64),
|
||||||
|
filename: msg.filename,
|
||||||
|
mimeType: msg.mimeType
|
||||||
|
})
|
||||||
|
send({ type: 'emoji-image-staged', attachment: att })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-search') {
|
||||||
|
platform.setSearch({ query: msg.query, scope: msg.scope })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'open-dm') {
|
||||||
|
await platform.openDM({
|
||||||
|
peerUserId: msg.peerUserId,
|
||||||
|
peerDisplayName: msg.peerDisplayName || msg.peerUserId?.slice(0, 8)
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-group-dm') {
|
||||||
|
await platform.createGroupDM({
|
||||||
|
peerUserIds: msg.peerUserIds || [],
|
||||||
|
name: msg.name
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'open-group-dm') {
|
||||||
|
await platform.openGroupDM({ channelId: msg.channelId })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'typing-start') {
|
||||||
|
platform.signalTyping()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-profile') {
|
||||||
|
await platform.updateProfile({ displayName: msg.displayName })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-profile-cosmetics') {
|
||||||
|
await platform.updateProfileCosmetics({
|
||||||
|
bannerColor: msg.bannerColor,
|
||||||
|
avatarRing: msg.avatarRing,
|
||||||
|
accentColor: msg.accentColor,
|
||||||
|
profileBio: msg.profileBio
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-profile-banner-image') {
|
||||||
|
await platform.setProfileBannerImage({
|
||||||
|
data: fromBase64(msg.dataBase64),
|
||||||
|
filename: msg.filename,
|
||||||
|
mimeType: msg.mimeType
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'request-profile-banner-image') {
|
||||||
|
const preview = await platform.readProfileBannerPreview(msg.attachmentId)
|
||||||
|
send({ type: 'profile-banner-image', preview })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'request-peer-profile-banner-image') {
|
||||||
|
const preview = await platform.readPeerProfileBannerPreview(msg.userId)
|
||||||
|
send({ type: 'profile-banner-image', preview })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'pin-message') {
|
||||||
|
await platform.pinMessage(msg.messageId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'unpin-message') {
|
||||||
|
await platform.unpinMessage(msg.messageId)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'ban-member') {
|
||||||
|
await platform.banMember({ userId: msg.userId, reason: msg.reason })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'kick-member') {
|
||||||
|
await platform.kickMember({ userId: msg.userId, reason: msg.reason })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'timeout-member') {
|
||||||
|
await platform.timeoutMember({
|
||||||
|
userId: msg.userId,
|
||||||
|
durationMinutes: msg.durationMinutes,
|
||||||
|
reason: msg.reason
|
||||||
|
})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-slowmode') {
|
||||||
|
await platform.setChannelSlowmode(msg.channelId, msg.slowmodeSeconds)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-member-role') {
|
||||||
|
await platform.setMemberRole(msg.userId, msg.role)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-notification-prefs') {
|
||||||
|
await platform.updateNotificationPrefs(msg.prefs || {})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'update-user-prefs') {
|
||||||
|
await platform.updateUserPrefs(msg.prefs || {})
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'mark-all-notifications-read') {
|
||||||
|
await platform.markAllNotificationsRead()
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'create-device-pair-code') {
|
||||||
|
const invite = await platform.createDevicePairCode({
|
||||||
|
deviceLabel: msg.deviceLabel,
|
||||||
|
ttlMs: msg.ttlMs
|
||||||
|
})
|
||||||
|
send({ type: 'device-pair-code', invite })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'link-device-pair-code') {
|
||||||
|
await platform.linkDevicePairCode(msg.code, { deviceLabel: msg.deviceLabel })
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'set-step-up-pin') {
|
||||||
|
await platform.setStepUpPin(msg.pin)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'verify-step-up-pin') {
|
||||||
|
await platform.verifyStepUpPin(msg.pin)
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'clear-step-up-pin') {
|
||||||
|
await platform.clearStepUpPin()
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t === 'refresh') {
|
||||||
|
pushState()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
dispatchUiMessage,
|
||||||
|
fromBase64,
|
||||||
|
normalizeEmojiImage
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "pearcord-ui-flow",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "commonjs",
|
||||||
|
"description": "Pearcord desktop UI IPC dispatch + automated flow scenarios for Bare smokes",
|
||||||
|
"dependencies": {
|
||||||
|
"b4a": "^1.6.7",
|
||||||
|
"pearcord-platform": "git+https://git.ssh.surf/pearcord/pearcord-platform.git#main"
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const { dispatchUiMessage } = require('./index')
|
||||||
|
|
||||||
|
async function ipc (platform, msg) {
|
||||||
|
const ok = await dispatchUiMessage(platform, msg, {})
|
||||||
|
if (!ok) throw new Error(`unhandled IPC type: ${msg?.type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full desktop UI journey: onboarding, settings, navigation, invite join, restore.
|
||||||
|
*/
|
||||||
|
async function runFullUiFlowScenario (platform) {
|
||||||
|
await ipc(platform, {
|
||||||
|
type: 'register',
|
||||||
|
username: 'ui_flow_user',
|
||||||
|
displayName: 'Pearcord UI'
|
||||||
|
})
|
||||||
|
let view = await platform.view()
|
||||||
|
if (!view.onboarded) throw new Error('onboarded after register')
|
||||||
|
if (!view.needsGuild) throw new Error('needsGuild after register')
|
||||||
|
|
||||||
|
await ipc(platform, { type: 'create-guild', name: 'Pearcord HQ' })
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.guild?.name !== 'Pearcord HQ') throw new Error('guild name')
|
||||||
|
if (view.needsGuild) throw new Error('needsGuild cleared')
|
||||||
|
if (view.mode !== 'guild') throw new Error('guild mode')
|
||||||
|
if (!view.activeChannelId) throw new Error('active channel')
|
||||||
|
|
||||||
|
const guildId = view.guild.id
|
||||||
|
const generalId = view.activeChannelId
|
||||||
|
|
||||||
|
await ipc(platform, { type: 'update-profile', displayName: 'UI Flow Tester' })
|
||||||
|
await ipc(platform, {
|
||||||
|
type: 'update-user-prefs',
|
||||||
|
prefs: { theme: 'amoled', density: 'compact', showTimestamps: true }
|
||||||
|
})
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.user?.displayName !== 'UI Flow Tester') throw new Error('profile display name')
|
||||||
|
if (view.userPrefs?.theme !== 'amoled') throw new Error('user prefs theme')
|
||||||
|
|
||||||
|
await ipc(platform, { type: 'update-guild-settings', name: 'Pearcord HQ Renamed' })
|
||||||
|
await ipc(platform, {
|
||||||
|
type: 'set-discovery-tags',
|
||||||
|
guildId,
|
||||||
|
tags: ['gaming', 'p2p']
|
||||||
|
})
|
||||||
|
await ipc(platform, {
|
||||||
|
type: 'set-automod-config',
|
||||||
|
config: { enabled: true, maxMentions: 4, blockInvites: true }
|
||||||
|
})
|
||||||
|
await ipc(platform, { type: 'add-guild-boost', amount: 1 })
|
||||||
|
await ipc(platform, { type: 'set-presence', status: 'idle', customStatus: 'smoke testing' })
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.guild?.name !== 'Pearcord HQ Renamed') throw new Error('guild renamed')
|
||||||
|
if (!view.automodConfig?.enabled) throw new Error('automod enabled')
|
||||||
|
if ((view.guildBoost?.boostCount || 0) < 1) throw new Error('guild boost')
|
||||||
|
if (view.presence?.self?.customStatus !== 'smoke testing') throw new Error('custom status')
|
||||||
|
|
||||||
|
await ipc(platform, { type: 'send-message', content: 'UI flow smoke message' })
|
||||||
|
await ipc(platform, { type: 'select-home' })
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.mode !== 'home') throw new Error('home mode after select-home')
|
||||||
|
|
||||||
|
const peerId = 'a'.repeat(32)
|
||||||
|
await ipc(platform, {
|
||||||
|
type: 'open-dm',
|
||||||
|
peerUserId: peerId,
|
||||||
|
peerDisplayName: 'Peer'
|
||||||
|
})
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.mode !== 'dm') throw new Error('dm mode')
|
||||||
|
await ipc(platform, { type: 'send-message', content: 'DM from UI flow' })
|
||||||
|
|
||||||
|
await ipc(platform, { type: 'select-guild', guildId })
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.mode !== 'guild') throw new Error('back to guild')
|
||||||
|
await ipc(platform, { type: 'select-channel', channelId: generalId })
|
||||||
|
if (view.activeChannelId !== generalId) {
|
||||||
|
view = await platform.view()
|
||||||
|
if (view.activeChannelId !== generalId) throw new Error('channel reselected')
|
||||||
|
}
|
||||||
|
|
||||||
|
await ipc(platform, {
|
||||||
|
type: 'update-notification-prefs',
|
||||||
|
prefs: { desktopEnabled: true, notifyOnAllMessages: true }
|
||||||
|
})
|
||||||
|
view = await platform.view()
|
||||||
|
if (!view.notificationPrefs?.desktopEnabled) throw new Error('notification prefs')
|
||||||
|
if (!view.notificationPrefs?.notifyOnAllMessages) throw new Error('notification prefs patch')
|
||||||
|
|
||||||
|
const invite = await platform.createInvite()
|
||||||
|
return {
|
||||||
|
guildId,
|
||||||
|
generalId,
|
||||||
|
inviteCode: invite.shareCode || invite.code,
|
||||||
|
view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
runFullUiFlowScenario,
|
||||||
|
ipc
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user