@dialtribe/react-sdk 0.1.0-alpha.5 → 0.1.0-alpha.7

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/dist/index.js CHANGED
@@ -32,6 +32,7 @@ function DialTribeProvider({
32
32
  sessionToken: initialToken,
33
33
  onTokenRefresh,
34
34
  onTokenExpired,
35
+ apiBaseUrl,
35
36
  children
36
37
  }) {
37
38
  const [sessionToken, setSessionTokenState] = react.useState(initialToken);
@@ -60,7 +61,8 @@ function DialTribeProvider({
60
61
  sessionToken,
61
62
  setSessionToken,
62
63
  isExpired,
63
- markExpired
64
+ markExpired,
65
+ apiBaseUrl
64
66
  };
65
67
  return /* @__PURE__ */ jsxRuntime.jsx(DialTribeContext.Provider, { value, children });
66
68
  }
@@ -75,18 +77,28 @@ function useDialTribe() {
75
77
  }
76
78
 
77
79
  // src/client/DialTribeClient.ts
78
- var DIALTRIBE_API_BASE = "https://dialtribe.com/api/public/v1";
79
- var ENDPOINTS = {
80
- broadcasts: `${DIALTRIBE_API_BASE}/broadcasts`,
81
- broadcast: (id) => `${DIALTRIBE_API_BASE}/broadcasts/${id}`,
82
- contentPlay: `${DIALTRIBE_API_BASE}/content/play`,
83
- presignedUrl: `${DIALTRIBE_API_BASE}/media/presigned-url`,
84
- sessionStart: `${DIALTRIBE_API_BASE}/session/start`,
85
- sessionPing: `${DIALTRIBE_API_BASE}/session/ping`
86
- };
80
+ function getDefaultApiBaseUrl() {
81
+ if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DIALTRIBE_API_URL) {
82
+ return process.env.NEXT_PUBLIC_DIALTRIBE_API_URL;
83
+ }
84
+ return "https://dialtribe.com/api/public/v1";
85
+ }
86
+ var DIALTRIBE_API_BASE = getDefaultApiBaseUrl();
87
+ function getEndpoints(baseUrl = DIALTRIBE_API_BASE) {
88
+ return {
89
+ broadcasts: `${baseUrl}/broadcasts`,
90
+ broadcast: (id) => `${baseUrl}/broadcasts/${id}`,
91
+ contentPlay: `${baseUrl}/content/play`,
92
+ presignedUrl: `${baseUrl}/media/presigned-url`,
93
+ sessionStart: `${baseUrl}/session/start`,
94
+ sessionPing: `${baseUrl}/session/ping`
95
+ };
96
+ }
97
+ var ENDPOINTS = getEndpoints();
87
98
  var DialTribeClient = class {
88
99
  constructor(config) {
89
100
  this.config = config;
101
+ this.endpoints = config.apiBaseUrl ? getEndpoints(config.apiBaseUrl) : ENDPOINTS;
90
102
  }
91
103
  /**
92
104
  * Make an authenticated request to DialTribe API
@@ -133,7 +145,7 @@ var DialTribeClient = class {
133
145
  if (params?.broadcastStatus) searchParams.set("broadcastStatus", params.broadcastStatus.toString());
134
146
  if (params?.search) searchParams.set("search", params.search);
135
147
  if (params?.includeDeleted) searchParams.set("includeDeleted", "true");
136
- const url = `${ENDPOINTS.broadcasts}${searchParams.toString() ? `?${searchParams}` : ""}`;
148
+ const url = `${this.endpoints.broadcasts}${searchParams.toString() ? `?${searchParams}` : ""}`;
137
149
  const response = await this.fetch(url);
138
150
  if (!response.ok) {
139
151
  throw new Error(`Failed to fetch broadcasts: ${response.status} ${response.statusText}`);
@@ -144,7 +156,7 @@ var DialTribeClient = class {
144
156
  * Get a single broadcast by ID
145
157
  */
146
158
  async getBroadcast(id) {
147
- const response = await this.fetch(ENDPOINTS.broadcast(id));
159
+ const response = await this.fetch(this.endpoints.broadcast(id));
148
160
  if (!response.ok) {
149
161
  if (response.status === 404) {
150
162
  throw new Error("Broadcast not found");
@@ -166,7 +178,7 @@ var DialTribeClient = class {
166
178
  });
167
179
  if (params.hash) searchParams.set("hash", params.hash);
168
180
  if (params.action) searchParams.set("action", params.action);
169
- const url = `${ENDPOINTS.contentPlay}?${searchParams}`;
181
+ const url = `${this.endpoints.contentPlay}?${searchParams}`;
170
182
  const response = await this.fetch(url, {
171
183
  redirect: "manual"
172
184
  // Don't follow redirect, we want the URL
@@ -190,7 +202,7 @@ var DialTribeClient = class {
190
202
  hash: params.hash,
191
203
  fileType: params.fileType
192
204
  });
193
- const url = `${ENDPOINTS.presignedUrl}?${searchParams}`;
205
+ const url = `${this.endpoints.presignedUrl}?${searchParams}`;
194
206
  const response = await this.fetch(url);
195
207
  if (!response.ok) {
196
208
  throw new Error(`Failed to refresh URL: ${response.status} ${response.statusText}`);
@@ -203,7 +215,7 @@ var DialTribeClient = class {
203
215
  * @returns audienceId and optional resumePosition
204
216
  */
205
217
  async startSession(params) {
206
- const response = await this.fetch(ENDPOINTS.sessionStart, {
218
+ const response = await this.fetch(this.endpoints.sessionStart, {
207
219
  method: "POST",
208
220
  body: JSON.stringify(params)
209
221
  });
@@ -222,7 +234,7 @@ var DialTribeClient = class {
222
234
  * - 3: UNMOUNT
223
235
  */
224
236
  async sendSessionPing(params) {
225
- const response = await this.fetch(ENDPOINTS.sessionPing, {
237
+ const response = await this.fetch(this.endpoints.sessionPing, {
226
238
  method: "POST",
227
239
  body: JSON.stringify(params)
228
240
  });
@@ -765,11 +777,12 @@ function BroadcastPlayer({
765
777
  className = "",
766
778
  enableKeyboardShortcuts = false
767
779
  }) {
768
- const { sessionToken, setSessionToken, markExpired } = useDialTribe();
780
+ const { sessionToken, setSessionToken, markExpired, apiBaseUrl } = useDialTribe();
769
781
  const clientRef = react.useRef(null);
770
782
  if (!clientRef.current && sessionToken) {
771
783
  clientRef.current = new DialTribeClient({
772
784
  sessionToken,
785
+ apiBaseUrl,
773
786
  onTokenRefresh: (newToken, expiresAt) => {
774
787
  debug.log(`[DialTribeClient] Token refreshed, expires at ${expiresAt}`);
775
788
  setSessionToken(newToken, expiresAt);
@@ -1428,8 +1441,8 @@ function BroadcastPlayer({
1428
1441
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center p-8", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { variant: "white", text: "Loading..." }) });
1429
1442
  }
1430
1443
  const hasTranscript = broadcast.transcriptStatus === 2 && transcriptData && (transcriptData.segments && transcriptData.segments.some((s) => s.words && s.words.length > 0) || transcriptData.words && transcriptData.words.length > 0);
1431
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `bg-black rounded-lg shadow-2xl w-full h-full flex flex-col ${className}`, children: [
1432
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-zinc-900/50 backdrop-blur-sm border-b border-zinc-800 px-4 md:px-6 py-3 md:py-4 flex justify-between items-center rounded-t-lg shrink-0", children: [
1444
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `bg-black rounded-lg shadow-2xl w-full max-h-full flex flex-col overflow-hidden ${className}`, children: [
1445
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-zinc-900/50 backdrop-blur-sm border-b border-zinc-800 px-3 sm:px-4 md:px-6 py-2 sm:py-3 md:py-4 flex justify-between items-center rounded-t-lg shrink-0", children: [
1433
1446
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1434
1447
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-white", children: broadcast.streamKeyRecord?.foreignName || "Broadcast" }),
1435
1448
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-400", children: [
@@ -1457,8 +1470,8 @@ function BroadcastPlayer({
1457
1470
  }
1458
1471
  ) })
1459
1472
  ] }),
1460
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
1461
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
1473
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row flex-1 min-h-0 overflow-hidden", children: [
1474
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 md:shrink md:flex-1 flex flex-col overflow-hidden", children: [
1462
1475
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${isAudioOnly ? "bg-linear-to-br from-zinc-900 via-zinc-800 to-zinc-900 flex items-stretch" : "bg-black"}`, children: [
1463
1476
  isAudioOnly ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative cursor-pointer w-full flex flex-col", onClick: handleVideoClick, children: !hasError ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full relative", children: [
1464
1477
  /* @__PURE__ */ jsxRuntime.jsx(AudioWaveform, { audioElement, isPlaying: isLiveStream ? true : playing, isLive: isLiveStream }),
@@ -1740,7 +1753,7 @@ function BroadcastPlayer({
1740
1753
  ] })
1741
1754
  ] }))
1742
1755
  ] }),
1743
- showTranscript && hasTranscript && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full md:w-96 bg-zinc-900 border-l border-zinc-800 flex flex-col overflow-hidden", children: [
1756
+ showTranscript && hasTranscript && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 md:flex-none min-h-0 w-full md:w-96 bg-zinc-900 border-t md:border-t-0 border-l border-zinc-800 flex flex-col overflow-hidden", children: [
1744
1757
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-zinc-800 bg-zinc-900/50 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1745
1758
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1746
1759
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-5 h-5 text-green-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }),
@@ -1769,7 +1782,7 @@ function BroadcastPlayer({
1769
1782
  ]
1770
1783
  }
1771
1784
  ) }),
1772
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: transcriptContainerRef, className: "flex-1 overflow-y-auto px-4 py-4 text-gray-300 leading-relaxed", children: isLoadingTranscript ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-6 border-2 border-gray-600 border-t-blue-500 rounded-full animate-spin" }) }) : transcriptData?.segments && transcriptData.segments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: (() => {
1785
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: transcriptContainerRef, className: "flex-1 min-h-0 overflow-y-auto px-4 py-4 text-gray-300 leading-relaxed", children: isLoadingTranscript ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-6 border-2 border-gray-600 border-t-blue-500 rounded-full animate-spin" }) }) : transcriptData?.segments && transcriptData.segments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: (() => {
1773
1786
  const filteredSegments = transcriptData.segments.filter((s) => s.words && s.words.length > 0);
1774
1787
  let globalWordIndex = 0;
1775
1788
  const wordMap = /* @__PURE__ */ new Map();
@@ -1929,14 +1942,12 @@ function BroadcastPlayerModal({
1929
1942
  }) {
1930
1943
  const closeButtonRef = react.useRef(null);
1931
1944
  const previousActiveElement = react.useRef(null);
1932
- if (!isOpen) return null;
1933
1945
  react.useEffect(() => {
1934
- if (isOpen) {
1935
- previousActiveElement.current = document.activeElement;
1936
- setTimeout(() => {
1937
- closeButtonRef.current?.focus();
1938
- }, 100);
1939
- }
1946
+ if (!isOpen) return;
1947
+ previousActiveElement.current = document.activeElement;
1948
+ setTimeout(() => {
1949
+ closeButtonRef.current?.focus();
1950
+ }, 100);
1940
1951
  return () => {
1941
1952
  if (previousActiveElement.current) {
1942
1953
  previousActiveElement.current.focus();
@@ -1944,6 +1955,7 @@ function BroadcastPlayerModal({
1944
1955
  };
1945
1956
  }, [isOpen]);
1946
1957
  react.useEffect(() => {
1958
+ if (!isOpen) return;
1947
1959
  const handleKeyDown = (e) => {
1948
1960
  if (e.key === "Escape") {
1949
1961
  onClose();
@@ -1951,7 +1963,8 @@ function BroadcastPlayerModal({
1951
1963
  };
1952
1964
  document.addEventListener("keydown", handleKeyDown);
1953
1965
  return () => document.removeEventListener("keydown", handleKeyDown);
1954
- }, [onClose]);
1966
+ }, [isOpen, onClose]);
1967
+ if (!isOpen) return null;
1955
1968
  const handleBackdropClick = (e) => {
1956
1969
  if (e.target === e.currentTarget) {
1957
1970
  onClose();
@@ -1960,18 +1973,18 @@ function BroadcastPlayerModal({
1960
1973
  return /* @__PURE__ */ jsxRuntime.jsx(
1961
1974
  "div",
1962
1975
  {
1963
- className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-4",
1976
+ className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-2 sm:p-4",
1964
1977
  onClick: handleBackdropClick,
1965
1978
  role: "dialog",
1966
1979
  "aria-modal": "true",
1967
1980
  "aria-label": "Broadcast player",
1968
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full max-w-7xl max-h-[90vh]", children: [
1981
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-7xl max-h-[95vh] sm:max-h-[90vh] overflow-hidden", children: [
1969
1982
  /* @__PURE__ */ jsxRuntime.jsx(
1970
1983
  "button",
1971
1984
  {
1972
1985
  ref: closeButtonRef,
1973
1986
  onClick: onClose,
1974
- className: "absolute top-4 right-4 z-10 text-gray-400 hover:text-white text-2xl leading-none transition-colors w-8 h-8 flex items-center justify-center bg-black/50 rounded-full",
1987
+ className: "absolute top-2 right-2 sm:top-4 sm:right-4 z-10 text-gray-400 hover:text-white text-2xl leading-none transition-colors w-8 h-8 flex items-center justify-center bg-black/50 rounded-full",
1975
1988
  title: "Close (ESC)",
1976
1989
  "aria-label": "Close player",
1977
1990
  children: "\xD7"