diff --git a/internal/ipc/ipc.go b/internal/ipc/ipc.go index 3a75ca0..e98aee2 100644 --- a/internal/ipc/ipc.go +++ b/internal/ipc/ipc.go @@ -459,6 +459,22 @@ func stateSnapshot(mgr *netmgr.Manager) proto.IpcMessage { msg.Rooms = append(msg.Rooms, r) } } + // Include all historically-known peers so the UI can resolve aliases in history. + if known, err := all[0].Store.KnownPeers(); err == nil { + connected := map[proto.PeerID]bool{} + for _, p := range msg.ConnectedPeers { + connected[p.ID] = true + } + for id, alias := range known { + if connected[id] { + continue // already in ConnectedPeers + } + msg.KnownPeers = append(msg.KnownPeers, proto.PeerInfo{ + ID: id, + Alias: alias, + }) + } + } } return msg diff --git a/internal/proto/proto.go b/internal/proto/proto.go index 418cee2..432fb02 100644 --- a/internal/proto/proto.go +++ b/internal/proto/proto.go @@ -350,6 +350,7 @@ type IpcMessage struct { MasterID string `json:"master_id,omitempty"` // daemon's master public key hex LocalPeer *PeerInfo `json:"local_peer,omitempty"` ConnectedPeers []PeerInfo `json:"connected_peers,omitempty"` + KnownPeers []PeerInfo `json:"known_peers,omitempty"` // historically seen, not currently connected Rooms []string `json:"rooms,omitempty"` // multi-network: all joined networks (additive) Networks []NetworkInfo `json:"networks,omitempty"` diff --git a/web/src/App.css b/web/src/App.css index e286821..8d48852 100644 --- a/web/src/App.css +++ b/web/src/App.css @@ -88,7 +88,7 @@ details summary { color: var(--muted); font-size: 12px; cursor: pointer; } .messages { flex: 1; overflow-y: auto; padding: 8px 0; display: flex; flex-direction: column; } .message { display: flex; align-items: baseline; gap: 0; padding: 2px 16px; line-height: 1.5; } .message:hover { background: rgba(255,255,255,0.02); } -.message-ts { color: var(--muted); font-size: 11px; white-space: nowrap; flex-shrink: 0; width: 52px; } +.message-ts { color: var(--muted); font-size: 11px; white-space: nowrap; flex-shrink: 0; width: 72px; } .message-alias { font-weight: 600; font-size: 13px; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 0; width: 90px; padding-right: 10px; } .message.mine .message-alias { color: var(--accent); } .message-text { word-break: break-word; font-size: 14px; color: var(--text); } diff --git a/web/src/components/MessagePane.tsx b/web/src/components/MessagePane.tsx index b845887..c8330ac 100644 --- a/web/src/components/MessagePane.tsx +++ b/web/src/components/MessagePane.tsx @@ -4,17 +4,16 @@ import { useWaste } from '../store' const today = new Date() today.setHours(0, 0, 0, 0) const todayMs = today.getTime() -const yesterdayMs = todayMs - 86400000 function formatTs(ts: number): string { - const time = new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }) + const d = new Date(ts) + const time = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }) if (ts >= todayMs) return time - if (ts >= yesterdayMs) return `Yesterday ${time}` - return new Date(ts).toLocaleDateString([], { month: 'short', day: 'numeric' }) + ' ' + time + return d.toLocaleDateString([], { month: 'short', day: 'numeric' }) + ' ' + time } export function MessagePane() { - const { messages, historyCutoff, activeRoom, activeNetworkId, localPeer, connectedPeers, send } = useWaste() + const { messages, historyCutoff, activeRoom, activeNetworkId, localPeer, connectedPeers, knownPeers, send } = useWaste() const [draft, setDraft] = useState('') const bottomRef = useRef(null) const roomMessages = messages[activeRoom] ?? [] @@ -53,7 +52,9 @@ export function MessagePane() { function aliasFor(fromId: string) { if (fromId === localPeer?.id) return localPeer.alias - return connectedPeers.find(p => p.id === fromId)?.alias ?? fromId.slice(0, 8) + return connectedPeers.find(p => p.id === fromId)?.alias + ?? knownPeers[fromId] + ?? fromId.slice(0, 8) } const roomLabel = activeRoom.startsWith('dm:') diff --git a/web/src/store/index.ts b/web/src/store/index.ts index 66b185f..e719cdb 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -29,6 +29,7 @@ interface WasteState { // peers connectedPeers: PeerInfo[] + knownPeers: Record // id → alias for historical peers // chat — keyed by room messages: Record @@ -87,6 +88,7 @@ export const useWaste = create((set, get) => ({ networks: [], activeNetworkId: null, connectedPeers: [], + knownPeers: {}, messages: {}, historyCutoff: {}, activeRoom: 'general', @@ -215,6 +217,8 @@ export const useWaste = create((set, get) => ({ switch (msg.type) { case 'state_snapshot': { const networks = msg.networks ?? [] + const knownPeers: Record = {} + for (const p of msg.known_peers ?? []) knownPeers[p.id] = p.alias set({ masterAlias: msg.master_alias ?? null, masterId: msg.master_id ?? null, @@ -222,6 +226,7 @@ export const useWaste = create((set, get) => ({ networks, connectedPeers: msg.connected_peers ?? [], activeNetworkId: networks[0]?.network_id ?? null, + knownPeers, }) break } diff --git a/web/src/types.ts b/web/src/types.ts index 57ab887..4f2e600 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -117,6 +117,7 @@ export interface IpcMessage { master_id?: string local_peer?: PeerInfo connected_peers?: PeerInfo[] + known_peers?: PeerInfo[] rooms?: string[] networks?: NetworkInfo[] error_message?: string