@dhis2/analytics 26.3.1 → 26.4.1

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 (23) hide show
  1. package/build/cjs/components/Interpretations/InterpretationModal/InterpretationModal.js +13 -4
  2. package/build/cjs/components/Interpretations/InterpretationModal/InterpretationThread.js +16 -9
  3. package/build/cjs/components/Interpretations/InterpretationModal/index.js +8 -1
  4. package/build/cjs/components/Interpretations/InterpretationsUnit/InterpretationList.js +8 -2
  5. package/build/cjs/components/Interpretations/InterpretationsUnit/InterpretationsUnit.js +20 -6
  6. package/build/cjs/components/Interpretations/common/Interpretation/Interpretation.js +23 -3
  7. package/build/cjs/components/Interpretations/common/Interpretation/useLike.js +12 -2
  8. package/build/cjs/index.js +61 -54
  9. package/build/cjs/locales/en/translations.json +2 -1
  10. package/build/cjs/visualizations/config/adapters/dhis_highcharts/subtitle/index.js +3 -2
  11. package/build/cjs/visualizations/config/adapters/dhis_highcharts/title/index.js +3 -2
  12. package/build/es/components/Interpretations/InterpretationModal/InterpretationModal.js +13 -4
  13. package/build/es/components/Interpretations/InterpretationModal/InterpretationThread.js +16 -9
  14. package/build/es/components/Interpretations/InterpretationModal/index.js +2 -1
  15. package/build/es/components/Interpretations/InterpretationsUnit/InterpretationList.js +8 -2
  16. package/build/es/components/Interpretations/InterpretationsUnit/InterpretationsUnit.js +20 -6
  17. package/build/es/components/Interpretations/common/Interpretation/Interpretation.js +24 -4
  18. package/build/es/components/Interpretations/common/Interpretation/useLike.js +12 -2
  19. package/build/es/index.js +2 -2
  20. package/build/es/locales/en/translations.json +2 -1
  21. package/build/es/visualizations/config/adapters/dhis_highcharts/subtitle/index.js +3 -2
  22. package/build/es/visualizations/config/adapters/dhis_highcharts/title/index.js +3 -2
  23. package/package.json +1 -1
@@ -73,7 +73,7 @@ const InterpretationModal = _ref2 => {
73
73
  });
74
74
  const interpretation = data === null || data === void 0 ? void 0 : data.interpretation;
75
75
  const shouldRenderModalContent = !error && interpretation;
76
- const shouldCssHideModal = loading || isVisualizationLoading;
76
+ const loadingInProgress = loading || isVisualizationLoading;
77
77
  const handleClose = () => {
78
78
  if (isDirty) {
79
79
  onInterpretationUpdate();
@@ -89,6 +89,14 @@ const InterpretationModal = _ref2 => {
89
89
  id: interpretationId
90
90
  });
91
91
  };
92
+ const onLikeToggled = _ref3 => {
93
+ let {
94
+ likedBy
95
+ } = _ref3;
96
+ setIsDirty(true);
97
+ interpretation.likedBy = likedBy;
98
+ interpretation.likes = likedBy.length;
99
+ };
92
100
  const onInterpretationDeleted = () => {
93
101
  setIsDirty(false);
94
102
  onInterpretationUpdate();
@@ -101,11 +109,11 @@ const InterpretationModal = _ref2 => {
101
109
  });
102
110
  }
103
111
  }, [interpretationId, refetch]);
104
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, shouldCssHideModal && /*#__PURE__*/_react.default.createElement(_ui.Layer, null, /*#__PURE__*/_react.default.createElement(_ui.CenteredContent, null, /*#__PURE__*/_react.default.createElement(_ui.CircularLoader, null))), /*#__PURE__*/_react.default.createElement(_ui.Modal, {
112
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, loadingInProgress && /*#__PURE__*/_react.default.createElement(_ui.Layer, null, /*#__PURE__*/_react.default.createElement(_ui.CenteredContent, null, /*#__PURE__*/_react.default.createElement(_ui.CircularLoader, null))), /*#__PURE__*/_react.default.createElement(_ui.Modal, {
105
113
  fluid: true,
106
114
  onClose: handleClose,
107
115
  className: (0, _classnames.default)(modalCSS.className, {
108
- hidden: shouldCssHideModal
116
+ hidden: loadingInProgress
109
117
  }),
110
118
  dataTest: "interpretation-modal"
111
119
  }, /*#__PURE__*/_react.default.createElement("h1", {
@@ -143,7 +151,8 @@ const InterpretationModal = _ref2 => {
143
151
  onInterpretationDeleted: onInterpretationDeleted,
144
152
  onThreadUpdated: onThreadUpdated,
145
153
  initialFocus: initialFocus,
146
- downloadMenuComponent: downloadMenuComponent
154
+ downloadMenuComponent: downloadMenuComponent,
155
+ onLikeToggled: onLikeToggled
147
156
  }))))), /*#__PURE__*/_react.default.createElement(_ui.ModalActions, null, /*#__PURE__*/_react.default.createElement(_ui.Button, {
148
157
  disabled: fetching,
149
158
  onClick: handleClose
@@ -23,9 +23,11 @@ const InterpretationThread = _ref => {
23
23
  fetching,
24
24
  interpretation,
25
25
  onInterpretationDeleted,
26
+ onLikeToggled,
26
27
  initialFocus,
27
28
  onThreadUpdated,
28
- downloadMenuComponent: DownloadMenu
29
+ downloadMenuComponent: DownloadMenu,
30
+ dashboardRedirectUrl
29
31
  } = _ref;
30
32
  const {
31
33
  fromServerDate
@@ -40,30 +42,33 @@ const InterpretationThread = _ref => {
40
42
  }, [initialFocus]);
41
43
  const interpretationAccess = (0, _index.getInterpretationAccess)(interpretation, currentUser);
42
44
  return /*#__PURE__*/_react.default.createElement("div", {
43
- className: "jsx-1846557365" + " " + ((0, _classnames.default)('container', {
44
- fetching
45
+ className: "jsx-3292109121" + " " + ((0, _classnames.default)('container', {
46
+ fetching,
47
+ dashboard: !!dashboardRedirectUrl
45
48
  }) || "")
46
49
  }, /*#__PURE__*/_react.default.createElement("div", {
47
- className: "jsx-1846557365" + " " + 'title'
50
+ className: "jsx-3292109121" + " " + 'title'
48
51
  }, /*#__PURE__*/_react.default.createElement(_ui.IconClock16, {
49
52
  color: _ui.colors.grey700
50
53
  }), (0, _moment.default)(fromServerDate(interpretation.created)).format('LLL')), DownloadMenu && /*#__PURE__*/_react.default.createElement(DownloadMenu, {
51
54
  relativePeriodDate: interpretation.created,
52
- className: "jsx-1846557365"
55
+ className: "jsx-3292109121"
53
56
  }), /*#__PURE__*/_react.default.createElement("div", {
54
- className: "jsx-1846557365" + " " + 'thread'
57
+ className: "jsx-3292109121" + " " + 'thread'
55
58
  }, /*#__PURE__*/_react.default.createElement(_index.Interpretation, {
56
59
  currentUser: currentUser,
57
60
  interpretation: interpretation,
61
+ onLikeToggled: onLikeToggled,
58
62
  onReplyIconClick: interpretationAccess.comment ? () => {
59
63
  var _focusRef$current;
60
64
  return (_focusRef$current = focusRef.current) === null || _focusRef$current === void 0 ? void 0 : _focusRef$current.focus();
61
65
  } : null,
62
66
  onUpdated: () => onThreadUpdated(true),
63
67
  onDeleted: onInterpretationDeleted,
68
+ dashboardRedirectUrl: dashboardRedirectUrl,
64
69
  isInThread: true
65
70
  }), /*#__PURE__*/_react.default.createElement("div", {
66
- className: "jsx-1846557365" + " " + 'comments'
71
+ className: "jsx-3292109121" + " " + 'comments'
67
72
  }, interpretation.comments.map(comment => /*#__PURE__*/_react.default.createElement(_Comment.Comment, {
68
73
  key: comment.id,
69
74
  comment: comment,
@@ -77,8 +82,8 @@ const InterpretationThread = _ref => {
77
82
  onSave: () => onThreadUpdated(true),
78
83
  focusRef: focusRef
79
84
  }), /*#__PURE__*/_react.default.createElement(_style.default, {
80
- id: "1846557365"
81
- }, [".thread.jsx-1846557365{margin-top:var(--spacers-dp16);overflow-y:auto;-webkit-scroll-behavior:smooth;-moz-scroll-behavior:smooth;-ms-scroll-behavior:smooth;scroll-behavior:smooth;}", ".container.jsx-1846557365{position:relative;overflow:auto;max-height:calc(100vh - 285px);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}", ".container.fetching.jsx-1846557365::before{content:'';position:absolute;inset:0px;background-color:rgba(255,255,255,0.8);}", ".container.fetching.jsx-1846557365::after{content:'';position:absolute;top:calc(50% - 12px);left:calc(50% - 12px);width:24px;height:24px;border-width:4px;border-style:solid;border-color:rgba(110,122,138,0.15) rgba(110,122,138,0.15) rgb(20,124,215);border-image:initial;border-radius:50%;-webkit-animation:1s linear 0s infinite normal none running rotation-jsx-1846557365;animation:1s linear 0s infinite normal none running rotation-jsx-1846557365;}", ".title.jsx-1846557365{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:var(--spacers-dp8);color:var(--colors-grey900);font-size:14px;line-height:18px;}", ".comments.jsx-1846557365{padding-left:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding-top:var(--spacers-dp4);gap:var(--spacers-dp4);}", "@-webkit-keyframes rotation-jsx-1846557365{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}", "@keyframes rotation-jsx-1846557365{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}"]));
85
+ id: "3292109121"
86
+ }, [".thread.jsx-3292109121{margin-top:var(--spacers-dp16);overflow-y:auto;-webkit-scroll-behavior:smooth;-moz-scroll-behavior:smooth;-ms-scroll-behavior:smooth;scroll-behavior:smooth;}", ".dashboard.jsx-3292109121 .thread.jsx-3292109121{overflow-y:hidden;}", ".container.jsx-3292109121{position:relative;overflow:auto;max-height:calc(100vh - 285px);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}", ".container.dashboard.jsx-3292109121{max-height:none;}", ".container.fetching.jsx-3292109121::before{content:'';position:absolute;inset:0px;background-color:rgba(255,255,255,0.8);}", ".container.fetching.jsx-3292109121::after{content:'';position:absolute;top:calc(50% - 12px);left:calc(50% - 12px);width:24px;height:24px;border-width:4px;border-style:solid;border-color:rgba(110,122,138,0.15) rgba(110,122,138,0.15) rgb(20,124,215);border-image:initial;border-radius:50%;-webkit-animation:1s linear 0s infinite normal none running rotation-jsx-3292109121;animation:1s linear 0s infinite normal none running rotation-jsx-3292109121;}", ".title.jsx-3292109121{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:var(--spacers-dp8);color:var(--colors-grey900);font-size:14px;line-height:18px;}", ".comments.jsx-3292109121{padding-left:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding-top:var(--spacers-dp4);gap:var(--spacers-dp4);}", "@-webkit-keyframes rotation-jsx-3292109121{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}", "@keyframes rotation-jsx-3292109121{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}"]));
82
87
  };
83
88
  exports.InterpretationThread = InterpretationThread;
84
89
  InterpretationThread.propTypes = {
@@ -86,6 +91,8 @@ InterpretationThread.propTypes = {
86
91
  fetching: _propTypes.default.bool.isRequired,
87
92
  interpretation: _propTypes.default.object.isRequired,
88
93
  onInterpretationDeleted: _propTypes.default.func.isRequired,
94
+ onLikeToggled: _propTypes.default.func.isRequired,
95
+ dashboardRedirectUrl: _propTypes.default.string,
89
96
  downloadMenuComponent: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]),
90
97
  initialFocus: _propTypes.default.bool,
91
98
  onThreadUpdated: _propTypes.default.func
@@ -9,4 +9,11 @@ Object.defineProperty(exports, "InterpretationModal", {
9
9
  return _InterpretationModal.InterpretationModal;
10
10
  }
11
11
  });
12
- var _InterpretationModal = require("./InterpretationModal.js");
12
+ Object.defineProperty(exports, "InterpretationThread", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _InterpretationThread.InterpretationThread;
16
+ }
17
+ });
18
+ var _InterpretationModal = require("./InterpretationModal.js");
19
+ var _InterpretationThread = require("./InterpretationThread.js");
@@ -28,9 +28,11 @@ const InterpretationList = _ref => {
28
28
  currentUser,
29
29
  interpretations,
30
30
  onInterpretationClick,
31
+ onLikeToggled,
31
32
  onReplyIconClick,
32
33
  refresh,
33
- disabled
34
+ disabled,
35
+ dashboardRedirectUrl
34
36
  } = _ref;
35
37
  const {
36
38
  fromServerDate
@@ -64,10 +66,12 @@ const InterpretationList = _ref => {
64
66
  interpretation: interpretation,
65
67
  currentUser: currentUser,
66
68
  onClick: onInterpretationClick,
69
+ onLikeToggled: onLikeToggled,
67
70
  onReplyIconClick: onReplyIconClick,
68
71
  onDeleted: refresh,
69
72
  onUpdated: refresh,
70
- disabled: disabled
73
+ disabled: disabled,
74
+ dashboardRedirectUrl: dashboardRedirectUrl
71
75
  }))))), /*#__PURE__*/_react.default.createElement(_style.default, {
72
76
  id: "4058400613",
73
77
  dynamic: [_ui.spacers.dp8, _ui.spacers.dp8, _ui.spacers.dp16, _ui.colors.grey800, _ui.spacers.dp12, _ui.spacers.dp12, _ui.spacers.dp32, _ui.spacers.dp4]
@@ -79,6 +83,8 @@ InterpretationList.propTypes = {
79
83
  interpretations: _propTypes.default.array.isRequired,
80
84
  refresh: _propTypes.default.func.isRequired,
81
85
  onInterpretationClick: _propTypes.default.func.isRequired,
86
+ onLikeToggled: _propTypes.default.func.isRequired,
82
87
  onReplyIconClick: _propTypes.default.func.isRequired,
88
+ dashboardRedirectUrl: _propTypes.default.string,
83
89
  disabled: _propTypes.default.bool
84
90
  };
@@ -40,17 +40,19 @@ const InterpretationsUnit = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
40
40
  onInterpretationClick,
41
41
  onReplyIconClick,
42
42
  disabled,
43
- renderId
43
+ renderId,
44
+ dashboardRedirectUrl
44
45
  } = _ref2;
45
46
  const [isExpanded, setIsExpanded] = (0, _react.useState)(true);
47
+ const [interpretations, setInterpretations] = (0, _react.useState)([]);
46
48
  const showNoTimeDimensionHelpText = type === 'eventVisualization' && !visualizationHasTimeDimension;
47
49
  const {
48
- data,
49
50
  loading,
50
51
  fetching,
51
52
  refetch
52
53
  } = (0, _appRuntime.useDataQuery)(interpretationsQuery, {
53
- lazy: true
54
+ lazy: true,
55
+ onComplete: data => setInterpretations(data.interpretations.interpretations)
54
56
  });
55
57
  const onCompleteAction = (0, _react.useCallback)(() => {
56
58
  refetch({
@@ -69,6 +71,15 @@ const InterpretationsUnit = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
69
71
  });
70
72
  }
71
73
  }, [type, id, renderId, refetch]);
74
+ const onLikeToggled = _ref3 => {
75
+ let {
76
+ id,
77
+ likedBy
78
+ } = _ref3;
79
+ const interpretation = interpretations.find(interp => interp.id === id);
80
+ interpretation.likedBy = likedBy;
81
+ interpretation.likes = likedBy.length;
82
+ };
72
83
  return /*#__PURE__*/_react.default.createElement("div", {
73
84
  className: _style.default.dynamic([["4120713286", [_ui.spacers.dp16, _ui.colors.grey400, _ui.colors.white, _ui.spacers.dp32, _ui.colors.grey900]]]) + " " + ((0, _classnames.default)('container', {
74
85
  expanded: isExpanded
@@ -90,7 +101,7 @@ const InterpretationsUnit = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
90
101
  className: _style.default.dynamic([["4120713286", [_ui.spacers.dp16, _ui.colors.grey400, _ui.colors.white, _ui.spacers.dp32, _ui.colors.grey900]]]) + " " + "loader"
91
102
  }, /*#__PURE__*/_react.default.createElement(_ui.CircularLoader, {
92
103
  small: true
93
- })), data && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_InterpretationForm.InterpretationForm, {
104
+ })), interpretations && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_InterpretationForm.InterpretationForm, {
94
105
  currentUser: currentUser,
95
106
  type: type,
96
107
  id: id,
@@ -99,11 +110,13 @@ const InterpretationsUnit = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
99
110
  showNoTimeDimensionHelpText: showNoTimeDimensionHelpText
100
111
  }), /*#__PURE__*/_react.default.createElement(_InterpretationList.InterpretationList, {
101
112
  currentUser: currentUser,
102
- interpretations: data.interpretations.interpretations,
113
+ interpretations: interpretations,
103
114
  onInterpretationClick: onInterpretationClick,
115
+ onLikeToggled: onLikeToggled,
104
116
  onReplyIconClick: onReplyIconClick,
105
117
  refresh: onCompleteAction,
106
- disabled: disabled
118
+ disabled: disabled,
119
+ dashboardRedirectUrl: dashboardRedirectUrl
107
120
  }))), /*#__PURE__*/_react.default.createElement(_style.default, {
108
121
  id: "4120713286",
109
122
  dynamic: [_ui.spacers.dp16, _ui.colors.grey400, _ui.colors.white, _ui.spacers.dp32, _ui.colors.grey900]
@@ -119,6 +132,7 @@ InterpretationsUnit.propTypes = {
119
132
  currentUser: _propTypes.default.object.isRequired,
120
133
  id: _propTypes.default.string.isRequired,
121
134
  type: _propTypes.default.string.isRequired,
135
+ dashboardRedirectUrl: _propTypes.default.string,
122
136
  disabled: _propTypes.default.bool,
123
137
  renderId: _propTypes.default.number,
124
138
  visualizationHasTimeDimension: _propTypes.default.bool,
@@ -24,7 +24,9 @@ const Interpretation = _ref => {
24
24
  onDeleted,
25
25
  disabled,
26
26
  onReplyIconClick,
27
- isInThread
27
+ dashboardRedirectUrl,
28
+ isInThread,
29
+ onLikeToggled
28
30
  } = _ref;
29
31
  const [isUpdateMode, setIsUpdateMode] = (0, _react.useState)(false);
30
32
  const [showSharingDialog, setShowSharingDialog] = (0, _react.useState)(false);
@@ -35,9 +37,12 @@ const Interpretation = _ref => {
35
37
  } = (0, _useLike.useLike)({
36
38
  interpretation,
37
39
  currentUser,
38
- onComplete: onUpdated
40
+ onComplete: likedBy => onLikeToggled({
41
+ id: interpretation.id,
42
+ likedBy
43
+ })
39
44
  });
40
- const shouldShowButton = !!onClick && !disabled;
45
+ const shouldShowButton = Boolean(!!onClick && !disabled & !dashboardRedirectUrl);
41
46
  const interpretationAccess = (0, _index.getInterpretationAccess)(interpretation, currentUser);
42
47
  let tooltip = _d2I18n.default.t('Reply');
43
48
  if (!interpretationAccess.comment) {
@@ -51,6 +56,9 @@ const Interpretation = _ref => {
51
56
  tooltip = _d2I18n.default.t('View replies');
52
57
  }
53
58
  }
59
+
60
+ // Maps still uses old url style /?id= instead of hash
61
+ const getAppInterpretationUrl = () => dashboardRedirectUrl.includes('?') ? `${dashboardRedirectUrl}&interpretationId=${interpretation.id}` : `${dashboardRedirectUrl}?interpretationId=${interpretation.id}`;
54
62
  return isUpdateMode ? /*#__PURE__*/_react.default.createElement(_InterpretationUpdateForm.InterpretationUpdateForm, {
55
63
  close: () => setIsUpdateMode(false),
56
64
  id: interpretation.id,
@@ -77,6 +85,16 @@ const Interpretation = _ref => {
77
85
  count: interpretation.comments.length,
78
86
  dataTest: "interpretation-reply-button",
79
87
  viewOnly: isInThread && !interpretationAccess.comment
88
+ }), dashboardRedirectUrl && !isInThread && /*#__PURE__*/_react.default.createElement(_index.MessageIconButton, {
89
+ tooltipContent: _d2I18n.default.t('See interpretation'),
90
+ iconComponent: _ui.IconView16,
91
+ onClick: () => onClick(interpretation.id),
92
+ dataTest: "interpretation-view-button"
93
+ }), dashboardRedirectUrl && /*#__PURE__*/_react.default.createElement(_index.MessageIconButton, {
94
+ tooltipContent: _d2I18n.default.t('Open in app'),
95
+ iconComponent: _ui.IconLaunch16,
96
+ onClick: () => window.open(getAppInterpretationUrl(), '_blank'),
97
+ dataTest: "interpretation-launch-in-app-button"
80
98
  }), interpretationAccess.share && /*#__PURE__*/_react.default.createElement(_index.MessageIconButton, {
81
99
  iconComponent: _ui.IconShare16,
82
100
  tooltipContent: _d2I18n.default.t('Share'),
@@ -109,8 +127,10 @@ Interpretation.propTypes = {
109
127
  currentUser: _propTypes.default.object.isRequired,
110
128
  interpretation: _propTypes.default.object.isRequired,
111
129
  onDeleted: _propTypes.default.func.isRequired,
130
+ onLikeToggled: _propTypes.default.func.isRequired,
112
131
  onReplyIconClick: _propTypes.default.func.isRequired,
113
132
  onUpdated: _propTypes.default.func.isRequired,
133
+ dashboardRedirectUrl: _propTypes.default.string,
114
134
  disabled: _propTypes.default.bool,
115
135
  isInThread: _propTypes.default.bool,
116
136
  onClick: _propTypes.default.func
@@ -24,12 +24,22 @@ const useLike = _ref => {
24
24
  const [like, {
25
25
  loading: likeLoading
26
26
  }] = (0, _appRuntime.useDataMutation)(likeMutationRef.current, {
27
- onComplete
27
+ onComplete: () => {
28
+ const newLikedBy = interpretation.likedBy.concat({
29
+ id: currentUser.id
30
+ });
31
+ setIsLikedByCurrentUser(true);
32
+ onComplete(newLikedBy);
33
+ }
28
34
  });
29
35
  const [unlike, {
30
36
  loading: unlikeLoading
31
37
  }] = (0, _appRuntime.useDataMutation)(unlikeMutationRef.current, {
32
- onComplete
38
+ onComplete: () => {
39
+ const newLikedBy = interpretation.likedBy.filter(lb => lb.id !== currentUser.id);
40
+ setIsLikedByCurrentUser(false);
41
+ onComplete(newLikedBy);
42
+ }
33
43
  });
34
44
  const [isLikedByCurrentUser, setIsLikedByCurrentUser] = (0, _react.useState)(false);
35
45
  const toggleLike = () => {
@@ -22,6 +22,7 @@ var _exportNames = {
22
22
  AboutAOUnit: true,
23
23
  InterpretationsUnit: true,
24
24
  InterpretationModal: true,
25
+ InterpretationThread: true,
25
26
  TranslationDialog: true,
26
27
  OfflineTooltip: true,
27
28
  CachedDataQueryProvider: true,
@@ -293,13 +294,13 @@ Object.defineProperty(exports, "Analytics", {
293
294
  Object.defineProperty(exports, "BIMONTHLY", {
294
295
  enumerable: true,
295
296
  get: function () {
296
- return _index9.BIMONTHLY;
297
+ return _index11.BIMONTHLY;
297
298
  }
298
299
  });
299
300
  Object.defineProperty(exports, "BIWEEKLY", {
300
301
  enumerable: true,
301
302
  get: function () {
302
- return _index9.BIWEEKLY;
303
+ return _index11.BIWEEKLY;
303
304
  }
304
305
  });
305
306
  Object.defineProperty(exports, "COLOR_SET_BASIC", {
@@ -359,7 +360,7 @@ Object.defineProperty(exports, "CachedDataQueryProvider", {
359
360
  Object.defineProperty(exports, "DAILY", {
360
361
  enumerable: true,
361
362
  get: function () {
362
- return _index9.DAILY;
363
+ return _index11.DAILY;
363
364
  }
364
365
  });
365
366
  Object.defineProperty(exports, "DEFAULT_AXIS_IDS", {
@@ -563,7 +564,7 @@ Object.defineProperty(exports, "DynamicDimension", {
563
564
  Object.defineProperty(exports, "FINANCIAL", {
564
565
  enumerable: true,
565
566
  get: function () {
566
- return _index9.FINANCIAL;
567
+ return _index11.FINANCIAL;
567
568
  }
568
569
  });
569
570
  Object.defineProperty(exports, "FONT_STYLE_AXIS_LABELS", {
@@ -653,25 +654,25 @@ Object.defineProperty(exports, "FONT_STYLE_VISUALIZATION_TITLE", {
653
654
  Object.defineProperty(exports, "FYAPR", {
654
655
  enumerable: true,
655
656
  get: function () {
656
- return _index9.FYAPR;
657
+ return _index11.FYAPR;
657
658
  }
658
659
  });
659
660
  Object.defineProperty(exports, "FYJUL", {
660
661
  enumerable: true,
661
662
  get: function () {
662
- return _index9.FYJUL;
663
+ return _index11.FYJUL;
663
664
  }
664
665
  });
665
666
  Object.defineProperty(exports, "FYNOV", {
666
667
  enumerable: true,
667
668
  get: function () {
668
- return _index9.FYNOV;
669
+ return _index11.FYNOV;
669
670
  }
670
671
  });
671
672
  Object.defineProperty(exports, "FYOCT", {
672
673
  enumerable: true,
673
674
  get: function () {
674
- return _index9.FYOCT;
675
+ return _index11.FYOCT;
675
676
  }
676
677
  });
677
678
  Object.defineProperty(exports, "FileMenu", {
@@ -707,13 +708,19 @@ Object.defineProperty(exports, "ITEM_PROP_ID", {
707
708
  Object.defineProperty(exports, "InterpretationModal", {
708
709
  enumerable: true,
709
710
  get: function () {
710
- return _InterpretationModal.InterpretationModal;
711
+ return _index3.InterpretationModal;
712
+ }
713
+ });
714
+ Object.defineProperty(exports, "InterpretationThread", {
715
+ enumerable: true,
716
+ get: function () {
717
+ return _index3.InterpretationThread;
711
718
  }
712
719
  });
713
720
  Object.defineProperty(exports, "InterpretationsUnit", {
714
721
  enumerable: true,
715
722
  get: function () {
716
- return _InterpretationsUnit.InterpretationsUnit;
723
+ return _index2.InterpretationsUnit;
717
724
  }
718
725
  });
719
726
  Object.defineProperty(exports, "LAYOUT", {
@@ -797,7 +804,7 @@ Object.defineProperty(exports, "LegendKey", {
797
804
  Object.defineProperty(exports, "MONTHLY", {
798
805
  enumerable: true,
799
806
  get: function () {
800
- return _index9.MONTHLY;
807
+ return _index11.MONTHLY;
801
808
  }
802
809
  });
803
810
  Object.defineProperty(exports, "OfflineTooltip", {
@@ -827,19 +834,19 @@ Object.defineProperty(exports, "PivotTable", {
827
834
  Object.defineProperty(exports, "QUARTERLY", {
828
835
  enumerable: true,
829
836
  get: function () {
830
- return _index9.QUARTERLY;
837
+ return _index11.QUARTERLY;
831
838
  }
832
839
  });
833
840
  Object.defineProperty(exports, "SIXMONTHLY", {
834
841
  enumerable: true,
835
842
  get: function () {
836
- return _index9.SIXMONTHLY;
843
+ return _index11.SIXMONTHLY;
837
844
  }
838
845
  });
839
846
  Object.defineProperty(exports, "SIXMONTHLYAPR", {
840
847
  enumerable: true,
841
848
  get: function () {
842
- return _index9.SIXMONTHLYAPR;
849
+ return _index11.SIXMONTHLYAPR;
843
850
  }
844
851
  });
845
852
  Object.defineProperty(exports, "TEXT_ALIGN_CENTER", {
@@ -863,25 +870,25 @@ Object.defineProperty(exports, "TEXT_ALIGN_RIGHT", {
863
870
  Object.defineProperty(exports, "TranslationDialog", {
864
871
  enumerable: true,
865
872
  get: function () {
866
- return _index3.TranslationDialog;
873
+ return _index5.TranslationDialog;
867
874
  }
868
875
  });
869
876
  Object.defineProperty(exports, "USER_ORG_UNIT", {
870
877
  enumerable: true,
871
878
  get: function () {
872
- return _index4.USER_ORG_UNIT;
879
+ return _index6.USER_ORG_UNIT;
873
880
  }
874
881
  });
875
882
  Object.defineProperty(exports, "USER_ORG_UNIT_CHILDREN", {
876
883
  enumerable: true,
877
884
  get: function () {
878
- return _index4.USER_ORG_UNIT_CHILDREN;
885
+ return _index6.USER_ORG_UNIT_CHILDREN;
879
886
  }
880
887
  });
881
888
  Object.defineProperty(exports, "USER_ORG_UNIT_GRANDCHILDREN", {
882
889
  enumerable: true,
883
890
  get: function () {
884
- return _index4.USER_ORG_UNIT_GRANDCHILDREN;
891
+ return _index6.USER_ORG_UNIT_GRANDCHILDREN;
885
892
  }
886
893
  });
887
894
  Object.defineProperty(exports, "VALUE_TYPE_AGE", {
@@ -1139,43 +1146,43 @@ Object.defineProperty(exports, "VisualizationOptions", {
1139
1146
  Object.defineProperty(exports, "WEEKLY", {
1140
1147
  enumerable: true,
1141
1148
  get: function () {
1142
- return _index9.WEEKLY;
1149
+ return _index11.WEEKLY;
1143
1150
  }
1144
1151
  });
1145
1152
  Object.defineProperty(exports, "WEEKLYSAT", {
1146
1153
  enumerable: true,
1147
1154
  get: function () {
1148
- return _index9.WEEKLYSAT;
1155
+ return _index11.WEEKLYSAT;
1149
1156
  }
1150
1157
  });
1151
1158
  Object.defineProperty(exports, "WEEKLYSUN", {
1152
1159
  enumerable: true,
1153
1160
  get: function () {
1154
- return _index9.WEEKLYSUN;
1161
+ return _index11.WEEKLYSUN;
1155
1162
  }
1156
1163
  });
1157
1164
  Object.defineProperty(exports, "WEEKLYTHU", {
1158
1165
  enumerable: true,
1159
1166
  get: function () {
1160
- return _index9.WEEKLYTHU;
1167
+ return _index11.WEEKLYTHU;
1161
1168
  }
1162
1169
  });
1163
1170
  Object.defineProperty(exports, "WEEKLYWED", {
1164
1171
  enumerable: true,
1165
1172
  get: function () {
1166
- return _index9.WEEKLYWED;
1173
+ return _index11.WEEKLYWED;
1167
1174
  }
1168
1175
  });
1169
1176
  Object.defineProperty(exports, "WEEKS_THIS_YEAR", {
1170
1177
  enumerable: true,
1171
1178
  get: function () {
1172
- return _index9.WEEKS_THIS_YEAR;
1179
+ return _index11.WEEKS_THIS_YEAR;
1173
1180
  }
1174
1181
  });
1175
1182
  Object.defineProperty(exports, "YEARLY", {
1176
1183
  enumerable: true,
1177
1184
  get: function () {
1178
- return _index9.YEARLY;
1185
+ return _index11.YEARLY;
1179
1186
  }
1180
1187
  });
1181
1188
  Object.defineProperty(exports, "apiFetchDimensions", {
@@ -1265,7 +1272,7 @@ Object.defineProperty(exports, "axisIsEmpty", {
1265
1272
  Object.defineProperty(exports, "canDimensionBeAddedToAxis", {
1266
1273
  enumerable: true,
1267
1274
  get: function () {
1268
- return _index7.canDimensionBeAddedToAxis;
1275
+ return _index9.canDimensionBeAddedToAxis;
1269
1276
  }
1270
1277
  });
1271
1278
  Object.defineProperty(exports, "colorSets", {
@@ -1277,13 +1284,13 @@ Object.defineProperty(exports, "colorSets", {
1277
1284
  Object.defineProperty(exports, "convertOuLevelsToUids", {
1278
1285
  enumerable: true,
1279
1286
  get: function () {
1280
- return _index5.convertOuLevelsToUids;
1287
+ return _index7.convertOuLevelsToUids;
1281
1288
  }
1282
1289
  });
1283
1290
  Object.defineProperty(exports, "createVisualization", {
1284
1291
  enumerable: true,
1285
1292
  get: function () {
1286
- return _index8.createVisualization;
1293
+ return _index10.createVisualization;
1287
1294
  }
1288
1295
  });
1289
1296
  Object.defineProperty(exports, "defaultFontStyle", {
@@ -1367,31 +1374,31 @@ Object.defineProperty(exports, "getAdaptedUiLayoutByType", {
1367
1374
  Object.defineProperty(exports, "getAllLockedDimensionIds", {
1368
1375
  enumerable: true,
1369
1376
  get: function () {
1370
- return _index7.getAllLockedDimensionIds;
1377
+ return _index9.getAllLockedDimensionIds;
1371
1378
  }
1372
1379
  });
1373
1380
  Object.defineProperty(exports, "getAvailableAxes", {
1374
1381
  enumerable: true,
1375
1382
  get: function () {
1376
- return _index7.getAvailableAxes;
1383
+ return _index9.getAvailableAxes;
1377
1384
  }
1378
1385
  });
1379
1386
  Object.defineProperty(exports, "getAxisMaxNumberOfDimensions", {
1380
1387
  enumerable: true,
1381
1388
  get: function () {
1382
- return _index7.getAxisMaxNumberOfDimensions;
1389
+ return _index9.getAxisMaxNumberOfDimensions;
1383
1390
  }
1384
1391
  });
1385
1392
  Object.defineProperty(exports, "getAxisMaxNumberOfItems", {
1386
1393
  enumerable: true,
1387
1394
  get: function () {
1388
- return _index7.getAxisMaxNumberOfItems;
1395
+ return _index9.getAxisMaxNumberOfItems;
1389
1396
  }
1390
1397
  });
1391
1398
  Object.defineProperty(exports, "getAxisMinNumberOfDimensions", {
1392
1399
  enumerable: true,
1393
1400
  get: function () {
1394
- return _index7.getAxisMinNumberOfDimensions;
1401
+ return _index9.getAxisMinNumberOfDimensions;
1395
1402
  }
1396
1403
  });
1397
1404
  Object.defineProperty(exports, "getAxisName", {
@@ -1409,7 +1416,7 @@ Object.defineProperty(exports, "getAxisNameByLayoutType", {
1409
1416
  Object.defineProperty(exports, "getAxisPerLockedDimension", {
1410
1417
  enumerable: true,
1411
1418
  get: function () {
1412
- return _index7.getAxisPerLockedDimension;
1419
+ return _index9.getAxisPerLockedDimension;
1413
1420
  }
1414
1421
  });
1415
1422
  Object.defineProperty(exports, "getColorByValueFromLegendSet", {
@@ -1427,7 +1434,7 @@ Object.defineProperty(exports, "getDimensionById", {
1427
1434
  Object.defineProperty(exports, "getDisallowedDimensions", {
1428
1435
  enumerable: true,
1429
1436
  get: function () {
1430
- return _index7.getDisallowedDimensions;
1437
+ return _index9.getDisallowedDimensions;
1431
1438
  }
1432
1439
  });
1433
1440
  Object.defineProperty(exports, "getDisplayNameByVisType", {
@@ -1493,13 +1500,13 @@ Object.defineProperty(exports, "getTextAlignOptions", {
1493
1500
  Object.defineProperty(exports, "getTransferableDimension", {
1494
1501
  enumerable: true,
1495
1502
  get: function () {
1496
- return _index7.getTransferableDimension;
1503
+ return _index9.getTransferableDimension;
1497
1504
  }
1498
1505
  });
1499
1506
  Object.defineProperty(exports, "hasAxisTooManyItems", {
1500
1507
  enumerable: true,
1501
1508
  get: function () {
1502
- return _index7.hasAxisTooManyItems;
1509
+ return _index9.hasAxisTooManyItems;
1503
1510
  }
1504
1511
  });
1505
1512
  Object.defineProperty(exports, "hasCustomAxes", {
@@ -1511,13 +1518,13 @@ Object.defineProperty(exports, "hasCustomAxes", {
1511
1518
  Object.defineProperty(exports, "hasRelativeItems", {
1512
1519
  enumerable: true,
1513
1520
  get: function () {
1514
- return _index6.hasRelativeItems;
1521
+ return _index8.hasRelativeItems;
1515
1522
  }
1516
1523
  });
1517
1524
  Object.defineProperty(exports, "isAxisFull", {
1518
1525
  enumerable: true,
1519
1526
  get: function () {
1520
- return _index7.isAxisFull;
1527
+ return _index9.isAxisFull;
1521
1528
  }
1522
1529
  });
1523
1530
  Object.defineProperty(exports, "isColumnBasedType", {
@@ -1529,7 +1536,7 @@ Object.defineProperty(exports, "isColumnBasedType", {
1529
1536
  Object.defineProperty(exports, "isDimensionLocked", {
1530
1537
  enumerable: true,
1531
1538
  get: function () {
1532
- return _index7.isDimensionLocked;
1539
+ return _index9.isDimensionLocked;
1533
1540
  }
1534
1541
  });
1535
1542
  Object.defineProperty(exports, "isDualAxisType", {
@@ -1679,7 +1686,7 @@ Object.defineProperty(exports, "layoutReplaceDimension", {
1679
1686
  Object.defineProperty(exports, "ouIdHelper", {
1680
1687
  enumerable: true,
1681
1688
  get: function () {
1682
- return _index4.ouIdHelper;
1689
+ return _index6.ouIdHelper;
1683
1690
  }
1684
1691
  });
1685
1692
  Object.defineProperty(exports, "preparePayloadForSaveAs", {
@@ -1722,21 +1729,21 @@ var _utils = require("./components/FileMenu/utils.js");
1722
1729
  var _VisTypeIcon = _interopRequireDefault(require("./components/VisTypeIcon.js"));
1723
1730
  var _LegendKey = _interopRequireDefault(require("./components/LegendKey/LegendKey.js"));
1724
1731
  var _AboutAOUnit = _interopRequireDefault(require("./components/AboutAOUnit/AboutAOUnit.js"));
1725
- var _InterpretationsUnit = require("./components/Interpretations/InterpretationsUnit/InterpretationsUnit.js");
1726
- var _InterpretationModal = require("./components/Interpretations/InterpretationModal/InterpretationModal.js");
1727
- var _index2 = require("./components/Toolbar/index.js");
1728
- Object.keys(_index2).forEach(function (key) {
1732
+ var _index2 = require("./components/Interpretations/InterpretationsUnit/index.js");
1733
+ var _index3 = require("./components/Interpretations/InterpretationModal/index.js");
1734
+ var _index4 = require("./components/Toolbar/index.js");
1735
+ Object.keys(_index4).forEach(function (key) {
1729
1736
  if (key === "default" || key === "__esModule") return;
1730
1737
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
1731
- if (key in exports && exports[key] === _index2[key]) return;
1738
+ if (key in exports && exports[key] === _index4[key]) return;
1732
1739
  Object.defineProperty(exports, key, {
1733
1740
  enumerable: true,
1734
1741
  get: function () {
1735
- return _index2[key];
1742
+ return _index4[key];
1736
1743
  }
1737
1744
  });
1738
1745
  });
1739
- var _index3 = require("./components/TranslationDialog/index.js");
1746
+ var _index5 = require("./components/TranslationDialog/index.js");
1740
1747
  var _OfflineTooltip = require("./components/OfflineTooltip.js");
1741
1748
  var _CachedDataQueryProvider = require("./components/CachedDataQueryProvider.js");
1742
1749
  var _Analytics = _interopRequireDefault(require("./api/analytics/Analytics.js"));
@@ -1744,10 +1751,10 @@ var _dimensions = require("./api/dimensions.js");
1744
1751
  var _organisationUnits = require("./api/organisationUnits.js");
1745
1752
  var _axis = require("./modules/axis.js");
1746
1753
  var _predefinedDimensions = require("./modules/predefinedDimensions.js");
1747
- var _index4 = require("./modules/ouIdHelper/index.js");
1748
- var _index5 = require("./modules/ouLevelUtils/index.js");
1754
+ var _index6 = require("./modules/ouIdHelper/index.js");
1755
+ var _index7 = require("./modules/ouLevelUtils/index.js");
1749
1756
  var _getAdaptedUiLayoutByType = require("./modules/getAdaptedUiLayoutByType.js");
1750
- var _index6 = require("./modules/relativeItems/index.js");
1757
+ var _index8 = require("./modules/relativeItems/index.js");
1751
1758
  var _layout = require("./modules/layout/layout.js");
1752
1759
  var _layoutFilterDimensions = require("./modules/layout/layoutFilterDimensions.js");
1753
1760
  var _layoutGetAllAxes = require("./modules/layout/layoutGetAllAxes.js");
@@ -1787,13 +1794,13 @@ var _itemIsValid = require("./modules/layout/itemIsValid.js");
1787
1794
  var _visTypeToLayoutType = require("./modules/visTypeToLayoutType.js");
1788
1795
  var _visTypes = require("./modules/visTypes.js");
1789
1796
  var _layoutTypes = require("./modules/layoutTypes.js");
1790
- var _index7 = require("./modules/layoutUiRules/index.js");
1791
- var _index8 = require("./visualizations/index.js");
1797
+ var _index9 = require("./modules/layoutUiRules/index.js");
1798
+ var _index10 = require("./visualizations/index.js");
1792
1799
  var _fontStyle = require("./modules/fontStyle.js");
1793
1800
  var _legends = require("./modules/legends.js");
1794
1801
  var _renderValue = require("./modules/renderValue.js");
1795
1802
  var _colorSets = require("./visualizations/util/colors/colorSets.js");
1796
- var _index9 = require("./components/PeriodDimension/utils/index.js");
1803
+ var _index11 = require("./components/PeriodDimension/utils/index.js");
1797
1804
  var _relativePeriods = require("./components/PeriodDimension/utils/relativePeriods.js");
1798
1805
  var _fixedPeriods = require("./components/PeriodDimension/utils/fixedPeriods.js");
1799
1806
  var _VisualizationOptions = _interopRequireDefault(require("./components/Options/VisualizationOptions.js"));
@@ -125,8 +125,9 @@
125
125
  "View replies": "View replies",
126
126
  "Unlike": "Unlike",
127
127
  "Like": "Like",
128
- "Share": "Share",
129
128
  "See interpretation": "See interpretation",
129
+ "Open in app": "Open in app",
130
+ "Share": "Share",
130
131
  "Manage sharing": "Manage sharing",
131
132
  "Could not update interpretation": "Could not update interpretation",
132
133
  "Enter interpretation text": "Enter interpretation text",
@@ -37,8 +37,9 @@ function _default(series, layout, metaData, dashboard) {
37
37
  }
38
38
 
39
39
  // DHIS2-578: allow for optional custom subtitle
40
- if ((0, _isString.default)(layout.subtitle)) {
41
- subtitle.text = layout.subtitle;
40
+ const customSubtitle = layout.subtitle && layout.displaySubtitle || layout.subtitle;
41
+ if ((0, _isString.default)(customSubtitle) && customSubtitle.length) {
42
+ subtitle.text = customSubtitle;
42
43
  } else {
43
44
  const filterTitle = (0, _getFilterText.default)(layout.filters, metaData);
44
45
  switch (layout.type) {
@@ -38,8 +38,9 @@ function _default(layout, metaData, dashboard) {
38
38
  if (layout.hideTitle) {
39
39
  return title;
40
40
  }
41
- if ((0, _isString.default)(layout.title) && layout.title.length) {
42
- title.text = layout.title;
41
+ const customTitle = layout.title && layout.displayTitle || layout.title;
42
+ if ((0, _isString.default)(customTitle) && customTitle.length) {
43
+ title.text = customTitle;
43
44
  } else {
44
45
  switch (layout.type) {
45
46
  case _visTypes.VIS_TYPE_GAUGE:
@@ -64,7 +64,7 @@ const InterpretationModal = _ref2 => {
64
64
  });
65
65
  const interpretation = data === null || data === void 0 ? void 0 : data.interpretation;
66
66
  const shouldRenderModalContent = !error && interpretation;
67
- const shouldCssHideModal = loading || isVisualizationLoading;
67
+ const loadingInProgress = loading || isVisualizationLoading;
68
68
  const handleClose = () => {
69
69
  if (isDirty) {
70
70
  onInterpretationUpdate();
@@ -80,6 +80,14 @@ const InterpretationModal = _ref2 => {
80
80
  id: interpretationId
81
81
  });
82
82
  };
83
+ const onLikeToggled = _ref3 => {
84
+ let {
85
+ likedBy
86
+ } = _ref3;
87
+ setIsDirty(true);
88
+ interpretation.likedBy = likedBy;
89
+ interpretation.likes = likedBy.length;
90
+ };
83
91
  const onInterpretationDeleted = () => {
84
92
  setIsDirty(false);
85
93
  onInterpretationUpdate();
@@ -92,11 +100,11 @@ const InterpretationModal = _ref2 => {
92
100
  });
93
101
  }
94
102
  }, [interpretationId, refetch]);
95
- return /*#__PURE__*/React.createElement(React.Fragment, null, shouldCssHideModal && /*#__PURE__*/React.createElement(Layer, null, /*#__PURE__*/React.createElement(CenteredContent, null, /*#__PURE__*/React.createElement(CircularLoader, null))), /*#__PURE__*/React.createElement(Modal, {
103
+ return /*#__PURE__*/React.createElement(React.Fragment, null, loadingInProgress && /*#__PURE__*/React.createElement(Layer, null, /*#__PURE__*/React.createElement(CenteredContent, null, /*#__PURE__*/React.createElement(CircularLoader, null))), /*#__PURE__*/React.createElement(Modal, {
96
104
  fluid: true,
97
105
  onClose: handleClose,
98
106
  className: cx(modalCSS.className, {
99
- hidden: shouldCssHideModal
107
+ hidden: loadingInProgress
100
108
  }),
101
109
  dataTest: "interpretation-modal"
102
110
  }, /*#__PURE__*/React.createElement("h1", {
@@ -134,7 +142,8 @@ const InterpretationModal = _ref2 => {
134
142
  onInterpretationDeleted: onInterpretationDeleted,
135
143
  onThreadUpdated: onThreadUpdated,
136
144
  initialFocus: initialFocus,
137
- downloadMenuComponent: downloadMenuComponent
145
+ downloadMenuComponent: downloadMenuComponent,
146
+ onLikeToggled: onLikeToggled
138
147
  }))))), /*#__PURE__*/React.createElement(ModalActions, null, /*#__PURE__*/React.createElement(Button, {
139
148
  disabled: fetching,
140
149
  onClick: handleClose
@@ -14,9 +14,11 @@ const InterpretationThread = _ref => {
14
14
  fetching,
15
15
  interpretation,
16
16
  onInterpretationDeleted,
17
+ onLikeToggled,
17
18
  initialFocus,
18
19
  onThreadUpdated,
19
- downloadMenuComponent: DownloadMenu
20
+ downloadMenuComponent: DownloadMenu,
21
+ dashboardRedirectUrl
20
22
  } = _ref;
21
23
  const {
22
24
  fromServerDate
@@ -31,30 +33,33 @@ const InterpretationThread = _ref => {
31
33
  }, [initialFocus]);
32
34
  const interpretationAccess = getInterpretationAccess(interpretation, currentUser);
33
35
  return /*#__PURE__*/React.createElement("div", {
34
- className: "jsx-1846557365" + " " + (cx('container', {
35
- fetching
36
+ className: "jsx-3292109121" + " " + (cx('container', {
37
+ fetching,
38
+ dashboard: !!dashboardRedirectUrl
36
39
  }) || "")
37
40
  }, /*#__PURE__*/React.createElement("div", {
38
- className: "jsx-1846557365" + " " + 'title'
41
+ className: "jsx-3292109121" + " " + 'title'
39
42
  }, /*#__PURE__*/React.createElement(IconClock16, {
40
43
  color: colors.grey700
41
44
  }), moment(fromServerDate(interpretation.created)).format('LLL')), DownloadMenu && /*#__PURE__*/React.createElement(DownloadMenu, {
42
45
  relativePeriodDate: interpretation.created,
43
- className: "jsx-1846557365"
46
+ className: "jsx-3292109121"
44
47
  }), /*#__PURE__*/React.createElement("div", {
45
- className: "jsx-1846557365" + " " + 'thread'
48
+ className: "jsx-3292109121" + " " + 'thread'
46
49
  }, /*#__PURE__*/React.createElement(Interpretation, {
47
50
  currentUser: currentUser,
48
51
  interpretation: interpretation,
52
+ onLikeToggled: onLikeToggled,
49
53
  onReplyIconClick: interpretationAccess.comment ? () => {
50
54
  var _focusRef$current;
51
55
  return (_focusRef$current = focusRef.current) === null || _focusRef$current === void 0 ? void 0 : _focusRef$current.focus();
52
56
  } : null,
53
57
  onUpdated: () => onThreadUpdated(true),
54
58
  onDeleted: onInterpretationDeleted,
59
+ dashboardRedirectUrl: dashboardRedirectUrl,
55
60
  isInThread: true
56
61
  }), /*#__PURE__*/React.createElement("div", {
57
- className: "jsx-1846557365" + " " + 'comments'
62
+ className: "jsx-3292109121" + " " + 'comments'
58
63
  }, interpretation.comments.map(comment => /*#__PURE__*/React.createElement(Comment, {
59
64
  key: comment.id,
60
65
  comment: comment,
@@ -68,14 +73,16 @@ const InterpretationThread = _ref => {
68
73
  onSave: () => onThreadUpdated(true),
69
74
  focusRef: focusRef
70
75
  }), /*#__PURE__*/React.createElement(_JSXStyle, {
71
- id: "1846557365"
72
- }, [".thread.jsx-1846557365{margin-top:var(--spacers-dp16);overflow-y:auto;-webkit-scroll-behavior:smooth;-moz-scroll-behavior:smooth;-ms-scroll-behavior:smooth;scroll-behavior:smooth;}", ".container.jsx-1846557365{position:relative;overflow:auto;max-height:calc(100vh - 285px);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}", ".container.fetching.jsx-1846557365::before{content:'';position:absolute;inset:0px;background-color:rgba(255,255,255,0.8);}", ".container.fetching.jsx-1846557365::after{content:'';position:absolute;top:calc(50% - 12px);left:calc(50% - 12px);width:24px;height:24px;border-width:4px;border-style:solid;border-color:rgba(110,122,138,0.15) rgba(110,122,138,0.15) rgb(20,124,215);border-image:initial;border-radius:50%;-webkit-animation:1s linear 0s infinite normal none running rotation-jsx-1846557365;animation:1s linear 0s infinite normal none running rotation-jsx-1846557365;}", ".title.jsx-1846557365{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:var(--spacers-dp8);color:var(--colors-grey900);font-size:14px;line-height:18px;}", ".comments.jsx-1846557365{padding-left:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding-top:var(--spacers-dp4);gap:var(--spacers-dp4);}", "@-webkit-keyframes rotation-jsx-1846557365{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}", "@keyframes rotation-jsx-1846557365{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}"]));
76
+ id: "3292109121"
77
+ }, [".thread.jsx-3292109121{margin-top:var(--spacers-dp16);overflow-y:auto;-webkit-scroll-behavior:smooth;-moz-scroll-behavior:smooth;-ms-scroll-behavior:smooth;scroll-behavior:smooth;}", ".dashboard.jsx-3292109121 .thread.jsx-3292109121{overflow-y:hidden;}", ".container.jsx-3292109121{position:relative;overflow:auto;max-height:calc(100vh - 285px);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}", ".container.dashboard.jsx-3292109121{max-height:none;}", ".container.fetching.jsx-3292109121::before{content:'';position:absolute;inset:0px;background-color:rgba(255,255,255,0.8);}", ".container.fetching.jsx-3292109121::after{content:'';position:absolute;top:calc(50% - 12px);left:calc(50% - 12px);width:24px;height:24px;border-width:4px;border-style:solid;border-color:rgba(110,122,138,0.15) rgba(110,122,138,0.15) rgb(20,124,215);border-image:initial;border-radius:50%;-webkit-animation:1s linear 0s infinite normal none running rotation-jsx-3292109121;animation:1s linear 0s infinite normal none running rotation-jsx-3292109121;}", ".title.jsx-3292109121{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:var(--spacers-dp8);color:var(--colors-grey900);font-size:14px;line-height:18px;}", ".comments.jsx-3292109121{padding-left:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding-top:var(--spacers-dp4);gap:var(--spacers-dp4);}", "@-webkit-keyframes rotation-jsx-3292109121{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}", "@keyframes rotation-jsx-3292109121{0%{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);}100%{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}"]));
73
78
  };
74
79
  InterpretationThread.propTypes = {
75
80
  currentUser: PropTypes.object.isRequired,
76
81
  fetching: PropTypes.bool.isRequired,
77
82
  interpretation: PropTypes.object.isRequired,
78
83
  onInterpretationDeleted: PropTypes.func.isRequired,
84
+ onLikeToggled: PropTypes.func.isRequired,
85
+ dashboardRedirectUrl: PropTypes.string,
79
86
  downloadMenuComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
80
87
  initialFocus: PropTypes.bool,
81
88
  onThreadUpdated: PropTypes.func
@@ -1 +1,2 @@
1
- export { InterpretationModal } from './InterpretationModal.js';
1
+ export { InterpretationModal } from './InterpretationModal.js';
2
+ export { InterpretationThread } from './InterpretationThread.js';
@@ -21,9 +21,11 @@ export const InterpretationList = _ref => {
21
21
  currentUser,
22
22
  interpretations,
23
23
  onInterpretationClick,
24
+ onLikeToggled,
24
25
  onReplyIconClick,
25
26
  refresh,
26
- disabled
27
+ disabled,
28
+ dashboardRedirectUrl
27
29
  } = _ref;
28
30
  const {
29
31
  fromServerDate
@@ -57,10 +59,12 @@ export const InterpretationList = _ref => {
57
59
  interpretation: interpretation,
58
60
  currentUser: currentUser,
59
61
  onClick: onInterpretationClick,
62
+ onLikeToggled: onLikeToggled,
60
63
  onReplyIconClick: onReplyIconClick,
61
64
  onDeleted: refresh,
62
65
  onUpdated: refresh,
63
- disabled: disabled
66
+ disabled: disabled,
67
+ dashboardRedirectUrl: dashboardRedirectUrl
64
68
  }))))), /*#__PURE__*/React.createElement(_JSXStyle, {
65
69
  id: "4058400613",
66
70
  dynamic: [spacers.dp8, spacers.dp8, spacers.dp16, colors.grey800, spacers.dp12, spacers.dp12, spacers.dp32, spacers.dp4]
@@ -71,6 +75,8 @@ InterpretationList.propTypes = {
71
75
  interpretations: PropTypes.array.isRequired,
72
76
  refresh: PropTypes.func.isRequired,
73
77
  onInterpretationClick: PropTypes.func.isRequired,
78
+ onLikeToggled: PropTypes.func.isRequired,
74
79
  onReplyIconClick: PropTypes.func.isRequired,
80
+ dashboardRedirectUrl: PropTypes.string,
75
81
  disabled: PropTypes.bool
76
82
  };
@@ -31,17 +31,19 @@ export const InterpretationsUnit = /*#__PURE__*/forwardRef((_ref2, ref) => {
31
31
  onInterpretationClick,
32
32
  onReplyIconClick,
33
33
  disabled,
34
- renderId
34
+ renderId,
35
+ dashboardRedirectUrl
35
36
  } = _ref2;
36
37
  const [isExpanded, setIsExpanded] = useState(true);
38
+ const [interpretations, setInterpretations] = useState([]);
37
39
  const showNoTimeDimensionHelpText = type === 'eventVisualization' && !visualizationHasTimeDimension;
38
40
  const {
39
- data,
40
41
  loading,
41
42
  fetching,
42
43
  refetch
43
44
  } = useDataQuery(interpretationsQuery, {
44
- lazy: true
45
+ lazy: true,
46
+ onComplete: data => setInterpretations(data.interpretations.interpretations)
45
47
  });
46
48
  const onCompleteAction = useCallback(() => {
47
49
  refetch({
@@ -60,6 +62,15 @@ export const InterpretationsUnit = /*#__PURE__*/forwardRef((_ref2, ref) => {
60
62
  });
61
63
  }
62
64
  }, [type, id, renderId, refetch]);
65
+ const onLikeToggled = _ref3 => {
66
+ let {
67
+ id,
68
+ likedBy
69
+ } = _ref3;
70
+ const interpretation = interpretations.find(interp => interp.id === id);
71
+ interpretation.likedBy = likedBy;
72
+ interpretation.likes = likedBy.length;
73
+ };
63
74
  return /*#__PURE__*/React.createElement("div", {
64
75
  className: _JSXStyle.dynamic([["4120713286", [spacers.dp16, colors.grey400, colors.white, spacers.dp32, colors.grey900]]]) + " " + (cx('container', {
65
76
  expanded: isExpanded
@@ -81,7 +92,7 @@ export const InterpretationsUnit = /*#__PURE__*/forwardRef((_ref2, ref) => {
81
92
  className: _JSXStyle.dynamic([["4120713286", [spacers.dp16, colors.grey400, colors.white, spacers.dp32, colors.grey900]]]) + " " + "loader"
82
93
  }, /*#__PURE__*/React.createElement(CircularLoader, {
83
94
  small: true
84
- })), data && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(InterpretationForm, {
95
+ })), interpretations && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(InterpretationForm, {
85
96
  currentUser: currentUser,
86
97
  type: type,
87
98
  id: id,
@@ -90,11 +101,13 @@ export const InterpretationsUnit = /*#__PURE__*/forwardRef((_ref2, ref) => {
90
101
  showNoTimeDimensionHelpText: showNoTimeDimensionHelpText
91
102
  }), /*#__PURE__*/React.createElement(InterpretationList, {
92
103
  currentUser: currentUser,
93
- interpretations: data.interpretations.interpretations,
104
+ interpretations: interpretations,
94
105
  onInterpretationClick: onInterpretationClick,
106
+ onLikeToggled: onLikeToggled,
95
107
  onReplyIconClick: onReplyIconClick,
96
108
  refresh: onCompleteAction,
97
- disabled: disabled
109
+ disabled: disabled,
110
+ dashboardRedirectUrl: dashboardRedirectUrl
98
111
  }))), /*#__PURE__*/React.createElement(_JSXStyle, {
99
112
  id: "4120713286",
100
113
  dynamic: [spacers.dp16, colors.grey400, colors.white, spacers.dp32, colors.grey900]
@@ -109,6 +122,7 @@ InterpretationsUnit.propTypes = {
109
122
  currentUser: PropTypes.object.isRequired,
110
123
  id: PropTypes.string.isRequired,
111
124
  type: PropTypes.string.isRequired,
125
+ dashboardRedirectUrl: PropTypes.string,
112
126
  disabled: PropTypes.bool,
113
127
  renderId: PropTypes.number,
114
128
  visualizationHasTimeDimension: PropTypes.bool,
@@ -1,5 +1,5 @@
1
1
  import i18n from '@dhis2/d2-i18n';
2
- import { Button, SharingDialog, IconReply16, IconShare16, IconThumbUp16, IconEdit16 } from '@dhis2/ui';
2
+ import { Button, SharingDialog, IconReply16, IconShare16, IconThumbUp16, IconEdit16, IconLaunch16, IconView16 } from '@dhis2/ui';
3
3
  import PropTypes from 'prop-types';
4
4
  import React, { useState } from 'react';
5
5
  import { Message, MessageStatsBar, MessageIconButton, getInterpretationAccess } from '../index.js';
@@ -15,7 +15,9 @@ export const Interpretation = _ref => {
15
15
  onDeleted,
16
16
  disabled,
17
17
  onReplyIconClick,
18
- isInThread
18
+ dashboardRedirectUrl,
19
+ isInThread,
20
+ onLikeToggled
19
21
  } = _ref;
20
22
  const [isUpdateMode, setIsUpdateMode] = useState(false);
21
23
  const [showSharingDialog, setShowSharingDialog] = useState(false);
@@ -26,9 +28,12 @@ export const Interpretation = _ref => {
26
28
  } = useLike({
27
29
  interpretation,
28
30
  currentUser,
29
- onComplete: onUpdated
31
+ onComplete: likedBy => onLikeToggled({
32
+ id: interpretation.id,
33
+ likedBy
34
+ })
30
35
  });
31
- const shouldShowButton = !!onClick && !disabled;
36
+ const shouldShowButton = Boolean(!!onClick && !disabled & !dashboardRedirectUrl);
32
37
  const interpretationAccess = getInterpretationAccess(interpretation, currentUser);
33
38
  let tooltip = i18n.t('Reply');
34
39
  if (!interpretationAccess.comment) {
@@ -42,6 +47,9 @@ export const Interpretation = _ref => {
42
47
  tooltip = i18n.t('View replies');
43
48
  }
44
49
  }
50
+
51
+ // Maps still uses old url style /?id= instead of hash
52
+ const getAppInterpretationUrl = () => dashboardRedirectUrl.includes('?') ? `${dashboardRedirectUrl}&interpretationId=${interpretation.id}` : `${dashboardRedirectUrl}?interpretationId=${interpretation.id}`;
45
53
  return isUpdateMode ? /*#__PURE__*/React.createElement(InterpretationUpdateForm, {
46
54
  close: () => setIsUpdateMode(false),
47
55
  id: interpretation.id,
@@ -68,6 +76,16 @@ export const Interpretation = _ref => {
68
76
  count: interpretation.comments.length,
69
77
  dataTest: "interpretation-reply-button",
70
78
  viewOnly: isInThread && !interpretationAccess.comment
79
+ }), dashboardRedirectUrl && !isInThread && /*#__PURE__*/React.createElement(MessageIconButton, {
80
+ tooltipContent: i18n.t('See interpretation'),
81
+ iconComponent: IconView16,
82
+ onClick: () => onClick(interpretation.id),
83
+ dataTest: "interpretation-view-button"
84
+ }), dashboardRedirectUrl && /*#__PURE__*/React.createElement(MessageIconButton, {
85
+ tooltipContent: i18n.t('Open in app'),
86
+ iconComponent: IconLaunch16,
87
+ onClick: () => window.open(getAppInterpretationUrl(), '_blank'),
88
+ dataTest: "interpretation-launch-in-app-button"
71
89
  }), interpretationAccess.share && /*#__PURE__*/React.createElement(MessageIconButton, {
72
90
  iconComponent: IconShare16,
73
91
  tooltipContent: i18n.t('Share'),
@@ -99,8 +117,10 @@ Interpretation.propTypes = {
99
117
  currentUser: PropTypes.object.isRequired,
100
118
  interpretation: PropTypes.object.isRequired,
101
119
  onDeleted: PropTypes.func.isRequired,
120
+ onLikeToggled: PropTypes.func.isRequired,
102
121
  onReplyIconClick: PropTypes.func.isRequired,
103
122
  onUpdated: PropTypes.func.isRequired,
123
+ dashboardRedirectUrl: PropTypes.string,
104
124
  disabled: PropTypes.bool,
105
125
  isInThread: PropTypes.bool,
106
126
  onClick: PropTypes.func
@@ -18,12 +18,22 @@ const useLike = _ref => {
18
18
  const [like, {
19
19
  loading: likeLoading
20
20
  }] = useDataMutation(likeMutationRef.current, {
21
- onComplete
21
+ onComplete: () => {
22
+ const newLikedBy = interpretation.likedBy.concat({
23
+ id: currentUser.id
24
+ });
25
+ setIsLikedByCurrentUser(true);
26
+ onComplete(newLikedBy);
27
+ }
22
28
  });
23
29
  const [unlike, {
24
30
  loading: unlikeLoading
25
31
  }] = useDataMutation(unlikeMutationRef.current, {
26
- onComplete
32
+ onComplete: () => {
33
+ const newLikedBy = interpretation.likedBy.filter(lb => lb.id !== currentUser.id);
34
+ setIsLikedByCurrentUser(false);
35
+ onComplete(newLikedBy);
36
+ }
27
37
  });
28
38
  const [isLikedByCurrentUser, setIsLikedByCurrentUser] = useState(false);
29
39
  const toggleLike = () => {
package/build/es/index.js CHANGED
@@ -16,8 +16,8 @@ export { preparePayloadForSaveAs } from './components/FileMenu/utils.js';
16
16
  export { default as VisTypeIcon } from './components/VisTypeIcon.js';
17
17
  export { default as LegendKey } from './components/LegendKey/LegendKey.js';
18
18
  export { default as AboutAOUnit } from './components/AboutAOUnit/AboutAOUnit.js';
19
- export { InterpretationsUnit } from './components/Interpretations/InterpretationsUnit/InterpretationsUnit.js';
20
- export { InterpretationModal } from './components/Interpretations/InterpretationModal/InterpretationModal.js';
19
+ export { InterpretationsUnit } from './components/Interpretations/InterpretationsUnit/index.js';
20
+ export { InterpretationModal, InterpretationThread } from './components/Interpretations/InterpretationModal/index.js';
21
21
  export * from './components/Toolbar/index.js';
22
22
  export { TranslationDialog } from './components/TranslationDialog/index.js';
23
23
  export { OfflineTooltip } from './components/OfflineTooltip.js';
@@ -125,8 +125,9 @@
125
125
  "View replies": "View replies",
126
126
  "Unlike": "Unlike",
127
127
  "Like": "Like",
128
- "Share": "Share",
129
128
  "See interpretation": "See interpretation",
129
+ "Open in app": "Open in app",
130
+ "Share": "Share",
130
131
  "Manage sharing": "Manage sharing",
131
132
  "Could not update interpretation": "Could not update interpretation",
132
133
  "Enter interpretation text": "Enter interpretation text",
@@ -30,8 +30,9 @@ export default function (series, layout, metaData, dashboard) {
30
30
  }
31
31
 
32
32
  // DHIS2-578: allow for optional custom subtitle
33
- if (isString(layout.subtitle)) {
34
- subtitle.text = layout.subtitle;
33
+ const customSubtitle = layout.subtitle && layout.displaySubtitle || layout.subtitle;
34
+ if (isString(customSubtitle) && customSubtitle.length) {
35
+ subtitle.text = customSubtitle;
35
36
  } else {
36
37
  const filterTitle = getFilterText(layout.filters, metaData);
37
38
  switch (layout.type) {
@@ -31,8 +31,9 @@ export default function (layout, metaData, dashboard) {
31
31
  if (layout.hideTitle) {
32
32
  return title;
33
33
  }
34
- if (isString(layout.title) && layout.title.length) {
35
- title.text = layout.title;
34
+ const customTitle = layout.title && layout.displayTitle || layout.title;
35
+ if (isString(customTitle) && customTitle.length) {
36
+ title.text = customTitle;
36
37
  } else {
37
38
  switch (layout.type) {
38
39
  case VIS_TYPE_GAUGE:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/analytics",
3
- "version": "26.3.1",
3
+ "version": "26.4.1",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "exports": {