@robotical/webapp-types 3.25.24 → 3.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marty-web-app",
3
- "version": "3.25.24",
3
+ "version": "3.26.0",
4
4
  "private": true,
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -1,7 +1,9 @@
1
1
  import { FeedbackRating } from "../../../utils/feedback/submitFeedback";
2
+ import type { FeedbackReason } from "../../../utils/feedback/feedbackReasons";
2
3
  type Props = {
3
4
  otherInfoObject?: Record<string, unknown>;
4
5
  rating?: FeedbackRating;
6
+ reason?: FeedbackReason;
5
7
  };
6
- export default function DetailedFeedbackModal({ otherInfoObject, rating, }: Props): import("react/jsx-runtime").JSX.Element;
8
+ export default function DetailedFeedbackModal({ otherInfoObject, rating, reason, }: Props): import("react/jsx-runtime").JSX.Element;
7
9
  export {};
@@ -56,7 +56,7 @@ import styles from "./styles.module.css";
56
56
  import SimpleButton from "../../disposables/buttons/SimpleButton";
57
57
  export default function DetailedFeedbackModal(_a) {
58
58
  var _this = this;
59
- var otherInfoObject = _a.otherInfoObject, rating = _a.rating;
59
+ var otherInfoObject = _a.otherInfoObject, rating = _a.rating, reason = _a.reason;
60
60
  var t = useTranslation().t;
61
61
  var _b = useState(""), report = _b[0], setReport = _b[1];
62
62
  var _c = useState(""), email = _c[0], setEmail = _c[1];
@@ -75,11 +75,7 @@ export default function DetailedFeedbackModal(_a) {
75
75
  _c.label = 1;
76
76
  case 1:
77
77
  _c.trys.push([1, 3, , 4]);
78
- return [4 /*yield*/, submitFeedback({
79
- rating: rating,
80
- context: otherInfoObject,
81
- source: "emoji-modal",
82
- })];
78
+ return [4 /*yield*/, submitFeedback(__assign(__assign({ rating: rating }, (reason ? { reason: reason } : {})), { context: otherInfoObject, source: "emoji-modal" }))];
83
79
  case 2:
84
80
  _c.sent();
85
81
  page = getFeedbackPromptPageFromContext(otherInfoObject);
@@ -140,13 +136,7 @@ export default function DetailedFeedbackModal(_a) {
140
136
  _c.label = 1;
141
137
  case 1:
142
138
  _c.trys.push([1, 3, 4, 5]);
143
- return [4 /*yield*/, submitFeedback({
144
- rating: rating,
145
- message: report,
146
- email: email,
147
- context: otherInfoObject,
148
- source: "detailed-feedback",
149
- })];
139
+ return [4 /*yield*/, submitFeedback(__assign(__assign({ rating: rating }, (reason ? { reason: reason } : {})), { message: report, email: email, context: otherInfoObject, source: "detailed-feedback" }))];
150
140
  case 2:
151
141
  _c.sent();
152
142
  page = getFeedbackPromptPageFromContext(otherInfoObject);
@@ -0,0 +1,21 @@
1
+ export declare const FEEDBACK_REASONS: readonly [{
2
+ readonly value: "connection";
3
+ readonly label: "Connection";
4
+ }, {
5
+ readonly value: "loading";
6
+ readonly label: "Loading";
7
+ }, {
8
+ readonly value: "controls";
9
+ readonly label: "Controls";
10
+ }, {
11
+ readonly value: "difficulty";
12
+ readonly label: "Difficulty";
13
+ }, {
14
+ readonly value: "bug";
15
+ readonly label: "Bug";
16
+ }, {
17
+ readonly value: "other";
18
+ readonly label: "Other";
19
+ }];
20
+ export type FeedbackReason = typeof FEEDBACK_REASONS[number]["value"];
21
+ export declare function normalizeFeedbackReason(rating: number | null | undefined, reason: unknown): FeedbackReason | null;
@@ -0,0 +1,15 @@
1
+ export var FEEDBACK_REASONS = [
2
+ { value: "connection", label: "Connection" },
3
+ { value: "loading", label: "Loading" },
4
+ { value: "controls", label: "Controls" },
5
+ { value: "difficulty", label: "Difficulty" },
6
+ { value: "bug", label: "Bug" },
7
+ { value: "other", label: "Other" },
8
+ ];
9
+ export function normalizeFeedbackReason(rating, reason) {
10
+ var _a, _b;
11
+ if (rating !== 1 && rating !== 2) {
12
+ return null;
13
+ }
14
+ return (_b = (_a = FEEDBACK_REASONS.find(function (option) { return option.value === reason; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null;
15
+ }
@@ -1,4 +1,5 @@
1
1
  import type { UserDeviceInfo } from "../../types/userDeviceInfo";
2
+ import { type FeedbackReason } from "./feedbackReasons";
2
3
  export type FeedbackRating = 1 | 2 | 3 | 4 | 5;
3
4
  export type FeedbackSource = "emoji-modal" | "detailed-feedback" | "help-bug-report";
4
5
  export type FeedbackAppRuntime = "phone-app" | "web-app";
@@ -27,6 +28,7 @@ export type FeedbackEnvironment = {
27
28
  };
28
29
  export type SubmitFeedbackInput = {
29
30
  rating?: FeedbackRating;
31
+ reason?: FeedbackReason;
30
32
  message?: string;
31
33
  email?: string;
32
34
  context?: Record<string, unknown> | null;
@@ -36,6 +38,7 @@ export type SubmitFeedbackInput = {
36
38
  };
37
39
  export type FeedbackRecord = {
38
40
  rating: FeedbackRating | null;
41
+ reason: FeedbackReason | null;
39
42
  message: string | null;
40
43
  email: string | null;
41
44
  source: FeedbackSource;
@@ -49,6 +49,7 @@ import Logger from "../../services/logger/Logger";
49
49
  import { FEEDBACK_REQUIRES_ROBOT_CONNECTION_MESSAGE, getActiveFeedbackRobotConnection, isRobotConnectionRequiredForFeedbackSource, } from "./feedbackConnectionEligibility";
50
50
  import randomHashGenerator from "../helpers/randomHashGenerator";
51
51
  import isPhoneApp from "../phone-app-communication/is-phone-app";
52
+ import { FEEDBACK_REASONS, normalizeFeedbackReason, } from "./feedbackReasons";
52
53
  var SHOW_LOGS = true;
53
54
  var TAG = "submitFeedback";
54
55
  var WEB_APP_FEEDBACK_DB_URL = "https://web-app-bug-reports-default-rtdb.europe-west1.firebasedatabase.app/";
@@ -167,6 +168,7 @@ function getEnvironmentTextLines(environment) {
167
168
  return lines.concat(deviceLines);
168
169
  }
169
170
  export function buildFeedbackText(record) {
171
+ var _a;
170
172
  var lines = ["Source: ".concat(record.source)];
171
173
  lines.push.apply(lines, getEnvironmentTextLines(record.environment));
172
174
  var page = record.context && typeof record.context.page === "string"
@@ -202,6 +204,13 @@ export function buildFeedbackText(record) {
202
204
  if (record.rating) {
203
205
  lines.push("Rating: ".concat(record.rating, "/5"));
204
206
  }
207
+ if (record.reason) {
208
+ var label = (_a = FEEDBACK_REASONS.find(function (_a) {
209
+ var value = _a.value;
210
+ return value === record.reason;
211
+ })) === null || _a === void 0 ? void 0 : _a.label;
212
+ lines.push("Reason: ".concat(label));
213
+ }
205
214
  if (record.screenshots.length > 0) {
206
215
  lines.push("Screenshots: ".concat(record.screenshots.length, " (view in Marty Web App Insights)"));
207
216
  }
@@ -219,6 +228,7 @@ export function createFeedbackRecord(input) {
219
228
  var environment = (_a = input.environment) !== null && _a !== void 0 ? _a : collectFeedbackEnvironment();
220
229
  var record = {
221
230
  rating: (_b = input.rating) !== null && _b !== void 0 ? _b : null,
231
+ reason: normalizeFeedbackReason(input.rating, input.reason),
222
232
  message: normalizedMessage,
223
233
  email: normalizedEmail,
224
234
  source: input.source,
@@ -1,5 +1,7 @@
1
1
  import { type AppUpdateSnapshot } from "../../types/communication-between-apps/wrapper-communication";
2
2
  type UpdateListener = (snapshot: AppUpdateSnapshot) => void;
3
+ export declare const UPDATE_REQUEST_TIMEOUT_MS = 20000;
4
+ export declare const UPDATE_CHECK_STALE_MS = 30000;
3
5
  export declare const isAppUpdateSnapshot: (value: unknown) => value is AppUpdateSnapshot;
4
6
  export declare const publishPhoneAppUpdateState: (value: unknown) => void;
5
7
  export declare const subscribeToPhoneAppUpdates: (listener: UpdateListener) => () => void;
@@ -50,6 +50,10 @@ import isPhoneApp from "./is-phone-app";
50
50
  import { useEffect, useState } from "react";
51
51
  var listeners = new Set();
52
52
  var latestSnapshot = null;
53
+ export var UPDATE_REQUEST_TIMEOUT_MS = 20000;
54
+ export var UPDATE_CHECK_STALE_MS = 30000;
55
+ var readyRequest = null;
56
+ var checkRequest = null;
53
57
  var isNativeUpdate = function (value) {
54
58
  if (!value || typeof value !== "object") {
55
59
  return false;
@@ -97,13 +101,44 @@ export var subscribeToPhoneAppUpdates = function (listener) {
97
101
  listeners.delete(listener);
98
102
  };
99
103
  };
104
+ var useCheckingDeadline = function (kind, status) {
105
+ useEffect(function () {
106
+ if (status !== 'checking') {
107
+ return;
108
+ }
109
+ var deadline = Date.now() + UPDATE_CHECK_STALE_MS;
110
+ var expire = function () {
111
+ var _a;
112
+ var _b;
113
+ if (Date.now() < deadline || ((_b = latestSnapshot === null || latestSnapshot === void 0 ? void 0 : latestSnapshot.checks) === null || _b === void 0 ? void 0 : _b[kind]) !== 'checking') {
114
+ return;
115
+ }
116
+ publishPhoneAppUpdateState(__assign(__assign({}, latestSnapshot), { checks: __assign(__assign({}, latestSnapshot.checks), (_a = {}, _a[kind] = 'unknown', _a)) }));
117
+ };
118
+ var timer = setTimeout(expire, UPDATE_CHECK_STALE_MS);
119
+ // WebView timers can pause in the background. Check elapsed time on return.
120
+ var onVisible = function () { if (!document.hidden) {
121
+ expire();
122
+ } };
123
+ document.addEventListener('visibilitychange', onVisible);
124
+ window.addEventListener('pageshow', expire);
125
+ return function () {
126
+ clearTimeout(timer);
127
+ document.removeEventListener('visibilitychange', onVisible);
128
+ window.removeEventListener('pageshow', expire);
129
+ };
130
+ }, [kind, status]);
131
+ };
100
132
  export var usePhoneAppUpdates = function () {
101
- var _a = useState(latestSnapshot), snapshot = _a[0], setSnapshot = _a[1];
133
+ var _a, _b;
134
+ var _c = useState(latestSnapshot), snapshot = _c[0], setSnapshot = _c[1];
102
135
  useEffect(function () { return subscribeToPhoneAppUpdates(setSnapshot); }, []);
136
+ useCheckingDeadline('native', (_a = snapshot === null || snapshot === void 0 ? void 0 : snapshot.checks) === null || _a === void 0 ? void 0 : _a.native);
137
+ useCheckingDeadline('web', (_b = snapshot === null || snapshot === void 0 ? void 0 : snapshot.checks) === null || _b === void 0 ? void 0 : _b.web);
103
138
  return snapshot;
104
139
  };
105
140
  var requestUpdateState = function (message) { return __awaiter(void 0, void 0, void 0, function () {
106
- var snapshot, _a;
141
+ var timer, snapshot, _a;
107
142
  return __generator(this, function (_b) {
108
143
  switch (_b.label) {
109
144
  case 0:
@@ -112,8 +147,11 @@ var requestUpdateState = function (message) { return __awaiter(void 0, void 0, v
112
147
  }
113
148
  _b.label = 1;
114
149
  case 1:
115
- _b.trys.push([1, 3, , 4]);
116
- return [4 /*yield*/, window.wrapperCommunicator.sendMessageAndWait(message, {})];
150
+ _b.trys.push([1, 3, 4, 5]);
151
+ return [4 /*yield*/, Promise.race([
152
+ window.wrapperCommunicator.sendMessageAndWait(message, {}),
153
+ new Promise(function (resolve) { timer = setTimeout(function () { return resolve(null); }, UPDATE_REQUEST_TIMEOUT_MS); }),
154
+ ])];
117
155
  case 2:
118
156
  snapshot = _b.sent();
119
157
  if (!isAppUpdateSnapshot(snapshot)) {
@@ -124,30 +162,47 @@ var requestUpdateState = function (message) { return __awaiter(void 0, void 0, v
124
162
  case 3:
125
163
  _a = _b.sent();
126
164
  return [2 /*return*/, null];
127
- case 4: return [2 /*return*/];
165
+ case 4:
166
+ clearTimeout(timer);
167
+ return [7 /*endfinally*/];
168
+ case 5: return [2 /*return*/];
128
169
  }
129
170
  });
130
171
  }); };
131
172
  export var announcePhoneAppUpdateUiReady = function () {
132
- return requestUpdateState(AppSentMessage.UPDATE_UI_READY);
173
+ if (!readyRequest) {
174
+ readyRequest = requestUpdateState(AppSentMessage.UPDATE_UI_READY).finally(function () { readyRequest = null; });
175
+ }
176
+ return readyRequest;
133
177
  };
134
178
  export var getPhoneAppUpdateState = function () {
135
179
  return requestUpdateState(AppSentMessage.UPDATE_GET_STATE);
136
180
  };
137
- export var checkPhoneAppUpdates = function () { return __awaiter(void 0, void 0, void 0, function () {
138
- var supportsChecks, result, _a, native, web;
139
- return __generator(this, function (_b) {
140
- switch (_b.label) {
181
+ var performUpdateCheck = function () { return __awaiter(void 0, void 0, void 0, function () {
182
+ var _a, supportsChecks, result, _b, native, web;
183
+ return __generator(this, function (_c) {
184
+ switch (_c.label) {
141
185
  case 0:
186
+ _a = !latestSnapshot;
187
+ if (!_a) return [3 /*break*/, 2];
188
+ return [4 /*yield*/, announcePhoneAppUpdateUiReady()];
189
+ case 1:
190
+ _a = !(_c.sent());
191
+ _c.label = 2;
192
+ case 2:
193
+ // A lost cold-start reply must not permanently remove access to update checks.
194
+ if (_a) {
195
+ return [2 /*return*/, null];
196
+ }
142
197
  supportsChecks = Boolean(latestSnapshot === null || latestSnapshot === void 0 ? void 0 : latestSnapshot.checks);
143
198
  if (latestSnapshot && supportsChecks) {
144
199
  publishPhoneAppUpdateState(__assign(__assign({}, latestSnapshot), { checks: { native: "checking", web: "checking" } }));
145
200
  }
146
201
  return [4 /*yield*/, requestUpdateState(supportsChecks ? AppSentMessage.UPDATE_CHECK_NOW : AppSentMessage.UPDATE_GET_STATE)];
147
- case 1:
148
- result = _b.sent();
202
+ case 3:
203
+ result = _c.sent();
149
204
  if (!result && (latestSnapshot === null || latestSnapshot === void 0 ? void 0 : latestSnapshot.checks)) {
150
- _a = latestSnapshot.checks, native = _a.native, web = _a.web;
205
+ _b = latestSnapshot.checks, native = _b.native, web = _b.web;
151
206
  publishPhoneAppUpdateState(__assign(__assign({}, latestSnapshot), { checks: {
152
207
  native: native === "checking" ? "unknown" : native,
153
208
  web: web === "checking" ? "unknown" : web,
@@ -157,6 +212,12 @@ export var checkPhoneAppUpdates = function () { return __awaiter(void 0, void 0,
157
212
  }
158
213
  });
159
214
  }); };
215
+ export var checkPhoneAppUpdates = function () {
216
+ if (!checkRequest) {
217
+ checkRequest = performUpdateCheck().finally(function () { checkRequest = null; });
218
+ }
219
+ return checkRequest;
220
+ };
160
221
  export var applyPhoneWebUpdate = function () { return __awaiter(void 0, void 0, void 0, function () {
161
222
  var response, _a;
162
223
  return __generator(this, function (_b) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robotical/webapp-types",
3
- "version": "3.25.24",
3
+ "version": "3.26.0",
4
4
  "description": "Type definitions for the Application Manager",
5
5
  "main": "dist/application-manager.d.ts",
6
6
  "types": "dist/application-manager.d.ts",