Visibility System for Notebooks and Journal Entries
Overview
The visibility system allows users to control who can see their notebooks and journal entries. It supports separate visibility controls for metadata (title, mood, tags, emotional tone) and content (the actual text).
Visibility Levels
user- Only the owner can see it (default)team- All members of a specific team can see itorg- All members of a specific organization can see itpublic- Anyone can see it
Separate Metadata and Content Visibility
You can set different visibility levels for metadata and content:
-
Metadata visibility - Controls who can see:
- Title
- Mood
- Tags
- Emotional tone/analysis (future feature)
- Creation date
- Other metadata fields
-
Content visibility - Controls who can see:
- The actual journal entry content/text
Example Use Cases
-
Share metadata only: Set
metadata_visibility: 'team'andcontent_visibility: 'user'- Team members can see you wrote something and what mood you were in
- But they can't read the actual content
-
Share everything with team: Set both to
'team'- Team members can see both metadata and content
-
Public mood tracking: Set
metadata_visibility: 'public'andcontent_visibility: 'user'- Anyone can see your mood trends
- But content remains private
Database Schema
Notebooks Table
metadata_visibility TEXT DEFAULT 'user' -- 'public', 'team', 'org', 'user'
content_visibility TEXT DEFAULT 'user' -- 'public', 'team', 'org', 'user'
team_id UUID REFERENCES teams(id) -- Required if visibility is 'team'
org_id UUID REFERENCES organizations(id) -- Required if visibility is 'org'
Journal Entries Table
metadata_visibility TEXT DEFAULT 'user' -- 'public', 'team', 'org', 'user'
content_visibility TEXT DEFAULT 'user' -- 'public', 'team', 'org', 'user'
team_id UUID REFERENCES teams(id) -- Required if visibility is 'team'
org_id UUID REFERENCES organizations(id) -- Required if visibility is 'org'
Inheritance
Journal entries can inherit visibility from their notebook:
- If an entry has explicit
metadata_visibilityorcontent_visibilityset, it uses those - If an entry's visibility is
'user'orNULL, it inherits from the notebook - If the entry has no notebook, it defaults to
'user'
API Usage
Create Notebook with Visibility
POST /api/notebooks
{
"name": "My Team Notebook",
"metadata_visibility": "team",
"content_visibility": "user",
"team_id": "team-uuid-here"
}
Create Entry with Visibility
POST /api/journal
{
"title": "Daily Reflection",
"content": "Today was great...",
"mood": "happy",
"notebook_id": "notebook-uuid",
"metadata_visibility": "team",
"content_visibility": "user",
"team_id": "team-uuid-here"
}
Update Visibility
PUT /api/notebooks/:id
{
"metadata_visibility": "org",
"org_id": "org-uuid-here"
}
Response Format
When fetching entries or notebooks, the API automatically filters based on visibility:
Entry Response (Content Hidden)
{
"id": "entry-uuid",
"title": "Daily Reflection",
"mood": "happy",
"tags": ["work", "personal"],
"content": null,
"content_hidden": true,
"metadata_visibility": "team",
"content_visibility": "user",
"created_at": "2024-01-01T00:00:00Z"
}
Entry Response (Content Visible)
{
"id": "entry-uuid",
"title": "Daily Reflection",
"content": "Today was great...",
"mood": "happy",
"tags": ["work", "personal"],
"metadata_visibility": "team",
"content_visibility": "team",
"created_at": "2024-01-01T00:00:00Z"
}
Permission Checks
The system uses helper functions to check visibility:
TypeScript Helpers
import {
canViewNotebookMetadata,
canViewNotebookContent,
canViewEntryMetadata,
canViewEntryContent,
filterEntryForViewer
} from '@/database/visibility'
// Check if user can view entry metadata
const canView = await canViewEntryMetadata(supabase, userId, entry, notebook)
// Filter entry for viewer (removes content if not visible)
const filtered = await filterEntryForViewer(supabase, userId, entry, notebook)
Database Functions
-- Check if user can view notebook metadata
SELECT can_view_notebook_metadata(
'viewer-user-id',
notebook_user_id,
metadata_visibility,
team_id,
org_id
)
-- Check if user can view entry content
SELECT can_view_entry_content(
'viewer-user-id',
entry_user_id,
content_visibility,
entry_team_id,
entry_org_id,
notebook_id
)
Row Level Security (RLS)
RLS policies ensure that:
- Users can only see entries/notebooks they own OR have visibility access to
- Super admins can see everything
- Team/org members can see items shared with their team/org
Validation
The system validates that:
team_idis set when visibility is'team'org_idis set when visibility is'org'- Visibility values are one of:
'public','team','org','user'
Future Enhancements
- Emotional tone analysis that respects metadata visibility
- Analytics/insights based on visible metadata
- Granular permissions (e.g., specific users instead of just teams/orgs)
- Time-based visibility (e.g., make public after 30 days)