cozy-bar 0.0.0-development

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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/cozy-bar.min.js +77 -0
  4. package/dist/cozy-bar.min.js.map +1 -0
  5. package/package.json +165 -0
  6. package/src/assets/icons/16/icon-storage-16.svg +3 -0
  7. package/src/assets/icons/24/icon-arrow-left.svg +3 -0
  8. package/src/assets/icons/32/icon-claudy.svg +1 -0
  9. package/src/assets/icons/apps/icon-collect.svg +25 -0
  10. package/src/assets/icons/apps/icon-drive.svg +17 -0
  11. package/src/assets/icons/apps/icon-market-soon.svg +25 -0
  12. package/src/assets/icons/apps/icon-photos.svg +19 -0
  13. package/src/assets/icons/apps/icon-soon.svg +21 -0
  14. package/src/assets/icons/apps/icon-store.svg +19 -0
  15. package/src/assets/icons/claudyActions/icon-bills.svg +6 -0
  16. package/src/assets/icons/claudyActions/icon-laptop.svg +7 -0
  17. package/src/assets/icons/claudyActions/icon-phone.svg +8 -0
  18. package/src/assets/icons/claudyActions/icon-question-mark.svg +6 -0
  19. package/src/assets/icons/comingsoon/icon-bank.svg +12 -0
  20. package/src/assets/icons/comingsoon/icon-sante.svg +12 -0
  21. package/src/assets/icons/comingsoon/icon-store.svg +6 -0
  22. package/src/assets/icons/icon-cozy.svg +3 -0
  23. package/src/assets/icons/icon-shield.svg +3 -0
  24. package/src/assets/icons/spinner.svg +4 -0
  25. package/src/assets/sprites/icon-apps.svg +1 -0
  26. package/src/assets/sprites/icon-cozy-home.svg +16 -0
  27. package/src/components/Apps/AppItem.jsx +117 -0
  28. package/src/components/Apps/AppItemPlaceholder.jsx +12 -0
  29. package/src/components/Apps/AppNavButtons.jsx +94 -0
  30. package/src/components/Apps/AppsContent.jsx +91 -0
  31. package/src/components/Apps/ButtonCozyHome.jsx +30 -0
  32. package/src/components/Apps/ButtonCozyHome.spec.jsx +53 -0
  33. package/src/components/Apps/IconCozyHome.jsx +38 -0
  34. package/src/components/Apps/index.jsx +72 -0
  35. package/src/components/Banner.jsx +41 -0
  36. package/src/components/Bar.jsx +295 -0
  37. package/src/components/Bar.spec.jsx +133 -0
  38. package/src/components/Claudy.jsx +81 -0
  39. package/src/components/ClaudyIcon.jsx +18 -0
  40. package/src/components/Drawer.jsx +227 -0
  41. package/src/components/Drawer.spec.jsx +98 -0
  42. package/src/components/SearchBar.jsx +358 -0
  43. package/src/components/Settings/SettingsContent.jsx +163 -0
  44. package/src/components/Settings/StorageData.jsx +29 -0
  45. package/src/components/Settings/helper.js +8 -0
  46. package/src/components/Settings/index.jsx +220 -0
  47. package/src/components/StorageIcon.jsx +16 -0
  48. package/src/components/SupportModal.jsx +59 -0
  49. package/src/components/__snapshots__/Bar.spec.jsx.snap +302 -0
  50. package/src/config/claudyActions.json +20 -0
  51. package/src/config/persistWhitelist.json +4 -0
  52. package/src/dom.js +80 -0
  53. package/src/index.jsx +242 -0
  54. package/src/index.spec.jsx +34 -0
  55. package/src/lib/api/helpers.js +13 -0
  56. package/src/lib/api/index.jsx +145 -0
  57. package/src/lib/exceptions.js +89 -0
  58. package/src/lib/expiringMemoize.js +13 -0
  59. package/src/lib/icon.js +77 -0
  60. package/src/lib/intents.js +16 -0
  61. package/src/lib/logger.js +11 -0
  62. package/src/lib/middlewares/appsI18n.js +57 -0
  63. package/src/lib/realtime.js +43 -0
  64. package/src/lib/reducers/apps.js +175 -0
  65. package/src/lib/reducers/apps.spec.js +59 -0
  66. package/src/lib/reducers/content.js +50 -0
  67. package/src/lib/reducers/context.js +86 -0
  68. package/src/lib/reducers/index.js +73 -0
  69. package/src/lib/reducers/locale.js +22 -0
  70. package/src/lib/reducers/settings.js +111 -0
  71. package/src/lib/reducers/theme.js +48 -0
  72. package/src/lib/reducers/unserializable.js +26 -0
  73. package/src/lib/stack-client.js +401 -0
  74. package/src/lib/stack.js +79 -0
  75. package/src/lib/store/index.js +44 -0
  76. package/src/locales/de.json +57 -0
  77. package/src/locales/en.json +57 -0
  78. package/src/locales/es.json +57 -0
  79. package/src/locales/fr.json +57 -0
  80. package/src/locales/it.json +57 -0
  81. package/src/locales/ja.json +57 -0
  82. package/src/locales/nl_NL.json +57 -0
  83. package/src/locales/pl.json +57 -0
  84. package/src/locales/ru.json +57 -0
  85. package/src/locales/sq.json +57 -0
  86. package/src/locales/zh_CN.json +57 -0
  87. package/src/proptypes/index.js +10 -0
  88. package/src/queries/index.js +16 -0
  89. package/src/styles/apps.css +248 -0
  90. package/src/styles/banner.css +64 -0
  91. package/src/styles/bar.css +106 -0
  92. package/src/styles/base.css +21 -0
  93. package/src/styles/claudy.css +98 -0
  94. package/src/styles/drawer.css +126 -0
  95. package/src/styles/index.styl +33 -0
  96. package/src/styles/indicators.css +58 -0
  97. package/src/styles/nav.css +81 -0
  98. package/src/styles/navigation_item.css +34 -0
  99. package/src/styles/searchbar.css +156 -0
  100. package/src/styles/settings.css +34 -0
  101. package/src/styles/storage.css +22 -0
  102. package/src/styles/supportModal.css +20 -0
  103. package/src/styles/theme.styl +25 -0
@@ -0,0 +1,163 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { isMobileApp } from 'cozy-device-helper'
5
+
6
+ import { translate } from 'cozy-ui/react/I18n'
7
+ import { ButtonLink } from 'cozy-ui/react/Button'
8
+ import Icon from 'cozy-ui/transpiled/react/Icon'
9
+ import PhoneIcon from 'cozy-ui/transpiled/react/Icons/Phone'
10
+ import CloudIcon from 'cozy-ui/transpiled/react/Icons/Cloud'
11
+ import PeopleIcon from 'cozy-ui/transpiled/react/Icons/People'
12
+ import CloudHappyIcon from 'cozy-ui/transpiled/react/Icons/CloudHappy'
13
+ import LogoutIcon from 'cozy-ui/transpiled/react/Icons/Logout'
14
+ import HelpIcon from 'cozy-ui/transpiled/react/Icons/Help'
15
+
16
+ import StorageData from 'components/Settings/StorageData'
17
+ import StorageIcon from 'components/StorageIcon'
18
+
19
+ const MenuIcon = ({ icon }) => {
20
+ return <Icon className="u-mr-half" color="var(--slateGrey)" icon={icon} />
21
+ }
22
+
23
+ const NavGroup = ({ children }) => {
24
+ return <ul className="coz-nav-group">{children}</ul>
25
+ }
26
+
27
+ const NavItem = ({ children }) => {
28
+ return <li className="coz-nav-settings-item">{children}</li>
29
+ }
30
+
31
+ const SettingsContent = ({
32
+ t,
33
+ onLogOut,
34
+ settingsAppURL,
35
+ storageData,
36
+ onClaudy,
37
+ isDrawer = false,
38
+ isClaudyLoading,
39
+ toggleSupport,
40
+ shoulDisplayViewOfferButton,
41
+ managerUrlPremiumLink,
42
+ viewOfferButtonText
43
+ }) => (
44
+ <div className="coz-nav-pop-content">
45
+ {isDrawer && <hr />}
46
+ {settingsAppURL && (
47
+ <NavGroup>
48
+ <NavItem>
49
+ <a
50
+ role="menuitem"
51
+ href={`${settingsAppURL}#/profile`}
52
+ target="_self"
53
+ title={t('profile')}
54
+ >
55
+ <MenuIcon className="u-mr-half" icon={PeopleIcon} />
56
+ {t('profile')}
57
+ </a>
58
+ </NavItem>
59
+ <NavItem>
60
+ <a
61
+ role="menuitem"
62
+ href={`${settingsAppURL}#/connectedDevices`}
63
+ target="_self"
64
+ title={t('connectedDevices')}
65
+ >
66
+ <MenuIcon icon={PhoneIcon} />
67
+ {t('connectedDevices')}
68
+ </a>
69
+ </NavItem>
70
+ </NavGroup>
71
+ )}
72
+ {isDrawer && onClaudy && !isMobileApp() && (
73
+ <NavGroup>
74
+ <NavItem>
75
+ <button
76
+ type="button"
77
+ role="menuitem"
78
+ className="coz-nav-settings-item-btn"
79
+ busy={isClaudyLoading}
80
+ onClick={onClaudy}
81
+ >
82
+ <MenuIcon icon={CloudIcon} /> {t('claudy.title')}
83
+ </button>
84
+ </NavItem>
85
+ </NavGroup>
86
+ )}
87
+ {!isDrawer && storageData && (
88
+ <NavGroup>
89
+ <NavItem>
90
+ <a
91
+ role="menuitem"
92
+ target="_self"
93
+ title={t('storage')}
94
+ href={`${settingsAppURL}#/storage`}
95
+ >
96
+ <MenuIcon icon={StorageIcon} /> {t('storage')}
97
+ <StorageData data={storageData} />
98
+ </a>
99
+ </NavItem>
100
+ </NavGroup>
101
+ )}
102
+ {(!isDrawer || !isMobileApp()) && shoulDisplayViewOfferButton && (
103
+ <NavGroup>
104
+ <NavItem>
105
+ <ButtonLink
106
+ subtle
107
+ role="menuitem"
108
+ className="coz-nav-settings-item-btn"
109
+ icon={<MenuIcon icon={CloudHappyIcon} />}
110
+ title={viewOfferButtonText}
111
+ label={viewOfferButtonText}
112
+ href={managerUrlPremiumLink}
113
+ />
114
+ </NavItem>
115
+ </NavGroup>
116
+ )}
117
+
118
+ {!isMobileApp() && (
119
+ <NavGroup>
120
+ <NavItem>
121
+ <button
122
+ type="button"
123
+ role="menuitem"
124
+ className="coz-nav-settings-item-btn"
125
+ onClick={toggleSupport}
126
+ >
127
+ <MenuIcon icon={HelpIcon} /> {t('help')}
128
+ </button>
129
+ </NavItem>
130
+ </NavGroup>
131
+ )}
132
+ <NavGroup>
133
+ <NavItem>
134
+ <button
135
+ type="button"
136
+ role="menuitem"
137
+ onClick={onLogOut}
138
+ title={t('logout')}
139
+ >
140
+ <MenuIcon icon={LogoutIcon} /> {t('logout')}
141
+ </button>
142
+ </NavItem>
143
+ </NavGroup>
144
+ </div>
145
+ )
146
+
147
+ SettingsContent.defaultProps = {
148
+ shoulDisplayViewOfferButton: false
149
+ }
150
+
151
+ SettingsContent.propTypes = {
152
+ shoulDisplayViewOfferButton: PropTypes.bool,
153
+ t: PropTypes.func.isRequired,
154
+ onLogOut: PropTypes.func.isRequired,
155
+ settingsAppURL: PropTypes.string,
156
+ storageData: PropTypes.object,
157
+ onClaudy: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
158
+ isDrawer: PropTypes.bool,
159
+ isClaudyLoading: PropTypes.bool,
160
+ toggleSupport: PropTypes.func.isRequired,
161
+ viewOfferButtonText: PropTypes.string
162
+ }
163
+ export default translate()(SettingsContent)
@@ -0,0 +1,29 @@
1
+ import React from 'react'
2
+ import { translate } from 'cozy-ui/react/I18n'
3
+
4
+ const StorageData = ({ t, data }) => {
5
+ const diskQuota = Number.isInteger(data.quota)
6
+ ? (data.quota / (1000 * 1000 * 1000)).toFixed(2)
7
+ : data.quota
8
+ const diskUsage = Number.isInteger(data.usage)
9
+ ? (data.usage / (1000 * 1000 * 1000)).toFixed(2)
10
+ : data.usage
11
+ return (
12
+ <div className="coz-nav-storage">
13
+ <p className="coz-nav-storage-text">
14
+ {t('storage_phrase', {
15
+ diskUsage,
16
+ diskQuota
17
+ })}
18
+ </p>
19
+ <progress
20
+ className="cozy-nav-storage-bar"
21
+ value={diskUsage}
22
+ max={diskQuota}
23
+ min="0"
24
+ />
25
+ </div>
26
+ )
27
+ }
28
+
29
+ export default translate()(StorageData)
@@ -0,0 +1,8 @@
1
+ import { compareClientVersion } from 'lib/stack-client'
2
+ export const isFetchingQueries = requests => {
3
+ return requests.some(request => request.fetchStatus === 'loading')
4
+ }
5
+
6
+ export const cozyClientCanCheckPremium = () => {
7
+ return compareClientVersion('8.3.0')
8
+ }
@@ -0,0 +1,220 @@
1
+ import React, { Component } from 'react'
2
+ import { connect } from 'react-redux'
3
+ import { compose } from 'redux'
4
+ import get from 'lodash/get'
5
+
6
+ import { translate } from 'cozy-ui/react/I18n'
7
+ import { Button } from 'cozy-ui/react/Button'
8
+ import GearIcon from 'cozy-ui/react/Icons/Gear'
9
+
10
+ import { queryConnect } from 'cozy-client/dist'
11
+ import { models } from 'cozy-client'
12
+ let instanceModel = undefined
13
+ let hasAnOffer = undefined
14
+ let isFremiumFixed = undefined
15
+ if (models) {
16
+ instanceModel = models.instance
17
+ // TODO fallback from cozy-client
18
+ isFremiumFixed = data => {
19
+ const GB = 1000 * 1000 * 1000
20
+ const PREMIUM_QUOTA = 50 * GB
21
+ const quota = get(data, 'diskUsage.data.attributes.quota', false)
22
+ return parseInt(quota) < PREMIUM_QUOTA
23
+ }
24
+ hasAnOffer = data => {
25
+ return (
26
+ !instanceModel.isSelfHosted(data) &&
27
+ instanceModel.arePremiumLinksEnabled(data) &&
28
+ instanceModel.getUuid(data) &&
29
+ !isFremiumFixed(data)
30
+ )
31
+ }
32
+ }
33
+
34
+ import SettingsContent from 'components/Settings/SettingsContent'
35
+ import {
36
+ fetchSettingsData,
37
+ getStorageData,
38
+ getSettingsAppURL,
39
+ isSettingsBusy,
40
+ isFetchingSettings,
41
+ logOut
42
+ } from 'lib/reducers'
43
+
44
+ import { instanceReq, contextReq, diskUsageReq } from '../../queries'
45
+
46
+ import {
47
+ isFetchingQueries,
48
+ cozyClientCanCheckPremium
49
+ } from 'components/Settings/helper'
50
+
51
+ export class Settings extends Component {
52
+ constructor(props) {
53
+ super(props)
54
+ this.state = {
55
+ opened: false
56
+ }
57
+ }
58
+
59
+ componentDidMount() {
60
+ document.body.addEventListener('click', this.onClickOutside)
61
+ }
62
+
63
+ componentWillUnmount() {
64
+ document.body.removeEventListener('click', this.onClickOutside)
65
+ }
66
+
67
+ onClickOutside = event => {
68
+ if (this.props.isFetching || this.state.opened) {
69
+ // if it's not a cozy-bar nav popup, close the opened popup
70
+ if (!this.rootRef.contains(event.target)) {
71
+ this.setState({ opened: false })
72
+ event.stopPropagation()
73
+ }
74
+ }
75
+ }
76
+
77
+ toggleMenu = () => {
78
+ let stateUpdate = { opened: false }
79
+ // if popup already opened, stop here to close it
80
+ if (this.state.opened) return this.setState(stateUpdate)
81
+ // fetch data
82
+ this.props.fetchSettingsData()
83
+ this.setState({ opened: true })
84
+ }
85
+
86
+ render() {
87
+ const {
88
+ isBusy,
89
+ logOut,
90
+ onLogOut,
91
+ t,
92
+ toggleSupport,
93
+ diskUsageQuery,
94
+ instanceQuery,
95
+ contextQuery,
96
+ storageData,
97
+ settingsAppURL,
98
+ isFetching
99
+ } = this.props
100
+
101
+ let shouldDisplayViewOfferButton = false
102
+ let managerUrlPremiumLink
103
+ let isFetchingFromQueries
104
+ let viewOfferButtonText = ''
105
+ const canCheckPremium = cozyClientCanCheckPremium()
106
+ if (canCheckPremium) {
107
+ isFetchingFromQueries = isFetchingQueries([
108
+ diskUsageQuery,
109
+ instanceQuery,
110
+ contextQuery
111
+ ])
112
+ if (!isFetchingFromQueries) {
113
+ const data = {
114
+ context: contextQuery,
115
+ diskUsage: diskUsageQuery,
116
+ instance: instanceQuery
117
+ }
118
+ shouldDisplayViewOfferButton =
119
+ instanceModel.shouldDisplayOffers(data) || hasAnOffer(data)
120
+
121
+ if (shouldDisplayViewOfferButton && !hasAnOffer(data)) {
122
+ viewOfferButtonText = t('view_offers')
123
+ } else if (hasAnOffer(data)) {
124
+ viewOfferButtonText = t('view_my_offer')
125
+ }
126
+ managerUrlPremiumLink = instanceModel.buildPremiumLink(data)
127
+ }
128
+ }
129
+
130
+ let areAllFetchingDone = false
131
+ if (!canCheckPremium) {
132
+ areAllFetchingDone = !isFetching
133
+ } else {
134
+ areAllFetchingDone = !isFetchingFromQueries && !isFetching
135
+ }
136
+
137
+ const { opened } = this.state
138
+ const openMenu = opened && areAllFetchingDone
139
+ return (
140
+ <div
141
+ className="coz-nav coz-nav-settings"
142
+ ref={ref => {
143
+ this.rootRef = ref
144
+ }}
145
+ >
146
+ <Button
147
+ type="button"
148
+ theme="text"
149
+ onClick={this.toggleMenu}
150
+ className="coz-nav-settings-btn"
151
+ aria-controls="coz-nav-pop--settings"
152
+ busy={isBusy}
153
+ icon={GearIcon}
154
+ label={t('menu.settings')}
155
+ />
156
+ <div
157
+ className="coz-nav-pop coz-nav-pop--settings"
158
+ id="coz-nav-pop--settings"
159
+ aria-hidden={!openMenu}
160
+ >
161
+ {areAllFetchingDone && (
162
+ <>
163
+ <SettingsContent
164
+ onLogOut={() => {
165
+ if (onLogOut && typeof onLogOut === 'function') {
166
+ onLogOut()
167
+ } else {
168
+ logOut()
169
+ }
170
+ }}
171
+ toggleSupport={toggleSupport}
172
+ storageData={storageData}
173
+ settingsAppURL={settingsAppURL}
174
+ shoulDisplayViewOfferButton={shouldDisplayViewOfferButton}
175
+ managerUrlPremiumLink={managerUrlPremiumLink}
176
+ viewOfferButtonText={viewOfferButtonText}
177
+ />
178
+ </>
179
+ )}
180
+ </div>
181
+ </div>
182
+ )
183
+ }
184
+ }
185
+
186
+ const mapStateToProps = state => ({
187
+ storageData: getStorageData(state),
188
+ settingsAppURL: getSettingsAppURL(state),
189
+ isBusy: isSettingsBusy(state),
190
+ isFetching: isFetchingSettings(state)
191
+ })
192
+
193
+ const mapDispatchToProps = dispatch => ({
194
+ fetchSettingsData: () => dispatch(fetchSettingsData()),
195
+ logOut: () => dispatch(logOut())
196
+ })
197
+ let exported
198
+ if (cozyClientCanCheckPremium()) {
199
+ exported = compose(
200
+ translate(),
201
+ queryConnect({
202
+ instanceQuery: instanceReq,
203
+ contextQuery: contextReq,
204
+ diskUsageQuery: diskUsageReq
205
+ }),
206
+ connect(
207
+ mapStateToProps,
208
+ mapDispatchToProps
209
+ )
210
+ )(Settings)
211
+ } else {
212
+ exported = compose(
213
+ translate(),
214
+ connect(
215
+ mapStateToProps,
216
+ mapDispatchToProps
217
+ )
218
+ )(Settings)
219
+ }
220
+ export default exported
@@ -0,0 +1,16 @@
1
+ /* Automatically generated via svgr */
2
+
3
+ import React from 'react'
4
+
5
+ function SvgIconStorage16(props) {
6
+ return (
7
+ <svg width={16} height={16} {...props}>
8
+ <path
9
+ fillRule="evenodd"
10
+ d="M1 4h14v10.004a1 1 0 01-1.007.996H2.007A1 1 0 011 14.004V4zM0 2c0-.552.445-1 1-1h14c.552 0 1 .444 1 1v1H0V2zm5 4h6v2H5V6z"
11
+ />
12
+ </svg>
13
+ )
14
+ }
15
+
16
+ export default SvgIconStorage16
@@ -0,0 +1,59 @@
1
+ import React, { Component } from 'react'
2
+ import Modal, { ModalContent } from 'cozy-ui/react/Modal'
3
+ import Spinner from 'cozy-ui/react/Spinner'
4
+ import { getIntents } from 'lib/stack'
5
+
6
+ class SupportModal extends Component {
7
+ constructor(props, context) {
8
+ super(props)
9
+ this.store = context.barStore
10
+ this.state = {
11
+ isLoading: false
12
+ }
13
+ this.intents = getIntents()
14
+ }
15
+
16
+ toggle = () => {
17
+ this.setState({ isLoading: true })
18
+ // init support intent
19
+ this.intents
20
+ .create('SUPPORT', 'io.cozy.settings')
21
+ .start(this.intentWrapperRef, () => {
22
+ this.setState({ isLoading: false })
23
+ })
24
+ }
25
+
26
+ componentDidMount() {
27
+ this.toggle()
28
+ }
29
+
30
+ render() {
31
+ const { isLoading } = this.state
32
+ return (
33
+ <div>
34
+ <Modal
35
+ secondaryAction={this.props.onClose}
36
+ className="coz-support-modal"
37
+ into="#cozy-bar-modal-dom-place"
38
+ closeBtnClassName="coz-support-modal-close"
39
+ >
40
+ <ModalContent className="coz-support-modal-wrapper u-mt-1">
41
+ <div className="coz-support-modal-content">
42
+ {isLoading && <Spinner size="xxlarge" middle />}
43
+ <div
44
+ className={`coz-support-intent-wrapper${
45
+ isLoading ? ' coz-hidden' : ''
46
+ }`}
47
+ ref={wrapper => {
48
+ this.intentWrapperRef = wrapper
49
+ }}
50
+ />
51
+ </div>
52
+ </ModalContent>
53
+ </Modal>
54
+ </div>
55
+ )
56
+ }
57
+ }
58
+
59
+ export default SupportModal