fix: normalize visibility enums + tab tracking across remaining files
Update remaining references from legacy 4-value visibility model (public/public_read/authenticated/members_only) to simplified 3-value model (public/permissioned/private) in rInbox, rVote, identity component, admin panel, and create-space page. Add tab trackRecent calls in shell. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
eb2859d849
commit
80e42596b3
|
|
@ -154,8 +154,8 @@ A **space** is a collaborative context — a team, community, project, or
|
|||
individual workspace. Each space has:
|
||||
|
||||
- **Slug** + optional subdomain (`alice.rspace.online`)
|
||||
- **Visibility**: `public` | `public_read` | `authenticated` | `members_only`
|
||||
- **Members**: `viewer` → `participant` → `moderator` → `admin`
|
||||
- **Visibility**: `public` (👁 green — anyone reads, sign in to write) | `permissioned` (🔑 yellow — sign in to read & write) | `private` (🔒 red — invite-only)
|
||||
- **Members**: `viewer` → `member` → `moderator` → `admin`
|
||||
- **Enabled modules**: which rApps are available in this space
|
||||
- **Module scoping**: per-module `space` (data lives in space) vs
|
||||
`global` (data follows identity)
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ routes.post("/api/mailboxes", async (c) => {
|
|||
try { claims = await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
|
||||
|
||||
const body = await c.req.json();
|
||||
const { slug, name, email, description, visibility = "members_only", imap_user, imap_password } = body;
|
||||
const { slug, name, email, description, visibility = "private", imap_user, imap_password } = body;
|
||||
if (!slug || !name || !email) return c.json({ error: "slug, name, email required" }, 400);
|
||||
if (!/^[a-z0-9-]+$/.test(slug)) return c.json({ error: "Invalid slug" }, 400);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class FolkVoteDashboard extends HTMLElement {
|
|||
slug: "community",
|
||||
name: "Community Governance",
|
||||
description: "Proposals for the rSpace ecosystem",
|
||||
visibility: "public_read",
|
||||
visibility: "public",
|
||||
promotion_threshold: 100,
|
||||
voting_period_days: 7,
|
||||
credits_per_day: 10,
|
||||
|
|
@ -328,7 +328,7 @@ class FolkVoteDashboard extends HTMLElement {
|
|||
<div class="card" data-space="${s.slug}">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<div class="card-title">${this.esc(s.name)}</div>
|
||||
<span class="badge" style="background:rgba(129,140,248,0.15);color:#818cf8">${s.visibility === "public_read" ? "Public" : s.visibility}</span>
|
||||
<span class="badge" style="background:rgba(129,140,248,0.15);color:#818cf8">${s.visibility === "public" ? "👁 Public" : s.visibility === "permissioned" ? "🔑 Permissioned" : s.visibility === "private" ? "🔒 Private" : s.visibility}</span>
|
||||
</div>
|
||||
<div class="card-desc">${this.esc(s.description || "")}</div>
|
||||
<div class="card-meta">
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ function ensureSpaceConfigDoc(space: string): ProposalDoc {
|
|||
name: '',
|
||||
description: '',
|
||||
ownerDid: '',
|
||||
visibility: 'public_read',
|
||||
visibility: 'public',
|
||||
promotionThreshold: 100,
|
||||
votingPeriodDays: 7,
|
||||
creditsPerDay: 10,
|
||||
|
|
@ -281,7 +281,7 @@ routes.post("/api/spaces", async (c) => {
|
|||
try { claims = await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
|
||||
|
||||
const body = await c.req.json();
|
||||
const { name, slug, description, visibility = "public_read" } = body;
|
||||
const { name, slug, description, visibility = "public" } = body;
|
||||
if (!name || !slug) return c.json({ error: "name and slug required" }, 400);
|
||||
if (!/^[a-z0-9-]+$/.test(slug)) return c.json({ error: "Invalid slug" }, 400);
|
||||
|
||||
|
|
|
|||
|
|
@ -291,6 +291,8 @@ export function renderShell(opts: ShellOptions): string {
|
|||
// Render all tabs with the current one active
|
||||
tabBar.setLayers(layers);
|
||||
tabBar.setAttribute('active', 'layer-' + currentModuleId);
|
||||
// Track current module as recently used
|
||||
if (tabBar.trackRecent) tabBar.trackRecent(currentModuleId);
|
||||
|
||||
// Helper: save current tab list to localStorage
|
||||
function saveTabs() {
|
||||
|
|
@ -576,6 +578,7 @@ export function renderExternalAppShell(opts: ExternalAppShellOptions): string {
|
|||
localStorage.setItem(TABS_KEY, JSON.stringify(layers));
|
||||
tabBar.setLayers(layers);
|
||||
tabBar.setAttribute('active', 'layer-' + currentModuleId);
|
||||
if (tabBar.trackRecent) tabBar.trackRecent(currentModuleId);
|
||||
function saveTabs() { localStorage.setItem(TABS_KEY, JSON.stringify(layers)); }
|
||||
tabBar.addEventListener('layer-switch', (e) => { saveTabs(); window.location.href = window.__rspaceNavUrl(spaceSlug, e.detail.moduleId); });
|
||||
tabBar.addEventListener('layer-add', (e) => { const { moduleId } = e.detail; if (!layers.find(l => l.moduleId === moduleId)) layers.push(makeLayer(moduleId, layers.length)); saveTabs(); window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId); });
|
||||
|
|
|
|||
|
|
@ -1300,17 +1300,17 @@ export class RStackIdentity extends HTMLElement {
|
|||
};
|
||||
|
||||
const visInfo = (v: string) =>
|
||||
v === "members_only" ? { icon: "🔒", cls: "vis-private", label: "private" }
|
||||
: v === "authenticated" ? { icon: "🔑", cls: "vis-permissioned", label: "permissioned" }
|
||||
v === "private" ? { icon: "🔒", cls: "vis-private", label: "private" }
|
||||
: v === "permissioned" ? { icon: "🔑", cls: "vis-permissioned", label: "permissioned" }
|
||||
: { icon: "👁", cls: "vis-public", label: "public" };
|
||||
|
||||
const displayName = (s: any) => {
|
||||
const v = s.visibility || "public_read";
|
||||
if (v === "members_only") {
|
||||
const v = s.visibility || "public";
|
||||
if (v === "private") {
|
||||
const username = getUsername();
|
||||
return username ? `${username}'s (you)rSpace` : "(you)rSpace";
|
||||
return username ? `${username}'s Space` : "My Space";
|
||||
}
|
||||
return `${(s.name || s.slug).replace(/</g, "<")} rSpace`;
|
||||
return (s.name || s.slug).replace(/</g, "<");
|
||||
};
|
||||
|
||||
const renderSpaces = (spaces: any[]) => {
|
||||
|
|
@ -1318,7 +1318,7 @@ export class RStackIdentity extends HTMLElement {
|
|||
const publicSpaces = spaces.filter((s) => !s.role && s.accessible);
|
||||
|
||||
const cardHTML = (s: any) => {
|
||||
const vis = visInfo(s.visibility || "public_read");
|
||||
const vis = visInfo(s.visibility || "public");
|
||||
return `
|
||||
<button class="space-card ${vis.cls}" data-slug="${s.slug}">
|
||||
<div class="space-card-initial">${(s.name || s.slug).charAt(0).toUpperCase()}</div>
|
||||
|
|
|
|||
|
|
@ -237,9 +237,8 @@
|
|||
}
|
||||
|
||||
.badge-public { background: rgba(34, 197, 94, 0.15); color: #4ade80; }
|
||||
.badge-public_read { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }
|
||||
.badge-authenticated { background: rgba(251, 191, 36, 0.15); color: #fbbf24; }
|
||||
.badge-members_only { background: rgba(239, 68, 68, 0.15); color: #f87171; }
|
||||
.badge-permissioned { background: rgba(251, 191, 36, 0.15); color: #fbbf24; }
|
||||
.badge-private { background: rgba(239, 68, 68, 0.15); color: #f87171; }
|
||||
|
||||
.num-cell {
|
||||
text-align: right;
|
||||
|
|
@ -433,9 +432,8 @@
|
|||
<input type="text" class="search-input" id="search-input" placeholder="Search spaces..." />
|
||||
<button class="filter-btn active" data-filter="all">All</button>
|
||||
<button class="filter-btn" data-filter="public">Public</button>
|
||||
<button class="filter-btn" data-filter="public_read">Public Read</button>
|
||||
<button class="filter-btn" data-filter="authenticated">Auth</button>
|
||||
<button class="filter-btn" data-filter="members_only">Private</button>
|
||||
<button class="filter-btn" data-filter="permissioned">Permissioned</button>
|
||||
<button class="filter-btn" data-filter="private">Private</button>
|
||||
<select class="sort-select" id="sort-select">
|
||||
<option value="created-desc">Newest First</option>
|
||||
<option value="created-asc">Oldest First</option>
|
||||
|
|
@ -607,10 +605,9 @@
|
|||
|
||||
function visibilityLabel(v) {
|
||||
const labels = {
|
||||
public: "Public",
|
||||
public_read: "Public Read",
|
||||
authenticated: "Auth Required",
|
||||
members_only: "Members Only",
|
||||
public: "👁 Public",
|
||||
permissioned: "🔑 Permissioned",
|
||||
private: "🔒 Private",
|
||||
};
|
||||
return labels[v] || v;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,10 +207,9 @@
|
|||
<div class="form-group">
|
||||
<label for="visibility">Visibility</label>
|
||||
<select id="visibility">
|
||||
<option value="public">Public — anyone can read and write</option>
|
||||
<option value="public_read" selected>Public Read — anyone can view, sign in to edit</option>
|
||||
<option value="authenticated">Authenticated — sign in to view and edit</option>
|
||||
<option value="members_only">Members Only — invite-only access</option>
|
||||
<option value="public" selected>Public — anyone can read, sign in to write</option>
|
||||
<option value="permissioned">Permissioned — sign in to read & write</option>
|
||||
<option value="private">Private — invite-only access</option>
|
||||
</select>
|
||||
<p class="help-text">You can change this later in space settings.</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue