chore: remove CLAUDE.md and stale config files
This commit is contained in:
@@ -398,7 +398,7 @@ class RPCClient {
|
||||
})
|
||||
}
|
||||
|
||||
async getReceivedMessages(): Promise<{ messages: Array<{ from_pubkey: string; message: string; timestamp: string }> }> {
|
||||
async getReceivedMessages(): Promise<{ messages: Array<{ from_pubkey: string; from_name?: string; message: string; timestamp: string; direction?: string }> }> {
|
||||
return this.call({
|
||||
method: 'node-messages-received',
|
||||
params: {},
|
||||
|
||||
@@ -276,6 +276,31 @@ export const useMeshStore = defineStore('mesh', () => {
|
||||
return result
|
||||
}
|
||||
|
||||
async function sendChannelMessage(channel: number, message: string) {
|
||||
const doSend = async () => {
|
||||
try {
|
||||
sending.value = true
|
||||
error.value = null
|
||||
const res = await rpcClient.call<{ sent: boolean; message_id: number; channel: number }>({
|
||||
method: 'mesh.send-channel',
|
||||
params: { channel, message: message.trim() },
|
||||
})
|
||||
if (res.sent) {
|
||||
await fetchMessages()
|
||||
}
|
||||
return res
|
||||
} catch (err: unknown) {
|
||||
error.value = err instanceof Error ? err.message : 'Failed to send channel message'
|
||||
throw err
|
||||
} finally {
|
||||
sending.value = false
|
||||
}
|
||||
}
|
||||
const result = sendQueue.then(doSend, doSend)
|
||||
sendQueue = result.then(() => {}, () => {})
|
||||
return result
|
||||
}
|
||||
|
||||
async function broadcastIdentity() {
|
||||
try {
|
||||
error.value = null
|
||||
@@ -456,6 +481,7 @@ export const useMeshStore = defineStore('mesh', () => {
|
||||
fetchPeers,
|
||||
fetchMessages,
|
||||
sendMessage,
|
||||
sendChannelMessage,
|
||||
broadcastIdentity,
|
||||
configure,
|
||||
refreshAll,
|
||||
|
||||
+61
-10
@@ -37,18 +37,34 @@ let pollInterval: ReturnType<typeof setInterval> | null = null
|
||||
// The Public channel (always available on Meshcore)
|
||||
const publicChannel = { index: 0, name: 'Public' }
|
||||
|
||||
// Channel contact_id convention: matches backend u32::MAX - channel_index
|
||||
function channelContactId(channelIndex: number): number {
|
||||
return 4294967295 - channelIndex // u32::MAX - index
|
||||
}
|
||||
|
||||
// Archipelago Channel — Tor-based messaging to all federated/peered nodes
|
||||
const archChannelActive = ref(false)
|
||||
const archMessages = ref<Array<{ from_pubkey: string; message: string; timestamp: string }>>([])
|
||||
const archMessages = ref<Array<{ from_pubkey: string; from_name?: string; message: string; timestamp: string; direction?: string }>>([])
|
||||
const archUnread = ref(0)
|
||||
let archPollInterval: ReturnType<typeof setInterval> | null = null
|
||||
// Federation node name cache: pubkey -> node name
|
||||
const fedNodeNames = ref<Record<string, string>>({})
|
||||
|
||||
function openArchChannel() {
|
||||
async function openArchChannel() {
|
||||
activeChatPeer.value = null
|
||||
activeChatChannel.value = null
|
||||
archChannelActive.value = true
|
||||
archUnread.value = 0
|
||||
mobileShowChat.value = true
|
||||
// Load federation node names for resolving pubkeys to names
|
||||
try {
|
||||
const res = await rpcClient.federationListNodes()
|
||||
const names: Record<string, string> = {}
|
||||
for (const node of res.nodes) {
|
||||
if (node.pubkey) names[node.pubkey] = node.name || node.did.slice(0, 12) + '...'
|
||||
}
|
||||
fedNodeNames.value = names
|
||||
} catch { /* non-fatal */ }
|
||||
loadArchMessages()
|
||||
if (!archPollInterval) {
|
||||
archPollInterval = setInterval(loadArchMessages, 15000)
|
||||
@@ -58,7 +74,18 @@ function openArchChannel() {
|
||||
async function loadArchMessages() {
|
||||
try {
|
||||
const res = await rpcClient.getReceivedMessages()
|
||||
archMessages.value = res.messages || []
|
||||
const newMessages = res.messages || []
|
||||
// Track unread: count new received messages since last load
|
||||
if (archMessages.value.length > 0 && !archChannelActive.value) {
|
||||
const newReceived = newMessages.filter(
|
||||
m => m.direction !== 'sent' && m.from_pubkey !== 'me'
|
||||
&& !archMessages.value.some(existing =>
|
||||
existing.from_pubkey === m.from_pubkey && existing.timestamp === m.timestamp
|
||||
)
|
||||
)
|
||||
archUnread.value += newReceived.length
|
||||
}
|
||||
archMessages.value = newMessages
|
||||
} catch { /* silent */ }
|
||||
}
|
||||
|
||||
@@ -142,6 +169,11 @@ async function handleToggleOffGrid() {
|
||||
onMounted(async () => {
|
||||
window.addEventListener('resize', handleResize)
|
||||
await Promise.all([mesh.refreshAll(), transport.fetchStatus()])
|
||||
// Start background polling for Archipelago (Tor) messages so unread count works
|
||||
loadArchMessages()
|
||||
if (!archPollInterval) {
|
||||
archPollInterval = setInterval(loadArchMessages, 15000)
|
||||
}
|
||||
pollInterval = setInterval(() => {
|
||||
mesh.fetchStatus()
|
||||
mesh.fetchPeers()
|
||||
@@ -154,6 +186,7 @@ onMounted(async () => {
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
if (pollInterval) clearInterval(pollInterval)
|
||||
if (archPollInterval) { clearInterval(archPollInterval); archPollInterval = null }
|
||||
})
|
||||
|
||||
// Active chat name for the header
|
||||
@@ -177,11 +210,21 @@ const hasActiveChat = computed(() => !!activeChatPeer.value || !!activeChatChann
|
||||
const chatMessages = computed(() => {
|
||||
if (archChannelActive.value) {
|
||||
return archMessages.value.map((m, i) => {
|
||||
const isSent = (m as Record<string, unknown>).direction === 'sent' || m.from_pubkey === 'me'
|
||||
const isSent = m.direction === 'sent' || m.from_pubkey === 'me'
|
||||
let peerName = 'Unknown'
|
||||
if (isSent) {
|
||||
peerName = 'You'
|
||||
} else if (m.from_name) {
|
||||
peerName = m.from_name
|
||||
} else if (fedNodeNames.value[m.from_pubkey]) {
|
||||
peerName = fedNodeNames.value[m.from_pubkey]
|
||||
} else {
|
||||
peerName = m.from_pubkey.slice(0, 12) + '...'
|
||||
}
|
||||
return {
|
||||
id: i,
|
||||
peer_contact_id: -99,
|
||||
peer_name: isSent ? 'You' : (m.from_pubkey.slice(0, 12) + '...'),
|
||||
peer_name: peerName,
|
||||
direction: (isSent ? 'sent' : 'received') as 'sent' | 'received',
|
||||
plaintext: m.message,
|
||||
timestamp: m.timestamp,
|
||||
@@ -193,7 +236,7 @@ const chatMessages = computed(() => {
|
||||
})
|
||||
}
|
||||
if (activeChatChannel.value) {
|
||||
const chanId = -(activeChatChannel.value.index + 1)
|
||||
const chanId = channelContactId(activeChatChannel.value.index)
|
||||
return mesh.messages.filter(m => m.peer_contact_id === chanId)
|
||||
}
|
||||
if (activeChatPeer.value) {
|
||||
@@ -236,6 +279,7 @@ function openChannelChat(channel: { index: number; name: string }) {
|
||||
messageText.value = ''
|
||||
activeTab.value = 'chat'
|
||||
mobileShowChat.value = true
|
||||
mesh.markChatRead(channelContactId(channel.index))
|
||||
nextTick(() => scrollChatToBottom())
|
||||
}
|
||||
|
||||
@@ -254,12 +298,18 @@ async function handleSendMessage() {
|
||||
nextTick(() => scrollChatToBottom())
|
||||
return
|
||||
}
|
||||
if (!activeChatPeer.value || !messageText.value.trim()) return
|
||||
if (!messageText.value.trim()) return
|
||||
sendError.value = ''
|
||||
try {
|
||||
await mesh.sendMessage(activeChatPeer.value.contact_id, messageText.value)
|
||||
messageText.value = ''
|
||||
nextTick(() => scrollChatToBottom())
|
||||
if (activeChatChannel.value) {
|
||||
await mesh.sendChannelMessage(activeChatChannel.value.index, messageText.value)
|
||||
messageText.value = ''
|
||||
nextTick(() => scrollChatToBottom())
|
||||
} else if (activeChatPeer.value) {
|
||||
await mesh.sendMessage(activeChatPeer.value.contact_id, messageText.value)
|
||||
messageText.value = ''
|
||||
nextTick(() => scrollChatToBottom())
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
sendError.value = err instanceof Error ? err.message : 'Send failed'
|
||||
}
|
||||
@@ -467,6 +517,7 @@ function truncatePubkey(hex: string | null): string {
|
||||
<div class="mesh-peer-name">Public</div>
|
||||
<div class="mesh-peer-sub">Mesh radio</div>
|
||||
</div>
|
||||
<span v-if="mesh.unreadCounts[channelContactId(0)]" class="ml-auto text-[10px] px-1.5 py-0.5 rounded-full bg-orange-500/30 text-orange-300">{{ mesh.unreadCounts[channelContactId(0)] }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="peer in sortedPeers" :key="peer.contact_id"
|
||||
|
||||
@@ -162,7 +162,7 @@ export function resolveAppUrl(id: string, routeQueryPath?: string): string {
|
||||
if (httpsProxy) return `${window.location.origin}${httpsProxy}`
|
||||
}
|
||||
|
||||
// HTTP: direct port access (iframes break with proxy paths due to root-relative assets)
|
||||
// HTTP: direct port access
|
||||
const port = APP_PORTS[id]
|
||||
if (!port) return ''
|
||||
let base = `http://${window.location.hostname}:${port}`
|
||||
|
||||
Reference in New Issue
Block a user