tiktok-comments 0.2.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 +47 -30
- package/dist/components/badge-drawer.d.ts +2 -1
- package/dist/components/comment-data.d.ts +29 -0
- package/dist/components/comment-input-bar.d.ts +17 -3
- package/dist/components/comment-item.d.ts +10 -3
- package/dist/components/comment-section.d.ts +27 -9
- package/dist/components/image-picker.d.ts +6 -0
- package/dist/components/mention-selector.d.ts +6 -2
- package/dist/components/profile-drawer.d.ts +4 -6
- package/dist/components/reply-item.d.ts +9 -3
- package/dist/components/sticker-drawer.d.ts +2 -1
- package/dist/components/sticker-selector.d.ts +8 -1
- package/dist/index.cjs +1682 -1682
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +12881 -12227
- package/dist/lib/comments-client.d.ts +52 -0
- package/dist/lib/format-comment-time.d.ts +7 -0
- package/dist/lib/mention-parser.d.ts +4 -5
- package/dist/lib/use-body-scroll-lock.d.ts +2 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -11,28 +11,15 @@ npm install tiktok-comments
|
|
|
11
11
|
## Usage (React SPA)
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
|
-
import {
|
|
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
|
-
|
|
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
|
|
42
|
+
return <CommentSection url="/api/comments" response={(raw) => raw as CommentApiPayload} />;
|
|
56
43
|
}
|
|
57
44
|
```
|
|
58
45
|
|
|
59
46
|
## API
|
|
60
47
|
|
|
61
|
-
`CommentSection`
|
|
48
|
+
`CommentSection` fetches from `url`. Map your API JSON with `response`.
|
|
62
49
|
|
|
63
50
|
| Prop | Type | Description |
|
|
64
51
|
|------|------|-------------|
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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({
|
|
31
|
+
export declare function CommentSection({ url, response, endpoints: endpointsConfig, className, theme, showThemeToggle, onThemeChange, onViewProfile, onReportSticker, }: CommentSectionProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { CommentProfile } from './comment-data';
|
|
1
2
|
interface MentionSelectorProps {
|
|
2
|
-
|
|
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 {
|
|
1
|
+
import { CommentProfile } from './comment-data';
|
|
2
2
|
interface ProfileDrawerProps {
|
|
3
|
-
|
|
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({
|
|
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
|
-
|
|
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 {};
|