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 {
|
try {
|
||||||
const vis = visibilitySelect.value;
|
const vis = visibilitySelect.value;
|
||||||
const params = { content: content || '' };
|
const params = { content: content || '' };
|
||||||
if (vis !== 'public') {
|
if (vis !== 'public' && vis !== 'fof_closed') {
|
||||||
params.visibility = vis;
|
params.visibility = vis;
|
||||||
}
|
}
|
||||||
if (vis === 'circle') {
|
if (vis === 'circle') {
|
||||||
|
|
@ -2686,28 +2686,11 @@ async function doPost() {
|
||||||
const reactPerm = document.getElementById('react-perm-select').value;
|
const reactPerm = document.getElementById('react-perm-select').value;
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
if (commentPerm === 'friends_of_friends') {
|
if (vis === 'fof_closed') {
|
||||||
// FoF Layer 2: body is still public (Mode 2) but the post
|
// Visibility = Extended Friends (FoF). Body + comments are
|
||||||
// carries a fof_gating block built from the author's
|
// encrypted under the FoF gating CEK. Mode 1.
|
||||||
// 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 (selectedFiles.length > 0 || params.postingIdHex) {
|
if (selectedFiles.length > 0 || params.postingIdHex) {
|
||||||
toast('FoF 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;
|
|
||||||
}
|
|
||||||
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.');
|
|
||||||
postBtn.disabled = false;
|
postBtn.disabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2715,6 +2698,17 @@ async function doPost() {
|
||||||
content: params.content,
|
content: params.content,
|
||||||
});
|
});
|
||||||
result = { id: created.postId };
|
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) {
|
} else if (selectedFiles.length > 0) {
|
||||||
// Convert ArrayBuffers to base64 strings
|
// Convert ArrayBuffers to base64 strings
|
||||||
const files = selectedFiles.map(f => {
|
const files = selectedFiles.map(f => {
|
||||||
|
|
@ -2747,7 +2741,7 @@ async function doPost() {
|
||||||
selectedFiles = [];
|
selectedFiles = [];
|
||||||
renderAttachmentPreview();
|
renderAttachmentPreview();
|
||||||
updateCharCount();
|
updateCharCount();
|
||||||
visibilitySelect.value = 'public';
|
visibilitySelect.value = 'fof_closed';
|
||||||
updateVisibilityUI();
|
updateVisibilityUI();
|
||||||
toast('Posted!');
|
toast('Posted!');
|
||||||
loadFeed(true);
|
loadFeed(true);
|
||||||
|
|
@ -3001,6 +2995,11 @@ async function loadCircleProfiles() {
|
||||||
function updateVisibilityUI() {
|
function updateVisibilityUI() {
|
||||||
const vis = visibilitySelect.value;
|
const vis = visibilitySelect.value;
|
||||||
circleSelect.classList.toggle('hidden', vis !== 'circle');
|
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() {
|
async function loadCircleOptions() {
|
||||||
|
|
@ -3018,6 +3017,9 @@ visibilitySelect.addEventListener('change', () => {
|
||||||
updateVisibilityUI();
|
updateVisibilityUI();
|
||||||
if (visibilitySelect.value === 'circle') loadCircleOptions();
|
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 ---
|
// --- Circles management ---
|
||||||
async function loadCircles() {
|
async function loadCircles() {
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,9 @@
|
||||||
<div id="visibility-row">
|
<div id="visibility-row">
|
||||||
<select id="persona-select" title="Post as" class="hidden"></select>
|
<select id="persona-select" title="Post as" class="hidden"></select>
|
||||||
<select id="visibility-select">
|
<select id="visibility-select">
|
||||||
<option value="public">Public</option>
|
<option value="fof_closed" selected>Extended Friends (FoF)</option>
|
||||||
<option value="friends">Friends</option>
|
<option value="friends">Friends</option>
|
||||||
|
<option value="public">Public</option>
|
||||||
<option value="circle">Circle</option>
|
<option value="circle">Circle</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="circle-select" class="hidden"></select>
|
<select id="circle-select" class="hidden"></select>
|
||||||
|
|
@ -105,7 +106,6 @@
|
||||||
<option value="public">Comments: All</option>
|
<option value="public">Comments: All</option>
|
||||||
<option value="followers_only">Comments: Followers</option>
|
<option value="followers_only">Comments: Followers</option>
|
||||||
<option value="friends_of_friends">Comments: Friends of Friends</option>
|
<option value="friends_of_friends">Comments: Friends of Friends</option>
|
||||||
<option value="fof_closed">Body+Comments: FoF only (Mode 1)</option>
|
|
||||||
<option value="none">Comments: Off</option>
|
<option value="none">Comments: Off</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="react-perm-select" title="React permission">
|
<select id="react-perm-select" title="React permission">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue