ActorBadge Component System
The ActorBadge is Mallnline's primary identity surface โ a context-aware component that renders user attribution with sigils, affiliation badges, and an interactive hover popover. It is the visual manifestation of the Sigil Taxonomy and the frontend counterpart of the User Identity Resolution pipeline.
Every place a user identity appears in the UI โ community Q&A threads, reviews, uChat participants, team listings โ uses ActorBadge to resolve raw userId values into rich, interactive identity cards.
Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ActorBadge โ
โ โ
โ Props: userId, profiles, authorAssociation, โ
โ maletHandle, isPrivate โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Identity Resolution (5-tier fallback) โ โ
โ โ 1. profile.handle โ r|mdenzo โ โ
โ โ 2. profile.displayName โ Meek Denzo โ โ
โ โ 3. Affiliation context โ ๐ช Ownerยทm|... โ โ
โ โ 4. Auto-fetched affil โ (same) โ โ
โ โ 5. Anonymous fallback โ Mallnline User โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Sigil Engine โ โ
โ โ v| โ Visitor (default) โ โ
โ โ r| โ Representative (owner/staff) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Hover Popover (context-aware) โ โ
โ โ Self: Edit Profile โ โ
โ โ Other: Follow + uChat โ โ
โ โ Anon: Sign in โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Usage
Basic (community thread)
<ActorBadge userId={comment.createdBy} {profiles} />
With affiliation context (Malet Owner reply)
<ActorBadge
userId={review.ownerResponse.authorId}
{profiles}
authorAssociation="OWNER"
maletHandle="luminara-crafts"
/>
Anonymous/private
<ActorBadge userId={userId} {profiles} isPrivate={true} />
Sigil Taxonomy
The sigil prefix dynamically switches based on affiliation context:
| Sigil | Context | Trigger |
|---|---|---|
v| |
Visitor (default) | No authorAssociation or resolvedAffiliation |
r| |
Representative | authorAssociation is OWNER, MEMBER, or COLLABORATOR โ OR resolvedAffiliation is set |
The sigil applies to both the inline badge and the popover handle display.
NOTE
The r| sigil represents a user acting in an official capacity for a Malet. It uses the user's own handle (e.g., r|mdenzo), never the Malet handle. The Malet affiliation is shown separately via the affiliation badge.
Identity Resolution
When rendering a user, ActorBadge follows a strict 5-tier fallback:
- Nodes profile handle โ
r|mdenzoorv|mdenzo - Nodes profile displayName โ
Meek Denzo - Inline affiliation context โ
๐ช Owner ยท m|luminara-crafts(whenmaletHandle+authorAssociationare passed but profile resolution fails) - Auto-fetched affiliation โ Same as above, but resolved via eager
userAffiliationsquery on mount - Anonymous fallback โ
Mallnline UserwithMUinitials
IMPORTANT
Raw user IDs are never exposed in the UI. The previous fallback userId.slice(0, 8) + 'โฆ' was replaced with 'Mallnline User' in profileResolver.ts. This is enforced in both formatUserDisplay() and getAvatarInitials().
Affiliation Badges
When a user has an association with a Malet (owner, staff), the badge renders:
- Inline association label โ
Owner,Member,Collaborator,Contributor,First Timer - Affiliation badge (in popover) โ
๐ช Owner ยท m|luminara-crafts
Affiliations are resolved via two paths:
- Explicit: Parent passes
authorAssociation+maletHandleprops - Auto-resolved: On mount, if the profile is unresolved and no explicit context exists,
ActorBadgeeagerly queriesuserAffiliations(userId)
Self-Aware Popover
The hover popover adapts based on whether the logged-in user is viewing their own badge:
| Context | Actions shown |
|---|---|
| Self | โ๏ธ Edit Profile โ /settings/profile |
| Authenticated (other) | Follow / Following โ + ๐ฌ uChat โ /uchat?dm={handle} |
| Unauthenticated | Sign in โ /auth |
The popover mini-profile (avatar + display name + handle) is clickable, linking to /u/{handle}.
TIP
The self-detection uses $currentUser?.id === userId derived from the currentUser Svelte store. This also skips the isFollowing API call for the user's own badge, saving a network request per badge instance.
uChat Integration
The popover's uChat button routes to /uchat?dm={handle} using the resolved profile handle โ never the raw UUID. The button only renders when:
- The user is viewing someone else's badge (not self)
- The profile handle is resolved
- The user is authenticated
Error Sanitization
The uChat store uses a shared sanitizeGqlError() helper to prevent raw GraphQL response dumps:
function sanitizeGqlError(err: any, fallback: string): string {
const gqlMessage = err?.response?.errors?.[0]?.message;
if (gqlMessage?.includes('Authentication required')) {
return 'Please sign in to access uChat.';
}
if (gqlMessage) return gqlMessage;
if (err instanceof Error) return err.message;
return fallback;
}
This is applied to loadConversations, openConversation, and createConversation.
Key Files
| File | Purpose |
|---|---|
src/lib/components/ActorBadge.svelte |
Main component |
src/lib/utils/profileResolver.ts |
formatUserDisplay(), getAvatarInitials(), resolveUserProfiles() |
src/lib/queries/malet.ts |
USER_AFFILIATIONS query |
src/lib/queries/follows.ts |
IS_FOLLOWING, FOLLOW_ENTITY, UNFOLLOW_ENTITY |
src/lib/uchat/store.svelte.ts |
sanitizeGqlError() and conversation management |
src/stores/auth.ts |
currentUser store for self-detection |
Related
- User Identity Resolution โ The profile resolver pipeline that ActorBadge depends on
- Handle System โ The sigil taxonomy (
v|,r|,m|,o|) - Community Features โ Q&A and reviews where ActorBadge is heavily used
- uChat Client SDK โ The messaging system integrated into the popover