@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.
- package/dist/index.js +168 -111
- 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
|
-
|
|
13718
|
-
|
|
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
|
-
|
|
13725
|
-
|
|
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)
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
clone.
|
|
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
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
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
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13779
|
-
|
|
13780
|
-
|
|
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
|
-
}
|
|
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.
|
|
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.
|
|
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",
|