ux: default visibility = Extended Friends (FoF) + clean visibility picker
#8 from the v0.7.0 device-test feedback round, confirmed by Scott. Promotes FoF from a comment-policy option to a top-level visibility choice, with FoF as the new default. - visibility-select: new option order with `fof_closed` selected by default: - Extended Friends (FoF) ← default - Friends - Public - Circle - comment-perm-select: removed the old `fof_closed` option (its job is now done by the visibility picker). Kept `friends_of_friends` for the Mode 2 combo (public body + FoF-gated comments). - updateVisibilityUI now hides comment-perm-select when visibility is `fof_closed` — the audience choice already implies the comment policy, no extra picker needed. Shown again on public/friends/circle. - Compose dispatch logic re-rooted on visibility instead of comment policy: - fof_closed → create_post_fof_closed (Mode 1) - public + comment-perm=friends_of_friends → create_post_with_fof_comments (Mode 2) - everything else → existing create_post path - After-post reset goes to `fof_closed` (not `public`) to preserve the privacy-by-default posture. Tracker memory updated: #8 marked complete; #10 clarified (per-group persona selection, scoped alongside the deferred Group UI work in #9). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
346d23d4d8
commit
83fd30753f
2 changed files with 27 additions and 25 deletions
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue