cozy-harvest-lib 11.0.0 → 11.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/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [11.2.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@11.1.0...cozy-harvest-lib@11.2.0) (2023-01-02)
7
+
8
+
9
+ ### Features
10
+
11
+ * **harvest:** Create a `ConnectionBackdrop` component ([3eb0909](https://github.com/cozy/cozy-libs/commit/3eb0909fd95975a0c3486ae9489aa0a88b469eee))
12
+ * **harvest:** Stop redirecting to the success screen once logged ([441e789](https://github.com/cozy/cozy-libs/commit/441e78969ebd506c4f090f1a2b877e403b0e6d69))
13
+ * **harvest:** Use the new connection backdrop, behind a flag ([43cbfa3](https://github.com/cozy/cozy-libs/commit/43cbfa3d270975566e5ad0d1136310731dd7b29d))
14
+
15
+
16
+
17
+
18
+
19
+ # [11.1.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@11.0.0...cozy-harvest-lib@11.1.0) (2022-12-21)
20
+
21
+
22
+ ### Features
23
+
24
+ * **Harvest:** See the different states of a connector ([a0fbf15](https://github.com/cozy/cozy-libs/commit/a0fbf156f514255f85ec22c191460ee87ddfb8cb))
25
+
26
+
27
+
28
+
29
+
6
30
  # [11.0.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@10.0.0...cozy-harvest-lib@11.0.0) (2022-12-21)
7
31
 
8
32
 
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { makeStyles } from 'cozy-ui/transpiled/react/styles';
4
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n';
5
+ import Backdrop from 'cozy-ui/transpiled/react/Backdrop';
6
+ import Icon from 'cozy-ui/transpiled/react/Icon';
7
+ import IconButton from 'cozy-ui/transpiled/react/IconButton';
8
+ import CrossMediumIcon from 'cozy-ui/transpiled/react/Icons/CrossMedium';
9
+ import LinearProgress from 'cozy-ui/transpiled/react/LinearProgress';
10
+ import MuiCozyTheme from 'cozy-ui/transpiled/react/MuiCozyTheme';
11
+ import Typography from 'cozy-ui/transpiled/react/Typography';
12
+ var useStyles = makeStyles({
13
+ container: {
14
+ position: 'fixed',
15
+ zIndex: 'var(--zIndex-modal)',
16
+ inset: 0
17
+ },
18
+ iconButton: {
19
+ position: 'absolute'
20
+ }
21
+ });
22
+
23
+ var ConnectionBackdrop = function ConnectionBackdrop(_ref) {
24
+ var name = _ref.name,
25
+ onClose = _ref.onClose;
26
+ var styles = useStyles();
27
+
28
+ var _useI18n = useI18n(),
29
+ t = _useI18n.t;
30
+
31
+ return /*#__PURE__*/React.createElement(MuiCozyTheme, {
32
+ variant: "inverted"
33
+ }, /*#__PURE__*/React.createElement("div", {
34
+ className: styles.container
35
+ }, /*#__PURE__*/React.createElement(Backdrop, {
36
+ open: true,
37
+ className: "u-p-2"
38
+ }, /*#__PURE__*/React.createElement(IconButton, {
39
+ size: "large",
40
+ onClick: onClose,
41
+ className: cx('u-right-0 u-top-0 u-m-1 u-m-0-s', styles.iconButton)
42
+ }, /*#__PURE__*/React.createElement(Icon, {
43
+ icon: CrossMediumIcon
44
+ })), /*#__PURE__*/React.createElement("div", {
45
+ className: "u-w-6 u-w-100-s"
46
+ }, /*#__PURE__*/React.createElement(Typography, {
47
+ variant: "h4",
48
+ className: "u-ta-center"
49
+ }, t('connectionBackdrop.connecting')), /*#__PURE__*/React.createElement(LinearProgress, {
50
+ className: "u-mt-1 u-w-100"
51
+ }), /*#__PURE__*/React.createElement(Typography, {
52
+ variant: "body2",
53
+ className: "u-mt-1 u-ta-center"
54
+ }, t('connectionBackdrop.progress', {
55
+ name: name
56
+ }))))));
57
+ };
58
+
59
+ export default ConnectionBackdrop;
@@ -39,6 +39,7 @@ import manifest from '../../helpers/manifest';
39
39
  import withKonnectorLocales from '../hoc/withKonnectorLocales';
40
40
  import withConnectionFlow from '../../models/withConnectionFlow';
41
41
  import CannotConnectModal from './CannotConnectModal';
42
+ import ConnectionBackdrop from './ConnectionBackdrop';
42
43
  var VALIDATION_ERROR_REQUIRED_FIELD = 'VALIDATION_ERROR_REQUIRED_FIELD';
43
44
  /**
44
45
  * AccountForm is reponsible of generating a form which will allow user to
@@ -87,7 +88,8 @@ export var AccountForm = /*#__PURE__*/function (_PureComponent) {
87
88
 
88
89
  _this.state = {
89
90
  showConfirmationModal: false,
90
- showCannotConnectModal: false
91
+ showCannotConnectModal: false,
92
+ showConnectionBackdrop: false
91
93
  };
92
94
  _this.inputs = {};
93
95
  _this.inputFocused = null;
@@ -99,6 +101,8 @@ export var AccountForm = /*#__PURE__*/function (_PureComponent) {
99
101
  _this.hideConfirmationModal = _this.hideConfirmationModal.bind(_assertThisInitialized(_this));
100
102
  _this.showCannotConnectModal = _this.showCannotConnectModal.bind(_assertThisInitialized(_this));
101
103
  _this.hideCannotConnectModal = _this.hideCannotConnectModal.bind(_assertThisInitialized(_this));
104
+ _this.showConnectionBackdrop = _this.showConnectionBackdrop.bind(_assertThisInitialized(_this));
105
+ _this.hideConnectionBackdrop = _this.hideConnectionBackdrop.bind(_assertThisInitialized(_this));
102
106
  return _this;
103
107
  }
104
108
  /**
@@ -217,6 +221,7 @@ export var AccountForm = /*#__PURE__*/function (_PureComponent) {
217
221
  form.reset(values);
218
222
  onSubmit(values);
219
223
  this.hideConfirmationModal();
224
+ this.showConnectionBackdrop();
220
225
  }
221
226
  /**
222
227
  * Callback passed to `<AccountFields />` element. Called with a field name,
@@ -274,6 +279,24 @@ export var AccountForm = /*#__PURE__*/function (_PureComponent) {
274
279
  });
275
280
  });
276
281
  }
282
+ }, {
283
+ key: "showConnectionBackdrop",
284
+ value: function showConnectionBackdrop() {
285
+ this.setState(function (prev) {
286
+ return _objectSpread(_objectSpread({}, prev), {}, {
287
+ showConnectionBackdrop: true
288
+ });
289
+ });
290
+ }
291
+ }, {
292
+ key: "hideConnectionBackdrop",
293
+ value: function hideConnectionBackdrop() {
294
+ this.setState(function (prev) {
295
+ return _objectSpread(_objectSpread({}, prev), {}, {
296
+ showConnectionBackdrop: false
297
+ });
298
+ });
299
+ }
277
300
  }, {
278
301
  key: "componentDidMount",
279
302
  value: function componentDidMount() {
@@ -358,7 +381,7 @@ export var AccountForm = /*#__PURE__*/function (_PureComponent) {
358
381
  component: "button",
359
382
  onClick: _this3.showCannotConnectModal
360
383
  }, t('accountForm.cannotConnectLink'))), /*#__PURE__*/React.createElement(Button, {
361
- busy: submitting,
384
+ busy: submitting && (!flag('harvest.inappconnectors.enabled') || !_this3.state.showConnectionBackdrop),
362
385
  className: "u-mt-2 u-mb-1-half",
363
386
  disabled: submitting || !_this3.isSubmittable({
364
387
  dirty: dirty,
@@ -372,6 +395,9 @@ export var AccountForm = /*#__PURE__*/function (_PureComponent) {
372
395
  return _this3.handleSubmit(values, form);
373
396
  },
374
397
  "data-testid": "submit-btn"
398
+ }), submitting && flag('harvest.inappconnectors.enabled') && _this3.state.showConnectionBackdrop && /*#__PURE__*/React.createElement(ConnectionBackdrop, {
399
+ name: name,
400
+ onClose: _this3.hideConnectionBackdrop
375
401
  })) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Media, {
376
402
  align: "top"
377
403
  }, /*#__PURE__*/React.createElement(Img, {
@@ -3,6 +3,7 @@ import React, { useContext } from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import cx from 'classnames';
5
5
  import { useClient } from 'cozy-client';
6
+ import flag from 'cozy-flags';
6
7
  import { useI18n } from 'cozy-ui/transpiled/react/I18n';
7
8
  import Spinner from 'cozy-ui/transpiled/react/Spinner';
8
9
  import Typography from 'cozy-ui/transpiled/react/Typography';
@@ -74,7 +75,7 @@ var NewAccountModal = function NewAccountModal(_ref) {
74
75
  var accountId = triggersModel.getAccountId(trigger);
75
76
  var path = "/accounts/".concat(accountId);
76
77
 
77
- if (trigger.worker !== 'client') {
78
+ if (!flag('harvest.inappconnectors.enabled') && trigger.worker !== 'client') {
78
79
  path += '/success';
79
80
  }
80
81
 
@@ -84,7 +85,7 @@ var NewAccountModal = function NewAccountModal(_ref) {
84
85
  var accountId = triggersModel.getAccountId(trigger);
85
86
  var path = "/accounts/".concat(accountId);
86
87
 
87
- if (trigger.worker !== 'client') {
88
+ if (!flag('harvest.inappconnectors.enabled') && trigger.worker !== 'client') {
88
89
  path += '/success';
89
90
  }
90
91
 
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Switch, Route, Redirect } from 'react-router-dom';
3
+ import flag from 'cozy-flags';
3
4
  import { ViewerModal } from '../../datacards/ViewerModal';
4
5
  import AccountModal from '../AccountModal';
5
6
  import NewAccountModal from '../NewAccountModal';
@@ -61,7 +62,7 @@ var RoutesV4 = function RoutesV4(_ref) {
61
62
  onDismiss: onDismiss
62
63
  });
63
64
  }
64
- }), /*#__PURE__*/React.createElement(Route, {
65
+ }), !flag('harvest.inappconnectors.enabled') && /*#__PURE__*/React.createElement(Route, {
65
66
  path: "".concat(konnectorRoot, "/accounts/:accountId/success"),
66
67
  exact: true,
67
68
  render: function render(_ref4) {
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Routes, Route, Navigate, useParams } from 'react-router-dom';
3
+ import flag from 'cozy-flags';
3
4
  import { ViewerModal } from '../../datacards/ViewerModal';
4
5
  import AccountModal from '../AccountModal';
5
6
  import NewAccountModal from '../NewAccountModal';
@@ -49,7 +50,7 @@ var RoutesV6 = function RoutesV6(_ref) {
49
50
  accounts: accountsAndTriggers
50
51
  });
51
52
  })
52
- }), /*#__PURE__*/React.createElement(Route, {
53
+ }), !flag('harvest.inappconnectors.enabled') && /*#__PURE__*/React.createElement(Route, {
53
54
  path: "accounts/:accountId/success",
54
55
  element: /*#__PURE__*/React.createElement(HarvestParamsWrapper, null, function (params) {
55
56
  return /*#__PURE__*/React.createElement(KonnectorSuccess, {
@@ -124,7 +124,7 @@ describe('LaunchTriggerCard', function () {
124
124
  }),
125
125
  root = _setup5.root;
126
126
 
127
- expect(root.html()).toContain('Running…');
127
+ expect(root.html()).toContain('Data recovery…');
128
128
  });
129
129
  it('should display a syncing message when a trigger launch is expected', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
130
130
  var flow, _setup6, root;
@@ -142,7 +142,7 @@ describe('LaunchTriggerCard', function () {
142
142
  flow: flow
143
143
  }
144
144
  }), root = _setup6.root;
145
- expect(root.html()).toContain('Running…');
145
+ expect(root.html()).toContain('Data recovery…');
146
146
 
147
147
  case 4:
148
148
  case "end":
@@ -1,8 +1,38 @@
1
+ import { formatLocallyDistanceToNow } from 'cozy-ui/transpiled/react/I18n/format';
2
+ var DEFAULT_TIME = 300000; // To milliseconds (5 minutes)
3
+
4
+ var getDifferenceInMinutes = function getDifferenceInMinutes(date) {
5
+ return Date.now() - new Date(date).getTime();
6
+ };
7
+ /**
8
+ * @param {object} options
9
+ * @param {object} options.t - i18n function
10
+ * @param {object} options.running - If the connector is running
11
+ * @param {object} options.expectingTriggerLaunch - If the trigger is waiting to be launched
12
+ * @param {object} options.lastSuccessDate - The last date when the trigger was successfully executed
13
+ * @returns {string}
14
+ */
15
+
16
+
1
17
  export var makeLabel = function makeLabel(_ref) {
2
18
  var t = _ref.t,
3
- f = _ref.f,
4
19
  running = _ref.running,
5
20
  expectingTriggerLaunch = _ref.expectingTriggerLaunch,
6
21
  lastSuccessDate = _ref.lastSuccessDate;
7
- return running || expectingTriggerLaunch ? t('card.launchTrigger.lastSync.syncing') : lastSuccessDate ? f(lastSuccessDate, t('card.launchTrigger.lastSync.format')) : t('card.launchTrigger.lastSync.unknown');
22
+
23
+ if (running || expectingTriggerLaunch) {
24
+ return t('card.launchTrigger.lastSync.syncing');
25
+ }
26
+
27
+ if (lastSuccessDate) {
28
+ if (getDifferenceInMinutes(lastSuccessDate) < DEFAULT_TIME) {
29
+ return t('card.launchTrigger.lastSync.justNow');
30
+ }
31
+
32
+ return t('card.launchTrigger.lastSync.afterSomeTimes', {
33
+ times: formatLocallyDistanceToNow(lastSuccessDate)
34
+ });
35
+ }
36
+
37
+ return t('card.launchTrigger.lastSync.unknown');
8
38
  };
@@ -1,4 +1,5 @@
1
1
  import get from 'lodash/get';
2
+ import MockDate from 'mockdate';
2
3
  import enLocales from '../../locales/en.json';
3
4
  import { makeLabel } from './helpers';
4
5
 
@@ -11,7 +12,7 @@ var f = function f(x) {
11
12
  };
12
13
 
13
14
  describe('makeLabel', function () {
14
- describe('it should return "Running…"', function () {
15
+ describe('it should return "Data recovery…"', function () {
15
16
  it('when running is true', function () {
16
17
  var res = makeLabel({
17
18
  t: t,
@@ -20,7 +21,7 @@ describe('makeLabel', function () {
20
21
  expectingTriggerLaunch: false,
21
22
  lastSuccessDate: '2021'
22
23
  });
23
- expect(res).toBe('Running…');
24
+ expect(res).toBe('Data recovery…');
24
25
  });
25
26
  it('when runggin and expectingTriggerLaunch are true', function () {
26
27
  var res = makeLabel({
@@ -30,7 +31,7 @@ describe('makeLabel', function () {
30
31
  expectingTriggerLaunch: true,
31
32
  lastSuccessDate: '2021'
32
33
  });
33
- expect(res).toBe('Running…');
34
+ expect(res).toBe('Data recovery…');
34
35
  });
35
36
  it('when expectingTriggerLaunch is true', function () {
36
37
  var res = makeLabel({
@@ -40,7 +41,7 @@ describe('makeLabel', function () {
40
41
  expectingTriggerLaunch: true,
41
42
  lastSuccessDate: '2021'
42
43
  });
43
- expect(res).toBe('Running…');
44
+ expect(res).toBe('Data recovery…');
44
45
  });
45
46
  it('when lastSuccessDate is null', function () {
46
47
  var res = makeLabel({
@@ -50,19 +51,35 @@ describe('makeLabel', function () {
50
51
  expectingTriggerLaunch: true,
51
52
  lastSuccessDate: null
52
53
  });
53
- expect(res).toBe('Running…');
54
+ expect(res).toBe('Data recovery…');
54
55
  });
55
56
  });
56
57
  describe('when running and expectingTriggerLaunch are false', function () {
57
- it('should return lastSuccessDate if defined', function () {
58
+ beforeEach(function () {
59
+ MockDate.set('2020-12-25T12:00:00.000Z');
60
+ });
61
+ afterEach(function () {
62
+ MockDate.reset();
63
+ });
64
+ it('should return "Sync. ago..." if lastSuccessDate is defined and > 5 minutes', function () {
58
65
  var res = makeLabel({
59
66
  t: t,
60
67
  f: f,
61
68
  running: false,
62
69
  expectingTriggerLaunch: false,
63
- lastSuccessDate: '2021'
70
+ lastSuccessDate: '2020-12-25T11:55:00.000Z'
71
+ });
72
+ expect(res).toContain("".concat(t('card.launchTrigger.lastSync.afterSomeTimes')));
73
+ });
74
+ it('should return "Sync. just now" if lastSuccessDate is defined and < 5 minutes', function () {
75
+ var res = makeLabel({
76
+ t: t,
77
+ f: f,
78
+ running: false,
79
+ expectingTriggerLaunch: false,
80
+ lastSuccessDate: '2020-12-25T11:55:01.000Z'
64
81
  });
65
- expect(res).toBe('2021');
82
+ expect(res).toBe("".concat(t('card.launchTrigger.lastSync.justNow')));
66
83
  });
67
84
  it('should return "Unknown" if lastSuccessDate is null', function () {
68
85
  var res = makeLabel({
@@ -88,9 +88,10 @@
88
88
  },
89
89
  "lastSync": {
90
90
  "label": "Update:",
91
- "syncing": "Running…",
91
+ "syncing": "Data recovery…",
92
92
  "unknown": "Unknown",
93
- "format": "MMMM D[,] YYYY [at] HH[:]mm"
93
+ "afterSomeTimes": "Sync. %{times} ago",
94
+ "justNow": "Sync. just now"
94
95
  }
95
96
  },
96
97
  "appLink": {
@@ -521,6 +522,10 @@
521
522
  "secondaryText": "Cancel"
522
523
  }
523
524
  },
525
+ "connectionBackdrop": {
526
+ "connecting": "Connection in progress",
527
+ "progress": "We are requesting %{name}, this can take up to 30 seconds..."
528
+ },
524
529
  "disconnectedAccountModal": {
525
530
  "disconnected-help": "This account is disconnected. Your data has been kept. If you want to restart the synchronisation, please reconfigure your account with the \"Add a bank\" button."
526
531
  },
@@ -88,9 +88,10 @@
88
88
  },
89
89
  "lastSync": {
90
90
  "label": "Mise à jour :",
91
- "syncing": "En cours...",
91
+ "syncing": "Récupération des données…",
92
92
  "unknown": "Indéterminée",
93
- "format": "Le D MMMM YYYY [à] HH[:]mm"
93
+ "afterSomeTimes": "Sync. il y a %{times}",
94
+ "justNow": "Sync. à l'instant"
94
95
  }
95
96
  },
96
97
  "appLink": {
@@ -521,6 +522,10 @@
521
522
  "secondaryText": "Annuler"
522
523
  }
523
524
  },
525
+ "connectionBackdrop": {
526
+ "connecting": "Connexion en cours",
527
+ "progress": "Nous interrogeons %{name}, cela peut prendre jusqu'à 30 secondes..."
528
+ },
524
529
  "disconnectedAccountModal": {
525
530
  "disconnected-help": "Vous avez déconnecté votre compte. Vous conservez l'historique de vos données déjà importées. Si vous souhaitez reprendre la connexion, reconfigurez votre compte depuis le bouton \"Ajouter une banque\"."
526
531
  },
@@ -85,7 +85,8 @@
85
85
  "label": "Bijwerken:",
86
86
  "syncing": "Bezig met uitvoeren…",
87
87
  "unknown": "Onbekend",
88
- "format": "D MMMM YYYY [om] HH[:]mm"
88
+ "afterSomeTimes": "Sync. %{times} geleden",
89
+ "justNow": "Sync. onmiddellijk"
89
90
  }
90
91
  },
91
92
  "appLink": {
@@ -540,4 +541,4 @@
540
541
  "caption": "Deze dienst haalt je recentste documenten op en maakt er een volledige back-up van."
541
542
  }
542
543
  }
543
- }
544
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-harvest-lib",
3
- "version": "11.0.0",
3
+ "version": "11.2.0",
4
4
  "description": "Provides logic, modules and components for Cozy's harvest applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -68,6 +68,7 @@
68
68
  "jest-resolve-cached": "1.0.0",
69
69
  "jsdom": "16.5.0",
70
70
  "leaflet": "1.8.0",
71
+ "mockdate": "^3.0.5",
71
72
  "prop-types": "15.7.2",
72
73
  "react": "16.12.0",
73
74
  "react-dom": "16.13.0",
@@ -89,5 +90,5 @@
89
90
  "react-router-dom": ">=4.3.1"
90
91
  },
91
92
  "sideEffects": false,
92
- "gitHead": "78f7a3bc41cdb2b7527878ec464fd8ba50905bd4"
93
+ "gitHead": "034e1cbcf44130b8ad3e697001e017a1a610698b"
93
94
  }
@@ -0,0 +1,55 @@
1
+ import React from 'react'
2
+ import cx from 'classnames'
3
+
4
+ import { makeStyles } from 'cozy-ui/transpiled/react/styles'
5
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
6
+ import Backdrop from 'cozy-ui/transpiled/react/Backdrop'
7
+ import Icon from 'cozy-ui/transpiled/react/Icon'
8
+ import IconButton from 'cozy-ui/transpiled/react/IconButton'
9
+ import CrossMediumIcon from 'cozy-ui/transpiled/react/Icons/CrossMedium'
10
+ import LinearProgress from 'cozy-ui/transpiled/react/LinearProgress'
11
+ import MuiCozyTheme from 'cozy-ui/transpiled/react/MuiCozyTheme'
12
+ import Typography from 'cozy-ui/transpiled/react/Typography'
13
+
14
+ const useStyles = makeStyles({
15
+ container: {
16
+ position: 'fixed',
17
+ zIndex: 'var(--zIndex-modal)',
18
+ inset: 0
19
+ },
20
+ iconButton: {
21
+ position: 'absolute'
22
+ }
23
+ })
24
+
25
+ const ConnectionBackdrop = ({ name, onClose }) => {
26
+ const styles = useStyles()
27
+ const { t } = useI18n()
28
+
29
+ return (
30
+ <MuiCozyTheme variant="inverted">
31
+ <div className={styles.container}>
32
+ <Backdrop open className="u-p-2">
33
+ <IconButton
34
+ size="large"
35
+ onClick={onClose}
36
+ className={cx('u-right-0 u-top-0 u-m-1 u-m-0-s', styles.iconButton)}
37
+ >
38
+ <Icon icon={CrossMediumIcon} />
39
+ </IconButton>
40
+ <div className="u-w-6 u-w-100-s">
41
+ <Typography variant="h4" className="u-ta-center">
42
+ {t('connectionBackdrop.connecting')}
43
+ </Typography>
44
+ <LinearProgress className="u-mt-1 u-w-100" />
45
+ <Typography variant="body2" className="u-mt-1 u-ta-center">
46
+ {t('connectionBackdrop.progress', { name })}
47
+ </Typography>
48
+ </div>
49
+ </Backdrop>
50
+ </div>
51
+ </MuiCozyTheme>
52
+ )
53
+ }
54
+
55
+ export default ConnectionBackdrop
@@ -28,6 +28,7 @@ import manifest from '../../helpers/manifest'
28
28
  import withKonnectorLocales from '../hoc/withKonnectorLocales'
29
29
  import withConnectionFlow from '../../models/withConnectionFlow'
30
30
  import CannotConnectModal from './CannotConnectModal'
31
+ import ConnectionBackdrop from './ConnectionBackdrop'
31
32
 
32
33
  const VALIDATION_ERROR_REQUIRED_FIELD = 'VALIDATION_ERROR_REQUIRED_FIELD'
33
34
 
@@ -47,7 +48,8 @@ export class AccountForm extends PureComponent {
47
48
 
48
49
  this.state = {
49
50
  showConfirmationModal: false,
50
- showCannotConnectModal: false
51
+ showCannotConnectModal: false,
52
+ showConnectionBackdrop: false
51
53
  }
52
54
 
53
55
  this.inputs = {}
@@ -61,6 +63,8 @@ export class AccountForm extends PureComponent {
61
63
  this.hideConfirmationModal = this.hideConfirmationModal.bind(this)
62
64
  this.showCannotConnectModal = this.showCannotConnectModal.bind(this)
63
65
  this.hideCannotConnectModal = this.hideCannotConnectModal.bind(this)
66
+ this.showConnectionBackdrop = this.showConnectionBackdrop.bind(this)
67
+ this.hideConnectionBackdrop = this.hideConnectionBackdrop.bind(this)
64
68
  }
65
69
 
66
70
  /**
@@ -162,6 +166,7 @@ export class AccountForm extends PureComponent {
162
166
  form.reset(values)
163
167
  onSubmit(values)
164
168
  this.hideConfirmationModal()
169
+ this.showConnectionBackdrop()
165
170
  }
166
171
 
167
172
  /**
@@ -209,6 +214,14 @@ export class AccountForm extends PureComponent {
209
214
  this.setState(prev => ({ ...prev, showCannotConnectModal: false }))
210
215
  }
211
216
 
217
+ showConnectionBackdrop() {
218
+ this.setState(prev => ({ ...prev, showConnectionBackdrop: true }))
219
+ }
220
+
221
+ hideConnectionBackdrop() {
222
+ this.setState(prev => ({ ...prev, showConnectionBackdrop: false }))
223
+ }
224
+
212
225
  manageSecretFieldOptions = () => {
213
226
  const secretFieldOptions = this.props.fieldOptions
214
227
  const secretInput = this.inputs[SECRET]
@@ -302,7 +315,6 @@ export class AccountForm extends PureComponent {
302
315
  inputRefByName={this.inputRefByName}
303
316
  t={t}
304
317
  />
305
-
306
318
  {flag('harvest.inappconnectors.enabled') && (
307
319
  <Typography>
308
320
  <Link
@@ -314,9 +326,12 @@ export class AccountForm extends PureComponent {
314
326
  </Link>
315
327
  </Typography>
316
328
  )}
317
-
318
329
  <Button
319
- busy={submitting}
330
+ busy={
331
+ submitting &&
332
+ (!flag('harvest.inappconnectors.enabled') ||
333
+ !this.state.showConnectionBackdrop)
334
+ }
320
335
  className="u-mt-2 u-mb-1-half"
321
336
  disabled={
322
337
  submitting ||
@@ -327,6 +342,14 @@ export class AccountForm extends PureComponent {
327
342
  onClick={() => this.handleSubmit(values, form)}
328
343
  data-testid="submit-btn"
329
344
  />
345
+ {submitting &&
346
+ flag('harvest.inappconnectors.enabled') &&
347
+ this.state.showConnectionBackdrop && (
348
+ <ConnectionBackdrop
349
+ name={name}
350
+ onClose={this.hideConnectionBackdrop}
351
+ />
352
+ )}
330
353
  </>
331
354
  ) : (
332
355
  <>
@@ -2,6 +2,7 @@ import React, { useContext } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import cx from 'classnames'
4
4
  import { useClient } from 'cozy-client'
5
+ import flag from 'cozy-flags'
5
6
 
6
7
  import { useI18n } from 'cozy-ui/transpiled/react/I18n'
7
8
  import Spinner from 'cozy-ui/transpiled/react/Spinner'
@@ -71,7 +72,10 @@ const NewAccountModal = ({ konnector, onDismiss }) => {
71
72
  onLoginSuccess={trigger => {
72
73
  const accountId = triggersModel.getAccountId(trigger)
73
74
  let path = `/accounts/${accountId}`
74
- if (trigger.worker !== 'client') {
75
+ if (
76
+ !flag('harvest.inappconnectors.enabled') &&
77
+ trigger.worker !== 'client'
78
+ ) {
75
79
  path += '/success'
76
80
  }
77
81
  replaceHistory(path)
@@ -79,7 +83,10 @@ const NewAccountModal = ({ konnector, onDismiss }) => {
79
83
  onSuccess={trigger => {
80
84
  const accountId = triggersModel.getAccountId(trigger)
81
85
  let path = `/accounts/${accountId}`
82
- if (trigger.worker !== 'client') {
86
+ if (
87
+ !flag('harvest.inappconnectors.enabled') &&
88
+ trigger.worker !== 'client'
89
+ ) {
83
90
  path += '/success'
84
91
  }
85
92
  replaceHistory(path)
@@ -1,6 +1,8 @@
1
1
  import React from 'react'
2
2
  import { Switch, Route, Redirect } from 'react-router-dom'
3
3
 
4
+ import flag from 'cozy-flags'
5
+
4
6
  import { ViewerModal } from '../../datacards/ViewerModal'
5
7
  import AccountModal from '../AccountModal'
6
8
  import NewAccountModal from '../NewAccountModal'
@@ -66,20 +68,22 @@ const RoutesV4 = ({
66
68
  />
67
69
  )}
68
70
  />
69
- <Route
70
- path={`${konnectorRoot}/accounts/:accountId/success`}
71
- exact
72
- render={({ match }) => {
73
- return (
74
- <KonnectorSuccess
75
- konnector={konnectorWithTriggers}
76
- accountId={match.params.accountId}
77
- accounts={accountsAndTriggers}
78
- onDismiss={onDismiss}
79
- />
80
- )
81
- }}
82
- />
71
+ {!flag('harvest.inappconnectors.enabled') && (
72
+ <Route
73
+ path={`${konnectorRoot}/accounts/:accountId/success`}
74
+ exact
75
+ render={({ match }) => {
76
+ return (
77
+ <KonnectorSuccess
78
+ konnector={konnectorWithTriggers}
79
+ accountId={match.params.accountId}
80
+ accounts={accountsAndTriggers}
81
+ onDismiss={onDismiss}
82
+ />
83
+ )
84
+ }}
85
+ />
86
+ )}
83
87
  <Redirect from={`${konnectorRoot}/*`} to={`${konnectorRoot}/`} />
84
88
  </Switch>
85
89
  )
@@ -1,6 +1,8 @@
1
1
  import React from 'react'
2
2
  import { Routes, Route, Navigate, useParams } from 'react-router-dom'
3
3
 
4
+ import flag from 'cozy-flags'
5
+
4
6
  import { ViewerModal } from '../../datacards/ViewerModal'
5
7
  import AccountModal from '../AccountModal'
6
8
  import NewAccountModal from '../NewAccountModal'
@@ -71,21 +73,23 @@ const RoutesV6 = ({
71
73
  </HarvestParamsWrapper>
72
74
  }
73
75
  />
74
- <Route
75
- path="accounts/:accountId/success"
76
- element={
77
- <HarvestParamsWrapper>
78
- {params => (
79
- <KonnectorSuccess
80
- konnector={konnectorWithTriggers}
81
- accountId={params.accountId}
82
- accounts={accountsAndTriggers}
83
- onDismiss={onDismiss}
84
- />
85
- )}
86
- </HarvestParamsWrapper>
87
- }
88
- />
76
+ {!flag('harvest.inappconnectors.enabled') && (
77
+ <Route
78
+ path="accounts/:accountId/success"
79
+ element={
80
+ <HarvestParamsWrapper>
81
+ {params => (
82
+ <KonnectorSuccess
83
+ konnector={konnectorWithTriggers}
84
+ accountId={params.accountId}
85
+ accounts={accountsAndTriggers}
86
+ onDismiss={onDismiss}
87
+ />
88
+ )}
89
+ </HarvestParamsWrapper>
90
+ }
91
+ />
92
+ )}
89
93
 
90
94
  <Route
91
95
  path="viewer/:accountId/:folderToSaveId/:fileIndex"
@@ -126,7 +126,7 @@ describe('LaunchTriggerCard', () => {
126
126
  flow
127
127
  }
128
128
  })
129
- expect(root.html()).toContain('Running…')
129
+ expect(root.html()).toContain('Data recovery…')
130
130
  })
131
131
 
132
132
  it('should display a syncing message when a trigger launch is expected', async () => {
@@ -138,6 +138,6 @@ describe('LaunchTriggerCard', () => {
138
138
  flow
139
139
  }
140
140
  })
141
- expect(root.html()).toContain('Running…')
141
+ expect(root.html()).toContain('Data recovery…')
142
142
  })
143
143
  })
@@ -1,13 +1,38 @@
1
+ import { formatLocallyDistanceToNow } from 'cozy-ui/transpiled/react/I18n/format'
2
+
3
+ const DEFAULT_TIME = 300_000 // To milliseconds (5 minutes)
4
+
5
+ const getDifferenceInMinutes = date => {
6
+ return Date.now() - new Date(date).getTime()
7
+ }
8
+
9
+ /**
10
+ * @param {object} options
11
+ * @param {object} options.t - i18n function
12
+ * @param {object} options.running - If the connector is running
13
+ * @param {object} options.expectingTriggerLaunch - If the trigger is waiting to be launched
14
+ * @param {object} options.lastSuccessDate - The last date when the trigger was successfully executed
15
+ * @returns {string}
16
+ */
1
17
  export const makeLabel = ({
2
18
  t,
3
- f,
4
19
  running,
5
20
  expectingTriggerLaunch,
6
21
  lastSuccessDate
7
22
  }) => {
8
- return running || expectingTriggerLaunch
9
- ? t('card.launchTrigger.lastSync.syncing')
10
- : lastSuccessDate
11
- ? f(lastSuccessDate, t('card.launchTrigger.lastSync.format'))
12
- : t('card.launchTrigger.lastSync.unknown')
23
+ if (running || expectingTriggerLaunch) {
24
+ return t('card.launchTrigger.lastSync.syncing')
25
+ }
26
+
27
+ if (lastSuccessDate) {
28
+ if (getDifferenceInMinutes(lastSuccessDate) < DEFAULT_TIME) {
29
+ return t('card.launchTrigger.lastSync.justNow')
30
+ }
31
+
32
+ return t('card.launchTrigger.lastSync.afterSomeTimes', {
33
+ times: formatLocallyDistanceToNow(lastSuccessDate)
34
+ })
35
+ }
36
+
37
+ return t('card.launchTrigger.lastSync.unknown')
13
38
  }
@@ -1,4 +1,5 @@
1
1
  import get from 'lodash/get'
2
+ import MockDate from 'mockdate'
2
3
 
3
4
  import enLocales from '../../locales/en.json'
4
5
  import { makeLabel } from './helpers'
@@ -7,7 +8,7 @@ const t = x => get(enLocales, x)
7
8
  const f = x => x
8
9
 
9
10
  describe('makeLabel', () => {
10
- describe('it should return "Running…"', () => {
11
+ describe('it should return "Data recovery…"', () => {
11
12
  it('when running is true', () => {
12
13
  const res = makeLabel({
13
14
  t,
@@ -17,7 +18,7 @@ describe('makeLabel', () => {
17
18
  lastSuccessDate: '2021'
18
19
  })
19
20
 
20
- expect(res).toBe('Running…')
21
+ expect(res).toBe('Data recovery…')
21
22
  })
22
23
 
23
24
  it('when runggin and expectingTriggerLaunch are true', () => {
@@ -29,7 +30,7 @@ describe('makeLabel', () => {
29
30
  lastSuccessDate: '2021'
30
31
  })
31
32
 
32
- expect(res).toBe('Running…')
33
+ expect(res).toBe('Data recovery…')
33
34
  })
34
35
 
35
36
  it('when expectingTriggerLaunch is true', () => {
@@ -41,7 +42,7 @@ describe('makeLabel', () => {
41
42
  lastSuccessDate: '2021'
42
43
  })
43
44
 
44
- expect(res).toBe('Running…')
45
+ expect(res).toBe('Data recovery…')
45
46
  })
46
47
 
47
48
  it('when lastSuccessDate is null', () => {
@@ -53,21 +54,42 @@ describe('makeLabel', () => {
53
54
  lastSuccessDate: null
54
55
  })
55
56
 
56
- expect(res).toBe('Running…')
57
+ expect(res).toBe('Data recovery…')
57
58
  })
58
59
  })
59
60
 
60
61
  describe('when running and expectingTriggerLaunch are false', () => {
61
- it('should return lastSuccessDate if defined', () => {
62
+ beforeEach(() => {
63
+ MockDate.set('2020-12-25T12:00:00.000Z')
64
+ })
65
+ afterEach(() => {
66
+ MockDate.reset()
67
+ })
68
+
69
+ it('should return "Sync. ago..." if lastSuccessDate is defined and > 5 minutes', () => {
62
70
  const res = makeLabel({
63
71
  t,
64
72
  f,
65
73
  running: false,
66
74
  expectingTriggerLaunch: false,
67
- lastSuccessDate: '2021'
75
+ lastSuccessDate: '2020-12-25T11:55:00.000Z'
76
+ })
77
+
78
+ expect(res).toContain(
79
+ `${t('card.launchTrigger.lastSync.afterSomeTimes')}`
80
+ )
81
+ })
82
+
83
+ it('should return "Sync. just now" if lastSuccessDate is defined and < 5 minutes', () => {
84
+ const res = makeLabel({
85
+ t,
86
+ f,
87
+ running: false,
88
+ expectingTriggerLaunch: false,
89
+ lastSuccessDate: '2020-12-25T11:55:01.000Z'
68
90
  })
69
91
 
70
- expect(res).toBe('2021')
92
+ expect(res).toBe(`${t('card.launchTrigger.lastSync.justNow')}`)
71
93
  })
72
94
 
73
95
  it('should return "Unknown" if lastSuccessDate is null', () => {
@@ -88,9 +88,10 @@
88
88
  },
89
89
  "lastSync": {
90
90
  "label": "Update:",
91
- "syncing": "Running…",
91
+ "syncing": "Data recovery…",
92
92
  "unknown": "Unknown",
93
- "format": "MMMM D[,] YYYY [at] HH[:]mm"
93
+ "afterSomeTimes": "Sync. %{times} ago",
94
+ "justNow": "Sync. just now"
94
95
  }
95
96
  },
96
97
  "appLink": {
@@ -521,6 +522,10 @@
521
522
  "secondaryText": "Cancel"
522
523
  }
523
524
  },
525
+ "connectionBackdrop": {
526
+ "connecting": "Connection in progress",
527
+ "progress": "We are requesting %{name}, this can take up to 30 seconds..."
528
+ },
524
529
  "disconnectedAccountModal": {
525
530
  "disconnected-help": "This account is disconnected. Your data has been kept. If you want to restart the synchronisation, please reconfigure your account with the \"Add a bank\" button."
526
531
  },
@@ -88,9 +88,10 @@
88
88
  },
89
89
  "lastSync": {
90
90
  "label": "Mise à jour :",
91
- "syncing": "En cours...",
91
+ "syncing": "Récupération des données…",
92
92
  "unknown": "Indéterminée",
93
- "format": "Le D MMMM YYYY [à] HH[:]mm"
93
+ "afterSomeTimes": "Sync. il y a %{times}",
94
+ "justNow": "Sync. à l'instant"
94
95
  }
95
96
  },
96
97
  "appLink": {
@@ -521,6 +522,10 @@
521
522
  "secondaryText": "Annuler"
522
523
  }
523
524
  },
525
+ "connectionBackdrop": {
526
+ "connecting": "Connexion en cours",
527
+ "progress": "Nous interrogeons %{name}, cela peut prendre jusqu'à 30 secondes..."
528
+ },
524
529
  "disconnectedAccountModal": {
525
530
  "disconnected-help": "Vous avez déconnecté votre compte. Vous conservez l'historique de vos données déjà importées. Si vous souhaitez reprendre la connexion, reconfigurez votre compte depuis le bouton \"Ajouter une banque\"."
526
531
  },
@@ -85,7 +85,8 @@
85
85
  "label": "Bijwerken:",
86
86
  "syncing": "Bezig met uitvoeren…",
87
87
  "unknown": "Onbekend",
88
- "format": "D MMMM YYYY [om] HH[:]mm"
88
+ "afterSomeTimes": "Sync. %{times} geleden",
89
+ "justNow": "Sync. onmiddellijk"
89
90
  }
90
91
  },
91
92
  "appLink": {
@@ -540,4 +541,4 @@
540
541
  "caption": "Deze dienst haalt je recentste documenten op en maakt er een volledige back-up van."
541
542
  }
542
543
  }
543
- }
544
+ }