@mesub/react 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -214,6 +214,9 @@ nor past its date to pay by. What came of it is said under the button: the
214
214
  payment on its way, or why not (a short wallet spends no retry). On Free,
215
215
  where Mesub does not try again on its own, the card shows that date to pay by.
216
216
 
217
+ When the server could not read a subscription's past payments, its window
218
+ says so in one line rather than show none.
219
+
217
220
  One whose charges are held back for a cause that is not the customer's
218
221
  (`blocked_since`) says so in one line: payments are on hold, nothing is asked
219
222
  of them, and until when their access runs. The cause is never named to them,
@@ -245,6 +248,9 @@ function Mine() {
245
248
  - `subscriptions` are newest first, each with `action`: `cancel`, `resume`,
246
249
  `close` or null, and `payable`: whether **Pay now** can succeed. Checkouts
247
250
  nobody signed are left out.
251
+ - `hasMore` is true when the customer holds more than the server listed (its
252
+ 500 newest): older ones are missing. `ManageSubscriptions` says so under its
253
+ list.
248
254
  - `payNow(id)` pays that late payment now and resolves `{ ok, message }`, the
249
255
  words to show; it never throws.
250
256
  - `manage(id)` opens the dialog for that action and resolves when it closes:
package/dist/index.cjs CHANGED
@@ -71,6 +71,10 @@ function paidOf(answer) {
71
71
  const whole = Array.isArray(answer.payments) && answer.payments.length < LISTED_AT_MOST;
72
72
  return whole ? read(answer.listed_paid) : null;
73
73
  }
74
+ function paymentsErrorOf(answer) {
75
+ const error = answer.payments_error;
76
+ return isObject(error) && typeof error.code === "string" && typeof error.message === "string" ? { code: error.code, message: error.message } : null;
77
+ }
74
78
  function createApi(options) {
75
79
  const base = options.endpoint.replace(/\/+$/, "");
76
80
  const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
@@ -141,7 +145,11 @@ function createApi(options) {
141
145
  ),
142
146
  subscriptions: async () => {
143
147
  const answer = await request("GET", "/subscriptions", void 0, REQUEST_TIMEOUT_MS);
144
- return Array.isArray(answer.subscriptions) ? answer.subscriptions : [];
148
+ return {
149
+ subscriptions: Array.isArray(answer.subscriptions) ? answer.subscriptions : [],
150
+ // An older server does not say: read as whole.
151
+ has_more: answer.has_more === true
152
+ };
145
153
  },
146
154
  subscription: async (id) => {
147
155
  const answer = await request("GET", at(id), void 0, REQUEST_TIMEOUT_MS);
@@ -149,7 +157,8 @@ function createApi(options) {
149
157
  subscription: answer.subscription,
150
158
  upcoming: Array.isArray(answer.upcoming) ? answer.upcoming : [],
151
159
  payments: Array.isArray(answer.payments) ? answer.payments : null,
152
- paid: paidOf(answer)
160
+ paid: paidOf(answer),
161
+ payments_error: paymentsErrorOf(answer)
153
162
  };
154
163
  },
155
164
  prepare: async (plan, wallet) => await request(
@@ -2185,6 +2194,7 @@ function payableOf(row, now) {
2185
2194
  const deadline = Date.parse(row.retry_deadline ?? "");
2186
2195
  return Number.isNaN(deadline) || deadline > now;
2187
2196
  }
2197
+ var MORE_NOT_LISTED = "Only your most recent subscriptions are listed here.";
2188
2198
  var PAY_NOW_SENT = "Payment sent. It shows here once the network confirms it.";
2189
2199
  function payNowRefusal(failure) {
2190
2200
  if (!(failure instanceof MesubClientError)) return "Something went wrong.";
@@ -2210,6 +2220,7 @@ function useSubscriptions() {
2210
2220
  const { api, revision, manage: open } = useMesubInternal();
2211
2221
  const [state, setState] = (0, import_react9.useState)("loading");
2212
2222
  const [rows, setRows] = (0, import_react9.useState)([]);
2223
+ const [hasMore, setHasMore] = (0, import_react9.useState)(false);
2213
2224
  const [error, setError] = (0, import_react9.useState)(null);
2214
2225
  const [now, setNow] = (0, import_react9.useState)(() => Date.now());
2215
2226
  const read = (0, import_react9.useRef)(0);
@@ -2225,7 +2236,8 @@ function useSubscriptions() {
2225
2236
  try {
2226
2237
  const served = await api.subscriptions();
2227
2238
  if (!alive.current || id !== read.current) return;
2228
- setRows(served.filter((row) => HELD.has(row.status)));
2239
+ setRows(served.subscriptions.filter((row) => HELD.has(row.status)));
2240
+ setHasMore(served.has_more);
2229
2241
  setNow(Date.now());
2230
2242
  setError(null);
2231
2243
  setState("ready");
@@ -2233,6 +2245,7 @@ function useSubscriptions() {
2233
2245
  if (!alive.current || id !== read.current) return;
2234
2246
  if (signedOut(failure)) {
2235
2247
  setRows([]);
2248
+ setHasMore(false);
2236
2249
  setError(null);
2237
2250
  setState("signed-out");
2238
2251
  return;
@@ -2286,7 +2299,7 @@ function useSubscriptions() {
2286
2299
  },
2287
2300
  [api, reload]
2288
2301
  );
2289
- return { state, subscriptions, error, reload, payNow, manage };
2302
+ return { state, subscriptions, hasMore, error, reload, payNow, manage };
2290
2303
  }
2291
2304
  var STATUS = {
2292
2305
  active: "Active",
@@ -2418,7 +2431,7 @@ function PayNow({ pay }) {
2418
2431
  }
2419
2432
  function ManageSubscriptions({ onChanged, ...rest }) {
2420
2433
  const { theme } = useMesubInternal();
2421
- const { state, subscriptions, error, reload, payNow, manage } = useSubscriptions();
2434
+ const { state, subscriptions, hasMore, error, reload, payNow, manage } = useSubscriptions();
2422
2435
  const plans = usePlans(subscriptions.flatMap((row) => row.plan ? [row.plan] : []));
2423
2436
  const [retrying, setRetrying] = (0, import_react9.useState)(false);
2424
2437
  async function act(id) {
@@ -2453,7 +2466,7 @@ function ManageSubscriptions({ onChanged, ...rest }) {
2453
2466
  }
2454
2467
  )
2455
2468
  ] });
2456
- } else if (subscriptions.length === 0) {
2469
+ } else if (subscriptions.length === 0 && !hasMore) {
2457
2470
  body = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { "data-mesub-empty": "none", children: "You have no subscription yet." });
2458
2471
  } else {
2459
2472
  body = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { "data-mesub-subscription-list": "", "aria-label": "Your subscriptions", children: subscriptions.map((row) => {
@@ -2515,14 +2528,17 @@ function ManageSubscriptions({ onChanged, ...rest }) {
2515
2528
  ] }, row.id);
2516
2529
  }) });
2517
2530
  }
2518
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2531
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2519
2532
  "div",
2520
2533
  {
2521
2534
  ...rest,
2522
2535
  "data-mesub-subscriptions": "",
2523
2536
  "data-mesub-state": state,
2524
2537
  "data-mesub-theme": theme,
2525
- children: body
2538
+ children: [
2539
+ body,
2540
+ state === "ready" && hasMore ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { "data-mesub-subscription-note": "", children: MORE_NOT_LISTED }) : null
2541
+ ]
2526
2542
  }
2527
2543
  );
2528
2544
  }
@@ -2587,6 +2603,7 @@ function useDetail(row) {
2587
2603
  }, [api, id, revision]);
2588
2604
  return detail?.subscription.id === id ? detail : null;
2589
2605
  }
2606
+ var PAYMENTS_UNREAD = "Past payments could not be read just now.";
2590
2607
  var PAST_SHOWN = 5;
2591
2608
  var PAST_STEP = 10;
2592
2609
  function SubscriptionDialog({
@@ -2596,7 +2613,7 @@ function SubscriptionDialog({
2596
2613
  const [shown, setShown] = (0, import_react10.useState)(PAST_SHOWN);
2597
2614
  const titleId = (0, import_react10.useId)();
2598
2615
  const { chain } = useMesubInternal();
2599
- const { state, subscriptions, error, reload, payNow, manage } = useSubscriptions();
2616
+ const { state, subscriptions, hasMore, error, reload, payNow, manage } = useSubscriptions();
2600
2617
  const row = pickSubscription(subscriptions, slug) ?? null;
2601
2618
  const plan = usePlan(row?.plan);
2602
2619
  const detail = useDetail(row);
@@ -2620,7 +2637,7 @@ function SubscriptionDialog({
2620
2637
  } else if (!row) {
2621
2638
  body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2622
2639
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { id: titleId, children: "No subscription yet" }),
2623
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "You are not subscribed to this plan." })
2640
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: hasMore ? "None of your most recent subscriptions is to this plan." : "You are not subscribed to this plan." })
2624
2641
  ] });
2625
2642
  } else {
2626
2643
  const name = plan?.name ?? row.plan ?? "Subscription";
@@ -2629,6 +2646,7 @@ function SubscriptionDialog({
2629
2646
  const note = noteOf(row);
2630
2647
  const upcoming = detail?.upcoming ?? [];
2631
2648
  const past = detail?.payments ?? [];
2649
+ const unread = detail?.payments === null && detail.payments_error !== null;
2632
2650
  const total = detail?.paid?.amount ? paidBrief(detail.paid.amount, plan, formatAmount) : null;
2633
2651
  const rate = plan ? briefPrice(plan) : null;
2634
2652
  const due = (display) => display ? `${grouped(display)}${symbolOf(plan)}` : "";
@@ -2680,7 +2698,7 @@ function SubscriptionDialog({
2680
2698
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { title: row.wallet, children: shortAddress(row.wallet) })
2681
2699
  ] })
2682
2700
  ] }),
2683
- upcoming.length > 0 || past.length > 0 ? (
2701
+ upcoming.length > 0 || past.length > 0 || unread ? (
2684
2702
  // Closed until asked for, as the terms are on the checkout: same toggle.
2685
2703
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("details", { "data-mesub-terms": "", "data-mesub-payments": "", children: [
2686
2704
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("summary", { children: "Payments" }),
@@ -2702,6 +2720,10 @@ function SubscriptionDialog({
2702
2720
  `next-${next.due_at}`
2703
2721
  )) })
2704
2722
  ] }) : null,
2723
+ unread ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { "aria-label": "Past payments", children: [
2724
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "Past" }),
2725
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { "data-mesub-payments-error": "", children: PAYMENTS_UNREAD })
2726
+ ] }) : null,
2705
2727
  past.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { "aria-label": "Past payments", children: [
2706
2728
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "Past" }),
2707
2729
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { children: past.slice(0, shown).map((payment) => {