@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.
@@ -8,6 +8,7 @@ function DialTribeProvider({
8
8
  sessionToken: initialToken,
9
9
  onTokenRefresh,
10
10
  onTokenExpired,
11
+ apiBaseUrl,
11
12
  children
12
13
  }) {
13
14
  const [sessionToken, setSessionTokenState] = useState(initialToken);
@@ -36,7 +37,8 @@ function DialTribeProvider({
36
37
  sessionToken,
37
38
  setSessionToken,
38
39
  isExpired,
39
- markExpired
40
+ markExpired,
41
+ apiBaseUrl
40
42
  };
41
43
  return /* @__PURE__ */ jsx(DialTribeContext.Provider, { value, children });
42
44
  }
@@ -51,18 +53,28 @@ function useDialTribe() {
51
53
  }
52
54
 
53
55
  // src/client/DialTribeClient.ts
54
- var DIALTRIBE_API_BASE = "https://dialtribe.com/api/public/v1";
55
- var ENDPOINTS = {
56
- broadcasts: `${DIALTRIBE_API_BASE}/broadcasts`,
57
- broadcast: (id) => `${DIALTRIBE_API_BASE}/broadcasts/${id}`,
58
- contentPlay: `${DIALTRIBE_API_BASE}/content/play`,
59
- presignedUrl: `${DIALTRIBE_API_BASE}/media/presigned-url`,
60
- sessionStart: `${DIALTRIBE_API_BASE}/session/start`,
61
- sessionPing: `${DIALTRIBE_API_BASE}/session/ping`
62
- };
56
+ function getDefaultApiBaseUrl() {
57
+ if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DIALTRIBE_API_URL) {
58
+ return process.env.NEXT_PUBLIC_DIALTRIBE_API_URL;
59
+ }
60
+ return "https://dialtribe.com/api/public/v1";
61
+ }
62
+ var DIALTRIBE_API_BASE = getDefaultApiBaseUrl();
63
+ function getEndpoints(baseUrl = DIALTRIBE_API_BASE) {
64
+ return {
65
+ broadcasts: `${baseUrl}/broadcasts`,
66
+ broadcast: (id) => `${baseUrl}/broadcasts/${id}`,
67
+ contentPlay: `${baseUrl}/content/play`,
68
+ presignedUrl: `${baseUrl}/media/presigned-url`,
69
+ sessionStart: `${baseUrl}/session/start`,
70
+ sessionPing: `${baseUrl}/session/ping`
71
+ };
72
+ }
73
+ var ENDPOINTS = getEndpoints();
63
74
  var DialTribeClient = class {
64
75
  constructor(config) {
65
76
  this.config = config;
77
+ this.endpoints = config.apiBaseUrl ? getEndpoints(config.apiBaseUrl) : ENDPOINTS;
66
78
  }
67
79
  /**
68
80
  * Make an authenticated request to DialTribe API
@@ -109,7 +121,7 @@ var DialTribeClient = class {
109
121
  if (params?.broadcastStatus) searchParams.set("broadcastStatus", params.broadcastStatus.toString());
110
122
  if (params?.search) searchParams.set("search", params.search);
111
123
  if (params?.includeDeleted) searchParams.set("includeDeleted", "true");
112
- const url = `${ENDPOINTS.broadcasts}${searchParams.toString() ? `?${searchParams}` : ""}`;
124
+ const url = `${this.endpoints.broadcasts}${searchParams.toString() ? `?${searchParams}` : ""}`;
113
125
  const response = await this.fetch(url);
114
126
  if (!response.ok) {
115
127
  throw new Error(`Failed to fetch broadcasts: ${response.status} ${response.statusText}`);
@@ -120,7 +132,7 @@ var DialTribeClient = class {
120
132
  * Get a single broadcast by ID
121
133
  */
122
134
  async getBroadcast(id) {
123
- const response = await this.fetch(ENDPOINTS.broadcast(id));
135
+ const response = await this.fetch(this.endpoints.broadcast(id));
124
136
  if (!response.ok) {
125
137
  if (response.status === 404) {
126
138
  throw new Error("Broadcast not found");
@@ -142,7 +154,7 @@ var DialTribeClient = class {
142
154
  });
143
155
  if (params.hash) searchParams.set("hash", params.hash);
144
156
  if (params.action) searchParams.set("action", params.action);
145
- const url = `${ENDPOINTS.contentPlay}?${searchParams}`;
157
+ const url = `${this.endpoints.contentPlay}?${searchParams}`;
146
158
  const response = await this.fetch(url, {
147
159
  redirect: "manual"
148
160
  // Don't follow redirect, we want the URL
@@ -166,7 +178,7 @@ var DialTribeClient = class {
166
178
  hash: params.hash,
167
179
  fileType: params.fileType
168
180
  });
169
- const url = `${ENDPOINTS.presignedUrl}?${searchParams}`;
181
+ const url = `${this.endpoints.presignedUrl}?${searchParams}`;
170
182
  const response = await this.fetch(url);
171
183
  if (!response.ok) {
172
184
  throw new Error(`Failed to refresh URL: ${response.status} ${response.statusText}`);
@@ -179,7 +191,7 @@ var DialTribeClient = class {
179
191
  * @returns audienceId and optional resumePosition
180
192
  */
181
193
  async startSession(params) {
182
- const response = await this.fetch(ENDPOINTS.sessionStart, {
194
+ const response = await this.fetch(this.endpoints.sessionStart, {
183
195
  method: "POST",
184
196
  body: JSON.stringify(params)
185
197
  });
@@ -198,7 +210,7 @@ var DialTribeClient = class {
198
210
  * - 3: UNMOUNT
199
211
  */
200
212
  async sendSessionPing(params) {
201
- const response = await this.fetch(ENDPOINTS.sessionPing, {
213
+ const response = await this.fetch(this.endpoints.sessionPing, {
202
214
  method: "POST",
203
215
  body: JSON.stringify(params)
204
216
  });
@@ -741,11 +753,12 @@ function BroadcastPlayer({
741
753
  className = "",
742
754
  enableKeyboardShortcuts = false
743
755
  }) {
744
- const { sessionToken, setSessionToken, markExpired } = useDialTribe();
756
+ const { sessionToken, setSessionToken, markExpired, apiBaseUrl } = useDialTribe();
745
757
  const clientRef = useRef(null);
746
758
  if (!clientRef.current && sessionToken) {
747
759
  clientRef.current = new DialTribeClient({
748
760
  sessionToken,
761
+ apiBaseUrl,
749
762
  onTokenRefresh: (newToken, expiresAt) => {
750
763
  debug.log(`[DialTribeClient] Token refreshed, expires at ${expiresAt}`);
751
764
  setSessionToken(newToken, expiresAt);
@@ -1404,8 +1417,8 @@ function BroadcastPlayer({
1404
1417
  return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-8", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white", text: "Loading..." }) });
1405
1418
  }
1406
1419
  const hasTranscript = broadcast.transcriptStatus === 2 && transcriptData && (transcriptData.segments && transcriptData.segments.some((s) => s.words && s.words.length > 0) || transcriptData.words && transcriptData.words.length > 0);
1407
- return /* @__PURE__ */ jsxs("div", { className: `bg-black rounded-lg shadow-2xl w-full h-full flex flex-col ${className}`, children: [
1408
- /* @__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: [
1420
+ return /* @__PURE__ */ jsxs("div", { className: `bg-black rounded-lg shadow-2xl w-full max-h-full flex flex-col overflow-hidden ${className}`, children: [
1421
+ /* @__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: [
1409
1422
  /* @__PURE__ */ jsxs("div", { children: [
1410
1423
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-white", children: broadcast.streamKeyRecord?.foreignName || "Broadcast" }),
1411
1424
  /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-400", children: [
@@ -1433,8 +1446,8 @@ function BroadcastPlayer({
1433
1446
  }
1434
1447
  ) })
1435
1448
  ] }),
1436
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
1437
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
1449
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row flex-1 min-h-0 overflow-hidden", children: [
1450
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 md:shrink md:flex-1 flex flex-col overflow-hidden", children: [
1438
1451
  /* @__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: [
1439
1452
  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: [
1440
1453
  /* @__PURE__ */ jsx(AudioWaveform, { audioElement, isPlaying: isLiveStream ? true : playing, isLive: isLiveStream }),
@@ -1716,7 +1729,7 @@ function BroadcastPlayer({
1716
1729
  ] })
1717
1730
  ] }))
1718
1731
  ] }),
1719
- 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: [
1732
+ 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: [
1720
1733
  /* @__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: [
1721
1734
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1722
1735
  /* @__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" }) }),
@@ -1745,7 +1758,7 @@ function BroadcastPlayer({
1745
1758
  ]
1746
1759
  }
1747
1760
  ) }),
1748
- /* @__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: (() => {
1761
+ /* @__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: (() => {
1749
1762
  const filteredSegments = transcriptData.segments.filter((s) => s.words && s.words.length > 0);
1750
1763
  let globalWordIndex = 0;
1751
1764
  const wordMap = /* @__PURE__ */ new Map();
@@ -1905,14 +1918,12 @@ function BroadcastPlayerModal({
1905
1918
  }) {
1906
1919
  const closeButtonRef = useRef(null);
1907
1920
  const previousActiveElement = useRef(null);
1908
- if (!isOpen) return null;
1909
1921
  useEffect(() => {
1910
- if (isOpen) {
1911
- previousActiveElement.current = document.activeElement;
1912
- setTimeout(() => {
1913
- closeButtonRef.current?.focus();
1914
- }, 100);
1915
- }
1922
+ if (!isOpen) return;
1923
+ previousActiveElement.current = document.activeElement;
1924
+ setTimeout(() => {
1925
+ closeButtonRef.current?.focus();
1926
+ }, 100);
1916
1927
  return () => {
1917
1928
  if (previousActiveElement.current) {
1918
1929
  previousActiveElement.current.focus();
@@ -1920,6 +1931,7 @@ function BroadcastPlayerModal({
1920
1931
  };
1921
1932
  }, [isOpen]);
1922
1933
  useEffect(() => {
1934
+ if (!isOpen) return;
1923
1935
  const handleKeyDown = (e) => {
1924
1936
  if (e.key === "Escape") {
1925
1937
  onClose();
@@ -1927,7 +1939,8 @@ function BroadcastPlayerModal({
1927
1939
  };
1928
1940
  document.addEventListener("keydown", handleKeyDown);
1929
1941
  return () => document.removeEventListener("keydown", handleKeyDown);
1930
- }, [onClose]);
1942
+ }, [isOpen, onClose]);
1943
+ if (!isOpen) return null;
1931
1944
  const handleBackdropClick = (e) => {
1932
1945
  if (e.target === e.currentTarget) {
1933
1946
  onClose();
@@ -1936,18 +1949,18 @@ function BroadcastPlayerModal({
1936
1949
  return /* @__PURE__ */ jsx(
1937
1950
  "div",
1938
1951
  {
1939
- className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-4",
1952
+ className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-2 sm:p-4",
1940
1953
  onClick: handleBackdropClick,
1941
1954
  role: "dialog",
1942
1955
  "aria-modal": "true",
1943
1956
  "aria-label": "Broadcast player",
1944
- children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full max-w-7xl max-h-[90vh]", children: [
1957
+ children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-7xl max-h-[95vh] sm:max-h-[90vh] overflow-hidden", children: [
1945
1958
  /* @__PURE__ */ jsx(
1946
1959
  "button",
1947
1960
  {
1948
1961
  ref: closeButtonRef,
1949
1962
  onClick: onClose,
1950
- 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",
1963
+ 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",
1951
1964
  title: "Close (ESC)",
1952
1965
  "aria-label": "Close player",
1953
1966
  children: "\xD7"