@ebay/muse-lib-react 1.3.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/README.md +19 -0
- package/build/dev/asset-manifest.json +11 -0
- package/build/dev/favicon.png +0 -0
- package/build/dev/lib-manifest.json +3374 -0
- package/build/dev/main.js +75839 -0
- package/build/dev/main.js.map +1 -0
- package/build/dev/muse.d.ts +170 -0
- package/build/dev/static/media/logo.b23b880b0dac2229042c.png +0 -0
- package/build/dev/static/media/subAppLoading2.bf08007b83457287ade1cedb71bc70f7.svg +7 -0
- package/build/dist/asset-manifest.json +11 -0
- package/build/dist/favicon.png +0 -0
- package/build/dist/lib-manifest.json +3603 -0
- package/build/dist/main.js +3 -0
- package/build/dist/main.js.LICENSE.txt +134 -0
- package/build/dist/main.js.map +1 -0
- package/build/dist/muse.d.ts +170 -0
- package/build/dist/static/media/logo.b23b880b0dac2229042c.png +0 -0
- package/build/dist/static/media/subAppLoading2.bf08007b83457287ade1cedb71bc70f7.svg +7 -0
- package/build/test/asset-manifest.json +11 -0
- package/build/test/favicon.png +0 -0
- package/build/test/lib-manifest.json +3587 -0
- package/build/test/main.js +3 -0
- package/build/test/main.js.LICENSE.txt +134 -0
- package/build/test/main.js.map +1 -0
- package/build/test/muse.d.ts +170 -0
- package/build/test/static/media/logo.b23b880b0dac2229042c.png +0 -0
- package/build/test/static/media/subAppLoading2.bf08007b83457287ade1cedb71bc70f7.svg +7 -0
- package/package.json +88 -0
- package/src/Root.js +195 -0
- package/src/common/configStore.js +40 -0
- package/src/common/history.js +25 -0
- package/src/common/rootReducer.js +40 -0
- package/src/common/routeConfig.js +135 -0
- package/src/common/store.js +21 -0
- package/src/features/common/ErrorBoundary.js +24 -0
- package/src/features/common/ErrorBoundary.less +6 -0
- package/src/features/common/Nodes.js +20 -0
- package/src/features/common/PageNotFound.js +3 -0
- package/src/features/common/PageNotFound.less +6 -0
- package/src/features/common/index.js +4 -0
- package/src/features/common/redux/actions.js +0 -0
- package/src/features/common/redux/constants.js +0 -0
- package/src/features/common/redux/initialState.js +12 -0
- package/src/features/common/redux/reducer.js +24 -0
- package/src/features/common/route.js +9 -0
- package/src/features/common/style.less +3 -0
- package/src/features/common/useExtPoint.js +28 -0
- package/src/features/common/utils.js +20 -0
- package/src/features/home/App.js +33 -0
- package/src/features/home/App.less +11 -0
- package/src/features/home/Homepage.js +46 -0
- package/src/features/home/Homepage.less +63 -0
- package/src/features/home/index.js +2 -0
- package/src/features/home/redux/actions.js +0 -0
- package/src/features/home/redux/constants.js +0 -0
- package/src/features/home/redux/initialState.js +4 -0
- package/src/features/home/redux/reducer.js +16 -0
- package/src/features/home/route.js +6 -0
- package/src/features/home/style.less +3 -0
- package/src/features/sub-app/C2SProxyFailed.js +10 -0
- package/src/features/sub-app/C2SProxyFailed.less +19 -0
- package/src/features/sub-app/FixedSubAppContainer.js +142 -0
- package/src/features/sub-app/FixedSubAppContainer.less +12 -0
- package/src/features/sub-app/LoadingSkeleton.js +21 -0
- package/src/features/sub-app/LoadingSkeleton.less +20 -0
- package/src/features/sub-app/SubAppContainer.js +203 -0
- package/src/features/sub-app/SubAppContainer.less +29 -0
- package/src/features/sub-app/SubAppContext.js +4 -0
- package/src/features/sub-app/hooks/useParentRouteChange.js +23 -0
- package/src/features/sub-app/index.js +5 -0
- package/src/features/sub-app/redux/actions.js +1 -0
- package/src/features/sub-app/redux/constants.js +2 -0
- package/src/features/sub-app/redux/hooks.js +1 -0
- package/src/features/sub-app/redux/initialState.js +12 -0
- package/src/features/sub-app/redux/reducer.js +25 -0
- package/src/features/sub-app/redux/setSubAppState.js +43 -0
- package/src/features/sub-app/route.js +43 -0
- package/src/features/sub-app/style.less +5 -0
- package/src/features/sub-app/subAppLoading.svg +7 -0
- package/src/features/sub-app/subAppLoading2.html +83 -0
- package/src/features/sub-app/subAppLoading2.svg +7 -0
- package/src/features/sub-app/subAppLoading3.svg +7 -0
- package/src/features/sub-app/subAppLoading4.svg +7 -0
- package/src/features/sub-app/urlUtils.js +101 -0
- package/src/images/logo.png +0 -0
- package/src/index.css +3 -0
- package/src/index.js +41 -0
- package/src/muse-ext.d.ts +1 -0
- package/src/muse.d.ts +163 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/styles/global.less +9 -0
- package/src/styles/index.less +5 -0
- package/src/styles/mixins.less +0 -0
- package/src/utils.js +26 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allows a plugin to easily integrate a sub app. It does below things:
|
|
3
|
+
* - Load sub app
|
|
4
|
+
* - URL sync
|
|
5
|
+
* - SSO Check
|
|
6
|
+
* - Communication
|
|
7
|
+
*
|
|
8
|
+
* FixedSubAppContainer is usually only used programatically, not by configure.
|
|
9
|
+
*
|
|
10
|
+
* The owner should handle the size of the sub app.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React, { useEffect, useMemo, useState, useCallback, useRef } from 'react';
|
|
14
|
+
import { useLocation } from 'react-use';
|
|
15
|
+
import _ from 'lodash';
|
|
16
|
+
import history from '../../common/history';
|
|
17
|
+
import urlUtils from './urlUtils';
|
|
18
|
+
import { LoadingSkeleton, C2SProxyFailed } from './';
|
|
19
|
+
|
|
20
|
+
const debouncedPush = _.debounce(url => {
|
|
21
|
+
history.push(url);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Map a url pattern to load another muse app in iframe
|
|
25
|
+
// For example: /groot-ui => https://grootapp.muse.vip.ebay.com
|
|
26
|
+
// It will sync path, querystring, hash between the parent and iframe
|
|
27
|
+
const msgEngine = window.MUSE_GLOBAL.msgEngine;
|
|
28
|
+
|
|
29
|
+
// Fixed sub app container is usualle embeded into some part of an app but not the full page
|
|
30
|
+
export default function FixedSubAppContainer({ context = null, subApp }) {
|
|
31
|
+
const iframeWrapper = useRef();
|
|
32
|
+
const [subAppState, setSubAppState] = useState();
|
|
33
|
+
const loc = useLocation();
|
|
34
|
+
// parentFullPath: https://cloud.ebay.com/app/musemanager/ecdx => /app/musemanager/ecdx
|
|
35
|
+
const parentFullPath = loc.href.replace(loc.origin, '');
|
|
36
|
+
|
|
37
|
+
// Get the sub app's path
|
|
38
|
+
const subPath = urlUtils.getChildUrlPath(subApp);
|
|
39
|
+
console.log('subPath', subPath);
|
|
40
|
+
const currentIframe = iframeWrapper.current?.firstChild;
|
|
41
|
+
|
|
42
|
+
const subUrl = `${urlUtils.getOrigin(subApp.url)}${subPath}`;
|
|
43
|
+
// When context is changed, send message to the child app
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (iframeWrapper.current && subAppState === 'app-loaded') {
|
|
46
|
+
msgEngine?.sendToChild(
|
|
47
|
+
{
|
|
48
|
+
type: 'sub-app-context-change',
|
|
49
|
+
data: context,
|
|
50
|
+
},
|
|
51
|
+
currentIframe,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}, [context, subApp, currentIframe, subAppState]);
|
|
55
|
+
|
|
56
|
+
// When sub app is first loaded, set the sub app url
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!subPath || !iframeWrapper.current) return;
|
|
59
|
+
|
|
60
|
+
// when first load, create iframe node to load sub app
|
|
61
|
+
let iframe = iframeWrapper.current?.firstChild;
|
|
62
|
+
if (!iframe) {
|
|
63
|
+
iframe = document.createElement('iframe');
|
|
64
|
+
iframe.src = subUrl;
|
|
65
|
+
iframeWrapper.current.appendChild(iframe);
|
|
66
|
+
}
|
|
67
|
+
}, [subUrl, subPath]);
|
|
68
|
+
|
|
69
|
+
// Whenever parent path is changed, notify child iframe (sub app)
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!currentIframe || !subPath) return;
|
|
72
|
+
|
|
73
|
+
msgEngine?.sendToChild(
|
|
74
|
+
{
|
|
75
|
+
type: 'parent-route-change',
|
|
76
|
+
path: subPath,
|
|
77
|
+
url: subPath, // use url to make it compatible with old muse-react
|
|
78
|
+
},
|
|
79
|
+
currentIframe,
|
|
80
|
+
);
|
|
81
|
+
}, [subPath, currentIframe]);
|
|
82
|
+
|
|
83
|
+
// handle sub app messages: route change, app load status, etc...
|
|
84
|
+
const handleSubAppMsg = useCallback(
|
|
85
|
+
msg => {
|
|
86
|
+
if (!msg.type) return;
|
|
87
|
+
// Here msg.path is the full path of sub app
|
|
88
|
+
if (msg.type === 'child-route-change' && msg.path) {
|
|
89
|
+
const newParentFullPath = urlUtils.getParentPath(msg.path, subApp);
|
|
90
|
+
if (newParentFullPath !== parentFullPath && newParentFullPath) {
|
|
91
|
+
// Need debounce because there maybe quick redirect of the sub app which may cause endless loop
|
|
92
|
+
debouncedPush(newParentFullPath);
|
|
93
|
+
}
|
|
94
|
+
} else if (msg.type === 'app-state-change') {
|
|
95
|
+
setSubAppState(msg.state);
|
|
96
|
+
if (msg.state === 'app-loaded') {
|
|
97
|
+
// for first load, need to set context to child
|
|
98
|
+
msgEngine?.sendToChild(
|
|
99
|
+
{
|
|
100
|
+
type: 'sub-app-context-change',
|
|
101
|
+
data: context,
|
|
102
|
+
},
|
|
103
|
+
currentIframe,
|
|
104
|
+
);
|
|
105
|
+
currentIframe.museLoaded = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
[subApp, parentFullPath, setSubAppState, context, currentIframe],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const k = Math.random();
|
|
114
|
+
msgEngine?.addListener(k, handleSubAppMsg);
|
|
115
|
+
return () => msgEngine?.removeListener(k);
|
|
116
|
+
}, [handleSubAppMsg]);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div className="muse-react_sub-app-fixed-sub-app-container">
|
|
120
|
+
{!currentIframe?.museLoaded &&
|
|
121
|
+
subAppState !== 'app-loaded' &&
|
|
122
|
+
subAppState !== 'app-failed' &&
|
|
123
|
+
subAppState !== 'login-page' &&
|
|
124
|
+
subAppState !== 'check-c2s-proxy-failed' && <LoadingSkeleton state={subAppState} />}
|
|
125
|
+
|
|
126
|
+
{subAppState === 'app-failed' && (
|
|
127
|
+
<div className="sub-app-sub-app-failed">
|
|
128
|
+
Failed to start sub app {subApp.name}: /{subApp.path} => {subApp.url}.
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
{subAppState === 'check-c2s-proxy-failed' && <C2SProxyFailed />}
|
|
132
|
+
|
|
133
|
+
<div
|
|
134
|
+
ref={iframeWrapper}
|
|
135
|
+
style={{
|
|
136
|
+
visibility: ['app-loaded', 'login-page'].includes(subAppState) ? 'visible' : 'hidden',
|
|
137
|
+
}}
|
|
138
|
+
className="sub-app-iframe-wrapper"
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint jsx-a11y/heading-has-content: 0*/
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ReactComponent as LoadingSvg } from './subAppLoading2.svg';
|
|
4
|
+
const stateMap = {
|
|
5
|
+
'app-starting': 'Loading sub app...',
|
|
6
|
+
'app-loading': 'Starting sub app...',
|
|
7
|
+
'fetch-user-info': 'Fetching user info...',
|
|
8
|
+
'app-begin-login': 'Checking authentication...',
|
|
9
|
+
'check-c2s-proxy': 'Checking c2s proxy...',
|
|
10
|
+
// 'redirect-login': 'Redirecting to login page...',
|
|
11
|
+
};
|
|
12
|
+
export default function LoadingSkeleton({ state }) {
|
|
13
|
+
return (
|
|
14
|
+
<div className="sub-app-loading-skeleton">
|
|
15
|
+
<div className="sub-app-loading-center-container">
|
|
16
|
+
{LoadingSvg ? React.createElement(LoadingSvg) : <></>}
|
|
17
|
+
<label>{stateMap[state] || 'Loading sub app...'}</label>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@import '../../styles/mixins';
|
|
2
|
+
|
|
3
|
+
.sub-app-loading-skeleton {
|
|
4
|
+
position: absolute;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
z-index: 1200;
|
|
8
|
+
background: #fff;
|
|
9
|
+
display: grid;
|
|
10
|
+
place-items: center;
|
|
11
|
+
svg {
|
|
12
|
+
width: 80px;
|
|
13
|
+
height: 80px;
|
|
14
|
+
}
|
|
15
|
+
label {
|
|
16
|
+
color: #777;
|
|
17
|
+
display: inline-block;
|
|
18
|
+
margin-top: 10px;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allows a plugin to easily integrate a sub app. It does below things:
|
|
3
|
+
* - Load sub app
|
|
4
|
+
* - URL sync
|
|
5
|
+
* - SSO Check
|
|
6
|
+
* - Communication
|
|
7
|
+
* - Cache pages
|
|
8
|
+
* - Auto resize iframe
|
|
9
|
+
*
|
|
10
|
+
* For example:
|
|
11
|
+
* Sub apps configured in muse-react plugin:
|
|
12
|
+
* [
|
|
13
|
+
* {
|
|
14
|
+
* path: '/muse-apps', // no array. Support regex: /app/.* /someapp
|
|
15
|
+
* url: 'https://demo.muse.vip.ebay.com/muse-apps',
|
|
16
|
+
* persist: false,
|
|
17
|
+
* name: 'musedemo',
|
|
18
|
+
* env: 'production',
|
|
19
|
+
* }
|
|
20
|
+
* ]
|
|
21
|
+
*/
|
|
22
|
+
import React, { useEffect, useMemo, useState, useCallback, useRef } from 'react';
|
|
23
|
+
import { useLocation, useEvent } from 'react-use';
|
|
24
|
+
import _ from 'lodash';
|
|
25
|
+
import history from '../../common/history';
|
|
26
|
+
import urlUtils from './urlUtils';
|
|
27
|
+
import { LoadingSkeleton, C2SProxyFailed } from './';
|
|
28
|
+
|
|
29
|
+
const debouncedPush = _.debounce(url => {
|
|
30
|
+
history.push(url);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Map a url pattern to load another muse app in iframe
|
|
34
|
+
// For example: /groot-ui => https://grootapp.muse.vip.ebay.com
|
|
35
|
+
// It will sync path, querystring, hash between the parent and iframe
|
|
36
|
+
const msgEngine = window.MUSE_GLOBAL.msgEngine;
|
|
37
|
+
const iframeCache = {};
|
|
38
|
+
|
|
39
|
+
export default function SubAppContainer({ context = null, subApp }) {
|
|
40
|
+
const iframeWrapper = useRef();
|
|
41
|
+
const [subAppState, setSubAppState] = useState();
|
|
42
|
+
const loc = useLocation();
|
|
43
|
+
const parentFullPath = loc.href.replace(loc.origin, '');
|
|
44
|
+
const subPath = urlUtils.getChildUrlPath(subApp);
|
|
45
|
+
const currentIframe = iframeCache[subApp.url];
|
|
46
|
+
|
|
47
|
+
const subUrl = `${urlUtils.getOrigin(subApp.url)}${subPath}`;
|
|
48
|
+
// When context is changed, send message to the child app
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (iframeWrapper.current && subAppState === 'app-loaded') {
|
|
51
|
+
msgEngine?.sendToChild(
|
|
52
|
+
{
|
|
53
|
+
type: 'sub-app-context-change',
|
|
54
|
+
data: context,
|
|
55
|
+
},
|
|
56
|
+
currentIframe, //iframeWrapper.current.firstChild,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}, [context, subApp, currentIframe, subAppState]);
|
|
60
|
+
|
|
61
|
+
// When current sub app is changed
|
|
62
|
+
// If not persist, delete old app:
|
|
63
|
+
// - delete iframe
|
|
64
|
+
// - delete app state
|
|
65
|
+
// If persist:
|
|
66
|
+
// - cache iframe and state: but the post message will pause?
|
|
67
|
+
|
|
68
|
+
// while iframe is loaded, ensure it's a muse app
|
|
69
|
+
const assertMuseApp = useCallback(async ifr => {
|
|
70
|
+
try {
|
|
71
|
+
await window.MUSE_GLOBAL?.msgEngine.assertMuseApp(ifr);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.log('Not a muse app: ', err);
|
|
74
|
+
setSubAppState('not-a-muse-app');
|
|
75
|
+
}
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
// Handle subapp iframe visibility
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!subPath || !iframeWrapper.current) return;
|
|
81
|
+
|
|
82
|
+
// when first load, create iframe node to load sub app
|
|
83
|
+
let iframe = iframeCache[subApp.url];
|
|
84
|
+
const rect = iframeWrapper.current.getBoundingClientRect();
|
|
85
|
+
if (!iframe) {
|
|
86
|
+
iframe = document.createElement('iframe');
|
|
87
|
+
iframe.src = subUrl;
|
|
88
|
+
iframe.onload = () => assertMuseApp(iframe);
|
|
89
|
+
iframeCache[subApp.url] = iframe;
|
|
90
|
+
Object.assign(iframe.style, {
|
|
91
|
+
position: 'fixed',
|
|
92
|
+
top: rect.top + 'px',
|
|
93
|
+
left: rect.left + 'px',
|
|
94
|
+
width: rect.width + 'px',
|
|
95
|
+
height: rect.height + 'px',
|
|
96
|
+
border: 'none',
|
|
97
|
+
zIndex: 1000,
|
|
98
|
+
margin: 0,
|
|
99
|
+
});
|
|
100
|
+
document.body.appendChild(iframe);
|
|
101
|
+
} else {
|
|
102
|
+
iframe.style.left = rect.left + 'px';
|
|
103
|
+
}
|
|
104
|
+
}, [subUrl, assertMuseApp, subPath, subApp.url]);
|
|
105
|
+
|
|
106
|
+
// Whenever parent path is changed, notify child iframe (sub app)
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!currentIframe) return;
|
|
109
|
+
msgEngine?.sendToChild(
|
|
110
|
+
{
|
|
111
|
+
type: 'parent-route-change',
|
|
112
|
+
path: subPath,
|
|
113
|
+
},
|
|
114
|
+
currentIframe,
|
|
115
|
+
);
|
|
116
|
+
}, [subPath, currentIframe]);
|
|
117
|
+
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
return () => {
|
|
120
|
+
const iframe = iframeCache[subApp.url];
|
|
121
|
+
if (iframe) {
|
|
122
|
+
iframe.style.left = '-100000px';
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}, [subApp.url]);
|
|
126
|
+
|
|
127
|
+
const onWindowResize = useCallback(() => {
|
|
128
|
+
const iframe = iframeCache[subApp.url];
|
|
129
|
+
|
|
130
|
+
if (iframe && iframeWrapper.current) {
|
|
131
|
+
const rect = iframeWrapper.current.getBoundingClientRect();
|
|
132
|
+
Object.assign(iframe.style, {
|
|
133
|
+
width: rect.width + 'px',
|
|
134
|
+
height: rect.height + 'px',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}, [subApp.url]);
|
|
138
|
+
|
|
139
|
+
useEvent('resize', onWindowResize);
|
|
140
|
+
|
|
141
|
+
// handle sub app messages: route change, app load status, etc...
|
|
142
|
+
const handleSubAppMsg = useCallback(
|
|
143
|
+
msg => {
|
|
144
|
+
if (!msg.type) return;
|
|
145
|
+
// Here msg.path is the full path of sub app
|
|
146
|
+
if (msg.type === 'child-route-change' && msg.path) {
|
|
147
|
+
const newParentFullPath = urlUtils.getParentPath(msg.path, subApp);
|
|
148
|
+
if (newParentFullPath !== parentFullPath) {
|
|
149
|
+
// Need debounce because there maybe quick redirect of the sub app which may cause endless loop
|
|
150
|
+
debouncedPush(newParentFullPath);
|
|
151
|
+
}
|
|
152
|
+
} else if (msg.type === 'app-state-change') {
|
|
153
|
+
setSubAppState(msg.state);
|
|
154
|
+
if (msg.state === 'app-loaded') {
|
|
155
|
+
// for first load, need to set context to child
|
|
156
|
+
msgEngine?.sendToChild(
|
|
157
|
+
{
|
|
158
|
+
type: 'sub-app-context-change',
|
|
159
|
+
data: context,
|
|
160
|
+
},
|
|
161
|
+
currentIframe,
|
|
162
|
+
);
|
|
163
|
+
currentIframe.museLoaded = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
[subApp, parentFullPath, setSubAppState, context, currentIframe],
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
const k = Math.random();
|
|
172
|
+
msgEngine?.addListener(k, handleSubAppMsg);
|
|
173
|
+
return () => msgEngine?.removeListener(k);
|
|
174
|
+
}, [handleSubAppMsg]);
|
|
175
|
+
|
|
176
|
+
if (!subPath) {
|
|
177
|
+
return 'Error: can not detect a sub app. Are you using sub app container correctly?';
|
|
178
|
+
}
|
|
179
|
+
return (
|
|
180
|
+
<div className="muse-react_sub-app-sub-app-container">
|
|
181
|
+
{!currentIframe?.museLoaded &&
|
|
182
|
+
subAppState !== 'app-loaded' &&
|
|
183
|
+
subAppState !== 'app-failed' &&
|
|
184
|
+
subAppState !== 'login-page' &&
|
|
185
|
+
subAppState !== 'check-c2s-proxy-failed' && <LoadingSkeleton state={subAppState} />}
|
|
186
|
+
|
|
187
|
+
{subAppState === 'app-failed' && (
|
|
188
|
+
<div className="sub-app-sub-app-failed">
|
|
189
|
+
Failed to start sub app {subApp.name}: /{subApp.path} => {subApp.url}.
|
|
190
|
+
</div>
|
|
191
|
+
)}
|
|
192
|
+
{subAppState === 'check-c2s-proxy-failed' && <C2SProxyFailed />}
|
|
193
|
+
|
|
194
|
+
<div
|
|
195
|
+
ref={iframeWrapper}
|
|
196
|
+
style={{
|
|
197
|
+
visibility: ['app-loaded', 'login-page'].includes(subAppState) ? 'visible' : 'hidden',
|
|
198
|
+
}}
|
|
199
|
+
className="sub-app-iframe-wrapper"
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@import '../../styles/mixins';
|
|
2
|
+
|
|
3
|
+
.muse-react_sub-app-sub-app-container {
|
|
4
|
+
height: 100vh;
|
|
5
|
+
position: relative;
|
|
6
|
+
.sub-app-sub-app-failed {
|
|
7
|
+
color: red;
|
|
8
|
+
padding: 16px;
|
|
9
|
+
}
|
|
10
|
+
.sub-app-iframe-wrapper {
|
|
11
|
+
position: absolute;
|
|
12
|
+
top: 0;
|
|
13
|
+
height: 100%;
|
|
14
|
+
width: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
iframe {
|
|
18
|
+
margin: 0;
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 100%;
|
|
21
|
+
border: none;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.muse-layout_home-main-layout-page-container > .sub-app-sub-app-container {
|
|
26
|
+
margin: -30px;
|
|
27
|
+
width: ~'calc(100% + 60px)';
|
|
28
|
+
height: ~'calc(100vh - 50px)';
|
|
29
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
function useParentRouteChange() {
|
|
5
|
+
const navigate = useNavigate();
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
window.MUSE_GLOBAL?.msgEngine?.addListener('muse-react_history', msg => {
|
|
9
|
+
// Parent may notify the child iframe to update url: implemented in SubAppContainer
|
|
10
|
+
// muse-boot will notify parent when child iframe url changed
|
|
11
|
+
const currentPath = document.location.href.replace(document.location.origin, '');
|
|
12
|
+
if (msg.type === 'parent-route-change' && msg.path !== currentPath) {
|
|
13
|
+
console.log('parent-route-change: ', msg.path);
|
|
14
|
+
navigate(msg.path);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return () => {
|
|
18
|
+
window.MUSE_GLOBAL?.msgEngine?.removeListener('muse-react_history');
|
|
19
|
+
};
|
|
20
|
+
}, [navigate]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default useParentRouteChange;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as SubAppContainer } from './SubAppContainer';
|
|
2
|
+
export { default as FixedSubAppContainer } from './FixedSubAppContainer';
|
|
3
|
+
export { default as LoadingSkeleton } from './LoadingSkeleton';
|
|
4
|
+
export { default as SubAppContext } from './SubAppContext';
|
|
5
|
+
export { default as C2SProxyFailed } from './C2SProxyFailed';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { setSubAppState } from './setSubAppState';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSetSubAppState } from './setSubAppState';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Initial state is the place you define all initial values for the Redux store of the feature.
|
|
2
|
+
// In the 'standard' way, initialState is defined in reducers: http://redux.js.org/docs/basics/Reducers.html
|
|
3
|
+
// But when application grows, there will be multiple reducers files, it's not intuitive what data is managed by the whole store.
|
|
4
|
+
// So Rekit extracts the initial state definition into a separate module so that you can have
|
|
5
|
+
// a quick view about what data is used for the feature, at any time.
|
|
6
|
+
|
|
7
|
+
// NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions.
|
|
8
|
+
const initialState = {
|
|
9
|
+
subAppState: {},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default initialState;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// This is the root reducer of the feature. It is used for:
|
|
2
|
+
// 1. Load reducers from each action in the feature and process them one by one.
|
|
3
|
+
// Note that this part of code is mainly maintained by Rekit, you usually don't need to edit them.
|
|
4
|
+
// 2. Write cross-topic reducers. If a reducer is not bound to some specific action.
|
|
5
|
+
// Then it could be written here.
|
|
6
|
+
// Learn more from the introduction of this approach:
|
|
7
|
+
// https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da.
|
|
8
|
+
|
|
9
|
+
import initialState from './initialState';
|
|
10
|
+
import { reducer as setSubAppStateReducer } from './setSubAppState';
|
|
11
|
+
|
|
12
|
+
const reducers = [
|
|
13
|
+
setSubAppStateReducer,
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export default function reducer(state = initialState, action) {
|
|
17
|
+
let newState;
|
|
18
|
+
switch (action.type) {
|
|
19
|
+
// Handle cross-topic actions here
|
|
20
|
+
default:
|
|
21
|
+
newState = state;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
return reducers.reduce((s, r) => r(s, action), newState);
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
import { SUB_APP_SET_SUB_APP_STATE, SUB_APP_CLEAR_SUB_APP_STATE } from './constants';
|
|
4
|
+
|
|
5
|
+
export function setSubAppState(state) {
|
|
6
|
+
return {
|
|
7
|
+
type: SUB_APP_SET_SUB_APP_STATE,
|
|
8
|
+
data: state,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function clearSubAppState() {
|
|
13
|
+
return {
|
|
14
|
+
type: SUB_APP_CLEAR_SUB_APP_STATE,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useSetSubAppState() {
|
|
19
|
+
const dispatch = useDispatch();
|
|
20
|
+
const subAppState = useSelector(state => state.subApp.subAppState);
|
|
21
|
+
const boundAction = useCallback((...params) => dispatch(setSubAppState(...params)), [dispatch]);
|
|
22
|
+
const boundClearSubAppState = useCallback((...params) => dispatch(clearSubAppState(...params)), [
|
|
23
|
+
dispatch,
|
|
24
|
+
]);
|
|
25
|
+
return { subAppState, setSubAppState: boundAction, clearSubAppState: boundClearSubAppState };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function reducer(state, action) {
|
|
29
|
+
switch (action.type) {
|
|
30
|
+
case SUB_APP_SET_SUB_APP_STATE:
|
|
31
|
+
return {
|
|
32
|
+
...state,
|
|
33
|
+
subAppState: action.data,
|
|
34
|
+
};
|
|
35
|
+
case SUB_APP_CLEAR_SUB_APP_STATE:
|
|
36
|
+
return {
|
|
37
|
+
...state,
|
|
38
|
+
subAppState: {},
|
|
39
|
+
};
|
|
40
|
+
default:
|
|
41
|
+
return state;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SubAppContainer } from './';
|
|
3
|
+
|
|
4
|
+
// Get sub app route defined in muse-react plugin
|
|
5
|
+
const subAppsRoute = [];
|
|
6
|
+
const pMuseLibReact = window.MUSE_GLOBAL?.plugins?.find(p => p.name === '@ebay/muse-lib-react');
|
|
7
|
+
// pMuseLibReact.config = {
|
|
8
|
+
// subApps: [
|
|
9
|
+
// {
|
|
10
|
+
// mountPoint: 'default',
|
|
11
|
+
// name: 'musedemo',
|
|
12
|
+
// path: '/demo',
|
|
13
|
+
// // persist: true,
|
|
14
|
+
// url: 'https://demo.muse.qa.ebay.com',
|
|
15
|
+
// // url: 'http://local.cloud.ebay.com:3031',
|
|
16
|
+
// // url: 'https://sam.muse.vip.ebay.com',
|
|
17
|
+
// // url: 'https://besconsole.muse.qa.ebay.com',
|
|
18
|
+
// },
|
|
19
|
+
// ],
|
|
20
|
+
// };
|
|
21
|
+
|
|
22
|
+
pMuseLibReact?.subApps
|
|
23
|
+
?.filter(s => s.mountPoint === 'default' || !s.mountPoint)
|
|
24
|
+
?.forEach(subApp => {
|
|
25
|
+
// console.log('pushing sub app route: ', subApp);
|
|
26
|
+
subAppsRoute.push({
|
|
27
|
+
path: subApp.path + '/*',
|
|
28
|
+
component: () => (
|
|
29
|
+
<SubAppContainer
|
|
30
|
+
key={subApp.url} // ensure different sub apps have different iframes
|
|
31
|
+
subApps={pMuseLibReact?.config?.subApps || []}
|
|
32
|
+
subApp={subApp}
|
|
33
|
+
/>
|
|
34
|
+
),
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const exportedRoute = {
|
|
39
|
+
path: 'sub-app',
|
|
40
|
+
childRoutes: [...subAppsRoute],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default exportedRoute;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(255, 255, 255); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
|
3
|
+
<g transform="translate(50 50)"> <g transform="translate(-17 -17) scale(0.5)"> <g>
|
|
4
|
+
<animateTransform attributeName="transform" type="rotate" values="0;45" keyTimes="0;1" dur="0.2s" begin="0s" repeatCount="indefinite"></animateTransform><path d="M37.3496987939662 -7 L47.3496987939662 -7 L47.3496987939662 7 L37.3496987939662 7 A38 38 0 0 1 31.359972760794346 21.46047782418268 L31.359972760794346 21.46047782418268 L38.431040572659825 28.531545636048154 L28.531545636048154 38.431040572659825 L21.46047782418268 31.359972760794346 A38 38 0 0 1 7.0000000000000036 37.3496987939662 L7.0000000000000036 37.3496987939662 L7.000000000000004 47.3496987939662 L-6.999999999999999 47.3496987939662 L-7 37.3496987939662 A38 38 0 0 1 -21.46047782418268 31.35997276079435 L-21.46047782418268 31.35997276079435 L-28.531545636048154 38.431040572659825 L-38.43104057265982 28.531545636048158 L-31.359972760794346 21.460477824182682 A38 38 0 0 1 -37.3496987939662 7.000000000000007 L-37.3496987939662 7.000000000000007 L-47.3496987939662 7.000000000000008 L-47.3496987939662 -6.9999999999999964 L-37.3496987939662 -6.999999999999997 A38 38 0 0 1 -31.35997276079435 -21.460477824182675 L-31.35997276079435 -21.460477824182675 L-38.431040572659825 -28.531545636048147 L-28.53154563604818 -38.4310405726598 L-21.4604778241827 -31.35997276079433 A38 38 0 0 1 -6.999999999999992 -37.3496987939662 L-6.999999999999992 -37.3496987939662 L-6.999999999999994 -47.3496987939662 L6.999999999999977 -47.3496987939662 L6.999999999999979 -37.3496987939662 A38 38 0 0 1 21.460477824182686 -31.359972760794342 L21.460477824182686 -31.359972760794342 L28.531545636048158 -38.43104057265982 L38.4310405726598 -28.53154563604818 L31.35997276079433 -21.4604778241827 A38 38 0 0 1 37.3496987939662 -6.999999999999995 M0 -23A23 23 0 1 0 0 23 A23 23 0 1 0 0 -23" fill="#abbd81"></path></g></g> <g transform="translate(0 22) scale(0.4)"> <g>
|
|
5
|
+
<animateTransform attributeName="transform" type="rotate" values="45;0" keyTimes="0;1" dur="0.2s" begin="-0.1s" repeatCount="indefinite"></animateTransform><path d="M37.3496987939662 -7 L47.3496987939662 -7 L47.3496987939662 7 L37.3496987939662 7 A38 38 0 0 1 31.359972760794346 21.46047782418268 L31.359972760794346 21.46047782418268 L38.431040572659825 28.531545636048154 L28.531545636048154 38.431040572659825 L21.46047782418268 31.359972760794346 A38 38 0 0 1 7.0000000000000036 37.3496987939662 L7.0000000000000036 37.3496987939662 L7.000000000000004 47.3496987939662 L-6.999999999999999 47.3496987939662 L-7 37.3496987939662 A38 38 0 0 1 -21.46047782418268 31.35997276079435 L-21.46047782418268 31.35997276079435 L-28.531545636048154 38.431040572659825 L-38.43104057265982 28.531545636048158 L-31.359972760794346 21.460477824182682 A38 38 0 0 1 -37.3496987939662 7.000000000000007 L-37.3496987939662 7.000000000000007 L-47.3496987939662 7.000000000000008 L-47.3496987939662 -6.9999999999999964 L-37.3496987939662 -6.999999999999997 A38 38 0 0 1 -31.35997276079435 -21.460477824182675 L-31.35997276079435 -21.460477824182675 L-38.431040572659825 -28.531545636048147 L-28.53154563604818 -38.4310405726598 L-21.4604778241827 -31.35997276079433 A38 38 0 0 1 -6.999999999999992 -37.3496987939662 L-6.999999999999992 -37.3496987939662 L-6.999999999999994 -47.3496987939662 L6.999999999999977 -47.3496987939662 L6.999999999999979 -37.3496987939662 A38 38 0 0 1 21.460477824182686 -31.359972760794342 L21.460477824182686 -31.359972760794342 L28.531545636048158 -38.43104057265982 L38.4310405726598 -28.53154563604818 L31.35997276079433 -21.4604778241827 A38 38 0 0 1 37.3496987939662 -6.999999999999995 M0 -23A23 23 0 1 0 0 23 A23 23 0 1 0 0 -23" fill="#e15b64"></path></g></g> <g transform="translate(28 4) scale(0.3)"> <g>
|
|
6
|
+
<animateTransform attributeName="transform" type="rotate" values="0;45" keyTimes="0;1" dur="0.2s" begin="-0.1s" repeatCount="indefinite"></animateTransform><path d="M37.3496987939662 -7 L47.3496987939662 -7 L47.3496987939662 7 L37.3496987939662 7 A38 38 0 0 1 31.359972760794346 21.46047782418268 L31.359972760794346 21.46047782418268 L38.431040572659825 28.531545636048154 L28.531545636048154 38.431040572659825 L21.46047782418268 31.359972760794346 A38 38 0 0 1 7.0000000000000036 37.3496987939662 L7.0000000000000036 37.3496987939662 L7.000000000000004 47.3496987939662 L-6.999999999999999 47.3496987939662 L-7 37.3496987939662 A38 38 0 0 1 -21.46047782418268 31.35997276079435 L-21.46047782418268 31.35997276079435 L-28.531545636048154 38.431040572659825 L-38.43104057265982 28.531545636048158 L-31.359972760794346 21.460477824182682 A38 38 0 0 1 -37.3496987939662 7.000000000000007 L-37.3496987939662 7.000000000000007 L-47.3496987939662 7.000000000000008 L-47.3496987939662 -6.9999999999999964 L-37.3496987939662 -6.999999999999997 A38 38 0 0 1 -31.35997276079435 -21.460477824182675 L-31.35997276079435 -21.460477824182675 L-38.431040572659825 -28.531545636048147 L-28.53154563604818 -38.4310405726598 L-21.4604778241827 -31.35997276079433 A38 38 0 0 1 -6.999999999999992 -37.3496987939662 L-6.999999999999992 -37.3496987939662 L-6.999999999999994 -47.3496987939662 L6.999999999999977 -47.3496987939662 L6.999999999999979 -37.3496987939662 A38 38 0 0 1 21.460477824182686 -31.359972760794342 L21.460477824182686 -31.359972760794342 L28.531545636048158 -38.43104057265982 L38.4310405726598 -28.53154563604818 L31.35997276079433 -21.4604778241827 A38 38 0 0 1 37.3496987939662 -6.999999999999995 M0 -23A23 23 0 1 0 0 23 A23 23 0 1 0 0 -23" fill="#f8b26a"></path></g></g></g>
|
|
7
|
+
<!-- [ldio] generated by https://loading.io/ --></svg>
|