tiktok-comments 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,28 +11,15 @@ npm install tiktok-comments
11
11
  ## Usage (React SPA)
12
12
 
13
13
  ```tsx
14
- import { useState, useCallback } from "react";
15
- import { CommentSection } from "tiktok-comments";
16
- import type { Attachment, Comment } from "tiktok-comments";
14
+ import { CommentSection, type CommentApiPayload } from "tiktok-comments";
17
15
  import "tiktok-comments/styles.css";
18
16
 
17
+ function mapResponse(raw: unknown): CommentApiPayload {
18
+ return raw as CommentApiPayload;
19
+ }
20
+
19
21
  export function VideoComments() {
20
- const [comments, setComments] = useState<Comment[]>([]);
21
-
22
- const handleSubmit = useCallback((text: string, attachment?: Attachment, replyTo?: string) => {
23
- // persist to your API, then update state
24
- }, []);
25
-
26
- return (
27
- <CommentSection
28
- comments={comments}
29
- currentUser={{ name: "You", avatar: "/avatar.png" }}
30
- onSubmit={handleSubmit}
31
- onLike={(id) => { /* ... */ }}
32
- onLikeReply={(commentId, replyId) => { /* ... */ }}
33
- theme="dark"
34
- />
35
- );
22
+ return <CommentSection url="/api/comments" response={mapResponse} theme="dark" />;
36
23
  }
37
24
  ```
38
25
 
@@ -49,29 +36,59 @@ Use the component in a client page or client component:
49
36
  ```tsx
50
37
  "use client";
51
38
 
52
- import { CommentSection } from "tiktok-comments";
39
+ import { CommentSection, type CommentApiPayload } from "tiktok-comments";
53
40
 
54
41
  export default function CommentsPage() {
55
- return <CommentSection comments={[]} onSubmit={() => {}} onLike={() => {}} onLikeReply={() => {}} />;
42
+ return <CommentSection url="/api/comments" response={(raw) => raw as CommentApiPayload} />;
56
43
  }
57
44
  ```
58
45
 
59
46
  ## API
60
47
 
61
- `CommentSection` is a controlled component. You own comment state and wire it to your backend.
48
+ `CommentSection` fetches from `url`. Map your API JSON with `response`.
62
49
 
63
50
  | Prop | Type | Description |
64
51
  |------|------|-------------|
65
- | `comments` | `Comment[]` | Comment list to render |
66
- | `currentUser` | `{ name, avatar }` | Shown in the input bar |
67
- | `onSubmit` | `(text, attachment?, replyTo?) => void` | New comment submitted |
68
- | `onLike` | `(commentId) => void` | Top-level comment liked |
69
- | `onLikeReply` | `(commentId, replyId) => void` | Reply liked |
70
- | `onReply` | `(commentId, username) => void` | Optional reply intent callback |
52
+ | `url` | `string` | Comments resource, e.g. `/api/comments` |
53
+ | `response` | `(raw: unknown) => CommentApiPayload` | Maps list and mutation JSON |
54
+ | `endpoints` | `Partial<CommentSectionEndpoints>` | Per-route URL overrides; unset keys default from `url` (see below) |
71
55
  | `theme` | `"dark" \| "light"` | Default `"dark"` |
72
56
  | `className` | `string` | Extra classes on root |
57
+ | `showThemeToggle` | `boolean` | Show theme toggle beside search. Default `false` |
58
+ | `onThemeChange` | `(theme: "dark" \| "light") => void` | Called when user toggles theme (use with `theme` for controlled mode) |
59
+ | `onViewProfile` | `(profile: CommentProfile) => void` | Optional host navigation for View profile (overrides `profile.profileUrl`) |
60
+ | `onReportSticker` | `(src: string) => void` | Optional host handler when user reports a posted sticker |
61
+
62
+ **`endpoints` defaults** (all derived from `url` unless overridden):
63
+
64
+ | Key | Default | Method |
65
+ |-----|---------|--------|
66
+ | `list` | `url` | GET + `q`, `sort`, `offset`, `limit` |
67
+ | `submit` | `url` | POST |
68
+ | `likeComment(id)` | `{url}/{id}/like` | POST |
69
+ | `dislikeComment(id)` | `{url}/{id}/dislike` | POST |
70
+ | `likeReply(commentId, replyId)` | `{url}/{commentId}/replies/{replyId}/like` | POST |
71
+ | `dislikeReply(commentId, replyId)` | `{url}/{commentId}/replies/{replyId}/dislike` | POST |
72
+ | `users` | `{url}/users` | GET |
73
+ | `userByName(name)` | `{url}/users?name=` | GET |
74
+ | `stickers` | `{url}/stickers` | GET + query |
75
+ | `stickerFavorite(id)` | `{url}/stickers/{id}/favorite` | POST |
76
+
77
+ ```tsx
78
+ <CommentSection
79
+ url="/api/comments"
80
+ response={mapResponse}
81
+ endpoints={{
82
+ users: "/api/authors",
83
+ userByName: (name) => `/api/authors?name=${encodeURIComponent(name)}`,
84
+ stickers: "/api/sticker-catalog",
85
+ }}
86
+ />
87
+ ```
88
+
89
+ Exported helpers: `defaultCommentSectionEndpoints`, `resolveCommentSectionEndpoints`, types `CommentSectionEndpoints`, `CommentSectionEndpointsConfig`.
73
90
 
74
- Exported types: `Comment`, `Reply`, `Attachment`, `CommentUser`, `EarnedBadge`, `SubscriptionTier`.
91
+ Exported types: `Comment`, `Reply`, `Attachment`, `CommentUser`, `CommentProfile`, `EarnedBadge`, `SubscriptionTier`, `ReplyTarget`, `CommentSort`, `CommentApiPayload`.
75
92
 
76
93
  ## CSS isolation
77
94
 
@@ -81,7 +98,7 @@ All Tailwind utilities use the `tcm:` prefix (e.g. `tcm:flex`, `tcm:hover:tcm:bg
81
98
 
82
99
  ```bash
83
100
  npm install
84
- npm run dev # demo playground
101
+ npm run dev # demo playground + in-memory mock API (`/api/comments`)
85
102
  npm run check # typecheck + lint
86
103
  npm run build # library + CSS to dist/
87
104
  ```
@@ -2,6 +2,7 @@ import { EarnedBadge } from './comment-data';
2
2
  interface BadgeDrawerProps {
3
3
  badge: EarnedBadge;
4
4
  onClose: () => void;
5
+ className?: string;
5
6
  }
6
- export declare function BadgeDrawer({ badge, onClose }: BadgeDrawerProps): import("react").JSX.Element;
7
+ export declare function BadgeDrawer({ badge, onClose, className }: BadgeDrawerProps): import("react").JSX.Element;
7
8
  export {};
@@ -3,6 +3,18 @@ export interface CommentUser {
3
3
  name: string;
4
4
  avatar: string;
5
5
  }
6
+ export interface CommentProfile extends CommentUser {
7
+ subscription?: SubscriptionTier;
8
+ badges?: string[];
9
+ /** Net score (e.g. likes minus downvotes on the user's comments). */
10
+ aura?: number;
11
+ commentCount?: number;
12
+ mangaReadCount?: number;
13
+ /** Leaderboard position (1 = top). */
14
+ rank?: number;
15
+ /** When set, View profile opens this URL (new tab). */
16
+ profileUrl?: string;
17
+ }
6
18
  export interface EarnedBadge {
7
19
  id: string;
8
20
  lucideIcon: string;
@@ -12,6 +24,19 @@ export interface EarnedBadge {
12
24
  rarity: "common" | "rare" | "epic" | "legendary";
13
25
  }
14
26
  export declare const BADGE_CATALOG: EarnedBadge[];
27
+ export interface StickerPack {
28
+ id: string;
29
+ icon: string;
30
+ name: string;
31
+ }
32
+ export interface StickerItem {
33
+ id: string;
34
+ url: string;
35
+ packId: string;
36
+ kind: "sticker" | "emoji";
37
+ label?: string;
38
+ favorited?: boolean;
39
+ }
15
40
  export interface Reply {
16
41
  id: string;
17
42
  name: string;
@@ -19,6 +44,8 @@ export interface Reply {
19
44
  text: string;
20
45
  likes: number;
21
46
  liked: boolean;
47
+ disliked?: boolean;
48
+ /** ISO 8601 UTC instant. The library formats this for display. */
22
49
  timestamp: string;
23
50
  replyingTo?: string;
24
51
  image?: string;
@@ -32,6 +59,8 @@ export interface Comment {
32
59
  text: string;
33
60
  likes: number;
34
61
  liked: boolean;
62
+ disliked?: boolean;
63
+ /** ISO 8601 UTC instant. The library formats this for display. */
35
64
  timestamp: string;
36
65
  pinned?: boolean;
37
66
  replies: Reply[];
@@ -1,13 +1,27 @@
1
- import { CommentUser } from './comment-data';
1
+ import { StickerFetchParams } from '../lib/comments-client';
2
+ import { CommentProfile, CommentUser, StickerItem, StickerPack } from './comment-data';
2
3
  export interface Attachment {
3
4
  type: "image" | "sticker";
4
5
  url: string;
5
6
  }
7
+ interface ReplyPreview {
8
+ username: string;
9
+ text: string;
10
+ }
6
11
  interface CommentInputBarProps {
7
- replyingTo: string | null;
12
+ replyingTo: ReplyPreview | null;
8
13
  onCancelReply: () => void;
9
14
  onSubmit: (text: string, attachment?: Attachment) => void;
10
15
  currentUser?: CommentUser;
16
+ mentionUsers?: CommentProfile[];
17
+ mentionUsersLoading?: boolean;
18
+ onOpenMentions?: () => void;
19
+ stickerPacks?: StickerPack[];
20
+ stickers?: StickerItem[];
21
+ stickersLoading?: boolean;
22
+ onFetchStickers?: (params: StickerFetchParams) => void;
23
+ onToggleStickerFavorite?: (id: string) => void;
24
+ onOpenStickers?: () => void;
11
25
  }
12
- export declare function CommentInputBar({ replyingTo, onCancelReply, onSubmit, currentUser }: CommentInputBarProps): import("react").JSX.Element;
26
+ export declare function CommentInputBar({ replyingTo, onCancelReply, onSubmit, currentUser, mentionUsers, mentionUsersLoading, onOpenMentions, stickerPacks, stickers, stickersLoading, onFetchStickers, onToggleStickerFavorite, onOpenStickers, }: CommentInputBarProps): import("react").JSX.Element;
13
27
  export {};
@@ -1,9 +1,16 @@
1
- import { Comment } from './comment-data';
1
+ import { Comment, CommentProfile } from './comment-data';
2
2
  interface CommentItemProps {
3
3
  comment: Comment;
4
+ expandReplies?: boolean;
5
+ mentionNames: string[];
4
6
  onLike: (id: string) => void;
7
+ onDislike: (id: string) => void;
5
8
  onLikeReply: (commentId: string, replyId: string) => void;
6
- onReply: (name: string) => void;
9
+ onDislikeReply: (commentId: string, replyId: string) => void;
10
+ onReply: (name: string, replyId: string | undefined, text: string) => void;
11
+ onMentionClick: (name: string) => Promise<CommentProfile | undefined>;
12
+ onViewProfile?: (profile: CommentProfile) => void;
13
+ onReportSticker?: (src: string) => void;
7
14
  }
8
- export declare function CommentItem({ comment, onLike, onLikeReply, onReply }: CommentItemProps): import("react").JSX.Element;
15
+ export declare function CommentItem({ comment, expandReplies, mentionNames, onLike, onDislike, onLikeReply, onDislikeReply, onReply, onMentionClick, onViewProfile, onReportSticker, }: CommentItemProps): import("react").JSX.Element;
9
16
  export {};
@@ -1,13 +1,31 @@
1
- import { Comment, CommentUser } from './comment-data';
2
- import { Attachment } from './comment-input-bar';
1
+ import { CommentApiPayload, CommentSectionEndpointsConfig } from '../lib/comments-client';
2
+ import { CommentProfile } from './comment-data';
3
+ export type { CommentApiPayload, CommentSectionEndpoints, CommentSectionEndpointsConfig, CommentSort, StickerFetchParams, } from '../lib/comments-client';
4
+ export interface ReplyTarget {
5
+ commentId: string;
6
+ replyId?: string;
7
+ username: string;
8
+ text: string;
9
+ }
3
10
  export interface CommentSectionProps {
4
- comments: Comment[];
5
- currentUser?: CommentUser;
6
- onSubmit: (text: string, attachment?: Attachment, replyTo?: string) => void;
7
- onLike: (commentId: string) => void;
8
- onLikeReply: (commentId: string, replyId: string) => void;
9
- onReply?: (commentId: string, username: string) => void;
11
+ /** Comments resource, e.g. `/api/comments`. */
12
+ url: string;
13
+ /** Maps any server JSON into the library payload. */
14
+ response: (raw: unknown) => CommentApiPayload;
15
+ /**
16
+ * Optional per-route overrides. Unset keys default from `url`:
17
+ * list/submit → `url`; like → `{url}/{id}/like`; users → `{url}/users`; etc.
18
+ */
19
+ endpoints?: CommentSectionEndpointsConfig;
10
20
  className?: string;
11
21
  theme?: "dark" | "light";
22
+ /** Show theme toggle beside search. Default `false`. */
23
+ showThemeToggle?: boolean;
24
+ /** Called when the user toggles theme (controlled mode with `theme`). */
25
+ onThemeChange?: (theme: "dark" | "light") => void;
26
+ /** Host navigation when View profile is tapped (overrides `profile.profileUrl`). */
27
+ onViewProfile?: (profile: CommentProfile) => void;
28
+ /** Host moderation when a posted sticker is reported. */
29
+ onReportSticker?: (src: string) => void;
12
30
  }
13
- export declare function CommentSection({ comments, currentUser, onSubmit, onLike, onLikeReply, onReply, className, theme, }: CommentSectionProps): import("react").JSX.Element;
31
+ export declare function CommentSection({ url, response, endpoints: endpointsConfig, className, theme, showThemeToggle, onThemeChange, onViewProfile, onReportSticker, }: CommentSectionProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ interface ImagePickerProps {
2
+ onSelect: (url: string) => void;
3
+ onClose: () => void;
4
+ }
5
+ export declare function ImagePicker({ onSelect, onClose }: ImagePickerProps): import("react").JSX.Element;
6
+ export {};
@@ -1,6 +1,10 @@
1
+ import { CommentProfile } from './comment-data';
1
2
  interface MentionSelectorProps {
2
- onSelect: (username: string) => void;
3
+ users: CommentProfile[];
4
+ loading?: boolean;
5
+ onSelect: (name: string) => void;
6
+ onInsertEmoji: (emoji: string) => void;
3
7
  onClose: () => void;
4
8
  }
5
- export declare function MentionSelector({ onSelect, onClose }: MentionSelectorProps): import("react").JSX.Element;
9
+ export declare function MentionSelector({ users, loading, onSelect, onInsertEmoji, onClose, }: MentionSelectorProps): import("react").JSX.Element;
6
10
  export {};
@@ -1,10 +1,8 @@
1
- import { SubscriptionTier } from './comment-data';
1
+ import { CommentProfile } from './comment-data';
2
2
  interface ProfileDrawerProps {
3
- name: string;
4
- avatar: string;
5
- subscription?: SubscriptionTier;
6
- badges?: string[];
3
+ profile: CommentProfile;
7
4
  onClose: () => void;
5
+ onViewProfile?: (profile: CommentProfile) => void;
8
6
  }
9
- export declare function ProfileDrawer({ name, avatar, subscription, badges, onClose }: ProfileDrawerProps): import("react").JSX.Element;
7
+ export declare function ProfileDrawer({ profile, onClose, onViewProfile }: ProfileDrawerProps): import("react").JSX.Element;
10
8
  export {};
@@ -1,8 +1,14 @@
1
- import { Reply } from './comment-data';
1
+ import { Reply, CommentProfile } from './comment-data';
2
2
  interface ReplyListProps {
3
3
  replies: Reply[];
4
+ expandReplies?: boolean;
5
+ mentionNames: string[];
4
6
  onLikeReply: (replyId: string) => void;
5
- onReply: (username: string) => void;
7
+ onDislikeReply: (replyId: string) => void;
8
+ onReply: (username: string, replyId: string | undefined, text: string) => void;
9
+ onMentionClick: (name: string) => Promise<CommentProfile | undefined>;
10
+ onViewProfile?: (profile: CommentProfile) => void;
11
+ onReportSticker?: (src: string) => void;
6
12
  }
7
- export declare function ReplyList({ replies, onLikeReply, onReply }: ReplyListProps): import("react").JSX.Element | null;
13
+ export declare function ReplyList({ replies, expandReplies, mentionNames, onLikeReply, onDislikeReply, onReply, onMentionClick, onViewProfile, onReportSticker, }: ReplyListProps): import("react").JSX.Element | null;
8
14
  export {};
@@ -1,6 +1,7 @@
1
1
  interface StickerDrawerProps {
2
2
  src: string;
3
3
  onClose: () => void;
4
+ onReport?: (src: string) => void;
4
5
  }
5
- export declare function StickerDrawer({ src, onClose }: StickerDrawerProps): import("react").JSX.Element;
6
+ export declare function StickerDrawer({ src, onClose, onReport }: StickerDrawerProps): import("react").JSX.Element;
6
7
  export {};
@@ -1,6 +1,13 @@
1
+ import { StickerFetchParams } from '../lib/comments-client';
2
+ import { StickerItem, StickerPack } from './comment-data';
1
3
  interface StickerSelectorProps {
4
+ packs: StickerPack[];
5
+ stickers: StickerItem[];
6
+ loading?: boolean;
2
7
  onSelect: (stickerUrl: string) => void;
3
8
  onClose: () => void;
9
+ onFetchStickers: (params: StickerFetchParams) => void;
10
+ onToggleFavorite: (id: string) => void;
4
11
  }
5
- export declare function StickerSelector({ onSelect, onClose }: StickerSelectorProps): import("react").JSX.Element;
12
+ export declare function StickerSelector({ packs, stickers, loading, onSelect, onClose, onFetchStickers, onToggleFavorite, }: StickerSelectorProps): import("react").JSX.Element;
6
13
  export {};