cozy-ui 90.3.0 → 90.5.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/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ # [90.5.0](https://github.com/cozy/cozy-ui/compare/v90.4.0...v90.5.0) (2023-08-07)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add DemoProvider to simplify doc and tests ([73efeb4](https://github.com/cozy/cozy-ui/commit/73efeb4))
7
+ * **useI18n:** Throw error if not used inside I18n provider ([a54de0f](https://github.com/cozy/cozy-ui/commit/a54de0f))
8
+
9
+ # [90.4.0](https://github.com/cozy/cozy-ui/compare/v90.3.0...v90.4.0) (2023-08-07)
10
+
11
+
12
+ ### Features
13
+
14
+ * **Paywall:** Allow to open premium on flagship when iap will be enabled ([b53abad](https://github.com/cozy/cozy-ui/commit/b53abad))
15
+
1
16
  # [90.3.0](https://github.com/cozy/cozy-ui/compare/v90.2.0...v90.3.0) (2023-08-02)
2
17
 
3
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "90.3.0",
3
+ "version": "90.5.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import { render, fireEvent } from '@testing-library/react'
3
3
 
4
+ import { I18nContext } from '../I18n'
4
5
  import DemoProvider from '../ContactsListModal/DemoProvider'
5
6
  import { BreakpointsProvider } from '../hooks/useBreakpoints'
6
7
  import contacts from '../ContactsList/_mockContacts.json'
@@ -8,9 +9,21 @@ import contacts from '../ContactsList/_mockContacts.json'
8
9
  import ContactPicker from '.'
9
10
 
10
11
  const Wrapper = ({ children }) => {
12
+ const lang = localStorage.getItem('lang') || 'en'
13
+
11
14
  return (
12
15
  <DemoProvider>
13
- <BreakpointsProvider>{children}</BreakpointsProvider>
16
+ <BreakpointsProvider>
17
+ <I18nContext.Provider
18
+ value={{
19
+ t: x => x,
20
+ f: () => '01 Jan. 2022',
21
+ lang
22
+ }}
23
+ >
24
+ {children}
25
+ </I18nContext.Provider>
26
+ </BreakpointsProvider>
14
27
  </DemoProvider>
15
28
  )
16
29
  }
@@ -21,26 +21,37 @@ client.registerPlugin(RealtimePlugin)
21
21
  ```
22
22
 
23
23
  ```jsx
24
- import ContactsListModal from 'cozy-ui/transpiled/react/ContactsListModal';
25
- import { BreakpointsProvider } from 'cozy-ui/transpiled/react/hooks/useBreakpoints';
26
- import DemoProvider from './DemoProvider';
24
+ import ContactsListModal from 'cozy-ui/transpiled/react/ContactsListModal'
25
+ import { BreakpointsProvider } from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
26
+ import { I18nContext } from 'cozy-ui/transpiled/react/I18n'
27
+ import DemoProvider from './DemoProvider'
27
28
 
28
- initialState = { opened: isTesting() };
29
+ initialState = { opened: isTesting() }
30
+
31
+ ;
29
32
 
30
33
  <DemoProvider>
31
34
  <BreakpointsProvider>
32
- <button type="button" onClick={() => setState({ opened: true })}>
33
- Open contacts list
34
- </button>
35
- {state.opened && (
36
- <ContactsListModal
37
- placeholder="Search a contact"
38
- dismissAction={() => setState({ opened: false })}
39
- onItemClick={contact => alert(`Clicked on ${contact._id}`)}
40
- addContactLabel="Add a contact"
41
- emptyMessage="No contact"
42
- />
43
- )}
35
+ <I18nContext.Provider
36
+ value={{
37
+ t: x => x,
38
+ f: () => '01 Jan. 2022',
39
+ lang: localStorage.getItem('lang') || 'en'
40
+ }}
41
+ >
42
+ <button type="button" onClick={() => setState({ opened: true })}>
43
+ Open contacts list
44
+ </button>
45
+ {state.opened && (
46
+ <ContactsListModal
47
+ placeholder="Search a contact"
48
+ dismissAction={() => setState({ opened: false })}
49
+ onItemClick={contact => alert(`Clicked on ${contact._id}`)}
50
+ addContactLabel="Add a contact"
51
+ emptyMessage="No contact"
52
+ />
53
+ )}
54
+ </I18nContext.Provider>
44
55
  </BreakpointsProvider>
45
56
  </DemoProvider>
46
57
  ```
@@ -63,7 +63,7 @@ import {
63
63
  PermissionDialog
64
64
  } from 'cozy-ui/transpiled/react/CozyDialogs'
65
65
 
66
- import { BreakpointsProvider } from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
66
+ import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider'
67
67
 
68
68
  import Button from 'cozy-ui/transpiled/react/Buttons'
69
69
  import Alerter from 'cozy-ui/transpiled/react/deprecated/Alerter'
@@ -243,7 +243,7 @@ const setFlagshipVars = () => {
243
243
  }
244
244
  ;
245
245
 
246
- <BreakpointsProvider>
246
+ <DemoProvider>
247
247
  <Button
248
248
  onClick={() => setFlagshipVars()}
249
249
  variant="secondary"
@@ -386,14 +386,15 @@ const setFlagshipVars = () => {
386
386
  </>
387
387
  )}
388
388
  </Variants>
389
- </BreakpointsProvider>
389
+ </DemoProvider>
390
390
  ```
391
391
 
392
392
  ### Dialogs with title button
393
393
 
394
394
  ```jsx
395
395
  import cx from 'classnames'
396
- import useBreakpoints, { BreakpointsProvider } from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
396
+ import useBreakpoints from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
397
+ import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider'
397
398
 
398
399
  import { Dialog } from 'cozy-ui/transpiled/react/CozyDialogs'
399
400
  import Button from 'cozy-ui/transpiled/react/Buttons'
@@ -433,11 +434,11 @@ const Modal = () => {
433
434
 
434
435
  ;
435
436
 
436
- <BreakpointsProvider>
437
+ <DemoProvider>
437
438
  <Button label="Open modal" onClick={() => setState({ showModal: true })}/>
438
439
 
439
440
  {state.showModal && (
440
441
  <Modal />
441
442
  )}
442
- </BreakpointsProvider>
443
+ </DemoProvider>
443
444
  ```
@@ -9,7 +9,8 @@ import {
9
9
  getBackButton,
10
10
  getBackCloseButton
11
11
  } from './testing'
12
- import useBreakpoints, { BreakpointsProvider } from '../hooks/useBreakpoints'
12
+ import useBreakpoints from '../hooks/useBreakpoints'
13
+ import DemoProvider from '../providers/DemoProvider'
13
14
 
14
15
  jest.mock('../hooks/useBreakpoints', () => ({
15
16
  ...jest.requireActual('../hooks/useBreakpoints'),
@@ -22,7 +23,7 @@ describe('testing utils for dialog', () => {
22
23
  const setup = ({ isMobile = false, onClose, onBack } = {}) => {
23
24
  useBreakpoints.mockReturnValue({ isMobile })
24
25
  const root = render(
25
- <BreakpointsProvider>
26
+ <DemoProvider>
26
27
  <Dialog
27
28
  open
28
29
  title="Title"
@@ -30,7 +31,7 @@ describe('testing utils for dialog', () => {
30
31
  onClose={onClose}
31
32
  onBack={onBack}
32
33
  />
33
- </BreakpointsProvider>
34
+ </DemoProvider>
34
35
  )
35
36
  return { root }
36
37
  }
@@ -8,9 +8,8 @@ will make sure that even your custom Dialogs will behave as CozyDialogs.
8
8
  ```jsx
9
9
  import Dialog, { DialogTitle, DialogActions } from 'cozy-ui/transpiled/react/Dialog'
10
10
  import { DialogBackButton, DialogCloseButton, useCozyDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
11
- import useBreakpoints, {
12
- BreakpointsProvider
13
- } from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
11
+ import useBreakpoints from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
12
+ import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider'
14
13
  import Alerter from 'cozy-ui/transpiled/react/deprecated/Alerter'
15
14
 
16
15
  import Divider from 'cozy-ui/transpiled/react/Divider'
@@ -134,9 +133,9 @@ const ExampleDialog = ({ open, onClose }) => {
134
133
  <button onClick={() => setState({ modalOpened: !state.modalOpened })}>
135
134
  Toggle modal
136
135
  </button>
137
- <BreakpointsProvider>
136
+ <DemoProvider>
138
137
  <Alerter />
139
138
  <ExampleDialog open={state.modalOpened} onClose={handleClose} />
140
- </BreakpointsProvider>
139
+ </DemoProvider>
141
140
  </>
142
141
  ```
@@ -0,0 +1,32 @@
1
+ import { useMemo } from 'react'
2
+ import Polyglot from 'node-polyglot'
3
+
4
+ import { initFormat } from './format'
5
+ import { useI18n, DEFAULT_LANG } from '.'
6
+
7
+ const createUseI18n = locales => () => {
8
+ const { lang } = useI18n() || { lang: DEFAULT_LANG }
9
+
10
+ return useMemo(() => {
11
+ const polyglot = new Polyglot({
12
+ locale: DEFAULT_LANG,
13
+ phrases: locales[DEFAULT_LANG]
14
+ })
15
+
16
+ if (lang && lang !== DEFAULT_LANG) {
17
+ try {
18
+ polyglot.locale(lang)
19
+ polyglot.extend(locales[lang])
20
+ } catch (e) {
21
+ console.warn(`The dict phrases for "${lang}" can't be loaded`)
22
+ }
23
+ }
24
+
25
+ const f = initFormat(lang)
26
+ const t = polyglot.t.bind(polyglot)
27
+
28
+ return { t, f, lang }
29
+ }, [lang])
30
+ }
31
+
32
+ export default createUseI18n
@@ -4,9 +4,8 @@
4
4
 
5
5
  'use strict'
6
6
 
7
- import React, { Component, useContext, useMemo, forwardRef } from 'react'
7
+ import React, { Component, useContext } from 'react'
8
8
  import PropTypes from 'prop-types'
9
- import Polyglot from 'node-polyglot'
10
9
 
11
10
  import { initTranslation } from './translation'
12
11
  import { initFormat } from './format'
@@ -15,6 +14,18 @@ export const DEFAULT_LANG = 'en'
15
14
 
16
15
  export const I18nContext = React.createContext()
17
16
 
17
+ export const useI18n = () => {
18
+ const context = useContext(I18nContext)
19
+
20
+ if (!context) {
21
+ throw new Error(
22
+ '`I18nContext` is missing. `useI18n()` must be used within a `<I18n>`'
23
+ )
24
+ }
25
+
26
+ return context
27
+ }
28
+
18
29
  // Provider root component
19
30
  export class I18n extends Component {
20
31
  constructor(props) {
@@ -76,53 +87,8 @@ I18n.childContextTypes = {
76
87
  lang: PropTypes.string
77
88
  }
78
89
 
79
- // higher order decorator for components that need `t` and/or `f`
80
- export const translate = () => WrappedComponent => {
81
- const Wrapper = forwardRef((props, ref) => {
82
- const i18nContext = useContext(I18nContext)
83
-
84
- return (
85
- <WrappedComponent
86
- {...props}
87
- ref={ref}
88
- t={i18nContext && i18nContext.t}
89
- f={i18nContext && i18nContext.f}
90
- lang={i18nContext && i18nContext.lang}
91
- />
92
- )
93
- })
94
-
95
- Wrapper.displayName = `withI18n(${WrappedComponent.displayName ||
96
- WrappedComponent.name})`
97
-
98
- return Wrapper
99
- }
100
-
101
- export const useI18n = () => {
102
- return useContext(I18nContext)
103
- }
104
-
105
- export const createUseI18n = locales => () => {
106
- const { lang } = useI18n() || { lang: DEFAULT_LANG }
107
- return useMemo(() => {
108
- const polyglot = new Polyglot({
109
- locale: DEFAULT_LANG,
110
- phrases: locales[DEFAULT_LANG]
111
- })
112
- if (lang && lang !== DEFAULT_LANG) {
113
- try {
114
- polyglot.locale(lang)
115
- polyglot.extend(locales[lang])
116
- } catch (e) {
117
- console.warn(`The dict phrases for "${lang}" can't be loaded`)
118
- }
119
- }
120
- const f = initFormat(lang)
121
- const t = polyglot.t.bind(polyglot)
122
- return { t, f, lang }
123
- }, [lang])
124
- }
125
-
126
90
  export { initTranslation, extend } from './translation'
91
+ export { default as translate } from './translate'
92
+ export { default as createUseI18n } from './createUseI18n'
127
93
 
128
94
  export default I18n
@@ -0,0 +1,27 @@
1
+ import React, { useContext, forwardRef } from 'react'
2
+
3
+ import { I18nContext } from '.'
4
+
5
+ // higher order decorator for components that need `t` and/or `f`
6
+ const translate = () => WrappedComponent => {
7
+ const Wrapper = forwardRef((props, ref) => {
8
+ const i18nContext = useContext(I18nContext)
9
+
10
+ return (
11
+ <WrappedComponent
12
+ {...props}
13
+ ref={ref}
14
+ t={i18nContext && i18nContext.t}
15
+ f={i18nContext && i18nContext.f}
16
+ lang={i18nContext && i18nContext.lang}
17
+ />
18
+ )
19
+ })
20
+
21
+ Wrapper.displayName = `withI18n(${WrappedComponent.displayName ||
22
+ WrappedComponent.name})`
23
+
24
+ return Wrapper
25
+ }
26
+
27
+ export default translate
@@ -6,7 +6,8 @@ import NestedSelectModal from './Modal'
6
6
  import ListItem from '../ListItem'
7
7
  import ListItemText from '../ListItemText'
8
8
  import Checkbox from '../Checkbox'
9
- import useBreakpoints, { BreakpointsProvider } from '../hooks/useBreakpoints'
9
+ import useBreakpoints from '../hooks/useBreakpoints'
10
+ import DemoProvider from '../providers/DemoProvider'
10
11
  import palette from 'cozy-ui/transpiled/react/palette'
11
12
 
12
13
  const Image = ({ letter }) => (
@@ -170,11 +171,11 @@ const InteractiveExample = () => {
170
171
  ;
171
172
 
172
173
  <>
173
- <BreakpointsProvider>
174
+ <DemoProvider>
174
175
  {isTesting()
175
176
  ? <StaticExample />
176
177
  : <InteractiveExample />
177
178
  }
178
- </BreakpointsProvider>
179
+ </DemoProvider>
179
180
  </>
180
181
  ```
@@ -2,9 +2,10 @@ import React from 'react'
2
2
  import ReactMarkdown from 'react-markdown'
3
3
  import PropTypes from 'prop-types'
4
4
 
5
- import { isMobileApp, isFlagshipApp } from 'cozy-device-helper'
5
+ import { isFlagshipApp } from 'cozy-device-helper'
6
6
  import { useClient } from 'cozy-client'
7
7
  import { buildPremiumLink } from 'cozy-client/dist/models/instance'
8
+ import flag from 'cozy-flags'
8
9
 
9
10
  import useInstance from '../helpers/useInstance'
10
11
  import Spinner from '../Spinner'
@@ -39,12 +40,14 @@ const Paywall = ({ variant, onClose, isPublic, contentInterpolation }) => {
39
40
  />
40
41
  )
41
42
 
42
- const isMobileAppVersion = isMobileApp() || isFlagshipApp()
43
+ const canOpenPremiumLink =
44
+ !isFlagshipApp() || (isFlagshipApp() && !!flag('flagship.iap.enabled'))
45
+
43
46
  const link = buildPremiumLink(instance)
44
47
  const type = makeType(instance, isPublic, link)
45
48
 
46
49
  const onAction = () => {
47
- return type === 'premium' && !isMobileAppVersion
50
+ return type === 'premium' && canOpenPremiumLink
48
51
  ? window.open(link, '_self')
49
52
  : onClose()
50
53
  }
@@ -66,9 +69,9 @@ const Paywall = ({ variant, onClose, isPublic, contentInterpolation }) => {
66
69
  <Button
67
70
  onClick={onAction}
68
71
  label={
69
- isMobileAppVersion
70
- ? t(`mobileApp.action`)
71
- : t(`${variant}Paywall.${type}.action`)
72
+ canOpenPremiumLink
73
+ ? t(`${variant}Paywall.${type}.action`)
74
+ : t(`mobileApp.action`)
72
75
  }
73
76
  />
74
77
  }
@@ -0,0 +1,206 @@
1
+ import React from 'react'
2
+ import { render, screen, fireEvent } from '@testing-library/react'
3
+
4
+ import { CozyProvider, createMockClient } from 'cozy-client'
5
+ import { isFlagshipApp } from 'cozy-device-helper'
6
+ import flag from 'cozy-flags'
7
+
8
+ import { I18n } from '../I18n'
9
+ import Paywall from './Paywall'
10
+ import { BreakpointsProvider } from '../hooks/useBreakpoints'
11
+ import useInstance from '../helpers/useInstance'
12
+
13
+ jest.mock('../helpers/useInstance')
14
+ jest.mock('cozy-device-helper', () => ({
15
+ ...jest.requireActual('cozy-device-helper'),
16
+ isFlagshipApp: jest.fn()
17
+ }))
18
+
19
+ jest.mock('cozy-flags')
20
+
21
+ describe('Paywall', () => {
22
+ const onCloseSpy = jest.fn()
23
+ const openSpy = jest.spyOn(window, 'open').mockImplementation()
24
+
25
+ beforeEach(() => {
26
+ jest.clearAllMocks()
27
+ })
28
+
29
+ const setup = ({
30
+ isPublic,
31
+ enablePremiumLinks = false,
32
+ hasUuid = false,
33
+ isFlagshipApp: isFlagshipAppReturnValue = false,
34
+ isIapEnabled = null
35
+ } = {}) => {
36
+ useInstance.mockReturnValue({
37
+ context: {
38
+ data: {
39
+ attributes: {
40
+ enable_premium_links: enablePremiumLinks,
41
+ manager_url: 'http://mycozy.cloud'
42
+ }
43
+ }
44
+ },
45
+ instance: {
46
+ data: {
47
+ attributes: { uuid: hasUuid ? '123' : null }
48
+ }
49
+ },
50
+ state: 'loaded'
51
+ })
52
+
53
+ isFlagshipApp.mockReturnValue(isFlagshipAppReturnValue)
54
+ flag.mockReturnValue(isIapEnabled)
55
+
56
+ const mockClient = createMockClient({})
57
+ return render(
58
+ <CozyProvider client={mockClient}>
59
+ <BreakpointsProvider>
60
+ <I18n lang="en" dictRequire={() => {}}>
61
+ <Paywall
62
+ variant="onlyOffice"
63
+ onClose={onCloseSpy}
64
+ isPublic={isPublic}
65
+ />
66
+ </I18n>
67
+ </BreakpointsProvider>
68
+ </CozyProvider>
69
+ )
70
+ }
71
+
72
+ it('should display the default case when nothing is defined', () => {
73
+ setup()
74
+
75
+ expect(screen.getByText('Information')).toBeInTheDocument()
76
+
77
+ const actionButton = screen.getByRole('button', {
78
+ name: 'I understand'
79
+ })
80
+ fireEvent.click(actionButton)
81
+ expect(onCloseSpy).toBeCalledTimes(1)
82
+ })
83
+
84
+ it('should display the default case when there is a premium link but it is not enabled', () => {
85
+ setup({
86
+ hasUuid: true
87
+ })
88
+
89
+ expect(screen.getByText('Information')).toBeInTheDocument()
90
+
91
+ const actionButton = screen.getByRole('button', {
92
+ name: 'I understand'
93
+ })
94
+ fireEvent.click(actionButton)
95
+ expect(onCloseSpy).toBeCalledTimes(1)
96
+ })
97
+
98
+ it('should display the premium case when the premium link is enabled and available', () => {
99
+ setup({
100
+ hasUuid: true,
101
+ enablePremiumLinks: true
102
+ })
103
+
104
+ expect(screen.getByText('Upgrade your plan')).toBeInTheDocument()
105
+
106
+ const actionButton = screen.getByRole('button', {
107
+ name: 'Check our plans'
108
+ })
109
+ fireEvent.click(actionButton)
110
+ expect(openSpy).toBeCalledWith(
111
+ 'http://mycozy.cloud/cozy/instances/123/premium',
112
+ '_self'
113
+ )
114
+ })
115
+
116
+ it('should display the public case when the premium link is available in public context (eg. sharing view)', () => {
117
+ setup({
118
+ hasUuid: true,
119
+ enablePremiumLinks: true,
120
+ isPublic: true
121
+ })
122
+
123
+ expect(
124
+ screen.getByText(
125
+ 'You cannot edit this file online. Please ask the document owner to update their Cozy subscription.'
126
+ )
127
+ ).toBeInTheDocument()
128
+
129
+ const actionButton = screen.getByRole('button', {
130
+ name: 'I understand'
131
+ })
132
+ fireEvent.click(actionButton)
133
+ expect(onCloseSpy).toBeCalledTimes(1)
134
+ })
135
+
136
+ it('should display the default case when the premium link is not available in public context', () => {
137
+ setup({
138
+ hasUuid: true,
139
+ enablePremiumLinks: false,
140
+ isPublic: true
141
+ })
142
+
143
+ expect(screen.getByText('Information')).toBeInTheDocument()
144
+
145
+ const actionButton = screen.getByRole('button', {
146
+ name: 'I understand'
147
+ })
148
+ fireEvent.click(actionButton)
149
+ expect(onCloseSpy).toBeCalledTimes(1)
150
+ })
151
+
152
+ describe('on flagship', () => {
153
+ it('should display the premium case without an action button to access the premium link', () => {
154
+ setup({
155
+ hasUuid: true,
156
+ enablePremiumLinks: true,
157
+ isFlagshipApp: true
158
+ })
159
+
160
+ expect(screen.getByText('Upgrade your plan')).toBeInTheDocument()
161
+
162
+ const actionButton = screen.getByRole('button', {
163
+ name: 'I understand'
164
+ })
165
+ fireEvent.click(actionButton)
166
+ expect(onCloseSpy).toBeCalledTimes(1)
167
+ })
168
+
169
+ it('should display the premium case without an action button to access the premium link when flag flagship.iap.enabled is false', () => {
170
+ setup({
171
+ hasUuid: true,
172
+ enablePremiumLinks: true,
173
+ isFlagshipApp: true,
174
+ isIapEnabled: false
175
+ })
176
+
177
+ expect(screen.getByText('Upgrade your plan')).toBeInTheDocument()
178
+
179
+ const actionButton = screen.getByRole('button', {
180
+ name: 'I understand'
181
+ })
182
+ fireEvent.click(actionButton)
183
+ expect(onCloseSpy).toBeCalledTimes(1)
184
+ })
185
+
186
+ it('should display the premium case with an action button to access the premium link when flag flagship.iap.enabled is true', () => {
187
+ setup({
188
+ hasUuid: true,
189
+ enablePremiumLinks: true,
190
+ isFlagshipApp: true,
191
+ isIapEnabled: true
192
+ })
193
+
194
+ expect(screen.getByText('Upgrade your plan')).toBeInTheDocument()
195
+
196
+ const actionButton = screen.getByRole('button', {
197
+ name: 'Check our plans'
198
+ })
199
+ fireEvent.click(actionButton)
200
+ expect(openSpy).toBeCalledWith(
201
+ 'http://mycozy.cloud/cozy/instances/123/premium',
202
+ '_self'
203
+ )
204
+ })
205
+ })
206
+ })
@@ -1,7 +1,7 @@
1
1
  A paywall is a modal designed to restrict access to a feature to encourage upgrading.
2
2
  There is different variant for each features so the wording can be different to adapt to the context of use.
3
3
 
4
- On mobile devices, we display "I understand" instead of the action button label for reasons of compatibility with the mobile application store.
4
+ When we're in our mobile app called Flagship, we can't display the premium action instead, we just display "I understand" which closes the paywall. This is because our subscription process does not comply with the app store policies. When In app payement (iap) will be implemented we can display premium action back with flag `flagship.iap.enabled`
5
5
 
6
6
  ### Variants
7
7
 
@@ -14,8 +14,6 @@ import {
14
14
  MaxPapersPaywall,
15
15
  QuotaPaywall
16
16
  } from "cozy-ui/transpiled/react/Paywall"
17
- import { CozyProvider } from "cozy-client"
18
- import { BreakpointsProvider } from "cozy-ui/transpiled/react/hooks/useBreakpoints"
19
17
  import Variants from 'cozy-ui/docs/components/Variants'
20
18
  import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
21
19
  import Button from 'cozy-ui/transpiled/react/Buttons'
@@ -293,9 +293,7 @@ the options to be displayed on top of all the layers.
293
293
  ```jsx
294
294
  import { useState, useEffect } from 'react'
295
295
  import { Dialog } from 'cozy-ui/transpiled/react/CozyDialogs'
296
- import {
297
- BreakpointsProvider
298
- } from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
296
+ import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider'
299
297
 
300
298
  import Button from 'cozy-ui/transpiled/react/deprecated/Button'
301
299
  import Typography from 'cozy-ui/transpiled/react/Typography'
@@ -375,10 +373,10 @@ const ExampleDialog = ({ open, onClose }) => {
375
373
  <button onClick={() => setState({ modalOpened: !state.modalOpened })}>
376
374
  Toggle modal
377
375
  </button>
378
- <BreakpointsProvider>
376
+ <DemoProvider>
379
377
  { state.modalOpened
380
378
  ? <ExampleDialog open={true} onClose={handleClose} />
381
379
  : null }
382
- </BreakpointsProvider>
380
+ </DemoProvider>
383
381
  </>
384
382
  ```
@@ -0,0 +1,44 @@
1
+ import React from 'react'
2
+
3
+ import { CozyProvider } from 'cozy-client'
4
+
5
+ import { BreakpointsProvider } from '../hooks/useBreakpoints'
6
+ import { I18nContext } from '../I18n'
7
+
8
+ const defaultClient = {
9
+ plugins: {
10
+ realtime: {
11
+ subscribe: () => {},
12
+ unsubscribe: () => {},
13
+ unsubscribeAll: () => {}
14
+ }
15
+ },
16
+ getStackClient: () => ({
17
+ uri: 'https://cozy.io/'
18
+ }),
19
+ getInstanceOptions: () => ({
20
+ subdomain: ''
21
+ })
22
+ }
23
+
24
+ const DemoProvider = ({ client, children }) => {
25
+ const lang = localStorage.getItem('lang') || 'en'
26
+
27
+ return (
28
+ <CozyProvider client={client || defaultClient}>
29
+ <BreakpointsProvider>
30
+ <I18nContext.Provider
31
+ value={{
32
+ t: x => x,
33
+ f: () => '01 Jan. 2022',
34
+ lang
35
+ }}
36
+ >
37
+ {children}
38
+ </I18nContext.Provider>
39
+ </BreakpointsProvider>
40
+ </CozyProvider>
41
+ )
42
+ }
43
+
44
+ export default DemoProvider
@@ -0,0 +1,39 @@
1
+ import { useMemo } from 'react';
2
+ import Polyglot from 'node-polyglot';
3
+ import { initFormat } from "cozy-ui/transpiled/react/I18n/format";
4
+ import { useI18n, DEFAULT_LANG } from "cozy-ui/transpiled/react/I18n";
5
+
6
+ var createUseI18n = function createUseI18n(locales) {
7
+ return function () {
8
+ var _ref = useI18n() || {
9
+ lang: DEFAULT_LANG
10
+ },
11
+ lang = _ref.lang;
12
+
13
+ return useMemo(function () {
14
+ var polyglot = new Polyglot({
15
+ locale: DEFAULT_LANG,
16
+ phrases: locales[DEFAULT_LANG]
17
+ });
18
+
19
+ if (lang && lang !== DEFAULT_LANG) {
20
+ try {
21
+ polyglot.locale(lang);
22
+ polyglot.extend(locales[lang]);
23
+ } catch (e) {
24
+ console.warn("The dict phrases for \"".concat(lang, "\" can't be loaded"));
25
+ }
26
+ }
27
+
28
+ var f = initFormat(lang);
29
+ var t = polyglot.t.bind(polyglot);
30
+ return {
31
+ t: t,
32
+ f: f,
33
+ lang: lang
34
+ };
35
+ }, [lang]);
36
+ };
37
+ };
38
+
39
+ export default createUseI18n;
@@ -3,7 +3,6 @@
3
3
  */
4
4
  'use strict';
5
5
 
6
- import _extends from "@babel/runtime/helpers/extends";
7
6
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
8
7
  import _createClass from "@babel/runtime/helpers/createClass";
9
8
  import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
@@ -16,13 +15,21 @@ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflec
16
15
 
17
16
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
18
17
 
19
- import React, { Component, useContext, useMemo, forwardRef } from 'react';
18
+ import React, { Component, useContext } from 'react';
20
19
  import PropTypes from 'prop-types';
21
- import Polyglot from 'node-polyglot';
22
20
  import { initTranslation } from "cozy-ui/transpiled/react/I18n/translation";
23
21
  import { initFormat } from "cozy-ui/transpiled/react/I18n/format";
24
22
  export var DEFAULT_LANG = 'en';
25
- export var I18nContext = /*#__PURE__*/React.createContext(); // Provider root component
23
+ export var I18nContext = /*#__PURE__*/React.createContext();
24
+ export var useI18n = function useI18n() {
25
+ var context = useContext(I18nContext);
26
+
27
+ if (!context) {
28
+ throw new Error('`I18nContext` is missing. `useI18n()` must be used within a `<I18n>`');
29
+ }
30
+
31
+ return context;
32
+ }; // Provider root component
26
33
 
27
34
  export var I18n = /*#__PURE__*/function (_Component) {
28
35
  _inherits(I18n, _Component);
@@ -104,57 +111,8 @@ I18n.childContextTypes = {
104
111
  t: PropTypes.func,
105
112
  f: PropTypes.func,
106
113
  lang: PropTypes.string
107
- }; // higher order decorator for components that need `t` and/or `f`
108
-
109
- export var translate = function translate() {
110
- return function (WrappedComponent) {
111
- var Wrapper = /*#__PURE__*/forwardRef(function (props, ref) {
112
- var i18nContext = useContext(I18nContext);
113
- return /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, {
114
- ref: ref,
115
- t: i18nContext && i18nContext.t,
116
- f: i18nContext && i18nContext.f,
117
- lang: i18nContext && i18nContext.lang
118
- }));
119
- });
120
- Wrapper.displayName = "withI18n(".concat(WrappedComponent.displayName || WrappedComponent.name, ")");
121
- return Wrapper;
122
- };
123
- };
124
- export var useI18n = function useI18n() {
125
- return useContext(I18nContext);
126
- };
127
- export var createUseI18n = function createUseI18n(locales) {
128
- return function () {
129
- var _ref = useI18n() || {
130
- lang: DEFAULT_LANG
131
- },
132
- lang = _ref.lang;
133
-
134
- return useMemo(function () {
135
- var polyglot = new Polyglot({
136
- locale: DEFAULT_LANG,
137
- phrases: locales[DEFAULT_LANG]
138
- });
139
-
140
- if (lang && lang !== DEFAULT_LANG) {
141
- try {
142
- polyglot.locale(lang);
143
- polyglot.extend(locales[lang]);
144
- } catch (e) {
145
- console.warn("The dict phrases for \"".concat(lang, "\" can't be loaded"));
146
- }
147
- }
148
-
149
- var f = initFormat(lang);
150
- var t = polyglot.t.bind(polyglot);
151
- return {
152
- t: t,
153
- f: f,
154
- lang: lang
155
- };
156
- }, [lang]);
157
- };
158
114
  };
159
115
  export { initTranslation, extend } from './translation';
116
+ export { default as translate } from './translate';
117
+ export { default as createUseI18n } from './createUseI18n';
160
118
  export default I18n;
@@ -0,0 +1,21 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { useContext, forwardRef } from 'react';
3
+ import { I18nContext } from "cozy-ui/transpiled/react/I18n"; // higher order decorator for components that need `t` and/or `f`
4
+
5
+ var translate = function translate() {
6
+ return function (WrappedComponent) {
7
+ var Wrapper = /*#__PURE__*/forwardRef(function (props, ref) {
8
+ var i18nContext = useContext(I18nContext);
9
+ return /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, {
10
+ ref: ref,
11
+ t: i18nContext && i18nContext.t,
12
+ f: i18nContext && i18nContext.f,
13
+ lang: i18nContext && i18nContext.lang
14
+ }));
15
+ });
16
+ Wrapper.displayName = "withI18n(".concat(WrappedComponent.displayName || WrappedComponent.name, ")");
17
+ return Wrapper;
18
+ };
19
+ };
20
+
21
+ export default translate;
@@ -7,9 +7,10 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
7
7
  import React from 'react';
8
8
  import ReactMarkdown from 'react-markdown';
9
9
  import PropTypes from 'prop-types';
10
- import { isMobileApp, isFlagshipApp } from 'cozy-device-helper';
10
+ import { isFlagshipApp } from 'cozy-device-helper';
11
11
  import { useClient } from 'cozy-client';
12
12
  import { buildPremiumLink } from 'cozy-client/dist/models/instance';
13
+ import flag from 'cozy-flags';
13
14
  import useInstance from "cozy-ui/transpiled/react/helpers/useInstance";
14
15
  import Spinner from "cozy-ui/transpiled/react/Spinner";
15
16
  import { IllustrationDialog } from "cozy-ui/transpiled/react/CozyDialogs";
@@ -47,12 +48,12 @@ var Paywall = function Paywall(_ref) {
47
48
  })),
48
49
  onClose: onClose
49
50
  });
50
- var isMobileAppVersion = isMobileApp() || isFlagshipApp();
51
+ var canOpenPremiumLink = !isFlagshipApp() || isFlagshipApp() && !!flag('flagship.iap.enabled');
51
52
  var link = buildPremiumLink(instance);
52
53
  var type = makeType(instance, isPublic, link);
53
54
 
54
55
  var onAction = function onAction() {
55
- return type === 'premium' && !isMobileAppVersion ? window.open(link, '_self') : onClose();
56
+ return type === 'premium' && canOpenPremiumLink ? window.open(link, '_self') : onClose();
56
57
  };
57
58
 
58
59
  return /*#__PURE__*/React.createElement(IllustrationDialog, {
@@ -71,7 +72,7 @@ var Paywall = function Paywall(_ref) {
71
72
  }, t("".concat(variant, "Paywall.").concat(type, ".title")))),
72
73
  actions: /*#__PURE__*/React.createElement(Button, {
73
74
  onClick: onAction,
74
- label: isMobileAppVersion ? t("mobileApp.action") : t("".concat(variant, "Paywall.").concat(type, ".action"))
75
+ label: canOpenPremiumLink ? t("".concat(variant, "Paywall.").concat(type, ".action")) : t("mobileApp.action")
75
76
  }),
76
77
  content: /*#__PURE__*/React.createElement(ReactMarkdown, {
77
78
  source: t("".concat(variant, "Paywall.").concat(type, ".content"), _objectSpread({}, contentInterpolation)),
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { CozyProvider } from 'cozy-client';
3
+ import { BreakpointsProvider } from "cozy-ui/transpiled/react/hooks/useBreakpoints";
4
+ import { I18nContext } from "cozy-ui/transpiled/react/I18n";
5
+ var defaultClient = {
6
+ plugins: {
7
+ realtime: {
8
+ subscribe: function subscribe() {},
9
+ unsubscribe: function unsubscribe() {},
10
+ unsubscribeAll: function unsubscribeAll() {}
11
+ }
12
+ },
13
+ getStackClient: function getStackClient() {
14
+ return {
15
+ uri: 'https://cozy.io/'
16
+ };
17
+ },
18
+ getInstanceOptions: function getInstanceOptions() {
19
+ return {
20
+ subdomain: ''
21
+ };
22
+ }
23
+ };
24
+
25
+ var DemoProvider = function DemoProvider(_ref) {
26
+ var client = _ref.client,
27
+ children = _ref.children;
28
+ var lang = localStorage.getItem('lang') || 'en';
29
+ return /*#__PURE__*/React.createElement(CozyProvider, {
30
+ client: client || defaultClient
31
+ }, /*#__PURE__*/React.createElement(BreakpointsProvider, null, /*#__PURE__*/React.createElement(I18nContext.Provider, {
32
+ value: {
33
+ t: function t(x) {
34
+ return x;
35
+ },
36
+ f: function f() {
37
+ return '01 Jan. 2022';
38
+ },
39
+ lang: lang
40
+ }
41
+ }, children)));
42
+ };
43
+
44
+ export default DemoProvider;