diff --git a/frontend/app.js b/frontend/app.js index 5bf2922..705cb18 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -2662,7 +2662,7 @@ async function doPost() { try { const vis = visibilitySelect.value; const params = { content: content || '' }; - if (vis !== 'public') { + if (vis !== 'public' && vis !== 'fof_closed') { params.visibility = vis; } if (vis === 'circle') { @@ -2686,28 +2686,11 @@ async function doPost() { const reactPerm = document.getElementById('react-perm-select').value; let result; - if (commentPerm === 'friends_of_friends') { - // FoF Layer 2: body is still public (Mode 2) but the post - // carries a fof_gating block built from the author's - // keyring. Routed through a dedicated command because the - // gating block is signed at publish time (can't be added - // via SetPolicy after the fact). + if (vis === 'fof_closed') { + // Visibility = Extended Friends (FoF). Body + comments are + // encrypted under the FoF gating CEK. Mode 1. if (selectedFiles.length > 0 || params.postingIdHex) { - toast('FoF posts with attachments or non-default persona not yet supported.'); - postBtn.disabled = false; - return; - } - const created = await invoke('create_post_with_fof_comments', { - content: params.content, - }); - result = { id: created.postId }; - } else if (commentPerm === 'fof_closed') { - // FoF Layer 3 / Mode 1: body itself encrypted under the - // gating CEK. Non-FoF observers see only ciphertext; - // FoF readers unlock + decrypt on render via - // read_fof_closed_body. - if (selectedFiles.length > 0 || params.postingIdHex) { - toast('FoFClosed posts with attachments or non-default persona not yet supported.'); + toast('FoF (Extended Friends) posts with attachments or non-default persona not yet supported.'); postBtn.disabled = false; return; } @@ -2715,6 +2698,17 @@ async function doPost() { content: params.content, }); result = { id: created.postId }; + } else if (vis === 'public' && commentPerm === 'friends_of_friends') { + // Public body, FoF-gated comments. Mode 2. + if (selectedFiles.length > 0 || params.postingIdHex) { + toast('FoF-comment posts with attachments or non-default persona not yet supported.'); + postBtn.disabled = false; + return; + } + const created = await invoke('create_post_with_fof_comments', { + content: params.content, + }); + result = { id: created.postId }; } else if (selectedFiles.length > 0) { // Convert ArrayBuffers to base64 strings const files = selectedFiles.map(f => { @@ -2747,7 +2741,7 @@ async function doPost() { selectedFiles = []; renderAttachmentPreview(); updateCharCount(); - visibilitySelect.value = 'public'; + visibilitySelect.value = 'fof_closed'; updateVisibilityUI(); toast('Posted!'); loadFeed(true); @@ -3001,6 +2995,11 @@ async function loadCircleProfiles() { function updateVisibilityUI() { const vis = visibilitySelect.value; circleSelect.classList.toggle('hidden', vis !== 'circle'); + // Hide the comment-permission picker for FoF (Extended Friends) — the + // visibility already implies comments-restricted-to-FoF. Show it + // again when audience is public / friends / circle. + const commentPerm = document.getElementById('comment-perm-select'); + if (commentPerm) commentPerm.classList.toggle('hidden', vis === 'fof_closed'); } async function loadCircleOptions() { @@ -3018,6 +3017,9 @@ visibilitySelect.addEventListener('change', () => { updateVisibilityUI(); if (visibilitySelect.value === 'circle') loadCircleOptions(); }); +// Run once on load so the comment-perm picker is hidden for the +// default FoF visibility (matches the dropdown's `selected` option). +updateVisibilityUI(); // --- Circles management --- async function loadCircles() { diff --git a/frontend/index.html b/frontend/index.html index ba80394..4c02f53 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -96,8 +96,9 @@
@@ -105,7 +106,6 @@ -