@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.mjs CHANGED
@@ -26,6 +26,7 @@ function DialTribeProvider({
26
26
  sessionToken: initialToken,
27
27
  onTokenRefresh,
28
28
  onTokenExpired,
29
+ apiBaseUrl,
29
30
  children
30
31
  }) {
31
32
  const [sessionToken, setSessionTokenState] = useState(initialToken);
@@ -54,7 +55,8 @@ function DialTribeProvider({
54
55
  sessionToken,
55
56
  setSessionToken,
56
57
  isExpired,
57
- markExpired
58
+ markExpired,
59
+ apiBaseUrl
58
60
  };
59
61
  return /* @__PURE__ */ jsx(DialTribeContext.Provider, { value, children });
60
62
  }
@@ -69,18 +71,28 @@ function useDialTribe() {
69
71
  }
70
72
 
71
73
  // src/client/DialTribeClient.ts
72
- var DIALTRIBE_API_BASE = "https://dialtribe.com/api/public/v1";
73
- var ENDPOINTS = {
74
- broadcasts: `${DIALTRIBE_API_BASE}/broadcasts`,
75
- broadcast: (id) => `${DIALTRIBE_API_BASE}/broadcasts/${id}`,
76
- contentPlay: `${DIALTRIBE_API_BASE}/content/play`,
77
- presignedUrl: `${DIALTRIBE_API_BASE}/media/presigned-url`,
78
- sessionStart: `${DIALTRIBE_API_BASE}/session/start`,
79
- sessionPing: `${DIALTRIBE_API_BASE}/session/ping`
80
- };
74
+ function getDefaultApiBaseUrl() {
75
+ if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DIALTRIBE_API_URL) {
76
+ return process.env.NEXT_PUBLIC_DIALTRIBE_API_URL;
77
+ }
78
+ return "https://dialtribe.com/api/public/v1";
79
+ }
80
+ var DIALTRIBE_API_BASE = getDefaultApiBaseUrl();
81
+ function getEndpoints(baseUrl = DIALTRIBE_API_BASE) {
82
+ return {
83
+ broadcasts: `${baseUrl}/broadcasts`,
84
+ broadcast: (id) => `${baseUrl}/broadcasts/${id}`,
85
+ contentPlay: `${baseUrl}/content/play`,
86
+ presignedUrl: `${baseUrl}/media/presigned-url`,
87
+ sessionStart: `${baseUrl}/session/start`,
88
+ sessionPing: `${baseUrl}/session/ping`
89
+ };
90
+ }
91
+ var ENDPOINTS = getEndpoints();
81
92
  var DialTribeClient = class {
82
93
  constructor(config) {
83
94
  this.config = config;
95
+ this.endpoints = config.apiBaseUrl ? getEndpoints(config.apiBaseUrl) : ENDPOINTS;
84
96
  }
85
97
  /**
86
98
  * Make an authenticated request to DialTribe API
@@ -127,7 +139,7 @@ var DialTribeClient = class {
127
139
  if (params?.broadcastStatus) searchParams.set("broadcastStatus", params.broadcastStatus.toString());
128
140
  if (params?.search) searchParams.set("search", params.search);
129
141
  if (params?.includeDeleted) searchParams.set("includeDeleted", "true");
130
- const url = `${ENDPOINTS.broadcasts}${searchParams.toString() ? `?${searchParams}` : ""}`;
142
+ const url = `${this.endpoints.broadcasts}${searchParams.toString() ? `?${searchParams}` : ""}`;
131
143
  const response = await this.fetch(url);
132
144
  if (!response.ok) {
133
145
  throw new Error(`Failed to fetch broadcasts: ${response.status} ${response.statusText}`);
@@ -138,7 +150,7 @@ var DialTribeClient = class {
138
150
  * Get a single broadcast by ID
139
151
  */
140
152
  async getBroadcast(id) {
141
- const response = await this.fetch(ENDPOINTS.broadcast(id));
153
+ const response = await this.fetch(this.endpoints.broadcast(id));
142
154
  if (!response.ok) {
143
155
  if (response.status === 404) {
144
156
  throw new Error("Broadcast not found");
@@ -160,7 +172,7 @@ var DialTribeClient = class {
160
172
  });
161
173
  if (params.hash) searchParams.set("hash", params.hash);
162
174
  if (params.action) searchParams.set("action", params.action);
163
- const url = `${ENDPOINTS.contentPlay}?${searchParams}`;
175
+ const url = `${this.endpoints.contentPlay}?${searchParams}`;
164
176
  const response = await this.fetch(url, {
165
177
  redirect: "manual"
166
178
  // Don't follow redirect, we want the URL
@@ -184,7 +196,7 @@ var DialTribeClient = class {
184
196
  hash: params.hash,
185
197
  fileType: params.fileType
186
198
  });
187
- const url = `${ENDPOINTS.presignedUrl}?${searchParams}`;
199
+ const url = `${this.endpoints.presignedUrl}?${searchParams}`;
188
200
  const response = await this.fetch(url);
189
201
  if (!response.ok) {
190
202
  throw new Error(`Failed to refresh URL: ${response.status} ${response.statusText}`);
@@ -197,7 +209,7 @@ var DialTribeClient = class {
197
209
  * @returns audienceId and optional resumePosition
198
210
  */
199
211
  async startSession(params) {
200
- const response = await this.fetch(ENDPOINTS.sessionStart, {
212
+ const response = await this.fetch(this.endpoints.sessionStart, {
201
213
  method: "POST",
202
214
  body: JSON.stringify(params)
203
215
  });
@@ -216,7 +228,7 @@ var DialTribeClient = class {
216
228
  * - 3: UNMOUNT
217
229
  */
218
230
  async sendSessionPing(params) {
219
- const response = await this.fetch(ENDPOINTS.sessionPing, {
231
+ const response = await this.fetch(this.endpoints.sessionPing, {
220
232
  method: "POST",
221
233
  body: JSON.stringify(params)
222
234
  });
@@ -759,11 +771,12 @@ function BroadcastPlayer({
759
771
  className = "",
760
772
  enableKeyboardShortcuts = false
761
773
  }) {
762
- const { sessionToken, setSessionToken, markExpired } = useDialTribe();
774
+ const { sessionToken, setSessionToken, markExpired, apiBaseUrl } = useDialTribe();
763
775
  const clientRef = useRef(null);
764
776
  if (!clientRef.current && sessionToken) {
765
777
  clientRef.current = new DialTribeClient({
766
778
  sessionToken,
779
+ apiBaseUrl,
767
780
  onTokenRefresh: (newToken, expiresAt) => {
768
781
  debug.log(`[DialTribeClient] Token refreshed, expires at ${expiresAt}`);
769
782
  setSessionToken(newToken, expiresAt);
@@ -1422,8 +1435,8 @@ function BroadcastPlayer({
1422
1435
  return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-8", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white", text: "Loading..." }) });
1423
1436
  }
1424
1437
  const hasTranscript = broadcast.transcriptStatus === 2 && transcriptData && (transcriptData.segments && transcriptData.segments.some((s) => s.words && s.words.length > 0) || transcriptData.words && transcriptData.words.length > 0);
1425
- return /* @__PURE__ */ jsxs("div", { className: `bg-black rounded-lg shadow-2xl w-full h-full flex flex-col ${className}`, children: [
1426
- /* @__PURE__ */ 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: [
1438
+ return /* @__PURE__ */ jsxs("div", { className: `bg-black rounded-lg shadow-2xl w-full max-h-full flex flex-col overflow-hidden ${className}`, children: [
1439
+ /* @__PURE__ */ 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: [
1427
1440
  /* @__PURE__ */ jsxs("div", { children: [
1428
1441
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-white", children: broadcast.streamKeyRecord?.foreignName || "Broadcast" }),
1429
1442
  /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-400", children: [
@@ -1451,8 +1464,8 @@ function BroadcastPlayer({
1451
1464
  }
1452
1465
  ) })
1453
1466
  ] }),
1454
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
1455
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
1467
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row flex-1 min-h-0 overflow-hidden", children: [
1468
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 md:shrink md:flex-1 flex flex-col overflow-hidden", children: [
1456
1469
  /* @__PURE__ */ jsxs("div", { className: `relative ${isAudioOnly ? "bg-linear-to-br from-zinc-900 via-zinc-800 to-zinc-900 flex items-stretch" : "bg-black"}`, children: [
1457
1470
  isAudioOnly ? /* @__PURE__ */ jsx("div", { className: "relative cursor-pointer w-full flex flex-col", onClick: handleVideoClick, children: !hasError ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "w-full h-full relative", children: [
1458
1471
  /* @__PURE__ */ jsx(AudioWaveform, { audioElement, isPlaying: isLiveStream ? true : playing, isLive: isLiveStream }),
@@ -1734,7 +1747,7 @@ function BroadcastPlayer({
1734
1747
  ] })
1735
1748
  ] }))
1736
1749
  ] }),
1737
- showTranscript && hasTranscript && /* @__PURE__ */ jsxs("div", { className: "w-full md:w-96 bg-zinc-900 border-l border-zinc-800 flex flex-col overflow-hidden", children: [
1750
+ showTranscript && hasTranscript && /* @__PURE__ */ 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: [
1738
1751
  /* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-zinc-800 bg-zinc-900/50 shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1739
1752
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1740
1753
  /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-green-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ 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" }) }),
@@ -1763,7 +1776,7 @@ function BroadcastPlayer({
1763
1776
  ]
1764
1777
  }
1765
1778
  ) }),
1766
- /* @__PURE__ */ jsx("div", { ref: transcriptContainerRef, className: "flex-1 overflow-y-auto px-4 py-4 text-gray-300 leading-relaxed", children: isLoadingTranscript ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ 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__ */ jsx("div", { className: "space-y-4", children: (() => {
1779
+ /* @__PURE__ */ 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__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ 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__ */ jsx("div", { className: "space-y-4", children: (() => {
1767
1780
  const filteredSegments = transcriptData.segments.filter((s) => s.words && s.words.length > 0);
1768
1781
  let globalWordIndex = 0;
1769
1782
  const wordMap = /* @__PURE__ */ new Map();
@@ -1923,14 +1936,12 @@ function BroadcastPlayerModal({
1923
1936
  }) {
1924
1937
  const closeButtonRef = useRef(null);
1925
1938
  const previousActiveElement = useRef(null);
1926
- if (!isOpen) return null;
1927
1939
  useEffect(() => {
1928
- if (isOpen) {
1929
- previousActiveElement.current = document.activeElement;
1930
- setTimeout(() => {
1931
- closeButtonRef.current?.focus();
1932
- }, 100);
1933
- }
1940
+ if (!isOpen) return;
1941
+ previousActiveElement.current = document.activeElement;
1942
+ setTimeout(() => {
1943
+ closeButtonRef.current?.focus();
1944
+ }, 100);
1934
1945
  return () => {
1935
1946
  if (previousActiveElement.current) {
1936
1947
  previousActiveElement.current.focus();
@@ -1938,6 +1949,7 @@ function BroadcastPlayerModal({
1938
1949
  };
1939
1950
  }, [isOpen]);
1940
1951
  useEffect(() => {
1952
+ if (!isOpen) return;
1941
1953
  const handleKeyDown = (e) => {
1942
1954
  if (e.key === "Escape") {
1943
1955
  onClose();
@@ -1945,7 +1957,8 @@ function BroadcastPlayerModal({
1945
1957
  };
1946
1958
  document.addEventListener("keydown", handleKeyDown);
1947
1959
  return () => document.removeEventListener("keydown", handleKeyDown);
1948
- }, [onClose]);
1960
+ }, [isOpen, onClose]);
1961
+ if (!isOpen) return null;
1949
1962
  const handleBackdropClick = (e) => {
1950
1963
  if (e.target === e.currentTarget) {
1951
1964
  onClose();
@@ -1954,18 +1967,18 @@ function BroadcastPlayerModal({
1954
1967
  return /* @__PURE__ */ jsx(
1955
1968
  "div",
1956
1969
  {
1957
- className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-4",
1970
+ className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-2 sm:p-4",
1958
1971
  onClick: handleBackdropClick,
1959
1972
  role: "dialog",
1960
1973
  "aria-modal": "true",
1961
1974
  "aria-label": "Broadcast player",
1962
- children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full max-w-7xl max-h-[90vh]", children: [
1975
+ children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-7xl max-h-[95vh] sm:max-h-[90vh] overflow-hidden", children: [
1963
1976
  /* @__PURE__ */ jsx(
1964
1977
  "button",
1965
1978
  {
1966
1979
  ref: closeButtonRef,
1967
1980
  onClick: onClose,
1968
- 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",
1981
+ 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",
1969
1982
  title: "Close (ESC)",
1970
1983
  "aria-label": "Close player",
1971
1984
  children: "\xD7"