@bananapus/nana-sdk-react 51.0.0 → 53.0.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.js CHANGED
@@ -68,9 +68,9 @@ import {
68
68
  } from "@bananapus/nana-sdk-core";
69
69
  import {
70
70
  createContext as createContext8,
71
- useCallback,
71
+ useCallback as useCallback2,
72
72
  useContext as useContext8,
73
- useMemo as useMemo5
73
+ useMemo as useMemo8
74
74
  } from "react";
75
75
  import { isAddressEqual as isAddressEqual4, zeroAddress as zeroAddress4 } from "viem";
76
76
 
@@ -4102,9 +4102,346 @@ function useJBUpcomingRuleset({
4102
4102
  };
4103
4103
  }
4104
4104
 
4105
+ // src/hooks/heldWrite/HeldWritesNotice.tsx
4106
+ import { HELD_RELEASE_LABEL } from "@bananapus/nana-sdk-core/review";
4107
+ import { useState } from "react";
4108
+ import { jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
4109
+ function HeldWritesNotice({
4110
+ items,
4111
+ className
4112
+ }) {
4113
+ const [busy, setBusy] = useState(null);
4114
+ const shown = items.filter(({ view }) => view.line);
4115
+ if (!shown.length) return null;
4116
+ return /* @__PURE__ */ jsx10("div", { role: "status", className, children: shown.map(({ key, label, view, release }) => /* @__PURE__ */ jsxs2("div", { "data-phase": view.phase, children: [
4117
+ label ? /* @__PURE__ */ jsx10("p", { children: label }) : null,
4118
+ /* @__PURE__ */ jsx10("p", { children: view.line }),
4119
+ view.warning ? /* @__PURE__ */ jsx10("p", { children: view.warning }) : null,
4120
+ view.notice ? /* @__PURE__ */ jsx10("p", { children: view.notice }) : null,
4121
+ view.exit ? /* @__PURE__ */ jsx10(
4122
+ "button",
4123
+ {
4124
+ type: "button",
4125
+ disabled: busy === key,
4126
+ onClick: () => {
4127
+ setBusy(key);
4128
+ void release().finally(() => setBusy(null));
4129
+ },
4130
+ children: HELD_RELEASE_LABEL
4131
+ }
4132
+ ) : null
4133
+ ] }, key)) });
4134
+ }
4135
+
4136
+ // src/hooks/heldWrite/useHeldWrite.ts
4137
+ import {
4138
+ createHeldWriteView,
4139
+ HELD_EXIT_LABEL,
4140
+ HELD_SEND_AGAIN_LABEL
4141
+ } from "@bananapus/nana-sdk-core/review";
4142
+ import { useEffect, useMemo as useMemo5, useRef, useSyncExternalStore } from "react";
4143
+ var NO_CALL = {
4144
+ to: "0x0000000000000000000000000000000000000000",
4145
+ data: "0x"
4146
+ };
4147
+ var IDLE = {
4148
+ phase: "clear",
4149
+ state: null,
4150
+ line: null,
4151
+ exit: false,
4152
+ adopting: false
4153
+ };
4154
+ var none = () => () => {
4155
+ };
4156
+ var keyOf = (value) => JSON.stringify(
4157
+ value,
4158
+ (_, field) => typeof field === "bigint" ? `${field}n` : field
4159
+ );
4160
+ var idle = () => IDLE;
4161
+ function useHeldWrite(options) {
4162
+ const {
4163
+ hold,
4164
+ chainId,
4165
+ account,
4166
+ safe = false,
4167
+ namespace,
4168
+ client,
4169
+ pollMs
4170
+ } = options;
4171
+ const callKey = keyOf(options.call ?? null);
4172
+ const legacyKey = keyOf(options.legacy ?? null);
4173
+ const latest = useRef(options);
4174
+ latest.current = options;
4175
+ const handle = useMemo5(() => {
4176
+ const call = latest.current.call;
4177
+ if (!call && !namespace) return void 0;
4178
+ const legacy = latest.current.legacy;
4179
+ return hold.open(
4180
+ {
4181
+ chainId,
4182
+ account,
4183
+ safe,
4184
+ call: call ?? NO_CALL,
4185
+ ...namespace ? { namespace } : {}
4186
+ },
4187
+ legacy ? { legacy } : void 0
4188
+ );
4189
+ }, [hold, chainId, account, safe, namespace, callKey, legacyKey]);
4190
+ const adopts = !!options.adopt;
4191
+ const active = useRef(void 0);
4192
+ const view = useMemo5(() => {
4193
+ if (!handle || !client) return void 0;
4194
+ const own = (callback) => (...args) => {
4195
+ if (active.current === created) callback(...args);
4196
+ };
4197
+ const created = createHeldWriteView(handle, {
4198
+ client,
4199
+ pollMs,
4200
+ onSettled: own(
4201
+ (settled, receipt) => latest.current.onSettled?.(settled, receipt)
4202
+ ),
4203
+ onReleased: own((by) => latest.current.onReleased?.(by)),
4204
+ onAdopted: own((state) => latest.current.onAdopted?.(state)),
4205
+ // A view made with a provider: the app's current one (a removed one
4206
+ // makes a new view, and this one stops).
4207
+ ...adopts ? { adopt: () => latest.current.adopt() } : {}
4208
+ });
4209
+ return created;
4210
+ }, [handle, client, pollMs, adopts]);
4211
+ useEffect(() => {
4212
+ if (!view) return;
4213
+ active.current = view;
4214
+ const stop = view.start();
4215
+ return () => {
4216
+ if (active.current === view) active.current = void 0;
4217
+ stop();
4218
+ };
4219
+ }, [view]);
4220
+ const shown = useSyncExternalStore(
4221
+ view?.subscribe ?? none,
4222
+ view?.get ?? idle,
4223
+ view?.get ?? idle
4224
+ );
4225
+ return {
4226
+ ...shown,
4227
+ exitAction: shown.exit && view ? { label: HELD_EXIT_LABEL, run: view.exit } : null,
4228
+ sendAgainAction: shown.phase === "succeeded" && view ? { label: HELD_SEND_AGAIN_LABEL, run: view.acknowledge } : null,
4229
+ send: (run) => {
4230
+ if (!view)
4231
+ return Promise.reject(
4232
+ new Error("Connect a wallet and build the call before sending.")
4233
+ );
4234
+ if (!!latest.current.adopt !== adopts)
4235
+ return Promise.reject(
4236
+ new Error(
4237
+ "Saved transactions are still being checked. Try again in a moment."
4238
+ )
4239
+ );
4240
+ return view.send(run);
4241
+ },
4242
+ check: () => view ? view.check() : Promise.resolve(IDLE),
4243
+ handle
4244
+ };
4245
+ }
4246
+
4247
+ // src/hooks/heldWrite/useHeldWrites.ts
4248
+ import {
4249
+ createHeldWriteView as createHeldWriteView2
4250
+ } from "@bananapus/nana-sdk-core/review";
4251
+ import { useEffect as useEffect2, useMemo as useMemo6, useState as useState2, useSyncExternalStore as useSyncExternalStore2 } from "react";
4252
+ function useHeldViews(entries, client, pollMs) {
4253
+ const views = useMemo6(
4254
+ () => client ? entries.map((entry) => ({
4255
+ entry,
4256
+ view: createHeldWriteView2(entry.handle, { client, pollMs })
4257
+ })) : [],
4258
+ [entries, client, pollMs]
4259
+ );
4260
+ useEffect2(() => {
4261
+ const stops = views.map(({ view }) => view.start());
4262
+ return () => stops.forEach((stop) => stop());
4263
+ }, [views]);
4264
+ const store = useMemo6(() => feed(views.map(({ view }) => view)), [views]);
4265
+ const shown = useSyncExternalStore2(store.subscribe, store.get, store.get);
4266
+ return views.map(({ entry, view }, index) => ({
4267
+ key: entry.key,
4268
+ ...entry.label ? { label: entry.label } : {},
4269
+ view: shown[index],
4270
+ release: view.exit
4271
+ })).filter(({ view }) => view.phase !== "clear");
4272
+ }
4273
+ function feed(views) {
4274
+ let snapshot = views.map((view) => view.get());
4275
+ return {
4276
+ subscribe(listener) {
4277
+ const stops = views.map(
4278
+ (view) => view.subscribe(() => {
4279
+ snapshot = views.map((each) => each.get());
4280
+ listener();
4281
+ })
4282
+ );
4283
+ return () => stops.forEach((stop) => stop());
4284
+ },
4285
+ get: () => snapshot
4286
+ };
4287
+ }
4288
+ function useHeldIntents({
4289
+ hold,
4290
+ chainId,
4291
+ account,
4292
+ client,
4293
+ intents,
4294
+ pollMs
4295
+ }) {
4296
+ const intentsKey = JSON.stringify(intents);
4297
+ const entries = useMemo6(
4298
+ () => account ? intents.map(({ namespace, label }) => ({
4299
+ key: namespace,
4300
+ label,
4301
+ handle: hold.open({
4302
+ chainId,
4303
+ account,
4304
+ safe: false,
4305
+ namespace,
4306
+ call: { to: account, data: "0x" }
4307
+ })
4308
+ })) : [],
4309
+ // The intents key stands for the intents.
4310
+ [hold, chainId, account, intentsKey]
4311
+ );
4312
+ return useHeldViews(entries, client, pollMs);
4313
+ }
4314
+ function useHeldWrites({
4315
+ hold,
4316
+ chainId,
4317
+ account,
4318
+ client,
4319
+ label,
4320
+ pollMs = 3e4
4321
+ }) {
4322
+ const [generation, setGeneration] = useState2(0);
4323
+ useEffect2(() => {
4324
+ const again = () => setGeneration((value) => value + 1);
4325
+ globalThis.addEventListener?.("storage", again);
4326
+ const timer = setInterval(again, pollMs);
4327
+ return () => {
4328
+ globalThis.removeEventListener?.("storage", again);
4329
+ clearInterval(timer);
4330
+ };
4331
+ }, [pollMs]);
4332
+ const listed = useMemo6(
4333
+ () => account ? hold.list({ account, chainId }) : [],
4334
+ [hold, account, chainId, generation]
4335
+ );
4336
+ const listedKey = listed.map(({ key, record }) => `${key}:${record.id}`).join("|");
4337
+ const entries = useMemo6(
4338
+ () => listed.map(({ key, record, handle }) => ({
4339
+ key,
4340
+ label: label?.(record),
4341
+ handle
4342
+ })),
4343
+ // The listed key stands for the list.
4344
+ [listedKey]
4345
+ );
4346
+ return useHeldViews(entries, client, pollMs);
4347
+ }
4348
+
4349
+ // src/hooks/heldWrite/useSavedAttempt.ts
4350
+ import {
4351
+ checkedItLabel,
4352
+ SAVED_ATTEMPT_CONTINUE_ANYWAY_LABEL,
4353
+ SAVED_ATTEMPT_UNREADABLE_LINE
4354
+ } from "@bananapus/nana-sdk-core/review";
4355
+ import {
4356
+ useCallback,
4357
+ useEffect as useEffect3,
4358
+ useLayoutEffect,
4359
+ useMemo as useMemo7,
4360
+ useRef as useRef2,
4361
+ useState as useState3
4362
+ } from "react";
4363
+ var keyOf2 = (attempt) => attempt ? JSON.stringify(
4364
+ { ...attempt, authenticate: void 0 },
4365
+ (_, value) => typeof value === "bigint" ? value.toString() : value
4366
+ ) : null;
4367
+ function useSavedAttempt(options) {
4368
+ const { recovery, client, checkNext } = options;
4369
+ const key = keyOf2(options.attempt);
4370
+ const attempt = options.attempt;
4371
+ const latest = useRef2(attempt);
4372
+ latest.current = attempt;
4373
+ const lifecycle = useMemo7(
4374
+ () => ({ attempt, recovery, client, request: 0 }),
4375
+ // The key stands for the attempt.
4376
+ [key, recovery, client]
4377
+ );
4378
+ const current = (of) => mounted.current && rendered.current === of;
4379
+ const rendered = useRef2(void 0);
4380
+ rendered.current = lifecycle;
4381
+ const mounted = useRef2(false);
4382
+ useLayoutEffect(() => {
4383
+ mounted.current = true;
4384
+ return () => {
4385
+ mounted.current = false;
4386
+ };
4387
+ }, []);
4388
+ const [shown, setShown] = useState3({ outcome: null, recovering: false });
4389
+ const [unreadable, setUnreadable] = useState3();
4390
+ const recover = useCallback(async () => {
4391
+ const { recovery: recovery2, client: client2 } = lifecycle;
4392
+ const attempt2 = latest.current;
4393
+ if (!attempt2 || !client2 || !current(lifecycle)) return null;
4394
+ const request = ++lifecycle.request;
4395
+ setShown((now) => ({
4396
+ of: lifecycle,
4397
+ outcome: now.of === lifecycle ? now.outcome : null,
4398
+ recovering: true
4399
+ }));
4400
+ const outcome2 = await recovery2.recover(attempt2, client2);
4401
+ if (!current(lifecycle) || lifecycle.request !== request) return null;
4402
+ setShown({ of: lifecycle, outcome: outcome2, recovering: false });
4403
+ return outcome2;
4404
+ }, [lifecycle]);
4405
+ useEffect3(() => {
4406
+ void recover();
4407
+ }, [recover]);
4408
+ const retire = useCallback(
4409
+ async (reason) => {
4410
+ const { recovery: recovery2 } = lifecycle;
4411
+ const attempt2 = latest.current;
4412
+ if (!attempt2 || !current(lifecycle)) return { retired: false };
4413
+ const retired = await recovery2.retire(attempt2, reason);
4414
+ if (!current(lifecycle)) return retired;
4415
+ if (!retired.retired && retired.reason === "unreadable")
4416
+ setUnreadable(lifecycle);
4417
+ await recover();
4418
+ return retired;
4419
+ },
4420
+ [lifecycle, recover]
4421
+ );
4422
+ const outcome = shown.of === lifecycle ? shown.outcome : null;
4423
+ return {
4424
+ outcome,
4425
+ recovering: shown.of === lifecycle && shown.recovering,
4426
+ recover,
4427
+ checkAction: outcome?.status === "check" ? {
4428
+ line: outcome.line,
4429
+ warning: outcome.warning,
4430
+ label: checkedItLabel(checkNext),
4431
+ run: () => retire({ by: "check", outcome })
4432
+ } : null,
4433
+ unreadableAction: outcome?.status === "check" && unreadable === lifecycle ? {
4434
+ line: SAVED_ATTEMPT_UNREADABLE_LINE,
4435
+ label: SAVED_ATTEMPT_CONTINUE_ANYWAY_LABEL,
4436
+ run: () => retire({ by: "check", outcome, force: "unreadable" })
4437
+ } : null,
4438
+ retire
4439
+ };
4440
+ }
4441
+
4105
4442
  // src/contexts/JBContractContext/JBContractContext.tsx
4106
4443
  import { useReadContract as useReadContract11 } from "wagmi";
4107
- import { jsx as jsx10 } from "react/jsx-runtime";
4444
+ import { jsx as jsx11 } from "react/jsx-runtime";
4108
4445
  function outsideProvider() {
4109
4446
  throw new Error(
4110
4447
  "JBContractContext was read outside a JBContractProvider. Wrap the tree in JBProjectProvider or JBContractProvider."
@@ -4164,7 +4501,7 @@ var JBContractProvider = ({
4164
4501
  version
4165
4502
  }) => {
4166
4503
  const chainId = useJBChainId();
4167
- const enabled = useCallback(
4504
+ const enabled = useCallback2(
4168
4505
  (selector) => {
4169
4506
  if (typeof include === "undefined") return true;
4170
4507
  return include.some((c) => selector.includes(c));
@@ -4187,7 +4524,7 @@ var JBContractProvider = ({
4187
4524
  chainId,
4188
4525
  args: usdcAddress && enabled([1 /* PrimaryNativePaymentTerminal */]) ? [projectId, usdcAddress] : void 0
4189
4526
  });
4190
- const primaryNativeTerminal = useMemo5(() => {
4527
+ const primaryNativeTerminal = useMemo8(() => {
4191
4528
  const isTerminal = (address) => address !== void 0 && !isAddressEqual4(address, zeroAddress4);
4192
4529
  if (isTerminal(primaryNativeTerminalEth.data))
4193
4530
  return primaryNativeTerminalEth;
@@ -4210,13 +4547,13 @@ var JBContractProvider = ({
4210
4547
  staleTime: Infinity
4211
4548
  }
4212
4549
  });
4213
- const controllerAddress = useMemo5(() => controller.data, [controller.data]);
4214
- const hasController = useMemo5(() => {
4550
+ const controllerAddress = useMemo8(() => controller.data, [controller.data]);
4551
+ const hasController = useMemo8(() => {
4215
4552
  return Boolean(
4216
4553
  controllerAddress && !isAddressEqual4(controllerAddress, zeroAddress4)
4217
4554
  );
4218
4555
  }, [controllerAddress]);
4219
- const resolvedControllerAddress = useMemo5(() => {
4556
+ const resolvedControllerAddress = useMemo8(() => {
4220
4557
  return hasController ? controllerAddress : void 0;
4221
4558
  }, [controllerAddress, hasController]);
4222
4559
  const fundAccessLimits = useReadContract11({
@@ -4263,7 +4600,7 @@ var JBContractProvider = ({
4263
4600
  ...query,
4264
4601
  isLoading: Boolean(query.isLoading || controller.isLoading)
4265
4602
  });
4266
- const contractAddress = useCallback(
4603
+ const contractAddress = useCallback2(
4267
4604
  (contract, _chainId) => {
4268
4605
  return getJBContractAddress3(contract, version, _chainId || chainId);
4269
4606
  },
@@ -4278,7 +4615,7 @@ var JBContractProvider = ({
4278
4615
  splits: dependent(splits)
4279
4616
  };
4280
4617
  debug10("JBContractContext", { projectId, contracts });
4281
- return /* @__PURE__ */ jsx10(
4618
+ return /* @__PURE__ */ jsx11(
4282
4619
  JBContractContext.Provider,
4283
4620
  {
4284
4621
  value: {
@@ -4436,7 +4773,7 @@ function useGetRelayrTxQuote() {
4436
4773
  }
4437
4774
 
4438
4775
  // src/lib/relayr/hooks/useGetRelayrTxBundle.ts
4439
- import { useCallback as useCallback2, useMemo as useMemo6, useState } from "react";
4776
+ import { useCallback as useCallback3, useMemo as useMemo9, useState as useState4 } from "react";
4440
4777
  import { useQuery as useQuery9 } from "wagmi/query";
4441
4778
  var POLL_INTERVAL = 2e3;
4442
4779
  var transactionSucceeded = (state) => state === "Success" || state === "Completed";
@@ -4454,14 +4791,14 @@ var fetchTxBundle = async (uuid, signal) => {
4454
4791
  return response.json();
4455
4792
  };
4456
4793
  function useGetRelayrTxBundle() {
4457
- const [uuid, setUuid] = useState();
4794
+ const [uuid, setUuid] = useState4();
4458
4795
  const getRelayrTxBundle = useQuery9({
4459
4796
  queryKey: ["txBundle", uuid],
4460
4797
  queryFn: ({ signal }) => fetchTxBundle(uuid, signal),
4461
4798
  enabled: !!uuid,
4462
4799
  refetchInterval: (query) => areTransactionsComplete(query.state.data) || hasTransactionFailure(query.state.data) ? false : POLL_INTERVAL
4463
4800
  });
4464
- const startPolling = useCallback2(
4801
+ const startPolling = useCallback3(
4465
4802
  (bundleUuid) => {
4466
4803
  if (bundleUuid === uuid) {
4467
4804
  void getRelayrTxBundle.refetch();
@@ -4471,11 +4808,11 @@ function useGetRelayrTxBundle() {
4471
4808
  },
4472
4809
  [getRelayrTxBundle.refetch, uuid]
4473
4810
  );
4474
- const isComplete = useMemo6(
4811
+ const isComplete = useMemo9(
4475
4812
  () => areTransactionsComplete(getRelayrTxBundle.data),
4476
4813
  [getRelayrTxBundle.data]
4477
4814
  );
4478
- const hasFailed = useMemo6(
4815
+ const hasFailed = useMemo9(
4479
4816
  () => hasTransactionFailure(getRelayrTxBundle.data),
4480
4817
  [getRelayrTxBundle.data]
4481
4818
  );
@@ -4494,13 +4831,13 @@ function useGetRelayrTxBundle() {
4494
4831
  }
4495
4832
 
4496
4833
  // src/lib/relayr/hooks/useSendRelayrTx.ts
4497
- import { useCallback as useCallback3 } from "react";
4834
+ import { useCallback as useCallback4 } from "react";
4498
4835
  import { useChainId, useSwitchChain as useSwitchChain2, useSendTransaction } from "wagmi";
4499
4836
  function useSendRelayrTx() {
4500
4837
  const chainId = useChainId();
4501
4838
  const { switchChainAsync } = useSwitchChain2();
4502
4839
  const _sendTransaction = useSendTransaction();
4503
- const sendRelayrTx = useCallback3(
4840
+ const sendRelayrTx = useCallback4(
4504
4841
  async (chainPayment) => {
4505
4842
  if (chainId !== chainPayment.chain) {
4506
4843
  try {
@@ -4532,6 +4869,7 @@ export {
4532
4869
  DASHBOARD,
4533
4870
  DEFAULT_ETHER_PRICE_ENDPOINT,
4534
4871
  DynamicContract,
4872
+ HeldWritesNotice,
4535
4873
  JBChainContext,
4536
4874
  JBChainProvider,
4537
4875
  JBContractContext,
@@ -4555,6 +4893,9 @@ export {
4555
4893
  useFind721DataHook,
4556
4894
  useGetRelayrTxBundle,
4557
4895
  useGetRelayrTxQuote,
4896
+ useHeldIntents,
4897
+ useHeldWrite,
4898
+ useHeldWrites,
4558
4899
  useJBChainId,
4559
4900
  useJBContractContext,
4560
4901
  useJBDataHookContext,
@@ -4573,6 +4914,7 @@ export {
4573
4914
  usePreparePayMetadata,
4574
4915
  useProjectMetadata,
4575
4916
  useResolveDataHook,
4917
+ useSavedAttempt,
4576
4918
  useSendRelayrTx,
4577
4919
  useShopPurchases,
4578
4920
  useSuckers,