@neurodyn/react-room-viewer 0.1.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +168 -111
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6830,69 +6830,6 @@ ${propertyDefaults}
6830
6830
  }
6831
6831
  var styleinline = __webpack_require__("./src/style.css?inline");
6832
6832
  const shadowRootCssText = createShadowRootCssText(styleinline.A);
6833
- function getRoomViewerBackendApiBaseUrl(baseUrl) {
6834
- const normalized = baseUrl?.trim().replace(/\/+$/, '') ?? '';
6835
- if (!normalized || normalized.includes('/v1/http') || normalized.endsWith('/v1')) return normalized || void 0;
6836
- const httpSegmentIndex = normalized.indexOf('/http');
6837
- if (httpSegmentIndex >= 0) return `${normalized.slice(0, httpSegmentIndex)}/v1${normalized.slice(httpSegmentIndex)}`;
6838
- return `${normalized}/v1`;
6839
- }
6840
- const shopifyProxyApiBaseUrl = getRoomViewerBackendApiBaseUrl("/apps/vizbl-wall-art/http") ?? '';
6841
- function getRoomViewerApiBaseUrl(baseUrl) {
6842
- return baseUrl?.trim().replace(/\/+$/, '').replace(/\/(?:v1(?:\/http)?|http)$/, '') || void 0;
6843
- }
6844
- function normalizeRoomViewerApiKey(apiKey) {
6845
- const trimmed = apiKey?.trim();
6846
- return trimmed || void 0;
6847
- }
6848
- function normalizeRoomViewerTinuuid(tinuuid) {
6849
- const trimmed = tinuuid?.trim();
6850
- return trimmed || void 0;
6851
- }
6852
- function shouldUseRoomViewerPublicApi(options) {
6853
- return 'shopify' === options.source || void 0 !== options.apiKey;
6854
- }
6855
- function getRoomViewerPublicApiProductType(options) {
6856
- if ('wall-panel' === options.resourceType) return 'panels';
6857
- if ('rugs' === options.resourceType) return 'rugs';
6858
- if ('flooring' === options.resourceType) return 'flooring';
6859
- return 'shopify' === options.source ? 'wallart-shopify' : 'wallart';
6860
- }
6861
- function getRoomViewerBackendProductType(options) {
6862
- if ('wall-panel' === options.resourceType) return 'panels';
6863
- if ('rugs' === options.resourceType) return 'rugs';
6864
- if ('flooring' === options.resourceType) return 'flooring';
6865
- return 'wallart';
6866
- }
6867
- function getRoomViewerPublicApiQuery(options) {
6868
- const productType = getRoomViewerPublicApiProductType(options);
6869
- return 'panels' === productType ? {
6870
- query: void 0,
6871
- config: {
6872
- params: {
6873
- productType
6874
- }
6875
- }
6876
- } : {
6877
- query: {
6878
- productType
6879
- },
6880
- config: {}
6881
- };
6882
- }
6883
- function getRoomViewerActivationQuery(options) {
6884
- return getRoomViewerPublicApiQuery('wall-panel' === options.resourceType ? options : {
6885
- ...options,
6886
- resourceType: void 0
6887
- });
6888
- }
6889
- function getRoomViewerAccessError(error) {
6890
- if (!isAxiosError(error)) return null;
6891
- const status = error.response?.status;
6892
- if (401 === status) return 'unauthorized';
6893
- if (402 === status) return 'payment-required';
6894
- return null;
6895
- }
6896
6833
  const DEFAULT_API_TIMEOUT = 30000;
6897
6834
  function createDefaultClient(defaultBaseURL) {
6898
6835
  return axios.create({
@@ -7317,6 +7254,17 @@ async function layoutControllerReadLayoutConfig(headers, config = {}) {
7317
7254
  });
7318
7255
  return res.data;
7319
7256
  }
7257
+ async function arLaunchControllerCreateArLaunch(data, config = {}) {
7258
+ const { client: request = client_client, ...requestConfig } = config;
7259
+ const requestData = data;
7260
+ const res = await request({
7261
+ method: "POST",
7262
+ url: "/public/ar-launches",
7263
+ data: requestData,
7264
+ ...requestConfig
7265
+ });
7266
+ return res.data;
7267
+ }
7320
7268
  async function publicApiControllerActivateWallart(params, config = {}) {
7321
7269
  const { client: request = client_client, ...requestConfig } = config;
7322
7270
  const res = await request({
@@ -7413,6 +7361,90 @@ async function widgetEventsControllerCreateWidgetEvent(data, config = {}) {
7413
7361
  });
7414
7362
  return res.data;
7415
7363
  }
7364
+ function getRoomViewerAccessError(error) {
7365
+ if (!isAxiosError(error)) return null;
7366
+ const status = error.response?.status;
7367
+ if (401 === status) return 'unauthorized';
7368
+ if (402 === status) return 'payment-required';
7369
+ return null;
7370
+ }
7371
+ function createArLaunchId() {
7372
+ if ("u" > typeof crypto && 'function' == typeof crypto.randomUUID) return crypto.randomUUID();
7373
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
7374
+ bytes[6] = 0x0F & bytes[6] | 0x40;
7375
+ bytes[8] = 0x3F & bytes[8] | 0x80;
7376
+ const hex = Array.from(bytes, (byte)=>byte.toString(16).padStart(2, '0')).join('');
7377
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
7378
+ }
7379
+ async function requestArLaunch(session, { format, launchId, tinuuid }) {
7380
+ try {
7381
+ await arLaunchControllerCreateArLaunch({
7382
+ entryPoint: 'ar_launched',
7383
+ format,
7384
+ launchId,
7385
+ tinuuid
7386
+ }, await session.getApiConfigForRequest());
7387
+ return 'granted';
7388
+ } catch (error) {
7389
+ return 'payment-required' === getRoomViewerAccessError(error) ? 'payment-required' : 'unavailable';
7390
+ }
7391
+ }
7392
+ function getRoomViewerBackendApiBaseUrl(baseUrl) {
7393
+ const normalized = baseUrl?.trim().replace(/\/+$/, '') ?? '';
7394
+ if (!normalized || normalized.includes('/v1/http') || normalized.endsWith('/v1')) return normalized || void 0;
7395
+ const httpSegmentIndex = normalized.indexOf('/http');
7396
+ if (httpSegmentIndex >= 0) return `${normalized.slice(0, httpSegmentIndex)}/v1${normalized.slice(httpSegmentIndex)}`;
7397
+ return `${normalized}/v1`;
7398
+ }
7399
+ const shopifyProxyApiBaseUrl = getRoomViewerBackendApiBaseUrl("/apps/vizbl-wall-art/http") ?? '';
7400
+ function getRoomViewerApiBaseUrl(baseUrl) {
7401
+ return baseUrl?.trim().replace(/\/+$/, '').replace(/\/(?:v1(?:\/http)?|http)$/, '') || void 0;
7402
+ }
7403
+ function normalizeRoomViewerApiKey(apiKey) {
7404
+ const trimmed = apiKey?.trim();
7405
+ return trimmed || void 0;
7406
+ }
7407
+ function normalizeRoomViewerTinuuid(tinuuid) {
7408
+ const trimmed = tinuuid?.trim();
7409
+ return trimmed || void 0;
7410
+ }
7411
+ function shouldUseRoomViewerPublicApi(options) {
7412
+ return 'shopify' === options.source || void 0 !== options.apiKey;
7413
+ }
7414
+ function getRoomViewerPublicApiProductType(options) {
7415
+ if ('wall-panel' === options.resourceType) return 'panels';
7416
+ if ('rugs' === options.resourceType) return 'rugs';
7417
+ if ('flooring' === options.resourceType) return 'flooring';
7418
+ return 'shopify' === options.source ? 'wallart-shopify' : 'wallart';
7419
+ }
7420
+ function getRoomViewerBackendProductType(options) {
7421
+ if ('wall-panel' === options.resourceType) return 'panels';
7422
+ if ('rugs' === options.resourceType) return 'rugs';
7423
+ if ('flooring' === options.resourceType) return 'flooring';
7424
+ return 'wallart';
7425
+ }
7426
+ function getRoomViewerPublicApiQuery(options) {
7427
+ const productType = getRoomViewerPublicApiProductType(options);
7428
+ return 'panels' === productType ? {
7429
+ query: void 0,
7430
+ config: {
7431
+ params: {
7432
+ productType
7433
+ }
7434
+ }
7435
+ } : {
7436
+ query: {
7437
+ productType
7438
+ },
7439
+ config: {}
7440
+ };
7441
+ }
7442
+ function getRoomViewerActivationQuery(options) {
7443
+ return getRoomViewerPublicApiQuery('wall-panel' === options.resourceType ? options : {
7444
+ ...options,
7445
+ resourceType: void 0
7446
+ });
7447
+ }
7416
7448
  const WIDGET_SESSION_TOKEN_HEADER = 'x-widget-session-token';
7417
7449
  const DEFAULT_REFRESH_THRESHOLD_MS = 30000;
7418
7450
  const createWidgetEventViaGeneratedApi = (data, config)=>widgetEventsControllerCreateWidgetEvent(data, config);
@@ -13609,6 +13641,7 @@ function buildRoomViewerArUrl({ height, language, name, src, tenantConfig, tinuu
13609
13641
  if (widgetSessionToken) url.searchParams.set('widgetSessionToken', widgetSessionToken);
13610
13642
  return url.toString();
13611
13643
  }
13644
+ const AR_LAUNCH_TIMEOUT_MS = 3000;
13612
13645
  async function whenArModeSelected(modelViewer) {
13613
13646
  await modelViewer.updateComplete;
13614
13647
  if (!modelViewer.canActivateAR && navigator.xr?.isSessionSupported) {
@@ -13637,7 +13670,7 @@ async function presentAr(modelViewer) {
13637
13670
  function AR({ className, buttonClassName, buttonLeadingIcon, buttonLabel, buttonSize, buttonVariant = 'contrast', ...props }) {
13638
13671
  const { type, width, height, name } = useRoomViewerMetadata();
13639
13672
  const { handoff } = resolveResourceProfile(type);
13640
- const { src, tinuuid, widgetSession } = useRoomViewerResource();
13673
+ const { setAccessError, src, tinuuid, widgetSession } = useRoomViewerResource();
13641
13674
  const { containerRef, resourceRef } = useRoomViewerSceneState();
13642
13675
  const { t, i18n } = useTranslation();
13643
13676
  const tenant = useRoomViewerTenant();
@@ -13713,16 +13746,34 @@ function AR({ className, buttonClassName, buttonLeadingIcon, buttonLabel, button
13713
13746
  modelViewerMountRef.current = null;
13714
13747
  setIsModelViewerRequested(false);
13715
13748
  }
13749
+ async function readWidgetSessionToken() {
13750
+ return widgetSession?.getTokenForRequest().catch(()=>widgetSession.token);
13751
+ }
13716
13752
  async function refreshHandoffWidgetSessionToken() {
13717
- const nextToken = await widgetSession?.getTokenForRequest().catch(()=>widgetSession.token);
13718
- setHandoffWidgetSessionToken(nextToken);
13753
+ setHandoffWidgetSessionToken(await readWidgetSessionToken());
13754
+ }
13755
+ async function payForArLaunch() {
13756
+ if (!widgetSession || !tinuuid) return 'unavailable';
13757
+ const timeout = new Promise((resolve)=>{
13758
+ window.setTimeout(()=>resolve('unavailable'), AR_LAUNCH_TIMEOUT_MS);
13759
+ });
13760
+ return Promise.race([
13761
+ requestArLaunch(widgetSession, {
13762
+ format: isIOS() ? 'usdz' : 'glb',
13763
+ launchId: createArLaunchId(),
13764
+ tinuuid
13765
+ }),
13766
+ timeout
13767
+ ]);
13719
13768
  }
13720
13769
  const handleClick = async ()=>{
13721
13770
  if (handoff?.ar === 'appclip') {
13722
13771
  if (!tinuuid) return;
13723
13772
  if (isIOS() && appClipUrl) {
13724
- sendWidgetEvent(widgetSession, 'appclip');
13725
- window.location.assign(appClipUrl);
13773
+ const token = await readWidgetSessionToken();
13774
+ const link = new URL(appClipUrl);
13775
+ if (token) link.searchParams.set('widgetSessionToken', token);
13776
+ window.location.assign(link.toString());
13726
13777
  return;
13727
13778
  }
13728
13779
  await refreshHandoffWidgetSessionToken();
@@ -13739,53 +13790,59 @@ function AR({ className, buttonClassName, buttonLeadingIcon, buttonLabel, button
13739
13790
  }
13740
13791
  const sceneResource = resourceRef.current;
13741
13792
  if (!sceneResource) return void releaseModelViewer();
13742
- if (modelViewer.canActivateAR) try {
13743
- setIsSceneExporting(true);
13744
- const clone = sceneResource.clone(true);
13745
- clone.position.set(0, 0, 0);
13746
- clone.quaternion.identity();
13747
- clone.updateMatrix();
13748
- clone.updateMatrixWorld(true);
13749
- if (isIOS()) {
13750
- clone.rotation.set(-Math.PI / 2, 0, 0);
13793
+ if (modelViewer.canActivateAR) {
13794
+ if (await payForArLaunch() === 'payment-required') {
13795
+ releaseModelViewer();
13796
+ setAccessError('payment-required');
13797
+ return;
13798
+ }
13799
+ try {
13800
+ setIsSceneExporting(true);
13801
+ const clone = sceneResource.clone(true);
13802
+ clone.position.set(0, 0, 0);
13803
+ clone.quaternion.identity();
13751
13804
  clone.updateMatrix();
13752
13805
  clone.updateMatrixWorld(true);
13753
- const usdzExporter = new USDZExporter();
13754
- const usdzBuffer = await usdzExporter.parseAsync(clone, {
13755
- ar: {
13756
- anchoring: {
13757
- type: 'plane'
13758
- },
13759
- planeAnchoring: {
13760
- alignment: 'vertical'
13806
+ if (isIOS()) {
13807
+ clone.rotation.set(-Math.PI / 2, 0, 0);
13808
+ clone.updateMatrix();
13809
+ clone.updateMatrixWorld(true);
13810
+ const usdzExporter = new USDZExporter();
13811
+ const usdzBuffer = await usdzExporter.parseAsync(clone, {
13812
+ ar: {
13813
+ anchoring: {
13814
+ type: 'plane'
13815
+ },
13816
+ planeAnchoring: {
13817
+ alignment: 'vertical'
13818
+ }
13761
13819
  }
13762
- }
13763
- });
13764
- const usdzFile = new File([
13765
- usdzBuffer
13766
- ], 'scene.usdz', {
13767
- type: 'model/vnd.usdz+zip'
13768
- });
13769
- setUsdzUrl(URL.createObjectURL(usdzFile));
13770
- } else {
13771
- const glbExporter = new GLTFExporter();
13772
- const glbBuffer = await glbExporter.parseAsync(clone, {
13773
- binary: true
13774
- });
13775
- const glbFile = new File([
13776
- glbBuffer
13777
- ], 'scene.glb', {
13778
- type: 'model/gltf-binary'
13779
- });
13780
- setGlbUrl(URL.createObjectURL(glbFile));
13820
+ });
13821
+ const usdzFile = new File([
13822
+ usdzBuffer
13823
+ ], 'scene.usdz', {
13824
+ type: 'model/vnd.usdz+zip'
13825
+ });
13826
+ setUsdzUrl(URL.createObjectURL(usdzFile));
13827
+ } else {
13828
+ const glbExporter = new GLTFExporter();
13829
+ const glbBuffer = await glbExporter.parseAsync(clone, {
13830
+ binary: true
13831
+ });
13832
+ const glbFile = new File([
13833
+ glbBuffer
13834
+ ], 'scene.glb', {
13835
+ type: 'model/gltf-binary'
13836
+ });
13837
+ setGlbUrl(URL.createObjectURL(glbFile));
13838
+ }
13839
+ } catch (error) {
13840
+ releaseModelViewer();
13841
+ throw error;
13842
+ } finally{
13843
+ setIsSceneExporting(false);
13781
13844
  }
13782
- } catch (error) {
13783
- releaseModelViewer();
13784
- throw error;
13785
- } finally{
13786
- setIsSceneExporting(false);
13787
- }
13788
- else {
13845
+ } else {
13789
13846
  releaseModelViewer();
13790
13847
  await refreshHandoffWidgetSessionToken();
13791
13848
  setOpen(true);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@neurodyn/react-room-viewer",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,7 +30,7 @@
30
30
  "@radix-ui/react-tabs": "^1.0.4",
31
31
  "@react-three/drei": "^10.7.7",
32
32
  "@react-three/fiber": "^9.5.0",
33
- "@tanstack/react-query": "5.90.12",
33
+ "@tanstack/react-query": "5.102.8",
34
34
  "axios": "^1.13.2",
35
35
  "class-variance-authority": "0.7.1",
36
36
  "clsx": "2.1.1",