// AIM-style "Instant Message" windows. Click a buddy and one opens, showing the
// activity BETWEEN YOU AND THEM — their replies/likes/reposts/quotes/mentions of
// your stuff, and yours of theirs — as a live chat log, oldest -> newest,
// appending in real time. (Global activity still drives presence/last-seen; the
// window is scoped to the relationship.)
//
// Two passes:
// 1. fast — their getAuthorFeed + live buffer + my recent feed, filtered to
// interactions between us, rendered immediately.
// 2. deep — download BOTH full repos (getRepo CAR) in the background and merge
// in older interactions, incl. likes across all history (and my own likes,
// which are otherwise auth-gated). See deep-history.js.
// Like/repost targets are resolved with getPosts; follow targets with getProfiles.
import { getAuthorFeed, getPosts, getProfiles } from './api.js';
import { esc } from './util.js';
import {
entryFromFeedItem, entryFromBuffer, involves, needsPostResolve, needsProfileResolve,
} from './chat-actions.js';
import { lineHtml } from './chat-render.js';
import { makeDraggable } from './drag.js';
import { loadRepoInteractions } from './deep-history.js';
const open = new Map(); // did -> window state
let zTop = 1000;
let cascade = 0;
let selfDid = null; // the signed-in user, set via setSelf
let selfActionsP = null; // cached promise of MY recent feed actions
const SYS_HEADER = `
— between you two —
`;
const EMPTY = `
no interactions between you two yet. go say hi 👋
`;
const MAX_LINES = 400; // how many (most-recent) interactions to keep in the DOM
const FLUSH_MS = 150; // coalesce a burst of live events into one update
export const isOpen = (did) => open.has(did);
// Called at sign-on (and reset to null on sign-off). Invalidates the cached feed.
export function setSelf(did) { selfDid = did; selfActionsP = null; }
// My recent actions, normalized once and reused across all chat windows.
function getSelfActions() {
if (!selfActionsP) {
selfActionsP = (async () => {
if (!selfDid) return [];
try {
const feed = await getAuthorFeed(selfDid, { limit: 60 });
return feed.map(entryFromFeedItem).filter(Boolean);
} catch { return []; }
})();
}
return selfActionsP;
}
// ---- window lifecycle ------------------------------------------------------
export async function openChat(buddy) {
if (!buddy) return;
const did = buddy.did;
if (open.has(did)) { focusWin(did); return; }
const handle = buddy.profile?.handle || did;
const name = buddy.profile?.displayName?.trim() || handle;
const el = document.createElement('div');
el.className = 'imwin';
const off = (cascade++ % 7) * 26;
el.style.left = `${70 + off}px`;
el.style.top = `${60 + off}px`;
el.style.zIndex = ++zTop;
el.innerHTML = `