@coxwave/tap-sdk 0.0.1

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/index.ts ADDED
@@ -0,0 +1,238 @@
1
+ import ChatBodyMaker from "./chatBody";
2
+ import FloatingButtonMaker from "./floatingButton";
3
+ import { TapIframeBridge } from "./services/tap-iframe-bridge";
4
+ import { EventManager } from "./services/event-manager";
5
+ import showPopupWithHtml from "./popUpImage";
6
+
7
+ import InitGA from "./ga";
8
+
9
+ import {
10
+ AlarmMessageInstanceType,
11
+ ChatApiParamsType,
12
+ TapSDKType,
13
+ CustomStylesType,
14
+ ShortcutKeyType,
15
+ SeekTimelineParamsType,
16
+ } from "./types";
17
+
18
+ class TapSDK {
19
+ private pluginKey: string;
20
+ private iframeBridge: TapIframeBridge;
21
+ public events: EventManager;
22
+
23
+ private chatBody: HTMLElement | null = null;
24
+ private chatBodyMaker: ChatBodyMaker;
25
+ private floatingButtonMaker: FloatingButtonMaker;
26
+ private shortcutKey: ShortcutKeyType = {
27
+ openChat: { key: "/", modifier: "" },
28
+ sendChat: { key: "Enter", modifier: "" },
29
+ };
30
+
31
+ private isProd: boolean;
32
+ private isLocal: boolean;
33
+
34
+ private isOpen: boolean;
35
+ private isPdfOpen: boolean;
36
+
37
+ constructor({ pluginKey, isProd = false, isLocal = false }: TapSDKType) {
38
+ // protocol
39
+ this.pluginKey = pluginKey;
40
+ this.iframeBridge = new TapIframeBridge({
41
+ hostClientUrl: window.location.origin,
42
+ pluginKey: this.pluginKey,
43
+ });
44
+
45
+ // events
46
+ this.events = new EventManager(this.iframeBridge);
47
+
48
+ // render
49
+ this.floatingButtonMaker = new FloatingButtonMaker();
50
+ this.chatBodyMaker = new ChatBodyMaker();
51
+
52
+ // ga log
53
+ this.isProd = isProd;
54
+ this.isLocal = isLocal;
55
+
56
+ // state
57
+ this.isOpen = false;
58
+ this.isPdfOpen = false;
59
+ }
60
+
61
+ #isClient() {
62
+ return typeof window !== "undefined" && typeof document !== "undefined";
63
+ }
64
+
65
+ private setIsOpen(isOpen: boolean) {
66
+ this.isOpen = isOpen;
67
+ }
68
+
69
+ private setIsPdfOpen(isPdfOpen: boolean) {
70
+ this.isPdfOpen = isPdfOpen;
71
+ }
72
+
73
+ seekTimeline({ clipId, clipPlayHead }: SeekTimelineParamsType) {
74
+ this.iframeBridge.postToTap({
75
+ type: "timeline:seek",
76
+ clipId,
77
+ clipPlayHead,
78
+ });
79
+ }
80
+
81
+ async initChat({
82
+ chatApiParams,
83
+ customStyles,
84
+ shortcutKey,
85
+ }: {
86
+ chatApiParams: ChatApiParamsType;
87
+ customStyles?: CustomStylesType;
88
+ shortcutKey?: ShortcutKeyType;
89
+ }) {
90
+ if (!this.#isClient()) throw new Error("not client");
91
+
92
+ this.shortcutKey = {
93
+ openChat: shortcutKey?.openChat ?? this.shortcutKey.openChat,
94
+ sendChat: shortcutKey?.sendChat ?? this.shortcutKey.sendChat,
95
+ };
96
+
97
+ // chat body render
98
+ if (!this.chatBody) {
99
+ this.chatBody = this.chatBodyMaker.createChatBody({
100
+ ...(customStyles?.chatBody && {
101
+ customChatBody: customStyles.chatBody,
102
+ }),
103
+ });
104
+ }
105
+
106
+ // chat iframe
107
+ if (!this.iframeBridge.hasIframe()) {
108
+ await this.iframeBridge.renderIframe({
109
+ chatBody: this.chatBody,
110
+ isProd: this.isProd,
111
+ isLocal: this.isLocal,
112
+ });
113
+ // Handshake will be performed after iframe load
114
+ }
115
+ try {
116
+ const initiatedMessage = await this.iframeBridge.performHandshake(
117
+ {
118
+ chatApiParams,
119
+ customStyles,
120
+ },
121
+ {
122
+ retryInterval: 500,
123
+ maxRetries: 10,
124
+ timeout: 10000,
125
+ }
126
+ );
127
+
128
+ // Initialize GA with the received gaId
129
+ new InitGA({ gaId: initiatedMessage.gaId });
130
+ } catch (error) {
131
+ console.error("Handshake failed:", error);
132
+ throw new Error(
133
+ `Chat initialization failed: ${error instanceof Error ? error.message : "Unknown error"}`
134
+ );
135
+ }
136
+
137
+ // floating button render
138
+ this.floatingButtonMaker.render({
139
+ ...(customStyles?.floatingButton && {
140
+ customFloatingButton: customStyles.floatingButton,
141
+ }),
142
+ });
143
+ this.floatingButtonMaker.addClickEvent({
144
+ callback: () => {
145
+ this.iframeBridge.postToTap({
146
+ type: this.isOpen ? "chat:close" : "chat:open",
147
+ });
148
+ },
149
+ });
150
+
151
+ this.floatingButtonMaker.addAlarmClickEvent({
152
+ callback: (messageInfo: AlarmMessageInstanceType) => {
153
+ this.iframeBridge.postToTap({ type: "chat:open", isAlarm: true });
154
+ this.iframeBridge.postToTap({ type: "alarm:click", messageInfo });
155
+ },
156
+ });
157
+
158
+ this.iframeBridge.listenToTap("chat:opened", () => {
159
+ this.setIsOpen(true);
160
+ this.chatBodyMaker.toggleVisibility(this.isOpen);
161
+ this.floatingButtonMaker.alarmRemove();
162
+ });
163
+ this.iframeBridge.listenToTap("chat:closed", () => {
164
+ this.setIsOpen(false);
165
+ this.chatBodyMaker.toggleVisibility(this.isOpen);
166
+ });
167
+
168
+ // alarm
169
+ this.iframeBridge.listenToTap("alarm:fadeIn", (data) => {
170
+ if (this.isOpen) return;
171
+ this.floatingButtonMaker.alarmFadeIn(data.messageInfo);
172
+ });
173
+
174
+ // popUp
175
+ this.iframeBridge.listenToTap("popUp:open", (data) => {
176
+ showPopupWithHtml({
177
+ htmlString: data.popUpInfo.html,
178
+ callback: () => this.iframeBridge.postToTap({ type: "popUp:close" }),
179
+ ...(customStyles?.chatBody && { customStyles: customStyles.chatBody }),
180
+ });
181
+ });
182
+
183
+ // pdf Open
184
+ this.iframeBridge.listenToTap("pdf:open", () => {
185
+ this.setIsPdfOpen(true);
186
+ this.chatBodyMaker.resizeChatBody(this.isPdfOpen, customStyles?.chatBody);
187
+ this.iframeBridge.postToTap({ type: "pdf:enlarged" });
188
+ });
189
+ this.iframeBridge.listenToTap("pdf:close", () => {
190
+ this.setIsPdfOpen(false);
191
+ this.chatBodyMaker.resizeChatBody(this.isPdfOpen, customStyles?.chatBody);
192
+ this.iframeBridge.postToTap({ type: "pdf:shrinked" });
193
+ });
194
+
195
+ // if dev, attach alaram:fadeIn event
196
+ if (this.isLocal) {
197
+ this.iframeBridge.listenToTap("alarm:fadeIn", (data) => {
198
+ this.floatingButtonMaker.alarmFadeIn(data.messageInfo);
199
+ });
200
+ }
201
+ }
202
+
203
+ removeChat() {
204
+ if (!this.chatBody) throw new Error("chatBody is not initialized");
205
+
206
+ this.chatBodyMaker.removeChatBody();
207
+ this.floatingButtonMaker.alarmRemove();
208
+ this.iframeBridge.removeIframe();
209
+ this.chatBody = null;
210
+ this.isOpen = false;
211
+ }
212
+
213
+ /**
214
+ * @deprecated use `seekTimeline` method. gotta be expired at v1.0.0
215
+ */
216
+ postChatInfo({
217
+ clipId,
218
+ clipPlayHead,
219
+ }: {
220
+ clipId: string;
221
+ clipPlayHead: number;
222
+ }) {
223
+ this.seekTimeline({ clipId, clipPlayHead });
224
+ }
225
+
226
+ /**
227
+ * @deprecated use `events.onTimelineSeek` method. gotta be expired at v1.0.0
228
+ */
229
+ getTimelineInfo({
230
+ callback,
231
+ }: {
232
+ callback: (clipPlayHead: number, clipId: string) => void;
233
+ }) {
234
+ this.events.onTimelineSeek(callback);
235
+ }
236
+ }
237
+
238
+ export default TapSDK;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@coxwave/tap-sdk",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "A vanilla JS SDK",
6
+ "main": "index.ts",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "build:analyze": "tsup --metafile",
17
+ "dev": "tsup --watch",
18
+ "prepare": "pnpm build",
19
+ "lint": "eslint .",
20
+ "publish:npm": "./scripts/publish.sh"
21
+ },
22
+ "keywords": [
23
+ "sdk",
24
+ "vanilla",
25
+ "javascript"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "dependencies": {
33
+ "@coxwave/tap-messages": "workspace:*"
34
+ },
35
+ "devDependencies": {
36
+ "@coxwave/config-eslint": "workspace:*",
37
+ "@coxwave/config-typescript": "workspace:*",
38
+ "@vanilla-extract/css": "^1.17.4",
39
+ "@vanilla-extract/esbuild-plugin": "^2.3.18",
40
+ "postcss": "^8.5.6",
41
+ "tsup": "^8.5.0"
42
+ }
43
+ }