@noverachat/sdk-react 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Novera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.ko.md ADDED
@@ -0,0 +1,99 @@
1
+ # @noverachat/sdk-react
2
+
3
+ > ๐Ÿ‡บ๐Ÿ‡ธ [English](./README.md)
4
+
5
+ NoveraChat์šฉ React ํ›…/๋ฐ”์ธ๋”ฉ โ€” [`@noverachat/sdk-web`](../sdk-web)(ํ—ค๋“œ๋ฆฌ์Šค `NoveraChat` / `Room` ์ฝ”์–ด) ์œ„์˜ ์–‡์€ ๋ ˆ์ด์–ด. ์ฑ„ํŒ… ๋กœ์ง์„ ์žฌ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  ์ฑ„ํŒ… UI๋„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค โ€” ์ด ํŒจํ‚ค์ง€๋Š” ๊ธ€๋ฃจ๋งŒ ๋‹ด๋‹นํ•œ๋‹ค: ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ๋ฐ”์ด๋”์™€, SDK ์ด๋ฒคํŠธ๋ฅผ React ์ƒํƒœ๋กœ ์˜ฎ๊ธฐ๋Š” ํ›…๋“ค.
6
+
7
+ ## ์„ค์น˜
8
+
9
+ ```sh
10
+ npm i @noverachat/sdk-react @noverachat/sdk-web react
11
+ ```
12
+
13
+ `react >= 18` peer dependency (`useSyncExternalStore` ์‚ฌ์šฉ).
14
+
15
+ ## ๋น ๋ฅธ ์‹œ์ž‘
16
+
17
+ ```tsx
18
+ import {
19
+ NoveraChatProvider,
20
+ useMessages,
21
+ useTyping,
22
+ } from "@noverachat/sdk-react";
23
+
24
+ function App() {
25
+ return (
26
+ <NoveraChatProvider
27
+ options={{
28
+ appId: "app_9f8k2x",
29
+ endpoint: "https://chat.example.com",
30
+ tokenProvider: async () => fetchJwt(),
31
+ }}
32
+ >
33
+ <ChatScreen roomId="room_123" />
34
+ </NoveraChatProvider>
35
+ );
36
+ }
37
+
38
+ function ChatScreen({ roomId }: { roomId: string }) {
39
+ const { messages, hasMore, store } = useMessages(roomId);
40
+ const { isAnyoneTyping, setTyping } = useTyping(roomId);
41
+
42
+ return (
43
+ <>
44
+ {hasMore && <button onClick={() => store.loadMore()}>์ด์ „ ๋Œ€ํ™”โ€ฆ</button>}
45
+ {messages.map((m) => (
46
+ <div key={m.id}>
47
+ {m.isDeleted ? "(์‚ญ์ œ๋จ)" : m.content}
48
+ {m.status === "sending" && " โณ"}
49
+ {m.status === "failed" && " โš ๏ธ"}
50
+ </div>
51
+ ))}
52
+ {isAnyoneTyping && <span>์ž…๋ ฅ ์ค‘โ€ฆ</span>}
53
+ <input
54
+ onChange={() => setTyping(true)}
55
+ onKeyDown={(e) => {
56
+ if (e.key !== "Enter") return;
57
+ store.send(e.currentTarget.value);
58
+ e.currentTarget.value = "";
59
+ }}
60
+ />
61
+ </>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## API
67
+
68
+ | Export | ์„ค๋ช… |
69
+ |---|---|
70
+ | `NoveraChatProvider` / `useNoveraChat()` | `NoveraChat`์„ ์ƒ์„ฑํ•˜๊ณ  ๋งˆ์šดํŠธ ๋ผ์ดํ”„์‚ฌ์ดํด์— ๋งž์ถฐ `connect()`/`disconnect()`๋ฅผ ์‹คํ–‰. ํ•˜์œ„ ์–ด๋””์„œ๋“  ํ›…์œผ๋กœ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ฝ๋Š”๋‹ค. options๋Š” **์ตœ์ดˆ 1ํšŒ๋งŒ** ์ฝํžŒ๋‹ค โ€” ๋‹ค๋ฅธ ์˜ต์…˜์œผ๋กœ ์žฌ์ ‘์†ํ•˜๋ ค๋ฉด ์ƒˆ `key`๋กœ ๋ฆฌ๋งˆ์šดํŠธ. |
71
+ | `useRoom(roomId)` | `Room` ํŒŒ์‚ฌ๋“œ. ํ›…์ด ์ปค๋ฒ„ํ•˜์ง€ ์•Š๋Š” SDK ์ง์ ‘ ํ˜ธ์ถœ์šฉ (๋ฉค๋ฒ„, ๊ณต์ง€, ์ดˆ๋Œ€ ๋งํฌ, ๋ชจ๋”๋ ˆ์ด์…˜ ๋“ฑ). |
72
+ | `useMessages(roomId, opts?)` | ๋ผ์ด๋ธŒ ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ(`ChatMessage[]`, ์˜ค๋ž˜๋œ ์ˆœ) + `hasMore` + `readWatermarks`, ๊ทธ๋ฆฌ๊ณ  ์•ก์…˜์šฉ `RoomStore`: `send`, `sendFile`(์—…๋กœ๋“œ ์ง„ํ–‰๋ฅ ), `edit`, `delete`, `toggleReaction`, `markRead`, `loadMore`, `search`, `isReadBy`, `readCount`. ์ „์†ก์€ ๋‚™๊ด€์  โ€” `sending` ๋ฒ„๋ธ”์ด ack์— `sent`๋กœ, ์‹คํŒจ ์‹œ `failed`๋กœ ๋ฐ”๋€๋‹ค. |
73
+ | `useTyping(roomId, opts?)` | ์ž…๋ ฅ ์ค‘์ธ ์‚ฌ์šฉ์ž ๋ชฉ๋ก(`timeoutMs`, ๊ธฐ๋ณธ 5์ดˆ ํ›„ ์ž๋™ ๋งŒ๋ฃŒ) + ๋‚ด ์ž…๋ ฅ ์ƒํƒœ๋ฅผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•˜๋Š” `setTyping`. |
74
+ | `useUnread(opts?)` | ๊ณ„์ • ์ „์ฒด ์•ˆ์ฝ์Œ ๋ฑƒ์ง€: `total`, ๋ฐฉ๋ณ„ `summary`, `refresh()`, ์„ ํƒ์  `refreshIntervalMs` ํด๋ง. |
75
+ | `useRoomList(opts?)` | ๋ผ์ด๋ธŒ ์ฑ„ํŒ…๋ฐฉ ๋ฆฌ์ŠคํŠธ(์ฑ„ํŒ… ํƒญ): ํ”„๋ฆฌ๋ทฐยท์•ˆ์ฝ์Œ ๋ฑƒ์ง€ยท์ตœ๊ทผ ํ™œ๋™์ˆœ ์ •๋ ฌ. ์ƒˆ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฐฉ์„ ๋งจ ์œ„๋กœ ์˜ฌ๋ฆฌ๊ณ , ๋ฉค๋ฒ„์‹ญ ์ด๋ฒคํŠธ๋Š” ๋””๋ฐ”์šด์Šค reload. ์•ก์…˜: `markRead`, `hideRoom`, `unhideRoom`. |
76
+ | `useMemberList(roomId)` | ๋ผ์ด๋ธŒ ๋ฉค๋ฒ„ ๋ชฉ๋ก โ€” ์—ญํ• , `operators`, `activeMemberIds`(์ฝ์Œ ์นด์šดํŠธ ๊ณ„์‚ฐ์šฉ), presence ์ ์€ in-place ๊ฐฑ์‹ . |
77
+ | `useRoomFiles(roomId, opts?)` | ํŒŒ์ผํ•จยท์•จ๋ฒ” ํŽ˜์ด์ง€๋„ค์ด์…˜(์ตœ์‹ ์ˆœ): `items`, `media`(์ด๋ฏธ์ง€/์˜์ƒ๋งŒ), `hasMore`, `store.loadMore()` / `store.refresh()`. |
78
+ | `useRoomSettings(roomId)` | ๋ฐฉ ์„ค์ • ์•ก์…˜: `setMuted` / `setPushTrigger`, ์ดˆ๋Œ€ ๋งํฌ CRUD, ์ฐธ์—ฌ ์š”์ฒญ ์ˆ˜์‹ ํ•จ(์Šน์ธ/๊ฑฐ์ ˆ), `leave` / `clearHistory` / `deleteMyMessages`, ์˜คํผ๋ ˆ์ดํ„ฐ ๋ชจ๋”๋ ˆ์ด์…˜(`freeze`, `setPublic`, `muteMember` ๋“ฑ). |
79
+ | `ChatMessage` | ํžˆ์Šคํ† ๋ฆฌ(REST)ยท๋ผ์ด๋ธŒ(WS)ยท๋‚™๊ด€์  ๋ฉ”์‹œ์ง€๋ฅผ ํ•˜๋‚˜๋กœ ํ†ต์ผํ•œ ๋ทฐ๋ชจ๋ธ. |
80
+ | `RoomStore` | `useMessages` ๋’ค์˜ ํ”„๋ ˆ์ž„์›Œํฌ ๋ฌด๊ด€ store. ํ›… ๋ฐ–์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ (ํ…Œ์ŠคํŠธ, ๋น„-React ๊ธ€๋ฃจ). |
81
+
82
+ `@noverachat/sdk-web`์˜ ๋ชจ๋“  export๋ฅผ ์žฌ๋…ธ์ถœํ•˜๋ฏ€๋กœ `NoveraChat`, `Room`, ๋ฉ”์‹œ์ง€/์ด๋ฒคํŠธ ํƒ€์ž… ๋“ฑ์„ ํ•œ ๋ฒˆ์˜ import๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค.
83
+
84
+ ## ๋ฌธ์„œ
85
+
86
+ React ํŠน์œ ์˜ ๊ฐ€์ด๋“œ๋Š” [NoveraChat ๋ฌธ์„œ ์‚ฌ์ดํŠธ](https://dev-chat-docs.novera.town/ko/react/getting-started/build-a-chat-screen)์— ๋ฐœํ–‰๋œ๋‹ค (์›๋ณธ์€ `docs/`) โ€” [์ฑ„ํŒ… ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ](https://dev-chat-docs.novera.town/ko/react/getting-started/build-a-chat-screen)๋ถ€ํ„ฐ.
87
+
88
+ | ์„น์…˜ | ๋‚ด์šฉ |
89
+ |---|---|
90
+ | [์‹œ์ž‘ํ•˜๊ธฐ](https://dev-chat-docs.novera.town/ko/react/getting-started/build-a-chat-screen) | ์„ค์น˜ + ์ฑ„ํŒ… ํ™”๋ฉด ์กฐ๋ฆฝ |
91
+ | [๊ฐ€์ด๋“œ](https://dev-chat-docs.novera.town/ko/react/guides/state-and-lifecycle) | ์ƒํƒœ์™€ ๋ผ์ดํ”„์‚ฌ์ดํด ยท ์ฑ„ํŒ…๋ฐฉ ๋ฆฌ์ŠคํŠธ ยท ์ฝ์Œ ํ‘œ์‹œ ยท ํŒŒ์ผ๊ณผ ๋ฏธ๋””์–ด ยท ๋ฐฉ ์„ค์ • ํ™”๋ฉด |
92
+ | [๋ ˆํผ๋Ÿฐ์Šค](https://dev-chat-docs.novera.town/ko/react/reference/hooks) | ํ›…ยทstore API |
93
+
94
+ ์ฑ„ํŒ… **๋™์ž‘**(์ธ์ฆยท์—ฐ๊ฒฐยท๋ฉ”์‹œ์ง•ยท๋ฃธยทํ‘ธ์‹œยท์—๋Ÿฌ)์€ [`@noverachat/sdk-web`](../sdk-web/README.ko.md)์— ๋ฌธ์„œํ™”๋ผ ์žˆ๊ณ , ์—ฌ๊ธฐ์„œ ์ค‘๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.
95
+
96
+ ## ์ฐธ๊ณ 
97
+
98
+ - ์ฝ์Œ ์›Œํ„ฐ๋งˆํฌ(`markRead`)๋Š” ์ฝ”์–ด SDK ์•ˆ์—์„œ ๋””๋ฐ”์šด์Šค๋œ๋‹ค. store๊ฐ€ ํƒญ ์ˆจ๊น€/๋‹ซํž˜๊ณผ ์–ธ๋งˆ์šดํŠธ ์‹œ ์ž๋™์œผ๋กœ flushํ•˜๋ฏ€๋กœ ์•ˆ์ฝ์Œ ์นด์šดํŠธ๊ฐ€ ๊ธฐ๊ธฐ ๊ฐ„์— ์ •ํ™•ํ•˜๊ฒŒ ์œ ์ง€๋œ๋‹ค.
99
+ - StrictMode ์•ˆ์ „: ์‹œ๋ฎฌ๋ ˆ์ด์…˜๋œ ์ด์ค‘ ๋งˆ์šดํŠธ์—์„œ ๊ตฌ๋…์ด ๊นจ๋—ํ•˜๊ฒŒ attach/detach๋˜๊ณ , ์ดˆ๊ธฐ ํžˆ์Šคํ† ๋ฆฌ ๋กœ๋“œ๋Š” ์ค‘๋ณต ๊ฐ€๋“œ๊ฐ€ ์žˆ๋‹ค.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # @noverachat/sdk-react
2
+
3
+ > ๐Ÿ‡ฐ๐Ÿ‡ท [ํ•œ๊ตญ์–ด](./README.ko.md)
4
+
5
+ React hooks/bindings for NoveraChat โ€” a thin layer over [`@noverachat/sdk-web`](../sdk-web) (the headless `NoveraChat` / `Room` core). It does NOT reimplement chat logic and it does NOT ship chat UI โ€” this package is only the glue: a lifecycle-managed provider and hooks that turn the SDK's events into React state.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm i @noverachat/sdk-react @noverachat/sdk-web react
11
+ ```
12
+
13
+ `react >= 18` is a peer dependency (uses `useSyncExternalStore`).
14
+
15
+ ## Quick start
16
+
17
+ ```tsx
18
+ import {
19
+ NoveraChatProvider,
20
+ useMessages,
21
+ useTyping,
22
+ } from "@noverachat/sdk-react";
23
+
24
+ function App() {
25
+ return (
26
+ <NoveraChatProvider
27
+ options={{
28
+ appId: "app_9f8k2x",
29
+ endpoint: "https://chat.example.com",
30
+ tokenProvider: async () => fetchJwt(),
31
+ }}
32
+ >
33
+ <ChatScreen roomId="room_123" />
34
+ </NoveraChatProvider>
35
+ );
36
+ }
37
+
38
+ function ChatScreen({ roomId }: { roomId: string }) {
39
+ const { messages, hasMore, store } = useMessages(roomId);
40
+ const { isAnyoneTyping, setTyping } = useTyping(roomId);
41
+
42
+ return (
43
+ <>
44
+ {hasMore && <button onClick={() => store.loadMore()}>olderโ€ฆ</button>}
45
+ {messages.map((m) => (
46
+ <div key={m.id}>
47
+ {m.isDeleted ? "(deleted)" : m.content}
48
+ {m.status === "sending" && " โณ"}
49
+ {m.status === "failed" && " โš ๏ธ"}
50
+ </div>
51
+ ))}
52
+ {isAnyoneTyping && <span>typingโ€ฆ</span>}
53
+ <input
54
+ onChange={() => setTyping(true)}
55
+ onKeyDown={(e) => {
56
+ if (e.key !== "Enter") return;
57
+ store.send(e.currentTarget.value);
58
+ e.currentTarget.value = "";
59
+ }}
60
+ />
61
+ </>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## API
67
+
68
+ | Export | What it is |
69
+ |---|---|
70
+ | `NoveraChatProvider` / `useNoveraChat()` | Creates a `NoveraChat`, runs `connect()`/`disconnect()` over the mount lifecycle; the hook reads it anywhere below. Options are read **once** โ€” remount with a new `key` to reconnect differently. |
71
+ | `useRoom(roomId)` | The `Room` facade, for direct SDK calls the hooks don't cover (members, announcements, invites, moderation, โ€ฆ). |
72
+ | `useMessages(roomId, opts?)` | Live message list (`ChatMessage[]`, oldest first) + `hasMore` + `readWatermarks`, plus the backing `RoomStore` for actions: `send`, `sendFile` (with upload progress), `edit`, `delete`, `toggleReaction`, `markRead`, `loadMore`, `search`, `isReadBy`, `readCount`. Sends are optimistic โ€” a `sending` bubble flips to `sent` on ack or `failed` on error. |
73
+ | `useTyping(roomId, opts?)` | Who's typing (auto-expires after `timeoutMs`, default 5s) + `setTyping` to broadcast your own state. |
74
+ | `useUnread(opts?)` | Account-wide unread badge: `total`, per-room `summary`, `refresh()`, optional `refreshIntervalMs` polling. |
75
+ | `useRoomList(opts?)` | Live chat-room list (์ฑ„ํŒ… ํƒญ): previews, unread badges, most-recent-first ordering. New messages bump rooms to the top; membership events trigger a debounced reload. Actions: `markRead`, `hideRoom`, `unhideRoom`. |
76
+ | `useMemberList(roomId)` | Live member list โ€” roles, `operators`, `activeMemberIds` (for read-receipt math), presence dots updated in place. |
77
+ | `useRoomFiles(roomId, opts?)` | Paginated media/file grid (ํŒŒ์ผํ•จยท์•จ๋ฒ”), newest first: `items`, `media` (images/videos only), `hasMore`, `store.loadMore()` / `store.refresh()`. |
78
+ | `useRoomSettings(roomId)` | Room-settings actions: `setMuted` / `setPushTrigger`, invite-link CRUD, join-request inbox (approve/reject), `leave` / `clearHistory` / `deleteMyMessages`, operator moderation (`freeze`, `setPublic`, `muteMember`, โ€ฆ). |
79
+ | `ChatMessage` | Uniform view model over history (REST), live (WS) and optimistic messages. |
80
+ | `RoomStore` | The framework-agnostic store behind `useMessages`, usable outside hooks (tests, non-React glue). |
81
+
82
+ Everything from `@noverachat/sdk-web` is re-exported, so a single import gets you `NoveraChat`, `Room`, message/event types, etc.
83
+
84
+ ## Documentation
85
+
86
+ React-specific guides are on the [NoveraChat docs site](https://dev-chat-docs.novera.town/react/getting-started/build-a-chat-screen) (source under `docs/`) โ€” start with [build a chat screen](https://dev-chat-docs.novera.town/react/getting-started/build-a-chat-screen).
87
+
88
+ | Section | Contents |
89
+ |---|---|
90
+ | [Getting started](https://dev-chat-docs.novera.town/react/getting-started/build-a-chat-screen) | Install + wire a chat screen |
91
+ | [Guides](https://dev-chat-docs.novera.town/react/guides/state-and-lifecycle) | State & lifecycle ยท chat-room list ยท read receipts ยท files & media ยท room settings screen |
92
+ | [Reference](https://dev-chat-docs.novera.town/react/reference/hooks) | Hooks & stores API |
93
+
94
+ Chat **behavior** โ€” auth, connection, messaging, rooms, push, errors โ€” is documented in [`@noverachat/sdk-web`](../sdk-web/README.md), not duplicated here.
95
+
96
+ ## Notes
97
+
98
+ - The read watermark (`markRead`) is debounced inside the core SDK; the store flushes it automatically when the tab is hidden/closed and on unmount, so unread counts stay correct across devices.
99
+ - StrictMode-safe: subscriptions attach/detach cleanly across the simulated double mount, and the initial history load is guarded against duplication.