@fewchurs/react 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
5
5
 
6
6
  // src/client/version.ts
7
- var SDK_VERSION = "1.2.1";
7
+ var SDK_VERSION = "1.4.0";
8
8
 
9
9
  // src/client/constants.ts
10
10
  var SDK_HEADER = `react/${SDK_VERSION}`;
@@ -226,7 +226,9 @@ function createFewchursClient(options = {}) {
226
226
  await delay(retryDelayMs);
227
227
  continue;
228
228
  }
229
- return { ok: false, error: networkError(messageOf(error)) };
229
+ const failure2 = networkError(messageOf(error));
230
+ logError(failure2);
231
+ return { ok: false, error: failure2 };
230
232
  }
231
233
  if (response.ok) {
232
234
  const payload = await readJson(response);
@@ -237,7 +239,7 @@ function createFewchursClient(options = {}) {
237
239
  continue;
238
240
  }
239
241
  const failure = errorFromResponse(response.status, await readJson(response));
240
- warnAboutKey(failure);
242
+ logError(failure);
241
243
  return { ok: false, error: failure };
242
244
  }
243
245
  return { ok: false, error: networkError() };
@@ -328,14 +330,15 @@ function delay(ms) {
328
330
  function messageOf(error) {
329
331
  return error instanceof Error ? error.message : "Network request failed.";
330
332
  }
331
- var warnedAboutKey = false;
332
- function warnAboutKey(error) {
333
- if (error.code !== "invalid_key" || warnedAboutKey) return;
333
+ var INVALID_KEY_HELP = "The API key was rejected. Check FEWCHURS_API_KEY (server) or the apiKey passed to FewchursProvider, and that the key is active in your Fewchurs dashboard.";
334
+ var loggedErrors = /* @__PURE__ */ new Set();
335
+ function logError(error) {
334
336
  if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
335
- warnedAboutKey = true;
336
- console.warn(
337
- error.status === 403 ? `[fewchurs] ${error.message}` : "[fewchurs] The API key was rejected. Check FEWCHURS_API_KEY (server) or the apiKey passed to FewchursProvider, and that the key is active in your Fewchurs dashboard."
338
- );
337
+ const isInvalidKey = error.code === "invalid_key" && error.status === 401;
338
+ const detail = isInvalidKey ? INVALID_KEY_HELP : error.message;
339
+ if (loggedErrors.has(detail)) return;
340
+ loggedErrors.add(detail);
341
+ console.warn(`[fewchurs] : ${detail}`);
339
342
  }
340
343
 
341
344
  // src/identity/deviceId.ts
@@ -765,6 +768,12 @@ var DEFAULT_LABELS = {
765
768
  loading: "Loading\u2026",
766
769
  loadMore: "Load more",
767
770
  retry: "Try again",
771
+ errorInvalidKeyTitle: "Invalid API key",
772
+ errorInvalidKeyDetail: "We can't connect to this board right now. The owner of this site needs to check its setup.",
773
+ errorWrongAppTitle: "Wrong platform",
774
+ errorWrongAppDetail: "This board isn't set up for this site or app yet. Its owner needs to add it in Fewchurs.",
775
+ errorTitle: "Something went wrong",
776
+ errorDetail: "We couldn't load the requests. Please try again.",
768
777
  comments: "Comments",
769
778
  commentPlaceholder: "Add a comment",
770
779
  postComment: "Post",
@@ -1240,6 +1249,12 @@ function Bar({ width, height }) {
1240
1249
 
1241
1250
  // src/components/FeatureList.tsx
1242
1251
  import { Fragment, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1252
+ function errorCopy(error, labels) {
1253
+ if (error.code !== "invalid_key") {
1254
+ return { title: labels.errorTitle, detail: labels.errorDetail };
1255
+ }
1256
+ return error.status === 403 ? { title: labels.errorWrongAppTitle, detail: labels.errorWrongAppDetail } : { title: labels.errorInvalidKeyTitle, detail: labels.errorInvalidKeyDetail };
1257
+ }
1243
1258
  function FeatureList() {
1244
1259
  const { initialFeatureId } = useBoard();
1245
1260
  return /* @__PURE__ */ jsxs6(Fragment, { children: [
@@ -1269,17 +1284,11 @@ function Listing({ excludeId }) {
1269
1284
  return showDeclined || feature.status !== "declined";
1270
1285
  });
1271
1286
  if (error && visible.length === 0) {
1287
+ const { title, detail } = errorCopy(error, labels);
1272
1288
  return /* @__PURE__ */ jsxs6("div", { className: cx("fw-empty"), role: "alert", children: [
1273
- /* @__PURE__ */ jsx9("p", { children: userMessage(error) }),
1274
- /* @__PURE__ */ jsx9(
1275
- "button",
1276
- {
1277
- type: "button",
1278
- className: cx("fw-btn", "fw-btn--ghost"),
1279
- onClick: () => void refresh(),
1280
- children: labels.retry
1281
- }
1282
- )
1289
+ /* @__PURE__ */ jsx9("p", { className: cx("fw-empty__title"), children: title }),
1290
+ /* @__PURE__ */ jsx9("p", { className: cx("fw-empty__detail"), children: detail }),
1291
+ /* @__PURE__ */ jsx9("button", { type: "button", className: cx("fw-btn-text"), onClick: () => void refresh(), children: labels.retry })
1283
1292
  ] });
1284
1293
  }
1285
1294
  if (isLoading && visible.length === 0) {
@@ -1693,16 +1702,22 @@ function FewchursBoardDialog({
1693
1702
  import { useCallback as useCallback7, useState as useState11 } from "react";
1694
1703
  import { createPortal } from "react-dom";
1695
1704
  import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1705
+ function launcherClasses(floating) {
1706
+ if (!floating) return "fw-launcher";
1707
+ const side = floating === "left" ? " fw-launcher--left" : "";
1708
+ return `fw-launcher fw-launcher--floating${side}`;
1709
+ }
1696
1710
  function FewchursButton({
1697
1711
  children = "Feedback",
1698
1712
  className,
1699
1713
  style,
1700
1714
  ariaLabel,
1715
+ floating = false,
1701
1716
  ...board
1702
1717
  }) {
1703
1718
  const [open, setOpen] = useState11(false);
1704
1719
  const close = useCallback7(() => setOpen(false), []);
1705
- const classes = [board.unstyled ? "" : "fw-launcher", className].filter(Boolean).join(" ");
1720
+ const classes = [board.unstyled ? "" : launcherClasses(floating), className].filter(Boolean).join(" ");
1706
1721
  return /* @__PURE__ */ jsxs10(Fragment3, { children: [
1707
1722
  /* @__PURE__ */ jsx13(
1708
1723
  "button",