Social
StoryRow
A horizontally scrolling row of user story bubbles. Each bubble shows an avatar with an optional unread indicator ring. Tapping a story calls onStoryPress.
| Prop | Type | Default | Description |
|---|---|---|---|
stories | Story[] | — | Array of story descriptors (see below) |
onStoryPress? | (story: Story) => void | — | Called when the user taps a story bubble |
size? | 'sm' | 'md' | 'lg' | 'md' | Avatar diameter |
gap? | SpacingProp | 'md' | Gap between story bubbles |
showAddButton? | boolean | false | Prepend an “add story” button before the list |
onAddPress? | () => void | — | Called when the add button is tapped |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
Story shape:
interface Story {
id: string;
username: string;
avatarUri?: string;
seen?: boolean; // When false, ring is highlighted to show unseen content
}
import { StoryRow } from '@objectifthunes/limestone-sdk';
const stories = [
{ id: '1', username: 'alice', avatarUri: 'https://example.com/alice.jpg', seen: false },
{ id: '2', username: 'bob', avatarUri: 'https://example.com/bob.jpg', seen: true },
{ id: '3', username: 'carol', seen: false },
];
<StoryRow
stories={stories}
onStoryPress={(story) => openStory(story.id)}
showAddButton
onAddPress={openCamera}
/>
ReactionPicker
A horizontal emoji reaction bar with a count display per reaction. Tapping an active reaction un-reacts; tapping an inactive one reacts. A long-press or ”+” button opens the full emoji picker.
| Prop | Type | Default | Description |
|---|---|---|---|
reactions | Reaction[] | — | Available reactions and their current counts |
onReact? | (emoji: string) => void | — | Called when the user selects an emoji |
onUnreact? | (emoji: string) => void | — | Called when the user taps an already-selected emoji |
userReaction? | string | — | Emoji the current user has already reacted with |
showPicker? | boolean | true | Show the ”+” button to open the full emoji picker |
pickerEmojis? | string[] | — | Emoji list shown in the expanded picker |
size? | 'sm' | 'md' | 'lg' | 'md' | Emoji and count size |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
Reaction shape:
interface Reaction {
emoji: string;
count: number;
}
import { ReactionPicker } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';
function PostReactions({ postId }: { postId: string }) {
const [reactions, setReactions] = useState([
{ emoji: '👍', count: 12 },
{ emoji: '❤️', count: 5 },
{ emoji: '😂', count: 3 },
]);
const [userReaction, setUserReaction] = useState<string | undefined>();
return (
<ReactionPicker
reactions={reactions}
userReaction={userReaction}
onReact={(emoji) => { addReaction(postId, emoji); setUserReaction(emoji); }}
onUnreact={(emoji) => { removeReaction(postId, emoji); setUserReaction(undefined); }}
/>
);
}
CommentThread
A scrollable list of comments with nested reply support. Each comment shows an avatar, username, timestamp, body text, and a reply affordance.
| Prop | Type | Default | Description |
|---|---|---|---|
comments | Comment[] | — | Flat or nested comment array |
onReply? | (commentId: string) => void | — | Called when the reply button on a comment is pressed |
onLike? | (commentId: string) => void | — | Called when the like button is pressed |
onLoadMore? | () => void | — | Called when the list scrolls near the end |
loading? | boolean | false | Shows a loading indicator |
maxDepth? | number | 2 | Maximum nesting level for inline replies |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
Comment shape:
interface Comment {
id: string;
authorName: string;
authorAvatarUri?: string;
body: string;
timestamp: string;
likeCount?: number;
liked?: boolean;
replies?: Comment[];
}
import { CommentThread } from '@objectifthunes/limestone-sdk';
<CommentThread
comments={comments}
onReply={(id) => openReplyInput(id)}
onLike={(id) => toggleLike(id)}
onLoadMore={fetchMoreComments}
/>
MentionInput
A text input that detects @ triggers and renders an autocomplete suggestion list. When the user selects a mention, it is inserted inline and highlighted.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled value |
onChangeText | (v: string) => void | — | Change handler |
suggestions | MentionSuggestion[] | — | Current suggestions to show |
onMentionSearch? | (query: string) => void | — | Called with the text after @ so you can fetch suggestions |
onMentionSelect? | (suggestion: MentionSuggestion) => void | — | Called when the user picks a suggestion |
placeholder? | string | — | Input placeholder |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Input size |
variant? | 'outline' | 'filled' | 'underline' | 'outline' | Input variant |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
MentionSuggestion shape:
interface MentionSuggestion {
id: string;
username: string;
displayName?: string;
avatarUri?: string;
}
import { MentionInput } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';
function CommentComposer() {
const [text, setText] = useState('');
const [suggestions, setSuggestions] = useState<MentionSuggestion[]>([]);
return (
<MentionInput
value={text}
onChangeText={setText}
suggestions={suggestions}
onMentionSearch={(query) => searchUsers(query).then(setSuggestions)}
onMentionSelect={(s) => console.log('Mentioned:', s.username)}
placeholder="Write a comment…"
/>
);
}
PresenceIndicator
A small dot or badge overlaid on an avatar or icon to show the online/offline/away/busy status of a user.
| Prop | Type | Default | Description |
|---|---|---|---|
status | 'online' | 'offline' | 'away' | 'busy' | — | Presence status; controls the dot color |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'sm' | Dot diameter |
placement? | 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'bottom-right' | Position relative to the child |
children | React.ReactNode | — | The element the indicator overlays (usually an Avatar) |
accessibilityLabel? | string | — | Accessibility label for the status dot |
testID? | string | — | Test identifier |
import { PresenceIndicator, Avatar } from '@objectifthunes/limestone-sdk';
function UserAvatar({ user }: { user: { name: string; status: 'online' | 'offline' | 'away' | 'busy' } }) {
return (
<PresenceIndicator status={user.status} placement="bottom-right">
<Avatar name={user.name} size="md" />
</PresenceIndicator>
);
}