@joai/react 1.0.6 → 2.0.0

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
@@ -378,7 +378,7 @@ type ManagedContentEditorProps = {
378
378
  labels?: ContentToolbarProp['labels'];
379
379
  messages?: Partial<ContentEditorMessages>;
380
380
  reauthenticateLabel?: string;
381
- reauthenticateUrl?: string | ((returnTo: string) => string);
381
+ reauthenticateUrl?: string | ((returnTo: string) => string | Promise<string>);
382
382
  sessionStore?: SessionTokenStore;
383
383
  sessionExpiredMessage?: string;
384
384
  title?: string;
@@ -404,7 +404,21 @@ declare function ManagedContentEditor({
404
404
  }: ManagedContentEditorProps): _$react.JSX.Element;
405
405
  //#endregion
406
406
  //#region src/content/return-url.d.ts
407
- declare const getContentSessionReturnUrl: (currentUrl: string) => string;
407
+ type ContentSessionReturnUrlOptions = {
408
+ currentUrl: string;
409
+ env?: AppEnv;
410
+ team: string;
411
+ sessionStorageKey: string;
412
+ brand?: string;
413
+ };
414
+ /** Builds a JoAi login URL for the content editor and stores the PKCE secret for the callback. */
415
+ declare const getContentSessionReturnUrl: ({
416
+ currentUrl,
417
+ env,
418
+ team,
419
+ sessionStorageKey,
420
+ brand
421
+ }: ContentSessionReturnUrlOptions) => Promise<string>;
408
422
  //#endregion
409
423
  //#region src/content/draft-store.d.ts
410
424
  type BrowserStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
@@ -413,7 +427,6 @@ declare const createContentDraftStore: (storageKey: string, storage?: BrowserSto
413
427
  //#region src/content/use-content-session.d.ts
414
428
  type ContentSessionOptions = {
415
429
  env?: AppEnv;
416
- exchange?: (handoffToken: string) => Promise<string>;
417
430
  sessionStore?: SessionTokenStore;
418
431
  sessionStorageKey: string;
419
432
  team: string;
@@ -425,10 +438,9 @@ type ContentSession = {
425
438
  };
426
439
  declare const useContentSession: ({
427
440
  env,
428
- exchange,
429
441
  sessionStore,
430
442
  sessionStorageKey,
431
443
  team
432
444
  }: ContentSessionOptions) => ContentSession;
433
445
  //#endregion
434
- export { ContentClient, ContentContext, ContentContextValue, ContentDraft, ContentDraftStore, ContentEditor, ContentEditorConfig, ContentEditorMessages, ContentEditorProp, ContentImagePickerLabels, ContentProvider, ContentProviderProp, ContentResource, ContentResponse, ContentSession, ContentSessionOptions, ContentToolbar, ContentToolbarProp, EditableContent, EditableContentProp, EditableImage, EditableImageProp, EmbedChatInput, EmbedChatItem, EmbedChatList, type ImageCompressionOptions, type ImageCropOptions, type ImageCropRectangle, type ImageCropState, type ImageDimensionRules, type ImageDimensions, ImageProcessingOptions, ManagedContentEditor, ManagedContentEditorProps, SessionTokenStore, StreamDataHandler, clearRoom, containedImageDimensions, createContentDraftStore, createSessionTokenStore, cropOutputDimensions, getContentSessionReturnUrl, getOrCreateGuestId, imageCropRectangle, loadRoom, openEventSourceStream, processImageFile, readImageDimensions, saveRoom, useContent, useContentSession, useContentValue, useEmbedChat, useUnsavedNavigation, validateImageDimensions, validateImageFile };
446
+ export { ContentClient, ContentContext, ContentContextValue, ContentDraft, ContentDraftStore, ContentEditor, ContentEditorConfig, ContentEditorMessages, ContentEditorProp, ContentImagePickerLabels, ContentProvider, ContentProviderProp, ContentResource, ContentResponse, ContentSession, ContentSessionOptions, ContentSessionReturnUrlOptions, ContentToolbar, ContentToolbarProp, EditableContent, EditableContentProp, EditableImage, EditableImageProp, EmbedChatInput, EmbedChatItem, EmbedChatList, type ImageCompressionOptions, type ImageCropOptions, type ImageCropRectangle, type ImageCropState, type ImageDimensionRules, type ImageDimensions, ImageProcessingOptions, ManagedContentEditor, ManagedContentEditorProps, SessionTokenStore, StreamDataHandler, clearRoom, containedImageDimensions, createContentDraftStore, createSessionTokenStore, cropOutputDimensions, getContentSessionReturnUrl, getOrCreateGuestId, imageCropRectangle, loadRoom, openEventSourceStream, processImageFile, readImageDimensions, saveRoom, useContent, useContentSession, useContentValue, useEmbedChat, useUnsavedNavigation, validateImageDimensions, validateImageFile };
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
  import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
- import { containedImageDimensions, containedImageDimensions as containedImageDimensions$1, cropOutputDimensions, cropOutputDimensions as cropOutputDimensions$1, getLoginUrl, imageCropRectangle, imageCropRectangle as imageCropRectangle$1, validateImageDimensions } from "@joai/core";
5
- import { createClient, getAuthFragmentToken } from "@joai/sdk";
4
+ import { containedImageDimensions, containedImageDimensions as containedImageDimensions$1, cropOutputDimensions, cropOutputDimensions as cropOutputDimensions$1, imageCropRectangle, imageCropRectangle as imageCropRectangle$1, validateImageDimensions } from "@joai/core";
5
+ import { beginBrowserAppAuth, completeBrowserAppAuth } from "@joai/sdk";
6
6
  //#region src/guest.ts
7
7
  function safeGet(key) {
8
8
  try {
@@ -1592,10 +1592,17 @@ const createContentDraftStore = (storageKey, storage) => {
1592
1592
  };
1593
1593
  //#endregion
1594
1594
  //#region src/content/return-url.ts
1595
- const getContentSessionReturnUrl = (currentUrl) => {
1596
- const returnUrl = new URL(currentUrl);
1597
- returnUrl.searchParams.set("platform", "app");
1598
- return returnUrl.toString();
1595
+ /** Builds a JoAi login URL for the content editor and stores the PKCE secret for the callback. */
1596
+ const getContentSessionReturnUrl = async ({ currentUrl, env, team, sessionStorageKey, brand }) => {
1597
+ const callback = new URL(currentUrl);
1598
+ callback.hash = "";
1599
+ return beginBrowserAppAuth({
1600
+ callbackUrl: callback.toString(),
1601
+ env,
1602
+ team,
1603
+ brand,
1604
+ storageKey: `${sessionStorageKey}:auth`
1605
+ });
1599
1606
  };
1600
1607
  //#endregion
1601
1608
  //#region src/content/session-store.ts
@@ -1659,24 +1666,24 @@ const createSessionTokenStore = (storageKey, storage) => {
1659
1666
  };
1660
1667
  //#endregion
1661
1668
  //#region src/content/use-content-session.ts
1662
- const useContentSession = ({ env = "mainnet", exchange, sessionStore, sessionStorageKey, team }) => {
1669
+ const useContentSession = ({ env = "mainnet", sessionStore, sessionStorageKey, team }) => {
1663
1670
  const defaultStore = useMemo(() => createSessionTokenStore(sessionStorageKey), [sessionStorageKey]);
1664
1671
  const store = sessionStore ?? defaultStore;
1665
1672
  const storedToken = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
1666
1673
  const [handoffToken, setHandoffToken] = useState(null);
1667
1674
  const [error, setError] = useState(null);
1668
1675
  useEffect(() => {
1669
- const handoff = getAuthFragmentToken(window.location.href);
1670
- if (!handoff) return;
1671
- window.history.replaceState(null, "", `${window.location.pathname}${window.location.search}`);
1672
1676
  const completeExchange = async () => {
1673
- const token = await (exchange ? exchange(handoff) : createClient({
1677
+ const token = await completeBrowserAppAuth({
1678
+ url: window.location.href,
1674
1679
  env,
1675
- team
1676
- }).auth.exchangeNativeSession(handoff).then((response) => {
1677
- if (!response.ok || !response.data?.token) throw new Error("Session exchange failed.");
1678
- return response.data.token;
1679
- }));
1680
+ storageKey: `${sessionStorageKey}:auth`
1681
+ });
1682
+ if (!token) return;
1683
+ const clean = new URL(window.location.href);
1684
+ clean.searchParams.delete("code");
1685
+ clean.searchParams.delete("state");
1686
+ window.history.replaceState(null, "", `${clean.pathname}${clean.search}${clean.hash}`);
1680
1687
  if (!store.set(token)) throw new Error("Session storage is unavailable.");
1681
1688
  setHandoffToken(token);
1682
1689
  };
@@ -1767,11 +1774,28 @@ function ManagedContentEditor({ authErrorClassName, authErrorMessage = "The sess
1767
1774
  }, [token]);
1768
1775
  useEffect(() => {
1769
1776
  if (!sessionExpired || typeof window === "undefined") return;
1770
- const returnTo = getContentSessionReturnUrl(window.location.href);
1771
- setLoginUrl(typeof reauthenticateUrl === "function" ? reauthenticateUrl(returnTo) : reauthenticateUrl ?? getLoginUrl(env, returnTo, { team }));
1777
+ const resolveLoginUrl = async () => {
1778
+ if (typeof reauthenticateUrl === "function") {
1779
+ setLoginUrl(await reauthenticateUrl(window.location.href));
1780
+ return;
1781
+ }
1782
+ if (reauthenticateUrl) {
1783
+ setLoginUrl(reauthenticateUrl);
1784
+ return;
1785
+ }
1786
+ setLoginUrl(await getContentSessionReturnUrl({
1787
+ currentUrl: window.location.href,
1788
+ env,
1789
+ team,
1790
+ sessionStorageKey
1791
+ }));
1792
+ };
1793
+ resolveLoginUrl();
1772
1794
  }, [
1773
1795
  env,
1796
+ reauthenticateUrl,
1774
1797
  sessionExpired,
1798
+ sessionStorageKey,
1775
1799
  team
1776
1800
  ]);
1777
1801
  return /* @__PURE__ */ jsxs(Fragment, { children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joai/react",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "Reusable React building blocks for JoAi applications",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@joai/core": "^5.4.6",
25
- "@joai/sdk": "^14.0.0"
25
+ "@joai/sdk": "^14.2.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": ">=18.0.0"