@escapenavigator/services 1.10.139 → 1.10.140

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.
@@ -71,6 +71,7 @@ import { uploadsApiDeclaration } from './uploads-api';
71
71
  import { upsellingsApiDeclaration } from './upsellings-api';
72
72
  import { userMessagesApiDeclaration } from './user-messages-api';
73
73
  import { usersApiDeclaration } from './users-api';
74
+ import { widgetSessionsApiDeclaration } from './widget-sessions-api';
74
75
  import { widgetsApiDeclaration } from './widgets-api';
75
76
  export type InitApiDeclaration = {
76
77
  customFieldsApi: Api<typeof customFieldsApiDeclaration>;
@@ -129,6 +130,7 @@ export type InitApiDeclaration = {
129
130
  partners: Api<typeof partnersApiDeclaration>;
130
131
  questrooms: Api<typeof questroomsApiDeclaration>;
131
132
  widgets: Api<typeof widgetsApiDeclaration>;
133
+ widgetSessions: Api<typeof widgetSessionsApiDeclaration>;
132
134
  profiles: Api<typeof profilesApiDeclaration>;
133
135
  users: Api<typeof usersApiDeclaration>;
134
136
  clients: Api<typeof clientsApiDeclaration>;
@@ -75,6 +75,7 @@ var uploads_api_1 = require("./uploads-api");
75
75
  var upsellings_api_1 = require("./upsellings-api");
76
76
  var user_messages_api_1 = require("./user-messages-api");
77
77
  var users_api_1 = require("./users-api");
78
+ var widget_sessions_api_1 = require("./widget-sessions-api");
78
79
  var widgets_api_1 = require("./widgets-api");
79
80
  var initClientApi = function (url, clientOptions) { return ({
80
81
  customFieldsApi: (0, _1.initApi)(custom_fields_api_1.customFieldsApiDeclaration, url, clientOptions),
@@ -133,6 +134,7 @@ var initClientApi = function (url, clientOptions) { return ({
133
134
  partners: (0, _1.initApi)(partners_api_1.partnersApiDeclaration, url, clientOptions),
134
135
  questrooms: (0, _1.initApi)(questrooms_api_1.questroomsApiDeclaration, url, clientOptions),
135
136
  widgets: (0, _1.initApi)(widgets_api_1.widgetsApiDeclaration, url, clientOptions),
137
+ widgetSessions: (0, _1.initApi)(widget_sessions_api_1.widgetSessionsApiDeclaration, url, clientOptions),
136
138
  profiles: (0, _1.initApi)(profiles_api_1.profilesApiDeclaration, url, clientOptions),
137
139
  users: (0, _1.initApi)(users_api_1.usersApiDeclaration, url, clientOptions),
138
140
  clients: (0, _1.initApi)(clients_api_1.clientsApiDeclaration, url, clientOptions),
@@ -0,0 +1,3 @@
1
+ import { WidgetSessionEventsRO } from '@escapenavigator/types/dist/widget-sessions/widget-session-events.ro';
2
+ import { ApiMethodDeclaration } from '..';
3
+ export declare const getFull: ApiMethodDeclaration<number, WidgetSessionEventsRO>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFull = void 0;
4
+ var getFull = function (id) { return ({
5
+ url: "/admin/widget-sessions/".concat(id, "/full"),
6
+ method: 'GET',
7
+ }); };
8
+ exports.getFull = getFull;
@@ -0,0 +1,12 @@
1
+ import { getFull } from './get-full';
2
+ import { list } from './list';
3
+ import { remove } from './remove';
4
+ import { updateComment } from './update-comment';
5
+ type ApiDeclaration = {
6
+ list: typeof list;
7
+ getFull: typeof getFull;
8
+ updateComment: typeof updateComment;
9
+ remove: typeof remove;
10
+ };
11
+ export declare const widgetSessionsApiDeclaration: ApiDeclaration;
12
+ export {};
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.widgetSessionsApiDeclaration = void 0;
4
+ var get_full_1 = require("./get-full");
5
+ var list_1 = require("./list");
6
+ var remove_1 = require("./remove");
7
+ var update_comment_1 = require("./update-comment");
8
+ exports.widgetSessionsApiDeclaration = {
9
+ list: list_1.list,
10
+ getFull: get_full_1.getFull,
11
+ updateComment: update_comment_1.updateComment,
12
+ remove: remove_1.remove,
13
+ };
@@ -0,0 +1,12 @@
1
+ import { QueryDto } from '@escapenavigator/types/dist/shared/query.dto';
2
+ import { QueryRO } from '@escapenavigator/types/dist/shared/query.ro';
3
+ import { WidgetSessionRO } from '@escapenavigator/types/dist/widget-sessions/widget-session.ro';
4
+ import { ApiMethodDeclaration } from '..';
5
+ type ResponseData = QueryRO<WidgetSessionRO & {
6
+ profile?: {
7
+ id: number;
8
+ title: string;
9
+ };
10
+ }>;
11
+ export declare const list: ApiMethodDeclaration<QueryDto, ResponseData>;
12
+ export {};
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.list = void 0;
4
+ var list = function (params) { return ({
5
+ url: '/admin/widget-sessions',
6
+ method: 'GET',
7
+ params: params,
8
+ }); };
9
+ exports.list = list;
@@ -0,0 +1,4 @@
1
+ import { ApiMethodDeclaration } from '..';
2
+ export declare const remove: ApiMethodDeclaration<number, {
3
+ success: boolean;
4
+ }>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.remove = void 0;
4
+ var remove = function (id) { return ({
5
+ url: "/admin/widget-sessions/".concat(id),
6
+ method: 'DELETE',
7
+ }); };
8
+ exports.remove = remove;
@@ -0,0 +1,9 @@
1
+ import { ApiMethodDeclaration } from '..';
2
+ type Params = {
3
+ id: number;
4
+ comment?: string;
5
+ };
6
+ export declare const updateComment: ApiMethodDeclaration<Params, {
7
+ success: boolean;
8
+ }>;
9
+ export {};
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateComment = void 0;
4
+ var updateComment = function (_a) {
5
+ var id = _a.id, comment = _a.comment;
6
+ return ({
7
+ url: "/admin/widget-sessions/".concat(id),
8
+ method: 'PATCH',
9
+ data: { comment: comment },
10
+ });
11
+ };
12
+ exports.updateComment = updateComment;
@@ -48,8 +48,33 @@ var api_2 = require("./reducers/api");
48
48
  var useApiMethod = function (_a) {
49
49
  var _b;
50
50
  var api = _a.api, successCallback = _a.successCallback, errorCallback = _a.errorCallback;
51
- var mounted = (0, react_1.useRef)(false);
51
+ // mounted стартует как `true`, а не `false`. Прежнее `useRef(false)` имело
52
+ // тонкий баг: между первым render'ом и первым flush-ем useEffect'а
53
+ // mounted=false, и если в этот микро-промежуток успевал свалиться запрос
54
+ // (быстрая сеть, кэш, microtask до commit'а) — catch-блок видел mounted=false
55
+ // и молча возвращал null без throw → консьюмер думал «всё ок», state
56
+ // оставался в loading навсегда. В React 18 StrictMode dev двойной mount
57
+ // только усугублял проблему (cleanup первой пары ставил false, и до
58
+ // useEffect второй пары mounted был false). Правда жизни: компонент уже
59
+ // смонтирован к моменту, как мы вообще можем вызвать fetch — значение
60
+ // должно быть true с самого начала, а cleanup сам поставит false.
61
+ var mounted = (0, react_1.useRef)(true);
52
62
  var source = (0, react_1.useRef)(null);
63
+ // Inflight-counter для устойчивого loading-state в кейсе нескольких
64
+ // sequential/parallel вызовов на одном хук-инстансе. Без него каждый
65
+ // успех/фейл одной итерации делал dispatch(fetchSuccess|Failure) с
66
+ // loading=false → между итерациями кнопка мигала enabled-состоянием
67
+ // (риск двойного клика), скелетоны таблиц моргали. Считаем
68
+ // активные fetch'и и dispatch'им «loading=false» только когда счётчик
69
+ // вернулся в 0. На реальную семантику data/error не влияет — последний
70
+ // успех всё равно перетирает state.data, последняя ошибка — state.error.
71
+ //
72
+ // ВАЖНО про cancellation: useApiMethod исторически отменяет предыдущий
73
+ // запрос при новом fetch() (single source.current). Из-за этого Promise.all
74
+ // на одном инстансе ломается — все, кроме последнего вызова, возвращают
75
+ // null. Inflight-counter эту семантику не меняет, только сглаживает
76
+ // флик loading-state между быстрыми последовательными fetch'ами.
77
+ var inflightRef = (0, react_1.useRef)(0);
53
78
  var _c = (0, react_1.useReducer)((api_2.apiReducer), api_2.initialState), state = _c[0], dispatch = _c[1];
54
79
  // КРИТИЧНО: коллбэки держим в ref, а не в deps useCallback. Иначе
55
80
  // call-site вроде `useApiMethod({ api, successCallback: () => ... })`
@@ -63,25 +88,36 @@ var useApiMethod = function (_a) {
63
88
  successCallbackRef.current = successCallback;
64
89
  errorCallbackRef.current = errorCallback;
65
90
  var fetch = (0, react_1.useCallback)(function (params) { return __awaiter(void 0, void 0, void 0, function () {
66
- var response, error_1, text, _a, err;
91
+ var wasIdle, response, error_1, text, _a, err;
67
92
  var _b, _c, _d, _e;
68
93
  return __generator(this, function (_f) {
69
94
  switch (_f.label) {
70
95
  case 0:
71
96
  (_b = source.current) === null || _b === void 0 ? void 0 : _b.cancel();
72
97
  source.current = axios_1.default.CancelToken.source();
98
+ wasIdle = inflightRef.current === 0;
99
+ inflightRef.current += 1;
73
100
  _f.label = 1;
74
101
  case 1:
75
- _f.trys.push([1, 3, , 8]);
76
- dispatch((0, api_1.fetchStart)());
102
+ _f.trys.push([1, 3, 8, 9]);
103
+ if (wasIdle)
104
+ dispatch((0, api_1.fetchStart)());
77
105
  return [4 /*yield*/, api(params, {
78
106
  cancelToken: source.current.token,
79
107
  })];
80
108
  case 2:
81
109
  response = _f.sent();
82
- if (successCallbackRef.current)
83
- successCallbackRef.current(response, params);
84
- dispatch((0, api_1.fetchSuccess)(response));
110
+ // Mounted-guard на success-path. Раньше успешный response
111
+ // после unmount всё равно дёргал successCallback (мог сделать
112
+ // navigate / showToast на размонтированном UI) и dispatch на
113
+ // мёртвый reducer (React 18 warning + лишний state-change в
114
+ // никуда). Теперь после unmount — тихо отдаём response наверх,
115
+ // и пусть консьюмер сам решает, что с ним делать.
116
+ if (mounted.current) {
117
+ if (successCallbackRef.current)
118
+ successCallbackRef.current(response, params);
119
+ dispatch((0, api_1.fetchSuccess)(response));
120
+ }
85
121
  return [2 /*return*/, response];
86
122
  case 3:
87
123
  error_1 = _f.sent();
@@ -112,7 +148,10 @@ var useApiMethod = function (_a) {
112
148
  dispatch((0, api_1.fetchFailure)(err));
113
149
  // eslint-disable-next-line @typescript-eslint/no-throw-literal
114
150
  throw err;
115
- case 8: return [2 /*return*/];
151
+ case 8:
152
+ inflightRef.current = Math.max(0, inflightRef.current - 1);
153
+ return [7 /*endfinally*/];
154
+ case 9: return [2 /*return*/];
116
155
  }
117
156
  });
118
157
  }); }, [api]);
@@ -24,7 +24,17 @@ var apiReducer = function (state, action) {
24
24
  if (state === void 0) { state = exports.initialState; }
25
25
  switch (action.type) {
26
26
  case api_1.ActionTypes.FETCH_START:
27
- return __assign(__assign({}, exports.initialState), { loading: true });
27
+ // ВАЖНО: НЕ сбрасываем `data`/`response`/`statusOk` это
28
+ // stale-while-revalidate. Прежнее `return { ...initialState, loading: true }`
29
+ // обнуляло данные на каждый ре-фетч, и любой консьюмер
30
+ // (таблицы списков, шапка с метой ордера, расписание) на
31
+ // время запроса показывал пустое состояние / скелетон, даже
32
+ // если новые данные через 200мс прилетят те же самые.
33
+ // Теперь данные сохраняются до прихода нового ответа —
34
+ // skeleton flicker на ре-фетчах исчезает. Только `error`
35
+ // ресетим, чтобы старая ошибка не «сидела» поверх нового
36
+ // запроса (если новый успешен, она и так заменится).
37
+ return __assign(__assign({}, state), { loading: true, error: null });
28
38
  case api_1.ActionTypes.FETCH_SUCCESS:
29
39
  return __assign(__assign({}, state), { loading: false, data: action.payload.response.data, response: action.payload.response, statusOk: true });
30
40
  case api_1.ActionTypes.FETCH_FAILURE:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@escapenavigator/services",
3
- "version": "1.10.139",
3
+ "version": "1.10.140",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -12,10 +12,10 @@
12
12
  "scripts": {
13
13
  "build": "rm -rf dist && tsc --project tsconfig.json"
14
14
  },
15
- "gitHead": "1b34f498be3d6fb27dccdd2a3663cef5169bbc91",
15
+ "gitHead": "cb2f3ab38ef5b8b2a38ab5b2c485e484a0cf9414",
16
16
  "dependencies": {
17
- "@escapenavigator/types": "^1.10.130",
18
- "@escapenavigator/utils": "^1.10.134",
17
+ "@escapenavigator/types": "^1.10.131",
18
+ "@escapenavigator/utils": "^1.10.135",
19
19
  "axios": "^0.21.4",
20
20
  "class-transformer": "^0.5.1",
21
21
  "class-validator": "^0.13.2",