cozy-bar 36.1.3 → 36.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/README.md CHANGED
@@ -119,6 +119,50 @@ To hide the AI assistant button in the search bar (e.g. when the assistant is al
119
119
  />
120
120
  ```
121
121
 
122
+ ## Standalone bar
123
+
124
+ `dist/standalone.js` is a self-contained bundle (React and every dependency included) for apps that are not Cozy React apps. Load it with a script tag, it exposes `window.TwakeBar`:
125
+
126
+ ```html
127
+ <script src="standalone.js"></script>
128
+ <script>
129
+ window.TwakeBar.mount({
130
+ appSlug: 'mail',
131
+ appName: 'Twake Mail',
132
+ appIcon: '/icon.svg', // optional, served by the app
133
+ appTextIcon: '/icon-text.svg', // optional, served by the app
134
+ locale: 'fr', // optional, 'en' by default
135
+ theme: 'dark', // optional, the device theme by default
136
+ onLogOut: () => myApp.logOut(), // required unless public
137
+ idToken: '…', // optional, the OIDC id token of the user
138
+ cozyURL: 'https://alice.mycozy.cloud' // required with idToken
139
+ })
140
+
141
+ // When the app logs in after mount(), or renews its id token
142
+ window.TwakeBar.setCredentials({
143
+ idToken: '…',
144
+ cozyURL: 'https://alice.mycozy.cloud'
145
+ })
146
+
147
+ // Whenever the app language or theme changes
148
+ window.TwakeBar.setLocale('en')
149
+ window.TwakeBar.setTheme('light')
150
+ </script>
151
+ ```
152
+
153
+ - Every call returns a promise, rejected when the bar cannot load or when the call fails, e.g. `mount()` with an invalid config.
154
+ - `mount(config)` prepends a `#cozy-bar` element to the body. The app must reserve its space: `3rem` height, full width.
155
+ - `public: true` displays the bar logged out. Otherwise the bar waits for credentials (skeleton avatar) and falls back to logged out after 30 seconds.
156
+ - The bar exchanges the id token for a token of the user's Cozy with the cozy-stack `POST /auth/token_exchange` (`exchange_type: 'app'`). The Cozy must accept the id token audience in its `app_token_exchange` config, and the token gets the permissions of the Cozy app it is linked to.
157
+ - `mount()` with `idToken` and `cozyURL` exchanges the token right away, and its promise resolves once the bar is logged in. `setCredentials()` does the same after `mount()`: it creates the Cozy client and displays the logged in bar. Calling it again, e.g. with a renewed id token, only updates the token of the existing client. If the first exchange fails, the promise is rejected and the bar is displayed logged out.
158
+ - `unmount()` removes the bar, e.g. to give the page its own header back when `mount()` failed.
159
+ - `setLocale(locale)` re-renders the mounted bar in another language, without recreating the Cozy client. It is ignored before `mount()`. Supported locales are `de`, `en`, `es`, `fr`, `it`, `ru` and `vi`, others fall back to `en`.
160
+ - `setTheme(theme)` does the same with the theme, `light` or `dark`. Locale and theme come from the app, not from `io.cozy.settings`, so the bar always matches it.
161
+ - The log out item of the user menu calls `onLogOut`. It is required unless the page is public, since the app owns the session: `mount()` throws without it.
162
+ - The bar renders in a shadow root so its styles do not leak into the page and the page styles do not leak into the bar. The only globals added to the document head are the cozy-ui theme variables, scoped to hidden `.TwakeTheme--light|dark` nodes, and the Inter font stylesheet of the stack.
163
+
164
+ Build it with `yarn build:standalone`, it is also built by `yarn build`.
165
+
122
166
  ## License
123
167
 
124
168
  Cozy Bar is distributed under the MIT license.
@@ -29,13 +29,6 @@ var _cozyFlags = _interopRequireDefault(require("cozy-flags"));
29
29
 
30
30
  var _Typography = _interopRequireDefault(require("cozy-ui/transpiled/react/Typography"));
31
31
 
32
- var styles = {
33
- "apps-menu-grid": "apps-menu-grid",
34
- "apps-menu-grid-item-wrapper": "apps-menu-grid-item-wrapper",
35
- "apps-menu-grid-item": "apps-menu-grid-item",
36
- "apps-menu-grid-item-icon": "apps-menu-grid-item-icon"
37
- };
38
-
39
32
  var AppsMenuContent = function AppsMenuContent(_ref) {
40
33
  var isFetchingApps = _ref.isFetchingApps,
41
34
  apps = _ref.apps,
@@ -55,7 +48,7 @@ var AppsMenuContent = function AppsMenuContent(_ref) {
55
48
 
56
49
  if (isFetchingApps) {
57
50
  return /*#__PURE__*/_react.default.createElement("div", {
58
- className: styles['apps-menu-grid']
51
+ className: "apps-menu-grid"
59
52
  }, /*#__PURE__*/_react.default.createElement(_AppItemPlaceholder.default, {
60
53
  key: "1"
61
54
  }), /*#__PURE__*/_react.default.createElement(_AppItemPlaceholder.default, {
@@ -72,7 +65,7 @@ var AppsMenuContent = function AppsMenuContent(_ref) {
72
65
  var sortedApps = (0, _cozyFlags.default)('apps.sort') ? (0, _applications.sortApplicationsList)(displayedApps, (0, _cozyFlags.default)('apps.sort')) : displayedApps;
73
66
  var entrypoints = (0, _helper.getEntrypoints)(apps);
74
67
  return /*#__PURE__*/_react.default.createElement("div", {
75
- className: styles['apps-menu-grid']
68
+ className: "apps-menu-grid"
76
69
  }, sortedApps.map(function (app) {
77
70
  return /*#__PURE__*/_react.default.createElement(_AppItem.default, {
78
71
  key: app.slug,
@@ -29,12 +29,6 @@ var _AppIcon = _interopRequireDefault(require("cozy-ui-plus/dist/AppIcon"));
29
29
 
30
30
  var _AppLinker = _interopRequireDefault(require("cozy-ui-plus/dist/AppLinker"));
31
31
 
32
- var styles = {
33
- "apps-menu-grid": "apps-menu-grid",
34
- "apps-menu-grid-item-wrapper": "apps-menu-grid-item-wrapper",
35
- "apps-menu-grid-item": "apps-menu-grid-item",
36
- "apps-menu-grid-item-icon": "apps-menu-grid-item-icon"
37
- };
38
32
  var useStyles = (0, _styles.makeStyles)(function () {
39
33
  return {
40
34
  text: {
@@ -71,11 +65,11 @@ var AppItem = function AppItem(_ref) {
71
65
  href: href,
72
66
  title: appName,
73
67
  onClick: onClick,
74
- className: (0, _classnames.default)(styles['apps-menu-grid-item-wrapper'], 'u-bdrs-5'),
68
+ className: (0, _classnames.default)('apps-menu-grid-item-wrapper', 'u-bdrs-5'),
75
69
  label: /*#__PURE__*/_react.default.createElement("div", {
76
- className: styles['apps-menu-grid-item']
70
+ className: "apps-menu-grid-item"
77
71
  }, /*#__PURE__*/_react.default.createElement("div", {
78
- className: styles['apps-menu-grid-item-icon']
72
+ className: "apps-menu-grid-item-icon"
79
73
  }, /*#__PURE__*/_react.default.createElement(_AppIcon.default, (0, _extends2.default)({
80
74
  app: app,
81
75
  key: app.slug
@@ -21,12 +21,6 @@ var _Typography = _interopRequireDefault(require("cozy-ui/transpiled/react/Typog
21
21
 
22
22
  var _styles = require("cozy-ui/transpiled/react/styles");
23
23
 
24
- var styles = {
25
- "apps-menu-grid": "apps-menu-grid",
26
- "apps-menu-grid-item-wrapper": "apps-menu-grid-item-wrapper",
27
- "apps-menu-grid-item": "apps-menu-grid-item",
28
- "apps-menu-grid-item-icon": "apps-menu-grid-item-icon"
29
- };
30
24
  var useStyles = (0, _styles.makeStyles)(function () {
31
25
  return {
32
26
  text: {
@@ -66,11 +60,11 @@ var EntrypointItem = function EntrypointItem(_ref) {
66
60
  variant: "text",
67
61
  href: entrypointUrl,
68
62
  title: title,
69
- className: (0, _classnames.default)(styles['apps-menu-grid-item-wrapper'], 'u-bdrs-5'),
63
+ className: (0, _classnames.default)('apps-menu-grid-item-wrapper', 'u-bdrs-5'),
70
64
  label: /*#__PURE__*/_react.default.createElement("div", {
71
- className: styles['apps-menu-grid-item']
65
+ className: "apps-menu-grid-item"
72
66
  }, /*#__PURE__*/_react.default.createElement("div", {
73
- className: styles['apps-menu-grid-item-icon']
67
+ className: "apps-menu-grid-item-icon"
74
68
  }, /*#__PURE__*/_react.default.createElement("img", {
75
69
  className: "u-bdrs-5 u-w-100",
76
70
  src: "data:image/svg+xml;base64,".concat(entrypoint.icon),
@@ -21,12 +21,6 @@ var _Typography = _interopRequireDefault(require("cozy-ui/transpiled/react/Typog
21
21
 
22
22
  var _styles = require("cozy-ui/transpiled/react/styles");
23
23
 
24
- var styles = {
25
- "apps-menu-grid": "apps-menu-grid",
26
- "apps-menu-grid-item-wrapper": "apps-menu-grid-item-wrapper",
27
- "apps-menu-grid-item": "apps-menu-grid-item",
28
- "apps-menu-grid-item-icon": "apps-menu-grid-item-icon"
29
- };
30
24
  var useStyles = (0, _styles.makeStyles)(function () {
31
25
  return {
32
26
  text: {
@@ -61,11 +55,11 @@ var ShortcutItem = function ShortcutItem(_ref) {
61
55
  variant: "text",
62
56
  href: url,
63
57
  title: filename,
64
- className: (0, _classnames.default)(styles['apps-menu-grid-item-wrapper'], 'u-bdrs-5'),
58
+ className: (0, _classnames.default)('apps-menu-grid-item-wrapper', 'u-bdrs-5'),
65
59
  label: /*#__PURE__*/_react.default.createElement("div", {
66
- className: styles['apps-menu-grid-item']
60
+ className: "apps-menu-grid-item"
67
61
  }, /*#__PURE__*/_react.default.createElement("div", {
68
- className: styles['apps-menu-grid-item-icon']
62
+ className: "apps-menu-grid-item-icon"
69
63
  }, shortcutImgSrc ? /*#__PURE__*/_react.default.createElement("img", {
70
64
  className: "u-bdrs-5 u-w-100",
71
65
  src: shortcutImgSrc,
@@ -57,6 +57,7 @@ var Bar = function Bar(_ref) {
57
57
  appTextIcon = _ref.appTextIcon,
58
58
  searchOptions = _ref.searchOptions,
59
59
  appSlug = _ref.appSlug,
60
+ appName = _ref.appName,
60
61
  componentsProps = _ref.componentsProps;
61
62
 
62
63
  var _useBreakpoints = (0, _Breakpoints.useBreakpoints)(),
@@ -90,6 +91,7 @@ var Bar = function Bar(_ref) {
90
91
  }, barLeft || /*#__PURE__*/_react.default.createElement(_BarLeft.default, (0, _extends2.default)({}, componentsProps === null || componentsProps === void 0 ? void 0 : componentsProps.BarLeft, {
91
92
  isPublic: isPublic,
92
93
  homeApp: homeApp,
94
+ appName: appName,
93
95
  appIcon: appIcon,
94
96
  appTextIcon: appTextIcon
95
97
  })), barCenter, /*#__PURE__*/_react.default.createElement("div", {
@@ -23,12 +23,48 @@ var _Grid = _interopRequireDefault(require("cozy-ui/transpiled/react/Grid"));
23
23
 
24
24
  var _Breakpoints = require("cozy-ui/transpiled/react/providers/Breakpoints");
25
25
 
26
- var BarLeft = function BarLeft(_ref) {
27
- var isPublic = _ref.isPublic,
28
- homeApp = _ref.homeApp,
26
+ // The standalone bar receives icons as URLs instead of SVG components
27
+ var AppTitleOrIcon = function AppTitleOrIcon(_ref) {
28
+ var appName = _ref.appName,
29
29
  appIcon = _ref.appIcon,
30
- appTextIcon = _ref.appTextIcon,
31
- noCozyHome = _ref.noCozyHome;
30
+ appTextIcon = _ref.appTextIcon;
31
+
32
+ if (typeof appIcon !== 'string') {
33
+ return /*#__PURE__*/_react.default.createElement(_AppTitle.default, {
34
+ appIcon: appIcon,
35
+ appTextIcon: appTextIcon
36
+ });
37
+ }
38
+
39
+ if (typeof appTextIcon !== 'string') {
40
+ return /*#__PURE__*/_react.default.createElement("img", {
41
+ src: appIcon,
42
+ alt: appName !== null && appName !== void 0 ? appName : '',
43
+ className: "coz-bar-app-icon"
44
+ });
45
+ }
46
+
47
+ return /*#__PURE__*/_react.default.createElement(_AppTitle.default, {
48
+ appIcon: /*#__PURE__*/_react.default.createElement("img", {
49
+ src: appIcon,
50
+ alt: "",
51
+ className: "coz-bar-app-icon"
52
+ }),
53
+ appTextIcon: /*#__PURE__*/_react.default.createElement("img", {
54
+ src: appTextIcon,
55
+ alt: appName !== null && appName !== void 0 ? appName : '',
56
+ className: "coz-bar-app-text-icon"
57
+ })
58
+ });
59
+ };
60
+
61
+ var BarLeft = function BarLeft(_ref2) {
62
+ var isPublic = _ref2.isPublic,
63
+ homeApp = _ref2.homeApp,
64
+ appName = _ref2.appName,
65
+ appIcon = _ref2.appIcon,
66
+ appTextIcon = _ref2.appTextIcon,
67
+ noCozyHome = _ref2.noCozyHome;
32
68
 
33
69
  var _useBreakpoints = (0, _Breakpoints.useBreakpoints)(),
34
70
  isMobile = _useBreakpoints.isMobile;
@@ -45,13 +81,15 @@ var BarLeft = function BarLeft(_ref) {
45
81
  return /*#__PURE__*/_react.default.createElement("a", {
46
82
  className: "coz-nav-apps-btns-home u-ml-half",
47
83
  href: homeHref
48
- }, /*#__PURE__*/_react.default.createElement(_AppTitle.default, {
84
+ }, /*#__PURE__*/_react.default.createElement(AppTitleOrIcon, {
85
+ appName: appName,
49
86
  appIcon: appIcon,
50
87
  appTextIcon: appTextIcon
51
88
  }));
52
89
  }
53
90
 
54
- return /*#__PURE__*/_react.default.createElement(_AppTitle.default, {
91
+ return /*#__PURE__*/_react.default.createElement(AppTitleOrIcon, {
92
+ appName: appName,
55
93
  appIcon: appIcon,
56
94
  appTextIcon: appTextIcon
57
95
  });
@@ -75,10 +113,12 @@ var BarLeft = function BarLeft(_ref) {
75
113
  })), noCozyHome && homeHref ? /*#__PURE__*/_react.default.createElement("a", {
76
114
  className: "coz-nav-apps-btns-home u-ml-half",
77
115
  href: homeHref
78
- }, /*#__PURE__*/_react.default.createElement(_AppTitle.default, {
116
+ }, /*#__PURE__*/_react.default.createElement(AppTitleOrIcon, {
117
+ appName: appName,
79
118
  appIcon: appIcon,
80
119
  appTextIcon: appTextIcon
81
- })) : /*#__PURE__*/_react.default.createElement(_AppTitle.default, {
120
+ })) : /*#__PURE__*/_react.default.createElement(AppTitleOrIcon, {
121
+ appName: appName,
82
122
  appIcon: appIcon,
83
123
  appTextIcon: appTextIcon
84
124
  }));
@@ -45,11 +45,6 @@ var _AvatarMyself = _interopRequireDefault(require("./components/AvatarMyself"))
45
45
 
46
46
  var _helpers = require("./helpers");
47
47
 
48
- var styles = {
49
- "user-menu-content": "user-menu-content",
50
- "user-menu-content-add-account": "user-menu-content-add-account"
51
- };
52
-
53
48
  var UserMenuContent = function UserMenuContent(_ref) {
54
49
  var onLogOut = _ref.onLogOut,
55
50
  instance = _ref.instance,
@@ -84,7 +79,7 @@ var UserMenuContent = function UserMenuContent(_ref) {
84
79
  });
85
80
  var gutters = isMobile ? 'disabled' : undefined;
86
81
  return /*#__PURE__*/_react.default.createElement("div", {
87
- className: (0, _classnames.default)(styles['user-menu-content'], 'u-flex u-flex-column')
82
+ className: (0, _classnames.default)('user-menu-content', 'u-flex u-flex-column')
88
83
  }, /*#__PURE__*/_react.default.createElement("div", {
89
84
  className: "u-flex u-flex-column u-flex-items-center u-mt-half u-ph-half"
90
85
  }, /*#__PURE__*/_react.default.createElement(_AvatarMyself.default, {
@@ -148,7 +143,8 @@ var UserMenuContent = function UserMenuContent(_ref) {
148
143
  webviewIntent: webviewIntent,
149
144
  onLogOut: onLogOut
150
145
  });
151
- }
146
+ },
147
+ "data-testid": "user-menu-logout"
152
148
  }, /*#__PURE__*/_react.default.createElement(_ListItemIcon.default, null, /*#__PURE__*/_react.default.createElement(_twakeIcons.Icon, {
153
149
  icon: _twakeIcons.Logout
154
150
  })), /*#__PURE__*/_react.default.createElement(_ListItemText.default, {
@@ -73,7 +73,8 @@ var UserMenu = function UserMenu(_ref) {
73
73
  ref: buttonRef,
74
74
  onClick: toggleMenu,
75
75
  disabled: !isLoaded,
76
- className: "u-p-0 u-ml-half"
76
+ className: "u-p-0 u-ml-half",
77
+ "data-testid": "user-menu-button"
77
78
  }, /*#__PURE__*/_react.default.createElement(_AvatarMyself.default, {
78
79
  size: isMobile ? 's' : 'm'
79
80
  })), isMobile ? /*#__PURE__*/_react.default.createElement(_CozyDialogs.ConfirmDialog, {
@@ -9,18 +9,17 @@ exports.getAppsData = void 0;
9
9
 
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
 
12
+ var _applications = require("cozy-client/dist/models/applications");
13
+
12
14
  var _cozyFlags = _interopRequireDefault(require("cozy-flags"));
13
15
 
14
16
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
15
17
 
16
18
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
17
19
 
18
- // cozy-client's useQuery normalizes JSON:API responses — attributes are
19
- // spread to the top level (e.g. app.slug, app.name exist directly).
20
- // We only ensure `href` is derived from `links.related` for AppLinker.
21
20
  var mapApp = function mapApp(app) {
22
21
  return _objectSpread(_objectSpread({}, app), {}, {
23
- href: app.links && app.links.related || app.href
22
+ href: (0, _applications.getUrl)(app) || app.href
24
23
  });
25
24
  };
26
25
 
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.default = void 0;
8
+ exports.locales = exports.default = void 0;
9
9
 
10
10
  var _de = _interopRequireDefault(require("../locales/de.json"));
11
11
 
@@ -32,6 +32,7 @@ var locales = {
32
32
  ru: _ru.default,
33
33
  vi: _vi.default
34
34
  };
35
+ exports.locales = locales;
35
36
  var useI18n = (0, _twakeI18n.createUseI18n)(locales);
36
37
  var _default = useI18n;
37
38
  exports.default = _default;
@@ -9,7 +9,7 @@ exports.default = void 0;
9
9
 
10
10
  var _twakeIcons = require("@linagora/twake-icons");
11
11
 
12
- var _react = _interopRequireDefault(require("react"));
12
+ var _react = _interopRequireWildcard(require("react"));
13
13
 
14
14
  var _cozyClient = require("cozy-client");
15
15
 
@@ -17,16 +17,19 @@ var _Breakpoints = require("cozy-ui/transpiled/react/providers/Breakpoints");
17
17
 
18
18
  var _AppIcon = _interopRequireDefault(require("cozy-ui-plus/dist/AppIcon"));
19
19
 
20
+ 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); }
21
+
22
+ 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; }
23
+
20
24
  var IconCozyHome = function IconCozyHome() {
21
25
  var _useBreakpoints = (0, _Breakpoints.useBreakpoints)(),
22
26
  isMobile = _useBreakpoints.isMobile;
23
27
 
24
- var client = (0, _cozyClient.useClient)();
28
+ var client = (0, _cozyClient.useClient)(); // AppIcon reloads the icon whenever fetchIcon changes
25
29
 
26
- var fetchIcon = function fetchIcon() {
30
+ var fetchIcon = (0, _react.useCallback)(function () {
27
31
  return "".concat(client.getStackClient().uri, "/assets/images/icon-cozy-home.svg");
28
- };
29
-
32
+ }, [client]);
30
33
  return /*#__PURE__*/_react.default.createElement(_AppIcon.default, {
31
34
  fetchIcon: fetchIcon,
32
35
  fallbackIcon: _twakeIcons.TwakeWorkplace,