@scalemule/nextjs 0.1.22 → 0.1.23

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.d.mts CHANGED
@@ -898,7 +898,7 @@ interface NarrationPlayerProps {
898
898
  providerLabel?: string;
899
899
  narrationLabel?: string;
900
900
  refreshing?: boolean;
901
- onRefresh?: () => void;
901
+ onRefresh?: () => void | Promise<void>;
902
902
  onPlaybackError?: () => void;
903
903
  }
904
904
  declare function NarrationPlayer({ audio, className, providerLabel, narrationLabel, refreshing, onRefresh, onPlaybackError, }: NarrationPlayerProps): react_jsx_runtime.JSX.Element | null;
package/dist/index.d.ts CHANGED
@@ -898,7 +898,7 @@ interface NarrationPlayerProps {
898
898
  providerLabel?: string;
899
899
  narrationLabel?: string;
900
900
  refreshing?: boolean;
901
- onRefresh?: () => void;
901
+ onRefresh?: () => void | Promise<void>;
902
902
  onPlaybackError?: () => void;
903
903
  }
904
904
  declare function NarrationPlayer({ audio, className, providerLabel, narrationLabel, refreshing, onRefresh, onPlaybackError, }: NarrationPlayerProps): react_jsx_runtime.JSX.Element | null;
package/dist/index.js CHANGED
@@ -11289,6 +11289,7 @@ function FeedbackWidget(props) {
11289
11289
  ] });
11290
11290
  }
11291
11291
  var SPEEDS = [1, 1.25, 1.5, 2];
11292
+ var EXPIRY_REFRESH_WINDOW_MS = 3e4;
11292
11293
  var styles = {
11293
11294
  root: {
11294
11295
  borderRadius: "1.75rem",
@@ -11466,6 +11467,9 @@ function NarrationPlayer({
11466
11467
  }) {
11467
11468
  const audioRef = React.useRef(null);
11468
11469
  const scrubRef = React.useRef(null);
11470
+ const pendingResumeRef = React.useRef(false);
11471
+ const pendingResumeTimeRef = React.useRef(null);
11472
+ const recoveredUrlRef = React.useRef(null);
11469
11473
  const [playing, setPlaying] = React.useState(false);
11470
11474
  const [currentTime, setCurrentTime] = React.useState(0);
11471
11475
  const [duration, setDuration] = React.useState(audio.duration_ms ? audio.duration_ms / 1e3 : 0);
@@ -11480,10 +11484,26 @@ function NarrationPlayer({
11480
11484
  React.useEffect(() => {
11481
11485
  const element = audioRef.current;
11482
11486
  if (!element) return;
11483
- setCurrentTime(0);
11487
+ recoveredUrlRef.current = null;
11488
+ const resumeTime = pendingResumeTimeRef.current;
11489
+ setCurrentTime(resumeTime ?? 0);
11484
11490
  setPlaying(false);
11485
11491
  element.pause();
11486
- element.currentTime = 0;
11492
+ try {
11493
+ element.currentTime = resumeTime ?? 0;
11494
+ } catch {
11495
+ element.currentTime = 0;
11496
+ }
11497
+ if (pendingResumeRef.current) {
11498
+ pendingResumeRef.current = false;
11499
+ pendingResumeTimeRef.current = null;
11500
+ element.play().catch(() => {
11501
+ setPlaying(false);
11502
+ onPlaybackError?.();
11503
+ });
11504
+ return;
11505
+ }
11506
+ pendingResumeTimeRef.current = null;
11487
11507
  }, [audio.url]);
11488
11508
  React.useEffect(() => {
11489
11509
  const element = audioRef.current;
@@ -11491,10 +11511,49 @@ function NarrationPlayer({
11491
11511
  element.playbackRate = speed;
11492
11512
  element.preservesPitch = true;
11493
11513
  }, [speed]);
11514
+ function isUrlExpiringSoon() {
11515
+ if (!audio.expires_at) {
11516
+ return false;
11517
+ }
11518
+ const expiresAtMs = Date.parse(audio.expires_at);
11519
+ if (!Number.isFinite(expiresAtMs)) {
11520
+ return false;
11521
+ }
11522
+ return expiresAtMs - Date.now() <= EXPIRY_REFRESH_WINDOW_MS;
11523
+ }
11524
+ async function refreshAndResume() {
11525
+ if (!onRefresh || refreshing) {
11526
+ return false;
11527
+ }
11528
+ if (recoveredUrlRef.current === (audio.url ?? null)) {
11529
+ return false;
11530
+ }
11531
+ recoveredUrlRef.current = audio.url ?? null;
11532
+ pendingResumeRef.current = true;
11533
+ pendingResumeTimeRef.current = currentTime;
11534
+ try {
11535
+ await Promise.resolve(onRefresh());
11536
+ return true;
11537
+ } catch {
11538
+ pendingResumeRef.current = false;
11539
+ pendingResumeTimeRef.current = null;
11540
+ onPlaybackError?.();
11541
+ return false;
11542
+ }
11543
+ }
11494
11544
  function togglePlayback() {
11495
11545
  const element = audioRef.current;
11496
11546
  if (!element) return;
11497
11547
  if (element.paused) {
11548
+ if (refreshing) {
11549
+ pendingResumeRef.current = true;
11550
+ pendingResumeTimeRef.current = currentTime;
11551
+ return;
11552
+ }
11553
+ if (onRefresh && isUrlExpiringSoon()) {
11554
+ void refreshAndResume();
11555
+ return;
11556
+ }
11498
11557
  element.play().catch(() => {
11499
11558
  setPlaying(false);
11500
11559
  onPlaybackError?.();
@@ -11537,7 +11596,17 @@ function NarrationPlayer({
11537
11596
  onPlay: () => setPlaying(true),
11538
11597
  onPause: () => setPlaying(false),
11539
11598
  onEnded: () => setPlaying(false),
11540
- onError: () => onPlaybackError?.()
11599
+ onError: () => {
11600
+ if (onRefresh) {
11601
+ void refreshAndResume().then((didRefresh) => {
11602
+ if (!didRefresh) {
11603
+ onPlaybackError?.();
11604
+ }
11605
+ });
11606
+ return;
11607
+ }
11608
+ onPlaybackError?.();
11609
+ }
11541
11610
  }
11542
11611
  ),
11543
11612
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.layout, children: [
package/dist/index.mjs CHANGED
@@ -11269,6 +11269,7 @@ function FeedbackWidget(props) {
11269
11269
  ] });
11270
11270
  }
11271
11271
  var SPEEDS = [1, 1.25, 1.5, 2];
11272
+ var EXPIRY_REFRESH_WINDOW_MS = 3e4;
11272
11273
  var styles = {
11273
11274
  root: {
11274
11275
  borderRadius: "1.75rem",
@@ -11446,6 +11447,9 @@ function NarrationPlayer({
11446
11447
  }) {
11447
11448
  const audioRef = useRef(null);
11448
11449
  const scrubRef = useRef(null);
11450
+ const pendingResumeRef = useRef(false);
11451
+ const pendingResumeTimeRef = useRef(null);
11452
+ const recoveredUrlRef = useRef(null);
11449
11453
  const [playing, setPlaying] = useState(false);
11450
11454
  const [currentTime, setCurrentTime] = useState(0);
11451
11455
  const [duration, setDuration] = useState(audio.duration_ms ? audio.duration_ms / 1e3 : 0);
@@ -11460,10 +11464,26 @@ function NarrationPlayer({
11460
11464
  useEffect(() => {
11461
11465
  const element = audioRef.current;
11462
11466
  if (!element) return;
11463
- setCurrentTime(0);
11467
+ recoveredUrlRef.current = null;
11468
+ const resumeTime = pendingResumeTimeRef.current;
11469
+ setCurrentTime(resumeTime ?? 0);
11464
11470
  setPlaying(false);
11465
11471
  element.pause();
11466
- element.currentTime = 0;
11472
+ try {
11473
+ element.currentTime = resumeTime ?? 0;
11474
+ } catch {
11475
+ element.currentTime = 0;
11476
+ }
11477
+ if (pendingResumeRef.current) {
11478
+ pendingResumeRef.current = false;
11479
+ pendingResumeTimeRef.current = null;
11480
+ element.play().catch(() => {
11481
+ setPlaying(false);
11482
+ onPlaybackError?.();
11483
+ });
11484
+ return;
11485
+ }
11486
+ pendingResumeTimeRef.current = null;
11467
11487
  }, [audio.url]);
11468
11488
  useEffect(() => {
11469
11489
  const element = audioRef.current;
@@ -11471,10 +11491,49 @@ function NarrationPlayer({
11471
11491
  element.playbackRate = speed;
11472
11492
  element.preservesPitch = true;
11473
11493
  }, [speed]);
11494
+ function isUrlExpiringSoon() {
11495
+ if (!audio.expires_at) {
11496
+ return false;
11497
+ }
11498
+ const expiresAtMs = Date.parse(audio.expires_at);
11499
+ if (!Number.isFinite(expiresAtMs)) {
11500
+ return false;
11501
+ }
11502
+ return expiresAtMs - Date.now() <= EXPIRY_REFRESH_WINDOW_MS;
11503
+ }
11504
+ async function refreshAndResume() {
11505
+ if (!onRefresh || refreshing) {
11506
+ return false;
11507
+ }
11508
+ if (recoveredUrlRef.current === (audio.url ?? null)) {
11509
+ return false;
11510
+ }
11511
+ recoveredUrlRef.current = audio.url ?? null;
11512
+ pendingResumeRef.current = true;
11513
+ pendingResumeTimeRef.current = currentTime;
11514
+ try {
11515
+ await Promise.resolve(onRefresh());
11516
+ return true;
11517
+ } catch {
11518
+ pendingResumeRef.current = false;
11519
+ pendingResumeTimeRef.current = null;
11520
+ onPlaybackError?.();
11521
+ return false;
11522
+ }
11523
+ }
11474
11524
  function togglePlayback() {
11475
11525
  const element = audioRef.current;
11476
11526
  if (!element) return;
11477
11527
  if (element.paused) {
11528
+ if (refreshing) {
11529
+ pendingResumeRef.current = true;
11530
+ pendingResumeTimeRef.current = currentTime;
11531
+ return;
11532
+ }
11533
+ if (onRefresh && isUrlExpiringSoon()) {
11534
+ void refreshAndResume();
11535
+ return;
11536
+ }
11478
11537
  element.play().catch(() => {
11479
11538
  setPlaying(false);
11480
11539
  onPlaybackError?.();
@@ -11517,7 +11576,17 @@ function NarrationPlayer({
11517
11576
  onPlay: () => setPlaying(true),
11518
11577
  onPause: () => setPlaying(false),
11519
11578
  onEnded: () => setPlaying(false),
11520
- onError: () => onPlaybackError?.()
11579
+ onError: () => {
11580
+ if (onRefresh) {
11581
+ void refreshAndResume().then((didRefresh) => {
11582
+ if (!didRefresh) {
11583
+ onPlaybackError?.();
11584
+ }
11585
+ });
11586
+ return;
11587
+ }
11588
+ onPlaybackError?.();
11589
+ }
11521
11590
  }
11522
11591
  ),
11523
11592
  /* @__PURE__ */ jsxs("div", { style: styles.layout, children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "ScaleMule SDK for Next.js applications - authentication, storage, and user management",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",