useBlock
Location: src/hooks/useBlock.js
Overview
Provides multiple hooks for managing user blocking. useBlockState checks the bidirectional block state between two users. useBlockedUsers lists all users blocked by the current user. useCanInteract provides permission flags for UI decisions. useProfileVisibility uses server-side logic to determine if a profile should be shown as 404.
Critical design note: When a user is blocked by someone, they see a 404 page with no evidence that a block exists (Invariant #14: Privacy Is Infrastructure).
Exported Hooks
useBlockState
Checks bidirectional block state between the current user and another user.
function useBlockState(otherUserId: string): {
loading: boolean,
error: string | null,
iBlockedThem: boolean,
theyBlockedMe: boolean,
myBlockId: string | null,
isBlocked: boolean,
blockUser: (reason?: string | null) => Promise<{ success?: boolean, error?: string }>,
unblockUser: () => Promise<{ success?: boolean, error?: string }>,
refetch: () => Promise<void>
}
| Property | Type | Description |
|---|---|---|
iBlockedThem | boolean | Whether the current user has blocked the other user |
theyBlockedMe | boolean | Whether the other user has blocked the current user |
myBlockId | string | null | ID of the block record if current user blocked the other |
isBlocked | boolean | true if a block exists in either direction |
blockUser | (reason?) => Promise<Result> | Block the other user with an optional private reason. Also removes any existing friendship. |
unblockUser | () => Promise<Result> | Remove the current user's block on the other user |
useBlockedUsers
Lists all users blocked by the current user.
function useBlockedUsers(): {
loading: boolean,
error: string | null,
blockedUsers: Array<{ id, created_at, reason, blocked_user: Profile }>,
count: number,
unblockById: (userId: string) => Promise<{ success?: boolean, error?: string }>,
refetch: () => Promise<void>
}
| Property | Type | Description |
|---|---|---|
blockedUsers | Array | Blocked users with profile info (display_name, username, avatar_url) |
count | number | Total number of blocked users |
unblockById | (userId) => Promise<Result> | Unblock a user by their ID. Updates local state optimistically. |
useCanInteract
Provides permission flags for UI elements based on block state.
function useCanInteract(otherUserId: string): {
loading: boolean,
canInteract: boolean,
canSendFriendRequest: boolean,
canComment: boolean,
canViewProfile: boolean,
shouldShow404: boolean,
iBlockedThem: boolean,
theyBlockedMe: boolean,
isBlocked: boolean
}
| Property | Type | Description |
|---|---|---|
canInteract | boolean | No block exists in either direction |
canSendFriendRequest | boolean | No block exists in either direction |
canComment | boolean | They have not blocked the current user |
canViewProfile | boolean | They have not blocked the current user |
shouldShow404 | boolean | true if they blocked the current user (show 404, no block evidence) |
useProfileVisibility
Server-side profile visibility check via can_view_profile RPC.
function useProfileVisibility(profileId: string): {
loading: boolean,
shouldShow404: boolean,
viewType: string | null
}
| Property | Type | Description |
|---|---|---|
shouldShow404 | boolean | Whether the profile should be shown as a 404 page |
viewType | string | null | The type of view allowed (e.g., 'not_found', 'full') |
Usage
import { useBlockState, useCanInteract } from '../hooks/useBlock';
function ProfilePage({ userId }) {
const { shouldShow404, canInteract } = useCanInteract(userId);
if (shouldShow404) return <NotFound />;
return (
<div>
<ProfileContent userId={userId} />
{canInteract && <FriendButton userId={userId} />}
</div>
);
}
function BlockedUsersSettings() {
const { blockedUsers, count, unblockById, loading } = useBlockedUsers();
return (
<div>
<h3>Blocked Users ({count})</h3>
{blockedUsers.map(b => (
<div key={b.id}>
<span>{b.blocked_user.display_name}</span>
<button onClick={() => unblockById(b.blocked_user.id)}>Unblock</button>
</div>
))}
</div>
);
}
Notes
- Blocking a user also removes any existing friendship between them.
- The
reasonfield on blocks is private and only visible to the blocking user. useCanInteractis a convenience wrapper arounduseBlockStatefor common UI permission patterns.useProfileVisibilityuses a server-side RPC function for accurate block checking that cannot be bypassed client-side.
Last updated: 2026-02-07