@taskon/widget-react 0.0.1-beta.2 → 0.0.1-beta.4

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.
Files changed (44) hide show
  1. package/README.md +56 -17
  2. package/dist/CommunityTaskList.css +1593 -1741
  3. package/dist/EligibilityInfo.css +1275 -582
  4. package/dist/LeaderboardWidget.css +355 -152
  5. package/dist/PageBuilder.css +0 -2
  6. package/dist/Quest.css +1140 -903
  7. package/dist/TaskOnProvider.css +50 -31
  8. package/dist/UserCenterWidget.css +108 -237
  9. package/dist/UserCenterWidget2.css +2016 -711
  10. package/dist/chunks/{CommunityTaskList-BlH1Wdd5.js → CommunityTaskList-C9Gv8KOF.js} +962 -827
  11. package/dist/chunks/{EligibilityInfo-C7GZ2G5u.js → EligibilityInfo-D-Fuy9GE.js} +1137 -449
  12. package/dist/chunks/{LeaderboardWidget-CmYfDeHV.js → LeaderboardWidget-BV2D2q1N.js} +15 -10
  13. package/dist/chunks/{PageBuilder-Bw0zSkFh.js → PageBuilder-DQoU4Mwf.js} +5 -5
  14. package/dist/chunks/{Quest-DKFZ-pPU.js → Quest-B5NyVr3o.js} +516 -325
  15. package/dist/chunks/{TaskOnProvider-BD6Vp2x8.js → TaskOnProvider-93UxARFo.js} +2 -207
  16. package/dist/chunks/{ThemeProvider-wnSXrNQb.js → ThemeProvider-CPI_roeh.js} +249 -57
  17. package/dist/chunks/{UserCenterWidget-Cw6h_5hT.js → UserCenterWidget-BRtigY_S.js} +206 -1002
  18. package/dist/chunks/UserCenterWidget-cADBSVg7.js +8358 -0
  19. package/dist/chunks/{WidgetShell-D_5OjvNZ.js → dynamic-import-helper-DwXlQC0S.js} +607 -40
  20. package/dist/chunks/useToast-CaRkylKe.js +304 -0
  21. package/dist/chunks/{usercenter-ja-uu-XfVF9.js → usercenter-ja-B2465c1O.js} +4 -10
  22. package/dist/chunks/{usercenter-ko-DYgUOVzd.js → usercenter-ko-xAEYxqLg.js} +4 -10
  23. package/dist/community-task.d.ts +34 -3
  24. package/dist/community-task.js +1 -1
  25. package/dist/core.d.ts +40 -3
  26. package/dist/core.js +9 -10
  27. package/dist/dynamic-import-helper.css +596 -289
  28. package/dist/index.d.ts +207 -10
  29. package/dist/index.js +21 -19
  30. package/dist/leaderboard.d.ts +8 -1
  31. package/dist/leaderboard.js +2 -2
  32. package/dist/page-builder.js +1 -1
  33. package/dist/quest.d.ts +8 -2
  34. package/dist/quest.js +1 -1
  35. package/dist/user-center.d.ts +20 -136
  36. package/dist/user-center.js +19 -236
  37. package/package.json +10 -2
  38. package/dist/TipPopover.css +0 -210
  39. package/dist/WidgetShell.css +0 -182
  40. package/dist/chunks/TipPopover-BrW8jo71.js +0 -2926
  41. package/dist/chunks/UserCenterWidget-BE329iS7.js +0 -3546
  42. package/dist/chunks/dynamic-import-helper-DxEFwm31.js +0 -537
  43. package/dist/chunks/useToast-B-wyO5zL.js +0 -93
  44. package/dist/chunks/useWidgetLocale-JDelxtt8.js +0 -74
@@ -1,11 +1,141 @@
1
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import React__default, { forwardRef, useRef, useEffect, useCallback, useImperativeHandle, useState, useMemo, useContext } from "react";
3
- import { createUserApi, ChainType, parseTitleExpress, UserIdentityType, SnsType, createQuestApi, createCommonApi, powIcon, contractInteractiveIcon, getSwapDexTitleExpress, RewardType, EligibilityTemplateId, UserEligibleStatus } from "@taskon/core";
4
- import { d as TaskOnContext, u as useTaskOnAuth } from "./ThemeProvider-wnSXrNQb.js";
5
- import { R as Root2, b as Trigger, P as Portal, C as Content2, A as Arrow2, T as TipPopover, a as useBindSocialAccount } from "./TipPopover-BrW8jo71.js";
6
- import { u as useWallet, b as useToast } from "./useToast-B-wyO5zL.js";
7
- import { D as Dialog } from "./WidgetShell-D_5OjvNZ.js";
8
- import '../EligibilityInfo.css';const Textarea = forwardRef(
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import React__default, { forwardRef, useRef, useEffect, useCallback, useImperativeHandle, useMemo, useContext, useState } from "react";
3
+ import { createUserApi, ChainType, getTxExplorerUrl, RewardType, parseTitleExpress, UserIdentityType, SnsType, createQuestApi, isUnauthorizedError, ErrorCode, ApiError, createCommonApi, powIcon, contractInteractiveIcon, getSwapDexTitleExpress, EligibilityTemplateId, UserEligibleStatus } from "@taskon/core";
4
+ import { u as useWallet, d as useToast, e as createEthereumAdapterFromProvider } from "./useToast-CaRkylKe.js";
5
+ import { k as useIsMobile, l as useChainMap, m as useNftClaim, C as ClaimNftDialog, n as PendingTxDialog, B as BindWalletDialog, R as Root2, o as Trigger, p as Portal, q as Content2, r as Arrow2, s as TipPopover, g as useBindSocialAccount, j as enMessages, a as useTokenAssets, u as useRewardDetails, c as usePointsHistory, L as LoadingState, i as TokenRewardContent, h as PointsList, E as EmptyState, N as NftRewardContent, W as WithdrawForm } from "./UserCenterWidget-cADBSVg7.js";
6
+ import { p as TaskOnContext, s as useTaskOnPortalContainer, u as useTaskOnAuth, b as useWidgetLocale } from "./ThemeProvider-CPI_roeh.js";
7
+ import { D as Dialog, _ as __variableDynamicImportRuntimeHelper } from "./dynamic-import-helper-DwXlQC0S.js";
8
+ import '../EligibilityInfo.css';const successIconUrl = new URL("data:image/svg+xml,%3csvg%20width='46'%20height='46'%20viewBox='0%200%2046%2046'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3ccircle%20cx='23'%20cy='23'%20r='23'%20fill='%2331FF90'/%3e%3cpath%20d='M16.4281%2022.8899L20.6446%2027.1063L29.6798%2018.0711'%20stroke='black'%20stroke-width='5'%20stroke-linecap='square'%20stroke-linejoin='round'/%3e%3c/svg%3e", import.meta.url).href;
9
+ const warnIconUrl = new URL("data:image/svg+xml,%3csvg%20width='46'%20height='46'%20viewBox='0%200%2046%2046'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M22.9783%200.0225143C10.2876%200.0225143%200%2010.3101%200%2023.0006C0%2035.6903%2010.2876%2045.9776%2022.9783%2045.9776C35.6682%2045.9776%2045.9556%2035.6903%2045.9556%2023.0006C45.9558%2010.3101%2035.6682%200.0225143%2022.9783%200.0225143ZM21.797%209.85031C21.797%209.01328%2022.4783%208.33197%2023.3154%208.33197C24.1524%208.33197%2024.8337%209.01328%2024.8337%209.85031V11.729C24.8337%2012.5662%2024.1524%2013.2475%2023.3154%2013.2475C22.4783%2013.2475%2021.797%2012.5662%2021.797%2011.7292V9.85031ZM12.0443%2023.1095H10.1657C9.32842%2023.1095%208.64717%2022.4282%208.64717%2021.5912C8.64717%2020.7541%209.32842%2020.0729%2010.1657%2020.0729H12.0443C12.8814%2020.0729%2013.5627%2020.7541%2013.5627%2021.5912C13.5627%2022.4282%2012.8819%2023.1095%2012.0443%2023.1095ZM12.6546%2014.9563C12.368%2014.6697%2012.2098%2014.2882%2012.2098%2013.8823C12.2098%2013.477%2012.3678%2013.0957%2012.6546%2012.8086C12.9411%2012.522%2013.3223%2012.3638%2013.7282%2012.3638C14.134%2012.3638%2014.515%2012.5216%2014.8021%2012.8086L16.1305%2014.137C16.4173%2014.4241%2016.5753%2014.8054%2016.5753%2015.2107C16.5753%2015.6165%2016.4171%2015.9976%2016.1305%2016.2847C15.843%2016.5715%2015.4617%2016.7295%2015.0565%2016.7295C14.6511%2016.7295%2014.2698%2016.5715%2013.9828%2016.2847L12.6546%2014.9563ZM24.2545%2039.3233H22.3764C21.5389%2039.3233%2020.8574%2038.642%2020.8574%2037.8045C20.8574%2036.9674%2021.5389%2036.2861%2022.3764%2036.2861H24.2545C25.0916%2036.2861%2025.7731%2036.9674%2025.7731%2037.8045C25.7732%2038.6421%2025.0916%2039.3233%2024.2545%2039.3233ZM26.1334%2035.8432H20.4976C19.6603%2035.8432%2018.9792%2035.1622%2018.9792%2034.3253C18.9792%2033.4875%2019.6603%2032.8063%2020.4976%2032.8063H26.133C26.9701%2032.8063%2027.6513%2033.4878%2027.6513%2034.3253C27.6518%2035.1624%2026.9705%2035.8432%2026.1334%2035.8432ZM23.3154%2032.033C18.335%2032.033%2014.2826%2027.9809%2014.2826%2023C14.2826%2018.0196%2018.3345%2013.9671%2023.3154%2013.9671C28.2958%2013.9671%2032.3478%2018.0191%2032.3478%2023C32.3478%2027.9809%2028.2958%2032.033%2023.3154%2032.033ZM31.574%2016.7295C31.1682%2016.7295%2030.7867%2016.5713%2030.5%2016.2843C29.9086%2015.6924%2029.9086%2014.7293%2030.5%2014.1374L31.8288%2012.8086C32.1155%2012.522%2032.4972%2012.364%2032.9026%2012.364C33.3082%2012.364%2033.6895%2012.522%2033.9762%2012.8084C34.568%2013.4009%2034.5676%2014.364%2033.9762%2014.9559L32.6478%2016.2843C32.3613%2016.5715%2031.98%2016.7295%2031.574%2016.7295ZM36.4652%2023.1095H34.5862C33.7491%2023.1095%2033.0678%2022.4282%2033.0678%2021.5912C33.0678%2020.7541%2033.7491%2020.0729%2034.5862%2020.0729H36.4652C37.3025%2020.0729%2037.9835%2020.7541%2037.9835%2021.5912C37.9836%2022.4282%2037.3025%2023.1095%2036.4652%2023.1095Z'%20fill='%23F89D35'/%3e%3c/svg%3e", import.meta.url).href;
10
+ function getNoticeIcon(type) {
11
+ if (type === "success") {
12
+ return successIconUrl;
13
+ }
14
+ if (type === "warn") {
15
+ return warnIconUrl;
16
+ }
17
+ return null;
18
+ }
19
+ function ConfirmNoticeDialog({
20
+ open,
21
+ onOpenChange,
22
+ type = "default",
23
+ title,
24
+ desc,
25
+ col = false,
26
+ cancelButton,
27
+ confirmButton,
28
+ onCancel,
29
+ onConfirm,
30
+ onClose,
31
+ closeOnCancel = true,
32
+ closeOnConfirm = true,
33
+ cancelDisabled = false,
34
+ confirmDisabled = false,
35
+ showCloseButton = false,
36
+ closeOnOverlayClick = true,
37
+ closeOnEscapeKey = true,
38
+ maxWidth = 470,
39
+ className,
40
+ contentClassName,
41
+ children,
42
+ actions,
43
+ footer,
44
+ accessibilityTitle,
45
+ accessibilityDescription
46
+ }) {
47
+ const noticeIcon = getNoticeIcon(type);
48
+ const shouldRenderDefaultActions = !actions && Boolean(cancelButton || confirmButton);
49
+ const closeDialog = React__default.useCallback(() => {
50
+ onOpenChange(false);
51
+ onClose == null ? void 0 : onClose();
52
+ }, [onOpenChange, onClose]);
53
+ const handleOpenChange = React__default.useCallback(
54
+ (nextOpen) => {
55
+ onOpenChange(nextOpen);
56
+ if (!nextOpen) {
57
+ onClose == null ? void 0 : onClose();
58
+ }
59
+ },
60
+ [onOpenChange, onClose]
61
+ );
62
+ const handleCancel = React__default.useCallback(() => {
63
+ onCancel == null ? void 0 : onCancel();
64
+ if (closeOnCancel) {
65
+ closeDialog();
66
+ }
67
+ }, [onCancel, closeOnCancel, closeDialog]);
68
+ const handleConfirm = React__default.useCallback(() => {
69
+ onConfirm == null ? void 0 : onConfirm();
70
+ if (closeOnConfirm) {
71
+ closeDialog();
72
+ }
73
+ }, [onConfirm, closeOnConfirm, closeDialog]);
74
+ const ariaTitle = accessibilityTitle ?? (typeof title === "string" ? title : "Confirm notice");
75
+ return /* @__PURE__ */ jsx(
76
+ Dialog,
77
+ {
78
+ open,
79
+ onOpenChange: handleOpenChange,
80
+ title: ariaTitle,
81
+ description: accessibilityDescription,
82
+ showCloseButton,
83
+ closeOnOverlayClick,
84
+ closeOnEscapeKey,
85
+ maxWidth,
86
+ contentClassName: `taskon-confirm-notice-dialog ${contentClassName || ""}`.trim(),
87
+ children: /* @__PURE__ */ jsxs(
88
+ "div",
89
+ {
90
+ className: `taskon-confirm-notice taskon-confirm-notice--${type} ${className || ""}`.trim(),
91
+ children: [
92
+ noticeIcon && /* @__PURE__ */ jsx(
93
+ "img",
94
+ {
95
+ className: "taskon-confirm-notice-icon",
96
+ src: noticeIcon,
97
+ alt: ""
98
+ }
99
+ ),
100
+ title && /* @__PURE__ */ jsx("h3", { className: "taskon-confirm-notice-title", children: title }),
101
+ desc && /* @__PURE__ */ jsx("p", { className: "taskon-confirm-notice-desc", children: desc }),
102
+ children,
103
+ (actions || shouldRenderDefaultActions) && /* @__PURE__ */ jsx(
104
+ "div",
105
+ {
106
+ className: `taskon-confirm-notice-buttons ${col ? "taskon-confirm-notice-buttons--col" : ""}`.trim(),
107
+ children: actions || /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ cancelButton && /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ className: "taskon-confirm-notice-button taskon-confirm-notice-button--cancel",
113
+ onClick: handleCancel,
114
+ disabled: cancelDisabled,
115
+ children: cancelButton
116
+ }
117
+ ),
118
+ confirmButton && /* @__PURE__ */ jsx(
119
+ "button",
120
+ {
121
+ type: "button",
122
+ className: "taskon-confirm-notice-button taskon-confirm-notice-button--confirm",
123
+ onClick: handleConfirm,
124
+ disabled: confirmDisabled,
125
+ children: confirmButton
126
+ }
127
+ )
128
+ ] })
129
+ }
130
+ ),
131
+ footer
132
+ ]
133
+ }
134
+ )
135
+ }
136
+ );
137
+ }
138
+ const Textarea = forwardRef(
9
139
  function Textarea2({
10
140
  value: value2,
11
141
  onChange,
@@ -90,25 +220,6 @@ import '../EligibilityInfo.css';const Textarea = forwardRef(
90
220
  ] });
91
221
  }
92
222
  );
93
- const MOBILE_BREAKPOINT = 750;
94
- function useIsMobile() {
95
- const [isMobile, setIsMobile] = useState(() => {
96
- if (typeof window === "undefined") return false;
97
- return window.innerWidth <= MOBILE_BREAKPOINT;
98
- });
99
- const handleResize = useCallback(() => {
100
- setIsMobile(window.innerWidth <= MOBILE_BREAKPOINT);
101
- }, []);
102
- useEffect(() => {
103
- if (typeof window === "undefined") return;
104
- handleResize();
105
- window.addEventListener("resize", handleResize);
106
- return () => {
107
- window.removeEventListener("resize", handleResize);
108
- };
109
- }, [handleResize]);
110
- return isMobile;
111
- }
112
223
  function detectEthereumProvider() {
113
224
  if (typeof window === "undefined") return false;
114
225
  return Boolean(window.ethereum);
@@ -400,6 +511,253 @@ function useBindWallet({
400
511
  hasAdapter
401
512
  };
402
513
  }
514
+ function isNftRewardType(type) {
515
+ return type === RewardType.Nft || type === RewardType.BMintedNft || type === RewardType.Cap;
516
+ }
517
+ function buildFallbackLayer(reward) {
518
+ return {
519
+ winner_index: 0,
520
+ layer_no: 0,
521
+ reward: [reward]
522
+ };
523
+ }
524
+ function useNftClaimFlow(options) {
525
+ const {
526
+ campaignId,
527
+ targetType,
528
+ onClaimSuccess,
529
+ onClaimError,
530
+ onWalletError,
531
+ messages
532
+ } = options;
533
+ const { toast } = useToast();
534
+ const { chainMap } = useChainMap();
535
+ const [isClaimDialogOpen, setIsClaimDialogOpen] = useState(false);
536
+ const [isWalletDialogOpen, setIsWalletDialogOpen] = useState(false);
537
+ const [isPendingDialogOpen, setIsPendingDialogOpen] = useState(false);
538
+ const [pendingTxDialogState, setPendingTxDialogState] = useState(null);
539
+ const [pendingWalletClaimTarget, setPendingWalletClaimTarget] = useState(null);
540
+ const pendingDecisionResolverRef = useRef(null);
541
+ const resolvePendingDecision = useCallback((claimAgain) => {
542
+ const resolver = pendingDecisionResolverRef.current;
543
+ if (resolver) {
544
+ pendingDecisionResolverRef.current = null;
545
+ resolver(claimAgain);
546
+ }
547
+ }, []);
548
+ useEffect(() => {
549
+ return () => {
550
+ resolvePendingDecision(false);
551
+ };
552
+ }, [resolvePendingDecision]);
553
+ const {
554
+ status: claimStatus,
555
+ error: claimError,
556
+ txHash: claimTxHash,
557
+ claimingNft,
558
+ claimNft,
559
+ reset: resetClaim
560
+ } = useNftClaim({
561
+ onSuccess: (txHash, item) => {
562
+ void (onClaimSuccess == null ? void 0 : onClaimSuccess(txHash, item.sourceReward, item.sourceLayer));
563
+ },
564
+ onError: (error, item) => {
565
+ void (onClaimError == null ? void 0 : onClaimError(error, item.sourceReward, item.sourceLayer));
566
+ },
567
+ onPendingFound: async (pendingKey, txHash, chainName, item) => {
568
+ const rewardValue = item.reward_value;
569
+ const receiveAddress = typeof rewardValue.receiver_address === "string" ? rewardValue.receiver_address : void 0;
570
+ resolvePendingDecision(false);
571
+ setIsClaimDialogOpen(false);
572
+ setPendingTxDialogState({
573
+ pendingKey,
574
+ txHash,
575
+ chainName,
576
+ receiveAddress
577
+ });
578
+ setIsPendingDialogOpen(true);
579
+ return new Promise((resolve) => {
580
+ pendingDecisionResolverRef.current = resolve;
581
+ });
582
+ },
583
+ onNeedWalletDialog: () => {
584
+ setIsClaimDialogOpen(false);
585
+ setIsWalletDialogOpen(true);
586
+ }
587
+ });
588
+ const claimExplorerUrl = useMemo(() => {
589
+ if (!claimTxHash || !claimingNft) {
590
+ return void 0;
591
+ }
592
+ const chainInfo = chainMap[claimingNft.chainName.toLowerCase()];
593
+ if (!chainInfo) {
594
+ return void 0;
595
+ }
596
+ return getTxExplorerUrl(chainInfo, claimTxHash);
597
+ }, [claimTxHash, claimingNft, chainMap]);
598
+ const pendingExplorerUrl = useMemo(() => {
599
+ if (!(pendingTxDialogState == null ? void 0 : pendingTxDialogState.txHash)) {
600
+ return void 0;
601
+ }
602
+ const chainInfo = chainMap[pendingTxDialogState.chainName.toLowerCase()];
603
+ if (!chainInfo) {
604
+ return void 0;
605
+ }
606
+ return getTxExplorerUrl(chainInfo, pendingTxDialogState.txHash);
607
+ }, [pendingTxDialogState, chainMap]);
608
+ const claimNftReward = useCallback(
609
+ async (reward, layer) => {
610
+ if (!isNftRewardType(reward.reward_type)) {
611
+ return;
612
+ }
613
+ const targetLayer = layer ?? buildFallbackLayer(reward);
614
+ const target = {
615
+ campaign_id: campaignId,
616
+ target_type: targetType,
617
+ reward_type: reward.reward_type,
618
+ reward_value: reward.reward_value,
619
+ reward_id: reward.reward_id,
620
+ sourceReward: reward,
621
+ sourceLayer: targetLayer
622
+ };
623
+ setPendingWalletClaimTarget(target);
624
+ setIsClaimDialogOpen(true);
625
+ await claimNft(target);
626
+ },
627
+ [campaignId, targetType, claimNft]
628
+ );
629
+ const isSupportedNftRewardType = useCallback((rewardType) => {
630
+ return isNftRewardType(rewardType);
631
+ }, []);
632
+ const handleWalletConnect = useCallback(
633
+ async (address, provider) => {
634
+ const target = pendingWalletClaimTarget ?? (claimingNft == null ? void 0 : claimingNft.item);
635
+ if (!target) {
636
+ throw new Error("No pending NFT claim target found");
637
+ }
638
+ if (!provider || typeof provider.request !== "function") {
639
+ throw new Error("Invalid wallet provider");
640
+ }
641
+ const popupAdapter = createEthereumAdapterFromProvider(
642
+ provider,
643
+ {
644
+ address
645
+ }
646
+ );
647
+ setIsWalletDialogOpen(false);
648
+ setIsClaimDialogOpen(true);
649
+ await claimNft(target, popupAdapter);
650
+ },
651
+ [pendingWalletClaimTarget, claimingNft, claimNft]
652
+ );
653
+ const handleWalletError = useCallback(
654
+ (errorMessage) => {
655
+ onWalletError == null ? void 0 : onWalletError(errorMessage);
656
+ if (!onWalletError) {
657
+ toast.error(errorMessage);
658
+ }
659
+ },
660
+ [onWalletError, toast]
661
+ );
662
+ const handlePendingContinueWaiting = useCallback(() => {
663
+ setIsPendingDialogOpen(false);
664
+ setPendingTxDialogState(null);
665
+ setPendingWalletClaimTarget(null);
666
+ resetClaim();
667
+ resolvePendingDecision(false);
668
+ }, [resetClaim, resolvePendingDecision]);
669
+ const handlePendingClaimAgain = useCallback(() => {
670
+ setIsPendingDialogOpen(false);
671
+ setPendingTxDialogState(null);
672
+ setIsClaimDialogOpen(true);
673
+ resolvePendingDecision(true);
674
+ }, [resolvePendingDecision]);
675
+ const handleCloseClaimDialog = useCallback(() => {
676
+ setIsClaimDialogOpen(false);
677
+ setTimeout(() => {
678
+ resetClaim();
679
+ setPendingWalletClaimTarget(null);
680
+ }, 200);
681
+ }, [resetClaim]);
682
+ const handleRetryClaim = useCallback(() => {
683
+ if (!claimingNft) {
684
+ return;
685
+ }
686
+ resetClaim();
687
+ setPendingWalletClaimTarget(claimingNft.item);
688
+ void claimNft(claimingNft.item);
689
+ }, [claimingNft, resetClaim, claimNft]);
690
+ const isClaiming = useMemo(() => {
691
+ return ["checking", "connecting", "switching", "signing", "confirming", "pending"].includes(
692
+ claimStatus
693
+ );
694
+ }, [claimStatus]);
695
+ const dialogs = useMemo(() => {
696
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
697
+ /* @__PURE__ */ jsx(
698
+ ClaimNftDialog,
699
+ {
700
+ open: isClaimDialogOpen,
701
+ onClose: handleCloseClaimDialog,
702
+ status: claimStatus,
703
+ error: claimError,
704
+ txHash: claimTxHash,
705
+ claimingNft,
706
+ explorerUrl: claimExplorerUrl,
707
+ messages: messages == null ? void 0 : messages.claimDialog,
708
+ onRetry: handleRetryClaim
709
+ }
710
+ ),
711
+ /* @__PURE__ */ jsx(
712
+ PendingTxDialog,
713
+ {
714
+ open: isPendingDialogOpen,
715
+ onClose: handlePendingContinueWaiting,
716
+ onClaimAgain: handlePendingClaimAgain,
717
+ onContinueWaiting: handlePendingContinueWaiting,
718
+ txHash: pendingTxDialogState == null ? void 0 : pendingTxDialogState.txHash,
719
+ explorerUrl: pendingExplorerUrl,
720
+ receiveAddress: pendingTxDialogState == null ? void 0 : pendingTxDialogState.receiveAddress,
721
+ messages: messages == null ? void 0 : messages.pendingDialog
722
+ }
723
+ ),
724
+ /* @__PURE__ */ jsx(
725
+ BindWalletDialog,
726
+ {
727
+ open: isWalletDialogOpen,
728
+ onOpenChange: setIsWalletDialogOpen,
729
+ onConnect: handleWalletConnect,
730
+ onError: handleWalletError
731
+ }
732
+ )
733
+ ] });
734
+ }, [
735
+ isClaimDialogOpen,
736
+ handleCloseClaimDialog,
737
+ claimStatus,
738
+ claimError,
739
+ claimTxHash,
740
+ claimingNft,
741
+ claimExplorerUrl,
742
+ messages,
743
+ handleRetryClaim,
744
+ isPendingDialogOpen,
745
+ handlePendingContinueWaiting,
746
+ handlePendingClaimAgain,
747
+ pendingTxDialogState,
748
+ pendingExplorerUrl,
749
+ isWalletDialogOpen,
750
+ handleWalletConnect,
751
+ handleWalletError
752
+ ]);
753
+ return {
754
+ claimNftReward,
755
+ isSupportedNftRewardType,
756
+ dialogs,
757
+ claimingNft,
758
+ isClaiming
759
+ };
760
+ }
403
761
  async function copyToClipboard(text2) {
404
762
  try {
405
763
  if (navigator.clipboard && navigator.clipboard.writeText) {
@@ -424,6 +782,7 @@ function CopyableText({
424
782
  children,
425
783
  copyText
426
784
  }) {
785
+ const portalContainer = useTaskOnPortalContainer();
427
786
  const [copied, setCopied] = useState(false);
428
787
  const [open, setOpen] = useState(false);
429
788
  const closeTimerRef = useRef(null);
@@ -468,7 +827,7 @@ function CopyableText({
468
827
  children
469
828
  }
470
829
  ) }),
471
- /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(
830
+ /* @__PURE__ */ jsx(Portal, { container: portalContainer ?? void 0, children: /* @__PURE__ */ jsxs(
472
831
  Content2,
473
832
  {
474
833
  className: "taskon-title-express-popover-content",
@@ -1683,6 +2042,31 @@ function sanitizeHtml(html2) {
1683
2042
  initHooks();
1684
2043
  return purify.sanitize(html2, PURIFY_CONFIG);
1685
2044
  }
2045
+ function CardDescExpress({
2046
+ label,
2047
+ noMaxHeight = false,
2048
+ className
2049
+ }) {
2050
+ const sanitizedHtml = useMemo(() => {
2051
+ if (!label) return "";
2052
+ return sanitizeHtml(label);
2053
+ }, [label]);
2054
+ if (!label) {
2055
+ return null;
2056
+ }
2057
+ const rootClassName = [
2058
+ "taskon-card-desc-rich",
2059
+ noMaxHeight ? "taskon-card-desc-rich--no-max-height" : "",
2060
+ className || ""
2061
+ ].filter(Boolean).join(" ");
2062
+ return /* @__PURE__ */ jsx(
2063
+ "div",
2064
+ {
2065
+ className: rootClassName,
2066
+ dangerouslySetInnerHTML: { __html: sanitizedHtml }
2067
+ }
2068
+ );
2069
+ }
1686
2070
  const twitterIconUrl = new URL("data:image/svg+xml,%3csvg%20width='50'%20height='50'%20viewBox='0%200%2050%2050'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3ccircle%20cx='25'%20cy='25'%20r='25'%20fill='%2358AFFF'/%3e%3cpath%20d='M36%2018.9643C35.5742%2019.75%2034.8645%2020.5357%2033.871%2021.0595V21.5833C33.871%2028.131%2028.0516%2033.5%2020.9548%2033.5C18.5419%2033.5%2016.129%2032.8452%2014%2031.6667C14.4258%2031.6667%2014.7097%2031.7976%2015.1355%2031.6667C17.2645%2031.6667%2019.2516%2031.0119%2020.8129%2029.8333C18.8258%2029.8333%2017.1226%2028.6548%2016.5548%2026.9524C16.8387%2026.9524%2017.1226%2027.0833%2017.4065%2027.0833C17.8323%2027.0833%2018.2581%2027.0833%2018.5419%2026.9524C16.4129%2026.5595%2014.8516%2024.8571%2014.8516%2022.8929C15.4194%2023.1548%2016.129%2023.4167%2016.8387%2023.4167C15.7032%2022.631%2014.9935%2021.3214%2014.9935%2019.881C14.9935%2019.0952%2015.2774%2018.4405%2015.5613%2017.7857C17.8323%2020.4048%2021.2387%2021.9762%2024.929%2022.1071C24.7871%2021.8452%2024.7871%2021.4524%2024.7871%2021.1905C24.7871%2018.8333%2026.7742%2017%2029.329%2017C30.6065%2017%2031.7419%2017.5238%2032.5935%2018.3095C33.5871%2018.1786%2034.5806%2017.7857%2035.4323%2017.2619C35.1484%2018.1786%2034.4387%2019.0952%2033.4452%2019.619C34.2968%2019.4881%2035.2903%2019.2262%2036%2018.9643Z'%20fill='black'/%3e%3c/svg%3e", import.meta.url).href;
1687
2071
  const retweetIconUrl = new URL("data:image/svg+xml,%3csvg%20width='50'%20height='50'%20viewBox='0%200%2050%2050'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3ccircle%20cx='25'%20cy='25'%20r='25'%20fill='%2358AFFF'/%3e%3cpath%20d='M27.6047%2016C27.1048%2016%2026.605%2016.3999%2026.605%2017.2296V20.3986L26.0401%2020.4286C23.5018%2020.5001%2021.0516%2021.3751%2019.0423%2022.9279C16.5081%2024.9972%2014.4787%2028.4562%2014.0088%2032.9098C13.9745%2033.1614%2014.0408%2033.4165%2014.1934%2033.6195C14.3459%2033.8226%2014.5724%2033.9573%2014.8236%2033.9945C14.8666%2034.0015%2014.9105%2034.0015%2014.9536%2033.9945C15.1097%2033.9982%2015.2638%2033.9587%2015.3988%2033.8801C15.5338%2033.8016%2015.6444%2033.6872%2015.7183%2033.5496C18.1526%2030.0507%2023.356%2029.4958%2025.9102%2029.4958H26.61V32.8898C26.6025%2033.0314%2026.6239%2033.173%2026.6726%2033.3061C26.7214%2033.4392%2026.7967%2033.561%2026.8938%2033.6642C26.991%2033.7674%2027.108%2033.8499%2027.2379%2033.9067C27.3678%2033.9634%2027.5079%2033.9933%2027.6496%2033.9945C27.803%2033.9913%2027.9541%2033.9574%2028.0941%2033.8947C28.234%2033.832%2028.36%2033.7419%2028.4644%2033.6296L35.3173%2026.3868C36.197%2025.4721%2036.242%2024.5224%2035.3623%2023.6077L28.3294%2016.3149C28.2364%2016.2159%2028.1241%2016.1369%2027.9995%2016.0828C27.8749%2016.0287%2027.7405%2016.0005%2027.6047%2016Z'%20fill='black'/%3e%3c/svg%3e", import.meta.url).href;
1688
2072
  const discordIconUrl = new URL("data:image/svg+xml,%3csvg%20width='50'%20height='50'%20viewBox='0%200%2050%2050'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3ccircle%20cx='25'%20cy='25'%20r='25'%20fill='%236671FF'/%3e%3cpath%20d='M33.2655%2018.9902C30.7797%2017.2501%2028.4181%2017.1258%2028.0452%2017.1258L27.7966%2017.3744C30.6554%2018.1202%2032.2712%2019.4874%2032.3955%2019.6117C27.9209%2017.3744%2022.9492%2017.2501%2018.4746%2018.9902C17.8531%2019.2388%2017.4802%2019.4874%2017.3559%2019.4874C17.6045%2019.3631%2019.096%2017.9959%2022.2034%2017.2501L21.9548%2017.0015C21.9548%2017.0015%2019.4689%2016.8772%2016.7345%2018.8659C16.7345%2018.8659%2014%2023.4648%2014%2029.3066C14%2029.3066%2015.6158%2031.9168%2019.7175%2032.0411C19.7175%2032.0411%2020.339%2031.2953%2020.9605%2030.5496C18.8475%2029.9281%2017.8531%2028.5609%2017.7288%2028.4366L18.4746%2028.8095C20.7119%2029.6795%2023.0734%2030.9225%2027.4237%2030.1767C28.2938%2030.0524%2029.2881%2029.8038%2030.1582%2029.4309C30.7797%2029.058%2031.5254%2028.8095%2032.2712%2028.3123C32.1469%2028.4366%2031.1525%2029.8038%2028.9153%2030.4253C29.4124%2031.171%2030.1582%2031.9168%2030.1582%2031.9168C34.3842%2031.7925%2036%2029.1823%2036%2029.4309C36%2023.7134%2033.2655%2018.9902%2033.2655%2018.9902ZM21.5819%2027.4422C20.5876%2027.4422%2019.7175%2026.5722%2019.7175%2025.4535C19.7175%2024.3349%2020.5876%2023.4648%2021.5819%2023.4648C22.5763%2023.4648%2023.4463%2024.4592%2023.4463%2025.4535C23.4463%2026.5722%2022.5763%2027.4422%2021.5819%2027.4422ZM28.4181%2027.4422C27.4237%2027.4422%2026.5537%2026.5722%2026.5537%2025.4535C26.5537%2024.3349%2027.4237%2023.4648%2028.4181%2023.4648C29.5367%2023.4648%2030.4068%2024.4592%2030.4068%2025.4535C30.4068%2026.5722%2029.5367%2027.4422%2028.4181%2027.4422Z'%20fill='black'/%3e%3c/svg%3e", import.meta.url).href;
@@ -1717,14 +2101,14 @@ function FinishedIcon({ size }) {
1717
2101
  "path",
1718
2102
  {
1719
2103
  d: "M14.2218 3.22183C19.2021 1.15893 24.7979 1.15893 29.7782 3.22183C34.7585 5.28473 38.7153 9.24155 40.7782 14.2218C42.8411 19.2021 42.8411 24.7979 40.7782 29.7782C38.7153 34.7585 34.7585 38.7153 29.7782 40.7782C24.7979 42.8411 19.2021 42.8411 14.2218 40.7782C9.24155 38.7153 5.28473 34.7585 3.22183 29.7782C1.15893 24.7979 1.15893 19.2021 3.22183 14.2218C5.28473 9.24154 9.24154 5.28473 14.2218 3.22183Z",
1720
- fill: "#31FF90"
2104
+ fill: "var(--taskon-color-secondary)"
1721
2105
  }
1722
2106
  ),
1723
2107
  /* @__PURE__ */ jsx(
1724
2108
  "path",
1725
2109
  {
1726
2110
  d: "M15.4805 21.9999L19.5136 26.033L28.156 17.3906",
1727
- stroke: "black",
2111
+ stroke: "var(--taskon-color-text-on-primary)",
1728
2112
  strokeWidth: "4",
1729
2113
  strokeLinecap: "round",
1730
2114
  strokeLinejoin: "round"
@@ -1734,24 +2118,24 @@ function FinishedIcon({ size }) {
1734
2118
  }
1735
2119
  function DoneIcon({ size }) {
1736
2120
  return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 34 34", fill: "none", children: [
1737
- /* @__PURE__ */ jsx("circle", { cx: "17", cy: "17", r: "17", fill: "#E2FF70" }),
2121
+ /* @__PURE__ */ jsx("circle", { cx: "17", cy: "17", r: "17", fill: "var(--taskon-color-primary)" }),
1738
2122
  /* @__PURE__ */ jsx(
1739
2123
  "path",
1740
2124
  {
1741
2125
  d: "M4.74 21V19.62H6.49C6.91667 19.62 7.29 19.5367 7.61 19.37C7.93 19.1967 8.17667 18.9467 8.35 18.62C8.52333 18.2933 8.61 17.9067 8.61 17.46C8.61 17.0133 8.52 16.63 8.34 16.31C8.16667 15.99 7.92 15.7433 7.6 15.57C7.28667 15.3967 6.91667 15.31 6.49 15.31H4.69V13.94H6.51C7.04333 13.94 7.53333 14.0267 7.98 14.2C8.43333 14.3667 8.82667 14.61 9.16 14.93C9.5 15.2433 9.76 15.6167 9.94 16.05C10.1267 16.4767 10.22 16.95 10.22 17.47C10.22 17.9833 10.1267 18.4567 9.94 18.89C9.76 19.3233 9.50333 19.6967 9.17 20.01C8.83667 20.3233 8.44333 20.5667 7.99 20.74C7.54333 20.9133 7.05667 21 6.53 21H4.74ZM3.68 21V13.94H5.25V21H3.68ZM14.0891 21.12C13.5557 21.12 13.0624 21.0267 12.6091 20.84C12.1624 20.6533 11.7691 20.3933 11.4291 20.06C11.0891 19.7267 10.8257 19.34 10.6391 18.9C10.4524 18.4533 10.3591 17.9733 10.3591 17.46C10.3591 16.94 10.4524 16.46 10.6391 16.02C10.8257 15.58 11.0857 15.1967 11.4191 14.87C11.7524 14.5367 12.1424 14.28 12.5891 14.1C13.0424 13.9133 13.5357 13.82 14.0691 13.82C14.5957 13.82 15.0824 13.9133 15.5291 14.1C15.9824 14.28 16.3757 14.5367 16.7091 14.87C17.0491 15.1967 17.3124 15.5833 17.4991 16.03C17.6857 16.47 17.7791 16.95 17.7791 17.47C17.7791 17.9833 17.6857 18.4633 17.4991 18.91C17.3124 19.35 17.0524 19.7367 16.7191 20.07C16.3857 20.3967 15.9924 20.6533 15.5391 20.84C15.0924 21.0267 14.6091 21.12 14.0891 21.12ZM14.0691 19.69C14.4891 19.69 14.8557 19.5967 15.1691 19.41C15.4891 19.2233 15.7357 18.9633 15.9091 18.63C16.0824 18.29 16.1691 17.9 16.1691 17.46C16.1691 17.1267 16.1191 16.8267 16.0191 16.56C15.9191 16.2867 15.7757 16.0533 15.5891 15.86C15.4024 15.66 15.1791 15.51 14.9191 15.41C14.6657 15.3033 14.3824 15.25 14.0691 15.25C13.6491 15.25 13.2791 15.3433 12.9591 15.53C12.6457 15.71 12.4024 15.9667 12.2291 16.3C12.0557 16.6267 11.9691 17.0133 11.9691 17.46C11.9691 17.7933 12.0191 18.0967 12.1191 18.37C12.2191 18.6433 12.3591 18.88 12.5391 19.08C12.7257 19.2733 12.9491 19.4233 13.2091 19.53C13.4691 19.6367 13.7557 19.69 14.0691 19.69ZM18.2757 21V13.94H19.3757L19.8457 15.35V21H18.2757ZM23.1457 21L18.9857 15.67L19.3757 13.94L23.5357 19.27L23.1457 21ZM23.1457 21L22.7257 19.59V13.94H24.2957V21H23.1457ZM25.1585 21V13.94H26.7285V21H25.1585ZM26.3585 21V19.64H30.2285V21H26.3585ZM26.3585 18.05V16.74H29.8785V18.05H26.3585ZM26.3585 15.29V13.94H30.1785V15.29H26.3585Z",
1742
- fill: "black"
2126
+ fill: "var(--taskon-color-text-on-primary)"
1743
2127
  }
1744
2128
  )
1745
2129
  ] });
1746
2130
  }
1747
2131
  function DefaultIcon({ size }) {
1748
2132
  return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 34 34", fill: "none", style: { borderRadius: "50%" }, children: [
1749
- /* @__PURE__ */ jsx("circle", { cx: "17", cy: "17", r: "17", fill: "#3B3F42" }),
2133
+ /* @__PURE__ */ jsx("circle", { cx: "17", cy: "17", r: "17", fill: "var(--taskon-color-bg-inset)" }),
1750
2134
  /* @__PURE__ */ jsx(
1751
2135
  "path",
1752
2136
  {
1753
2137
  d: "M17 10V17L21 19",
1754
- stroke: "#9CA3AF",
2138
+ stroke: "var(--taskon-color-text-tertiary)",
1755
2139
  strokeWidth: "2",
1756
2140
  strokeLinecap: "round",
1757
2141
  strokeLinejoin: "round"
@@ -2053,6 +2437,37 @@ function getSnsTypeFromIdentity(identityType) {
2053
2437
  return void 0;
2054
2438
  }
2055
2439
  }
2440
+ const VERIFY_FAILED_TITLE = "Verify Failed";
2441
+ const ELIGIBILITY_FAILED_TITLE = "Not Eligible";
2442
+ const DEFAULT_VERIFY_FAILED_MESSAGE = "Oops! You did not pass this task.";
2443
+ const DEFAULT_ELIGIBILITY_FAILED_MESSAGE = "You do not meet the campaign eligibility requirements.";
2444
+ const CAMPAIGN_INELIGIBILITY_CODE = "CAMPAIGN_INELIGIBILITY";
2445
+ function extractErrorMessage(error) {
2446
+ if (error instanceof Error) {
2447
+ return error.message;
2448
+ }
2449
+ if (typeof error === "string") {
2450
+ return error;
2451
+ }
2452
+ if (error && typeof error === "object" && "message" in error) {
2453
+ const maybeMessage = error.message;
2454
+ if (typeof maybeMessage === "string") {
2455
+ return maybeMessage;
2456
+ }
2457
+ }
2458
+ return "Unknown error";
2459
+ }
2460
+ function buildInviteNotReachMessage(errorData) {
2461
+ if (!errorData || typeof errorData !== "object") {
2462
+ return null;
2463
+ }
2464
+ const data2 = errorData;
2465
+ if (typeof data2.invited !== "number" || typeof data2.min_required !== "number") {
2466
+ return null;
2467
+ }
2468
+ const remaining = Math.max(0, data2.min_required - data2.invited);
2469
+ return `You have invited ${data2.invited} users, but need ${remaining} more to complete this task.`;
2470
+ }
2056
2471
  function useTaskSubmit({
2057
2472
  campaignId,
2058
2473
  taskId,
@@ -2062,6 +2477,8 @@ function useTaskSubmit({
2062
2477
  taskValue,
2063
2478
  onSuccess,
2064
2479
  onError,
2480
+ onCampaignIneligibility,
2481
+ onVerifyFailedNotice,
2065
2482
  onNeedWalletDialog
2066
2483
  }) {
2067
2484
  const context = useContext(TaskOnContext);
@@ -2094,40 +2511,49 @@ function useTaskSubmit({
2094
2511
  },
2095
2512
  [userInfo]
2096
2513
  );
2097
- const showVerifyFailedToast = useCallback(
2514
+ const buildVerifyFailedMessage = useCallback(
2098
2515
  (taskPlatform) => {
2099
2516
  if (taskPlatform === "Twitter") {
2100
2517
  const name2 = getSnsUserName("Twitter");
2101
- toast.error(
2102
- `Oops! Verification Failed!
2103
- This is usually due to X(Twitter) delays.
2104
- Please try again in 30 seconds and make sure you used your linked X account${name2 ? `, ${name2}` : ""}.`,
2105
- 1e4
2106
- );
2107
- } else if (taskPlatform === "Discord") {
2518
+ return `This is usually due to X(Twitter) delays.
2519
+ Please try again in 30 seconds and make sure you used your linked X account${name2 ? `, ${name2}` : ""}.`;
2520
+ }
2521
+ if (taskPlatform === "Discord") {
2108
2522
  const name2 = getSnsUserName("Discord");
2109
- toast.error(
2110
- `Oops! Verification Failed!
2111
- This is usually due to Discord delays.
2112
- Please try again in 30 seconds and make sure you used your linked Discord account${name2 ? `, ${name2}` : ""}.`,
2113
- 1e4
2114
- );
2115
- } else if (taskPlatform === "Telegram") {
2523
+ return `This is usually due to Discord delays.
2524
+ Please try again in 30 seconds and make sure you used your linked Discord account${name2 ? `, ${name2}` : ""}.`;
2525
+ }
2526
+ if (taskPlatform === "Telegram") {
2116
2527
  const name2 = getSnsUserName("Telegram");
2117
- toast.error(
2118
- `Oops! Verification Failed!
2119
- This is usually due to Telegram delays.
2120
- Please try again in 30 seconds and make sure you used your linked Telegram account${name2 ? `, ${name2}` : ""}.`,
2121
- 1e4
2122
- );
2123
- } else {
2124
- toast.error("Oops! You did not pass this task.");
2528
+ return `This is usually due to Telegram delays.
2529
+ Please try again in 30 seconds and make sure you used your linked Telegram account${name2 ? `, ${name2}` : ""}.`;
2530
+ }
2531
+ return DEFAULT_VERIFY_FAILED_MESSAGE;
2532
+ },
2533
+ [getSnsUserName]
2534
+ );
2535
+ const notifyVerifyFailed = useCallback(
2536
+ (notice) => {
2537
+ if (onVerifyFailedNotice) {
2538
+ onVerifyFailedNotice(notice);
2539
+ return;
2125
2540
  }
2541
+ if (notice.source === "verify_failed") {
2542
+ if (notice.message === DEFAULT_VERIFY_FAILED_MESSAGE) {
2543
+ toast.error(DEFAULT_VERIFY_FAILED_MESSAGE);
2544
+ return;
2545
+ }
2546
+ toast.error(`Oops! Verification Failed!
2547
+ ${notice.message}`, 1e4);
2548
+ return;
2549
+ }
2550
+ toast.error(notice.message);
2126
2551
  },
2127
- [getSnsUserName, toast]
2552
+ [onVerifyFailedNotice, toast]
2128
2553
  );
2129
2554
  const {
2130
2555
  bindIfNeed: bindSns,
2556
+ reAuth,
2131
2557
  isWaitingAuth,
2132
2558
  isBound: isSnsBound
2133
2559
  } = useBindSocialAccount({
@@ -2135,6 +2561,10 @@ Please try again in 30 seconds and make sure you used your linked Telegram accou
2135
2561
  onSuccess: async () => {
2136
2562
  await doSubmit();
2137
2563
  },
2564
+ // Re-auth success: resubmit task with OAuth token (same as Vue flow)
2565
+ onAuthSuccess: async (token) => {
2566
+ await doSubmit(token);
2567
+ },
2138
2568
  onFailed: (err) => {
2139
2569
  setError(err);
2140
2570
  onError == null ? void 0 : onError(err);
@@ -2163,6 +2593,13 @@ Please try again in 30 seconds and make sure you used your linked Telegram accou
2163
2593
  if (!questApi) {
2164
2594
  const err = "Quest API not initialized";
2165
2595
  setError(err);
2596
+ notifyVerifyFailed({
2597
+ title: VERIFY_FAILED_TITLE,
2598
+ message: err,
2599
+ source: "api_error",
2600
+ platform,
2601
+ rawError: err
2602
+ });
2166
2603
  onError == null ? void 0 : onError(err);
2167
2604
  return;
2168
2605
  }
@@ -2177,16 +2614,64 @@ Please try again in 30 seconds and make sure you used your linked Telegram accou
2177
2614
  if ((_a = result.success_tasks) == null ? void 0 : _a.length) {
2178
2615
  onSuccess == null ? void 0 : onSuccess(result);
2179
2616
  } else {
2180
- const errorMsg = "Task verification failed";
2181
- setError(errorMsg);
2182
- showVerifyFailedToast(platform);
2183
- onError == null ? void 0 : onError(errorMsg);
2617
+ const verifyFailedMessage = buildVerifyFailedMessage(platform);
2618
+ setError(verifyFailedMessage);
2619
+ notifyVerifyFailed({
2620
+ title: VERIFY_FAILED_TITLE,
2621
+ message: verifyFailedMessage,
2622
+ source: "verify_failed",
2623
+ platform
2624
+ });
2625
+ onError == null ? void 0 : onError(verifyFailedMessage);
2184
2626
  }
2185
2627
  } catch (e) {
2186
- const err = e instanceof Error ? e.message : "Unknown error";
2187
- setError(err);
2188
- toast.error(err);
2189
- onError == null ? void 0 : onError(err);
2628
+ const apiError = e instanceof ApiError ? e : null;
2629
+ const errorCode = apiError == null ? void 0 : apiError.code;
2630
+ const rawErrorMessage = extractErrorMessage(e);
2631
+ if (errorCode && isUnauthorizedError(errorCode) && needsSnsBinding) {
2632
+ await reAuth();
2633
+ return;
2634
+ }
2635
+ if (errorCode === CAMPAIGN_INELIGIBILITY_CODE) {
2636
+ const eligibilityMessage = rawErrorMessage || DEFAULT_ELIGIBILITY_FAILED_MESSAGE;
2637
+ setError(eligibilityMessage);
2638
+ if (onCampaignIneligibility) {
2639
+ await onCampaignIneligibility();
2640
+ } else {
2641
+ notifyVerifyFailed({
2642
+ title: ELIGIBILITY_FAILED_TITLE,
2643
+ message: eligibilityMessage,
2644
+ source: "api_error",
2645
+ platform,
2646
+ rawError: rawErrorMessage
2647
+ });
2648
+ }
2649
+ onError == null ? void 0 : onError(eligibilityMessage);
2650
+ return;
2651
+ }
2652
+ if (errorCode === ErrorCode.INVITE_JOIN_IS_NOT_REACH) {
2653
+ const inviteNotReachMessage = buildInviteNotReachMessage(apiError == null ? void 0 : apiError.data);
2654
+ const inviteErrorMessage = inviteNotReachMessage || rawErrorMessage;
2655
+ setError(inviteErrorMessage);
2656
+ notifyVerifyFailed({
2657
+ title: VERIFY_FAILED_TITLE,
2658
+ message: inviteErrorMessage,
2659
+ source: "verify_failed",
2660
+ platform,
2661
+ rawError: rawErrorMessage
2662
+ });
2663
+ onError == null ? void 0 : onError(inviteErrorMessage);
2664
+ return;
2665
+ }
2666
+ setError(rawErrorMessage);
2667
+ notifyVerifyFailed({
2668
+ title: VERIFY_FAILED_TITLE,
2669
+ message: rawErrorMessage,
2670
+ source: "api_error",
2671
+ platform,
2672
+ rawError: rawErrorMessage
2673
+ });
2674
+ onError == null ? void 0 : onError(rawErrorMessage);
2190
2675
  } finally {
2191
2676
  setIsSubmitting(false);
2192
2677
  }
@@ -2198,8 +2683,11 @@ Please try again in 30 seconds and make sure you used your linked Telegram accou
2198
2683
  platform,
2199
2684
  onSuccess,
2200
2685
  onError,
2201
- showVerifyFailedToast,
2202
- toast
2686
+ onCampaignIneligibility,
2687
+ buildVerifyFailedMessage,
2688
+ notifyVerifyFailed,
2689
+ needsSnsBinding,
2690
+ reAuth
2203
2691
  ]
2204
2692
  );
2205
2693
  const submit = useCallback(
@@ -2331,6 +2819,33 @@ function useLinkTaskTracker(taskId, isLinkTask) {
2331
2819
  needClickLink
2332
2820
  };
2333
2821
  }
2822
+ const DEFAULT_TITLE = "Verify Failed";
2823
+ const DEFAULT_MESSAGE = "Oops! You did not pass this task.";
2824
+ function TaskVerifyFailedDialog({
2825
+ open,
2826
+ title = DEFAULT_TITLE,
2827
+ message = DEFAULT_MESSAGE,
2828
+ onClose
2829
+ }) {
2830
+ return /* @__PURE__ */ jsx(
2831
+ ConfirmNoticeDialog,
2832
+ {
2833
+ open,
2834
+ onOpenChange: (nextOpen) => {
2835
+ if (!nextOpen) {
2836
+ onClose();
2837
+ }
2838
+ },
2839
+ type: "warn",
2840
+ title,
2841
+ showCloseButton: true,
2842
+ contentClassName: "taskon-task-verify-failed-dialog",
2843
+ accessibilityTitle: title,
2844
+ accessibilityDescription: message,
2845
+ children: /* @__PURE__ */ jsx("div", { className: "taskon-task-verify-failed-dialog-message", children: message })
2846
+ }
2847
+ );
2848
+ }
2334
2849
  function SpinnerIcon() {
2335
2850
  return /* @__PURE__ */ jsx(
2336
2851
  "svg",
@@ -2356,118 +2871,150 @@ function SpinnerIcon() {
2356
2871
  }
2357
2872
  );
2358
2873
  }
2359
- const VerifyButton = forwardRef(function VerifyButton2({
2360
- campaignId,
2361
- taskId,
2362
- snsType,
2363
- chainType,
2364
- platform,
2365
- label,
2366
- isLoading: externalLoading = false,
2367
- disabled = false,
2368
- needLink = false,
2369
- isLinkClicked: isLinkClicked2 = false,
2370
- onBeforeVerify,
2371
- onSuccess,
2372
- onError,
2373
- className
2374
- }, ref) {
2375
- const { toast } = useToast();
2376
- const { submit, isSubmitting, clearError, isSnsBindingRequired, isWalletBindingRequired } = useTaskSubmit({
2874
+ const VerifyButton = forwardRef(
2875
+ function VerifyButton2({
2377
2876
  campaignId,
2378
2877
  taskId,
2379
2878
  snsType,
2380
2879
  chainType,
2381
2880
  platform,
2881
+ label,
2882
+ isLoading: externalLoading = false,
2883
+ disabled = false,
2884
+ needLink = false,
2885
+ isLinkClicked: isLinkClicked2 = false,
2886
+ onBeforeVerify,
2382
2887
  onSuccess,
2383
- onError
2384
- });
2385
- const [counting, setCounting] = useState(false);
2386
- const [percent, setPercent] = useState(0);
2387
- const intervalRef = useRef(null);
2388
- const [isCheckingEligibility, setIsCheckingEligibility] = useState(false);
2389
- const countDown = useCallback((durationSecond) => {
2390
- if (intervalRef.current) {
2391
- clearInterval(intervalRef.current);
2392
- }
2393
- setCounting(true);
2394
- setPercent(0);
2395
- let remainingTime = durationSecond;
2396
- intervalRef.current = setInterval(() => {
2397
- remainingTime -= 1;
2398
- const newPercent = (durationSecond - remainingTime) / durationSecond * 100;
2399
- setPercent(newPercent);
2400
- if (remainingTime < 0) {
2401
- setCounting(false);
2402
- if (intervalRef.current) {
2403
- clearInterval(intervalRef.current);
2404
- intervalRef.current = null;
2405
- }
2888
+ onError,
2889
+ className
2890
+ }, ref) {
2891
+ const { toast } = useToast();
2892
+ const [verifyFailedNotice, setVerifyFailedNotice] = useState(null);
2893
+ const {
2894
+ submit,
2895
+ isSubmitting,
2896
+ clearError,
2897
+ isSnsBindingRequired,
2898
+ isWalletBindingRequired
2899
+ } = useTaskSubmit({
2900
+ campaignId,
2901
+ taskId,
2902
+ snsType,
2903
+ chainType,
2904
+ platform,
2905
+ onSuccess,
2906
+ onError,
2907
+ // 当后端返回 CAMPAIGN_INELIGIBILITY 时,复用现有 eligibility 检查逻辑
2908
+ onCampaignIneligibility: onBeforeVerify,
2909
+ onVerifyFailedNotice: (notice) => {
2910
+ setVerifyFailedNotice(notice);
2406
2911
  }
2407
- }, 1e3);
2408
- }, []);
2409
- useEffect(() => {
2410
- return () => {
2912
+ });
2913
+ const [counting, setCounting] = useState(false);
2914
+ const [percent, setPercent] = useState(0);
2915
+ const intervalRef = useRef(null);
2916
+ const [isCheckingEligibility, setIsCheckingEligibility] = useState(false);
2917
+ const countDown = useCallback((durationSecond) => {
2411
2918
  if (intervalRef.current) {
2412
2919
  clearInterval(intervalRef.current);
2413
2920
  }
2414
- };
2415
- }, []);
2416
- useImperativeHandle(ref, () => ({
2417
- countDown
2418
- }), [countDown]);
2419
- const handleClick = async () => {
2420
- if (needLink && !isLinkClicked2) {
2421
- toast.error("Please view the link before verifying.");
2422
- return;
2423
- }
2424
- if (onBeforeVerify) {
2425
- setIsCheckingEligibility(true);
2426
- try {
2427
- const canProceed = await onBeforeVerify();
2428
- if (!canProceed) {
2429
- return;
2921
+ setCounting(true);
2922
+ setPercent(0);
2923
+ let remainingTime = durationSecond;
2924
+ intervalRef.current = setInterval(() => {
2925
+ remainingTime -= 1;
2926
+ const newPercent = (durationSecond - remainingTime) / durationSecond * 100;
2927
+ setPercent(newPercent);
2928
+ if (remainingTime < 0) {
2929
+ setCounting(false);
2930
+ if (intervalRef.current) {
2931
+ clearInterval(intervalRef.current);
2932
+ intervalRef.current = null;
2933
+ }
2430
2934
  }
2431
- } finally {
2432
- setIsCheckingEligibility(false);
2433
- }
2434
- }
2435
- clearError();
2436
- await submit();
2437
- };
2438
- const isLoading = externalLoading || isSubmitting || isCheckingEligibility;
2439
- const isDisabled = disabled || isLoading || counting;
2440
- const buttonLabel = React__default.useMemo(() => {
2441
- if (label) return label;
2442
- if (isSnsBindingRequired || isWalletBindingRequired) {
2443
- return "Link & Verify";
2444
- }
2445
- return "Verify";
2446
- }, [label, isSnsBindingRequired, isWalletBindingRequired]);
2447
- return /* @__PURE__ */ jsxs("div", { className: "taskon-verify-btn-wrap", children: [
2448
- /* @__PURE__ */ jsxs(
2449
- "button",
2450
- {
2451
- type: "button",
2452
- className: `taskon-verify-btn ${isLoading ? "taskon-verify-btn--loading" : ""} ${className || ""}`,
2453
- onClick: handleClick,
2454
- disabled: isDisabled,
2455
- "aria-busy": isLoading,
2456
- children: [
2457
- isLoading && /* @__PURE__ */ jsx(SpinnerIcon, {}),
2458
- /* @__PURE__ */ jsx("span", { children: isLoading ? "Verifying..." : buttonLabel })
2459
- ]
2935
+ }, 1e3);
2936
+ }, []);
2937
+ useEffect(() => {
2938
+ return () => {
2939
+ if (intervalRef.current) {
2940
+ clearInterval(intervalRef.current);
2941
+ }
2942
+ };
2943
+ }, []);
2944
+ useImperativeHandle(
2945
+ ref,
2946
+ () => ({
2947
+ countDown
2948
+ }),
2949
+ [countDown]
2950
+ );
2951
+ const handleClick = async () => {
2952
+ if (needLink && !isLinkClicked2) {
2953
+ toast.error("Please view the link before verifying.");
2954
+ return;
2460
2955
  }
2461
- ),
2462
- counting && /* @__PURE__ */ jsx("div", { className: "taskon-verify-btn-progress", children: /* @__PURE__ */ jsx(
2463
- "div",
2464
- {
2465
- className: "taskon-verify-btn-progress-bar",
2466
- style: { width: `${percent}%` }
2956
+ if (onBeforeVerify) {
2957
+ setIsCheckingEligibility(true);
2958
+ try {
2959
+ const canProceed = await onBeforeVerify();
2960
+ if (!canProceed) {
2961
+ return;
2962
+ }
2963
+ } finally {
2964
+ setIsCheckingEligibility(false);
2965
+ }
2467
2966
  }
2468
- ) })
2469
- ] });
2470
- });
2967
+ clearError();
2968
+ await submit();
2969
+ };
2970
+ const closeVerifyFailedDialog = useCallback(() => {
2971
+ setVerifyFailedNotice(null);
2972
+ }, []);
2973
+ const isLoading = externalLoading || isSubmitting || isCheckingEligibility;
2974
+ const isDisabled = disabled || isLoading || counting;
2975
+ const buttonLabel = React__default.useMemo(() => {
2976
+ if (label) return label;
2977
+ if (isSnsBindingRequired || isWalletBindingRequired) {
2978
+ return "Link & Verify";
2979
+ }
2980
+ return "Verify";
2981
+ }, [label, isSnsBindingRequired, isWalletBindingRequired]);
2982
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2983
+ /* @__PURE__ */ jsxs("div", { className: "taskon-verify-btn-wrap", children: [
2984
+ /* @__PURE__ */ jsxs(
2985
+ "button",
2986
+ {
2987
+ type: "button",
2988
+ className: `taskon-verify-btn ${isLoading ? "taskon-verify-btn--loading" : ""} ${className || ""}`,
2989
+ onClick: handleClick,
2990
+ disabled: isDisabled,
2991
+ "aria-busy": isLoading,
2992
+ children: [
2993
+ isLoading && /* @__PURE__ */ jsx(SpinnerIcon, {}),
2994
+ /* @__PURE__ */ jsx("span", { children: isLoading ? "Verifying..." : buttonLabel })
2995
+ ]
2996
+ }
2997
+ ),
2998
+ counting && /* @__PURE__ */ jsx("div", { className: "taskon-verify-btn-progress", children: /* @__PURE__ */ jsx(
2999
+ "div",
3000
+ {
3001
+ className: "taskon-verify-btn-progress-bar",
3002
+ style: { width: `${percent}%` }
3003
+ }
3004
+ ) })
3005
+ ] }),
3006
+ /* @__PURE__ */ jsx(
3007
+ TaskVerifyFailedDialog,
3008
+ {
3009
+ open: Boolean(verifyFailedNotice),
3010
+ title: verifyFailedNotice == null ? void 0 : verifyFailedNotice.title,
3011
+ message: verifyFailedNotice == null ? void 0 : verifyFailedNotice.message,
3012
+ onClose: closeVerifyFailedDialog
3013
+ }
3014
+ )
3015
+ ] });
3016
+ }
3017
+ );
2471
3018
  function ExpandableContent({
2472
3019
  children,
2473
3020
  maxHeight = 21,
@@ -2594,21 +3141,64 @@ function TimeRange({
2594
3141
  /* @__PURE__ */ jsx("span", { className: "taskon-task-time-range-value", children: timeRangeText })
2595
3142
  ] });
2596
3143
  }
3144
+ const VUE_RATE_LIMIT_TWITTER_TASK_IDS = /* @__PURE__ */ new Set([
3145
+ "FollowTwitter",
3146
+ "RetweetTwitter",
3147
+ "QuoteTweetAndTag",
3148
+ "QuoteTweetAndHashTag",
3149
+ "ReplyTweet",
3150
+ "ReplyTweetAndHashTag",
3151
+ "PostTweetAndHashTag",
3152
+ "PostTweetAndTag",
3153
+ "PostTweetAndContent"
3154
+ ]);
3155
+ const VUE_DEDICATED_TASK_IDS_WITHOUT_VERIFY_COUNTDOWN = /* @__PURE__ */ new Set([
3156
+ "LikeATweet",
3157
+ "PostTweet",
3158
+ "JoinDiscord",
3159
+ "JoinTelegram",
3160
+ "LinkTask",
3161
+ "TokenBalance",
3162
+ "NftHolder",
3163
+ "BindEmail",
3164
+ "JoinCommunity",
3165
+ "OuterAPI",
3166
+ "Youtube",
3167
+ "QuizChoose",
3168
+ "SurveyChoose",
3169
+ "AMA",
3170
+ "MultiQuizzes",
3171
+ "LearnAndQuiz",
3172
+ "Poh",
3173
+ "SwapVolume",
3174
+ "TaskonPohScore",
3175
+ "ChaingeSwap",
3176
+ "ChaingeBridge"
3177
+ ]);
3178
+ function shouldEnableVueVerifyFailCooldown(templateId, hasSnsBinding, hasChainBinding) {
3179
+ if (VUE_RATE_LIMIT_TWITTER_TASK_IDS.has(templateId)) {
3180
+ return true;
3181
+ }
3182
+ if (VUE_DEDICATED_TASK_IDS_WITHOUT_VERIFY_COUNTDOWN.has(templateId)) {
3183
+ return false;
3184
+ }
3185
+ return hasSnsBinding || hasChainBinding;
3186
+ }
2597
3187
  function TaskContent({
2598
- params,
2599
- isRichText
3188
+ params
2600
3189
  }) {
2601
3190
  const { desc, desc_fields } = params;
2602
3191
  const hasContent = desc || desc_fields && desc_fields.length > 0;
2603
3192
  if (!hasContent) return null;
2604
3193
  return /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsxs("div", { className: "taskon-task-item-content", children: [
2605
- desc && /* @__PURE__ */ jsx("div", { className: "taskon-task-item-desc", children: isRichText ? /* @__PURE__ */ jsx(
2606
- "div",
3194
+ desc && /* @__PURE__ */ jsx(
3195
+ CardDescExpress,
2607
3196
  {
2608
- className: "taskon-task-item-rich-text",
2609
- dangerouslySetInnerHTML: { __html: sanitizeHtml(desc) }
3197
+ label: desc,
3198
+ noMaxHeight: true,
3199
+ className: "taskon-task-item-desc"
2610
3200
  }
2611
- ) : desc }),
3201
+ ),
2612
3202
  desc_fields && desc_fields.length > 0 && /* @__PURE__ */ jsx("div", { className: "taskon-task-item-fields", children: desc_fields.filter((field) => field.display !== "dialog").map((field, index2) => /* @__PURE__ */ jsxs("div", { className: "taskon-task-item-field", children: [
2613
3203
  /* @__PURE__ */ jsx("span", { className: "taskon-task-item-field-key", children: field.key }),
2614
3204
  field.link ? /* @__PURE__ */ jsx(
@@ -2669,6 +3259,13 @@ function TemplateTask({
2669
3259
  [params.user_identity_type]
2670
3260
  );
2671
3261
  const chainType = useChainType(params.network);
3262
+ const shouldStartVerifyFailCooldown = useMemo(() => {
3263
+ return shouldEnableVueVerifyFailCooldown(
3264
+ task.template_id,
3265
+ Boolean(snsType),
3266
+ Boolean(chainType)
3267
+ );
3268
+ }, [task.template_id, snsType, chainType]);
2672
3269
  const isLinkTask = Boolean(params.is_link_task);
2673
3270
  const {
2674
3271
  hasClicked: isLinkClicked2,
@@ -2683,13 +3280,12 @@ function TemplateTask({
2683
3280
  }
2684
3281
  prevCurrentDoneRef.current = isCurrentDone;
2685
3282
  }, [isCurrentDone, isLinkTask, resetLinkClicked]);
2686
- const isRichText = params.is_rich_text || task.is_rich_text;
2687
3283
  const titleLinkUrl = useMemo(() => {
2688
- if (params.link && isRichText) {
3284
+ if (params.link) {
2689
3285
  return params.link;
2690
3286
  }
2691
3287
  return void 0;
2692
- }, [params.link, isRichText]);
3288
+ }, [params.link]);
2693
3289
  const taskTitle = useMemo(() => {
2694
3290
  if (params.custom_title) return params.custom_title;
2695
3291
  if (params.title_express) {
@@ -2721,8 +3317,10 @@ function TemplateTask({
2721
3317
  var _a2;
2722
3318
  setIsLoading(false);
2723
3319
  console.error("Task verification failed:", error);
2724
- (_a2 = verifyButtonRef.current) == null ? void 0 : _a2.countDown(5);
2725
- }, []);
3320
+ if (shouldStartVerifyFailCooldown) {
3321
+ (_a2 = verifyButtonRef.current) == null ? void 0 : _a2.countDown(5);
3322
+ }
3323
+ }, [shouldStartVerifyFailCooldown]);
2726
3324
  const handleCooldownComplete = useCallback(() => {
2727
3325
  setCoolDownEnd(null);
2728
3326
  onCooldownComplete == null ? void 0 : onCooldownComplete();
@@ -2801,7 +3399,7 @@ function TemplateTask({
2801
3399
  end: params.end_time * 1e3
2802
3400
  }
2803
3401
  ),
2804
- /* @__PURE__ */ jsx(TaskContent, { params, isRichText })
3402
+ /* @__PURE__ */ jsx(TaskContent, { params })
2805
3403
  ] })
2806
3404
  }
2807
3405
  );
@@ -3074,13 +3672,7 @@ function PowTask({
3074
3672
  pointsValue: (_a = task.points) == null ? void 0 : _a.amount,
3075
3673
  pointsTip: "The point rewards will be distributed to you after the quest ends if your task is verified as completed.",
3076
3674
  children: /* @__PURE__ */ jsxs(ExpandableContent, { children: [
3077
- params.desc && /* @__PURE__ */ jsx(
3078
- "div",
3079
- {
3080
- className: "taskon-pow-desc",
3081
- dangerouslySetInnerHTML: { __html: sanitizeHtml(params.desc) }
3082
- }
3083
- ),
3675
+ params.desc && /* @__PURE__ */ jsx(CardDescExpress, { label: params.desc, noMaxHeight: true }),
3084
3676
  showSubmitArea && /* @__PURE__ */ jsxs("div", { className: "taskon-pow-input-area", children: [
3085
3677
  params.type === PowTaskType.URL && /* @__PURE__ */ jsxs("div", { className: "taskon-pow-url-row", children: [
3086
3678
  /* @__PURE__ */ jsx(
@@ -3230,34 +3822,78 @@ function ContractInteractiveTask({
3230
3822
  return 0;
3231
3823
  }, [userStatus == null ? void 0 : userStatus.next_time]);
3232
3824
  const chainType = useChainType(params.chain);
3233
- const { submit, isSubmitting } = useTaskSubmit({
3234
- campaignId,
3235
- taskId: task.id,
3236
- chainType,
3237
- // 钱包绑定:从 params.chain 获取
3238
- onSuccess: (result) => {
3239
- onCompleted == null ? void 0 : onCompleted(task.id, result);
3240
- },
3241
- onError: (error) => {
3242
- console.error("Verify failed:", error);
3825
+ const taskOnContext = useContext(TaskOnContext);
3826
+ const client = (taskOnContext == null ? void 0 : taskOnContext.client) ?? null;
3827
+ const questApi = useMemo(() => {
3828
+ if (!client) {
3829
+ return null;
3243
3830
  }
3244
- });
3245
- const [isCheckingEligibility, setIsCheckingEligibility] = useState(false);
3246
- const isLoading = isSubmitting || isCheckingEligibility;
3247
- const handleVerify = useCallback(async () => {
3248
- if (onBeforeVerify) {
3249
- setIsCheckingEligibility(true);
3831
+ return createQuestApi(client);
3832
+ }, [client]);
3833
+ const autoSubmitAttemptedRef = useRef(false);
3834
+ useEffect(() => {
3835
+ autoSubmitAttemptedRef.current = false;
3836
+ }, [task.id]);
3837
+ useEffect(() => {
3838
+ if (!questApi) {
3839
+ return;
3840
+ }
3841
+ if (!userStatus) {
3842
+ return;
3843
+ }
3844
+ if (disabled || isSubmitted) {
3845
+ return;
3846
+ }
3847
+ if (autoSubmitAttemptedRef.current) {
3848
+ return;
3849
+ }
3850
+ autoSubmitAttemptedRef.current = true;
3851
+ let cancelled = false;
3852
+ const runAutoSubmit = async () => {
3853
+ var _a2;
3250
3854
  try {
3251
- const canProceed = await onBeforeVerify();
3252
- if (!canProceed) {
3855
+ const result = await questApi.submitTask({
3856
+ task_ids: [task.id],
3857
+ value: ""
3858
+ });
3859
+ if (cancelled) {
3253
3860
  return;
3254
3861
  }
3255
- } finally {
3256
- setIsCheckingEligibility(false);
3862
+ if ((_a2 = result.success_tasks) == null ? void 0 : _a2.length) {
3863
+ onCompleted == null ? void 0 : onCompleted(task.id, result);
3864
+ }
3865
+ } catch (error) {
3866
+ if (cancelled) {
3867
+ return;
3868
+ }
3869
+ const apiError = error instanceof ApiError ? error : null;
3870
+ if ((apiError == null ? void 0 : apiError.code) === "CAMPAIGN_INELIGIBILITY") {
3871
+ await (onBeforeVerify == null ? void 0 : onBeforeVerify());
3872
+ }
3257
3873
  }
3258
- }
3259
- await submit();
3260
- }, [onBeforeVerify, submit]);
3874
+ };
3875
+ void runAutoSubmit();
3876
+ return () => {
3877
+ cancelled = true;
3878
+ };
3879
+ }, [
3880
+ questApi,
3881
+ userStatus,
3882
+ disabled,
3883
+ isSubmitted,
3884
+ task.id,
3885
+ onCompleted,
3886
+ onBeforeVerify
3887
+ ]);
3888
+ const handleVerifySuccess = useCallback(
3889
+ (result) => {
3890
+ onCompleted == null ? void 0 : onCompleted(task.id, result);
3891
+ },
3892
+ [task.id, onCompleted]
3893
+ );
3894
+ const handleVerifyError = useCallback((error) => {
3895
+ console.error("Verify failed:", error);
3896
+ }, []);
3261
3897
  const title = params.task_name || "Interact With Contract";
3262
3898
  const needShowVerify = useMemo(() => {
3263
3899
  if (noVerify) {
@@ -3291,7 +3927,6 @@ function ContractInteractiveTask({
3291
3927
  icon: contractInteractiveIcon,
3292
3928
  isCompleted,
3293
3929
  isSubmitted,
3294
- isLoading,
3295
3930
  disabled,
3296
3931
  recurrenceType: task.recurrence,
3297
3932
  isPeriodic,
@@ -3300,24 +3935,21 @@ function ContractInteractiveTask({
3300
3935
  onCooldownComplete,
3301
3936
  pointsValue: (_a = task.points) == null ? void 0 : _a.amount,
3302
3937
  actionSlot: (
3303
- /* Verify button - Vue 版本 BaseTask 的 VerifyButton 逻辑一致 */
3938
+ /* Verify button - 复用通用 VerifyButton,统一异常弹窗与绑定逻辑 */
3304
3939
  needShowVerify ? /* @__PURE__ */ jsx(
3305
- "button",
3940
+ VerifyButton,
3306
3941
  {
3307
- className: `taskon-verify-btn ${isLoading ? "taskon-verify-btn--loading" : ""}`,
3308
- onClick: handleVerify,
3309
- disabled: isLoading,
3310
- children: isLoading ? "Verifying..." : "Verify"
3942
+ campaignId,
3943
+ taskId: task.id,
3944
+ chainType,
3945
+ platform: task.platform,
3946
+ onBeforeVerify,
3947
+ onSuccess: handleVerifySuccess,
3948
+ onError: handleVerifyError
3311
3949
  }
3312
3950
  ) : void 0
3313
3951
  ),
3314
- children: params.desc && /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsx(
3315
- "div",
3316
- {
3317
- className: "taskon-contract-desc",
3318
- dangerouslySetInnerHTML: { __html: sanitizeHtml(params.desc) }
3319
- }
3320
- ) })
3952
+ children: params.desc && /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsx(CardDescExpress, { label: params.desc, noMaxHeight: true }) })
3321
3953
  }
3322
3954
  ) });
3323
3955
  }
@@ -3357,6 +3989,7 @@ function DynamicPoints({
3357
3989
  onRefresh,
3358
3990
  className
3359
3991
  }) {
3992
+ const { requestLogin } = useTaskOnAuth();
3360
3993
  const [isCoolingDown, setIsCoolingDown] = useState(false);
3361
3994
  const [coolDownLeft, setCoolDownLeft] = useState(coolDown / 1e3);
3362
3995
  const [isAnimating, setIsAnimating] = useState(false);
@@ -3416,6 +4049,7 @@ ${computedTipDesc}`;
3416
4049
  }, [noTipTitle, computedTipTitle, computedTipDesc]);
3417
4050
  const handleRefresh = useCallback(() => {
3418
4051
  if (!isLoggedIn) {
4052
+ requestLogin();
3419
4053
  return;
3420
4054
  }
3421
4055
  if (isCoolingDown) return;
@@ -3439,7 +4073,7 @@ ${computedTipDesc}`;
3439
4073
  });
3440
4074
  }, 1e3);
3441
4075
  }, 1e3);
3442
- }, [isLoggedIn, isCoolingDown, onRefresh, coolDown]);
4076
+ }, [isLoggedIn, isCoolingDown, onRefresh, coolDown, requestLogin]);
3443
4077
  return /* @__PURE__ */ jsxs(
3444
4078
  "div",
3445
4079
  {
@@ -3524,22 +4158,49 @@ function SwapDexTask({
3524
4158
  const chainInfo = chains.find((c) => c.name.toLowerCase() === normalized);
3525
4159
  return (chainInfo == null ? void 0 : chainInfo.label) || params.chain;
3526
4160
  }, [params.chain, context == null ? void 0 : context.chains]);
4161
+ const isDynamicPoint = !params.is_fixed_reward_type;
4162
+ const isDynamicTimes = useMemo(() => {
4163
+ const { min_trades_times, is_fixed_reward_type } = params;
4164
+ return Boolean(
4165
+ is_fixed_reward_type && min_trades_times && min_trades_times !== 1
4166
+ );
4167
+ }, [params]);
4168
+ const useLegacyVerifyFlow = isDynamicPoint || isDynamicTimes;
4169
+ const [verifyFailedNotice, setVerifyFailedNotice] = useState(null);
3527
4170
  const { submit, isSubmitting } = useTaskSubmit({
3528
4171
  campaignId,
3529
4172
  taskId: task.id,
3530
4173
  chainType,
3531
4174
  // 钱包绑定:从 params.chain 获取
4175
+ platform: task.platform,
3532
4176
  onSuccess: (result) => {
4177
+ if (!useLegacyVerifyFlow) {
4178
+ return;
4179
+ }
3533
4180
  onCompleted == null ? void 0 : onCompleted(task.id, result);
3534
4181
  onVerifyAttempted == null ? void 0 : onVerifyAttempted(task.id, true, result);
3535
4182
  },
3536
4183
  onError: () => {
4184
+ if (!useLegacyVerifyFlow) {
4185
+ return;
4186
+ }
3537
4187
  onVerifyAttempted == null ? void 0 : onVerifyAttempted(task.id, false);
4188
+ },
4189
+ // 当后端返回 CAMPAIGN_INELIGIBILITY 时,复用现有 eligibility 检查逻辑
4190
+ onCampaignIneligibility: onBeforeVerify,
4191
+ onVerifyFailedNotice: (notice) => {
4192
+ if (!useLegacyVerifyFlow) {
4193
+ return;
4194
+ }
4195
+ setVerifyFailedNotice(notice);
3538
4196
  }
3539
4197
  });
3540
4198
  const [isCheckingEligibility, setIsCheckingEligibility] = useState(false);
3541
- const isLoading = isSubmitting || isCheckingEligibility;
4199
+ const isLoading = useLegacyVerifyFlow && (isSubmitting || isCheckingEligibility);
3542
4200
  const handleVerify = useCallback(async () => {
4201
+ if (!useLegacyVerifyFlow) {
4202
+ return;
4203
+ }
3543
4204
  if (onBeforeVerify) {
3544
4205
  setIsCheckingEligibility(true);
3545
4206
  try {
@@ -3552,7 +4213,17 @@ function SwapDexTask({
3552
4213
  }
3553
4214
  }
3554
4215
  await submit();
3555
- }, [onBeforeVerify, submit]);
4216
+ }, [useLegacyVerifyFlow, onBeforeVerify, submit]);
4217
+ const handleVerifyButtonSuccess = useCallback(
4218
+ (result) => {
4219
+ onCompleted == null ? void 0 : onCompleted(task.id, result);
4220
+ onVerifyAttempted == null ? void 0 : onVerifyAttempted(task.id, true, result);
4221
+ },
4222
+ [task.id, onCompleted, onVerifyAttempted]
4223
+ );
4224
+ const handleVerifyButtonError = useCallback(() => {
4225
+ onVerifyAttempted == null ? void 0 : onVerifyAttempted(task.id, false);
4226
+ }, [task.id, onVerifyAttempted]);
3556
4227
  const titleExpress = useMemo(() => {
3557
4228
  return getSwapDexTitleExpress({
3558
4229
  tokenOutList: params.token_out_list,
@@ -3569,14 +4240,13 @@ function SwapDexTask({
3569
4240
  const title = useMemo(() => {
3570
4241
  return /* @__PURE__ */ jsx(TitleExpress, { express: titleExpress });
3571
4242
  }, [titleExpress]);
3572
- const isDynamicPoint = !params.is_fixed_reward_type;
3573
- const isDynamicTimes = useMemo(() => {
3574
- const { min_trades_times, is_fixed_reward_type } = params;
3575
- return Boolean(
3576
- is_fixed_reward_type && min_trades_times && min_trades_times !== 1
3577
- );
3578
- }, [params]);
3579
4243
  const pointsName = ((_a = task.points) == null ? void 0 : _a.points_name) || "Points";
4244
+ const rulesMaxText = useMemo(() => {
4245
+ if (typeof params.swap_receive_max_points === "number" && params.swap_receive_max_points >= 0) {
4246
+ return `(Max ${params.swap_receive_max_points} ${pointsName})`;
4247
+ }
4248
+ return "";
4249
+ }, [params.swap_receive_max_points, pointsName]);
3580
4250
  const communityLogo = (communityInfo == null ? void 0 : communityInfo.logo) || "";
3581
4251
  const userDynamicPoint = (userStatus == null ? void 0 : userStatus.total_points) || 0;
3582
4252
  const userDynamicTimes = (userStatus == null ? void 0 : userStatus.current_trades_times) || 0;
@@ -3604,7 +4274,7 @@ function SwapDexTask({
3604
4274
  const handleRefresh = useCallback(() => {
3605
4275
  handleVerify();
3606
4276
  }, [handleVerify]);
3607
- const hasContent = isDynamicPoint && params.each_trading_volume && params.each_receive_points || params.desc;
4277
+ const hasContent = isDynamicPoint || params.desc;
3608
4278
  const dynamicPointsSlot = useMemo(() => {
3609
4279
  var _a2;
3610
4280
  if (isDynamicPoint) {
@@ -3703,68 +4373,77 @@ function SwapDexTask({
3703
4373
  isCompleted,
3704
4374
  isSubmitted
3705
4375
  ]);
3706
- return /* @__PURE__ */ jsx("div", { className: `taskon-swap-task ${className || ""}`, children: /* @__PURE__ */ jsx(
3707
- TaskCardBase,
3708
- {
3709
- taskId: task.id,
3710
- templateId: task.template_id,
3711
- title,
3712
- icon: communityLogo,
3713
- isCompleted,
3714
- isSubmitted,
3715
- isLoading,
3716
- disabled,
3717
- recurrenceType: task.recurrence,
3718
- isPeriodic,
3719
- isCurrentDone,
3720
- coolDownRemaining,
3721
- onCooldownComplete,
3722
- pointsValue: isDynamicPoint || isDynamicTimes ? void 0 : (_d = task.points) == null ? void 0 : _d.amount,
3723
- pointsSlot: dynamicPointsSlot,
3724
- pointsName: (_e = task.points) == null ? void 0 : _e.points_name,
3725
- totalEarnedPoints: userStatus == null ? void 0 : userStatus.total_points,
3726
- actionSlot: showVerifyButton ? /* @__PURE__ */ jsx(
3727
- "button",
3728
- {
3729
- className: `taskon-verify-btn ${isLoading ? "taskon-verify-btn--loading" : ""}`,
3730
- onClick: handleVerify,
3731
- disabled: isLoading,
3732
- children: isLoading ? "Verifying..." : "Verify"
3733
- }
3734
- ) : void 0,
3735
- children: hasContent && /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsxs("div", { className: "taskon-swap-content", children: [
3736
- isDynamicPoint && params.each_trading_volume && params.each_receive_points && /* @__PURE__ */ jsxs("div", { className: "taskon-swap-rules-section", children: [
3737
- /* @__PURE__ */ jsx("div", { className: "taskon-swap-rules-title", children: "Rules" }),
3738
- /* @__PURE__ */ jsxs("div", { className: "taskon-swap-rules-content", children: [
3739
- /* @__PURE__ */ jsx("span", { children: "Every " }),
3740
- /* @__PURE__ */ jsxs("span", { className: "taskon-swap-rules-highlight", children: [
3741
- "$",
3742
- params.each_trading_volume,
3743
- " USD"
3744
- ] }),
3745
- /* @__PURE__ */ jsx("span", { children: " you swap gives you " }),
3746
- /* @__PURE__ */ jsx("span", { className: "taskon-swap-rules-highlight", children: params.each_receive_points }),
3747
- /* @__PURE__ */ jsxs("span", { className: "taskon-swap-rules-point-name", children: [
3748
- " ",
3749
- pointsName
4376
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4377
+ /* @__PURE__ */ jsx("div", { className: `taskon-swap-task ${className || ""}`, children: /* @__PURE__ */ jsx(
4378
+ TaskCardBase,
4379
+ {
4380
+ taskId: task.id,
4381
+ templateId: task.template_id,
4382
+ title,
4383
+ icon: communityLogo,
4384
+ isCompleted,
4385
+ isSubmitted,
4386
+ isLoading,
4387
+ disabled,
4388
+ recurrenceType: task.recurrence,
4389
+ isPeriodic,
4390
+ isCurrentDone,
4391
+ coolDownRemaining,
4392
+ onCooldownComplete,
4393
+ pointsValue: isDynamicPoint || isDynamicTimes ? void 0 : (_d = task.points) == null ? void 0 : _d.amount,
4394
+ pointsSlot: dynamicPointsSlot,
4395
+ pointsName: (_e = task.points) == null ? void 0 : _e.points_name,
4396
+ totalEarnedPoints: userStatus == null ? void 0 : userStatus.total_points,
4397
+ actionSlot: showVerifyButton ? /* @__PURE__ */ jsx(
4398
+ VerifyButton,
4399
+ {
4400
+ campaignId,
4401
+ taskId: task.id,
4402
+ chainType,
4403
+ platform: task.platform,
4404
+ onBeforeVerify,
4405
+ onSuccess: handleVerifyButtonSuccess,
4406
+ onError: handleVerifyButtonError
4407
+ }
4408
+ ) : void 0,
4409
+ children: hasContent && /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsxs("div", { className: "taskon-swap-content", children: [
4410
+ isDynamicPoint && /* @__PURE__ */ jsxs("div", { className: "taskon-swap-rules-section", children: [
4411
+ /* @__PURE__ */ jsx("div", { className: "taskon-swap-rules-title", children: "Rules" }),
4412
+ /* @__PURE__ */ jsxs("div", { className: "taskon-swap-rules-content", children: [
4413
+ /* @__PURE__ */ jsx("span", { children: "Every " }),
4414
+ /* @__PURE__ */ jsxs("span", { className: "taskon-swap-rules-highlight", children: [
4415
+ "$",
4416
+ params.each_trading_volume || "0",
4417
+ " USD"
4418
+ ] }),
4419
+ /* @__PURE__ */ jsx("span", { children: " will give you " }),
4420
+ /* @__PURE__ */ jsx("span", { className: "taskon-swap-rules-highlight", children: params.each_receive_points }),
4421
+ /* @__PURE__ */ jsxs("span", { className: "taskon-swap-rules-point-name", children: [
4422
+ " ",
4423
+ pointsName,
4424
+ rulesMaxText && /* @__PURE__ */ jsx("span", { className: "taskon-swap-rules-max", children: rulesMaxText })
4425
+ ] })
3750
4426
  ] })
4427
+ ] }),
4428
+ params.desc && /* @__PURE__ */ jsxs("div", { className: "taskon-swap-instructions-section", children: [
4429
+ /* @__PURE__ */ jsx("div", { className: "taskon-swap-instructions-title", children: "Instructions" }),
4430
+ /* @__PURE__ */ jsx("div", { className: "taskon-swap-instructions-content", children: /* @__PURE__ */ jsx(CardDescExpress, { label: params.desc, noMaxHeight: true }) })
3751
4431
  ] })
3752
- ] }),
3753
- params.desc && /* @__PURE__ */ jsxs("div", { className: "taskon-swap-instructions-section", children: [
3754
- /* @__PURE__ */ jsx("div", { className: "taskon-swap-instructions-title", children: "Instructions" }),
3755
- /* @__PURE__ */ jsx("div", { className: "taskon-swap-instructions-content", children: /* @__PURE__ */ jsx(
3756
- "div",
3757
- {
3758
- className: "taskon-swap-desc",
3759
- dangerouslySetInnerHTML: {
3760
- __html: sanitizeHtml(params.desc)
3761
- }
3762
- }
3763
- ) })
3764
- ] })
3765
- ] }) })
3766
- }
3767
- ) });
4432
+ ] }) })
4433
+ }
4434
+ ) }),
4435
+ useLegacyVerifyFlow && /* @__PURE__ */ jsx(
4436
+ TaskVerifyFailedDialog,
4437
+ {
4438
+ open: Boolean(verifyFailedNotice),
4439
+ title: verifyFailedNotice == null ? void 0 : verifyFailedNotice.title,
4440
+ message: verifyFailedNotice == null ? void 0 : verifyFailedNotice.message,
4441
+ onClose: () => {
4442
+ setVerifyFailedNotice(null);
4443
+ }
4444
+ }
4445
+ )
4446
+ ] });
3768
4447
  }
3769
4448
  function outerAPISnsTypeToSnsType(snsType) {
3770
4449
  if (!snsType) return void 0;
@@ -3933,17 +4612,24 @@ function OuterPointAPITask({
3933
4612
  const chainType = useChainType(
3934
4613
  params.account_type !== "SNS" && !hasCustomizedParam ? params.network : void 0
3935
4614
  );
4615
+ const [verifyFailedNotice, setVerifyFailedNotice] = useState(null);
3936
4616
  const { submit, isSubmitting } = useTaskSubmit({
3937
4617
  campaignId,
3938
4618
  taskId: task.id,
3939
4619
  snsType,
3940
4620
  chainType,
4621
+ platform: task.platform,
3941
4622
  onSuccess: (result) => {
3942
4623
  onCompleted == null ? void 0 : onCompleted(task.id, result);
3943
4624
  onVerifyAttempted == null ? void 0 : onVerifyAttempted(task.id, true, result);
3944
4625
  },
3945
4626
  onError: () => {
3946
4627
  onVerifyAttempted == null ? void 0 : onVerifyAttempted(task.id, false);
4628
+ },
4629
+ // 当后端返回 CAMPAIGN_INELIGIBILITY 时,复用现有 eligibility 检查逻辑
4630
+ onCampaignIneligibility: onBeforeVerify,
4631
+ onVerifyFailedNotice: (notice) => {
4632
+ setVerifyFailedNotice(notice);
3947
4633
  }
3948
4634
  });
3949
4635
  const [isCheckingEligibility, setIsCheckingEligibility] = useState(false);
@@ -4051,15 +4737,7 @@ function OuterPointAPITask({
4051
4737
  pointsName,
4052
4738
  totalEarnedPoints: userStatus == null ? void 0 : userStatus.total_points,
4053
4739
  actionSlot: void 0,
4054
- children: hasContent && /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsx(
4055
- "div",
4056
- {
4057
- className: "taskon-outer-point-api-desc",
4058
- dangerouslySetInnerHTML: {
4059
- __html: sanitizeHtml(params.desc)
4060
- }
4061
- }
4062
- ) })
4740
+ children: hasContent && /* @__PURE__ */ jsx(ExpandableContent, { children: /* @__PURE__ */ jsx(CardDescExpress, { label: params.desc, noMaxHeight: true }) })
4063
4741
  }
4064
4742
  ),
4065
4743
  hasCustomizedParam && /* @__PURE__ */ jsx(
@@ -4070,6 +4748,17 @@ function OuterPointAPITask({
4070
4748
  onConfirm: handleDialogConfirm,
4071
4749
  onCancel: handleDialogCancel
4072
4750
  }
4751
+ ),
4752
+ /* @__PURE__ */ jsx(
4753
+ TaskVerifyFailedDialog,
4754
+ {
4755
+ open: Boolean(verifyFailedNotice),
4756
+ title: verifyFailedNotice == null ? void 0 : verifyFailedNotice.title,
4757
+ message: verifyFailedNotice == null ? void 0 : verifyFailedNotice.message,
4758
+ onClose: () => {
4759
+ setVerifyFailedNotice(null);
4760
+ }
4761
+ }
4073
4762
  )
4074
4763
  ] });
4075
4764
  }
@@ -4140,6 +4829,130 @@ function TaskItem({
4140
4829
  }
4141
4830
  );
4142
4831
  }
4832
+ function RewardModuleDialog({
4833
+ open,
4834
+ onOpenChange,
4835
+ type,
4836
+ pointsInfo
4837
+ }) {
4838
+ const isToken = type === "token";
4839
+ const isPoints = type === "points";
4840
+ const isNft = type === "nft";
4841
+ const { messages, isLoading } = useWidgetLocale({
4842
+ widgetId: "UserCenterWidget",
4843
+ defaultMessages: enMessages,
4844
+ loadMessages: (locale) => __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "../../../../UserCenter/locales/en.json": () => import("./UserCenterWidget-cADBSVg7.js").then((n) => n.w), "../../../../UserCenter/locales/ja.json": () => import("./usercenter-ja-B2465c1O.js"), "../../../../UserCenter/locales/ko.json": () => import("./usercenter-ko-xAEYxqLg.js") }), `../../../../UserCenter/locales/${locale}.json`, 7)
4845
+ });
4846
+ const tokenAssets = useTokenAssets({
4847
+ autoLoad: open && isToken
4848
+ });
4849
+ const tokenHistory = useRewardDetails({
4850
+ rewardType: RewardType.Token,
4851
+ autoLoad: open && isToken
4852
+ });
4853
+ const nftHistory = useRewardDetails({
4854
+ rewardType: RewardType.Nft,
4855
+ autoLoad: open && isNft
4856
+ });
4857
+ const [showWithdrawForm, setShowWithdrawForm] = useState(false);
4858
+ const [selectedTokenForWithdraw, setSelectedTokenForWithdraw] = useState(null);
4859
+ const handleWithdraw = useCallback((token) => {
4860
+ setSelectedTokenForWithdraw(token);
4861
+ setShowWithdrawForm(true);
4862
+ }, []);
4863
+ const handleBatchWithdraw = useCallback(() => {
4864
+ setSelectedTokenForWithdraw(null);
4865
+ setShowWithdrawForm(true);
4866
+ }, []);
4867
+ const pointsHistory = usePointsHistory({
4868
+ pointsId: (pointsInfo == null ? void 0 : pointsInfo.points_id) ?? 0,
4869
+ autoLoad: open && isPoints && Boolean(pointsInfo == null ? void 0 : pointsInfo.points_id)
4870
+ });
4871
+ const dialogTitle = useMemo(() => {
4872
+ if (isToken) return messages.rewardToken ?? "Token";
4873
+ if (isPoints) return messages.pointsHistory ?? "Points History";
4874
+ if (isNft) return messages.rewardNft ?? "NFT";
4875
+ return "";
4876
+ }, [isToken, isPoints, isNft, messages.rewardToken, messages.pointsHistory, messages.rewardNft]);
4877
+ const handleOpenChange = useCallback(
4878
+ (nextOpen) => {
4879
+ if (!nextOpen) {
4880
+ setShowWithdrawForm(false);
4881
+ setSelectedTokenForWithdraw(null);
4882
+ }
4883
+ onOpenChange(nextOpen);
4884
+ },
4885
+ [onOpenChange]
4886
+ );
4887
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4888
+ /* @__PURE__ */ jsx(
4889
+ Dialog,
4890
+ {
4891
+ open,
4892
+ onOpenChange: handleOpenChange,
4893
+ title: dialogTitle,
4894
+ showCloseButton: true,
4895
+ maxWidth: 960,
4896
+ contentClassName: "taskon-quest-reward-dialog",
4897
+ children: isLoading ? /* @__PURE__ */ jsx(LoadingState, { message: messages.loading }) : /* @__PURE__ */ jsxs("div", { className: "taskon-user-center taskon-user-center--popup", children: [
4898
+ isToken && /* @__PURE__ */ jsx(
4899
+ TokenRewardContent,
4900
+ {
4901
+ tokenAssets: tokenAssets.data,
4902
+ tokenAssetsLoading: tokenAssets.loading,
4903
+ tokenAssetsError: tokenAssets.error,
4904
+ pendingWithdrawals: tokenAssets.pendingWithdrawals,
4905
+ tokenHistory: tokenHistory.data,
4906
+ tokenHistoryLoading: tokenHistory.loading,
4907
+ tokenHistoryError: tokenHistory.error,
4908
+ tokenHistoryPagination: tokenHistory.pagination,
4909
+ messages,
4910
+ onWithdraw: handleWithdraw,
4911
+ onBatchWithdraw: handleBatchWithdraw
4912
+ }
4913
+ ),
4914
+ isPoints && (pointsInfo ? /* @__PURE__ */ jsx(
4915
+ PointsList,
4916
+ {
4917
+ pointsInfo,
4918
+ data: pointsHistory.data,
4919
+ loading: pointsHistory.loading,
4920
+ error: pointsHistory.error,
4921
+ pagination: pointsHistory.pagination,
4922
+ messages
4923
+ }
4924
+ ) : /* @__PURE__ */ jsx(EmptyState, { message: messages.emptyPoints })),
4925
+ isNft && /* @__PURE__ */ jsx(
4926
+ NftRewardContent,
4927
+ {
4928
+ nftList: nftHistory.data,
4929
+ loading: nftHistory.loading,
4930
+ error: nftHistory.error,
4931
+ pagination: nftHistory.pagination,
4932
+ messages
4933
+ }
4934
+ )
4935
+ ] })
4936
+ }
4937
+ ),
4938
+ isToken && /* @__PURE__ */ jsx(
4939
+ WithdrawForm,
4940
+ {
4941
+ open: showWithdrawForm,
4942
+ messages,
4943
+ tokenAssets: tokenAssets.data,
4944
+ tokenAssetsLoading: tokenAssets.loading,
4945
+ initialTokenId: selectedTokenForWithdraw == null ? void 0 : selectedTokenForWithdraw.token_id,
4946
+ initialChain: selectedTokenForWithdraw == null ? void 0 : selectedTokenForWithdraw.chain,
4947
+ onClose: () => setShowWithdrawForm(false),
4948
+ onSuccess: () => {
4949
+ tokenAssets.refresh();
4950
+ setShowWithdrawForm(false);
4951
+ }
4952
+ }
4953
+ )
4954
+ ] });
4955
+ }
4143
4956
  function getDefaultExportFromCjs(x) {
4144
4957
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
4145
4958
  }
@@ -21293,93 +22106,6 @@ const BlindBoxDialog = forwardRef(
21293
22106
  ] });
21294
22107
  }
21295
22108
  );
21296
- function formatAmount(amount) {
21297
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
21298
- if (isNaN(num)) return "0";
21299
- if (num >= 1e9) {
21300
- return `${(num / 1e9).toFixed(2)}B`;
21301
- }
21302
- if (num >= 1e6) {
21303
- return `${(num / 1e6).toFixed(2)}M`;
21304
- }
21305
- if (num >= 1e3) {
21306
- return `${(num / 1e3).toFixed(2)}K`;
21307
- }
21308
- if (num < 1 && num > 0) {
21309
- return num.toFixed(6).replace(/\.?0+$/, "");
21310
- }
21311
- return num.toFixed(2).replace(/\.?0+$/, "");
21312
- }
21313
- function formatUsdValue(amount, price) {
21314
- if (!price || price <= 0) return null;
21315
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
21316
- if (isNaN(num) || num <= 0) return null;
21317
- const value2 = num * price;
21318
- return `≈ $${formatAmount(value2)}`;
21319
- }
21320
- function RewardCard({
21321
- reward,
21322
- tokenPrice
21323
- }) {
21324
- if (reward.reward_type !== RewardType.Token) {
21325
- return null;
21326
- }
21327
- const tokenValue = reward.reward_value;
21328
- const amount = tokenValue.amount || "0";
21329
- const tokenName = tokenValue.token_name || "Token";
21330
- const tokenLogo = tokenValue.token_logo;
21331
- const usdValue = formatUsdValue(amount, tokenPrice);
21332
- return /* @__PURE__ */ jsxs("div", { className: "taskon-quest-blindbox-reward-card", children: [
21333
- tokenLogo && /* @__PURE__ */ jsx(
21334
- "img",
21335
- {
21336
- className: "taskon-quest-blindbox-reward-card-icon",
21337
- src: tokenLogo,
21338
- alt: tokenName
21339
- }
21340
- ),
21341
- /* @__PURE__ */ jsxs("div", { className: "taskon-quest-blindbox-reward-card-amount", children: [
21342
- "+ ",
21343
- formatAmount(amount),
21344
- " ",
21345
- tokenName
21346
- ] }),
21347
- usdValue && /* @__PURE__ */ jsx("div", { className: "taskon-quest-blindbox-reward-card-value", children: usdValue })
21348
- ] });
21349
- }
21350
- function BlindBoxRewardDialog(props) {
21351
- const { rewards, emptyReward, tokenPrice, loading, onClose } = props;
21352
- const hasRewards = rewards.length > 0;
21353
- const title = hasRewards ? "Congratulations!" : "Oops! Better Luck Next Time!";
21354
- const subtitle = hasRewards ? "You Have Won" : "Rewards Missed";
21355
- const displayRewards = useMemo(() => {
21356
- if (hasRewards) {
21357
- return rewards;
21358
- }
21359
- return emptyReward ? [emptyReward] : [];
21360
- }, [hasRewards, rewards, emptyReward]);
21361
- return /* @__PURE__ */ jsxs("div", { className: "taskon-quest-blindbox-reward", children: [
21362
- /* @__PURE__ */ jsx("h2", { className: "taskon-quest-blindbox-reward-title", children: title }),
21363
- /* @__PURE__ */ jsx("p", { className: "taskon-quest-blindbox-reward-subtitle", children: subtitle }),
21364
- displayRewards.length > 0 && /* @__PURE__ */ jsx("div", { className: "taskon-quest-blindbox-reward-list", children: displayRewards.map((reward, index2) => /* @__PURE__ */ jsx(
21365
- RewardCard,
21366
- {
21367
- reward,
21368
- tokenPrice
21369
- },
21370
- `${reward.reward_id}-${index2}`
21371
- )) }),
21372
- /* @__PURE__ */ jsx("div", { className: "taskon-quest-blindbox-reward-btn-wrap", children: /* @__PURE__ */ jsx(
21373
- "button",
21374
- {
21375
- className: "taskon-quest-blindbox-reward-btn",
21376
- disabled: loading,
21377
- onClick: onClose,
21378
- children: loading ? "Loading..." : "Back"
21379
- }
21380
- ) })
21381
- ] });
21382
- }
21383
22109
  function TokenBalance({
21384
22110
  params
21385
22111
  }) {
@@ -21463,13 +22189,9 @@ function SupportedCountry({
21463
22189
  }
21464
22190
  );
21465
22191
  }
21466
- function getCommunityUrl(communityKey) {
21467
- return `https://taskon.xyz/space/${communityKey}`;
21468
- }
21469
22192
  function CommunityPoint({
21470
22193
  params
21471
22194
  }) {
21472
- const communityUrl = getCommunityUrl(params.community_key);
21473
22195
  const isGte = params.operator === "Gte";
21474
22196
  return /* @__PURE__ */ jsxs("div", { className: "taskon-quest-eligs-type", children: [
21475
22197
  "Your",
@@ -21479,20 +22201,7 @@ function CommunityPoint({
21479
22201
  "is ",
21480
22202
  isGte ? "≥" : "<",
21481
22203
  " ",
21482
- /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.points.amount }),
21483
- " ",
21484
- "in",
21485
- " ",
21486
- /* @__PURE__ */ jsx(
21487
- "a",
21488
- {
21489
- className: "taskon-quest-eligs-link",
21490
- href: communityUrl,
21491
- target: "_blank",
21492
- rel: "noopener noreferrer",
21493
- children: params.community_name
21494
- }
21495
- )
22204
+ /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.points.amount })
21496
22205
  ] });
21497
22206
  }
21498
22207
  const TASKON_CLUB_COMMUNITY_ID = 2;
@@ -21511,40 +22220,17 @@ function CommunityLevel({
21511
22220
  "Lv is ",
21512
22221
  isGte ? "≥" : "<",
21513
22222
  " ",
21514
- /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.level }),
21515
- " ",
21516
- "in",
21517
- " ",
21518
- /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.community_name })
22223
+ /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.level })
21519
22224
  ] });
21520
22225
  }
21521
- function getCommunityTaskUrl(communityKey, taskId) {
21522
- return `https://taskon.xyz/space/${communityKey}?task=${taskId}`;
21523
- }
21524
22226
  function CommunityTask({
21525
- params,
21526
- communityKey,
21527
- communityName
22227
+ params
21528
22228
  }) {
21529
22229
  const isNot = params.operator === "Not";
21530
- const taskUrl = communityKey ? getCommunityTaskUrl(communityKey, params.task_id) : void 0;
21531
22230
  return /* @__PURE__ */ jsxs("div", { className: "taskon-quest-eligs-type", children: [
21532
22231
  isNot ? "Your did not complete task:" : "Your have completed task:",
21533
22232
  " ",
21534
- /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.task_name }),
21535
- " ",
21536
- "in",
21537
- " ",
21538
- taskUrl ? /* @__PURE__ */ jsx(
21539
- "a",
21540
- {
21541
- className: "taskon-quest-eligs-link",
21542
- href: taskUrl,
21543
- target: "_blank",
21544
- rel: "noopener noreferrer",
21545
- children: communityName
21546
- }
21547
- ) : /* @__PURE__ */ jsx("span", { children: communityName })
22233
+ /* @__PURE__ */ jsx("span", { className: "taskon-quest-eligs-highlight", children: params.task_name })
21548
22234
  ] });
21549
22235
  }
21550
22236
  function CommunityMilestone({
@@ -21872,12 +22558,12 @@ function PassedIcon$1() {
21872
22558
  fill: "none",
21873
22559
  xmlns: "http://www.w3.org/2000/svg",
21874
22560
  children: [
21875
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "#00D395" }),
22561
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "currentColor" }),
21876
22562
  /* @__PURE__ */ jsx(
21877
22563
  "path",
21878
22564
  {
21879
22565
  d: "M4.5 8L7 10.5L11.5 6",
21880
- stroke: "white",
22566
+ stroke: "var(--taskon-color-bg-canvas)",
21881
22567
  strokeWidth: "1.5",
21882
22568
  strokeLinecap: "round",
21883
22569
  strokeLinejoin: "round"
@@ -21898,12 +22584,12 @@ function FailedIcon$1() {
21898
22584
  fill: "none",
21899
22585
  xmlns: "http://www.w3.org/2000/svg",
21900
22586
  children: [
21901
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "#FF4D4F" }),
22587
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "currentColor" }),
21902
22588
  /* @__PURE__ */ jsx(
21903
22589
  "path",
21904
22590
  {
21905
22591
  d: "M5.5 5.5L10.5 10.5M10.5 5.5L5.5 10.5",
21906
- stroke: "white",
22592
+ stroke: "var(--taskon-color-bg-canvas)",
21907
22593
  strokeWidth: "1.5",
21908
22594
  strokeLinecap: "round",
21909
22595
  strokeLinejoin: "round"
@@ -22051,19 +22737,19 @@ function PassedIcon() {
22051
22737
  return /* @__PURE__ */ jsxs(
22052
22738
  "svg",
22053
22739
  {
22054
- className: "taskon-quest-eligs-header-status",
22740
+ className: "taskon-quest-eligs-header-status taskon-quest-eligs-header-status--passed",
22055
22741
  width: "16",
22056
22742
  height: "16",
22057
22743
  viewBox: "0 0 16 16",
22058
22744
  fill: "none",
22059
22745
  xmlns: "http://www.w3.org/2000/svg",
22060
22746
  children: [
22061
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "#00D395" }),
22747
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "currentColor" }),
22062
22748
  /* @__PURE__ */ jsx(
22063
22749
  "path",
22064
22750
  {
22065
22751
  d: "M4.5 8L7 10.5L11.5 6",
22066
- stroke: "white",
22752
+ stroke: "var(--taskon-color-bg-canvas)",
22067
22753
  strokeWidth: "1.5",
22068
22754
  strokeLinecap: "round",
22069
22755
  strokeLinejoin: "round"
@@ -22077,19 +22763,19 @@ function FailedIcon() {
22077
22763
  return /* @__PURE__ */ jsxs(
22078
22764
  "svg",
22079
22765
  {
22080
- className: "taskon-quest-eligs-header-status",
22766
+ className: "taskon-quest-eligs-header-status taskon-quest-eligs-header-status--failed",
22081
22767
  width: "16",
22082
22768
  height: "16",
22083
22769
  viewBox: "0 0 16 16",
22084
22770
  fill: "none",
22085
22771
  xmlns: "http://www.w3.org/2000/svg",
22086
22772
  children: [
22087
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "#FF4D4F" }),
22773
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8", fill: "currentColor" }),
22088
22774
  /* @__PURE__ */ jsx(
22089
22775
  "path",
22090
22776
  {
22091
22777
  d: "M5.5 5.5L10.5 10.5M10.5 5.5L5.5 10.5",
22092
- stroke: "white",
22778
+ stroke: "var(--taskon-color-bg-canvas)",
22093
22779
  strokeWidth: "1.5",
22094
22780
  strokeLinecap: "round",
22095
22781
  strokeLinejoin: "round"
@@ -22214,15 +22900,17 @@ function EligibilityInfo({
22214
22900
  ] });
22215
22901
  }
22216
22902
  export {
22217
- BlindBoxRewardDialog as B,
22903
+ BlindBoxDialog as B,
22904
+ ConfirmNoticeDialog as C,
22218
22905
  EligibilityInfo as E,
22906
+ RewardModuleDialog as R,
22219
22907
  TitleExpress as T,
22220
- useBindWallet as a,
22221
- BlindBoxDialog as b,
22908
+ CardDescExpress as a,
22909
+ useNftClaimFlow as b,
22222
22910
  TaskItem as c,
22223
22911
  Textarea as d,
22224
22912
  EligibilityList as e,
22225
22913
  getDefaultExportFromCjs as g,
22226
22914
  sanitizeHtml as s,
22227
- useIsMobile as u
22915
+ useBindWallet as u
22228
22916
  };