@atlaskit/smart-hooks 0.0.2 → 0.1.2
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 +25 -0
- package/dist/cjs/services/use-function-usage-tracking/index.js +16 -9
- package/dist/cjs/services/use-user-recommendations/analytics.js +82 -0
- package/dist/cjs/services/use-user-recommendations/index.js +118 -35
- package/dist/cjs/services/use-user-recommendations/ufoExperiences.js +18 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/services/use-function-usage-tracking/index.js +8 -3
- package/dist/es2019/services/use-user-recommendations/analytics.js +51 -0
- package/dist/es2019/services/use-user-recommendations/index.js +84 -24
- package/dist/es2019/services/use-user-recommendations/ufoExperiences.js +9 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/services/use-function-usage-tracking/index.js +14 -8
- package/dist/esm/services/use-user-recommendations/analytics.js +61 -0
- package/dist/esm/services/use-user-recommendations/index.js +109 -37
- package/dist/esm/services/use-user-recommendations/ufoExperiences.js +9 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/services/use-function-usage-tracking/index.d.ts +1 -1
- package/dist/types/services/use-user-recommendations/analytics.d.ts +20 -0
- package/dist/types/services/use-user-recommendations/index.d.ts +4 -5
- package/dist/types/services/use-user-recommendations/ufoExperiences.d.ts +2 -0
- package/package.json +12 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @atlaskit/smart-hooks
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 0.1.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`c7cd471ace6`](https://bitbucket.org/atlassian/atlassian-frontend/commits/c7cd471ace6) - [ux] add abortcontrollers, replace ufo experience identifier, memoization for search query
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
|
|
16
|
+
## 0.1.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [`6a3107dd76a`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6a3107dd76a) - Adding analytics + UFO
|
|
21
|
+
|
|
22
|
+
## 0.0.3
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
|
|
3
28
|
## 0.0.2
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -11,21 +11,28 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
11
11
|
|
|
12
12
|
var _react = require("react");
|
|
13
13
|
|
|
14
|
+
var _memoizeOne = _interopRequireDefault(require("memoize-one"));
|
|
15
|
+
|
|
14
16
|
function useFunctionUsageTracking(trackedFunction) {
|
|
15
17
|
var _useState = (0, _react.useState)(false),
|
|
16
18
|
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
17
19
|
isUsed = _useState2[0],
|
|
18
|
-
setIsUsed = _useState2[1];
|
|
20
|
+
setIsUsed = _useState2[1]; // trackingFunction needs to be stable for hook consumers...
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
var trackingFunction = (0, _react.useMemo)(function () {
|
|
21
|
-
return function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
return (// ...and the function that the factory returns needs to be stable for hook consumers as well
|
|
25
|
+
(0, _memoizeOne.default)(function () {
|
|
26
|
+
if (!isUsed) {
|
|
27
|
+
setIsUsed(true);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return trackedFunction;
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
}, // isUsed deliberately left out to avoid new tracking fn being generated
|
|
34
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
+
[trackedFunction]);
|
|
29
36
|
return {
|
|
30
37
|
isUsed: isUsed,
|
|
31
38
|
trackingFunction: trackingFunction
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.fireUserSelectedEvent = exports.findUserPosition = exports.createDefaultAttributes = void 0;
|
|
9
|
+
|
|
10
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
|
+
|
|
12
|
+
var _analyticsGasTypes = require("@atlaskit/analytics-gas-types");
|
|
13
|
+
|
|
14
|
+
var _analyticsListeners = require("@atlaskit/analytics-listeners");
|
|
15
|
+
|
|
16
|
+
var _analyticsNext = require("@atlaskit/analytics-next");
|
|
17
|
+
|
|
18
|
+
var _version = require("../../version.json");
|
|
19
|
+
|
|
20
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
21
|
+
|
|
22
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
23
|
+
|
|
24
|
+
var createAndFireEventInElementsChannel = (0, _analyticsNext.createAndFireEvent)(_analyticsListeners.FabricChannel.elements);
|
|
25
|
+
|
|
26
|
+
var createEvent = function createEvent(eventType, action, actionSubject) {
|
|
27
|
+
var attributes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
28
|
+
return {
|
|
29
|
+
eventType: eventType,
|
|
30
|
+
action: action,
|
|
31
|
+
actionSubject: actionSubject,
|
|
32
|
+
source: '@atlaskit/smart-hooks/use-user-recommendations',
|
|
33
|
+
attributes: _objectSpread({
|
|
34
|
+
packageName: _version.name,
|
|
35
|
+
packageVersion: _version.version
|
|
36
|
+
}, attributes)
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
var findUserPosition = function findUserPosition(loadedUsers, userId) {
|
|
41
|
+
return loadedUsers.findIndex(function (val) {
|
|
42
|
+
return val.id === userId;
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
exports.findUserPosition = findUserPosition;
|
|
47
|
+
|
|
48
|
+
var createDefaultAttributes = function createDefaultAttributes(props, renderId, sessionId, query) {
|
|
49
|
+
var fieldId = props.fieldId,
|
|
50
|
+
objectId = props.objectId,
|
|
51
|
+
containerId = props.containerId,
|
|
52
|
+
childObjectId = props.childObjectId,
|
|
53
|
+
preload = props.preload,
|
|
54
|
+
includeTeams = props.includeTeams,
|
|
55
|
+
productKey = props.productKey,
|
|
56
|
+
principalId = props.principalId,
|
|
57
|
+
tenantId = props.tenantId,
|
|
58
|
+
maxNumberOfResults = props.maxNumberOfResults;
|
|
59
|
+
return {
|
|
60
|
+
context: fieldId,
|
|
61
|
+
childObjectId: childObjectId,
|
|
62
|
+
containerId: containerId,
|
|
63
|
+
includeTeams: includeTeams,
|
|
64
|
+
maxNumberOfResults: maxNumberOfResults,
|
|
65
|
+
objectId: objectId,
|
|
66
|
+
preload: preload,
|
|
67
|
+
principalId: principalId,
|
|
68
|
+
productKey: productKey,
|
|
69
|
+
queryLength: (query || '').length,
|
|
70
|
+
renderId: renderId,
|
|
71
|
+
sessionId: sessionId,
|
|
72
|
+
tenantId: tenantId
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
exports.createDefaultAttributes = createDefaultAttributes;
|
|
77
|
+
|
|
78
|
+
var fireUserSelectedEvent = function fireUserSelectedEvent(createAnalyticsEvent, payloadAttributes) {
|
|
79
|
+
return createAndFireEventInElementsChannel(createEvent(_analyticsGasTypes.TRACK_EVENT_TYPE, 'selected', 'user', payloadAttributes))(createAnalyticsEvent);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
exports.fireUserSelectedEvent = fireUserSelectedEvent;
|
|
@@ -19,10 +19,20 @@ var _react = require("react");
|
|
|
19
19
|
|
|
20
20
|
var _debounce = _interopRequireDefault(require("lodash/debounce"));
|
|
21
21
|
|
|
22
|
+
var _memoizeOne = _interopRequireDefault(require("memoize-one"));
|
|
23
|
+
|
|
24
|
+
var _uuid = require("uuid");
|
|
25
|
+
|
|
26
|
+
var _analyticsNext = require("@atlaskit/analytics-next");
|
|
27
|
+
|
|
22
28
|
var _smartCommon = require("@atlaskit/smart-common");
|
|
23
29
|
|
|
24
30
|
var _useFunctionUsageTracking = _interopRequireDefault(require("../use-function-usage-tracking"));
|
|
25
31
|
|
|
32
|
+
var _analytics = require("./analytics");
|
|
33
|
+
|
|
34
|
+
var _ufoExperiences = require("./ufoExperiences");
|
|
35
|
+
|
|
26
36
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
27
37
|
|
|
28
38
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
@@ -77,16 +87,58 @@ var useUserRecommendations = function useUserRecommendations(props) {
|
|
|
77
87
|
error = _useState6[0],
|
|
78
88
|
setError = _useState6[1];
|
|
79
89
|
|
|
90
|
+
var _useState7 = (0, _react.useState)(function () {
|
|
91
|
+
return (0, _uuid.v4)();
|
|
92
|
+
}),
|
|
93
|
+
_useState8 = (0, _slicedToArray2.default)(_useState7, 1),
|
|
94
|
+
renderId = _useState8[0];
|
|
95
|
+
|
|
96
|
+
var sessionId = (0, _react.useRef)('');
|
|
97
|
+
var currentQuery = (0, _react.useRef)('');
|
|
98
|
+
var lastAbortController = (0, _react.useRef)();
|
|
99
|
+
|
|
100
|
+
var _useAnalyticsEvents = (0, _analyticsNext.useAnalyticsEvents)(),
|
|
101
|
+
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
|
|
102
|
+
|
|
103
|
+
var getAnalyticsAttributes = (0, _react.useCallback)(function () {
|
|
104
|
+
return _objectSpread({}, (0, _analytics.createDefaultAttributes)({
|
|
105
|
+
fieldId: fieldId,
|
|
106
|
+
objectId: objectId,
|
|
107
|
+
containerId: containerId,
|
|
108
|
+
childObjectId: childObjectId,
|
|
109
|
+
preload: preload,
|
|
110
|
+
includeTeams: includeTeams,
|
|
111
|
+
productKey: productKey,
|
|
112
|
+
principalId: principalId,
|
|
113
|
+
tenantId: tenantId,
|
|
114
|
+
maxNumberOfResults: maxNumberOfResults
|
|
115
|
+
}, renderId, sessionId.current, currentQuery.current));
|
|
116
|
+
}, [childObjectId, containerId, fieldId, includeTeams, maxNumberOfResults, objectId, preload, principalId, productKey, renderId, tenantId]);
|
|
80
117
|
var fetchRecommendations = (0, _react.useCallback)( /*#__PURE__*/function () {
|
|
81
|
-
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(query
|
|
82
|
-
var
|
|
118
|
+
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(query) {
|
|
119
|
+
var _lastAbortController$, currentController, recommendedUsersResult;
|
|
120
|
+
|
|
83
121
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
84
122
|
while (1) {
|
|
85
123
|
switch (_context.prev = _context.next) {
|
|
86
124
|
case 0:
|
|
87
|
-
setIsLoading(true);
|
|
88
|
-
|
|
89
|
-
|
|
125
|
+
setIsLoading(true); // restart session
|
|
126
|
+
|
|
127
|
+
sessionId.current = (0, _uuid.v4)();
|
|
128
|
+
|
|
129
|
+
_ufoExperiences.UsersFetchedUfoExperience.getInstance(fieldId).start();
|
|
130
|
+
|
|
131
|
+
_context.prev = 3;
|
|
132
|
+
|
|
133
|
+
if (lastAbortController.current) {
|
|
134
|
+
(_lastAbortController$ = lastAbortController.current) === null || _lastAbortController$ === void 0 ? void 0 : _lastAbortController$.abort();
|
|
135
|
+
} // in case there are still IE users, check AbortController before init,
|
|
136
|
+
// IE has no timeout applied on fetch since AbortController isn't available
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
currentController = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
140
|
+
lastAbortController.current = currentController;
|
|
141
|
+
_context.next = 9;
|
|
90
142
|
return (0, _smartCommon.fetchUserRecommendations)({
|
|
91
143
|
baseUrl: baseUrl,
|
|
92
144
|
context: {
|
|
@@ -98,7 +150,7 @@ var useUserRecommendations = function useUserRecommendations(props) {
|
|
|
98
150
|
productKey: productKey,
|
|
99
151
|
contextType: fieldId,
|
|
100
152
|
siteId: tenantId,
|
|
101
|
-
sessionId: sessionId
|
|
153
|
+
sessionId: sessionId.current
|
|
102
154
|
},
|
|
103
155
|
includeUsers: includeUsers,
|
|
104
156
|
includeGroups: includeGroups,
|
|
@@ -106,61 +158,92 @@ var useUserRecommendations = function useUserRecommendations(props) {
|
|
|
106
158
|
maxNumberOfResults: maxNumberOfResults,
|
|
107
159
|
searchQuery: cpusSearchQuery,
|
|
108
160
|
query: query
|
|
161
|
+
}, currentController === null || currentController === void 0 ? void 0 : currentController.signal);
|
|
162
|
+
|
|
163
|
+
case 9:
|
|
164
|
+
recommendedUsersResult = _context.sent;
|
|
165
|
+
currentQuery.current = query !== null && query !== void 0 ? query : '';
|
|
166
|
+
|
|
167
|
+
_ufoExperiences.UsersFetchedUfoExperience.getInstance(fieldId).success({
|
|
168
|
+
metadata: _objectSpread(_objectSpread({}, getAnalyticsAttributes()), {}, {
|
|
169
|
+
loadedUsersSize: recommendedUsersResult.length
|
|
170
|
+
})
|
|
109
171
|
});
|
|
110
172
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
setRecommendations(recommendedUsers);
|
|
114
|
-
_context.next = 11;
|
|
173
|
+
setRecommendations(recommendedUsersResult);
|
|
174
|
+
_context.next = 24;
|
|
115
175
|
break;
|
|
116
176
|
|
|
117
|
-
case
|
|
118
|
-
_context.prev =
|
|
119
|
-
_context.t0 = _context["catch"](
|
|
177
|
+
case 15:
|
|
178
|
+
_context.prev = 15;
|
|
179
|
+
_context.t0 = _context["catch"](3);
|
|
180
|
+
|
|
181
|
+
if (!(_context.t0 instanceof Error && _context.t0.name === 'AbortError')) {
|
|
182
|
+
_context.next = 22;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Request aborted client-side
|
|
187
|
+
_ufoExperiences.UsersFetchedUfoExperience.getInstance(fieldId).abort(); // return early to prevent disabling isLoading
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
return _context.abrupt("return");
|
|
191
|
+
|
|
192
|
+
case 22:
|
|
193
|
+
_ufoExperiences.UsersFetchedUfoExperience.getInstance(fieldId).failure({
|
|
194
|
+
metadata: getAnalyticsAttributes()
|
|
195
|
+
});
|
|
196
|
+
|
|
120
197
|
setError(_context.t0);
|
|
121
198
|
|
|
122
|
-
case
|
|
123
|
-
setIsLoading(false);
|
|
199
|
+
case 24:
|
|
200
|
+
setIsLoading(false); // clear abort controller
|
|
201
|
+
|
|
202
|
+
lastAbortController.current = undefined;
|
|
124
203
|
|
|
125
|
-
case
|
|
204
|
+
case 26:
|
|
126
205
|
case "end":
|
|
127
206
|
return _context.stop();
|
|
128
207
|
}
|
|
129
208
|
}
|
|
130
|
-
}, _callee, null, [[
|
|
209
|
+
}, _callee, null, [[3, 15]]);
|
|
131
210
|
}));
|
|
132
211
|
|
|
133
|
-
return function (_x
|
|
212
|
+
return function (_x) {
|
|
134
213
|
return _ref.apply(this, arguments);
|
|
135
214
|
};
|
|
136
|
-
}(), [baseUrl, childObjectId, containerId,
|
|
137
|
-
var
|
|
138
|
-
return (0, _debounce.default)(fetchRecommendations, debounceTimeMs);
|
|
215
|
+
}(), [baseUrl, childObjectId, containerId, objectId, principalId, productAttributes, productKey, fieldId, tenantId, includeUsers, includeGroups, includeTeams, maxNumberOfResults, cpusSearchQuery, getAnalyticsAttributes]);
|
|
216
|
+
var memoizedDebouncedFetchRecommendations = (0, _react.useMemo)(function () {
|
|
217
|
+
return (0, _memoizeOne.default)((0, _debounce.default)(fetchRecommendations, debounceTimeMs));
|
|
139
218
|
}, [fetchRecommendations, debounceTimeMs]);
|
|
140
219
|
(0, _react.useEffect)(function () {
|
|
141
220
|
if (preload) {
|
|
142
|
-
|
|
221
|
+
memoizedDebouncedFetchRecommendations('');
|
|
143
222
|
}
|
|
144
|
-
}, [preload,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
223
|
+
}, [preload, memoizedDebouncedFetchRecommendations]);
|
|
224
|
+
var onInputChange = (0, _react.useCallback)(function (query) {
|
|
225
|
+
memoizedDebouncedFetchRecommendations(query !== null && query !== void 0 ? query : '');
|
|
226
|
+
}, [memoizedDebouncedFetchRecommendations]);
|
|
227
|
+
var onUserSelect = (0, _react.useCallback)(function (userId) {
|
|
228
|
+
(0, _analytics.fireUserSelectedEvent)(createAnalyticsEvent, _objectSpread(_objectSpread({}, getAnalyticsAttributes()), {}, {
|
|
229
|
+
selectedUser: userId,
|
|
230
|
+
loadedUsersSize: recommendations.length,
|
|
231
|
+
position: (0, _analytics.findUserPosition)(recommendations, userId)
|
|
232
|
+
}));
|
|
233
|
+
}, [createAnalyticsEvent, getAnalyticsAttributes, recommendations]);
|
|
151
234
|
|
|
152
235
|
var _useFunctionUsageTrac = (0, _useFunctionUsageTracking.default)(onInputChange),
|
|
153
236
|
isOnInputChangeUsed = _useFunctionUsageTrac.isUsed,
|
|
154
237
|
usageTrackedOnInputChange = _useFunctionUsageTrac.trackingFunction;
|
|
155
238
|
|
|
156
|
-
var _useFunctionUsageTrac2 = (0, _useFunctionUsageTracking.default)(
|
|
157
|
-
|
|
158
|
-
|
|
239
|
+
var _useFunctionUsageTrac2 = (0, _useFunctionUsageTracking.default)(onUserSelect),
|
|
240
|
+
isOnUserSelectUsed = _useFunctionUsageTrac2.isUsed,
|
|
241
|
+
usageTrackedOnUserSelect = _useFunctionUsageTrac2.trackingFunction;
|
|
159
242
|
|
|
160
243
|
return {
|
|
161
|
-
recommendations: isOnInputChangeUsed &&
|
|
162
|
-
|
|
163
|
-
|
|
244
|
+
recommendations: isOnInputChangeUsed && isOnUserSelectUsed ? recommendations : instrumentFailureOption,
|
|
245
|
+
triggerSearchFactory: usageTrackedOnInputChange,
|
|
246
|
+
selectUserFactory: usageTrackedOnUserSelect,
|
|
164
247
|
isLoading: isLoading,
|
|
165
248
|
error: error
|
|
166
249
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.UsersFetchedUfoExperience = void 0;
|
|
7
|
+
|
|
8
|
+
var _ufo = require("@atlaskit/ufo");
|
|
9
|
+
|
|
10
|
+
var COMPONENT_NAME = 'smart-hooks.use-user-recommendations';
|
|
11
|
+
var UsersFetchedUfoExperience = new _ufo.ConcurrentExperience('users-fetched', {
|
|
12
|
+
platform: {
|
|
13
|
+
component: COMPONENT_NAME
|
|
14
|
+
},
|
|
15
|
+
type: _ufo.ExperienceTypes.Load,
|
|
16
|
+
performanceType: _ufo.ExperiencePerformanceTypes.InlineResult
|
|
17
|
+
});
|
|
18
|
+
exports.UsersFetchedUfoExperience = UsersFetchedUfoExperience;
|
package/dist/cjs/version.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react';
|
|
2
|
+
import memoizeOne from 'memoize-one';
|
|
2
3
|
|
|
3
4
|
function useFunctionUsageTracking(trackedFunction) {
|
|
4
|
-
const [isUsed, setIsUsed] = useState(false);
|
|
5
|
-
|
|
5
|
+
const [isUsed, setIsUsed] = useState(false); // trackingFunction needs to be stable for hook consumers...
|
|
6
|
+
|
|
7
|
+
const trackingFunction = useMemo(() => // ...and the function that the factory returns needs to be stable for hook consumers as well
|
|
8
|
+
memoizeOne(() => {
|
|
6
9
|
if (!isUsed) {
|
|
7
10
|
setIsUsed(true);
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
return trackedFunction;
|
|
11
|
-
},
|
|
14
|
+
}), // isUsed deliberately left out to avoid new tracking fn being generated
|
|
15
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
16
|
+
[trackedFunction]);
|
|
12
17
|
return {
|
|
13
18
|
isUsed,
|
|
14
19
|
trackingFunction
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { TRACK_EVENT_TYPE } from '@atlaskit/analytics-gas-types';
|
|
2
|
+
import { FabricChannel } from '@atlaskit/analytics-listeners';
|
|
3
|
+
import { createAndFireEvent } from '@atlaskit/analytics-next';
|
|
4
|
+
import { name as packageName, version as packageVersion } from '../../version.json';
|
|
5
|
+
const createAndFireEventInElementsChannel = createAndFireEvent(FabricChannel.elements);
|
|
6
|
+
|
|
7
|
+
const createEvent = (eventType, action, actionSubject, attributes = {}) => ({
|
|
8
|
+
eventType,
|
|
9
|
+
action,
|
|
10
|
+
actionSubject,
|
|
11
|
+
source: '@atlaskit/smart-hooks/use-user-recommendations',
|
|
12
|
+
attributes: {
|
|
13
|
+
packageName,
|
|
14
|
+
packageVersion,
|
|
15
|
+
...attributes
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const findUserPosition = (loadedUsers, userId) => {
|
|
20
|
+
return loadedUsers.findIndex(val => val.id === userId);
|
|
21
|
+
};
|
|
22
|
+
export const createDefaultAttributes = (props, renderId, sessionId, query) => {
|
|
23
|
+
const {
|
|
24
|
+
fieldId,
|
|
25
|
+
objectId,
|
|
26
|
+
containerId,
|
|
27
|
+
childObjectId,
|
|
28
|
+
preload,
|
|
29
|
+
includeTeams,
|
|
30
|
+
productKey,
|
|
31
|
+
principalId,
|
|
32
|
+
tenantId,
|
|
33
|
+
maxNumberOfResults
|
|
34
|
+
} = props;
|
|
35
|
+
return {
|
|
36
|
+
context: fieldId,
|
|
37
|
+
childObjectId,
|
|
38
|
+
containerId,
|
|
39
|
+
includeTeams,
|
|
40
|
+
maxNumberOfResults,
|
|
41
|
+
objectId,
|
|
42
|
+
preload,
|
|
43
|
+
principalId,
|
|
44
|
+
productKey,
|
|
45
|
+
queryLength: (query || '').length,
|
|
46
|
+
renderId,
|
|
47
|
+
sessionId,
|
|
48
|
+
tenantId
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export const fireUserSelectedEvent = (createAnalyticsEvent, payloadAttributes) => createAndFireEventInElementsChannel(createEvent(TRACK_EVENT_TYPE, 'selected', 'user', payloadAttributes))(createAnalyticsEvent);
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import debounce from 'lodash/debounce';
|
|
3
|
+
import memoizeOne from 'memoize-one';
|
|
4
|
+
import { v4 as uuid } from 'uuid';
|
|
5
|
+
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
3
6
|
import { EntityType, fetchUserRecommendations } from '@atlaskit/smart-common';
|
|
4
7
|
import useFunctionUsageTracking from '../use-function-usage-tracking';
|
|
8
|
+
import { createDefaultAttributes, findUserPosition, fireUserSelectedEvent } from './analytics';
|
|
9
|
+
import { UsersFetchedUfoExperience } from './ufoExperiences';
|
|
5
10
|
const MAX_NUMBER_RESULTS = 25;
|
|
6
11
|
const DEFAULT_DEBOUNCE_TIME_MS = 150;
|
|
7
12
|
const defaultProps = {
|
|
@@ -41,11 +46,44 @@ const useUserRecommendations = props => {
|
|
|
41
46
|
const [recommendations, setRecommendations] = useState([]);
|
|
42
47
|
const [isLoading, setIsLoading] = useState(false);
|
|
43
48
|
const [error, setError] = useState();
|
|
44
|
-
const
|
|
45
|
-
|
|
49
|
+
const [renderId] = useState(() => uuid());
|
|
50
|
+
const sessionId = useRef('');
|
|
51
|
+
const currentQuery = useRef('');
|
|
52
|
+
const lastAbortController = useRef();
|
|
53
|
+
const {
|
|
54
|
+
createAnalyticsEvent
|
|
55
|
+
} = useAnalyticsEvents();
|
|
56
|
+
const getAnalyticsAttributes = useCallback(() => ({ ...createDefaultAttributes({
|
|
57
|
+
fieldId,
|
|
58
|
+
objectId,
|
|
59
|
+
containerId,
|
|
60
|
+
childObjectId,
|
|
61
|
+
preload,
|
|
62
|
+
includeTeams,
|
|
63
|
+
productKey,
|
|
64
|
+
principalId,
|
|
65
|
+
tenantId,
|
|
66
|
+
maxNumberOfResults
|
|
67
|
+
}, renderId, sessionId.current, currentQuery.current)
|
|
68
|
+
}), [childObjectId, containerId, fieldId, includeTeams, maxNumberOfResults, objectId, preload, principalId, productKey, renderId, tenantId]);
|
|
69
|
+
const fetchRecommendations = useCallback(async query => {
|
|
70
|
+
setIsLoading(true); // restart session
|
|
71
|
+
|
|
72
|
+
sessionId.current = uuid();
|
|
73
|
+
UsersFetchedUfoExperience.getInstance(fieldId).start();
|
|
46
74
|
|
|
47
75
|
try {
|
|
48
|
-
|
|
76
|
+
if (lastAbortController.current) {
|
|
77
|
+
var _lastAbortController$;
|
|
78
|
+
|
|
79
|
+
(_lastAbortController$ = lastAbortController.current) === null || _lastAbortController$ === void 0 ? void 0 : _lastAbortController$.abort();
|
|
80
|
+
} // in case there are still IE users, check AbortController before init,
|
|
81
|
+
// IE has no timeout applied on fetch since AbortController isn't available
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
const currentController = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
85
|
+
lastAbortController.current = currentController;
|
|
86
|
+
const recommendedUsersResult = await fetchUserRecommendations({
|
|
49
87
|
baseUrl,
|
|
50
88
|
context: {
|
|
51
89
|
childObjectId,
|
|
@@ -56,7 +94,7 @@ const useUserRecommendations = props => {
|
|
|
56
94
|
productKey,
|
|
57
95
|
contextType: fieldId,
|
|
58
96
|
siteId: tenantId,
|
|
59
|
-
sessionId: sessionId
|
|
97
|
+
sessionId: sessionId.current
|
|
60
98
|
},
|
|
61
99
|
includeUsers,
|
|
62
100
|
includeGroups,
|
|
@@ -64,38 +102,60 @@ const useUserRecommendations = props => {
|
|
|
64
102
|
maxNumberOfResults,
|
|
65
103
|
searchQuery: cpusSearchQuery,
|
|
66
104
|
query
|
|
105
|
+
}, currentController === null || currentController === void 0 ? void 0 : currentController.signal);
|
|
106
|
+
currentQuery.current = query !== null && query !== void 0 ? query : '';
|
|
107
|
+
UsersFetchedUfoExperience.getInstance(fieldId).success({
|
|
108
|
+
metadata: { ...getAnalyticsAttributes(),
|
|
109
|
+
loadedUsersSize: recommendedUsersResult.length
|
|
110
|
+
}
|
|
67
111
|
});
|
|
68
|
-
setRecommendations(
|
|
112
|
+
setRecommendations(recommendedUsersResult);
|
|
69
113
|
} catch (error) {
|
|
70
|
-
|
|
114
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
115
|
+
// Request aborted client-side
|
|
116
|
+
UsersFetchedUfoExperience.getInstance(fieldId).abort(); // return early to prevent disabling isLoading
|
|
117
|
+
|
|
118
|
+
return;
|
|
119
|
+
} else {
|
|
120
|
+
UsersFetchedUfoExperience.getInstance(fieldId).failure({
|
|
121
|
+
metadata: getAnalyticsAttributes()
|
|
122
|
+
});
|
|
123
|
+
setError(error);
|
|
124
|
+
}
|
|
71
125
|
}
|
|
72
126
|
|
|
73
|
-
setIsLoading(false);
|
|
74
|
-
|
|
75
|
-
|
|
127
|
+
setIsLoading(false); // clear abort controller
|
|
128
|
+
|
|
129
|
+
lastAbortController.current = undefined;
|
|
130
|
+
}, [baseUrl, childObjectId, containerId, objectId, principalId, productAttributes, productKey, fieldId, tenantId, includeUsers, includeGroups, includeTeams, maxNumberOfResults, cpusSearchQuery, getAnalyticsAttributes]);
|
|
131
|
+
const memoizedDebouncedFetchRecommendations = useMemo(() => memoizeOne(debounce(fetchRecommendations, debounceTimeMs)), [fetchRecommendations, debounceTimeMs]);
|
|
76
132
|
useEffect(() => {
|
|
77
133
|
if (preload) {
|
|
78
|
-
|
|
134
|
+
memoizedDebouncedFetchRecommendations('');
|
|
79
135
|
}
|
|
80
|
-
}, [preload,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
136
|
+
}, [preload, memoizedDebouncedFetchRecommendations]);
|
|
137
|
+
const onInputChange = useCallback(query => {
|
|
138
|
+
memoizedDebouncedFetchRecommendations(query !== null && query !== void 0 ? query : '');
|
|
139
|
+
}, [memoizedDebouncedFetchRecommendations]);
|
|
140
|
+
const onUserSelect = useCallback(userId => {
|
|
141
|
+
fireUserSelectedEvent(createAnalyticsEvent, { ...getAnalyticsAttributes(),
|
|
142
|
+
selectedUser: userId,
|
|
143
|
+
loadedUsersSize: recommendations.length,
|
|
144
|
+
position: findUserPosition(recommendations, userId)
|
|
145
|
+
});
|
|
146
|
+
}, [createAnalyticsEvent, getAnalyticsAttributes, recommendations]);
|
|
87
147
|
const {
|
|
88
148
|
isUsed: isOnInputChangeUsed,
|
|
89
149
|
trackingFunction: usageTrackedOnInputChange
|
|
90
150
|
} = useFunctionUsageTracking(onInputChange);
|
|
91
151
|
const {
|
|
92
|
-
isUsed:
|
|
93
|
-
trackingFunction:
|
|
94
|
-
} = useFunctionUsageTracking(
|
|
152
|
+
isUsed: isOnUserSelectUsed,
|
|
153
|
+
trackingFunction: usageTrackedOnUserSelect
|
|
154
|
+
} = useFunctionUsageTracking(onUserSelect);
|
|
95
155
|
return {
|
|
96
|
-
recommendations: isOnInputChangeUsed &&
|
|
97
|
-
|
|
98
|
-
|
|
156
|
+
recommendations: isOnInputChangeUsed && isOnUserSelectUsed ? recommendations : instrumentFailureOption,
|
|
157
|
+
triggerSearchFactory: usageTrackedOnInputChange,
|
|
158
|
+
selectUserFactory: usageTrackedOnUserSelect,
|
|
99
159
|
isLoading,
|
|
100
160
|
error
|
|
101
161
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ConcurrentExperience, ExperiencePerformanceTypes, ExperienceTypes } from '@atlaskit/ufo';
|
|
2
|
+
const COMPONENT_NAME = 'smart-hooks.use-user-recommendations';
|
|
3
|
+
export const UsersFetchedUfoExperience = new ConcurrentExperience('users-fetched', {
|
|
4
|
+
platform: {
|
|
5
|
+
component: COMPONENT_NAME
|
|
6
|
+
},
|
|
7
|
+
type: ExperienceTypes.Load,
|
|
8
|
+
performanceType: ExperiencePerformanceTypes.InlineResult
|
|
9
|
+
});
|
package/dist/es2019/version.json
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
2
|
import { useMemo, useState } from 'react';
|
|
3
|
+
import memoizeOne from 'memoize-one';
|
|
3
4
|
|
|
4
5
|
function useFunctionUsageTracking(trackedFunction) {
|
|
5
6
|
var _useState = useState(false),
|
|
6
7
|
_useState2 = _slicedToArray(_useState, 2),
|
|
7
8
|
isUsed = _useState2[0],
|
|
8
|
-
setIsUsed = _useState2[1];
|
|
9
|
+
setIsUsed = _useState2[1]; // trackingFunction needs to be stable for hook consumers...
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
var trackingFunction = useMemo(function () {
|
|
11
|
-
return function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
return (// ...and the function that the factory returns needs to be stable for hook consumers as well
|
|
14
|
+
memoizeOne(function () {
|
|
15
|
+
if (!isUsed) {
|
|
16
|
+
setIsUsed(true);
|
|
17
|
+
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
return trackedFunction;
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}, // isUsed deliberately left out to avoid new tracking fn being generated
|
|
23
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
24
|
+
[trackedFunction]);
|
|
19
25
|
return {
|
|
20
26
|
isUsed: isUsed,
|
|
21
27
|
trackingFunction: trackingFunction
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
|
|
3
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
4
|
+
|
|
5
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
6
|
+
|
|
7
|
+
import { TRACK_EVENT_TYPE } from '@atlaskit/analytics-gas-types';
|
|
8
|
+
import { FabricChannel } from '@atlaskit/analytics-listeners';
|
|
9
|
+
import { createAndFireEvent } from '@atlaskit/analytics-next';
|
|
10
|
+
import { name as packageName, version as packageVersion } from '../../version.json';
|
|
11
|
+
var createAndFireEventInElementsChannel = createAndFireEvent(FabricChannel.elements);
|
|
12
|
+
|
|
13
|
+
var createEvent = function createEvent(eventType, action, actionSubject) {
|
|
14
|
+
var attributes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
15
|
+
return {
|
|
16
|
+
eventType: eventType,
|
|
17
|
+
action: action,
|
|
18
|
+
actionSubject: actionSubject,
|
|
19
|
+
source: '@atlaskit/smart-hooks/use-user-recommendations',
|
|
20
|
+
attributes: _objectSpread({
|
|
21
|
+
packageName: packageName,
|
|
22
|
+
packageVersion: packageVersion
|
|
23
|
+
}, attributes)
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export var findUserPosition = function findUserPosition(loadedUsers, userId) {
|
|
28
|
+
return loadedUsers.findIndex(function (val) {
|
|
29
|
+
return val.id === userId;
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
export var createDefaultAttributes = function createDefaultAttributes(props, renderId, sessionId, query) {
|
|
33
|
+
var fieldId = props.fieldId,
|
|
34
|
+
objectId = props.objectId,
|
|
35
|
+
containerId = props.containerId,
|
|
36
|
+
childObjectId = props.childObjectId,
|
|
37
|
+
preload = props.preload,
|
|
38
|
+
includeTeams = props.includeTeams,
|
|
39
|
+
productKey = props.productKey,
|
|
40
|
+
principalId = props.principalId,
|
|
41
|
+
tenantId = props.tenantId,
|
|
42
|
+
maxNumberOfResults = props.maxNumberOfResults;
|
|
43
|
+
return {
|
|
44
|
+
context: fieldId,
|
|
45
|
+
childObjectId: childObjectId,
|
|
46
|
+
containerId: containerId,
|
|
47
|
+
includeTeams: includeTeams,
|
|
48
|
+
maxNumberOfResults: maxNumberOfResults,
|
|
49
|
+
objectId: objectId,
|
|
50
|
+
preload: preload,
|
|
51
|
+
principalId: principalId,
|
|
52
|
+
productKey: productKey,
|
|
53
|
+
queryLength: (query || '').length,
|
|
54
|
+
renderId: renderId,
|
|
55
|
+
sessionId: sessionId,
|
|
56
|
+
tenantId: tenantId
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
export var fireUserSelectedEvent = function fireUserSelectedEvent(createAnalyticsEvent, payloadAttributes) {
|
|
60
|
+
return createAndFireEventInElementsChannel(createEvent(TRACK_EVENT_TYPE, 'selected', 'user', payloadAttributes))(createAnalyticsEvent);
|
|
61
|
+
};
|
|
@@ -7,10 +7,15 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
7
7
|
|
|
8
8
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
9
9
|
|
|
10
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
10
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
11
11
|
import debounce from 'lodash/debounce';
|
|
12
|
+
import memoizeOne from 'memoize-one';
|
|
13
|
+
import { v4 as uuid } from 'uuid';
|
|
14
|
+
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
12
15
|
import { EntityType, fetchUserRecommendations } from '@atlaskit/smart-common';
|
|
13
16
|
import useFunctionUsageTracking from '../use-function-usage-tracking';
|
|
17
|
+
import { createDefaultAttributes, findUserPosition, fireUserSelectedEvent } from './analytics';
|
|
18
|
+
import { UsersFetchedUfoExperience } from './ufoExperiences';
|
|
14
19
|
var MAX_NUMBER_RESULTS = 25;
|
|
15
20
|
var DEFAULT_DEBOUNCE_TIME_MS = 150;
|
|
16
21
|
var defaultProps = {
|
|
@@ -60,16 +65,56 @@ var useUserRecommendations = function useUserRecommendations(props) {
|
|
|
60
65
|
error = _useState6[0],
|
|
61
66
|
setError = _useState6[1];
|
|
62
67
|
|
|
68
|
+
var _useState7 = useState(function () {
|
|
69
|
+
return uuid();
|
|
70
|
+
}),
|
|
71
|
+
_useState8 = _slicedToArray(_useState7, 1),
|
|
72
|
+
renderId = _useState8[0];
|
|
73
|
+
|
|
74
|
+
var sessionId = useRef('');
|
|
75
|
+
var currentQuery = useRef('');
|
|
76
|
+
var lastAbortController = useRef();
|
|
77
|
+
|
|
78
|
+
var _useAnalyticsEvents = useAnalyticsEvents(),
|
|
79
|
+
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
|
|
80
|
+
|
|
81
|
+
var getAnalyticsAttributes = useCallback(function () {
|
|
82
|
+
return _objectSpread({}, createDefaultAttributes({
|
|
83
|
+
fieldId: fieldId,
|
|
84
|
+
objectId: objectId,
|
|
85
|
+
containerId: containerId,
|
|
86
|
+
childObjectId: childObjectId,
|
|
87
|
+
preload: preload,
|
|
88
|
+
includeTeams: includeTeams,
|
|
89
|
+
productKey: productKey,
|
|
90
|
+
principalId: principalId,
|
|
91
|
+
tenantId: tenantId,
|
|
92
|
+
maxNumberOfResults: maxNumberOfResults
|
|
93
|
+
}, renderId, sessionId.current, currentQuery.current));
|
|
94
|
+
}, [childObjectId, containerId, fieldId, includeTeams, maxNumberOfResults, objectId, preload, principalId, productKey, renderId, tenantId]);
|
|
63
95
|
var fetchRecommendations = useCallback( /*#__PURE__*/function () {
|
|
64
|
-
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(query
|
|
65
|
-
var
|
|
96
|
+
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(query) {
|
|
97
|
+
var _lastAbortController$, currentController, recommendedUsersResult;
|
|
98
|
+
|
|
66
99
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
67
100
|
while (1) {
|
|
68
101
|
switch (_context.prev = _context.next) {
|
|
69
102
|
case 0:
|
|
70
|
-
setIsLoading(true);
|
|
71
|
-
|
|
72
|
-
|
|
103
|
+
setIsLoading(true); // restart session
|
|
104
|
+
|
|
105
|
+
sessionId.current = uuid();
|
|
106
|
+
UsersFetchedUfoExperience.getInstance(fieldId).start();
|
|
107
|
+
_context.prev = 3;
|
|
108
|
+
|
|
109
|
+
if (lastAbortController.current) {
|
|
110
|
+
(_lastAbortController$ = lastAbortController.current) === null || _lastAbortController$ === void 0 ? void 0 : _lastAbortController$.abort();
|
|
111
|
+
} // in case there are still IE users, check AbortController before init,
|
|
112
|
+
// IE has no timeout applied on fetch since AbortController isn't available
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
currentController = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
116
|
+
lastAbortController.current = currentController;
|
|
117
|
+
_context.next = 9;
|
|
73
118
|
return fetchUserRecommendations({
|
|
74
119
|
baseUrl: baseUrl,
|
|
75
120
|
context: {
|
|
@@ -81,7 +126,7 @@ var useUserRecommendations = function useUserRecommendations(props) {
|
|
|
81
126
|
productKey: productKey,
|
|
82
127
|
contextType: fieldId,
|
|
83
128
|
siteId: tenantId,
|
|
84
|
-
sessionId: sessionId
|
|
129
|
+
sessionId: sessionId.current
|
|
85
130
|
},
|
|
86
131
|
includeUsers: includeUsers,
|
|
87
132
|
includeGroups: includeGroups,
|
|
@@ -89,61 +134,88 @@ var useUserRecommendations = function useUserRecommendations(props) {
|
|
|
89
134
|
maxNumberOfResults: maxNumberOfResults,
|
|
90
135
|
searchQuery: cpusSearchQuery,
|
|
91
136
|
query: query
|
|
137
|
+
}, currentController === null || currentController === void 0 ? void 0 : currentController.signal);
|
|
138
|
+
|
|
139
|
+
case 9:
|
|
140
|
+
recommendedUsersResult = _context.sent;
|
|
141
|
+
currentQuery.current = query !== null && query !== void 0 ? query : '';
|
|
142
|
+
UsersFetchedUfoExperience.getInstance(fieldId).success({
|
|
143
|
+
metadata: _objectSpread(_objectSpread({}, getAnalyticsAttributes()), {}, {
|
|
144
|
+
loadedUsersSize: recommendedUsersResult.length
|
|
145
|
+
})
|
|
92
146
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
recommendedUsers = _context.sent;
|
|
96
|
-
setRecommendations(recommendedUsers);
|
|
97
|
-
_context.next = 11;
|
|
147
|
+
setRecommendations(recommendedUsersResult);
|
|
148
|
+
_context.next = 24;
|
|
98
149
|
break;
|
|
99
150
|
|
|
100
|
-
case
|
|
101
|
-
_context.prev =
|
|
102
|
-
_context.t0 = _context["catch"](
|
|
151
|
+
case 15:
|
|
152
|
+
_context.prev = 15;
|
|
153
|
+
_context.t0 = _context["catch"](3);
|
|
154
|
+
|
|
155
|
+
if (!(_context.t0 instanceof Error && _context.t0.name === 'AbortError')) {
|
|
156
|
+
_context.next = 22;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Request aborted client-side
|
|
161
|
+
UsersFetchedUfoExperience.getInstance(fieldId).abort(); // return early to prevent disabling isLoading
|
|
162
|
+
|
|
163
|
+
return _context.abrupt("return");
|
|
164
|
+
|
|
165
|
+
case 22:
|
|
166
|
+
UsersFetchedUfoExperience.getInstance(fieldId).failure({
|
|
167
|
+
metadata: getAnalyticsAttributes()
|
|
168
|
+
});
|
|
103
169
|
setError(_context.t0);
|
|
104
170
|
|
|
105
|
-
case
|
|
106
|
-
setIsLoading(false);
|
|
171
|
+
case 24:
|
|
172
|
+
setIsLoading(false); // clear abort controller
|
|
173
|
+
|
|
174
|
+
lastAbortController.current = undefined;
|
|
107
175
|
|
|
108
|
-
case
|
|
176
|
+
case 26:
|
|
109
177
|
case "end":
|
|
110
178
|
return _context.stop();
|
|
111
179
|
}
|
|
112
180
|
}
|
|
113
|
-
}, _callee, null, [[
|
|
181
|
+
}, _callee, null, [[3, 15]]);
|
|
114
182
|
}));
|
|
115
183
|
|
|
116
|
-
return function (_x
|
|
184
|
+
return function (_x) {
|
|
117
185
|
return _ref.apply(this, arguments);
|
|
118
186
|
};
|
|
119
|
-
}(), [baseUrl, childObjectId, containerId,
|
|
120
|
-
var
|
|
121
|
-
return debounce(fetchRecommendations, debounceTimeMs);
|
|
187
|
+
}(), [baseUrl, childObjectId, containerId, objectId, principalId, productAttributes, productKey, fieldId, tenantId, includeUsers, includeGroups, includeTeams, maxNumberOfResults, cpusSearchQuery, getAnalyticsAttributes]);
|
|
188
|
+
var memoizedDebouncedFetchRecommendations = useMemo(function () {
|
|
189
|
+
return memoizeOne(debounce(fetchRecommendations, debounceTimeMs));
|
|
122
190
|
}, [fetchRecommendations, debounceTimeMs]);
|
|
123
191
|
useEffect(function () {
|
|
124
192
|
if (preload) {
|
|
125
|
-
|
|
193
|
+
memoizedDebouncedFetchRecommendations('');
|
|
126
194
|
}
|
|
127
|
-
}, [preload,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
195
|
+
}, [preload, memoizedDebouncedFetchRecommendations]);
|
|
196
|
+
var onInputChange = useCallback(function (query) {
|
|
197
|
+
memoizedDebouncedFetchRecommendations(query !== null && query !== void 0 ? query : '');
|
|
198
|
+
}, [memoizedDebouncedFetchRecommendations]);
|
|
199
|
+
var onUserSelect = useCallback(function (userId) {
|
|
200
|
+
fireUserSelectedEvent(createAnalyticsEvent, _objectSpread(_objectSpread({}, getAnalyticsAttributes()), {}, {
|
|
201
|
+
selectedUser: userId,
|
|
202
|
+
loadedUsersSize: recommendations.length,
|
|
203
|
+
position: findUserPosition(recommendations, userId)
|
|
204
|
+
}));
|
|
205
|
+
}, [createAnalyticsEvent, getAnalyticsAttributes, recommendations]);
|
|
134
206
|
|
|
135
207
|
var _useFunctionUsageTrac = useFunctionUsageTracking(onInputChange),
|
|
136
208
|
isOnInputChangeUsed = _useFunctionUsageTrac.isUsed,
|
|
137
209
|
usageTrackedOnInputChange = _useFunctionUsageTrac.trackingFunction;
|
|
138
210
|
|
|
139
|
-
var _useFunctionUsageTrac2 = useFunctionUsageTracking(
|
|
140
|
-
|
|
141
|
-
|
|
211
|
+
var _useFunctionUsageTrac2 = useFunctionUsageTracking(onUserSelect),
|
|
212
|
+
isOnUserSelectUsed = _useFunctionUsageTrac2.isUsed,
|
|
213
|
+
usageTrackedOnUserSelect = _useFunctionUsageTrac2.trackingFunction;
|
|
142
214
|
|
|
143
215
|
return {
|
|
144
|
-
recommendations: isOnInputChangeUsed &&
|
|
145
|
-
|
|
146
|
-
|
|
216
|
+
recommendations: isOnInputChangeUsed && isOnUserSelectUsed ? recommendations : instrumentFailureOption,
|
|
217
|
+
triggerSearchFactory: usageTrackedOnInputChange,
|
|
218
|
+
selectUserFactory: usageTrackedOnUserSelect,
|
|
147
219
|
isLoading: isLoading,
|
|
148
220
|
error: error
|
|
149
221
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ConcurrentExperience, ExperiencePerformanceTypes, ExperienceTypes } from '@atlaskit/ufo';
|
|
2
|
+
var COMPONENT_NAME = 'smart-hooks.use-user-recommendations';
|
|
3
|
+
export var UsersFetchedUfoExperience = new ConcurrentExperience('users-fetched', {
|
|
4
|
+
platform: {
|
|
5
|
+
component: COMPONENT_NAME
|
|
6
|
+
},
|
|
7
|
+
type: ExperienceTypes.Load,
|
|
8
|
+
performanceType: ExperiencePerformanceTypes.InlineResult
|
|
9
|
+
});
|
package/dist/esm/version.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare function useFunctionUsageTracking<InputFunctionType extends (...any: any) => any>(trackedFunction: InputFunctionType): {
|
|
2
2
|
isUsed: boolean;
|
|
3
|
-
trackingFunction: () => InputFunctionType
|
|
3
|
+
trackingFunction: import("memoize-one").MemoizedFn<() => InputFunctionType>;
|
|
4
4
|
};
|
|
5
5
|
export default useFunctionUsageTracking;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AnalyticsEventPayload, CreateUIAnalyticsEvent } from '@atlaskit/analytics-next';
|
|
2
|
+
import { UserSearchItem } from '@atlaskit/smart-common';
|
|
3
|
+
import { UseUserRecommendationsProps } from '../../types';
|
|
4
|
+
export declare const findUserPosition: (loadedUsers: UserSearchItem[], userId: string) => number;
|
|
5
|
+
export declare const createDefaultAttributes: (props: UseUserRecommendationsProps, renderId: string, sessionId: string, query: string) => {
|
|
6
|
+
context: string;
|
|
7
|
+
childObjectId: string | undefined;
|
|
8
|
+
containerId: string | undefined;
|
|
9
|
+
includeTeams: boolean | undefined;
|
|
10
|
+
maxNumberOfResults: number | undefined;
|
|
11
|
+
objectId: string | undefined;
|
|
12
|
+
preload: boolean | undefined;
|
|
13
|
+
principalId: string | undefined;
|
|
14
|
+
productKey: string;
|
|
15
|
+
queryLength: number;
|
|
16
|
+
renderId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
tenantId: string;
|
|
19
|
+
};
|
|
20
|
+
export declare const fireUserSelectedEvent: (createAnalyticsEvent: CreateUIAnalyticsEvent, payloadAttributes: AnalyticsEventPayload) => import("@atlaskit/analytics-next").UIAnalyticsEvent;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { EntityType, UserSearchItem } from '@atlaskit/smart-common';
|
|
2
|
-
import { ActionTypes, Value } from '@atlaskit/user-picker';
|
|
3
2
|
import type { UseUserRecommendationsProps } from '../../types';
|
|
4
3
|
export declare const instrumentFailureOption: {
|
|
5
4
|
id: string;
|
|
@@ -8,14 +7,14 @@ export declare const instrumentFailureOption: {
|
|
|
8
7
|
avatarUrl: string;
|
|
9
8
|
}[];
|
|
10
9
|
declare const useUserRecommendations: (props: UseUserRecommendationsProps) => {
|
|
11
|
-
recommendations: {
|
|
10
|
+
recommendations: UserSearchItem[] | {
|
|
12
11
|
id: string;
|
|
13
12
|
name: string;
|
|
14
13
|
entityType: EntityType;
|
|
15
14
|
avatarUrl: string;
|
|
16
|
-
}[]
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
}[];
|
|
16
|
+
triggerSearchFactory: import("memoize-one").MemoizedFn<() => (query?: string | undefined) => void>;
|
|
17
|
+
selectUserFactory: import("memoize-one").MemoizedFn<() => (userId: string) => void>;
|
|
19
18
|
isLoading: boolean;
|
|
20
19
|
error: any;
|
|
21
20
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/smart-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/"
|
|
6
6
|
},
|
|
@@ -18,19 +18,22 @@
|
|
|
18
18
|
"releaseModel": "continuous"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@atlaskit/
|
|
22
|
-
"@atlaskit/
|
|
21
|
+
"@atlaskit/analytics-gas-types": "^5.0.5",
|
|
22
|
+
"@atlaskit/analytics-listeners": "^8.1.3",
|
|
23
|
+
"@atlaskit/analytics-next": "^8.0.0",
|
|
24
|
+
"@atlaskit/smart-common": "^0.1.0",
|
|
25
|
+
"@atlaskit/ufo": "^0.1.0",
|
|
26
|
+
"@atlaskit/user-picker": "^8.5.0",
|
|
23
27
|
"@babel/runtime": "^7.0.0",
|
|
24
|
-
"lodash": "^4.17.21"
|
|
28
|
+
"lodash": "^4.17.21",
|
|
29
|
+
"memoize-one": "^6.0.0",
|
|
30
|
+
"uuid": "^3.1.0"
|
|
25
31
|
},
|
|
26
32
|
"peerDependencies": {
|
|
27
33
|
"react": "^16.8.0",
|
|
28
34
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
29
35
|
},
|
|
30
36
|
"devDependencies": {
|
|
31
|
-
"@atlaskit/analytics-gas-types": "^5.0.0",
|
|
32
|
-
"@atlaskit/analytics-listeners": "^8.0.0",
|
|
33
|
-
"@atlaskit/analytics-next": "^8.2.0",
|
|
34
37
|
"@atlaskit/analytics-viewer": "^0.4.5",
|
|
35
38
|
"@atlaskit/button": "^16.0.0",
|
|
36
39
|
"@atlaskit/docs": "*",
|
|
@@ -40,10 +43,12 @@
|
|
|
40
43
|
"@atlaskit/visual-regression": "*",
|
|
41
44
|
"@atlaskit/webdriver-runner": "*",
|
|
42
45
|
"@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",
|
|
46
|
+
"@atlassian/ufo": "^0.1.0",
|
|
43
47
|
"@atlassiansox/analytics-web-client": "^4.2.0",
|
|
44
48
|
"@storybook/addon-knobs": "^5.3.18",
|
|
45
49
|
"@testing-library/react": "^8.0.1",
|
|
46
50
|
"@testing-library/react-hooks": "^1.0.4",
|
|
51
|
+
"@welldone-software/why-did-you-render": "^6.2.0",
|
|
47
52
|
"enzyme": "^3.10.0",
|
|
48
53
|
"fetch-mock": "^8.0.0",
|
|
49
54
|
"react-dom": "^16.8.0",
|