cozy-external-bridge 0.8.0 → 0.9.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 +15 -1
- package/dist/container/constants.js +8 -0
- package/dist/container/helpers.js +13 -1
- package/dist/container/hooks.js +53 -0
- package/dist/container/hooks.spec.ts +64 -0
- package/dist/container/index.js +13 -55
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ This library allows communication between a container app and an app embedded in
|
|
|
4
4
|
|
|
5
5
|
## For container app
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Add the `useExternalBridge` hook and it will :
|
|
8
8
|
|
|
9
9
|
- allow history syncing
|
|
10
10
|
- expose a `getContacts` method
|
|
@@ -18,6 +18,20 @@ const App = () => {
|
|
|
18
18
|
}
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
You also need to manage routing if embedded app use history syncing :
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
<HashRouter>
|
|
25
|
+
<Routes>
|
|
26
|
+
<Route element={<AppLayout />}>
|
|
27
|
+
<Route path="/" element={<OutletWrapper Component={App} />}>
|
|
28
|
+
<Route path="bridge/*" />
|
|
29
|
+
</Route>
|
|
30
|
+
</Route>
|
|
31
|
+
</Routes>
|
|
32
|
+
</HashRouter>
|
|
33
|
+
```
|
|
34
|
+
|
|
21
35
|
## For embedded app
|
|
22
36
|
|
|
23
37
|
Import `dist/embedded/bundle.js` script. It exposes method in `window._cozyBridge`.
|
|
@@ -3,7 +3,19 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.handleRequestParentOrigin = exports.extractUrl = void 0;
|
|
6
|
+
exports.handleRequestParentOrigin = exports.getIframe = exports.extractUrl = void 0;
|
|
7
|
+
|
|
8
|
+
var getIframe = function getIframe() {
|
|
9
|
+
var iframe = document.getElementById('embeddedApp');
|
|
10
|
+
|
|
11
|
+
if (iframe === null) {
|
|
12
|
+
throw new Error('No iframe found');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return iframe;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
exports.getIframe = getIframe;
|
|
7
19
|
|
|
8
20
|
var extractUrl = function extractUrl(url) {
|
|
9
21
|
if (url.startsWith('http')) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useParentOrigin = exports.useInitialRedirection = void 0;
|
|
7
|
+
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
|
|
10
|
+
var _reactRouterDom = require("react-router-dom");
|
|
11
|
+
|
|
12
|
+
var _cozyClient = require("cozy-client");
|
|
13
|
+
|
|
14
|
+
var _constants = require("./constants");
|
|
15
|
+
|
|
16
|
+
var _helpers = require("./helpers");
|
|
17
|
+
|
|
18
|
+
// Initial redirection is necessary when we load a page from a direct link
|
|
19
|
+
// to load the iframe to the direct link
|
|
20
|
+
var useInitialRedirection = function useInitialRedirection() {
|
|
21
|
+
var location = (0, _reactRouterDom.useLocation)();
|
|
22
|
+
(0, _react.useEffect)(function () {
|
|
23
|
+
if (location.pathname.startsWith(_constants.BRIDGE_ROUTE_PREFIX)) {
|
|
24
|
+
var iframe = (0, _helpers.getIframe)();
|
|
25
|
+
var destUrl = new URL(iframe.src);
|
|
26
|
+
destUrl.pathname = location.pathname.replace(_constants.BRIDGE_ROUTE_PREFIX, '');
|
|
27
|
+
destUrl.hash = location.hash;
|
|
28
|
+
destUrl.search = location.search;
|
|
29
|
+
iframe.src = destUrl.toString();
|
|
30
|
+
}
|
|
31
|
+
}, []);
|
|
32
|
+
}; // Allow the iframe to request the origin of the parent window
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
exports.useInitialRedirection = useInitialRedirection;
|
|
36
|
+
|
|
37
|
+
var useParentOrigin = function useParentOrigin(origin) {
|
|
38
|
+
var client = (0, _cozyClient.useClient)();
|
|
39
|
+
(0, _react.useEffect)(function () {
|
|
40
|
+
if (!client) return;
|
|
41
|
+
|
|
42
|
+
var requestParentOriginHandler = function requestParentOriginHandler(event) {
|
|
43
|
+
return (0, _helpers.handleRequestParentOrigin)(event, origin);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
window.addEventListener('message', requestParentOriginHandler);
|
|
47
|
+
return function () {
|
|
48
|
+
window.removeEventListener('message', requestParentOriginHandler);
|
|
49
|
+
};
|
|
50
|
+
}, [client, origin]);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
exports.useParentOrigin = useParentOrigin;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks'
|
|
2
|
+
import { useLocation } from 'react-router-dom'
|
|
3
|
+
|
|
4
|
+
import { getIframe } from './helpers'
|
|
5
|
+
import { useInitialRedirection } from './hooks'
|
|
6
|
+
|
|
7
|
+
jest.mock('react-router-dom', () => ({
|
|
8
|
+
useLocation: jest.fn()
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
jest.mock('./helpers', () => ({
|
|
12
|
+
getIframe: jest.fn()
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
const mockedUseLocation = useLocation as jest.Mock
|
|
16
|
+
const mockedGetIframe = getIframe as jest.Mock
|
|
17
|
+
|
|
18
|
+
describe('useInitialRedirection', () => {
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
jest.clearAllMocks()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should not modify iframe.src when pathname does not start with bridge prefix', () => {
|
|
24
|
+
const mockLocation = { pathname: 'assistant', hash: '', search: '' }
|
|
25
|
+
mockedUseLocation.mockReturnValue(mockLocation)
|
|
26
|
+
|
|
27
|
+
const mockIframe = { src: 'https://alice-chat.mycozy.cloud' }
|
|
28
|
+
mockedGetIframe.mockReturnValue(mockIframe)
|
|
29
|
+
|
|
30
|
+
renderHook(() => useInitialRedirection())
|
|
31
|
+
|
|
32
|
+
expect(mockIframe.src).toBe('https://alice-chat.mycozy.cloud')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should modify iframe.src when pathname start with bridge prefix', () => {
|
|
36
|
+
const mockLocation = { pathname: '/bridge/welcome', hash: '', search: '' }
|
|
37
|
+
mockedUseLocation.mockReturnValue(mockLocation)
|
|
38
|
+
|
|
39
|
+
const mockIframe = { src: 'https://alice-chat.mycozy.cloud' }
|
|
40
|
+
mockedGetIframe.mockReturnValue(mockIframe)
|
|
41
|
+
|
|
42
|
+
renderHook(() => useInitialRedirection())
|
|
43
|
+
|
|
44
|
+
expect(mockIframe.src).toBe('https://alice-chat.mycozy.cloud/welcome')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should modify iframe.src when pathname start with bridge prefix and allow bridge keyword in iframe url', () => {
|
|
48
|
+
const mockLocation = {
|
|
49
|
+
pathname: '/bridge/welcome/bridge',
|
|
50
|
+
hash: '',
|
|
51
|
+
search: ''
|
|
52
|
+
}
|
|
53
|
+
mockedUseLocation.mockReturnValue(mockLocation)
|
|
54
|
+
|
|
55
|
+
const mockIframe = { src: 'https://alice-chat.mycozy.cloud' }
|
|
56
|
+
mockedGetIframe.mockReturnValue(mockIframe)
|
|
57
|
+
|
|
58
|
+
renderHook(() => useInitialRedirection())
|
|
59
|
+
|
|
60
|
+
expect(mockIframe.src).toBe(
|
|
61
|
+
'https://alice-chat.mycozy.cloud/welcome/bridge'
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
})
|
package/dist/container/index.js
CHANGED
|
@@ -21,68 +21,25 @@ var _cozyClient = require("cozy-client");
|
|
|
21
21
|
|
|
22
22
|
var _cozyFlags = _interopRequireDefault(require("cozy-flags"));
|
|
23
23
|
|
|
24
|
-
var
|
|
25
|
-
|
|
26
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
27
|
-
|
|
28
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
29
|
-
|
|
30
|
-
/* eslint-disable no-console */
|
|
31
|
-
var getIframe = function getIframe() {
|
|
32
|
-
var iframe = document.getElementById('embeddedApp');
|
|
24
|
+
var _cozyMinilog = _interopRequireDefault(require("cozy-minilog"));
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
throw new Error('No iframe found');
|
|
36
|
-
}
|
|
26
|
+
var _constants = require("./constants");
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
}; // Initial redirection is necessary when we load a page from a direct link
|
|
40
|
-
// to load the iframe to the direct link
|
|
28
|
+
var _helpers = require("./helpers");
|
|
41
29
|
|
|
30
|
+
var _hooks = require("./hooks");
|
|
42
31
|
|
|
43
|
-
var
|
|
44
|
-
var location = (0, _reactRouterDom.useLocation)();
|
|
45
|
-
(0, _react.useEffect)(function () {
|
|
46
|
-
// If current url is root url, do nothing
|
|
47
|
-
if (location.pathname === '/' && location.hash === '' && location.search === '') {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
var iframe = getIframe();
|
|
52
|
-
var destUrl = new URL(iframe.src);
|
|
53
|
-
destUrl.pathname = location.pathname;
|
|
54
|
-
destUrl.hash = location.hash;
|
|
55
|
-
destUrl.search = location.search;
|
|
56
|
-
var currentIframeUrl = new URL(iframe.src);
|
|
57
|
-
|
|
58
|
-
if (destUrl.toString() !== currentIframeUrl.toString()) {
|
|
59
|
-
iframe.src = destUrl.toString();
|
|
60
|
-
}
|
|
61
|
-
}, []);
|
|
62
|
-
}; // Allow the iframe to request the origin of the parent window
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
var useParentOrigin = function useParentOrigin(origin) {
|
|
66
|
-
var client = (0, _cozyClient.useClient)();
|
|
67
|
-
(0, _react.useEffect)(function () {
|
|
68
|
-
if (!client) return;
|
|
32
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
69
33
|
|
|
70
|
-
|
|
71
|
-
return (0, _helpers.handleRequestParentOrigin)(event, origin);
|
|
72
|
-
};
|
|
34
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
73
35
|
|
|
74
|
-
|
|
75
|
-
return function () {
|
|
76
|
-
window.removeEventListener('message', requestParentOriginHandler);
|
|
77
|
-
};
|
|
78
|
-
}, [client, origin]);
|
|
79
|
-
};
|
|
36
|
+
var log = (0, _cozyMinilog.default)('🌉 [Container bridge]');
|
|
80
37
|
|
|
81
38
|
var useExternalBridge = function useExternalBridge(origin) {
|
|
82
39
|
var client = (0, _cozyClient.useClient)();
|
|
83
40
|
var navigate = (0, _reactRouterDom.useNavigate)();
|
|
84
|
-
useInitialRedirection();
|
|
85
|
-
useParentOrigin(origin);
|
|
41
|
+
(0, _hooks.useInitialRedirection)();
|
|
42
|
+
(0, _hooks.useParentOrigin)(origin);
|
|
86
43
|
(0, _react.useEffect)(function () {
|
|
87
44
|
if (!client) return;
|
|
88
45
|
var exposedMethods = {
|
|
@@ -123,13 +80,14 @@ var useExternalBridge = function useExternalBridge(origin) {
|
|
|
123
80
|
// Proof of concepts of Twake <-> Cozy URL synchronization
|
|
124
81
|
updateHistory: function updateHistory(newUrl) {
|
|
125
82
|
var url = (0, _helpers.extractUrl)(newUrl);
|
|
126
|
-
|
|
127
|
-
navigate(url, {
|
|
83
|
+
log.debug("Navigating to ".concat(url, " because received ").concat(newUrl, " from embedded app"));
|
|
84
|
+
navigate(_constants.BRIDGE_ROUTE_PREFIX + url, {
|
|
128
85
|
replace: true
|
|
129
86
|
});
|
|
130
87
|
}
|
|
131
88
|
};
|
|
132
|
-
Comlink.expose(exposedMethods, Comlink.windowEndpoint(getIframe().contentWindow, self, origin));
|
|
89
|
+
Comlink.expose(exposedMethods, Comlink.windowEndpoint((0, _helpers.getIframe)().contentWindow, self, origin));
|
|
90
|
+
log.debug('Bridge initialized');
|
|
133
91
|
}, [navigate, client, origin]);
|
|
134
92
|
};
|
|
135
93
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-external-bridge",
|
|
3
3
|
"description": "Library allows communication between a container app and an app embedded in an iframe",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cozy Cloud",
|
|
7
7
|
"url": "https://github.com/cozy"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"peerDependencies": {
|
|
16
16
|
"cozy-client": ">=54.0.0",
|
|
17
17
|
"cozy-flags": ">=4.7.0",
|
|
18
|
+
"cozy-minilog": ">=3.10.0",
|
|
18
19
|
"react": ">=16.12.0",
|
|
19
20
|
"react-dom": ">=16.12.0",
|
|
20
21
|
"react-router-dom": ">=6.14.2"
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
"babel-preset-cozy-app": "^2.8.1",
|
|
32
33
|
"cozy-client": "^54.0.0",
|
|
33
34
|
"cozy-flags": "^4.7.0",
|
|
35
|
+
"cozy-minilog": "^3.10.0",
|
|
34
36
|
"jest": "26.6.3",
|
|
35
37
|
"react": "16.12.0",
|
|
36
38
|
"react-dom": "16.12.0",
|
|
@@ -66,5 +68,5 @@
|
|
|
66
68
|
"lint": "cd ../.. && yarn eslint --ext js,jsx,ts packages/cozy-external-bridge"
|
|
67
69
|
},
|
|
68
70
|
"types": "dist/index.d.ts",
|
|
69
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "32b4cd0c82173f5b7703066949011d74810f273e"
|
|
70
72
|
}
|