@taskon/widget-react 0.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1065 -0
- package/dist/CommunityTaskList.css +4893 -0
- package/dist/EligibilityInfo.css +2337 -0
- package/dist/LeaderboardWidget.css +815 -0
- package/dist/PageBuilder.css +54 -0
- package/dist/Quest.css +4214 -0
- package/dist/TaskOnProvider.css +163 -0
- package/dist/TipPopover.css +210 -0
- package/dist/UserCenterWidget.css +297 -0
- package/dist/UserCenterWidget2.css +3519 -0
- package/dist/WidgetShell.css +182 -0
- package/dist/chunks/CommunityTaskList-DoPGZsw1.js +6813 -0
- package/dist/chunks/EligibilityInfo-C7GZ2G5u.js +22228 -0
- package/dist/chunks/LeaderboardWidget-CmYfDeHV.js +1068 -0
- package/dist/chunks/PageBuilder-Tmhf2GTS.js +150 -0
- package/dist/chunks/Quest-DKFZ-pPU.js +8839 -0
- package/dist/chunks/TaskOnProvider-BD6Vp2x8.js +1435 -0
- package/dist/chunks/ThemeProvider-wnSXrNQb.js +1118 -0
- package/dist/chunks/TipPopover-BrW8jo71.js +2926 -0
- package/dist/chunks/UserCenterWidget-BE329iS7.js +3546 -0
- package/dist/chunks/UserCenterWidget-BVw_IEEd.js +3989 -0
- package/dist/chunks/WidgetShell-D_5OjvNZ.js +1517 -0
- package/dist/chunks/common-ja-DWhTaFHb.js +23 -0
- package/dist/chunks/common-ko-80ezXsMG.js +23 -0
- package/dist/chunks/dynamic-import-helper-DxEFwm31.js +537 -0
- package/dist/chunks/index-CwMvO_wZ.js +777 -0
- package/dist/chunks/leaderboardwidget-ja-Bj6gz6y1.js +119 -0
- package/dist/chunks/leaderboardwidget-ko-f1cLO9ic.js +119 -0
- package/dist/chunks/useToast-B-wyO5zL.js +93 -0
- package/dist/chunks/useWidgetLocale-JDelxtt8.js +74 -0
- package/dist/chunks/usercenter-ja-uu-XfVF9.js +332 -0
- package/dist/chunks/usercenter-ko-DYgUOVzd.js +332 -0
- package/dist/community-task.d.ts +451 -0
- package/dist/community-task.js +9 -0
- package/dist/core.d.ts +803 -0
- package/dist/core.js +22 -0
- package/dist/dynamic-import-helper.css +389 -0
- package/dist/index.d.ts +1660 -0
- package/dist/index.js +41 -0
- package/dist/leaderboard.d.ts +547 -0
- package/dist/leaderboard.js +18 -0
- package/dist/page-builder.d.ts +20 -0
- package/dist/page-builder.js +4 -0
- package/dist/quest.d.ts +400 -0
- package/dist/quest.js +8 -0
- package/dist/user-center.d.ts +1780 -0
- package/dist/user-center.js +713 -0
- package/package.json +105 -0
|
@@ -0,0 +1,3989 @@
|
|
|
1
|
+
import { T as Table, u as usePagination, B as Button, _ as __variableDynamicImportRuntimeHelper } from "./dynamic-import-helper-DxEFwm31.js";
|
|
2
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useCallback, useLayoutEffect, useMemo, useEffect } from "react";
|
|
4
|
+
import { UserCenterRewardCardType, USER_CENTER_PAGE_SIZE, createUserCenterApi, GasStationRequestStatus, SnsType, createUserApi, VerifyCodeType, createNftClaimApi, NftClaimError, NftClaimErrorType, getNftClaimType, getNftChainName, generatePendingKey, getPendingHash, shouldUseGasStation, NftClaimType, bMintedNftAbi, capNftAbi, setPendingHash, clearPendingHash, RewardType, isNftClaimed, isNftClaimable, getChainName, truncateAddress, filterEnabledAccounts, filterEnabledWallets, getTxExplorerUrl, openInNewTab, USER_CENTER_REWARD_CARD_TYPES, getDefaultTabConfig, filterEnabledTabs, UserCenterTabType } from "@taskon/core";
|
|
5
|
+
import { b as useTaskOnContext } from "./ThemeProvider-wnSXrNQb.js";
|
|
6
|
+
import { L as LoadingState, E as EmptyState, P as Pagination, g as formatDateTime, e as buildRewardCards, h as TxErrorType, d as toGasFreeWithdrawItem, i as TransactionError, j as isSameAddress, p as parseTxError, a as useTokenAssets, u as useRewardDetails, c as usePointsHistory, W as WithdrawForm, k as PointsList, l as TokenRewardContent, m as enMessages } from "./UserCenterWidget-BE329iS7.js";
|
|
7
|
+
import { D as Dialog, u as useResolvedWidgetConfig, W as WidgetShell } from "./WidgetShell-D_5OjvNZ.js";
|
|
8
|
+
import { u as useWidgetLocale } from "./useWidgetLocale-JDelxtt8.js";
|
|
9
|
+
import { u as useChainMap, a as useBindSocialAccount, I as Input } from "./TipPopover-BrW8jo71.js";
|
|
10
|
+
import { u as useWallet, b as useToast } from "./useToast-B-wyO5zL.js";
|
|
11
|
+
import '../UserCenterWidget.css';function Tabs({
|
|
12
|
+
items,
|
|
13
|
+
activeKey,
|
|
14
|
+
defaultActiveKey,
|
|
15
|
+
onChange,
|
|
16
|
+
className = "",
|
|
17
|
+
style,
|
|
18
|
+
gap = 40
|
|
19
|
+
}) {
|
|
20
|
+
var _a;
|
|
21
|
+
const [internalActiveKey, setInternalActiveKey] = useState(
|
|
22
|
+
defaultActiveKey ?? ((_a = items[0]) == null ? void 0 : _a.key) ?? ""
|
|
23
|
+
);
|
|
24
|
+
const currentActiveKey = activeKey ?? internalActiveKey;
|
|
25
|
+
const tabsRef = useRef(null);
|
|
26
|
+
const [indicatorStyle, setIndicatorStyle] = useState({});
|
|
27
|
+
const updateIndicator = useCallback(() => {
|
|
28
|
+
if (!tabsRef.current) return;
|
|
29
|
+
const activeTab = tabsRef.current.querySelector(
|
|
30
|
+
`.taskon-tabs__tab--active`
|
|
31
|
+
);
|
|
32
|
+
if (activeTab) {
|
|
33
|
+
setIndicatorStyle({
|
|
34
|
+
width: activeTab.offsetWidth,
|
|
35
|
+
transform: `translateX(${activeTab.offsetLeft}px)`
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}, []);
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
updateIndicator();
|
|
41
|
+
}, [currentActiveKey, updateIndicator]);
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
window.addEventListener("resize", updateIndicator);
|
|
44
|
+
return () => window.removeEventListener("resize", updateIndicator);
|
|
45
|
+
}, [updateIndicator]);
|
|
46
|
+
const handleTabClick = (item) => {
|
|
47
|
+
if (item.disabled) return;
|
|
48
|
+
if (activeKey === void 0) {
|
|
49
|
+
setInternalActiveKey(item.key);
|
|
50
|
+
}
|
|
51
|
+
onChange == null ? void 0 : onChange(item.key);
|
|
52
|
+
};
|
|
53
|
+
return /* @__PURE__ */ jsxs(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
ref: tabsRef,
|
|
57
|
+
className: `taskon-tabs ${className}`,
|
|
58
|
+
style: { ...style, "--taskon-tabs-gap": `${gap}px` },
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-tabs__list", children: items.map((item) => /* @__PURE__ */ jsx(
|
|
61
|
+
"button",
|
|
62
|
+
{
|
|
63
|
+
type: "button",
|
|
64
|
+
className: `taskon-tabs__tab ${currentActiveKey === item.key ? "taskon-tabs__tab--active" : ""} ${item.disabled ? "taskon-tabs__tab--disabled" : ""}`,
|
|
65
|
+
onClick: () => handleTabClick(item),
|
|
66
|
+
disabled: item.disabled,
|
|
67
|
+
"aria-selected": currentActiveKey === item.key,
|
|
68
|
+
role: "tab",
|
|
69
|
+
children: item.label
|
|
70
|
+
},
|
|
71
|
+
item.key
|
|
72
|
+
)) }),
|
|
73
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-tabs__indicator", style: indicatorStyle })
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function formatTimeRange(startTime, endTime) {
|
|
79
|
+
const formatDate = (timestamp) => {
|
|
80
|
+
const date = new Date(timestamp);
|
|
81
|
+
const year = date.getFullYear();
|
|
82
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
83
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
84
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
85
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
86
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
87
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
88
|
+
};
|
|
89
|
+
return `${formatDate(startTime)} - ${formatDate(endTime)}`;
|
|
90
|
+
}
|
|
91
|
+
function ActivityHistoryList({
|
|
92
|
+
data,
|
|
93
|
+
loading,
|
|
94
|
+
error,
|
|
95
|
+
pagination,
|
|
96
|
+
messages,
|
|
97
|
+
mode = "pagination",
|
|
98
|
+
onItemClick,
|
|
99
|
+
onRetry
|
|
100
|
+
}) {
|
|
101
|
+
const columns = useMemo(
|
|
102
|
+
() => [
|
|
103
|
+
{
|
|
104
|
+
key: "campaign_name",
|
|
105
|
+
title: messages.activityName,
|
|
106
|
+
cellClassName: "taskon-activity-history__cell--name",
|
|
107
|
+
render: (_value, row) => /* @__PURE__ */ jsxs("div", { className: "taskon-activity-history__name-cell", children: [
|
|
108
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-activity-history__campaign-name", children: row.campaign_name }),
|
|
109
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-activity-history__campaign-type", children: row.campaign_type })
|
|
110
|
+
] })
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
key: "time",
|
|
114
|
+
title: messages.activityTime,
|
|
115
|
+
width: "380px",
|
|
116
|
+
align: "right",
|
|
117
|
+
cellClassName: "taskon-activity-history__cell--time",
|
|
118
|
+
render: (_value, row) => formatTimeRange(row.start_time, row.end_time)
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
[messages.activityName, messages.activityTime]
|
|
122
|
+
);
|
|
123
|
+
const rowConfig = useMemo(
|
|
124
|
+
() => ({
|
|
125
|
+
getRowKey: (row) => `${row.campaign_type}-${row.campaign_id}`,
|
|
126
|
+
onRowClick: onItemClick
|
|
127
|
+
}),
|
|
128
|
+
[onItemClick]
|
|
129
|
+
);
|
|
130
|
+
if (loading && data.length === 0) {
|
|
131
|
+
return /* @__PURE__ */ jsx(LoadingState, { message: messages.loading });
|
|
132
|
+
}
|
|
133
|
+
if (error && data.length === 0) {
|
|
134
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-user-center-error", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("p", { className: "taskon-user-center-error__message", children: error.message }),
|
|
136
|
+
onRetry && /* @__PURE__ */ jsx(
|
|
137
|
+
"button",
|
|
138
|
+
{
|
|
139
|
+
type: "button",
|
|
140
|
+
className: "taskon-user-center-error__retry",
|
|
141
|
+
onClick: onRetry,
|
|
142
|
+
children: messages.retry
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
if (!loading && data.length === 0) {
|
|
148
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.emptyActivityHistory });
|
|
149
|
+
}
|
|
150
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-activity-history", children: [
|
|
151
|
+
/* @__PURE__ */ jsx(
|
|
152
|
+
Table,
|
|
153
|
+
{
|
|
154
|
+
columns,
|
|
155
|
+
data,
|
|
156
|
+
rowConfig,
|
|
157
|
+
className: "taskon-activity-history__table",
|
|
158
|
+
compact: true,
|
|
159
|
+
loading,
|
|
160
|
+
loadingText: messages.loading
|
|
161
|
+
}
|
|
162
|
+
),
|
|
163
|
+
mode === "pagination" ? /* @__PURE__ */ jsx(
|
|
164
|
+
Pagination,
|
|
165
|
+
{
|
|
166
|
+
page: pagination.page,
|
|
167
|
+
totalPages: pagination.totalPages,
|
|
168
|
+
onPrevious: pagination.goToPrevious,
|
|
169
|
+
onNext: pagination.goToNext,
|
|
170
|
+
hasPrevious: pagination.hasPrevious,
|
|
171
|
+
hasNext: pagination.hasNext,
|
|
172
|
+
messages
|
|
173
|
+
}
|
|
174
|
+
) : pagination.hasMore && /* @__PURE__ */ jsx("div", { className: "taskon-activity-history__load-more", children: /* @__PURE__ */ jsx(
|
|
175
|
+
"button",
|
|
176
|
+
{
|
|
177
|
+
type: "button",
|
|
178
|
+
className: "taskon-activity-history__load-more-btn",
|
|
179
|
+
onClick: pagination.loadMore,
|
|
180
|
+
disabled: loading,
|
|
181
|
+
children: loading ? messages.loading : messages.loadMore
|
|
182
|
+
}
|
|
183
|
+
) })
|
|
184
|
+
] });
|
|
185
|
+
}
|
|
186
|
+
function getCardTitle(type, messages, pointsData) {
|
|
187
|
+
switch (type) {
|
|
188
|
+
case UserCenterRewardCardType.Token:
|
|
189
|
+
return messages.rewardToken;
|
|
190
|
+
case UserCenterRewardCardType.Nft:
|
|
191
|
+
return messages.rewardNft;
|
|
192
|
+
case UserCenterRewardCardType.Whitelist:
|
|
193
|
+
return messages.rewardWhitelist;
|
|
194
|
+
case UserCenterRewardCardType.DiscordRole:
|
|
195
|
+
return messages.rewardDiscordRole;
|
|
196
|
+
case UserCenterRewardCardType.Points:
|
|
197
|
+
return (pointsData == null ? void 0 : pointsData.points_name) ?? messages.rewardPoints;
|
|
198
|
+
case UserCenterRewardCardType.XpLevel:
|
|
199
|
+
return messages.rewardXpLevel;
|
|
200
|
+
default:
|
|
201
|
+
return type;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function getCardIconUrl(type, pointsData) {
|
|
205
|
+
if (type === UserCenterRewardCardType.Points && (pointsData == null ? void 0 : pointsData.points_icon)) {
|
|
206
|
+
return pointsData.points_icon;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
function TokenIcon() {
|
|
211
|
+
return /* @__PURE__ */ jsxs("svg", { width: "25", height: "28", viewBox: "0 0 25 28", fill: "none", children: [
|
|
212
|
+
/* @__PURE__ */ jsx("path", { d: "M12.5 2L3 10H22L12.5 2Z", fill: "url(#token-gem-top)" }),
|
|
213
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10L12.5 26L12.5 10H3Z", fill: "url(#token-gem-left)" }),
|
|
214
|
+
/* @__PURE__ */ jsx("path", { d: "M22 10L12.5 26L12.5 10H22Z", fill: "url(#token-gem-right)" }),
|
|
215
|
+
/* @__PURE__ */ jsx("path", { d: "M12.5 2L8 10H17L12.5 2Z", fill: "url(#token-gem-highlight)", fillOpacity: "0.6" }),
|
|
216
|
+
/* @__PURE__ */ jsxs("defs", { children: [
|
|
217
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "token-gem-top", x1: "12.5", y1: "2", x2: "12.5", y2: "10", gradientUnits: "userSpaceOnUse", children: [
|
|
218
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#81D4FA" }),
|
|
219
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#4FC3F7" })
|
|
220
|
+
] }),
|
|
221
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "token-gem-left", x1: "3", y1: "10", x2: "12.5", y2: "26", gradientUnits: "userSpaceOnUse", children: [
|
|
222
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#4FC3F7" }),
|
|
223
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#0288D1" })
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "token-gem-right", x1: "22", y1: "10", x2: "12.5", y2: "26", gradientUnits: "userSpaceOnUse", children: [
|
|
226
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#29B6F6" }),
|
|
227
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#0277BD" })
|
|
228
|
+
] }),
|
|
229
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "token-gem-highlight", x1: "12.5", y1: "2", x2: "12.5", y2: "10", gradientUnits: "userSpaceOnUse", children: [
|
|
230
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#E1F5FE" }),
|
|
231
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#81D4FA" })
|
|
232
|
+
] })
|
|
233
|
+
] })
|
|
234
|
+
] });
|
|
235
|
+
}
|
|
236
|
+
function NftIcon() {
|
|
237
|
+
return /* @__PURE__ */ jsx("span", { className: "taskon-asset-card__icon-nft", children: "NFT" });
|
|
238
|
+
}
|
|
239
|
+
function WhitelistIcon() {
|
|
240
|
+
return /* @__PURE__ */ jsx("span", { className: "taskon-asset-card__icon-wl", children: "WL" });
|
|
241
|
+
}
|
|
242
|
+
function DiscordRoleIcon() {
|
|
243
|
+
return /* @__PURE__ */ jsx("svg", { width: "28", height: "22", viewBox: "0 0 28 22", fill: "none", children: /* @__PURE__ */ jsx(
|
|
244
|
+
"path",
|
|
245
|
+
{
|
|
246
|
+
d: "M23.7 1.84A23.15 23.15 0 0018 0a.09.09 0 00-.09.04c-.25.44-.52 1.01-.71 1.46a21.37 21.37 0 00-6.4 0 14.77 14.77 0 00-.72-1.46.09.09 0 00-.1-.04 23.08 23.08 0 00-5.7 1.84.08.08 0 00-.04.03C.62 7.86-.37 13.68.12 19.41a.1.1 0 00.04.07 23.26 23.26 0 007 3.54.1.1 0 00.1-.04c.54-.74.97-1.52 1.33-2.33a.09.09 0 00-.05-.13 15.32 15.32 0 01-2.19-1.04.1.1 0 01-.01-.15c.15-.11.3-.22.43-.34a.09.09 0 01.09-.01c4.6 2.1 9.57 2.1 14.12 0a.09.09 0 01.1.01c.14.12.28.23.43.34a.1.1 0 01-.01.15c-.7.41-1.42.75-2.19 1.04a.09.09 0 00-.05.13c.38.81.8 1.59 1.32 2.33a.1.1 0 00.1.04 23.17 23.17 0 007.01-3.54.1.1 0 00.04-.07c.58-6.07-.97-11.84-4.13-16.84a.08.08 0 00-.04-.03zM9.36 15.91c-1.39 0-2.52-1.27-2.52-2.83s1.11-2.83 2.52-2.83c1.42 0 2.55 1.29 2.52 2.83 0 1.56-1.11 2.83-2.52 2.83zm9.32 0c-1.39 0-2.52-1.27-2.52-2.83s1.11-2.83 2.52-2.83c1.42 0 2.55 1.29 2.52 2.83 0 1.56-1.1 2.83-2.52 2.83z",
|
|
247
|
+
fill: "#ffffff"
|
|
248
|
+
}
|
|
249
|
+
) });
|
|
250
|
+
}
|
|
251
|
+
function XpLevelIcon() {
|
|
252
|
+
return /* @__PURE__ */ jsxs("svg", { width: "26", height: "26", viewBox: "0 0 26 26", fill: "none", children: [
|
|
253
|
+
/* @__PURE__ */ jsx(
|
|
254
|
+
"path",
|
|
255
|
+
{
|
|
256
|
+
d: "M13 2L15.9 8.9L23 9.5L17.5 14.4L19.1 21.5L13 17.8L6.9 21.5L8.5 14.4L3 9.5L10.1 8.9L13 2Z",
|
|
257
|
+
fill: "url(#xp-gradient)",
|
|
258
|
+
stroke: "#F59E0B",
|
|
259
|
+
strokeWidth: "1.5",
|
|
260
|
+
strokeLinejoin: "round"
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: "xp-gradient", x1: "13", y1: "2", x2: "13", y2: "21.5", gradientUnits: "userSpaceOnUse", children: [
|
|
264
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#FDE68A" }),
|
|
265
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#F59E0B" })
|
|
266
|
+
] }) })
|
|
267
|
+
] });
|
|
268
|
+
}
|
|
269
|
+
function PointsIcon() {
|
|
270
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [
|
|
271
|
+
/* @__PURE__ */ jsx(
|
|
272
|
+
"path",
|
|
273
|
+
{
|
|
274
|
+
d: "M12 2L14.4 8.6L21.5 9.2L16.2 13.8L17.8 20.8L12 17.1L6.2 20.8L7.8 13.8L2.5 9.2L9.6 8.6L12 2Z",
|
|
275
|
+
fill: "url(#points-gradient)"
|
|
276
|
+
}
|
|
277
|
+
),
|
|
278
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: "points-gradient", x1: "12", y1: "2", x2: "12", y2: "20.8", gradientUnits: "userSpaceOnUse", children: [
|
|
279
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#A78BFA" }),
|
|
280
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#7C3AED" })
|
|
281
|
+
] }) })
|
|
282
|
+
] });
|
|
283
|
+
}
|
|
284
|
+
function DefaultIcon({
|
|
285
|
+
type
|
|
286
|
+
}) {
|
|
287
|
+
switch (type) {
|
|
288
|
+
case UserCenterRewardCardType.Token:
|
|
289
|
+
return /* @__PURE__ */ jsx(TokenIcon, {});
|
|
290
|
+
case UserCenterRewardCardType.Nft:
|
|
291
|
+
return /* @__PURE__ */ jsx(NftIcon, {});
|
|
292
|
+
case UserCenterRewardCardType.Whitelist:
|
|
293
|
+
return /* @__PURE__ */ jsx(WhitelistIcon, {});
|
|
294
|
+
case UserCenterRewardCardType.DiscordRole:
|
|
295
|
+
return /* @__PURE__ */ jsx(DiscordRoleIcon, {});
|
|
296
|
+
case UserCenterRewardCardType.Points:
|
|
297
|
+
return /* @__PURE__ */ jsx(PointsIcon, {});
|
|
298
|
+
case UserCenterRewardCardType.XpLevel:
|
|
299
|
+
return /* @__PURE__ */ jsx(XpLevelIcon, {});
|
|
300
|
+
default:
|
|
301
|
+
return /* @__PURE__ */ jsx(PointsIcon, {});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function needsLightBackground(type) {
|
|
305
|
+
return type === UserCenterRewardCardType.Whitelist;
|
|
306
|
+
}
|
|
307
|
+
function AssetCard({
|
|
308
|
+
type,
|
|
309
|
+
value,
|
|
310
|
+
selected = false,
|
|
311
|
+
onClick,
|
|
312
|
+
messages,
|
|
313
|
+
pointsData
|
|
314
|
+
}) {
|
|
315
|
+
const title = getCardTitle(type, messages, pointsData);
|
|
316
|
+
const iconUrl = getCardIconUrl(type, pointsData);
|
|
317
|
+
const iconClassName = `taskon-asset-card__icon${needsLightBackground(type) ? " taskon-asset-card__icon--light" : ""}`;
|
|
318
|
+
return /* @__PURE__ */ jsxs(
|
|
319
|
+
"button",
|
|
320
|
+
{
|
|
321
|
+
type: "button",
|
|
322
|
+
className: `taskon-asset-card ${selected ? "taskon-asset-card--selected" : ""}`,
|
|
323
|
+
onClick,
|
|
324
|
+
"aria-pressed": selected,
|
|
325
|
+
children: [
|
|
326
|
+
/* @__PURE__ */ jsx("div", { className: iconClassName, children: iconUrl ? /* @__PURE__ */ jsx("img", { src: iconUrl, alt: title, className: "taskon-asset-card__icon-img" }) : /* @__PURE__ */ jsx(DefaultIcon, { type }) }),
|
|
327
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-asset-card__content", children: [
|
|
328
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-asset-card__title", children: title }),
|
|
329
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-asset-card__value", children: value })
|
|
330
|
+
] })
|
|
331
|
+
]
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
function AssetCarousel({
|
|
336
|
+
cards,
|
|
337
|
+
selectedCard,
|
|
338
|
+
selectedPointsId,
|
|
339
|
+
onSelectCard,
|
|
340
|
+
messages
|
|
341
|
+
}) {
|
|
342
|
+
const containerRef = useRef(null);
|
|
343
|
+
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
|
344
|
+
const [showRightArrow, setShowRightArrow] = useState(false);
|
|
345
|
+
const visibleCards = cards.filter((card) => card.visible);
|
|
346
|
+
const checkScrollArrows = useCallback(() => {
|
|
347
|
+
const container = containerRef.current;
|
|
348
|
+
if (!container) return;
|
|
349
|
+
const { scrollLeft, scrollWidth, clientWidth } = container;
|
|
350
|
+
setShowLeftArrow(scrollLeft > 0);
|
|
351
|
+
setShowRightArrow(scrollLeft + clientWidth < scrollWidth - 1);
|
|
352
|
+
}, []);
|
|
353
|
+
useCallback(
|
|
354
|
+
(direction) => {
|
|
355
|
+
const container = containerRef.current;
|
|
356
|
+
if (!container) return;
|
|
357
|
+
const scrollAmount = 200;
|
|
358
|
+
const newScrollLeft = direction === "left" ? container.scrollLeft - scrollAmount : container.scrollLeft + scrollAmount;
|
|
359
|
+
container.scrollTo({
|
|
360
|
+
left: newScrollLeft,
|
|
361
|
+
behavior: "smooth"
|
|
362
|
+
});
|
|
363
|
+
},
|
|
364
|
+
[]
|
|
365
|
+
);
|
|
366
|
+
const isCardSelected = useCallback(
|
|
367
|
+
(card) => {
|
|
368
|
+
if (selectedCard !== card.type) return false;
|
|
369
|
+
if (card.type === "Points" && card.pointsData) {
|
|
370
|
+
return selectedPointsId === card.pointsData.points_id;
|
|
371
|
+
}
|
|
372
|
+
return true;
|
|
373
|
+
},
|
|
374
|
+
[selectedCard, selectedPointsId]
|
|
375
|
+
);
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
const container = containerRef.current;
|
|
378
|
+
if (!container) return;
|
|
379
|
+
checkScrollArrows();
|
|
380
|
+
container.addEventListener("scroll", checkScrollArrows);
|
|
381
|
+
window.addEventListener("resize", checkScrollArrows);
|
|
382
|
+
return () => {
|
|
383
|
+
container.removeEventListener("scroll", checkScrollArrows);
|
|
384
|
+
window.removeEventListener("resize", checkScrollArrows);
|
|
385
|
+
};
|
|
386
|
+
}, [checkScrollArrows, visibleCards]);
|
|
387
|
+
if (visibleCards.length === 0) {
|
|
388
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-asset-carousel--empty" });
|
|
389
|
+
}
|
|
390
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-asset-carousel", children: [
|
|
391
|
+
/* @__PURE__ */ jsx("h3", { className: "taskon-asset-carousel__title", children: messages.sectionYouHaveEarned ?? "You have earned" }),
|
|
392
|
+
/* @__PURE__ */ jsx("div", { ref: containerRef, className: "taskon-asset-carousel__container", children: visibleCards.map((card, index) => {
|
|
393
|
+
var _a;
|
|
394
|
+
return /* @__PURE__ */ jsx(
|
|
395
|
+
AssetCard,
|
|
396
|
+
{
|
|
397
|
+
type: card.type,
|
|
398
|
+
value: card.value,
|
|
399
|
+
selected: isCardSelected(card),
|
|
400
|
+
onClick: () => {
|
|
401
|
+
var _a2;
|
|
402
|
+
return onSelectCard(card.type, (_a2 = card.pointsData) == null ? void 0 : _a2.points_id);
|
|
403
|
+
},
|
|
404
|
+
messages,
|
|
405
|
+
pointsData: card.pointsData
|
|
406
|
+
},
|
|
407
|
+
`${card.type}-${((_a = card.pointsData) == null ? void 0 : _a.points_id) ?? index}`
|
|
408
|
+
);
|
|
409
|
+
}) })
|
|
410
|
+
] });
|
|
411
|
+
}
|
|
412
|
+
function ProgressBar({
|
|
413
|
+
value,
|
|
414
|
+
max,
|
|
415
|
+
showPercent = false,
|
|
416
|
+
showValues = false,
|
|
417
|
+
height = 8,
|
|
418
|
+
className = "",
|
|
419
|
+
color,
|
|
420
|
+
backgroundColor
|
|
421
|
+
}) {
|
|
422
|
+
const percent = max > 0 ? Math.min(100, Math.max(0, value / max * 100)) : 0;
|
|
423
|
+
const formatValue = (val) => {
|
|
424
|
+
if (val >= 1e6) {
|
|
425
|
+
return `${(val / 1e6).toFixed(1)}M`;
|
|
426
|
+
}
|
|
427
|
+
if (val >= 1e3) {
|
|
428
|
+
return `${(val / 1e3).toFixed(1)}K`;
|
|
429
|
+
}
|
|
430
|
+
return val.toLocaleString();
|
|
431
|
+
};
|
|
432
|
+
const containerStyle = {
|
|
433
|
+
height: `${height}px`,
|
|
434
|
+
...backgroundColor && { backgroundColor }
|
|
435
|
+
};
|
|
436
|
+
const fillStyle = {
|
|
437
|
+
width: `${percent}%`,
|
|
438
|
+
...color && { backgroundColor: color }
|
|
439
|
+
};
|
|
440
|
+
return /* @__PURE__ */ jsxs("div", { className: `taskon-progress-bar ${className}`, children: [
|
|
441
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-progress-bar__container", style: containerStyle, children: /* @__PURE__ */ jsx("div", { className: "taskon-progress-bar__fill", style: fillStyle }) }),
|
|
442
|
+
(showPercent || showValues) && /* @__PURE__ */ jsxs("div", { className: "taskon-progress-bar__info", children: [
|
|
443
|
+
showValues && /* @__PURE__ */ jsxs("span", { className: "taskon-progress-bar__values", children: [
|
|
444
|
+
formatValue(value),
|
|
445
|
+
" / ",
|
|
446
|
+
formatValue(max)
|
|
447
|
+
] }),
|
|
448
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: "taskon-progress-bar__percent", children: [
|
|
449
|
+
percent.toFixed(0),
|
|
450
|
+
"%"
|
|
451
|
+
] })
|
|
452
|
+
] })
|
|
453
|
+
] });
|
|
454
|
+
}
|
|
455
|
+
function formatNumber(val) {
|
|
456
|
+
if (Math.abs(val) >= 1e6) {
|
|
457
|
+
return `${(val / 1e6).toFixed(1)}M`;
|
|
458
|
+
}
|
|
459
|
+
if (Math.abs(val) >= 1e3) {
|
|
460
|
+
return `${(val / 1e3).toFixed(1)}K`;
|
|
461
|
+
}
|
|
462
|
+
return val.toLocaleString();
|
|
463
|
+
}
|
|
464
|
+
function XpLevelCard({
|
|
465
|
+
xpData,
|
|
466
|
+
historyData,
|
|
467
|
+
historyLoading,
|
|
468
|
+
historyError,
|
|
469
|
+
pagination,
|
|
470
|
+
messages,
|
|
471
|
+
className = ""
|
|
472
|
+
}) {
|
|
473
|
+
const { level, currentXp, nextLevelXp, totalXp } = xpData;
|
|
474
|
+
const xpToNext = Math.max(0, nextLevelXp - currentXp);
|
|
475
|
+
const columns = [
|
|
476
|
+
{
|
|
477
|
+
key: "detail",
|
|
478
|
+
title: messages.columnDetail,
|
|
479
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-points-list__name", children: row.name })
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
key: "time",
|
|
483
|
+
title: messages.columnTime,
|
|
484
|
+
width: 160,
|
|
485
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-points-list__time", children: formatDateTime(row.receive_time) })
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
key: "amount",
|
|
489
|
+
title: `${messages.columnAmount} (XP)`,
|
|
490
|
+
width: 160,
|
|
491
|
+
align: "right",
|
|
492
|
+
render: (_, row) => {
|
|
493
|
+
const isPositive = row.points.amount >= 0;
|
|
494
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-points-list__amount-cell", children: [
|
|
495
|
+
row.points.points_icon && /* @__PURE__ */ jsx(
|
|
496
|
+
"img",
|
|
497
|
+
{
|
|
498
|
+
src: row.points.points_icon,
|
|
499
|
+
alt: "",
|
|
500
|
+
className: "taskon-points-list__amount-icon"
|
|
501
|
+
}
|
|
502
|
+
),
|
|
503
|
+
/* @__PURE__ */ jsxs(
|
|
504
|
+
"span",
|
|
505
|
+
{
|
|
506
|
+
className: `taskon-points-list__amount-value ${isPositive ? "taskon-points-list__amount-value--positive" : "taskon-points-list__amount-value--negative"}`,
|
|
507
|
+
children: [
|
|
508
|
+
isPositive ? "+" : "",
|
|
509
|
+
formatNumber(row.points.amount)
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
] });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
];
|
|
517
|
+
return /* @__PURE__ */ jsxs("div", { className: `taskon-xp-level-card ${className}`, children: [
|
|
518
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__header", children: [
|
|
519
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__level", children: [
|
|
520
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-xp-level-card__level-badge", children: level }),
|
|
521
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__level-info", children: [
|
|
522
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-xp-level-card__level-label", children: messages.level }),
|
|
523
|
+
/* @__PURE__ */ jsxs("span", { className: "taskon-xp-level-card__level-value", children: [
|
|
524
|
+
messages.level,
|
|
525
|
+
" ",
|
|
526
|
+
level
|
|
527
|
+
] })
|
|
528
|
+
] })
|
|
529
|
+
] }),
|
|
530
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__xp", children: [
|
|
531
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-xp-level-card__xp-label", children: messages.totalXp }),
|
|
532
|
+
/* @__PURE__ */ jsxs("span", { className: "taskon-xp-level-card__xp-value", children: [
|
|
533
|
+
formatNumber(totalXp),
|
|
534
|
+
" XP"
|
|
535
|
+
] })
|
|
536
|
+
] })
|
|
537
|
+
] }),
|
|
538
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__progress", children: [
|
|
539
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__progress-label", children: [
|
|
540
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
541
|
+
formatNumber(currentXp),
|
|
542
|
+
" / ",
|
|
543
|
+
formatNumber(nextLevelXp),
|
|
544
|
+
" XP"
|
|
545
|
+
] }),
|
|
546
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
547
|
+
messages.xpToNextLevel,
|
|
548
|
+
": ",
|
|
549
|
+
formatNumber(xpToNext)
|
|
550
|
+
] })
|
|
551
|
+
] }),
|
|
552
|
+
/* @__PURE__ */ jsx(ProgressBar, { value: currentXp, max: nextLevelXp, height: 10 })
|
|
553
|
+
] }),
|
|
554
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-xp-level-card__history", children: [
|
|
555
|
+
/* @__PURE__ */ jsx("h4", { className: "taskon-xp-level-card__history-title", children: messages.xpHistory }),
|
|
556
|
+
historyLoading && historyData.length === 0 && /* @__PURE__ */ jsx(LoadingState, { message: messages.loading }),
|
|
557
|
+
historyError && historyData.length === 0 && /* @__PURE__ */ jsx("div", { className: "taskon-user-center-error", children: /* @__PURE__ */ jsx("p", { className: "taskon-user-center-error__message", children: historyError.message }) }),
|
|
558
|
+
!historyLoading && !historyError && historyData.length === 0 && /* @__PURE__ */ jsx(EmptyState, { message: messages.noData }),
|
|
559
|
+
historyData.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
560
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-xp-level-card__table-wrap", children: /* @__PURE__ */ jsx(
|
|
561
|
+
Table,
|
|
562
|
+
{
|
|
563
|
+
columns,
|
|
564
|
+
data: historyData,
|
|
565
|
+
rowConfig: {
|
|
566
|
+
getRowKey: (row, index) => `${row.campaign_id ?? row.task_id ?? "item"}-${row.receive_time}-${index}`
|
|
567
|
+
},
|
|
568
|
+
loading: historyLoading && historyData.length > 0,
|
|
569
|
+
loadingText: messages.loading,
|
|
570
|
+
empty: {
|
|
571
|
+
title: messages.noData
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
) }),
|
|
575
|
+
pagination.totalPages > 1 && /* @__PURE__ */ jsx(
|
|
576
|
+
Pagination,
|
|
577
|
+
{
|
|
578
|
+
page: pagination.page,
|
|
579
|
+
totalPages: pagination.totalPages,
|
|
580
|
+
onPrevious: pagination.goToPrevious,
|
|
581
|
+
onNext: pagination.goToNext,
|
|
582
|
+
hasPrevious: pagination.hasPrevious,
|
|
583
|
+
hasNext: pagination.hasNext,
|
|
584
|
+
messages
|
|
585
|
+
}
|
|
586
|
+
)
|
|
587
|
+
] })
|
|
588
|
+
] })
|
|
589
|
+
] });
|
|
590
|
+
}
|
|
591
|
+
const DEFAULT_WHITELIST_IMAGE = "data:image/svg+xml,%3Csvg width='80' height='81' viewBox='0 0 80 81' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cmask id='mask0_7084_12942' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='80' height='81'%3E%3Crect y='0.00146484' width='80' height='80' rx='4' fill='%23CBFF01'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_7084_12942)'%3E%3Crect width='80' height='80' rx='4' fill='url(%23paint0_linear_7084_12942)'/%3E%3Cpath d='M19.416 20.3864C5.81087 25.7182 -0.895974 41.0695 4.4358 54.6746C9.76758 68.2797 25.119 74.9866 38.724 69.6548L60.3663 61.1733C73.9714 55.8415 80.6783 40.4901 75.3465 26.885C70.0147 13.2799 54.6634 6.57309 41.0583 11.9049L19.416 20.3864Z' fill='%2300FFA3'/%3E%3Cpath d='M53.1374 21.1072H26.6534C15.7884 21.1072 6.98047 29.915 6.98047 40.7801C6.98047 51.6451 15.7884 60.4529 26.6534 60.4529H53.1374C64.0025 60.4529 72.8104 51.6451 72.8104 40.7801C72.8104 29.915 64.0025 21.1072 53.1374 21.1072Z' fill='black'/%3E%3Cpath d='M17.5691 43.286L14.8984 34.8704H16.9994L18.9223 41.3513H18.0083L19.9074 34.8704H21.6048L23.504 41.3513H22.6019L24.5247 34.8704H26.6138L23.9313 43.286H22.222L20.3229 36.817H21.1775L19.2902 43.286H17.5691Z' fill='white'/%3E%3Cpath d='M31.1209 43.286V40.01C31.1209 39.7409 31.0418 39.5312 30.8836 39.3809C30.7253 39.2226 30.5235 39.1435 30.2782 39.1435C30.112 39.1435 29.9617 39.1791 29.8272 39.2503C29.7005 39.3215 29.6016 39.4205 29.5304 39.5471C29.4592 39.6737 29.4236 39.828 29.4236 40.01L28.6164 39.6302C28.6164 39.1633 28.7114 38.7637 28.9013 38.4313C29.0991 38.0911 29.3682 37.8299 29.7085 37.6479C30.0487 37.458 30.4444 37.363 30.8954 37.363C31.3623 37.363 31.7659 37.458 32.1061 37.6479C32.4543 37.8299 32.7194 38.0831 32.9014 38.4076C33.0913 38.732 33.1863 39.1079 33.1863 39.5352V43.286H31.1209ZM27.3583 43.286V34.633H29.4236V43.286H27.3583Z' fill='white'/%3E%3Cpath d='M34.2665 43.286V37.4817H36.3437V43.286H34.2665ZM35.2992 36.8052C34.9906 36.8052 34.7334 36.7023 34.5276 36.4966C34.3219 36.2829 34.219 36.0178 34.219 35.7013C34.219 35.3927 34.3219 35.1315 34.5276 34.9179C34.7334 34.7042 34.9906 34.5974 35.2992 34.5974C35.6236 34.5974 35.8847 34.7042 36.0826 34.9179C36.2883 35.1315 36.3912 35.3927 36.3912 35.7013C36.3912 36.0178 36.2883 36.2829 36.0826 36.4966C35.8847 36.7023 35.6236 36.8052 35.2992 36.8052Z' fill='white'/%3E%3Cpath d='M38.3424 43.286V35.0841H40.4078V43.286H38.3424ZM37.0605 39.2028V37.4817H41.6897V39.2028H37.0605Z' fill='white'/%3E%3Cpath d='M45.2547 43.4166C44.6058 43.4166 44.0321 43.29 43.5336 43.0368C43.035 42.7756 42.6433 42.4156 42.3585 41.9566C42.0736 41.4977 41.9312 40.9714 41.9312 40.3779C41.9312 39.7924 42.0696 39.2741 42.3466 38.823C42.6236 38.3641 42.9994 38.004 43.4742 37.7429C43.9569 37.4738 44.499 37.3393 45.1004 37.3393C45.6859 37.3393 46.2042 37.4659 46.6553 37.7191C47.1143 37.9644 47.4703 38.3087 47.7236 38.7518C47.9847 39.187 48.1153 39.6855 48.1153 40.2474C48.1153 40.3661 48.1073 40.4887 48.0915 40.6153C48.0836 40.734 48.0599 40.8725 48.0203 41.0308L42.9045 41.0545V39.7251L47.2013 39.6895L46.2755 40.2711C46.2675 39.9467 46.2201 39.6816 46.133 39.4758C46.046 39.2622 45.9194 39.1 45.7532 38.9892C45.587 38.8784 45.3773 38.823 45.1241 38.823C44.863 38.823 44.6374 38.8863 44.4475 39.0129C44.2576 39.1395 44.1112 39.3176 44.0084 39.5471C43.9055 39.7765 43.854 40.0535 43.854 40.3779C43.854 40.7103 43.9094 40.9952 44.0202 41.2326C44.131 41.462 44.2893 41.6361 44.495 41.7548C44.7008 41.8735 44.954 41.9329 45.2547 41.9329C45.5395 41.9329 45.7888 41.8854 46.0025 41.7904C46.224 41.6955 46.4219 41.5491 46.5959 41.3513L47.6761 42.4314C47.3912 42.7558 47.043 43.0011 46.6316 43.1673C46.228 43.3335 45.769 43.4166 45.2547 43.4166Z' fill='white'/%3E%3Cpath d='M48.8952 43.286V34.633H50.9606V43.286H48.8952Z' fill='white'/%3E%3Cpath d='M52.1637 43.286V37.4817H54.2409V43.286H52.1637ZM53.1964 36.8052C52.8878 36.8052 52.6306 36.7023 52.4249 36.4966C52.2191 36.2829 52.1163 36.0178 52.1163 35.7013C52.1163 35.3927 52.2191 35.1315 52.4249 34.9179C52.6306 34.7042 52.8878 34.5974 53.1964 34.5974C53.5208 34.5974 53.782 34.7042 53.9798 34.9179C54.1855 35.1315 54.2884 35.3927 54.2884 35.7013C54.2884 36.0178 54.1855 36.2829 53.9798 36.4966C53.782 36.7023 53.5208 36.8052 53.1964 36.8052Z' fill='white'/%3E%3Cpath d='M57.6522 43.4522C57.2961 43.4522 56.944 43.4047 56.5958 43.3098C56.2555 43.2148 55.935 43.0882 55.6343 42.9299C55.3415 42.7638 55.0923 42.5738 54.8865 42.3602L56.0142 41.2088C56.2041 41.4067 56.4296 41.561 56.6907 41.6717C56.9598 41.7825 57.2446 41.8379 57.5453 41.8379C57.7194 41.8379 57.85 41.8142 57.937 41.7667C58.032 41.7192 58.0795 41.652 58.0795 41.5649C58.0795 41.4383 58.0122 41.3433 57.8777 41.28C57.7511 41.2088 57.581 41.1495 57.3673 41.102C57.1616 41.0466 56.9439 40.9833 56.7145 40.9121C56.485 40.8329 56.2634 40.7301 56.0498 40.6035C55.8361 40.4769 55.662 40.2988 55.5275 40.0693C55.4009 39.8399 55.3376 39.5471 55.3376 39.191C55.3376 38.8191 55.4365 38.4946 55.6343 38.2177C55.8401 37.9328 56.1249 37.7112 56.4889 37.553C56.8529 37.3868 57.2803 37.3037 57.7709 37.3037C58.2694 37.3037 58.7442 37.3907 59.1952 37.5648C59.6463 37.731 60.0103 37.9842 60.2872 38.3245L59.1478 39.4758C58.9578 39.2622 58.7481 39.1158 58.5187 39.0367C58.2892 38.9496 58.0795 38.9061 57.8896 38.9061C57.7076 38.9061 57.577 38.9338 57.4979 38.9892C57.4187 39.0367 57.3792 39.1039 57.3792 39.191C57.3792 39.2938 57.4425 39.3769 57.5691 39.4402C57.7036 39.5035 57.8737 39.5629 58.0795 39.6183C58.2931 39.6658 58.5107 39.7291 58.7323 39.8082C58.9618 39.8873 59.1794 39.9981 59.3851 40.1406C59.5988 40.2751 59.7689 40.4571 59.8955 40.6866C60.0301 40.916 60.0973 41.2128 60.0973 41.5768C60.0973 42.1544 59.8758 42.6134 59.4326 42.9537C58.9895 43.286 58.396 43.4522 57.6522 43.4522Z' fill='white'/%3E%3Cpath d='M61.8268 43.286V35.0841H63.8921V43.286H61.8268ZM60.5449 39.2028V37.4817H65.174V39.2028H60.5449Z' fill='white'/%3E%3Cpath d='M47.7919 52.0511C47.7919 52.0511 44.2559 55.8169 39.894 55.8169C35.5321 55.8169 31.9961 52.0511 31.9961 52.0511C31.9961 52.0511 35.5321 48.2854 39.8938 48.2854C44.2556 48.2854 47.7919 52.0511 47.7919 52.0511Z' fill='%2300FFA3'/%3E%3Cpath d='M38.8667 54.9634L36.1367 52.2334L37.0466 51.3234L38.8667 53.1435L42.6369 49.3734L43.5468 50.2833L38.8667 54.9634Z' fill='black'/%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_7084_12942' x1='-5.83666e-07' y1='33.1271' x2='80.0095' y2='32.9475' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%2300FFA3'/%3E%3Cstop offset='1' stop-color='%23F2FFC1'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E";
|
|
592
|
+
function AssetImage({
|
|
593
|
+
src,
|
|
594
|
+
chain,
|
|
595
|
+
alt = "",
|
|
596
|
+
size = 60,
|
|
597
|
+
className = "",
|
|
598
|
+
useWhitelistDefault = false
|
|
599
|
+
}) {
|
|
600
|
+
const { chainMap } = useChainMap();
|
|
601
|
+
const chainInfo = chain ? chainMap[chain.toLowerCase()] : void 0;
|
|
602
|
+
const imageSrc = src || (useWhitelistDefault ? DEFAULT_WHITELIST_IMAGE : "");
|
|
603
|
+
return /* @__PURE__ */ jsxs(
|
|
604
|
+
"div",
|
|
605
|
+
{
|
|
606
|
+
className: `taskon-asset-image ${className}`,
|
|
607
|
+
style: { width: size, height: size },
|
|
608
|
+
children: [
|
|
609
|
+
imageSrc ? /* @__PURE__ */ jsx(
|
|
610
|
+
"img",
|
|
611
|
+
{
|
|
612
|
+
src: imageSrc,
|
|
613
|
+
alt,
|
|
614
|
+
className: "taskon-asset-image__main"
|
|
615
|
+
}
|
|
616
|
+
) : /* @__PURE__ */ jsx("div", { className: "taskon-asset-image__placeholder" }),
|
|
617
|
+
(chainInfo == null ? void 0 : chainInfo.icon) && /* @__PURE__ */ jsx(
|
|
618
|
+
"img",
|
|
619
|
+
{
|
|
620
|
+
src: chainInfo.icon,
|
|
621
|
+
alt: chainInfo.label || chain,
|
|
622
|
+
className: "taskon-asset-image__chain"
|
|
623
|
+
}
|
|
624
|
+
)
|
|
625
|
+
]
|
|
626
|
+
}
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
function NftTable({
|
|
630
|
+
data,
|
|
631
|
+
loading,
|
|
632
|
+
error,
|
|
633
|
+
pagination,
|
|
634
|
+
messages,
|
|
635
|
+
onClaim,
|
|
636
|
+
onTxClick
|
|
637
|
+
}) {
|
|
638
|
+
if (loading && data.length === 0) {
|
|
639
|
+
return /* @__PURE__ */ jsx(LoadingState, { message: messages.loading });
|
|
640
|
+
}
|
|
641
|
+
if (error && data.length === 0) {
|
|
642
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-user-center-error", children: /* @__PURE__ */ jsx("p", { className: "taskon-user-center-error__message", children: error.message }) });
|
|
643
|
+
}
|
|
644
|
+
if (!loading && data.length === 0) {
|
|
645
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.emptyNft });
|
|
646
|
+
}
|
|
647
|
+
const renderStatus = (item) => {
|
|
648
|
+
const nftValue = item.reward_value;
|
|
649
|
+
if (nftValue.tx_hash) {
|
|
650
|
+
return /* @__PURE__ */ jsx(
|
|
651
|
+
"button",
|
|
652
|
+
{
|
|
653
|
+
type: "button",
|
|
654
|
+
className: "taskon-reward-table__link",
|
|
655
|
+
onClick: (e) => {
|
|
656
|
+
e.stopPropagation();
|
|
657
|
+
onTxClick == null ? void 0 : onTxClick(item);
|
|
658
|
+
},
|
|
659
|
+
title: nftValue.tx_hash,
|
|
660
|
+
children: messages.claimedTxn
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
if (nftValue.claimable) {
|
|
665
|
+
return /* @__PURE__ */ jsx(
|
|
666
|
+
"button",
|
|
667
|
+
{
|
|
668
|
+
type: "button",
|
|
669
|
+
className: "taskon-reward-table__claim-btn",
|
|
670
|
+
onClick: (e) => {
|
|
671
|
+
e.stopPropagation();
|
|
672
|
+
onClaim == null ? void 0 : onClaim(item);
|
|
673
|
+
},
|
|
674
|
+
children: messages.claim
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
return /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__manual-drop", children: messages.manualDrop });
|
|
679
|
+
};
|
|
680
|
+
const columns = [
|
|
681
|
+
{
|
|
682
|
+
key: "nft",
|
|
683
|
+
title: messages.columnNft,
|
|
684
|
+
width: 100,
|
|
685
|
+
render: (_, row) => {
|
|
686
|
+
const nftValue = row.reward_value;
|
|
687
|
+
return /* @__PURE__ */ jsx(
|
|
688
|
+
AssetImage,
|
|
689
|
+
{
|
|
690
|
+
src: nftValue.collection_image,
|
|
691
|
+
chain: nftValue.chain,
|
|
692
|
+
alt: nftValue.collection_name,
|
|
693
|
+
size: 60
|
|
694
|
+
}
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
key: "detail",
|
|
700
|
+
title: messages.columnDetail,
|
|
701
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__campaign-name", children: row.campaign_name })
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
key: "time",
|
|
705
|
+
title: messages.columnTime,
|
|
706
|
+
width: 160,
|
|
707
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__time", children: formatDateTime(row.reward_time) })
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
key: "status",
|
|
711
|
+
title: messages.columnStatus,
|
|
712
|
+
width: 120,
|
|
713
|
+
align: "right",
|
|
714
|
+
render: (_, row) => renderStatus(row)
|
|
715
|
+
}
|
|
716
|
+
];
|
|
717
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-reward-table", children: [
|
|
718
|
+
/* @__PURE__ */ jsx(
|
|
719
|
+
Table,
|
|
720
|
+
{
|
|
721
|
+
columns,
|
|
722
|
+
data,
|
|
723
|
+
rowConfig: {
|
|
724
|
+
getRowKey: (row, index) => `${row.campaign_id}-${row.reward_time}-${index}`
|
|
725
|
+
},
|
|
726
|
+
loading: loading && data.length > 0,
|
|
727
|
+
loadingText: messages.loading,
|
|
728
|
+
empty: {
|
|
729
|
+
title: messages.emptyNft
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
),
|
|
733
|
+
pagination.totalPages > 1 && /* @__PURE__ */ jsx(
|
|
734
|
+
Pagination,
|
|
735
|
+
{
|
|
736
|
+
page: pagination.page,
|
|
737
|
+
totalPages: pagination.totalPages,
|
|
738
|
+
onPrevious: pagination.goToPrevious,
|
|
739
|
+
onNext: pagination.goToNext,
|
|
740
|
+
hasPrevious: pagination.hasPrevious,
|
|
741
|
+
hasNext: pagination.hasNext,
|
|
742
|
+
messages
|
|
743
|
+
}
|
|
744
|
+
)
|
|
745
|
+
] });
|
|
746
|
+
}
|
|
747
|
+
function WhitelistTable({
|
|
748
|
+
data,
|
|
749
|
+
loading,
|
|
750
|
+
error,
|
|
751
|
+
pagination,
|
|
752
|
+
messages
|
|
753
|
+
}) {
|
|
754
|
+
if (loading && data.length === 0) {
|
|
755
|
+
return /* @__PURE__ */ jsx(LoadingState, { message: messages.loading });
|
|
756
|
+
}
|
|
757
|
+
if (error && data.length === 0) {
|
|
758
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-user-center-error", children: /* @__PURE__ */ jsx("p", { className: "taskon-user-center-error__message", children: error.message }) });
|
|
759
|
+
}
|
|
760
|
+
if (!loading && data.length === 0) {
|
|
761
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.noData });
|
|
762
|
+
}
|
|
763
|
+
const columns = [
|
|
764
|
+
{
|
|
765
|
+
key: "whitelist",
|
|
766
|
+
title: messages.columnWhitelist,
|
|
767
|
+
width: 100,
|
|
768
|
+
render: (_, row) => {
|
|
769
|
+
const wlValue = row.reward_value;
|
|
770
|
+
return /* @__PURE__ */ jsx(
|
|
771
|
+
AssetImage,
|
|
772
|
+
{
|
|
773
|
+
src: wlValue.nft_image,
|
|
774
|
+
chain: wlValue.chain,
|
|
775
|
+
alt: "Whitelist",
|
|
776
|
+
size: 60,
|
|
777
|
+
useWhitelistDefault: !wlValue.nft_image
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
key: "detail",
|
|
784
|
+
title: messages.columnDetail,
|
|
785
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__campaign-name", children: row.campaign_name })
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
key: "time",
|
|
789
|
+
title: messages.columnTime,
|
|
790
|
+
width: 160,
|
|
791
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__time", children: formatDateTime(row.reward_time) })
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
key: "status",
|
|
795
|
+
title: messages.columnStatus,
|
|
796
|
+
width: 120,
|
|
797
|
+
align: "right",
|
|
798
|
+
render: () => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__manual-drop", children: messages.manualDrop })
|
|
799
|
+
}
|
|
800
|
+
];
|
|
801
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-reward-table", children: [
|
|
802
|
+
/* @__PURE__ */ jsx(
|
|
803
|
+
Table,
|
|
804
|
+
{
|
|
805
|
+
columns,
|
|
806
|
+
data,
|
|
807
|
+
rowConfig: {
|
|
808
|
+
getRowKey: (row, index) => `${row.campaign_id}-${row.reward_time}-${index}`
|
|
809
|
+
},
|
|
810
|
+
loading: loading && data.length > 0,
|
|
811
|
+
loadingText: messages.loading,
|
|
812
|
+
empty: {
|
|
813
|
+
title: messages.noData
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
),
|
|
817
|
+
pagination.totalPages > 1 && /* @__PURE__ */ jsx(
|
|
818
|
+
Pagination,
|
|
819
|
+
{
|
|
820
|
+
page: pagination.page,
|
|
821
|
+
totalPages: pagination.totalPages,
|
|
822
|
+
onPrevious: pagination.goToPrevious,
|
|
823
|
+
onNext: pagination.goToNext,
|
|
824
|
+
hasPrevious: pagination.hasPrevious,
|
|
825
|
+
hasNext: pagination.hasNext,
|
|
826
|
+
messages
|
|
827
|
+
}
|
|
828
|
+
)
|
|
829
|
+
] });
|
|
830
|
+
}
|
|
831
|
+
function DiscordRoleTable({
|
|
832
|
+
data,
|
|
833
|
+
loading,
|
|
834
|
+
error,
|
|
835
|
+
pagination,
|
|
836
|
+
messages
|
|
837
|
+
}) {
|
|
838
|
+
if (loading && data.length === 0) {
|
|
839
|
+
return /* @__PURE__ */ jsx(LoadingState, { message: messages.loading });
|
|
840
|
+
}
|
|
841
|
+
if (error && data.length === 0) {
|
|
842
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-user-center-error", children: /* @__PURE__ */ jsx("p", { className: "taskon-user-center-error__message", children: error.message }) });
|
|
843
|
+
}
|
|
844
|
+
if (!loading && data.length === 0) {
|
|
845
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.noData });
|
|
846
|
+
}
|
|
847
|
+
const columns = [
|
|
848
|
+
{
|
|
849
|
+
key: "discordRole",
|
|
850
|
+
title: messages.columnDiscordRole,
|
|
851
|
+
width: 160,
|
|
852
|
+
render: (_, row) => {
|
|
853
|
+
const dcValue = row.reward_value;
|
|
854
|
+
return /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__role-name", children: dcValue.role_name });
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
key: "detail",
|
|
859
|
+
title: messages.columnDetail,
|
|
860
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__campaign-name", children: row.milestone_name || row.campaign_name })
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
key: "time",
|
|
864
|
+
title: messages.columnTime,
|
|
865
|
+
width: 160,
|
|
866
|
+
render: (_, row) => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__time", children: formatDateTime(row.reward_time) })
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
key: "status",
|
|
870
|
+
title: messages.columnStatus,
|
|
871
|
+
width: 120,
|
|
872
|
+
align: "right",
|
|
873
|
+
render: () => /* @__PURE__ */ jsx("span", { className: "taskon-reward-table__manual-drop", children: messages.manualDrop })
|
|
874
|
+
}
|
|
875
|
+
];
|
|
876
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-reward-table", children: [
|
|
877
|
+
/* @__PURE__ */ jsx(
|
|
878
|
+
Table,
|
|
879
|
+
{
|
|
880
|
+
columns,
|
|
881
|
+
data,
|
|
882
|
+
rowConfig: {
|
|
883
|
+
getRowKey: (row, index) => `${row.campaign_id}-${row.reward_time}-${index}`
|
|
884
|
+
},
|
|
885
|
+
loading: loading && data.length > 0,
|
|
886
|
+
loadingText: messages.loading,
|
|
887
|
+
empty: {
|
|
888
|
+
title: messages.noData
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
),
|
|
892
|
+
pagination.totalPages > 1 && /* @__PURE__ */ jsx(
|
|
893
|
+
Pagination,
|
|
894
|
+
{
|
|
895
|
+
page: pagination.page,
|
|
896
|
+
totalPages: pagination.totalPages,
|
|
897
|
+
onPrevious: pagination.goToPrevious,
|
|
898
|
+
onNext: pagination.goToNext,
|
|
899
|
+
hasPrevious: pagination.hasPrevious,
|
|
900
|
+
hasNext: pagination.hasNext,
|
|
901
|
+
messages
|
|
902
|
+
}
|
|
903
|
+
)
|
|
904
|
+
] });
|
|
905
|
+
}
|
|
906
|
+
function useActivityHistory(options = {}) {
|
|
907
|
+
const {
|
|
908
|
+
pageSize = USER_CENTER_PAGE_SIZE,
|
|
909
|
+
mode = "pagination",
|
|
910
|
+
autoLoad = true
|
|
911
|
+
} = options;
|
|
912
|
+
const { client } = useTaskOnContext();
|
|
913
|
+
const [data, setData] = useState([]);
|
|
914
|
+
const [total, setTotal] = useState(0);
|
|
915
|
+
const [loading, setLoading] = useState(false);
|
|
916
|
+
const [error, setError] = useState(null);
|
|
917
|
+
const isInitialLoad = useRef(true);
|
|
918
|
+
const lastFetchedPage = useRef(null);
|
|
919
|
+
const api = useMemo(() => {
|
|
920
|
+
if (!client) return null;
|
|
921
|
+
return createUserCenterApi(client);
|
|
922
|
+
}, [client]);
|
|
923
|
+
const pagination = usePagination({
|
|
924
|
+
total,
|
|
925
|
+
pageSize,
|
|
926
|
+
initialPage: 0,
|
|
927
|
+
// 使用 0-based 索引
|
|
928
|
+
mode
|
|
929
|
+
});
|
|
930
|
+
const fetchData = useCallback(
|
|
931
|
+
async (pageNo, append = false) => {
|
|
932
|
+
if (!api) {
|
|
933
|
+
setError(new Error("TaskOn client not initialized"));
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (lastFetchedPage.current === pageNo && !append) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
setLoading(true);
|
|
940
|
+
setError(null);
|
|
941
|
+
lastFetchedPage.current = pageNo;
|
|
942
|
+
try {
|
|
943
|
+
const response = await api.getMyCampaignHistory({
|
|
944
|
+
page: {
|
|
945
|
+
page_no: pageNo,
|
|
946
|
+
// API 使用 0-based 索引
|
|
947
|
+
size: pageSize
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
if (append) {
|
|
951
|
+
setData((prev) => [...prev, ...response.data]);
|
|
952
|
+
} else {
|
|
953
|
+
setData(response.data);
|
|
954
|
+
}
|
|
955
|
+
setTotal(response.total);
|
|
956
|
+
} catch (err) {
|
|
957
|
+
setError(
|
|
958
|
+
err instanceof Error ? err : new Error("Failed to fetch activity history")
|
|
959
|
+
);
|
|
960
|
+
} finally {
|
|
961
|
+
setLoading(false);
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
[api, pageSize]
|
|
965
|
+
);
|
|
966
|
+
const refresh = useCallback(async () => {
|
|
967
|
+
pagination.goToPage(0);
|
|
968
|
+
setData([]);
|
|
969
|
+
lastFetchedPage.current = null;
|
|
970
|
+
await fetchData(0, false);
|
|
971
|
+
}, [fetchData, pagination]);
|
|
972
|
+
useEffect(() => {
|
|
973
|
+
if (autoLoad && isInitialLoad.current) {
|
|
974
|
+
isInitialLoad.current = false;
|
|
975
|
+
fetchData(0, false);
|
|
976
|
+
}
|
|
977
|
+
}, [autoLoad, fetchData]);
|
|
978
|
+
useEffect(() => {
|
|
979
|
+
if (isInitialLoad.current) {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (mode === "pagination") {
|
|
983
|
+
fetchData(pagination.page, false);
|
|
984
|
+
}
|
|
985
|
+
}, [mode, pagination.page, fetchData]);
|
|
986
|
+
useEffect(() => {
|
|
987
|
+
if (mode === "infinite" && pagination.loadedCount > pageSize) {
|
|
988
|
+
const nextPage = Math.ceil(pagination.loadedCount / pageSize) - 1;
|
|
989
|
+
fetchData(nextPage, true);
|
|
990
|
+
}
|
|
991
|
+
}, [mode, pagination.loadedCount, pageSize, fetchData]);
|
|
992
|
+
return {
|
|
993
|
+
data,
|
|
994
|
+
loading,
|
|
995
|
+
error,
|
|
996
|
+
total,
|
|
997
|
+
pagination,
|
|
998
|
+
refresh
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
function useUserRewards(options = {}) {
|
|
1002
|
+
const { autoLoad = true } = options;
|
|
1003
|
+
const { client } = useTaskOnContext();
|
|
1004
|
+
const [rewards, setRewards] = useState(null);
|
|
1005
|
+
const [userInfo, setUserInfo] = useState(null);
|
|
1006
|
+
const [selectedCard, setSelectedCard] = useState(null);
|
|
1007
|
+
const [loading, setLoading] = useState(false);
|
|
1008
|
+
const [error, setError] = useState(null);
|
|
1009
|
+
const api = useMemo(() => {
|
|
1010
|
+
if (!client) return null;
|
|
1011
|
+
return createUserCenterApi(client);
|
|
1012
|
+
}, [client]);
|
|
1013
|
+
const cards = useMemo(() => {
|
|
1014
|
+
if (!rewards) return [];
|
|
1015
|
+
return buildRewardCards(rewards, userInfo);
|
|
1016
|
+
}, [rewards, userInfo]);
|
|
1017
|
+
const fetchData = useCallback(async () => {
|
|
1018
|
+
if (!api) {
|
|
1019
|
+
setError(new Error("TaskOn client not initialized"));
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
setLoading(true);
|
|
1023
|
+
setError(null);
|
|
1024
|
+
try {
|
|
1025
|
+
const [rewardsResponse, userInfoResponse] = await Promise.all([
|
|
1026
|
+
api.getMyCommunityRewards({}),
|
|
1027
|
+
api.getCUserInfo({})
|
|
1028
|
+
]);
|
|
1029
|
+
setRewards(rewardsResponse);
|
|
1030
|
+
setUserInfo(userInfoResponse);
|
|
1031
|
+
const builtCards = buildRewardCards(rewardsResponse, userInfoResponse);
|
|
1032
|
+
const firstVisibleCard = builtCards.find((c) => c.visible);
|
|
1033
|
+
if (firstVisibleCard && !selectedCard) {
|
|
1034
|
+
setSelectedCard(firstVisibleCard.type);
|
|
1035
|
+
}
|
|
1036
|
+
} catch (err) {
|
|
1037
|
+
setError(
|
|
1038
|
+
err instanceof Error ? err : new Error("Failed to fetch rewards")
|
|
1039
|
+
);
|
|
1040
|
+
} finally {
|
|
1041
|
+
setLoading(false);
|
|
1042
|
+
}
|
|
1043
|
+
}, [api, selectedCard]);
|
|
1044
|
+
const selectCard = useCallback((type) => {
|
|
1045
|
+
setSelectedCard(type);
|
|
1046
|
+
}, []);
|
|
1047
|
+
const refresh = useCallback(async () => {
|
|
1048
|
+
await fetchData();
|
|
1049
|
+
}, [fetchData]);
|
|
1050
|
+
useEffect(() => {
|
|
1051
|
+
if (autoLoad) {
|
|
1052
|
+
fetchData();
|
|
1053
|
+
}
|
|
1054
|
+
}, [autoLoad, fetchData]);
|
|
1055
|
+
return {
|
|
1056
|
+
rewards,
|
|
1057
|
+
userInfo,
|
|
1058
|
+
cards,
|
|
1059
|
+
selectedCard,
|
|
1060
|
+
selectCard,
|
|
1061
|
+
loading,
|
|
1062
|
+
error,
|
|
1063
|
+
refresh
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
function useGasFreeWithdraw(options = {}) {
|
|
1067
|
+
const {
|
|
1068
|
+
userAddress,
|
|
1069
|
+
onSuccess,
|
|
1070
|
+
onCancel,
|
|
1071
|
+
onError,
|
|
1072
|
+
onConnect,
|
|
1073
|
+
pollingInterval = 3e3,
|
|
1074
|
+
maxPollingAttempts = 100
|
|
1075
|
+
} = options;
|
|
1076
|
+
const { client } = useTaskOnContext();
|
|
1077
|
+
const { evmAddress, connectEvm } = useWallet();
|
|
1078
|
+
const { isLoaded: chainMapLoaded } = useChainMap();
|
|
1079
|
+
const [status, setStatus] = useState("init");
|
|
1080
|
+
const [tokens, setTokens] = useState([]);
|
|
1081
|
+
const [error, setError] = useState(null);
|
|
1082
|
+
const [errorType, setErrorType] = useState(null);
|
|
1083
|
+
const [txHash, setTxHash] = useState(null);
|
|
1084
|
+
const [gasFreeRemaining, setGasFreeRemaining] = useState(null);
|
|
1085
|
+
const [gasFreeFetching, setGasFreeFetching] = useState(false);
|
|
1086
|
+
const [connectedAddress, setConnectedAddress] = useState(null);
|
|
1087
|
+
const pollingTimerRef = useRef(null);
|
|
1088
|
+
const pollingAttemptsRef = useRef(0);
|
|
1089
|
+
const api = useMemo(() => {
|
|
1090
|
+
if (!client) return null;
|
|
1091
|
+
return createUserCenterApi(client);
|
|
1092
|
+
}, [client]);
|
|
1093
|
+
useEffect(() => {
|
|
1094
|
+
return () => {
|
|
1095
|
+
if (pollingTimerRef.current) {
|
|
1096
|
+
clearTimeout(pollingTimerRef.current);
|
|
1097
|
+
pollingTimerRef.current = null;
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
}, []);
|
|
1101
|
+
const refreshGasFreeTimes = useCallback(
|
|
1102
|
+
async (chain) => {
|
|
1103
|
+
if (!api) return;
|
|
1104
|
+
setGasFreeFetching(true);
|
|
1105
|
+
try {
|
|
1106
|
+
const times = await api.getTokenWithdrawGasFreeTimes({ chain });
|
|
1107
|
+
setGasFreeRemaining(times);
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
console.error("Failed to get gas free times:", err);
|
|
1110
|
+
} finally {
|
|
1111
|
+
setGasFreeFetching(false);
|
|
1112
|
+
}
|
|
1113
|
+
},
|
|
1114
|
+
[api]
|
|
1115
|
+
);
|
|
1116
|
+
const pollGasStationResult = useCallback(
|
|
1117
|
+
async (requestId) => {
|
|
1118
|
+
if (!api) return;
|
|
1119
|
+
pollingAttemptsRef.current += 1;
|
|
1120
|
+
try {
|
|
1121
|
+
const result = await api.getGasStationAirdropResult({
|
|
1122
|
+
gas_station_request_id: requestId
|
|
1123
|
+
});
|
|
1124
|
+
switch (result.gas_station_request_status) {
|
|
1125
|
+
case GasStationRequestStatus.Success:
|
|
1126
|
+
setTxHash(result.tx_hash);
|
|
1127
|
+
setStatus("success");
|
|
1128
|
+
onSuccess == null ? void 0 : onSuccess(result.tx_hash);
|
|
1129
|
+
break;
|
|
1130
|
+
case GasStationRequestStatus.Failed:
|
|
1131
|
+
const failError = new Error("Gas Station withdrawal failed");
|
|
1132
|
+
setError(failError);
|
|
1133
|
+
setErrorType(TxErrorType.TransactionFailed);
|
|
1134
|
+
setStatus("init");
|
|
1135
|
+
onError == null ? void 0 : onError(failError);
|
|
1136
|
+
break;
|
|
1137
|
+
case GasStationRequestStatus.Pending:
|
|
1138
|
+
case GasStationRequestStatus.Processing:
|
|
1139
|
+
if (pollingAttemptsRef.current >= maxPollingAttempts) {
|
|
1140
|
+
const timeoutError = new Error(
|
|
1141
|
+
"Withdrawal timeout, please check later"
|
|
1142
|
+
);
|
|
1143
|
+
setError(timeoutError);
|
|
1144
|
+
setErrorType(TxErrorType.TransactionFailed);
|
|
1145
|
+
setStatus("init");
|
|
1146
|
+
onError == null ? void 0 : onError(timeoutError);
|
|
1147
|
+
} else {
|
|
1148
|
+
pollingTimerRef.current = setTimeout(() => {
|
|
1149
|
+
pollGasStationResult(requestId);
|
|
1150
|
+
}, pollingInterval);
|
|
1151
|
+
}
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
} catch (err) {
|
|
1155
|
+
const pollError = err instanceof Error ? err : new Error("Failed to check status");
|
|
1156
|
+
setError(pollError);
|
|
1157
|
+
setErrorType(TxErrorType.TransactionFailed);
|
|
1158
|
+
setStatus("init");
|
|
1159
|
+
onError == null ? void 0 : onError(pollError);
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
[api, maxPollingAttempts, pollingInterval, onSuccess, onError]
|
|
1163
|
+
);
|
|
1164
|
+
const initWithdraw = useCallback(
|
|
1165
|
+
(token) => {
|
|
1166
|
+
if (!token.can_withdraw) {
|
|
1167
|
+
setError(new Error("This token cannot be withdrawn"));
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
setTokens([toGasFreeWithdrawItem(token)]);
|
|
1171
|
+
setError(null);
|
|
1172
|
+
setErrorType(null);
|
|
1173
|
+
setTxHash(null);
|
|
1174
|
+
setConnectedAddress(null);
|
|
1175
|
+
setStatus("init");
|
|
1176
|
+
refreshGasFreeTimes(token.chain);
|
|
1177
|
+
},
|
|
1178
|
+
[refreshGasFreeTimes]
|
|
1179
|
+
);
|
|
1180
|
+
const initBatchWithdraw = useCallback(
|
|
1181
|
+
(tokenList) => {
|
|
1182
|
+
const withdrawableTokens = tokenList.filter((t) => t.can_withdraw);
|
|
1183
|
+
if (withdrawableTokens.length === 0) {
|
|
1184
|
+
setError(new Error("No tokens available for withdrawal"));
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
setTokens(withdrawableTokens.map(toGasFreeWithdrawItem));
|
|
1188
|
+
setError(null);
|
|
1189
|
+
setErrorType(null);
|
|
1190
|
+
setTxHash(null);
|
|
1191
|
+
setConnectedAddress(null);
|
|
1192
|
+
setStatus("init");
|
|
1193
|
+
const firstToken = withdrawableTokens[0];
|
|
1194
|
+
if (firstToken) {
|
|
1195
|
+
refreshGasFreeTimes(firstToken.chain);
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1198
|
+
[refreshGasFreeTimes]
|
|
1199
|
+
);
|
|
1200
|
+
const checkAndConnect = useCallback(async () => {
|
|
1201
|
+
try {
|
|
1202
|
+
let address = evmAddress;
|
|
1203
|
+
if (!address) {
|
|
1204
|
+
address = await connectEvm();
|
|
1205
|
+
if (!address) {
|
|
1206
|
+
throw new TransactionError(
|
|
1207
|
+
TxErrorType.ConnectFailed,
|
|
1208
|
+
"Failed to connect wallet"
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
setConnectedAddress(address);
|
|
1213
|
+
onConnect == null ? void 0 : onConnect(address);
|
|
1214
|
+
if (userAddress && !isSameAddress(address, userAddress)) {
|
|
1215
|
+
throw new TransactionError(
|
|
1216
|
+
TxErrorType.AccountMismatch,
|
|
1217
|
+
`Connected address ${address} does not match target address ${userAddress}`
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
if (gasFreeRemaining !== null && gasFreeRemaining <= 0) {
|
|
1221
|
+
setError(new Error("No free withdrawal remaining"));
|
|
1222
|
+
return false;
|
|
1223
|
+
}
|
|
1224
|
+
setStatus("confirm");
|
|
1225
|
+
return true;
|
|
1226
|
+
} catch (err) {
|
|
1227
|
+
const errType = parseTxError(err);
|
|
1228
|
+
setError(err instanceof Error ? err : new Error("Connection failed"));
|
|
1229
|
+
setErrorType(errType);
|
|
1230
|
+
if (errType === TxErrorType.Canceled) {
|
|
1231
|
+
setStatus("cancel");
|
|
1232
|
+
} else {
|
|
1233
|
+
setStatus("init");
|
|
1234
|
+
}
|
|
1235
|
+
onError == null ? void 0 : onError(err instanceof Error ? err : new Error("Connection failed"));
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
}, [evmAddress, connectEvm, userAddress, gasFreeRemaining, onConnect, onError]);
|
|
1239
|
+
const confirmWithdraw = useCallback(async () => {
|
|
1240
|
+
if (!api) {
|
|
1241
|
+
setError(new Error("TaskOn client not initialized"));
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
if (!connectedAddress) {
|
|
1245
|
+
setError(new Error("Wallet not connected"));
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
if (tokens.length === 0) {
|
|
1249
|
+
setError(new Error("No tokens to withdraw"));
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
if (gasFreeRemaining !== null && gasFreeRemaining <= 0) {
|
|
1253
|
+
setError(new Error("No free withdrawal remaining"));
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
setStatus("loading");
|
|
1257
|
+
setError(null);
|
|
1258
|
+
setErrorType(null);
|
|
1259
|
+
try {
|
|
1260
|
+
const firstToken = tokens[0];
|
|
1261
|
+
if (!firstToken) {
|
|
1262
|
+
throw new Error("No tokens provided");
|
|
1263
|
+
}
|
|
1264
|
+
const chainName = firstToken.chain;
|
|
1265
|
+
const tokenParams = tokens.map((t) => ({
|
|
1266
|
+
token_id: t.tokenId,
|
|
1267
|
+
amount: t.amount
|
|
1268
|
+
}));
|
|
1269
|
+
const requestId = await api.tokenWithdrawFromGasStation({
|
|
1270
|
+
chain: chainName,
|
|
1271
|
+
receiver_address: connectedAddress,
|
|
1272
|
+
tokens: tokenParams
|
|
1273
|
+
});
|
|
1274
|
+
setStatus("polling");
|
|
1275
|
+
pollingAttemptsRef.current = 0;
|
|
1276
|
+
pollGasStationResult(requestId);
|
|
1277
|
+
} catch (err) {
|
|
1278
|
+
const errType = parseTxError(err);
|
|
1279
|
+
const error2 = err instanceof Error ? err : new Error("Withdrawal failed");
|
|
1280
|
+
setError(error2);
|
|
1281
|
+
setErrorType(errType);
|
|
1282
|
+
if (errType === TxErrorType.Canceled) {
|
|
1283
|
+
setStatus("cancel");
|
|
1284
|
+
onCancel == null ? void 0 : onCancel();
|
|
1285
|
+
} else {
|
|
1286
|
+
setStatus("init");
|
|
1287
|
+
onError == null ? void 0 : onError(error2);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}, [api, connectedAddress, tokens, gasFreeRemaining, pollGasStationResult, onCancel, onError]);
|
|
1291
|
+
const cancelWithdraw = useCallback(() => {
|
|
1292
|
+
if (pollingTimerRef.current) {
|
|
1293
|
+
clearTimeout(pollingTimerRef.current);
|
|
1294
|
+
pollingTimerRef.current = null;
|
|
1295
|
+
}
|
|
1296
|
+
setStatus("cancel");
|
|
1297
|
+
onCancel == null ? void 0 : onCancel();
|
|
1298
|
+
setTimeout(() => {
|
|
1299
|
+
setStatus("init");
|
|
1300
|
+
setTokens([]);
|
|
1301
|
+
setError(null);
|
|
1302
|
+
setErrorType(null);
|
|
1303
|
+
setConnectedAddress(null);
|
|
1304
|
+
}, 100);
|
|
1305
|
+
}, [onCancel]);
|
|
1306
|
+
const reset = useCallback(() => {
|
|
1307
|
+
if (pollingTimerRef.current) {
|
|
1308
|
+
clearTimeout(pollingTimerRef.current);
|
|
1309
|
+
pollingTimerRef.current = null;
|
|
1310
|
+
}
|
|
1311
|
+
setStatus("init");
|
|
1312
|
+
setTokens([]);
|
|
1313
|
+
setError(null);
|
|
1314
|
+
setErrorType(null);
|
|
1315
|
+
setTxHash(null);
|
|
1316
|
+
setConnectedAddress(null);
|
|
1317
|
+
pollingAttemptsRef.current = 0;
|
|
1318
|
+
}, []);
|
|
1319
|
+
return {
|
|
1320
|
+
status,
|
|
1321
|
+
tokens,
|
|
1322
|
+
error,
|
|
1323
|
+
errorType,
|
|
1324
|
+
txHash,
|
|
1325
|
+
gasFreeRemaining,
|
|
1326
|
+
chainMapLoaded,
|
|
1327
|
+
gasFreeFetching,
|
|
1328
|
+
connectedAddress,
|
|
1329
|
+
initWithdraw,
|
|
1330
|
+
initBatchWithdraw,
|
|
1331
|
+
checkAndConnect,
|
|
1332
|
+
confirmWithdraw,
|
|
1333
|
+
cancelWithdraw,
|
|
1334
|
+
reset,
|
|
1335
|
+
refreshGasFreeTimes
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
const SNS_TYPE_MAP = {
|
|
1339
|
+
Twitter: "Twitter",
|
|
1340
|
+
Discord: "Discord",
|
|
1341
|
+
Telegram: "Telegram",
|
|
1342
|
+
Email: "Email",
|
|
1343
|
+
Reddit: "Reddit",
|
|
1344
|
+
FSLID: "FSLID"
|
|
1345
|
+
};
|
|
1346
|
+
const CHAIN_TYPE_MAP = {
|
|
1347
|
+
evm: "evm",
|
|
1348
|
+
solana: "solana",
|
|
1349
|
+
sui: "sui",
|
|
1350
|
+
aptos: "aptos",
|
|
1351
|
+
btc: "btc",
|
|
1352
|
+
starknet: "starknet",
|
|
1353
|
+
tron: "tron",
|
|
1354
|
+
ton: "ton",
|
|
1355
|
+
nibiru: "nibiru",
|
|
1356
|
+
kaspa: "kaspa"
|
|
1357
|
+
};
|
|
1358
|
+
const ALL_SOCIAL_TYPES = [
|
|
1359
|
+
SnsType.Twitter,
|
|
1360
|
+
SnsType.Discord,
|
|
1361
|
+
SnsType.Telegram,
|
|
1362
|
+
SnsType.Email,
|
|
1363
|
+
SnsType.Reddit,
|
|
1364
|
+
SnsType.FSLID
|
|
1365
|
+
];
|
|
1366
|
+
const ALL_CHAIN_TYPES = [
|
|
1367
|
+
"evm",
|
|
1368
|
+
"solana",
|
|
1369
|
+
"sui",
|
|
1370
|
+
"aptos",
|
|
1371
|
+
"btc",
|
|
1372
|
+
"starknet",
|
|
1373
|
+
"tron",
|
|
1374
|
+
"ton",
|
|
1375
|
+
"nibiru",
|
|
1376
|
+
"kaspa"
|
|
1377
|
+
];
|
|
1378
|
+
function getSocialProfileUrl(snsType, username) {
|
|
1379
|
+
switch (snsType) {
|
|
1380
|
+
case SnsType.Twitter:
|
|
1381
|
+
return `https://twitter.com/${username}`;
|
|
1382
|
+
case SnsType.Discord:
|
|
1383
|
+
return null;
|
|
1384
|
+
case SnsType.Telegram:
|
|
1385
|
+
return `https://t.me/${username}`;
|
|
1386
|
+
case SnsType.Reddit:
|
|
1387
|
+
return `https://reddit.com/user/${username}`;
|
|
1388
|
+
case SnsType.Email:
|
|
1389
|
+
return null;
|
|
1390
|
+
case SnsType.FSLID:
|
|
1391
|
+
return null;
|
|
1392
|
+
default:
|
|
1393
|
+
return null;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
function useIdentityData() {
|
|
1397
|
+
const { userInfo } = useTaskOnContext();
|
|
1398
|
+
const rawSns = (userInfo == null ? void 0 : userInfo.sns) ?? [];
|
|
1399
|
+
const rawAddresses = (userInfo == null ? void 0 : userInfo.address) ?? [];
|
|
1400
|
+
const isLoaded = userInfo !== null;
|
|
1401
|
+
const socialAccounts = useMemo(() => {
|
|
1402
|
+
return ALL_SOCIAL_TYPES.map((snsType) => {
|
|
1403
|
+
const boundAccount = rawSns.find((s) => s.sns_type === snsType);
|
|
1404
|
+
const accountType = SNS_TYPE_MAP[snsType];
|
|
1405
|
+
return {
|
|
1406
|
+
accountType: accountType ?? snsType,
|
|
1407
|
+
snsType,
|
|
1408
|
+
isBound: !!boundAccount,
|
|
1409
|
+
account: boundAccount ?? null,
|
|
1410
|
+
profileUrl: boundAccount ? getSocialProfileUrl(snsType, boundAccount.sns_user_name) : null
|
|
1411
|
+
};
|
|
1412
|
+
});
|
|
1413
|
+
}, [rawSns]);
|
|
1414
|
+
const walletAddresses = useMemo(() => {
|
|
1415
|
+
return ALL_CHAIN_TYPES.map((chainType) => {
|
|
1416
|
+
const boundAddress = rawAddresses.find((a) => a.chain_type === chainType);
|
|
1417
|
+
const widgetChainType = CHAIN_TYPE_MAP[chainType];
|
|
1418
|
+
return {
|
|
1419
|
+
chainType: widgetChainType ?? chainType,
|
|
1420
|
+
coreChainType: chainType,
|
|
1421
|
+
isBound: !!boundAddress,
|
|
1422
|
+
address: boundAddress ?? null,
|
|
1423
|
+
isPrimary: (boundAddress == null ? void 0 : boundAddress.is_primary) ?? false,
|
|
1424
|
+
// 暂时没有 KYC 信息,后续可扩展
|
|
1425
|
+
isKycVerified: void 0
|
|
1426
|
+
};
|
|
1427
|
+
});
|
|
1428
|
+
}, [rawAddresses]);
|
|
1429
|
+
const totalBoundCount = useMemo(() => {
|
|
1430
|
+
const boundSocialCount = socialAccounts.filter((a) => a.isBound).length;
|
|
1431
|
+
const boundWalletCount = walletAddresses.filter((a) => a.isBound).length;
|
|
1432
|
+
return boundSocialCount + boundWalletCount;
|
|
1433
|
+
}, [socialAccounts, walletAddresses]);
|
|
1434
|
+
return {
|
|
1435
|
+
socialAccounts,
|
|
1436
|
+
walletAddresses,
|
|
1437
|
+
rawSns,
|
|
1438
|
+
rawAddresses,
|
|
1439
|
+
isLoaded,
|
|
1440
|
+
totalBoundCount
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
function useUnbindSocial(options = {}) {
|
|
1444
|
+
const { onUnbindSuccess, onUnbindError } = options;
|
|
1445
|
+
const { client } = useTaskOnContext();
|
|
1446
|
+
const [unbindStatus, setUnbindStatus] = useState("idle");
|
|
1447
|
+
const [error, setError] = useState(null);
|
|
1448
|
+
const [processingType, setProcessingType] = useState(null);
|
|
1449
|
+
const api = useMemo(() => {
|
|
1450
|
+
if (!client) return null;
|
|
1451
|
+
return createUserApi(client);
|
|
1452
|
+
}, [client]);
|
|
1453
|
+
const unbind = useCallback(
|
|
1454
|
+
async (snsId, type) => {
|
|
1455
|
+
if (!api) {
|
|
1456
|
+
const err = new Error("TaskOn client not initialized");
|
|
1457
|
+
setError(err);
|
|
1458
|
+
onUnbindError == null ? void 0 : onUnbindError(err);
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1461
|
+
setProcessingType(type);
|
|
1462
|
+
setUnbindStatus("loading");
|
|
1463
|
+
setError(null);
|
|
1464
|
+
try {
|
|
1465
|
+
const result = await api.unbindSNS({
|
|
1466
|
+
sns_id: snsId,
|
|
1467
|
+
sns_type: type
|
|
1468
|
+
});
|
|
1469
|
+
if (result) {
|
|
1470
|
+
setUnbindStatus("success");
|
|
1471
|
+
onUnbindSuccess == null ? void 0 : onUnbindSuccess();
|
|
1472
|
+
return true;
|
|
1473
|
+
} else {
|
|
1474
|
+
const err = new Error("Unbind failed");
|
|
1475
|
+
setUnbindStatus("error");
|
|
1476
|
+
setError(err);
|
|
1477
|
+
onUnbindError == null ? void 0 : onUnbindError(err);
|
|
1478
|
+
return false;
|
|
1479
|
+
}
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
const error2 = err instanceof Error ? err : new Error("Unbind failed");
|
|
1482
|
+
setUnbindStatus("error");
|
|
1483
|
+
setError(error2);
|
|
1484
|
+
onUnbindError == null ? void 0 : onUnbindError(error2);
|
|
1485
|
+
return false;
|
|
1486
|
+
} finally {
|
|
1487
|
+
setProcessingType(null);
|
|
1488
|
+
}
|
|
1489
|
+
},
|
|
1490
|
+
[api, onUnbindSuccess, onUnbindError]
|
|
1491
|
+
);
|
|
1492
|
+
const reset = useCallback(() => {
|
|
1493
|
+
setUnbindStatus("idle");
|
|
1494
|
+
setError(null);
|
|
1495
|
+
setProcessingType(null);
|
|
1496
|
+
}, []);
|
|
1497
|
+
return {
|
|
1498
|
+
unbindStatus,
|
|
1499
|
+
error,
|
|
1500
|
+
processingType,
|
|
1501
|
+
unbind,
|
|
1502
|
+
reset
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
const COUNTDOWN_SECONDS = 60;
|
|
1506
|
+
const EMAIL_REGEX$1 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1507
|
+
const SNS_BIND_DUPLICATE_ERROR = 10016;
|
|
1508
|
+
const INVALID_EMAIL_VERIFY_CODE = 10017;
|
|
1509
|
+
function useBindEmail(options = {}) {
|
|
1510
|
+
const { onSendCodeSuccess, onSendCodeError, onBindSuccess, onBindError } = options;
|
|
1511
|
+
const { client } = useTaskOnContext();
|
|
1512
|
+
const [status, setStatus] = useState("idle");
|
|
1513
|
+
const [countdown, setCountdown] = useState(0);
|
|
1514
|
+
const [error, setError] = useState(null);
|
|
1515
|
+
const [isEmailDuplicate, setIsEmailDuplicate] = useState(false);
|
|
1516
|
+
const [isCodeInvalid, setIsCodeInvalid] = useState(false);
|
|
1517
|
+
const [email, setEmail] = useState("");
|
|
1518
|
+
const countdownTimerRef = useRef(null);
|
|
1519
|
+
const api = useMemo(() => {
|
|
1520
|
+
if (!client) return null;
|
|
1521
|
+
return createUserApi(client);
|
|
1522
|
+
}, [client]);
|
|
1523
|
+
const startCountdown = useCallback(() => {
|
|
1524
|
+
setCountdown(COUNTDOWN_SECONDS);
|
|
1525
|
+
countdownTimerRef.current = setInterval(() => {
|
|
1526
|
+
setCountdown((prev) => {
|
|
1527
|
+
if (prev <= 1) {
|
|
1528
|
+
if (countdownTimerRef.current) {
|
|
1529
|
+
clearInterval(countdownTimerRef.current);
|
|
1530
|
+
countdownTimerRef.current = null;
|
|
1531
|
+
}
|
|
1532
|
+
return 0;
|
|
1533
|
+
}
|
|
1534
|
+
return prev - 1;
|
|
1535
|
+
});
|
|
1536
|
+
}, 1e3);
|
|
1537
|
+
}, []);
|
|
1538
|
+
const stopCountdown = useCallback(() => {
|
|
1539
|
+
if (countdownTimerRef.current) {
|
|
1540
|
+
clearInterval(countdownTimerRef.current);
|
|
1541
|
+
countdownTimerRef.current = null;
|
|
1542
|
+
}
|
|
1543
|
+
setCountdown(0);
|
|
1544
|
+
}, []);
|
|
1545
|
+
useEffect(() => {
|
|
1546
|
+
return () => {
|
|
1547
|
+
if (countdownTimerRef.current) {
|
|
1548
|
+
clearInterval(countdownTimerRef.current);
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
}, []);
|
|
1552
|
+
const open = useCallback(() => {
|
|
1553
|
+
setStatus("inputEmail");
|
|
1554
|
+
setError(null);
|
|
1555
|
+
setIsEmailDuplicate(false);
|
|
1556
|
+
setIsCodeInvalid(false);
|
|
1557
|
+
}, []);
|
|
1558
|
+
const close = useCallback(() => {
|
|
1559
|
+
setStatus("idle");
|
|
1560
|
+
setError(null);
|
|
1561
|
+
setIsEmailDuplicate(false);
|
|
1562
|
+
setIsCodeInvalid(false);
|
|
1563
|
+
setEmail("");
|
|
1564
|
+
stopCountdown();
|
|
1565
|
+
}, [stopCountdown]);
|
|
1566
|
+
const clearError = useCallback(() => {
|
|
1567
|
+
setError(null);
|
|
1568
|
+
setIsEmailDuplicate(false);
|
|
1569
|
+
setIsCodeInvalid(false);
|
|
1570
|
+
}, []);
|
|
1571
|
+
const sendCode = useCallback(
|
|
1572
|
+
async (emailAddress) => {
|
|
1573
|
+
if (!api) {
|
|
1574
|
+
const err = new Error("TaskOn client not initialized");
|
|
1575
|
+
setError(err);
|
|
1576
|
+
onSendCodeError == null ? void 0 : onSendCodeError(err);
|
|
1577
|
+
return false;
|
|
1578
|
+
}
|
|
1579
|
+
if (!EMAIL_REGEX$1.test(emailAddress)) {
|
|
1580
|
+
const err = new Error("Invalid email format");
|
|
1581
|
+
setError(err);
|
|
1582
|
+
onSendCodeError == null ? void 0 : onSendCodeError(err);
|
|
1583
|
+
return false;
|
|
1584
|
+
}
|
|
1585
|
+
setStatus("sendingCode");
|
|
1586
|
+
setError(null);
|
|
1587
|
+
setIsEmailDuplicate(false);
|
|
1588
|
+
try {
|
|
1589
|
+
const result = await api.requestEmailVerifyCode({
|
|
1590
|
+
email: emailAddress,
|
|
1591
|
+
type: VerifyCodeType.Bind
|
|
1592
|
+
});
|
|
1593
|
+
if (result) {
|
|
1594
|
+
setStatus("inputCode");
|
|
1595
|
+
setEmail(emailAddress);
|
|
1596
|
+
startCountdown();
|
|
1597
|
+
onSendCodeSuccess == null ? void 0 : onSendCodeSuccess();
|
|
1598
|
+
return true;
|
|
1599
|
+
} else {
|
|
1600
|
+
const err = new Error("Failed to send verification code");
|
|
1601
|
+
setStatus("inputEmail");
|
|
1602
|
+
setError(err);
|
|
1603
|
+
onSendCodeError == null ? void 0 : onSendCodeError(err);
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
} catch (err) {
|
|
1607
|
+
const error2 = err;
|
|
1608
|
+
if (error2.code === SNS_BIND_DUPLICATE_ERROR) {
|
|
1609
|
+
setIsEmailDuplicate(true);
|
|
1610
|
+
}
|
|
1611
|
+
setStatus("inputEmail");
|
|
1612
|
+
setError(error2);
|
|
1613
|
+
onSendCodeError == null ? void 0 : onSendCodeError(error2);
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
},
|
|
1617
|
+
[api, onSendCodeSuccess, onSendCodeError, startCountdown]
|
|
1618
|
+
);
|
|
1619
|
+
const bind = useCallback(
|
|
1620
|
+
async (emailAddress, code) => {
|
|
1621
|
+
if (!api) {
|
|
1622
|
+
const err = new Error("TaskOn client not initialized");
|
|
1623
|
+
setError(err);
|
|
1624
|
+
onBindError == null ? void 0 : onBindError(err);
|
|
1625
|
+
return null;
|
|
1626
|
+
}
|
|
1627
|
+
setStatus("submitting");
|
|
1628
|
+
setError(null);
|
|
1629
|
+
setIsCodeInvalid(false);
|
|
1630
|
+
try {
|
|
1631
|
+
const result = await api.bindSNS({
|
|
1632
|
+
sns_type: SnsType.Email,
|
|
1633
|
+
token: code,
|
|
1634
|
+
sns_user_name: emailAddress
|
|
1635
|
+
});
|
|
1636
|
+
if (result.result) {
|
|
1637
|
+
setStatus("success");
|
|
1638
|
+
onBindSuccess == null ? void 0 : onBindSuccess(result);
|
|
1639
|
+
return result;
|
|
1640
|
+
} else {
|
|
1641
|
+
const err = new Error("Email is already linked to another account");
|
|
1642
|
+
setStatus("inputCode");
|
|
1643
|
+
setError(err);
|
|
1644
|
+
onBindError == null ? void 0 : onBindError(err);
|
|
1645
|
+
return null;
|
|
1646
|
+
}
|
|
1647
|
+
} catch (err) {
|
|
1648
|
+
const error2 = err;
|
|
1649
|
+
if (error2.code === INVALID_EMAIL_VERIFY_CODE) {
|
|
1650
|
+
setIsCodeInvalid(true);
|
|
1651
|
+
}
|
|
1652
|
+
setStatus("inputCode");
|
|
1653
|
+
setError(error2);
|
|
1654
|
+
onBindError == null ? void 0 : onBindError(error2);
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
},
|
|
1658
|
+
[api, onBindSuccess, onBindError]
|
|
1659
|
+
);
|
|
1660
|
+
const reset = useCallback(() => {
|
|
1661
|
+
setStatus("idle");
|
|
1662
|
+
setError(null);
|
|
1663
|
+
setIsEmailDuplicate(false);
|
|
1664
|
+
setIsCodeInvalid(false);
|
|
1665
|
+
setEmail("");
|
|
1666
|
+
stopCountdown();
|
|
1667
|
+
}, [stopCountdown]);
|
|
1668
|
+
return {
|
|
1669
|
+
status,
|
|
1670
|
+
countdown,
|
|
1671
|
+
error,
|
|
1672
|
+
isEmailDuplicate,
|
|
1673
|
+
isCodeInvalid,
|
|
1674
|
+
email,
|
|
1675
|
+
open,
|
|
1676
|
+
close,
|
|
1677
|
+
setEmail,
|
|
1678
|
+
sendCode,
|
|
1679
|
+
bind,
|
|
1680
|
+
reset,
|
|
1681
|
+
clearError
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
function generateSignMessage(action, address, nonce) {
|
|
1685
|
+
return `TaskOn ${action} wallet
|
|
1686
|
+
Address: ${address}
|
|
1687
|
+
Nonce: ${nonce}`;
|
|
1688
|
+
}
|
|
1689
|
+
function generateNonce() {
|
|
1690
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
1691
|
+
}
|
|
1692
|
+
function useBindWallet(options = {}) {
|
|
1693
|
+
const {
|
|
1694
|
+
onBindSuccess,
|
|
1695
|
+
onBindError,
|
|
1696
|
+
onUnbindSuccess,
|
|
1697
|
+
onUnbindError
|
|
1698
|
+
} = options;
|
|
1699
|
+
const { client } = useTaskOnContext();
|
|
1700
|
+
const {
|
|
1701
|
+
connectEvm,
|
|
1702
|
+
signEvmMessage,
|
|
1703
|
+
evmAddress: connectedAddress,
|
|
1704
|
+
isEvmConnected: isConnected
|
|
1705
|
+
} = useWallet();
|
|
1706
|
+
const [bindStatus, setBindStatus] = useState("idle");
|
|
1707
|
+
const [unbindStatus, setUnbindStatus] = useState("idle");
|
|
1708
|
+
const [error, setError] = useState(null);
|
|
1709
|
+
const [processingChain, setProcessingChain] = useState(null);
|
|
1710
|
+
const api = useMemo(() => {
|
|
1711
|
+
if (!client) return null;
|
|
1712
|
+
return createUserApi(client);
|
|
1713
|
+
}, [client]);
|
|
1714
|
+
const bind = useCallback(
|
|
1715
|
+
async (chainType) => {
|
|
1716
|
+
if (!api) {
|
|
1717
|
+
const err = new Error("TaskOn client not initialized");
|
|
1718
|
+
setError(err);
|
|
1719
|
+
onBindError == null ? void 0 : onBindError(err);
|
|
1720
|
+
return false;
|
|
1721
|
+
}
|
|
1722
|
+
setProcessingChain(chainType);
|
|
1723
|
+
setBindStatus("connecting");
|
|
1724
|
+
setError(null);
|
|
1725
|
+
try {
|
|
1726
|
+
let walletAddress = connectedAddress;
|
|
1727
|
+
if (!isConnected || !walletAddress) {
|
|
1728
|
+
setBindStatus("connecting");
|
|
1729
|
+
const connectResult = await connectEvm();
|
|
1730
|
+
if (!connectResult) {
|
|
1731
|
+
throw new Error("Failed to connect wallet");
|
|
1732
|
+
}
|
|
1733
|
+
walletAddress = connectResult;
|
|
1734
|
+
}
|
|
1735
|
+
if (!walletAddress) {
|
|
1736
|
+
throw new Error("No wallet address available");
|
|
1737
|
+
}
|
|
1738
|
+
const nonce = generateNonce();
|
|
1739
|
+
const message = generateSignMessage("bind", walletAddress, nonce);
|
|
1740
|
+
setBindStatus("signing");
|
|
1741
|
+
const signature = await signEvmMessage(message);
|
|
1742
|
+
if (!signature) {
|
|
1743
|
+
throw new Error("Failed to sign message");
|
|
1744
|
+
}
|
|
1745
|
+
setBindStatus("submitting");
|
|
1746
|
+
const result = await api.bindAddress({
|
|
1747
|
+
chain_type: chainType,
|
|
1748
|
+
address: walletAddress,
|
|
1749
|
+
nonce,
|
|
1750
|
+
sig: signature
|
|
1751
|
+
});
|
|
1752
|
+
if (result) {
|
|
1753
|
+
setBindStatus("success");
|
|
1754
|
+
onBindSuccess == null ? void 0 : onBindSuccess();
|
|
1755
|
+
return true;
|
|
1756
|
+
} else {
|
|
1757
|
+
throw new Error("Bind wallet failed");
|
|
1758
|
+
}
|
|
1759
|
+
} catch (err) {
|
|
1760
|
+
const error2 = err instanceof Error ? err : new Error("Bind wallet failed");
|
|
1761
|
+
setBindStatus("error");
|
|
1762
|
+
setError(error2);
|
|
1763
|
+
onBindError == null ? void 0 : onBindError(error2);
|
|
1764
|
+
return false;
|
|
1765
|
+
} finally {
|
|
1766
|
+
setProcessingChain(null);
|
|
1767
|
+
}
|
|
1768
|
+
},
|
|
1769
|
+
[api, connectEvm, signEvmMessage, connectedAddress, isConnected, onBindSuccess, onBindError]
|
|
1770
|
+
);
|
|
1771
|
+
const unbind = useCallback(
|
|
1772
|
+
async (chainType, address) => {
|
|
1773
|
+
if (!api) {
|
|
1774
|
+
const err = new Error("TaskOn client not initialized");
|
|
1775
|
+
setError(err);
|
|
1776
|
+
onUnbindError == null ? void 0 : onUnbindError(err);
|
|
1777
|
+
return false;
|
|
1778
|
+
}
|
|
1779
|
+
setProcessingChain(chainType);
|
|
1780
|
+
setUnbindStatus("connecting");
|
|
1781
|
+
setError(null);
|
|
1782
|
+
try {
|
|
1783
|
+
let walletAddress = connectedAddress;
|
|
1784
|
+
if (!isConnected || !walletAddress) {
|
|
1785
|
+
setUnbindStatus("connecting");
|
|
1786
|
+
const connectResult = await connectEvm();
|
|
1787
|
+
if (!connectResult) {
|
|
1788
|
+
throw new Error("Failed to connect wallet");
|
|
1789
|
+
}
|
|
1790
|
+
walletAddress = connectResult;
|
|
1791
|
+
}
|
|
1792
|
+
if ((walletAddress == null ? void 0 : walletAddress.toLowerCase()) !== address.toLowerCase()) {
|
|
1793
|
+
throw new Error(
|
|
1794
|
+
"Connected wallet address does not match the address to unbind. Please connect the correct wallet."
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
const nonce = generateNonce();
|
|
1798
|
+
const message = generateSignMessage("unbind", address, nonce);
|
|
1799
|
+
setUnbindStatus("signing");
|
|
1800
|
+
const signature = await signEvmMessage(message);
|
|
1801
|
+
if (!signature) {
|
|
1802
|
+
throw new Error("Failed to sign message");
|
|
1803
|
+
}
|
|
1804
|
+
setUnbindStatus("submitting");
|
|
1805
|
+
const result = await api.unbindAddress({
|
|
1806
|
+
chain_type: chainType,
|
|
1807
|
+
address,
|
|
1808
|
+
nonce,
|
|
1809
|
+
sig: signature
|
|
1810
|
+
});
|
|
1811
|
+
if (result) {
|
|
1812
|
+
setUnbindStatus("success");
|
|
1813
|
+
onUnbindSuccess == null ? void 0 : onUnbindSuccess();
|
|
1814
|
+
return true;
|
|
1815
|
+
} else {
|
|
1816
|
+
throw new Error("Unbind wallet failed");
|
|
1817
|
+
}
|
|
1818
|
+
} catch (err) {
|
|
1819
|
+
const error2 = err instanceof Error ? err : new Error("Unbind wallet failed");
|
|
1820
|
+
setUnbindStatus("error");
|
|
1821
|
+
setError(error2);
|
|
1822
|
+
onUnbindError == null ? void 0 : onUnbindError(error2);
|
|
1823
|
+
return false;
|
|
1824
|
+
} finally {
|
|
1825
|
+
setProcessingChain(null);
|
|
1826
|
+
}
|
|
1827
|
+
},
|
|
1828
|
+
[api, connectEvm, signEvmMessage, connectedAddress, isConnected, onUnbindSuccess, onUnbindError]
|
|
1829
|
+
);
|
|
1830
|
+
const reset = useCallback(() => {
|
|
1831
|
+
setBindStatus("idle");
|
|
1832
|
+
setUnbindStatus("idle");
|
|
1833
|
+
setError(null);
|
|
1834
|
+
setProcessingChain(null);
|
|
1835
|
+
}, []);
|
|
1836
|
+
return {
|
|
1837
|
+
bindStatus,
|
|
1838
|
+
unbindStatus,
|
|
1839
|
+
error,
|
|
1840
|
+
processingChain,
|
|
1841
|
+
bind,
|
|
1842
|
+
unbind,
|
|
1843
|
+
reset
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
const OAUTH_SNS_TYPES = ["Twitter", "Discord", "Telegram"];
|
|
1847
|
+
function useOAuthBindings(options = {}) {
|
|
1848
|
+
const { onSuccess, onFailed } = options;
|
|
1849
|
+
const {
|
|
1850
|
+
bindIfNeed: bindTwitter,
|
|
1851
|
+
isWaitingAuth: isTwitterBinding
|
|
1852
|
+
} = useBindSocialAccount({
|
|
1853
|
+
snsType: "Twitter",
|
|
1854
|
+
onSuccess,
|
|
1855
|
+
onFailed
|
|
1856
|
+
});
|
|
1857
|
+
const {
|
|
1858
|
+
bindIfNeed: bindDiscord,
|
|
1859
|
+
isWaitingAuth: isDiscordBinding
|
|
1860
|
+
} = useBindSocialAccount({
|
|
1861
|
+
snsType: "Discord",
|
|
1862
|
+
onSuccess,
|
|
1863
|
+
onFailed
|
|
1864
|
+
});
|
|
1865
|
+
const {
|
|
1866
|
+
bindIfNeed: bindTelegram,
|
|
1867
|
+
isWaitingAuth: isTelegramBinding
|
|
1868
|
+
} = useBindSocialAccount({
|
|
1869
|
+
snsType: "Telegram",
|
|
1870
|
+
onSuccess,
|
|
1871
|
+
onFailed
|
|
1872
|
+
});
|
|
1873
|
+
const bind = useCallback(
|
|
1874
|
+
(snsType) => {
|
|
1875
|
+
switch (snsType) {
|
|
1876
|
+
case "Twitter":
|
|
1877
|
+
bindTwitter();
|
|
1878
|
+
break;
|
|
1879
|
+
case "Discord":
|
|
1880
|
+
bindDiscord();
|
|
1881
|
+
break;
|
|
1882
|
+
case "Telegram":
|
|
1883
|
+
bindTelegram();
|
|
1884
|
+
break;
|
|
1885
|
+
default:
|
|
1886
|
+
console.warn(`[useOAuthBindings] Unsupported snsType: ${snsType}`);
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
[bindTwitter, bindDiscord, bindTelegram]
|
|
1890
|
+
);
|
|
1891
|
+
const isBinding = useCallback(
|
|
1892
|
+
(snsType) => {
|
|
1893
|
+
switch (snsType.toLowerCase()) {
|
|
1894
|
+
case "twitter":
|
|
1895
|
+
return isTwitterBinding;
|
|
1896
|
+
case "discord":
|
|
1897
|
+
return isDiscordBinding;
|
|
1898
|
+
case "telegram":
|
|
1899
|
+
return isTelegramBinding;
|
|
1900
|
+
default:
|
|
1901
|
+
return false;
|
|
1902
|
+
}
|
|
1903
|
+
},
|
|
1904
|
+
[isTwitterBinding, isDiscordBinding, isTelegramBinding]
|
|
1905
|
+
);
|
|
1906
|
+
const isOAuthSupported = useCallback((snsType) => {
|
|
1907
|
+
return OAUTH_SNS_TYPES.includes(snsType);
|
|
1908
|
+
}, []);
|
|
1909
|
+
return {
|
|
1910
|
+
bind,
|
|
1911
|
+
isBinding,
|
|
1912
|
+
isOAuthSupported
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
const UNBIND_COOLDOWN_MS = 24 * 60 * 60 * 1e3;
|
|
1916
|
+
const MIN_LOGIN_METHODS = 1;
|
|
1917
|
+
function isInCooldown(bindTime) {
|
|
1918
|
+
const now = Date.now();
|
|
1919
|
+
return now - bindTime < UNBIND_COOLDOWN_MS;
|
|
1920
|
+
}
|
|
1921
|
+
function getCooldownRemaining(bindTime) {
|
|
1922
|
+
const now = Date.now();
|
|
1923
|
+
const elapsed = now - bindTime;
|
|
1924
|
+
const remaining = UNBIND_COOLDOWN_MS - elapsed;
|
|
1925
|
+
return Math.max(0, remaining);
|
|
1926
|
+
}
|
|
1927
|
+
function formatCooldownTime(ms) {
|
|
1928
|
+
const hours = Math.ceil(ms / (60 * 60 * 1e3));
|
|
1929
|
+
if (hours <= 1) {
|
|
1930
|
+
return "less than 1 hour";
|
|
1931
|
+
}
|
|
1932
|
+
return `${hours} hours`;
|
|
1933
|
+
}
|
|
1934
|
+
function useDisableUnlink() {
|
|
1935
|
+
const { totalBoundCount } = useIdentityData();
|
|
1936
|
+
const isLastLoginMethod = totalBoundCount <= MIN_LOGIN_METHODS;
|
|
1937
|
+
const checkSocialUnlink = useMemo(() => {
|
|
1938
|
+
return (account) => {
|
|
1939
|
+
if (isLastLoginMethod) {
|
|
1940
|
+
return {
|
|
1941
|
+
disabled: true,
|
|
1942
|
+
reason: "lastLoginMethod",
|
|
1943
|
+
message: "Cannot unbind the last login method. You must keep at least one login method.",
|
|
1944
|
+
cooldownRemaining: null,
|
|
1945
|
+
needsKycWarning: false
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
const bindTimeMs = account.bind_time;
|
|
1949
|
+
if (isInCooldown(bindTimeMs)) {
|
|
1950
|
+
const remaining = getCooldownRemaining(bindTimeMs);
|
|
1951
|
+
return {
|
|
1952
|
+
disabled: true,
|
|
1953
|
+
reason: "cooldown",
|
|
1954
|
+
message: `Cannot unbind within 24 hours of binding. Please wait ${formatCooldownTime(remaining)}.`,
|
|
1955
|
+
cooldownRemaining: remaining,
|
|
1956
|
+
needsKycWarning: false
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
return {
|
|
1960
|
+
disabled: false,
|
|
1961
|
+
reason: null,
|
|
1962
|
+
message: null,
|
|
1963
|
+
cooldownRemaining: null,
|
|
1964
|
+
needsKycWarning: false
|
|
1965
|
+
};
|
|
1966
|
+
};
|
|
1967
|
+
}, [isLastLoginMethod]);
|
|
1968
|
+
const checkWalletUnlink = useMemo(() => {
|
|
1969
|
+
return (address, isKycVerified = false) => {
|
|
1970
|
+
if (isLastLoginMethod) {
|
|
1971
|
+
return {
|
|
1972
|
+
disabled: true,
|
|
1973
|
+
reason: "lastLoginMethod",
|
|
1974
|
+
message: "Cannot unbind the last login method. You must keep at least one login method.",
|
|
1975
|
+
cooldownRemaining: null,
|
|
1976
|
+
needsKycWarning: false
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
const bindTimeMs = address.bind_time;
|
|
1980
|
+
if (isInCooldown(bindTimeMs)) {
|
|
1981
|
+
const remaining = getCooldownRemaining(bindTimeMs);
|
|
1982
|
+
return {
|
|
1983
|
+
disabled: true,
|
|
1984
|
+
reason: "cooldown",
|
|
1985
|
+
message: `Cannot unbind within 24 hours of binding. Please wait ${formatCooldownTime(remaining)}.`,
|
|
1986
|
+
cooldownRemaining: remaining,
|
|
1987
|
+
needsKycWarning: isKycVerified
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
if (isKycVerified) {
|
|
1991
|
+
return {
|
|
1992
|
+
disabled: false,
|
|
1993
|
+
reason: "kycVerified",
|
|
1994
|
+
message: "This address has been KYC verified. Unbinding will clear your KYC verification status.",
|
|
1995
|
+
cooldownRemaining: null,
|
|
1996
|
+
needsKycWarning: true
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
return {
|
|
2000
|
+
disabled: false,
|
|
2001
|
+
reason: null,
|
|
2002
|
+
message: null,
|
|
2003
|
+
cooldownRemaining: null,
|
|
2004
|
+
needsKycWarning: false
|
|
2005
|
+
};
|
|
2006
|
+
};
|
|
2007
|
+
}, [isLastLoginMethod]);
|
|
2008
|
+
return {
|
|
2009
|
+
checkSocialUnlink,
|
|
2010
|
+
checkWalletUnlink,
|
|
2011
|
+
totalBoundCount,
|
|
2012
|
+
isLastLoginMethod
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
function getRewardId(item) {
|
|
2016
|
+
const rewardValue = item.reward_value;
|
|
2017
|
+
if (typeof rewardValue.reward_id === "number") {
|
|
2018
|
+
return rewardValue.reward_id;
|
|
2019
|
+
}
|
|
2020
|
+
if (typeof rewardValue.collection_id === "number") {
|
|
2021
|
+
return rewardValue.collection_id;
|
|
2022
|
+
}
|
|
2023
|
+
return void 0;
|
|
2024
|
+
}
|
|
2025
|
+
function checkNftClaimable(item) {
|
|
2026
|
+
if (item.reward_type !== RewardType.Nft && item.reward_type !== RewardType.Cap && item.reward_type !== RewardType.BMintedNft) {
|
|
2027
|
+
return {
|
|
2028
|
+
canClaim: false,
|
|
2029
|
+
error: new NftClaimError(
|
|
2030
|
+
NftClaimErrorType.NotClaimable,
|
|
2031
|
+
`Invalid reward type: ${item.reward_type}`
|
|
2032
|
+
)
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
const rewardValue = item.reward_value;
|
|
2036
|
+
if (isNftClaimed(rewardValue)) {
|
|
2037
|
+
return {
|
|
2038
|
+
canClaim: false,
|
|
2039
|
+
error: new NftClaimError(
|
|
2040
|
+
NftClaimErrorType.AlreadyClaimed,
|
|
2041
|
+
"NFT has already been claimed"
|
|
2042
|
+
)
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
if (!isNftClaimable(rewardValue)) {
|
|
2046
|
+
return {
|
|
2047
|
+
canClaim: false,
|
|
2048
|
+
error: new NftClaimError(
|
|
2049
|
+
NftClaimErrorType.NotClaimable,
|
|
2050
|
+
"NFT is not claimable (manual drop)"
|
|
2051
|
+
)
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
const rewardId = getRewardId(item);
|
|
2055
|
+
if (rewardId === void 0) {
|
|
2056
|
+
return {
|
|
2057
|
+
canClaim: false,
|
|
2058
|
+
error: new NftClaimError(
|
|
2059
|
+
NftClaimErrorType.MissingRewardId,
|
|
2060
|
+
"Missing reward_id in reward data"
|
|
2061
|
+
)
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
return { canClaim: true };
|
|
2065
|
+
}
|
|
2066
|
+
function useNftClaim(options = {}) {
|
|
2067
|
+
const { onSuccess, onError, onPendingFound } = options;
|
|
2068
|
+
const { client, userId } = useTaskOnContext();
|
|
2069
|
+
const {
|
|
2070
|
+
evmAdapter,
|
|
2071
|
+
evmAddress,
|
|
2072
|
+
evmChainId,
|
|
2073
|
+
isEvmConnected,
|
|
2074
|
+
connectEvm
|
|
2075
|
+
} = useWallet();
|
|
2076
|
+
const { chainMap } = useChainMap();
|
|
2077
|
+
const [status, setStatus] = useState("idle");
|
|
2078
|
+
const [error, setError] = useState(null);
|
|
2079
|
+
const [txHash, setTxHash] = useState(null);
|
|
2080
|
+
const [claimingNft, setClaimingNft] = useState(null);
|
|
2081
|
+
const api = useMemo(() => {
|
|
2082
|
+
if (!client) return null;
|
|
2083
|
+
return createNftClaimApi(client);
|
|
2084
|
+
}, [client]);
|
|
2085
|
+
const claimNft = useCallback(
|
|
2086
|
+
async (item) => {
|
|
2087
|
+
if (!api) {
|
|
2088
|
+
const err = new NftClaimError(
|
|
2089
|
+
NftClaimErrorType.ApiError,
|
|
2090
|
+
"TaskOn client not initialized"
|
|
2091
|
+
);
|
|
2092
|
+
setError(err);
|
|
2093
|
+
onError == null ? void 0 : onError(err, item);
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
if (!userId) {
|
|
2097
|
+
const err = new NftClaimError(
|
|
2098
|
+
NftClaimErrorType.ApiError,
|
|
2099
|
+
"User not logged in"
|
|
2100
|
+
);
|
|
2101
|
+
setError(err);
|
|
2102
|
+
onError == null ? void 0 : onError(err, item);
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
const { canClaim, error: checkError } = checkNftClaimable(item);
|
|
2106
|
+
if (!canClaim && checkError) {
|
|
2107
|
+
setError(checkError);
|
|
2108
|
+
onError == null ? void 0 : onError(checkError, item);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
const rewardValue = item.reward_value;
|
|
2112
|
+
const nftType = getNftClaimType(rewardValue);
|
|
2113
|
+
const chainName = getNftChainName(rewardValue);
|
|
2114
|
+
const rewardId = getRewardId(item);
|
|
2115
|
+
const pendingKey = generatePendingKey(nftType, item.campaign_id, rewardId);
|
|
2116
|
+
setClaimingNft({
|
|
2117
|
+
item,
|
|
2118
|
+
nftType,
|
|
2119
|
+
chainName,
|
|
2120
|
+
pendingKey
|
|
2121
|
+
});
|
|
2122
|
+
try {
|
|
2123
|
+
setStatus("checking");
|
|
2124
|
+
setError(null);
|
|
2125
|
+
setTxHash(null);
|
|
2126
|
+
const pending = getPendingHash(userId, pendingKey);
|
|
2127
|
+
if (pending) {
|
|
2128
|
+
onPendingFound == null ? void 0 : onPendingFound(pendingKey, pending.hash, pending.chainName);
|
|
2129
|
+
throw new NftClaimError(
|
|
2130
|
+
NftClaimErrorType.PendingTransaction,
|
|
2131
|
+
`Transaction ${pending.hash} is pending`
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
const chainInfo = chainMap[chainName.toLowerCase()];
|
|
2135
|
+
if (!chainInfo) {
|
|
2136
|
+
throw new NftClaimError(
|
|
2137
|
+
NftClaimErrorType.ChainNotFound,
|
|
2138
|
+
`Chain ${chainName} not found`
|
|
2139
|
+
);
|
|
2140
|
+
}
|
|
2141
|
+
const useGasStation = shouldUseGasStation(rewardValue);
|
|
2142
|
+
if (useGasStation) {
|
|
2143
|
+
setStatus("signing");
|
|
2144
|
+
await api.claimCampaignRewardFromGasStation({
|
|
2145
|
+
campaign_id: item.campaign_id,
|
|
2146
|
+
reward_id: rewardId
|
|
2147
|
+
});
|
|
2148
|
+
setStatus("success");
|
|
2149
|
+
onSuccess == null ? void 0 : onSuccess("", item);
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
setStatus("connecting");
|
|
2153
|
+
if (!(evmAdapter == null ? void 0 : evmAdapter.invokeContract)) {
|
|
2154
|
+
throw new NftClaimError(
|
|
2155
|
+
NftClaimErrorType.NoWallet,
|
|
2156
|
+
"Wallet adapter does not support contract invocation. Please provide a wallet adapter with invokeContract method."
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
let walletAddress = evmAddress;
|
|
2160
|
+
if (!isEvmConnected || !walletAddress) {
|
|
2161
|
+
const connectResult = await connectEvm();
|
|
2162
|
+
if (!connectResult) {
|
|
2163
|
+
throw new NftClaimError(
|
|
2164
|
+
NftClaimErrorType.WalletRejected,
|
|
2165
|
+
"Failed to connect wallet"
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
walletAddress = connectResult;
|
|
2169
|
+
}
|
|
2170
|
+
const receiverAddress = rewardValue.receiver_address;
|
|
2171
|
+
if (receiverAddress && walletAddress.toLowerCase() !== receiverAddress.toLowerCase()) {
|
|
2172
|
+
throw new NftClaimError(
|
|
2173
|
+
NftClaimErrorType.AddressMismatch,
|
|
2174
|
+
`Connected address ${walletAddress} does not match receiver address ${receiverAddress}`
|
|
2175
|
+
);
|
|
2176
|
+
}
|
|
2177
|
+
if (evmChainId !== chainInfo.id) {
|
|
2178
|
+
setStatus("switching");
|
|
2179
|
+
if (evmAdapter.switchNetwork) {
|
|
2180
|
+
try {
|
|
2181
|
+
await evmAdapter.switchNetwork(chainInfo.id);
|
|
2182
|
+
} catch (e) {
|
|
2183
|
+
throw new NftClaimError(
|
|
2184
|
+
NftClaimErrorType.NetworkSwitchFailed,
|
|
2185
|
+
`Failed to switch to ${chainInfo.name}`,
|
|
2186
|
+
e instanceof Error ? e : void 0
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
} else {
|
|
2190
|
+
throw new NftClaimError(
|
|
2191
|
+
NftClaimErrorType.NetworkSwitchFailed,
|
|
2192
|
+
`Please switch to ${chainInfo.name} network manually`
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
setStatus("signing");
|
|
2197
|
+
let hash;
|
|
2198
|
+
if (nftType === NftClaimType.Minted) {
|
|
2199
|
+
const sigRes = await api.getBMintNFTCampaignNftSig({
|
|
2200
|
+
campaign_reward_id: rewardId
|
|
2201
|
+
});
|
|
2202
|
+
setStatus("confirming");
|
|
2203
|
+
hash = await evmAdapter.invokeContract({
|
|
2204
|
+
contract: sigRes.entry_address,
|
|
2205
|
+
abi: bMintedNftAbi,
|
|
2206
|
+
method: "claim",
|
|
2207
|
+
params: [
|
|
2208
|
+
sigRes.nft_address,
|
|
2209
|
+
{
|
|
2210
|
+
dropId: BigInt(sigRes.distribute_id),
|
|
2211
|
+
userId: BigInt(sigRes.user_id),
|
|
2212
|
+
limit: BigInt(sigRes.limit),
|
|
2213
|
+
totalLimit: BigInt(sigRes.total_limit),
|
|
2214
|
+
amount: BigInt(1),
|
|
2215
|
+
nonce: BigInt(sigRes.nonce),
|
|
2216
|
+
uri: sigRes.token_uri
|
|
2217
|
+
},
|
|
2218
|
+
[sigRes.signature]
|
|
2219
|
+
],
|
|
2220
|
+
chainId: chainInfo.id
|
|
2221
|
+
});
|
|
2222
|
+
} else {
|
|
2223
|
+
const sigRes = await api.claimCampaignRewardNft({
|
|
2224
|
+
chain: chainName,
|
|
2225
|
+
campaign_id: item.campaign_id,
|
|
2226
|
+
reward_id: rewardId
|
|
2227
|
+
});
|
|
2228
|
+
setStatus("confirming");
|
|
2229
|
+
hash = await evmAdapter.invokeContract({
|
|
2230
|
+
contract: sigRes.contract_address,
|
|
2231
|
+
abi: capNftAbi,
|
|
2232
|
+
method: "mint",
|
|
2233
|
+
params: [
|
|
2234
|
+
walletAddress,
|
|
2235
|
+
BigInt(sigRes.campaign_id),
|
|
2236
|
+
sigRes.token_uri,
|
|
2237
|
+
BigInt(sigRes.total),
|
|
2238
|
+
sigRes.hash,
|
|
2239
|
+
sigRes.signature
|
|
2240
|
+
],
|
|
2241
|
+
chainId: chainInfo.id
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
setPendingHash(userId, pendingKey, hash, chainName);
|
|
2245
|
+
setStatus("pending");
|
|
2246
|
+
setTxHash(hash);
|
|
2247
|
+
setStatus("success");
|
|
2248
|
+
onSuccess == null ? void 0 : onSuccess(hash, item);
|
|
2249
|
+
} catch (e) {
|
|
2250
|
+
const claimError = NftClaimError.fromUnknown(e);
|
|
2251
|
+
setError(claimError);
|
|
2252
|
+
setStatus("error");
|
|
2253
|
+
onError == null ? void 0 : onError(claimError, item);
|
|
2254
|
+
}
|
|
2255
|
+
},
|
|
2256
|
+
[
|
|
2257
|
+
api,
|
|
2258
|
+
userId,
|
|
2259
|
+
chainMap,
|
|
2260
|
+
evmAdapter,
|
|
2261
|
+
evmAddress,
|
|
2262
|
+
evmChainId,
|
|
2263
|
+
isEvmConnected,
|
|
2264
|
+
connectEvm,
|
|
2265
|
+
onSuccess,
|
|
2266
|
+
onError,
|
|
2267
|
+
onPendingFound
|
|
2268
|
+
]
|
|
2269
|
+
);
|
|
2270
|
+
const reset = useCallback(() => {
|
|
2271
|
+
setStatus("idle");
|
|
2272
|
+
setError(null);
|
|
2273
|
+
setTxHash(null);
|
|
2274
|
+
setClaimingNft(null);
|
|
2275
|
+
}, []);
|
|
2276
|
+
const clearPending = useCallback(
|
|
2277
|
+
(pendingKey) => {
|
|
2278
|
+
if (userId) {
|
|
2279
|
+
clearPendingHash(userId, pendingKey);
|
|
2280
|
+
}
|
|
2281
|
+
},
|
|
2282
|
+
[userId]
|
|
2283
|
+
);
|
|
2284
|
+
return {
|
|
2285
|
+
status,
|
|
2286
|
+
error,
|
|
2287
|
+
txHash,
|
|
2288
|
+
claimingNft,
|
|
2289
|
+
claimNft,
|
|
2290
|
+
reset,
|
|
2291
|
+
clearPending
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
const twitterIcon = new URL("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH3SURBVHgBzZeNkYIwEIXjVYAdcB1QAtcBVqBWcFoBdgBWgB1YAtcBJUAHlrCXxxEnEzaQIMi9mR0zIT+fSd4GhDBERLGMTEZN86uSUcgIhU3yYdABvEuYK+AgKnq/KgXz0bGkMiLxfkXd3GJDf/tVi3X1hRVJzdrz+Sw2mw0b2+1WNE0jXHU8Htt+u91uqFkiiDkbj8eDoigi2YCCIKAwDNtAGXVxHDsdgMvl0rZHX4w5oFrYnlRV9ZwYZQWIQVGX5zkNCc8VRF3XNCYx9DDLst4/0gHLsmT7oV6tpgvEKAiEbcCgp9NpEFAJEyvQ+/1OrhoFsQ3MAaKt2ro0TclHoyAQAMylNgH18+ML4QwC4Z+bjtEBkyRpy/idImcQm2MUoKNNXweBdDdwlkbemCovEEg5BglPt7QJuDgI5GvpxUDUFplJjQNcDEQ/E+YK6Ja2Zd3ZQHSb7vf7nmWLopi0RV4g5m1qszQHOBuIDqFfZNwF53NLe4FwKV6XSmqw9BDgSyCut6l6kdIdwwFOAvG5TW2O4QC9QKbcplxSc7W0FWTqbaqSGveuO2RpgNRmpcdLb09orybm4nA4cN0qfNfkssG3WFc3gMSyUIp19fkhP35+ZOEq1tNVMjRtif7BR/hTHYxbPp5HeQ/CAApl3GiZFao7gNic9xdG16HkLDPmwQAAAABJRU5ErkJggg==", import.meta.url).href;
|
|
2295
|
+
const discordIcon = new URL("data:image/svg+xml,%3csvg%20width='24'%20height='25'%20viewBox='0%200%2024%2025'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3crect%20y='0.187012'%20width='24'%20height='24.2243'%20rx='8'%20fill='%237870FF'/%3e%3cpath%20d='M18.7627%206.96988C16.7288%205.45184%2014.7966%205.34341%2014.4915%205.34341L14.2881%205.56027C16.6271%206.21086%2017.9492%207.4036%2018.0508%207.51203C14.3898%205.56027%2010.322%205.45184%206.66102%206.96988C6.15254%207.18674%205.84746%207.4036%205.74576%207.4036C5.94915%207.29517%207.16949%206.10243%209.71186%205.45184L9.50847%205.23498C9.50847%205.23498%207.47458%205.12655%205.23729%206.86145C5.23729%206.86145%203%2010.8734%203%2015.9697C3%2015.9697%204.32203%2018.2467%207.67797%2018.3551C7.67797%2018.3551%208.18644%2017.7046%208.69492%2017.054C6.9661%2016.5118%206.15254%2015.3191%206.05085%2015.2106L6.66102%2015.5359C8.49153%2016.2949%2010.4237%2017.3793%2013.9831%2016.7287C14.6949%2016.6202%2015.5085%2016.4034%2016.2203%2016.0781C16.7288%2015.7528%2017.339%2015.5359%2017.9492%2015.1022C17.8475%2015.2106%2017.0339%2016.4034%2015.2034%2016.9455C15.6102%2017.5961%2016.2203%2018.2467%2016.2203%2018.2467C19.678%2018.1383%2021%2015.8612%2021%2016.0781C21%2011.0903%2018.7627%206.96988%2018.7627%206.96988ZM9.20339%2014.3432C8.38983%2014.3432%207.67797%2013.5842%207.67797%2012.6083C7.67797%2011.6324%208.38983%2010.8734%209.20339%2010.8734C10.0169%2010.8734%2010.7288%2011.7408%2010.7288%2012.6083C10.7288%2013.5842%2010.0169%2014.3432%209.20339%2014.3432ZM14.7966%2014.3432C13.9831%2014.3432%2013.2712%2013.5842%2013.2712%2012.6083C13.2712%2011.6324%2013.9831%2010.8734%2014.7966%2010.8734C15.7119%2010.8734%2016.4237%2011.7408%2016.4237%2012.6083C16.4237%2013.5842%2015.7119%2014.3432%2014.7966%2014.3432Z'%20fill='white'/%3e%3c/svg%3e", import.meta.url).href;
|
|
2296
|
+
const telegramIcon = new URL("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%3e%3crect%20y='0.000396729'%20width='24'%20height='24'%20rx='8'%20fill='%2354AEFF'/%3e%3cpath%20d='M18.9513%206.24633L16.5438%2017.617C16.377%2018.424%2015.8902%2018.6174%2015.21%2018.2439L11.542%2015.5296L9.76804%2017.2435C9.56797%2017.4436%209.40125%2017.617%209.03445%2017.617C8.55428%2017.617%208.63431%2017.4369%208.47425%2016.9835L7.20714%2012.8553L3.57252%2011.7216C2.78558%2011.4882%202.77891%2010.948%203.74592%2010.5545L17.9243%205.08592C18.5711%204.79915%2019.1914%205.24598%2018.9446%206.23966L18.9513%206.24633Z'%20fill='white'/%3e%3c/svg%3e", import.meta.url).href;
|
|
2297
|
+
const redditIcon = new URL("data:image/svg+xml,%3csvg%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20id='Group%2048098228'%3e%3crect%20id='Rectangle%206202'%20y='0.000488281'%20width='24'%20height='24'%20rx='8'%20fill='%23FF4500'/%3e%3cpath%20id='Vector'%20d='M20.0319%2012.242C20.0319%2011.2761%2019.2031%2010.4913%2018.185%2010.4913C17.7441%2010.4913%2017.3313%2010.6436%2016.9987%2010.9114L16.9658%2010.9306C15.7642%2010.2012%2014.1714%209.73623%2012.4103%209.67371L13.4612%206.83847L16.0864%207.43325C16.0881%208.22042%2016.763%208.86009%2017.5934%208.86009C18.4255%208.86009%2019.102%208.21882%2019.102%207.43005C19.102%206.64128%2018.4255%206%2017.5951%206C16.9626%206%2016.4231%206.37034%2016.1995%206.89298L13.0893%206.18837L11.7987%209.6673C9.96066%209.69936%208.29575%2010.1707%207.05007%2010.9242C6.71195%2010.6442%206.28667%2010.4911%205.84768%2010.4913C4.82725%2010.4905%204%2011.2761%204%2012.2412C4%2012.8384%204.32465%2013.3899%204.8521%2013.7113C4.82003%2013.8853%204.79919%2014.0616%204.79919%2014.2412C4.79919%2016.7694%208.03042%2018.8255%2012.0015%2018.8255C15.9734%2018.8255%2019.2055%2016.7694%2019.2055%2014.242C19.2055%2014.0681%2019.187%2013.8965%2019.1566%2013.7274C19.6968%2013.4083%2020.0319%2012.8488%2020.0319%2012.242ZM14.4512%2014.5089C13.8243%2014.5089%2013.3169%2014.028%2013.3169%2013.4332C13.3169%2012.8392%2013.8243%2012.3575%2014.4512%2012.3575C15.078%2012.3575%2015.5863%2012.8384%2015.5863%2013.4332C15.5863%2014.028%2015.0772%2014.5089%2014.4512%2014.5089ZM14.6764%2016.3438C14.6428%2016.3758%2013.8292%2017.1614%2011.9887%2017.1614C10.1386%2017.1614%209.39954%2016.3662%209.36748%2016.3318C9.3428%2016.3048%209.32396%2016.273%209.31214%2016.2384C9.30032%2016.2039%209.29577%2016.1672%209.29878%2016.1308C9.30179%2016.0943%209.3123%2016.0589%209.32964%2016.0268C9.34697%2015.9946%209.37078%2015.9663%209.39954%2015.9438C9.45882%2015.8964%209.53372%2015.873%209.60941%2015.8782C9.68511%2015.8834%209.7561%2015.9168%209.80836%2015.9718C9.82439%2015.9895%2010.4416%2016.6131%2011.9887%2016.6131C13.5622%2016.6131%2014.2524%2015.9678%2014.2596%2015.9614C14.3147%2015.9094%2014.3873%2015.88%2014.4631%2015.8791C14.5388%2015.8782%2014.6122%2015.9059%2014.6684%2015.9566C14.6955%2015.9808%2014.7174%2016.0103%2014.7325%2016.0433C14.7477%2016.0763%2014.756%2016.1121%2014.7567%2016.1485C14.7575%2016.1848%2014.7507%2016.2209%2014.7369%2016.2545C14.7231%2016.2881%2014.7025%2016.3185%2014.6764%2016.3438ZM8.57871%2013.4332C8.57871%2012.8392%209.08612%2012.3575%209.71457%2012.3575C10.3398%2012.3575%2010.8488%2012.8384%2010.8488%2013.4332C10.8488%2014.028%2010.3398%2014.5089%209.71457%2014.5089C9.08692%2014.5089%208.57871%2014.028%208.57871%2013.4332Z'%20fill='white'/%3e%3c/g%3e%3c/svg%3e", import.meta.url).href;
|
|
2298
|
+
const emailIcon = new URL("data:image/svg+xml,%3csvg%20width='24'%20height='25'%20viewBox='0%200%2024%2025'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3crect%20width='24'%20height='24.2243'%20rx='8'%20fill='%23DD9F00'/%3e%3cpath%20d='M5.14286%2015.2063L9.41607%2012L4%207.93687V16.0612L4.89286%2015.3919L5.14286%2015.2063ZM12%2012.4631L18.8571%207.32L19.8393%206.585C19.6393%206.23437%2019.275%206%2018.8571%206H5.14286C4.75893%206%204.41964%206.19875%204.2125%206.5025L5.14286%207.32L12%2012.4631ZM13.6%2012.7369L12%2013.9369L10.4%2012.7369L5.14286%2016.68L4.89286%2016.8956L4.20536%2017.4862C4.41071%2017.7975%204.75536%2018%205.14286%2018H18.8571C19.2768%2018%2019.6429%2017.7638%2019.8411%2017.4113L18.8571%2016.68L13.6%2012.7369ZM14.5821%2012L20%2016.0631V7.93687L14.5821%2012Z'%20fill='white'/%3e%3c/svg%3e", import.meta.url).href;
|
|
2299
|
+
const SOCIAL_ICONS = {
|
|
2300
|
+
[SnsType.Twitter]: twitterIcon,
|
|
2301
|
+
[SnsType.Discord]: discordIcon,
|
|
2302
|
+
[SnsType.Telegram]: telegramIcon,
|
|
2303
|
+
[SnsType.Reddit]: redditIcon,
|
|
2304
|
+
[SnsType.Email]: emailIcon
|
|
2305
|
+
};
|
|
2306
|
+
function SocialIcon({
|
|
2307
|
+
type,
|
|
2308
|
+
size = 24,
|
|
2309
|
+
className = ""
|
|
2310
|
+
}) {
|
|
2311
|
+
const iconSrc = SOCIAL_ICONS[type];
|
|
2312
|
+
if (iconSrc) {
|
|
2313
|
+
return /* @__PURE__ */ jsx(
|
|
2314
|
+
"img",
|
|
2315
|
+
{
|
|
2316
|
+
src: iconSrc,
|
|
2317
|
+
alt: type,
|
|
2318
|
+
width: size,
|
|
2319
|
+
height: size,
|
|
2320
|
+
className: `taskon-social-icon ${className}`,
|
|
2321
|
+
style: { objectFit: "contain" }
|
|
2322
|
+
}
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
return /* @__PURE__ */ jsx(
|
|
2326
|
+
"svg",
|
|
2327
|
+
{
|
|
2328
|
+
width: size,
|
|
2329
|
+
height: size,
|
|
2330
|
+
viewBox: "0 0 24 24",
|
|
2331
|
+
fill: "none",
|
|
2332
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2333
|
+
className,
|
|
2334
|
+
children: /* @__PURE__ */ jsx(
|
|
2335
|
+
"path",
|
|
2336
|
+
{
|
|
2337
|
+
d: "M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1ZM8 13h8v-2H8v2Zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5Z",
|
|
2338
|
+
fill: "#9CA3AF"
|
|
2339
|
+
}
|
|
2340
|
+
)
|
|
2341
|
+
}
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
function ChainIcon({ chain }) {
|
|
2345
|
+
if (chain === "evm") {
|
|
2346
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2347
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "white" }),
|
|
2348
|
+
/* @__PURE__ */ jsx("path", { d: "M12 4L6 12L12 9.5V4Z", fill: "#343434" }),
|
|
2349
|
+
/* @__PURE__ */ jsx("path", { d: "M12 4L18 12L12 9.5V4Z", fill: "#8C8C8C" }),
|
|
2350
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16.5L6 12L12 14.5V16.5Z", fill: "#3C3C3B" }),
|
|
2351
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16.5L18 12L12 14.5V16.5Z", fill: "#8C8C8C" }),
|
|
2352
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17.5L6 13L12 20V17.5Z", fill: "#141414" }),
|
|
2353
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17.5L18 13L12 20V17.5Z", fill: "#393939" })
|
|
2354
|
+
] });
|
|
2355
|
+
}
|
|
2356
|
+
if (chain === "sui") {
|
|
2357
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2358
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#4DA2FF" }),
|
|
2359
|
+
/* @__PURE__ */ jsx("path", { d: "M16.5 8.5C15.5 7.5 14 7 12.5 7.5C11 8 10 9 9.5 10.5C9 12 9 13.5 9.5 15C10 16.5 11 17.5 12.5 18C14 18.5 15.5 18 16.5 17", stroke: "white", strokeWidth: "2", strokeLinecap: "round" }),
|
|
2360
|
+
/* @__PURE__ */ jsx("circle", { cx: "15", cy: "10", r: "1.5", fill: "white" })
|
|
2361
|
+
] });
|
|
2362
|
+
}
|
|
2363
|
+
if (chain === "solana") {
|
|
2364
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2365
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "black" }),
|
|
2366
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: "solana-grad", x1: "4", y1: "20", x2: "20", y2: "4", children: [
|
|
2367
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#9945FF" }),
|
|
2368
|
+
/* @__PURE__ */ jsx("stop", { offset: "50%", stopColor: "#14F195" }),
|
|
2369
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#00D1FF" })
|
|
2370
|
+
] }) }),
|
|
2371
|
+
/* @__PURE__ */ jsx("path", { d: "M6 15.5L8.5 13H18L15.5 15.5H6Z", fill: "url(#solana-grad)" }),
|
|
2372
|
+
/* @__PURE__ */ jsx("path", { d: "M6 8.5L8.5 11H18L15.5 8.5H6Z", fill: "url(#solana-grad)" }),
|
|
2373
|
+
/* @__PURE__ */ jsx("path", { d: "M6 12L8.5 9.5H18L15.5 12H6Z", fill: "url(#solana-grad)" })
|
|
2374
|
+
] });
|
|
2375
|
+
}
|
|
2376
|
+
if (chain === "starknet") {
|
|
2377
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2378
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#EC796B" }),
|
|
2379
|
+
/* @__PURE__ */ jsx("path", { d: "M12 6L8 10L12 8L16 10L12 6Z", fill: "white" }),
|
|
2380
|
+
/* @__PURE__ */ jsx("path", { d: "M8 10L12 14L8 12V10Z", fill: "white", fillOpacity: "0.6" }),
|
|
2381
|
+
/* @__PURE__ */ jsx("path", { d: "M16 10L12 14L16 12V10Z", fill: "white", fillOpacity: "0.8" }),
|
|
2382
|
+
/* @__PURE__ */ jsx("path", { d: "M12 14L8 12L12 18L16 12L12 14Z", fill: "white" })
|
|
2383
|
+
] });
|
|
2384
|
+
}
|
|
2385
|
+
if (chain === "aptos") {
|
|
2386
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2387
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "white" }),
|
|
2388
|
+
/* @__PURE__ */ jsx("path", { d: "M16 8H14L12 12L10 8H8L11 14H9V16H15V14H13L16 8Z", fill: "black" })
|
|
2389
|
+
] });
|
|
2390
|
+
}
|
|
2391
|
+
if (chain === "btc") {
|
|
2392
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2393
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#F7931A" }),
|
|
2394
|
+
/* @__PURE__ */ jsx("path", { d: "M15.5 10.5C15.5 8.5 14 8 12 8V6H11V8H10V6H9V8H7V10H8.5V14H7V16H9V18H10V16H11V18H12V16C14.5 16 16 15 16 13C16 11.5 15 11 15.5 10.5ZM10 10H12C13 10 13.5 10.5 13.5 11C13.5 11.5 13 12 12 12H10V10ZM12.5 14H10V12H12.5C13.5 12 14 12.5 14 13C14 13.5 13.5 14 12.5 14Z", fill: "white" })
|
|
2395
|
+
] });
|
|
2396
|
+
}
|
|
2397
|
+
if (chain === "tron") {
|
|
2398
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2399
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#FF0013" }),
|
|
2400
|
+
/* @__PURE__ */ jsx("path", { d: "M7 7L12 19L17 7H7Z", fill: "white" }),
|
|
2401
|
+
/* @__PURE__ */ jsx("path", { d: "M9 9L12 16L15 9H9Z", fill: "#FF0013" })
|
|
2402
|
+
] });
|
|
2403
|
+
}
|
|
2404
|
+
if (chain === "ton") {
|
|
2405
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2406
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#0088CC" }),
|
|
2407
|
+
/* @__PURE__ */ jsx("path", { d: "M12 6L6 18H10L12 14L14 18H18L12 6Z", fill: "white" })
|
|
2408
|
+
] });
|
|
2409
|
+
}
|
|
2410
|
+
if (chain === "nibiru") {
|
|
2411
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2412
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#1E1E2F" }),
|
|
2413
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "5", stroke: "#7B68EE", strokeWidth: "2" }),
|
|
2414
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2", fill: "#7B68EE" })
|
|
2415
|
+
] });
|
|
2416
|
+
}
|
|
2417
|
+
if (chain === "kaspa") {
|
|
2418
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2419
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#49EACB" }),
|
|
2420
|
+
/* @__PURE__ */ jsx("path", { d: "M8 8L12 12L8 16M12 12L16 8M12 12L16 16", stroke: "white", strokeWidth: "2", strokeLinecap: "round" })
|
|
2421
|
+
] });
|
|
2422
|
+
}
|
|
2423
|
+
return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2424
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#4B5563" }),
|
|
2425
|
+
/* @__PURE__ */ jsx("path", { d: "M12 6C8.69 6 6 8.69 6 12s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z", fill: "white" })
|
|
2426
|
+
] });
|
|
2427
|
+
}
|
|
2428
|
+
function EvmChainIcons() {
|
|
2429
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-network-chain-icons", children: [
|
|
2430
|
+
/* @__PURE__ */ jsx(ChainIcon, { chain: "evm" }),
|
|
2431
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2432
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#213147" }),
|
|
2433
|
+
/* @__PURE__ */ jsx("path", { d: "M12 6L16.5 12L12 18L7.5 12L12 6Z", fill: "#28A0F0" }),
|
|
2434
|
+
/* @__PURE__ */ jsx("path", { d: "M12 6L7.5 12L12 14.5V6Z", fill: "white" })
|
|
2435
|
+
] }),
|
|
2436
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2437
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#8247E5" }),
|
|
2438
|
+
/* @__PURE__ */ jsx("path", { d: "M15.5 9L12 7L8.5 9V13L12 15L15.5 13V9Z", stroke: "white", strokeWidth: "1.5" }),
|
|
2439
|
+
/* @__PURE__ */ jsx("path", { d: "M12 11L15.5 9M12 11L8.5 9M12 11V15", stroke: "white", strokeWidth: "1.5" })
|
|
2440
|
+
] }),
|
|
2441
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2442
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#F3BA2F" }),
|
|
2443
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7L9.5 9.5L10.5 10.5L12 9L13.5 10.5L14.5 9.5L12 7Z", fill: "white" }),
|
|
2444
|
+
/* @__PURE__ */ jsx("path", { d: "M7 12L8 11L9 12L8 13L7 12Z", fill: "white" }),
|
|
2445
|
+
/* @__PURE__ */ jsx("path", { d: "M17 12L16 11L15 12L16 13L17 12Z", fill: "white" }),
|
|
2446
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17L14.5 14.5L13.5 13.5L12 15L10.5 13.5L9.5 14.5L12 17Z", fill: "white" }),
|
|
2447
|
+
/* @__PURE__ */ jsx("path", { d: "M12 11L11 12L12 13L13 12L12 11Z", fill: "white" })
|
|
2448
|
+
] }),
|
|
2449
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2450
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#FF0420" }),
|
|
2451
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "12", r: "3", fill: "white" }),
|
|
2452
|
+
/* @__PURE__ */ jsx("path", { d: "M14 9H17V10.5H15V11.5H17V13H15V15H14V9Z", fill: "white" })
|
|
2453
|
+
] }),
|
|
2454
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2455
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#E84142" }),
|
|
2456
|
+
/* @__PURE__ */ jsx("path", { d: "M8 16L12 8L16 16H13L12 14L11 16H8Z", fill: "white" })
|
|
2457
|
+
] }),
|
|
2458
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2459
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#0052FF" }),
|
|
2460
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "5", stroke: "white", strokeWidth: "2" }),
|
|
2461
|
+
/* @__PURE__ */ jsx("path", { d: "M12 9V12H15", stroke: "white", strokeWidth: "2", strokeLinecap: "round" })
|
|
2462
|
+
] }),
|
|
2463
|
+
/* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2464
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", rx: "8", fill: "#121212" }),
|
|
2465
|
+
/* @__PURE__ */ jsx("path", { d: "M7 17V7H9V15H17V17H7Z", fill: "white" })
|
|
2466
|
+
] }),
|
|
2467
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-chain-icons__more", children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "3", viewBox: "0 0 12 3", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2468
|
+
/* @__PURE__ */ jsx("circle", { cx: "1.5", cy: "1.5", r: "1.5", fill: "#666" }),
|
|
2469
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "1.5", r: "1.5", fill: "#666" }),
|
|
2470
|
+
/* @__PURE__ */ jsx("circle", { cx: "10.5", cy: "1.5", r: "1.5", fill: "#666" })
|
|
2471
|
+
] }) })
|
|
2472
|
+
] });
|
|
2473
|
+
}
|
|
2474
|
+
function ArrowButton({
|
|
2475
|
+
onClick,
|
|
2476
|
+
disabled,
|
|
2477
|
+
isLoading,
|
|
2478
|
+
title
|
|
2479
|
+
}) {
|
|
2480
|
+
return /* @__PURE__ */ jsx(
|
|
2481
|
+
"button",
|
|
2482
|
+
{
|
|
2483
|
+
className: "taskon-identity-social-btn",
|
|
2484
|
+
onClick,
|
|
2485
|
+
disabled: disabled || isLoading,
|
|
2486
|
+
title,
|
|
2487
|
+
children: isLoading ? /* @__PURE__ */ jsx("span", { className: "taskon-identity-social-btn__spinner" }) : /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2488
|
+
/* @__PURE__ */ jsx("path", { d: "M10.8225 4.44751L15.375 9.00001L10.8225 13.5525", stroke: "currentColor", strokeWidth: "1.5", strokeMiterlimit: "10", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2489
|
+
/* @__PURE__ */ jsx("path", { d: "M2.625 9H15.2475", stroke: "currentColor", strokeWidth: "1.5", strokeMiterlimit: "10", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
2490
|
+
] })
|
|
2491
|
+
}
|
|
2492
|
+
);
|
|
2493
|
+
}
|
|
2494
|
+
function CopyButton({ text }) {
|
|
2495
|
+
const [copied, setCopied] = useState(false);
|
|
2496
|
+
const handleCopy = useCallback(() => {
|
|
2497
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
2498
|
+
setCopied(true);
|
|
2499
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
2500
|
+
});
|
|
2501
|
+
}, [text]);
|
|
2502
|
+
return /* @__PURE__ */ jsx(
|
|
2503
|
+
"button",
|
|
2504
|
+
{
|
|
2505
|
+
className: "taskon-network-copy-btn",
|
|
2506
|
+
onClick: handleCopy,
|
|
2507
|
+
title: copied ? "Copied!" : "Copy",
|
|
2508
|
+
children: copied ? (
|
|
2509
|
+
// 复制成功图标(勾选)
|
|
2510
|
+
/* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M3 7L6 10L11 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
2511
|
+
) : (
|
|
2512
|
+
// 复制图标
|
|
2513
|
+
/* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2514
|
+
/* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
2515
|
+
/* @__PURE__ */ jsx("path", { d: "M2 10V3C2 2.44772 2.44772 2 3 2H10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
2516
|
+
] })
|
|
2517
|
+
)
|
|
2518
|
+
}
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2521
|
+
function SocialAccountItem({
|
|
2522
|
+
data,
|
|
2523
|
+
onBind,
|
|
2524
|
+
onUnbind,
|
|
2525
|
+
isLoading,
|
|
2526
|
+
disabled,
|
|
2527
|
+
disabledReason,
|
|
2528
|
+
messages
|
|
2529
|
+
}) {
|
|
2530
|
+
const displayText = data.isBound && data.account ? data.account.sns_user_name : (messages.linkPlatformAccount ?? "Link {platform} Account").replace("{platform}", data.snsType);
|
|
2531
|
+
return /* @__PURE__ */ jsxs(
|
|
2532
|
+
"div",
|
|
2533
|
+
{
|
|
2534
|
+
className: `taskon-identity-social-item ${data.isBound ? "taskon-identity-social-item--bound" : ""} ${data.isBound && disabled ? "taskon-identity-social-item--unlink-disabled" : ""}`,
|
|
2535
|
+
children: [
|
|
2536
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-identity-social-item__content", children: [
|
|
2537
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-identity-social-item__icon", children: /* @__PURE__ */ jsx(SocialIcon, { type: data.snsType }) }),
|
|
2538
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-identity-social-item__text", children: data.isBound && data.profileUrl ? /* @__PURE__ */ jsx(
|
|
2539
|
+
"a",
|
|
2540
|
+
{
|
|
2541
|
+
href: data.profileUrl,
|
|
2542
|
+
target: "_blank",
|
|
2543
|
+
rel: "noopener noreferrer",
|
|
2544
|
+
className: "taskon-identity-social-item__link",
|
|
2545
|
+
children: displayText
|
|
2546
|
+
}
|
|
2547
|
+
) : /* @__PURE__ */ jsx("span", { className: "taskon-identity-social-item__name", children: displayText }) })
|
|
2548
|
+
] }),
|
|
2549
|
+
data.isBound ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2550
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-identity-social-item__linked", children: messages.linked ?? "Linked" }),
|
|
2551
|
+
!disabled && /* @__PURE__ */ jsx(
|
|
2552
|
+
"button",
|
|
2553
|
+
{
|
|
2554
|
+
className: "taskon-identity-social-item__unlink-btn",
|
|
2555
|
+
onClick: onUnbind,
|
|
2556
|
+
disabled: isLoading,
|
|
2557
|
+
"aria-label": messages.unbind ?? "Unbind",
|
|
2558
|
+
children: isLoading ? /* @__PURE__ */ jsx("span", { className: "taskon-identity-social-btn__spinner" }) : /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M11 3L3 11M3 3L11 11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
2559
|
+
}
|
|
2560
|
+
)
|
|
2561
|
+
] }) : (
|
|
2562
|
+
/* 未绑定状态:显示箭头按钮 */
|
|
2563
|
+
/* @__PURE__ */ jsx(
|
|
2564
|
+
ArrowButton,
|
|
2565
|
+
{
|
|
2566
|
+
onClick: onBind,
|
|
2567
|
+
disabled: false,
|
|
2568
|
+
isLoading
|
|
2569
|
+
}
|
|
2570
|
+
)
|
|
2571
|
+
)
|
|
2572
|
+
]
|
|
2573
|
+
}
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
function WalletItem({
|
|
2577
|
+
data,
|
|
2578
|
+
addresses,
|
|
2579
|
+
onBind,
|
|
2580
|
+
onUnbind,
|
|
2581
|
+
isLoading,
|
|
2582
|
+
disabled,
|
|
2583
|
+
disabledReason,
|
|
2584
|
+
messages
|
|
2585
|
+
}) {
|
|
2586
|
+
const name = getChainName(data.coreChainType);
|
|
2587
|
+
const isEvm = data.coreChainType === "evm";
|
|
2588
|
+
if (isEvm && addresses && addresses.length > 0) {
|
|
2589
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-network-card taskon-network-card--multi", children: [
|
|
2590
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__header", children: /* @__PURE__ */ jsx("span", { className: "taskon-network-card__label", children: "EVM Chain" }) }),
|
|
2591
|
+
/* @__PURE__ */ jsx(EvmChainIcons, {}),
|
|
2592
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__addresses", children: addresses.map((addr, index) => {
|
|
2593
|
+
var _a, _b, _c;
|
|
2594
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-network-address-row", children: [
|
|
2595
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-network-address-row__left", children: [
|
|
2596
|
+
/* @__PURE__ */ jsx("span", { className: `taskon-network-address-row__addr ${!addr.isPrimary ? "taskon-network-address-row__addr--secondary" : ""}`, children: truncateAddress(((_a = addr.address) == null ? void 0 : _a.address) ?? "") }),
|
|
2597
|
+
/* @__PURE__ */ jsx(CopyButton, { text: ((_b = addr.address) == null ? void 0 : _b.address) ?? "" })
|
|
2598
|
+
] }),
|
|
2599
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-address-row__divider" }),
|
|
2600
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-network-address-row__right", children: [
|
|
2601
|
+
/* @__PURE__ */ jsx("span", { className: `taskon-network-address-row__type ${addr.isPrimary ? "taskon-network-address-row__type--primary" : "taskon-network-address-row__type--secondary"}`, children: addr.isPrimary ? "Primary Address" : "Secondary Address" }),
|
|
2602
|
+
/* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "taskon-network-address-row__edit", children: [
|
|
2603
|
+
/* @__PURE__ */ jsx("path", { d: "M2 12H4L10.5 5.5L8.5 3.5L2 10V12Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2604
|
+
/* @__PURE__ */ jsx("path", { d: "M8.5 3.5L10.5 5.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
2605
|
+
] })
|
|
2606
|
+
] }),
|
|
2607
|
+
/* @__PURE__ */ jsx(
|
|
2608
|
+
"button",
|
|
2609
|
+
{
|
|
2610
|
+
className: "taskon-network-unbind-badge",
|
|
2611
|
+
onClick: () => {
|
|
2612
|
+
var _a2;
|
|
2613
|
+
return onUnbind((_a2 = addr.address) == null ? void 0 : _a2.address);
|
|
2614
|
+
},
|
|
2615
|
+
disabled: isLoading || disabled,
|
|
2616
|
+
title: disabledReason,
|
|
2617
|
+
children: messages.unbind ?? "Unbind"
|
|
2618
|
+
}
|
|
2619
|
+
)
|
|
2620
|
+
] }, ((_c = addr.address) == null ? void 0 : _c.address) ?? index);
|
|
2621
|
+
}) })
|
|
2622
|
+
] });
|
|
2623
|
+
}
|
|
2624
|
+
if (isEvm && !data.isBound) {
|
|
2625
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-network-card taskon-network-card--multi", children: [
|
|
2626
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__header", children: /* @__PURE__ */ jsx("span", { className: "taskon-network-card__label", children: "EVM Chain" }) }),
|
|
2627
|
+
/* @__PURE__ */ jsx(EvmChainIcons, {}),
|
|
2628
|
+
/* @__PURE__ */ jsx(
|
|
2629
|
+
"button",
|
|
2630
|
+
{
|
|
2631
|
+
className: "taskon-network-connect-btn",
|
|
2632
|
+
onClick: onBind,
|
|
2633
|
+
disabled: isLoading,
|
|
2634
|
+
children: isLoading ? messages.loading : messages.connectWallet ?? "Connect Wallet"
|
|
2635
|
+
}
|
|
2636
|
+
)
|
|
2637
|
+
] });
|
|
2638
|
+
}
|
|
2639
|
+
if (data.isBound && data.address) {
|
|
2640
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-network-card taskon-network-card--single", children: [
|
|
2641
|
+
/* @__PURE__ */ jsx(
|
|
2642
|
+
"button",
|
|
2643
|
+
{
|
|
2644
|
+
className: "taskon-network-unbind-badge taskon-network-unbind-badge--corner",
|
|
2645
|
+
onClick: () => onUnbind(),
|
|
2646
|
+
disabled: isLoading || disabled,
|
|
2647
|
+
title: disabledReason,
|
|
2648
|
+
children: messages.unbind ?? "Unbind"
|
|
2649
|
+
}
|
|
2650
|
+
),
|
|
2651
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__header", children: /* @__PURE__ */ jsx("span", { className: "taskon-network-card__label", children: name }) }),
|
|
2652
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__chain-icon", children: /* @__PURE__ */ jsx(ChainIcon, { chain: data.coreChainType }) }),
|
|
2653
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-network-address-input", children: [
|
|
2654
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-network-address-input__text", children: truncateAddress(data.address.address) }),
|
|
2655
|
+
/* @__PURE__ */ jsx(CopyButton, { text: data.address.address })
|
|
2656
|
+
] })
|
|
2657
|
+
] });
|
|
2658
|
+
}
|
|
2659
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-network-card taskon-network-card--single", children: [
|
|
2660
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__header", children: /* @__PURE__ */ jsx("span", { className: "taskon-network-card__label", children: name }) }),
|
|
2661
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-card__chain-icon", children: /* @__PURE__ */ jsx(ChainIcon, { chain: data.coreChainType }) }),
|
|
2662
|
+
/* @__PURE__ */ jsx(
|
|
2663
|
+
"button",
|
|
2664
|
+
{
|
|
2665
|
+
className: "taskon-network-connect-btn",
|
|
2666
|
+
onClick: onBind,
|
|
2667
|
+
disabled: isLoading,
|
|
2668
|
+
children: isLoading ? messages.loading : messages.connectWallet ?? "Connect Wallet"
|
|
2669
|
+
}
|
|
2670
|
+
)
|
|
2671
|
+
] });
|
|
2672
|
+
}
|
|
2673
|
+
function WarningIcon() {
|
|
2674
|
+
return /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx(
|
|
2675
|
+
"path",
|
|
2676
|
+
{
|
|
2677
|
+
d: "M12 9V13M12 17H12.01M10.29 3.86L1.82 18C1.64 18.3 1.55 18.65 1.55 19C1.56 19.35 1.65 19.69 1.82 20C2 20.3 2.25 20.56 2.55 20.73C2.85 20.91 3.19 21 3.54 21H20.46C20.81 21 21.15 20.91 21.45 20.73C21.75 20.56 22 20.3 22.18 20C22.35 19.69 22.44 19.35 22.45 19C22.45 18.65 22.36 18.3 22.18 18L13.71 3.86C13.53 3.56 13.28 3.32 12.98 3.15C12.68 2.98 12.34 2.89 12 2.89C11.66 2.89 11.32 2.98 11.02 3.15C10.72 3.32 10.47 3.56 10.29 3.86Z",
|
|
2678
|
+
stroke: "currentColor",
|
|
2679
|
+
strokeWidth: "2",
|
|
2680
|
+
strokeLinecap: "round",
|
|
2681
|
+
strokeLinejoin: "round"
|
|
2682
|
+
}
|
|
2683
|
+
) });
|
|
2684
|
+
}
|
|
2685
|
+
function ConfirmUnlinkDialog({
|
|
2686
|
+
open,
|
|
2687
|
+
onOpenChange,
|
|
2688
|
+
onConfirm,
|
|
2689
|
+
onCancel,
|
|
2690
|
+
loading = false,
|
|
2691
|
+
messages
|
|
2692
|
+
}) {
|
|
2693
|
+
return /* @__PURE__ */ jsx(
|
|
2694
|
+
Dialog,
|
|
2695
|
+
{
|
|
2696
|
+
open,
|
|
2697
|
+
onOpenChange,
|
|
2698
|
+
title: messages.confirmUnbind,
|
|
2699
|
+
showCloseButton: false,
|
|
2700
|
+
maxWidth: 400,
|
|
2701
|
+
children: /* @__PURE__ */ jsxs("div", { className: "taskon-confirm-unlink", children: [
|
|
2702
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-confirm-unlink__icon", children: /* @__PURE__ */ jsx(WarningIcon, {}) }),
|
|
2703
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-confirm-unlink__text", children: [
|
|
2704
|
+
/* @__PURE__ */ jsx("h4", { className: "taskon-confirm-unlink__title", children: messages.confirmUnbind }),
|
|
2705
|
+
/* @__PURE__ */ jsx("p", { className: "taskon-confirm-unlink__description", children: messages.unbindWarning })
|
|
2706
|
+
] }),
|
|
2707
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-confirm-unlink__actions", children: [
|
|
2708
|
+
/* @__PURE__ */ jsx(
|
|
2709
|
+
"button",
|
|
2710
|
+
{
|
|
2711
|
+
className: "taskon-confirm-unlink__btn taskon-confirm-unlink__btn--cancel",
|
|
2712
|
+
onClick: onCancel,
|
|
2713
|
+
disabled: loading,
|
|
2714
|
+
children: messages.cancel
|
|
2715
|
+
}
|
|
2716
|
+
),
|
|
2717
|
+
/* @__PURE__ */ jsx(
|
|
2718
|
+
"button",
|
|
2719
|
+
{
|
|
2720
|
+
className: "taskon-confirm-unlink__btn taskon-confirm-unlink__btn--confirm",
|
|
2721
|
+
onClick: onConfirm,
|
|
2722
|
+
disabled: loading,
|
|
2723
|
+
children: loading ? messages.unbinding : messages.confirm
|
|
2724
|
+
}
|
|
2725
|
+
)
|
|
2726
|
+
] })
|
|
2727
|
+
] })
|
|
2728
|
+
}
|
|
2729
|
+
);
|
|
2730
|
+
}
|
|
2731
|
+
function SocialAccountsSection({
|
|
2732
|
+
accounts,
|
|
2733
|
+
messages,
|
|
2734
|
+
onBind,
|
|
2735
|
+
onUnbind,
|
|
2736
|
+
isProcessing,
|
|
2737
|
+
checkUnlink,
|
|
2738
|
+
onShowWarning
|
|
2739
|
+
}) {
|
|
2740
|
+
if (accounts.length === 0) {
|
|
2741
|
+
return null;
|
|
2742
|
+
}
|
|
2743
|
+
const [confirmTarget, setConfirmTarget] = useState(null);
|
|
2744
|
+
const showConfirm = confirmTarget !== null;
|
|
2745
|
+
const handleBind = useCallback(
|
|
2746
|
+
(account) => {
|
|
2747
|
+
onBind(account.snsType);
|
|
2748
|
+
},
|
|
2749
|
+
[onBind]
|
|
2750
|
+
);
|
|
2751
|
+
const handleUnbind = useCallback(
|
|
2752
|
+
(account) => {
|
|
2753
|
+
if (!account.account) return;
|
|
2754
|
+
const check = checkUnlink(account.account);
|
|
2755
|
+
if (check.disabled) {
|
|
2756
|
+
onShowWarning(check.message ?? messages.unbindFailed);
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
setConfirmTarget(account);
|
|
2760
|
+
},
|
|
2761
|
+
[checkUnlink, onShowWarning, messages]
|
|
2762
|
+
);
|
|
2763
|
+
const handleConfirmUnbind = useCallback(() => {
|
|
2764
|
+
if (!(confirmTarget == null ? void 0 : confirmTarget.account)) return;
|
|
2765
|
+
onUnbind(confirmTarget.account.sns_id, confirmTarget.snsType);
|
|
2766
|
+
setConfirmTarget(null);
|
|
2767
|
+
}, [confirmTarget, onUnbind]);
|
|
2768
|
+
return /* @__PURE__ */ jsxs("section", { className: "taskon-identity-social-section", children: [
|
|
2769
|
+
/* @__PURE__ */ jsx("h3", { className: "taskon-identity-social-section__title", children: messages.socialAccounts ?? "Social Media Accounts" }),
|
|
2770
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-identity-social-grid", children: accounts.map((account) => {
|
|
2771
|
+
const check = account.account ? checkUnlink(account.account) : { disabled: false, message: null };
|
|
2772
|
+
return /* @__PURE__ */ jsx(
|
|
2773
|
+
SocialAccountItem,
|
|
2774
|
+
{
|
|
2775
|
+
data: account,
|
|
2776
|
+
onBind: () => handleBind(account),
|
|
2777
|
+
onUnbind: () => handleUnbind(account),
|
|
2778
|
+
isLoading: isProcessing(account.snsType),
|
|
2779
|
+
disabled: check.disabled,
|
|
2780
|
+
disabledReason: check.message ?? void 0,
|
|
2781
|
+
messages
|
|
2782
|
+
},
|
|
2783
|
+
account.snsType
|
|
2784
|
+
);
|
|
2785
|
+
}) }),
|
|
2786
|
+
/* @__PURE__ */ jsx(
|
|
2787
|
+
ConfirmUnlinkDialog,
|
|
2788
|
+
{
|
|
2789
|
+
open: showConfirm,
|
|
2790
|
+
onOpenChange: (open) => {
|
|
2791
|
+
if (!open) setConfirmTarget(null);
|
|
2792
|
+
},
|
|
2793
|
+
onConfirm: handleConfirmUnbind,
|
|
2794
|
+
onCancel: () => setConfirmTarget(null),
|
|
2795
|
+
loading: confirmTarget ? isProcessing(confirmTarget.snsType) : false,
|
|
2796
|
+
messages
|
|
2797
|
+
}
|
|
2798
|
+
)
|
|
2799
|
+
] });
|
|
2800
|
+
}
|
|
2801
|
+
function WalletsSection({
|
|
2802
|
+
wallets,
|
|
2803
|
+
rawAddresses,
|
|
2804
|
+
messages,
|
|
2805
|
+
onBind,
|
|
2806
|
+
onUnbind,
|
|
2807
|
+
isProcessing,
|
|
2808
|
+
checkUnlink,
|
|
2809
|
+
onShowWarning,
|
|
2810
|
+
onShowInfo
|
|
2811
|
+
}) {
|
|
2812
|
+
if (wallets.length === 0) {
|
|
2813
|
+
return null;
|
|
2814
|
+
}
|
|
2815
|
+
const evmAddresses = useMemo(() => {
|
|
2816
|
+
return rawAddresses.filter((addr) => addr.chain_type === "evm").map((addr) => ({
|
|
2817
|
+
chainType: "EVM",
|
|
2818
|
+
// UserCenterChainType.EVM
|
|
2819
|
+
coreChainType: "evm",
|
|
2820
|
+
isBound: true,
|
|
2821
|
+
address: addr,
|
|
2822
|
+
isPrimary: addr.is_primary ?? false,
|
|
2823
|
+
isKycVerified: void 0
|
|
2824
|
+
}));
|
|
2825
|
+
}, [rawAddresses]);
|
|
2826
|
+
const [confirmTarget, setConfirmTarget] = useState(null);
|
|
2827
|
+
const showConfirm = confirmTarget !== null;
|
|
2828
|
+
const handleBind = useCallback(
|
|
2829
|
+
(wallet) => {
|
|
2830
|
+
onBind(wallet.coreChainType);
|
|
2831
|
+
},
|
|
2832
|
+
[onBind]
|
|
2833
|
+
);
|
|
2834
|
+
const handleUnbind = useCallback(
|
|
2835
|
+
(wallet, specificAddress) => {
|
|
2836
|
+
var _a;
|
|
2837
|
+
const addressToUnbind = specificAddress ?? ((_a = wallet.address) == null ? void 0 : _a.address);
|
|
2838
|
+
if (!addressToUnbind || !wallet.address) return;
|
|
2839
|
+
const check = checkUnlink(wallet.address, wallet.isKycVerified);
|
|
2840
|
+
if (check.disabled) {
|
|
2841
|
+
onShowWarning(check.message ?? messages.unbindFailed);
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
if (check.needsKycWarning) {
|
|
2845
|
+
onShowInfo(messages.kycWarning);
|
|
2846
|
+
}
|
|
2847
|
+
setConfirmTarget({ wallet, address: addressToUnbind });
|
|
2848
|
+
},
|
|
2849
|
+
[checkUnlink, onShowWarning, onShowInfo, messages]
|
|
2850
|
+
);
|
|
2851
|
+
const handleConfirmUnbind = useCallback(() => {
|
|
2852
|
+
if (!confirmTarget) return;
|
|
2853
|
+
onUnbind(confirmTarget.wallet.coreChainType, confirmTarget.address);
|
|
2854
|
+
setConfirmTarget(null);
|
|
2855
|
+
}, [confirmTarget, onUnbind]);
|
|
2856
|
+
return /* @__PURE__ */ jsxs("section", { className: "taskon-network-section", children: [
|
|
2857
|
+
/* @__PURE__ */ jsx("h3", { className: "taskon-network-section__title", children: messages.walletAddresses ?? "Network List" }),
|
|
2858
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-network-grid", children: wallets.map((wallet) => {
|
|
2859
|
+
const check = wallet.address ? checkUnlink(wallet.address, wallet.isKycVerified) : { disabled: false, message: null };
|
|
2860
|
+
const isEvm = wallet.coreChainType === "evm";
|
|
2861
|
+
return /* @__PURE__ */ jsx(
|
|
2862
|
+
WalletItem,
|
|
2863
|
+
{
|
|
2864
|
+
data: wallet,
|
|
2865
|
+
addresses: isEvm && evmAddresses.length > 0 ? evmAddresses : void 0,
|
|
2866
|
+
onBind: () => handleBind(wallet),
|
|
2867
|
+
onUnbind: (addr) => handleUnbind(wallet, addr),
|
|
2868
|
+
isLoading: isProcessing(wallet.chainType),
|
|
2869
|
+
disabled: check.disabled,
|
|
2870
|
+
disabledReason: check.message ?? void 0,
|
|
2871
|
+
messages
|
|
2872
|
+
},
|
|
2873
|
+
wallet.coreChainType
|
|
2874
|
+
);
|
|
2875
|
+
}) }),
|
|
2876
|
+
/* @__PURE__ */ jsx(
|
|
2877
|
+
ConfirmUnlinkDialog,
|
|
2878
|
+
{
|
|
2879
|
+
open: showConfirm,
|
|
2880
|
+
onOpenChange: (open) => {
|
|
2881
|
+
if (!open) setConfirmTarget(null);
|
|
2882
|
+
},
|
|
2883
|
+
onConfirm: handleConfirmUnbind,
|
|
2884
|
+
onCancel: () => setConfirmTarget(null),
|
|
2885
|
+
loading: confirmTarget ? isProcessing(confirmTarget.wallet.chainType) : false,
|
|
2886
|
+
messages
|
|
2887
|
+
}
|
|
2888
|
+
)
|
|
2889
|
+
] });
|
|
2890
|
+
}
|
|
2891
|
+
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2892
|
+
function EmailBindDialog({
|
|
2893
|
+
visible,
|
|
2894
|
+
status,
|
|
2895
|
+
countdown,
|
|
2896
|
+
isEmailDuplicate,
|
|
2897
|
+
isCodeInvalid,
|
|
2898
|
+
messages,
|
|
2899
|
+
onClose,
|
|
2900
|
+
onSendCode,
|
|
2901
|
+
onBind,
|
|
2902
|
+
onClearError
|
|
2903
|
+
}) {
|
|
2904
|
+
const [email, setEmail] = useState("");
|
|
2905
|
+
const [code, setCode] = useState("");
|
|
2906
|
+
const isEmailValid = useMemo(() => EMAIL_REGEX.test(email), [email]);
|
|
2907
|
+
const isCodeValid = useMemo(() => code.length >= 4, [code]);
|
|
2908
|
+
const isSending = status === "sendingCode";
|
|
2909
|
+
const isSubmitting = status === "submitting";
|
|
2910
|
+
const showCodeInput = status === "inputCode" || status === "submitting" || status === "success";
|
|
2911
|
+
const emailError = useMemo(() => {
|
|
2912
|
+
if (isEmailDuplicate) {
|
|
2913
|
+
return messages.emailAlreadyLinked ?? "This email is already linked to another account";
|
|
2914
|
+
}
|
|
2915
|
+
if (email && !isEmailValid) {
|
|
2916
|
+
return messages.pleaseEnterValidEmail ?? "Please enter a valid email address";
|
|
2917
|
+
}
|
|
2918
|
+
return void 0;
|
|
2919
|
+
}, [isEmailDuplicate, email, isEmailValid, messages]);
|
|
2920
|
+
const codeError = useMemo(() => {
|
|
2921
|
+
if (isCodeInvalid) {
|
|
2922
|
+
return messages.pleaseEnterValidCode ?? "Please enter a valid verification code";
|
|
2923
|
+
}
|
|
2924
|
+
return void 0;
|
|
2925
|
+
}, [isCodeInvalid, messages]);
|
|
2926
|
+
const handleEmailChange = useCallback(
|
|
2927
|
+
(value) => {
|
|
2928
|
+
setEmail(value);
|
|
2929
|
+
onClearError();
|
|
2930
|
+
},
|
|
2931
|
+
[onClearError]
|
|
2932
|
+
);
|
|
2933
|
+
const handleCodeChange = useCallback(
|
|
2934
|
+
(value) => {
|
|
2935
|
+
const numericValue = value.replace(/[^0-9]/g, "").slice(0, 6);
|
|
2936
|
+
setCode(numericValue);
|
|
2937
|
+
onClearError();
|
|
2938
|
+
},
|
|
2939
|
+
[onClearError]
|
|
2940
|
+
);
|
|
2941
|
+
const handleSendCode = useCallback(async () => {
|
|
2942
|
+
if (!isEmailValid || isSending) return;
|
|
2943
|
+
await onSendCode(email);
|
|
2944
|
+
}, [email, isEmailValid, isSending, onSendCode]);
|
|
2945
|
+
const handleBind = useCallback(async () => {
|
|
2946
|
+
if (!isEmailValid || !isCodeValid || isSubmitting) return;
|
|
2947
|
+
const result = await onBind(email, code);
|
|
2948
|
+
if (result == null ? void 0 : result.result) {
|
|
2949
|
+
setEmail("");
|
|
2950
|
+
setCode("");
|
|
2951
|
+
onClose();
|
|
2952
|
+
}
|
|
2953
|
+
}, [email, code, isEmailValid, isCodeValid, isSubmitting, onBind, onClose]);
|
|
2954
|
+
const handleClose = useCallback(() => {
|
|
2955
|
+
setEmail("");
|
|
2956
|
+
setCode("");
|
|
2957
|
+
onClose();
|
|
2958
|
+
}, [onClose]);
|
|
2959
|
+
return /* @__PURE__ */ jsx(
|
|
2960
|
+
Dialog,
|
|
2961
|
+
{
|
|
2962
|
+
open: visible,
|
|
2963
|
+
onOpenChange: (open) => {
|
|
2964
|
+
if (!open) handleClose();
|
|
2965
|
+
},
|
|
2966
|
+
title: messages.linkEmailAccount ?? "Link Email Account",
|
|
2967
|
+
showCloseButton: true,
|
|
2968
|
+
maxWidth: 400,
|
|
2969
|
+
children: /* @__PURE__ */ jsxs("div", { className: "taskon-email-bind-dialog", children: [
|
|
2970
|
+
/* @__PURE__ */ jsx("h2", { className: "taskon-email-bind-dialog__title", children: messages.linkEmailAccount ?? "Link Email Account" }),
|
|
2971
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-email-bind-dialog__field", children: [
|
|
2972
|
+
/* @__PURE__ */ jsx("label", { className: "taskon-email-bind-dialog__label", children: messages.emailAddress ?? "Email Address" }),
|
|
2973
|
+
/* @__PURE__ */ jsx(
|
|
2974
|
+
Input,
|
|
2975
|
+
{
|
|
2976
|
+
type: "email",
|
|
2977
|
+
value: email,
|
|
2978
|
+
onChange: handleEmailChange,
|
|
2979
|
+
placeholder: messages.pleaseEnterEmail ?? "Please enter email address",
|
|
2980
|
+
maxLength: 64,
|
|
2981
|
+
disabled: showCodeInput,
|
|
2982
|
+
error: emailError,
|
|
2983
|
+
onBlur: () => setEmail(email.trim())
|
|
2984
|
+
}
|
|
2985
|
+
)
|
|
2986
|
+
] }),
|
|
2987
|
+
showCodeInput && /* @__PURE__ */ jsxs("div", { className: "taskon-email-bind-dialog__field", children: [
|
|
2988
|
+
/* @__PURE__ */ jsx("label", { className: "taskon-email-bind-dialog__label", children: messages.verificationCode ?? "Verification Code" }),
|
|
2989
|
+
/* @__PURE__ */ jsx(
|
|
2990
|
+
Input,
|
|
2991
|
+
{
|
|
2992
|
+
type: "text",
|
|
2993
|
+
inputMode: "numeric",
|
|
2994
|
+
value: code,
|
|
2995
|
+
onChange: handleCodeChange,
|
|
2996
|
+
placeholder: messages.pleaseEnterCode ?? "Please enter verification code",
|
|
2997
|
+
maxLength: 6,
|
|
2998
|
+
disabled: isSubmitting,
|
|
2999
|
+
error: codeError,
|
|
3000
|
+
rightSlot: /* @__PURE__ */ jsx(
|
|
3001
|
+
Button,
|
|
3002
|
+
{
|
|
3003
|
+
variant: "ghost",
|
|
3004
|
+
size: "small",
|
|
3005
|
+
disabled: countdown > 0 || isSending,
|
|
3006
|
+
loading: isSending,
|
|
3007
|
+
onClick: handleSendCode,
|
|
3008
|
+
children: countdown > 0 ? `${messages.resendCode ?? "Resend"} (${countdown}s)` : messages.resendCode ?? "Resend"
|
|
3009
|
+
}
|
|
3010
|
+
)
|
|
3011
|
+
}
|
|
3012
|
+
)
|
|
3013
|
+
] }),
|
|
3014
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-email-bind-dialog__actions", children: !showCodeInput ? (
|
|
3015
|
+
/* 发送验证码按钮 */
|
|
3016
|
+
/* @__PURE__ */ jsx(
|
|
3017
|
+
Button,
|
|
3018
|
+
{
|
|
3019
|
+
variant: "primary",
|
|
3020
|
+
size: "large",
|
|
3021
|
+
disabled: !isEmailValid || isSending,
|
|
3022
|
+
loading: isSending,
|
|
3023
|
+
onClick: handleSendCode,
|
|
3024
|
+
style: { width: "100%" },
|
|
3025
|
+
children: messages.sendVerificationCode ?? "Send Verification Code"
|
|
3026
|
+
}
|
|
3027
|
+
)
|
|
3028
|
+
) : (
|
|
3029
|
+
/* 确认绑定按钮 */
|
|
3030
|
+
/* @__PURE__ */ jsx(
|
|
3031
|
+
Button,
|
|
3032
|
+
{
|
|
3033
|
+
variant: "primary",
|
|
3034
|
+
size: "large",
|
|
3035
|
+
disabled: !isEmailValid || !isCodeValid || isSubmitting,
|
|
3036
|
+
loading: isSubmitting,
|
|
3037
|
+
onClick: handleBind,
|
|
3038
|
+
style: { width: "100%" },
|
|
3039
|
+
children: messages.confirm ?? "Confirm"
|
|
3040
|
+
}
|
|
3041
|
+
)
|
|
3042
|
+
) })
|
|
3043
|
+
] })
|
|
3044
|
+
}
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
function EmailSection({
|
|
3048
|
+
emailAccount,
|
|
3049
|
+
messages,
|
|
3050
|
+
onUnbind,
|
|
3051
|
+
isUnbinding,
|
|
3052
|
+
checkUnlink,
|
|
3053
|
+
onShowSuccess,
|
|
3054
|
+
onShowError,
|
|
3055
|
+
onShowWarning
|
|
3056
|
+
}) {
|
|
3057
|
+
const [dialogVisible, setDialogVisible] = useState(false);
|
|
3058
|
+
const {
|
|
3059
|
+
status: emailBindStatus,
|
|
3060
|
+
countdown: emailCountdown,
|
|
3061
|
+
isEmailDuplicate,
|
|
3062
|
+
isCodeInvalid,
|
|
3063
|
+
sendCode: sendEmailCode,
|
|
3064
|
+
bind: bindEmail,
|
|
3065
|
+
reset: resetEmailBind,
|
|
3066
|
+
clearError: clearEmailError
|
|
3067
|
+
} = useBindEmail({
|
|
3068
|
+
onSendCodeSuccess: () => {
|
|
3069
|
+
onShowSuccess(messages.sendCodeSuccess ?? "Verification code sent");
|
|
3070
|
+
},
|
|
3071
|
+
onSendCodeError: (error) => {
|
|
3072
|
+
if (!error.message.includes("duplicate")) {
|
|
3073
|
+
onShowError(messages.sendCodeFailed ?? "Failed to send verification code");
|
|
3074
|
+
}
|
|
3075
|
+
},
|
|
3076
|
+
onBindSuccess: () => {
|
|
3077
|
+
onShowSuccess(messages.bindSuccess);
|
|
3078
|
+
setDialogVisible(false);
|
|
3079
|
+
},
|
|
3080
|
+
onBindError: () => {
|
|
3081
|
+
onShowError(messages.bindFailed);
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
const handleBind = useCallback(() => {
|
|
3085
|
+
setDialogVisible(true);
|
|
3086
|
+
}, []);
|
|
3087
|
+
const handleUnbind = useCallback(() => {
|
|
3088
|
+
if (!emailAccount.account) return;
|
|
3089
|
+
const check2 = checkUnlink(emailAccount.account);
|
|
3090
|
+
if (check2.disabled) {
|
|
3091
|
+
onShowWarning(check2.message ?? messages.unbindFailed);
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
onUnbind(emailAccount.account.sns_id, "Email");
|
|
3095
|
+
}, [emailAccount, checkUnlink, onUnbind, onShowWarning, messages]);
|
|
3096
|
+
const handleDialogClose = useCallback(() => {
|
|
3097
|
+
setDialogVisible(false);
|
|
3098
|
+
resetEmailBind();
|
|
3099
|
+
}, [resetEmailBind]);
|
|
3100
|
+
const check = emailAccount.account ? checkUnlink(emailAccount.account) : { disabled: false, message: null };
|
|
3101
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3102
|
+
/* @__PURE__ */ jsxs("section", { className: "taskon-identity-email-section", children: [
|
|
3103
|
+
/* @__PURE__ */ jsx("h3", { className: "taskon-identity-email-section__title", children: messages.emailAddress ?? "Email Address" }),
|
|
3104
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-identity-email-card", children: /* @__PURE__ */ jsx(
|
|
3105
|
+
SocialAccountItem,
|
|
3106
|
+
{
|
|
3107
|
+
data: emailAccount,
|
|
3108
|
+
onBind: handleBind,
|
|
3109
|
+
onUnbind: handleUnbind,
|
|
3110
|
+
isLoading: isUnbinding,
|
|
3111
|
+
disabled: check.disabled,
|
|
3112
|
+
disabledReason: check.message ?? void 0,
|
|
3113
|
+
messages
|
|
3114
|
+
}
|
|
3115
|
+
) })
|
|
3116
|
+
] }),
|
|
3117
|
+
/* @__PURE__ */ jsx(
|
|
3118
|
+
EmailBindDialog,
|
|
3119
|
+
{
|
|
3120
|
+
visible: dialogVisible,
|
|
3121
|
+
status: emailBindStatus,
|
|
3122
|
+
countdown: emailCountdown,
|
|
3123
|
+
isEmailDuplicate,
|
|
3124
|
+
isCodeInvalid,
|
|
3125
|
+
messages,
|
|
3126
|
+
onClose: handleDialogClose,
|
|
3127
|
+
onSendCode: sendEmailCode,
|
|
3128
|
+
onBind: bindEmail,
|
|
3129
|
+
onClearError: clearEmailError
|
|
3130
|
+
}
|
|
3131
|
+
)
|
|
3132
|
+
] });
|
|
3133
|
+
}
|
|
3134
|
+
function IdentityContent({
|
|
3135
|
+
config,
|
|
3136
|
+
messages
|
|
3137
|
+
}) {
|
|
3138
|
+
const identityTab = config.find((t) => t.tab === "Identity");
|
|
3139
|
+
const enabledAccounts = identityTab ? filterEnabledAccounts(identityTab) : [];
|
|
3140
|
+
const enabledWallets = identityTab ? filterEnabledWallets(identityTab) : [];
|
|
3141
|
+
const { socialAccounts, walletAddresses, rawAddresses, isLoaded } = useIdentityData();
|
|
3142
|
+
const { checkSocialUnlink, checkWalletUnlink } = useDisableUnlink();
|
|
3143
|
+
const { toast } = useToast();
|
|
3144
|
+
const {
|
|
3145
|
+
unbind: unbindSocial,
|
|
3146
|
+
unbindStatus: socialUnbindStatus,
|
|
3147
|
+
processingType: socialProcessingType
|
|
3148
|
+
} = useUnbindSocial({
|
|
3149
|
+
onUnbindSuccess: () => {
|
|
3150
|
+
toast.success(messages.unbindSuccess);
|
|
3151
|
+
},
|
|
3152
|
+
onUnbindError: () => {
|
|
3153
|
+
toast.error(messages.unbindFailed);
|
|
3154
|
+
}
|
|
3155
|
+
});
|
|
3156
|
+
const { bind: bindOAuth, isBinding: isOAuthBinding, isOAuthSupported } = useOAuthBindings({
|
|
3157
|
+
onSuccess: () => {
|
|
3158
|
+
toast.success(messages.bindSuccess);
|
|
3159
|
+
},
|
|
3160
|
+
onFailed: (error) => {
|
|
3161
|
+
toast.error(error || messages.bindFailed);
|
|
3162
|
+
}
|
|
3163
|
+
});
|
|
3164
|
+
const {
|
|
3165
|
+
bind: bindWallet,
|
|
3166
|
+
unbind: unbindWallet,
|
|
3167
|
+
bindStatus: walletBindStatus,
|
|
3168
|
+
unbindStatus: walletUnbindStatus,
|
|
3169
|
+
processingChain: walletProcessingChain
|
|
3170
|
+
} = useBindWallet({
|
|
3171
|
+
onBindSuccess: () => {
|
|
3172
|
+
toast.success(messages.bindSuccess);
|
|
3173
|
+
},
|
|
3174
|
+
onUnbindSuccess: () => {
|
|
3175
|
+
toast.success(messages.unbindSuccess);
|
|
3176
|
+
},
|
|
3177
|
+
onBindError: () => {
|
|
3178
|
+
toast.error(messages.bindFailed);
|
|
3179
|
+
},
|
|
3180
|
+
onUnbindError: () => {
|
|
3181
|
+
toast.error(messages.unbindFailed);
|
|
3182
|
+
}
|
|
3183
|
+
});
|
|
3184
|
+
const filteredSocialAccounts = useMemo(() => {
|
|
3185
|
+
return socialAccounts.filter((account) => {
|
|
3186
|
+
if (enabledAccounts.length === 0) return true;
|
|
3187
|
+
return enabledAccounts.some(
|
|
3188
|
+
(cfg) => cfg.account.toLowerCase() === account.snsType.toLowerCase()
|
|
3189
|
+
);
|
|
3190
|
+
});
|
|
3191
|
+
}, [socialAccounts, enabledAccounts]);
|
|
3192
|
+
const filteredWalletAddresses = useMemo(() => {
|
|
3193
|
+
return walletAddresses.filter((wallet) => {
|
|
3194
|
+
if (enabledWallets.length === 0) return true;
|
|
3195
|
+
return enabledWallets.some(
|
|
3196
|
+
(cfg) => cfg.chain.toLowerCase() === wallet.coreChainType.toLowerCase()
|
|
3197
|
+
);
|
|
3198
|
+
});
|
|
3199
|
+
}, [walletAddresses, enabledWallets]);
|
|
3200
|
+
const emailAccount = useMemo(() => {
|
|
3201
|
+
return filteredSocialAccounts.find((a) => a.snsType === SnsType.Email);
|
|
3202
|
+
}, [filteredSocialAccounts]);
|
|
3203
|
+
const socialAccountsWithoutEmail = useMemo(() => {
|
|
3204
|
+
return filteredSocialAccounts.filter((a) => a.snsType !== SnsType.Email);
|
|
3205
|
+
}, [filteredSocialAccounts]);
|
|
3206
|
+
const handleSocialBind = useCallback(
|
|
3207
|
+
(snsType) => {
|
|
3208
|
+
if (isOAuthSupported(snsType)) {
|
|
3209
|
+
bindOAuth(snsType);
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
toast.info(messages.bindSocialTodo ?? "This binding method is not yet supported");
|
|
3213
|
+
},
|
|
3214
|
+
[bindOAuth, isOAuthSupported, toast, messages]
|
|
3215
|
+
);
|
|
3216
|
+
const isSocialProcessing = useCallback(
|
|
3217
|
+
(snsType) => {
|
|
3218
|
+
if (isOAuthBinding(snsType)) return true;
|
|
3219
|
+
return String(socialProcessingType).toLowerCase() === snsType.toLowerCase() && socialUnbindStatus === "loading";
|
|
3220
|
+
},
|
|
3221
|
+
[isOAuthBinding, socialProcessingType, socialUnbindStatus]
|
|
3222
|
+
);
|
|
3223
|
+
const handleWalletBind = useCallback(
|
|
3224
|
+
(chainType) => {
|
|
3225
|
+
bindWallet(chainType);
|
|
3226
|
+
},
|
|
3227
|
+
[bindWallet]
|
|
3228
|
+
);
|
|
3229
|
+
const handleWalletUnbind = useCallback(
|
|
3230
|
+
(chainType, address) => {
|
|
3231
|
+
unbindWallet(chainType, address);
|
|
3232
|
+
},
|
|
3233
|
+
[unbindWallet]
|
|
3234
|
+
);
|
|
3235
|
+
const isWalletProcessing = useCallback(
|
|
3236
|
+
(chainType) => {
|
|
3237
|
+
return String(walletProcessingChain) === String(chainType) && (walletBindStatus !== "idle" || walletUnbindStatus !== "idle");
|
|
3238
|
+
},
|
|
3239
|
+
[walletProcessingChain, walletBindStatus, walletUnbindStatus]
|
|
3240
|
+
);
|
|
3241
|
+
const isEmailUnbinding = useMemo(() => {
|
|
3242
|
+
return socialProcessingType === SnsType.Email && socialUnbindStatus === "loading";
|
|
3243
|
+
}, [socialProcessingType, socialUnbindStatus]);
|
|
3244
|
+
if (!isLoaded) {
|
|
3245
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-identity", children: /* @__PURE__ */ jsx("div", { className: "taskon-identity-loading", children: messages.loading }) });
|
|
3246
|
+
}
|
|
3247
|
+
const isEmpty = socialAccountsWithoutEmail.length === 0 && !emailAccount && filteredWalletAddresses.length === 0;
|
|
3248
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-identity", children: [
|
|
3249
|
+
/* @__PURE__ */ jsx(
|
|
3250
|
+
SocialAccountsSection,
|
|
3251
|
+
{
|
|
3252
|
+
accounts: socialAccountsWithoutEmail,
|
|
3253
|
+
messages,
|
|
3254
|
+
onBind: handleSocialBind,
|
|
3255
|
+
onUnbind: unbindSocial,
|
|
3256
|
+
isProcessing: isSocialProcessing,
|
|
3257
|
+
checkUnlink: checkSocialUnlink,
|
|
3258
|
+
onShowWarning: toast.warning
|
|
3259
|
+
}
|
|
3260
|
+
),
|
|
3261
|
+
/* @__PURE__ */ jsx(
|
|
3262
|
+
WalletsSection,
|
|
3263
|
+
{
|
|
3264
|
+
wallets: filteredWalletAddresses,
|
|
3265
|
+
rawAddresses,
|
|
3266
|
+
messages,
|
|
3267
|
+
onBind: handleWalletBind,
|
|
3268
|
+
onUnbind: handleWalletUnbind,
|
|
3269
|
+
isProcessing: isWalletProcessing,
|
|
3270
|
+
checkUnlink: checkWalletUnlink,
|
|
3271
|
+
onShowWarning: toast.warning,
|
|
3272
|
+
onShowInfo: toast.info
|
|
3273
|
+
}
|
|
3274
|
+
),
|
|
3275
|
+
emailAccount && /* @__PURE__ */ jsx(
|
|
3276
|
+
EmailSection,
|
|
3277
|
+
{
|
|
3278
|
+
emailAccount,
|
|
3279
|
+
messages,
|
|
3280
|
+
onUnbind: unbindSocial,
|
|
3281
|
+
isUnbinding: isEmailUnbinding,
|
|
3282
|
+
checkUnlink: checkSocialUnlink,
|
|
3283
|
+
onShowSuccess: toast.success,
|
|
3284
|
+
onShowError: toast.error,
|
|
3285
|
+
onShowWarning: toast.warning
|
|
3286
|
+
}
|
|
3287
|
+
),
|
|
3288
|
+
isEmpty && /* @__PURE__ */ jsx("div", { className: "taskon-identity-empty", children: messages.noData ?? "No identity options configured" })
|
|
3289
|
+
] });
|
|
3290
|
+
}
|
|
3291
|
+
function LoadingSpinner() {
|
|
3292
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-claim-dialog-spinner", children: /* @__PURE__ */ jsx("div", { className: "taskon-claim-dialog-spinner__circle" }) });
|
|
3293
|
+
}
|
|
3294
|
+
function SuccessIcon() {
|
|
3295
|
+
return /* @__PURE__ */ jsxs(
|
|
3296
|
+
"svg",
|
|
3297
|
+
{
|
|
3298
|
+
className: "taskon-claim-dialog-icon taskon-claim-dialog-icon--success",
|
|
3299
|
+
viewBox: "0 0 24 24",
|
|
3300
|
+
fill: "none",
|
|
3301
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3302
|
+
children: [
|
|
3303
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
|
|
3304
|
+
/* @__PURE__ */ jsx(
|
|
3305
|
+
"path",
|
|
3306
|
+
{
|
|
3307
|
+
d: "M8 12L11 15L16 9",
|
|
3308
|
+
stroke: "currentColor",
|
|
3309
|
+
strokeWidth: "2",
|
|
3310
|
+
strokeLinecap: "round",
|
|
3311
|
+
strokeLinejoin: "round"
|
|
3312
|
+
}
|
|
3313
|
+
)
|
|
3314
|
+
]
|
|
3315
|
+
}
|
|
3316
|
+
);
|
|
3317
|
+
}
|
|
3318
|
+
function ErrorIcon() {
|
|
3319
|
+
return /* @__PURE__ */ jsxs(
|
|
3320
|
+
"svg",
|
|
3321
|
+
{
|
|
3322
|
+
className: "taskon-claim-dialog-icon taskon-claim-dialog-icon--error",
|
|
3323
|
+
viewBox: "0 0 24 24",
|
|
3324
|
+
fill: "none",
|
|
3325
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3326
|
+
children: [
|
|
3327
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
|
|
3328
|
+
/* @__PURE__ */ jsx(
|
|
3329
|
+
"path",
|
|
3330
|
+
{
|
|
3331
|
+
d: "M15 9L9 15M9 9L15 15",
|
|
3332
|
+
stroke: "currentColor",
|
|
3333
|
+
strokeWidth: "2",
|
|
3334
|
+
strokeLinecap: "round"
|
|
3335
|
+
}
|
|
3336
|
+
)
|
|
3337
|
+
]
|
|
3338
|
+
}
|
|
3339
|
+
);
|
|
3340
|
+
}
|
|
3341
|
+
function getStatusMessage(status, messages) {
|
|
3342
|
+
switch (status) {
|
|
3343
|
+
case "checking":
|
|
3344
|
+
return messages.claimingNft ?? "Claiming NFT...";
|
|
3345
|
+
case "connecting":
|
|
3346
|
+
return messages.claimConnectingWallet ?? "Connecting wallet...";
|
|
3347
|
+
case "switching":
|
|
3348
|
+
return messages.claimSwitchingNetwork ?? "Switching network...";
|
|
3349
|
+
case "signing":
|
|
3350
|
+
return messages.claimGettingSignature ?? "Getting signature...";
|
|
3351
|
+
case "confirming":
|
|
3352
|
+
return messages.claimConfirmInWallet ?? "Please confirm in your wallet";
|
|
3353
|
+
case "pending":
|
|
3354
|
+
return messages.claimTransactionPending ?? "Transaction pending...";
|
|
3355
|
+
case "success":
|
|
3356
|
+
return messages.claimSuccess ?? "Claim successful!";
|
|
3357
|
+
case "error":
|
|
3358
|
+
return messages.claimFailed ?? "Claim failed";
|
|
3359
|
+
default:
|
|
3360
|
+
return "";
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
function formatTxHash(hash) {
|
|
3364
|
+
if (hash.length <= 16) return hash;
|
|
3365
|
+
return `${hash.slice(0, 10)}...${hash.slice(-6)}`;
|
|
3366
|
+
}
|
|
3367
|
+
function ClaimNftDialog({
|
|
3368
|
+
open,
|
|
3369
|
+
onClose,
|
|
3370
|
+
status,
|
|
3371
|
+
error,
|
|
3372
|
+
txHash,
|
|
3373
|
+
claimingNft,
|
|
3374
|
+
explorerUrl,
|
|
3375
|
+
messages,
|
|
3376
|
+
onRetry
|
|
3377
|
+
}) {
|
|
3378
|
+
const isLoading = [
|
|
3379
|
+
"checking",
|
|
3380
|
+
"connecting",
|
|
3381
|
+
"switching",
|
|
3382
|
+
"signing",
|
|
3383
|
+
"confirming",
|
|
3384
|
+
"pending"
|
|
3385
|
+
].includes(status);
|
|
3386
|
+
const canClose = status === "success" || status === "error" || status === "idle";
|
|
3387
|
+
const handleClose = () => {
|
|
3388
|
+
if (canClose) {
|
|
3389
|
+
onClose();
|
|
3390
|
+
}
|
|
3391
|
+
};
|
|
3392
|
+
const handleViewTx = () => {
|
|
3393
|
+
if (explorerUrl) {
|
|
3394
|
+
window.open(explorerUrl, "_blank", "noopener,noreferrer");
|
|
3395
|
+
}
|
|
3396
|
+
};
|
|
3397
|
+
return /* @__PURE__ */ jsx(
|
|
3398
|
+
Dialog,
|
|
3399
|
+
{
|
|
3400
|
+
open,
|
|
3401
|
+
onOpenChange: (isOpen) => {
|
|
3402
|
+
if (!isOpen && canClose) {
|
|
3403
|
+
onClose();
|
|
3404
|
+
}
|
|
3405
|
+
},
|
|
3406
|
+
title: messages.claimNft ?? "Claim NFT",
|
|
3407
|
+
showCloseButton: canClose,
|
|
3408
|
+
contentClassName: "taskon-claim-dialog",
|
|
3409
|
+
maxWidth: 400,
|
|
3410
|
+
children: /* @__PURE__ */ jsxs("div", { className: "taskon-claim-dialog-content", children: [
|
|
3411
|
+
claimingNft && /* @__PURE__ */ jsx("div", { className: "taskon-claim-dialog-nft", children: claimingNft.item.reward_value.collection_image && /* @__PURE__ */ jsx(
|
|
3412
|
+
"img",
|
|
3413
|
+
{
|
|
3414
|
+
src: claimingNft.item.reward_value.collection_image,
|
|
3415
|
+
alt: "NFT",
|
|
3416
|
+
className: "taskon-claim-dialog-nft__image"
|
|
3417
|
+
}
|
|
3418
|
+
) }),
|
|
3419
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-claim-dialog-status", children: [
|
|
3420
|
+
isLoading && /* @__PURE__ */ jsx(LoadingSpinner, {}),
|
|
3421
|
+
status === "success" && /* @__PURE__ */ jsx(SuccessIcon, {}),
|
|
3422
|
+
status === "error" && /* @__PURE__ */ jsx(ErrorIcon, {})
|
|
3423
|
+
] }),
|
|
3424
|
+
/* @__PURE__ */ jsx("p", { className: "taskon-claim-dialog-message", children: status === "error" && error ? error.message : getStatusMessage(status, messages) }),
|
|
3425
|
+
txHash && (status === "pending" || status === "success") && /* @__PURE__ */ jsxs("div", { className: "taskon-claim-dialog-tx", children: [
|
|
3426
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-claim-dialog-tx__label", children: "Tx:" }),
|
|
3427
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-claim-dialog-tx__hash", children: formatTxHash(txHash) })
|
|
3428
|
+
] }),
|
|
3429
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-claim-dialog-actions", children: [
|
|
3430
|
+
txHash && explorerUrl && /* @__PURE__ */ jsx(
|
|
3431
|
+
"button",
|
|
3432
|
+
{
|
|
3433
|
+
type: "button",
|
|
3434
|
+
className: "taskon-claim-dialog-btn taskon-claim-dialog-btn--secondary",
|
|
3435
|
+
onClick: handleViewTx,
|
|
3436
|
+
children: messages.viewOnExplorer ?? "View on Explorer"
|
|
3437
|
+
}
|
|
3438
|
+
),
|
|
3439
|
+
status === "error" && onRetry && /* @__PURE__ */ jsx(
|
|
3440
|
+
"button",
|
|
3441
|
+
{
|
|
3442
|
+
type: "button",
|
|
3443
|
+
className: "taskon-claim-dialog-btn taskon-claim-dialog-btn--primary",
|
|
3444
|
+
onClick: onRetry,
|
|
3445
|
+
children: messages.retry ?? "Retry"
|
|
3446
|
+
}
|
|
3447
|
+
),
|
|
3448
|
+
canClose && /* @__PURE__ */ jsx(
|
|
3449
|
+
"button",
|
|
3450
|
+
{
|
|
3451
|
+
type: "button",
|
|
3452
|
+
className: "taskon-claim-dialog-btn taskon-claim-dialog-btn--primary",
|
|
3453
|
+
onClick: handleClose,
|
|
3454
|
+
children: messages.close ?? "Close"
|
|
3455
|
+
}
|
|
3456
|
+
)
|
|
3457
|
+
] })
|
|
3458
|
+
] })
|
|
3459
|
+
}
|
|
3460
|
+
);
|
|
3461
|
+
}
|
|
3462
|
+
function NftRewardContent({
|
|
3463
|
+
nftList,
|
|
3464
|
+
loading,
|
|
3465
|
+
error,
|
|
3466
|
+
pagination,
|
|
3467
|
+
messages,
|
|
3468
|
+
onClaim: customOnClaim,
|
|
3469
|
+
onTxClick: customOnTxClick,
|
|
3470
|
+
onClaimSuccess,
|
|
3471
|
+
onClaimError
|
|
3472
|
+
}) {
|
|
3473
|
+
const { chainMap } = useChainMap();
|
|
3474
|
+
const [isClaimDialogOpen, setIsClaimDialogOpen] = useState(false);
|
|
3475
|
+
const {
|
|
3476
|
+
status: claimStatus,
|
|
3477
|
+
error: claimError,
|
|
3478
|
+
txHash: claimTxHash,
|
|
3479
|
+
claimingNft,
|
|
3480
|
+
claimNft,
|
|
3481
|
+
reset: resetClaim
|
|
3482
|
+
} = useNftClaim({
|
|
3483
|
+
onSuccess: (txHash, item) => {
|
|
3484
|
+
onClaimSuccess == null ? void 0 : onClaimSuccess(txHash, item);
|
|
3485
|
+
},
|
|
3486
|
+
onError: (err, item) => {
|
|
3487
|
+
onClaimError == null ? void 0 : onClaimError(err, item);
|
|
3488
|
+
},
|
|
3489
|
+
onPendingFound: (pendingKey, txHash, chainName) => {
|
|
3490
|
+
console.log("Pending transaction found:", pendingKey, txHash, chainName);
|
|
3491
|
+
}
|
|
3492
|
+
});
|
|
3493
|
+
const explorerUrl = useMemo(() => {
|
|
3494
|
+
if (!claimTxHash || !claimingNft) return void 0;
|
|
3495
|
+
const chainName = claimingNft.chainName;
|
|
3496
|
+
const chainInfo = chainMap[chainName.toLowerCase()];
|
|
3497
|
+
if (!chainInfo) return void 0;
|
|
3498
|
+
return getTxExplorerUrl(chainInfo, claimTxHash);
|
|
3499
|
+
}, [claimTxHash, claimingNft, chainMap]);
|
|
3500
|
+
const handleNftClaim = useCallback(
|
|
3501
|
+
(item) => {
|
|
3502
|
+
if (customOnClaim) {
|
|
3503
|
+
customOnClaim(item);
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3506
|
+
setIsClaimDialogOpen(true);
|
|
3507
|
+
claimNft(item);
|
|
3508
|
+
},
|
|
3509
|
+
[customOnClaim, claimNft]
|
|
3510
|
+
);
|
|
3511
|
+
const handleNftTxClick = useCallback(
|
|
3512
|
+
(item) => {
|
|
3513
|
+
if (customOnTxClick) {
|
|
3514
|
+
customOnTxClick(item);
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
const nftValue = item.reward_value;
|
|
3518
|
+
if (!nftValue.tx_hash) {
|
|
3519
|
+
console.warn("No tx_hash found for NFT item:", item);
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
const chainName = getNftChainName(nftValue);
|
|
3523
|
+
if (!chainName) {
|
|
3524
|
+
console.warn("No chain info found for NFT item:", item);
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
const chainInfo = chainMap[chainName.toLowerCase()];
|
|
3528
|
+
if (!chainInfo) {
|
|
3529
|
+
console.warn(`Chain info not found for: ${chainName}`);
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
const txUrl = getTxExplorerUrl(chainInfo, nftValue.tx_hash);
|
|
3533
|
+
openInNewTab(txUrl);
|
|
3534
|
+
},
|
|
3535
|
+
[customOnTxClick, chainMap]
|
|
3536
|
+
);
|
|
3537
|
+
const handleCloseDialog = useCallback(() => {
|
|
3538
|
+
setIsClaimDialogOpen(false);
|
|
3539
|
+
setTimeout(() => {
|
|
3540
|
+
resetClaim();
|
|
3541
|
+
}, 200);
|
|
3542
|
+
}, [resetClaim]);
|
|
3543
|
+
const handleRetry = useCallback(() => {
|
|
3544
|
+
if (claimingNft) {
|
|
3545
|
+
resetClaim();
|
|
3546
|
+
claimNft(claimingNft.item);
|
|
3547
|
+
}
|
|
3548
|
+
}, [claimingNft, resetClaim, claimNft]);
|
|
3549
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3550
|
+
/* @__PURE__ */ jsx(
|
|
3551
|
+
NftTable,
|
|
3552
|
+
{
|
|
3553
|
+
data: nftList,
|
|
3554
|
+
loading,
|
|
3555
|
+
error,
|
|
3556
|
+
pagination,
|
|
3557
|
+
messages,
|
|
3558
|
+
onClaim: handleNftClaim,
|
|
3559
|
+
onTxClick: handleNftTxClick
|
|
3560
|
+
}
|
|
3561
|
+
),
|
|
3562
|
+
/* @__PURE__ */ jsx(
|
|
3563
|
+
ClaimNftDialog,
|
|
3564
|
+
{
|
|
3565
|
+
open: isClaimDialogOpen,
|
|
3566
|
+
onClose: handleCloseDialog,
|
|
3567
|
+
status: claimStatus,
|
|
3568
|
+
error: claimError,
|
|
3569
|
+
txHash: claimTxHash,
|
|
3570
|
+
claimingNft,
|
|
3571
|
+
explorerUrl,
|
|
3572
|
+
messages,
|
|
3573
|
+
onRetry: handleRetry
|
|
3574
|
+
}
|
|
3575
|
+
)
|
|
3576
|
+
] });
|
|
3577
|
+
}
|
|
3578
|
+
function MyRewardsContent({
|
|
3579
|
+
messages,
|
|
3580
|
+
defaultRewardCard,
|
|
3581
|
+
defaultPointId
|
|
3582
|
+
}) {
|
|
3583
|
+
const [selectedPointsId, setSelectedPointsId] = useState(null);
|
|
3584
|
+
const hasUserSelectedRef = useRef(false);
|
|
3585
|
+
const appliedDefaultKeyRef = useRef(null);
|
|
3586
|
+
const [showWithdrawForm, setShowWithdrawForm] = useState(false);
|
|
3587
|
+
const [selectedTokenForWithdraw, setSelectedTokenForWithdraw] = useState(null);
|
|
3588
|
+
const {
|
|
3589
|
+
cards,
|
|
3590
|
+
selectedCard,
|
|
3591
|
+
selectCard,
|
|
3592
|
+
loading: rewardsLoading,
|
|
3593
|
+
error: rewardsError
|
|
3594
|
+
} = useUserRewards({});
|
|
3595
|
+
const selectedPointsData = useMemo(() => {
|
|
3596
|
+
if (selectedCard !== USER_CENTER_REWARD_CARD_TYPES.Points) return null;
|
|
3597
|
+
const selectedCardData = cards.find(
|
|
3598
|
+
(c) => {
|
|
3599
|
+
var _a;
|
|
3600
|
+
return c.type === selectedCard && ((_a = c.pointsData) == null ? void 0 : _a.points_id) === selectedPointsId;
|
|
3601
|
+
}
|
|
3602
|
+
);
|
|
3603
|
+
return (selectedCardData == null ? void 0 : selectedCardData.pointsData) ?? null;
|
|
3604
|
+
}, [selectedCard, cards, selectedPointsId]);
|
|
3605
|
+
const selectedXpLevelData = useMemo(() => {
|
|
3606
|
+
if (selectedCard !== USER_CENTER_REWARD_CARD_TYPES.XpLevel) return null;
|
|
3607
|
+
const xpLevelCard = cards.find(
|
|
3608
|
+
(c) => c.type === USER_CENTER_REWARD_CARD_TYPES.XpLevel
|
|
3609
|
+
);
|
|
3610
|
+
return (xpLevelCard == null ? void 0 : xpLevelCard.xpLevelData) ?? null;
|
|
3611
|
+
}, [selectedCard, cards]);
|
|
3612
|
+
const {
|
|
3613
|
+
data: tokenAssets,
|
|
3614
|
+
loading: tokenAssetsLoading,
|
|
3615
|
+
error: tokenAssetsError,
|
|
3616
|
+
pendingWithdrawals,
|
|
3617
|
+
refresh: refreshTokenAssets
|
|
3618
|
+
} = useTokenAssets({
|
|
3619
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.Token
|
|
3620
|
+
});
|
|
3621
|
+
const {
|
|
3622
|
+
data: tokenHistory,
|
|
3623
|
+
loading: tokenHistoryLoading,
|
|
3624
|
+
error: tokenHistoryError,
|
|
3625
|
+
pagination: tokenHistoryPagination
|
|
3626
|
+
} = useRewardDetails({
|
|
3627
|
+
rewardType: RewardType.Token,
|
|
3628
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.Token
|
|
3629
|
+
});
|
|
3630
|
+
const {
|
|
3631
|
+
data: nftList,
|
|
3632
|
+
loading: nftLoading,
|
|
3633
|
+
error: nftError,
|
|
3634
|
+
pagination: nftPagination,
|
|
3635
|
+
refresh: refreshNftList
|
|
3636
|
+
} = useRewardDetails({
|
|
3637
|
+
rewardType: RewardType.Nft,
|
|
3638
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.Nft
|
|
3639
|
+
});
|
|
3640
|
+
const {
|
|
3641
|
+
data: whitelistList,
|
|
3642
|
+
loading: whitelistLoading,
|
|
3643
|
+
error: whitelistError,
|
|
3644
|
+
pagination: whitelistPagination
|
|
3645
|
+
} = useRewardDetails({
|
|
3646
|
+
rewardType: RewardType.Whitelist,
|
|
3647
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.Whitelist
|
|
3648
|
+
});
|
|
3649
|
+
const {
|
|
3650
|
+
data: discordRoleList,
|
|
3651
|
+
loading: discordRoleLoading,
|
|
3652
|
+
error: discordRoleError,
|
|
3653
|
+
pagination: discordRolePagination
|
|
3654
|
+
} = useRewardDetails({
|
|
3655
|
+
rewardType: RewardType.DiscordRole,
|
|
3656
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.DiscordRole
|
|
3657
|
+
});
|
|
3658
|
+
const {
|
|
3659
|
+
data: pointsHistory,
|
|
3660
|
+
loading: pointsLoading,
|
|
3661
|
+
error: pointsError,
|
|
3662
|
+
pagination: pointsPagination
|
|
3663
|
+
} = usePointsHistory({
|
|
3664
|
+
pointsId: (selectedPointsData == null ? void 0 : selectedPointsData.points_id) ?? 0,
|
|
3665
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.Points && !!selectedPointsData
|
|
3666
|
+
});
|
|
3667
|
+
const {
|
|
3668
|
+
data: xpLevelHistory,
|
|
3669
|
+
loading: xpLevelHistoryLoading,
|
|
3670
|
+
error: xpLevelHistoryError,
|
|
3671
|
+
pagination: xpLevelHistoryPagination
|
|
3672
|
+
} = usePointsHistory({
|
|
3673
|
+
pointsId: (selectedXpLevelData == null ? void 0 : selectedXpLevelData.xpPointsId) ?? 0,
|
|
3674
|
+
autoLoad: selectedCard === USER_CENTER_REWARD_CARD_TYPES.XpLevel && !!selectedXpLevelData
|
|
3675
|
+
});
|
|
3676
|
+
useGasFreeWithdraw({
|
|
3677
|
+
onSuccess: () => refreshTokenAssets(),
|
|
3678
|
+
onError: (err) => console.error("Gas-free withdraw failed:", err)
|
|
3679
|
+
});
|
|
3680
|
+
const handleWithdraw = (token) => {
|
|
3681
|
+
setSelectedTokenForWithdraw(token);
|
|
3682
|
+
setShowWithdrawForm(true);
|
|
3683
|
+
};
|
|
3684
|
+
const handleBatchWithdraw = () => {
|
|
3685
|
+
setSelectedTokenForWithdraw(null);
|
|
3686
|
+
setShowWithdrawForm(true);
|
|
3687
|
+
};
|
|
3688
|
+
if (rewardsLoading && cards.length === 0) {
|
|
3689
|
+
return /* @__PURE__ */ jsx(LoadingState, { message: messages.loading });
|
|
3690
|
+
}
|
|
3691
|
+
if (rewardsError && cards.length === 0) {
|
|
3692
|
+
return /* @__PURE__ */ jsx("div", { className: "taskon-user-center-error", children: /* @__PURE__ */ jsx("p", { className: "taskon-user-center-error__message", children: rewardsError.message }) });
|
|
3693
|
+
}
|
|
3694
|
+
const hasVisibleCards = cards.some((c) => c.visible);
|
|
3695
|
+
if (!rewardsLoading && !hasVisibleCards) {
|
|
3696
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.emptyRewards });
|
|
3697
|
+
}
|
|
3698
|
+
const handleSelectCard = (type, pointsId) => {
|
|
3699
|
+
hasUserSelectedRef.current = true;
|
|
3700
|
+
selectCard(type);
|
|
3701
|
+
setSelectedPointsId(pointsId ?? null);
|
|
3702
|
+
};
|
|
3703
|
+
useEffect(() => {
|
|
3704
|
+
var _a, _b;
|
|
3705
|
+
if (!defaultRewardCard) return;
|
|
3706
|
+
if (hasUserSelectedRef.current) return;
|
|
3707
|
+
if (cards.length === 0) return;
|
|
3708
|
+
const applyKey = `${defaultRewardCard}:${defaultPointId ?? ""}`;
|
|
3709
|
+
if (appliedDefaultKeyRef.current === applyKey) return;
|
|
3710
|
+
const visibleDefaultCard = cards.find(
|
|
3711
|
+
(card) => card.visible && card.type === defaultRewardCard
|
|
3712
|
+
);
|
|
3713
|
+
if (!visibleDefaultCard) {
|
|
3714
|
+
appliedDefaultKeyRef.current = applyKey;
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
selectCard(defaultRewardCard);
|
|
3718
|
+
if (defaultRewardCard === USER_CENTER_REWARD_CARD_TYPES.Points) {
|
|
3719
|
+
const matchedPointsCard = cards.find(
|
|
3720
|
+
(card) => {
|
|
3721
|
+
var _a2;
|
|
3722
|
+
return card.type === USER_CENTER_REWARD_CARD_TYPES.Points && ((_a2 = card.pointsData) == null ? void 0 : _a2.points_id) === defaultPointId;
|
|
3723
|
+
}
|
|
3724
|
+
);
|
|
3725
|
+
if (((_a = matchedPointsCard == null ? void 0 : matchedPointsCard.pointsData) == null ? void 0 : _a.points_id) != null) {
|
|
3726
|
+
setSelectedPointsId(matchedPointsCard.pointsData.points_id);
|
|
3727
|
+
} else if (((_b = visibleDefaultCard.pointsData) == null ? void 0 : _b.points_id) != null) {
|
|
3728
|
+
setSelectedPointsId(visibleDefaultCard.pointsData.points_id);
|
|
3729
|
+
}
|
|
3730
|
+
} else {
|
|
3731
|
+
setSelectedPointsId(null);
|
|
3732
|
+
}
|
|
3733
|
+
appliedDefaultKeyRef.current = applyKey;
|
|
3734
|
+
}, [cards, defaultPointId, defaultRewardCard, selectCard]);
|
|
3735
|
+
const renderSelectedContent = () => {
|
|
3736
|
+
switch (selectedCard) {
|
|
3737
|
+
case USER_CENTER_REWARD_CARD_TYPES.Token:
|
|
3738
|
+
return /* @__PURE__ */ jsx(
|
|
3739
|
+
TokenRewardContent,
|
|
3740
|
+
{
|
|
3741
|
+
tokenAssets,
|
|
3742
|
+
tokenAssetsLoading,
|
|
3743
|
+
tokenAssetsError,
|
|
3744
|
+
pendingWithdrawals,
|
|
3745
|
+
tokenHistory,
|
|
3746
|
+
tokenHistoryLoading,
|
|
3747
|
+
tokenHistoryError,
|
|
3748
|
+
tokenHistoryPagination,
|
|
3749
|
+
messages,
|
|
3750
|
+
onWithdraw: handleWithdraw,
|
|
3751
|
+
onBatchWithdraw: handleBatchWithdraw
|
|
3752
|
+
}
|
|
3753
|
+
);
|
|
3754
|
+
case USER_CENTER_REWARD_CARD_TYPES.XpLevel:
|
|
3755
|
+
if (!selectedXpLevelData) {
|
|
3756
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.noData });
|
|
3757
|
+
}
|
|
3758
|
+
return /* @__PURE__ */ jsx(
|
|
3759
|
+
XpLevelCard,
|
|
3760
|
+
{
|
|
3761
|
+
xpData: {
|
|
3762
|
+
level: selectedXpLevelData.level,
|
|
3763
|
+
currentXp: selectedXpLevelData.currentXp,
|
|
3764
|
+
nextLevelXp: selectedXpLevelData.nextLevelXp,
|
|
3765
|
+
totalXp: selectedXpLevelData.currentXp
|
|
3766
|
+
},
|
|
3767
|
+
historyData: xpLevelHistory,
|
|
3768
|
+
historyLoading: xpLevelHistoryLoading,
|
|
3769
|
+
historyError: xpLevelHistoryError,
|
|
3770
|
+
pagination: xpLevelHistoryPagination,
|
|
3771
|
+
messages
|
|
3772
|
+
}
|
|
3773
|
+
);
|
|
3774
|
+
case USER_CENTER_REWARD_CARD_TYPES.Nft:
|
|
3775
|
+
return /* @__PURE__ */ jsx(
|
|
3776
|
+
NftRewardContent,
|
|
3777
|
+
{
|
|
3778
|
+
nftList,
|
|
3779
|
+
loading: nftLoading,
|
|
3780
|
+
error: nftError,
|
|
3781
|
+
pagination: nftPagination,
|
|
3782
|
+
messages,
|
|
3783
|
+
onClaimSuccess: () => {
|
|
3784
|
+
refreshNftList();
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
);
|
|
3788
|
+
case USER_CENTER_REWARD_CARD_TYPES.Whitelist:
|
|
3789
|
+
return /* @__PURE__ */ jsx(
|
|
3790
|
+
WhitelistTable,
|
|
3791
|
+
{
|
|
3792
|
+
data: whitelistList,
|
|
3793
|
+
loading: whitelistLoading,
|
|
3794
|
+
error: whitelistError,
|
|
3795
|
+
pagination: whitelistPagination,
|
|
3796
|
+
messages
|
|
3797
|
+
}
|
|
3798
|
+
);
|
|
3799
|
+
case USER_CENTER_REWARD_CARD_TYPES.DiscordRole:
|
|
3800
|
+
return /* @__PURE__ */ jsx(
|
|
3801
|
+
DiscordRoleTable,
|
|
3802
|
+
{
|
|
3803
|
+
data: discordRoleList,
|
|
3804
|
+
loading: discordRoleLoading,
|
|
3805
|
+
error: discordRoleError,
|
|
3806
|
+
pagination: discordRolePagination,
|
|
3807
|
+
messages
|
|
3808
|
+
}
|
|
3809
|
+
);
|
|
3810
|
+
case USER_CENTER_REWARD_CARD_TYPES.Points:
|
|
3811
|
+
if (!selectedPointsData) {
|
|
3812
|
+
return /* @__PURE__ */ jsx(EmptyState, { message: messages.emptyPoints });
|
|
3813
|
+
}
|
|
3814
|
+
return /* @__PURE__ */ jsx(
|
|
3815
|
+
PointsList,
|
|
3816
|
+
{
|
|
3817
|
+
pointsInfo: selectedPointsData,
|
|
3818
|
+
data: pointsHistory,
|
|
3819
|
+
loading: pointsLoading,
|
|
3820
|
+
error: pointsError,
|
|
3821
|
+
pagination: pointsPagination,
|
|
3822
|
+
messages
|
|
3823
|
+
}
|
|
3824
|
+
);
|
|
3825
|
+
default:
|
|
3826
|
+
return null;
|
|
3827
|
+
}
|
|
3828
|
+
};
|
|
3829
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-my-rewards", children: [
|
|
3830
|
+
/* @__PURE__ */ jsx(
|
|
3831
|
+
AssetCarousel,
|
|
3832
|
+
{
|
|
3833
|
+
cards,
|
|
3834
|
+
selectedCard,
|
|
3835
|
+
selectedPointsId,
|
|
3836
|
+
onSelectCard: handleSelectCard,
|
|
3837
|
+
messages
|
|
3838
|
+
}
|
|
3839
|
+
),
|
|
3840
|
+
renderSelectedContent(),
|
|
3841
|
+
/* @__PURE__ */ jsx(
|
|
3842
|
+
WithdrawForm,
|
|
3843
|
+
{
|
|
3844
|
+
open: showWithdrawForm,
|
|
3845
|
+
messages,
|
|
3846
|
+
tokenAssets,
|
|
3847
|
+
tokenAssetsLoading,
|
|
3848
|
+
initialTokenId: selectedTokenForWithdraw == null ? void 0 : selectedTokenForWithdraw.token_id,
|
|
3849
|
+
initialChain: selectedTokenForWithdraw == null ? void 0 : selectedTokenForWithdraw.chain,
|
|
3850
|
+
onClose: () => setShowWithdrawForm(false),
|
|
3851
|
+
onSuccess: () => {
|
|
3852
|
+
refreshTokenAssets();
|
|
3853
|
+
setShowWithdrawForm(false);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
)
|
|
3857
|
+
] });
|
|
3858
|
+
}
|
|
3859
|
+
function ActivityHistoryContent({
|
|
3860
|
+
messages
|
|
3861
|
+
}) {
|
|
3862
|
+
const { data, loading, error, pagination, refresh } = useActivityHistory({
|
|
3863
|
+
mode: "pagination"
|
|
3864
|
+
});
|
|
3865
|
+
return /* @__PURE__ */ jsx(
|
|
3866
|
+
ActivityHistoryList,
|
|
3867
|
+
{
|
|
3868
|
+
data,
|
|
3869
|
+
loading,
|
|
3870
|
+
error,
|
|
3871
|
+
pagination,
|
|
3872
|
+
messages,
|
|
3873
|
+
mode: "pagination",
|
|
3874
|
+
onRetry: refresh
|
|
3875
|
+
}
|
|
3876
|
+
);
|
|
3877
|
+
}
|
|
3878
|
+
function mergeUserCenterConfig(props, cloud) {
|
|
3879
|
+
return {
|
|
3880
|
+
config: props.config ?? cloud ?? getDefaultTabConfig()
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
const TAB_LABEL_KEYS = {
|
|
3884
|
+
[UserCenterTabType.MyRewards]: "tabMyRewards",
|
|
3885
|
+
[UserCenterTabType.Identity]: "tabIdentity",
|
|
3886
|
+
[UserCenterTabType.ActivityHistory]: "tabActivityHistory"
|
|
3887
|
+
};
|
|
3888
|
+
const TAB_DEFAULT_LABELS = {
|
|
3889
|
+
[UserCenterTabType.MyRewards]: "My Rewards",
|
|
3890
|
+
[UserCenterTabType.Identity]: "Identity",
|
|
3891
|
+
[UserCenterTabType.ActivityHistory]: "Activity History"
|
|
3892
|
+
};
|
|
3893
|
+
function UserCenterWidget(props) {
|
|
3894
|
+
const { widgetId } = props;
|
|
3895
|
+
const { functionConfig, cloudTheme, isConfigLoading, configError } = useResolvedWidgetConfig(widgetId);
|
|
3896
|
+
const mergedConfig = useMemo(() => {
|
|
3897
|
+
return mergeUserCenterConfig(
|
|
3898
|
+
props,
|
|
3899
|
+
functionConfig ?? null
|
|
3900
|
+
);
|
|
3901
|
+
}, [props.config, functionConfig]);
|
|
3902
|
+
return /* @__PURE__ */ jsx(
|
|
3903
|
+
WidgetShell,
|
|
3904
|
+
{
|
|
3905
|
+
widgetId,
|
|
3906
|
+
isConfigLoading,
|
|
3907
|
+
cloudTheme,
|
|
3908
|
+
className: "taskon-user-center",
|
|
3909
|
+
errorMessage: configError ?? void 0,
|
|
3910
|
+
children: /* @__PURE__ */ jsx(
|
|
3911
|
+
UserCenterWidgetInner,
|
|
3912
|
+
{
|
|
3913
|
+
...props,
|
|
3914
|
+
config: mergedConfig.config
|
|
3915
|
+
}
|
|
3916
|
+
)
|
|
3917
|
+
}
|
|
3918
|
+
);
|
|
3919
|
+
}
|
|
3920
|
+
function UserCenterWidgetInner({
|
|
3921
|
+
config = getDefaultTabConfig(),
|
|
3922
|
+
defaultTab,
|
|
3923
|
+
defaultRewardCard,
|
|
3924
|
+
defaultPointId,
|
|
3925
|
+
onTabChange,
|
|
3926
|
+
className = "",
|
|
3927
|
+
style
|
|
3928
|
+
}) {
|
|
3929
|
+
var _a;
|
|
3930
|
+
const { isInitializing } = useTaskOnContext();
|
|
3931
|
+
const { messages, isLoading: isLocaleLoading } = useWidgetLocale({
|
|
3932
|
+
widgetId: "UserCenterWidget",
|
|
3933
|
+
defaultMessages: enMessages,
|
|
3934
|
+
loadMessages: (locale) => __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./locales/en.json": () => import("./UserCenterWidget-BE329iS7.js").then((n) => n.n), "./locales/ja.json": () => import("./usercenter-ja-uu-XfVF9.js"), "./locales/ko.json": () => import("./usercenter-ko-DYgUOVzd.js") }), `./locales/${locale}.json`, 3)
|
|
3935
|
+
});
|
|
3936
|
+
const enabledTabs = useMemo(() => filterEnabledTabs(config), [config]);
|
|
3937
|
+
const tabItems = useMemo(() => {
|
|
3938
|
+
return enabledTabs.map((tabConfig) => ({
|
|
3939
|
+
key: tabConfig.tab,
|
|
3940
|
+
label: messages[TAB_LABEL_KEYS[tabConfig.tab]] ?? TAB_DEFAULT_LABELS[tabConfig.tab]
|
|
3941
|
+
}));
|
|
3942
|
+
}, [enabledTabs, messages]);
|
|
3943
|
+
const initialTab = defaultTab && enabledTabs.some((t) => t.tab === defaultTab) ? defaultTab : ((_a = enabledTabs[0]) == null ? void 0 : _a.tab) ?? UserCenterTabType.MyRewards;
|
|
3944
|
+
const [activeTab, setActiveTab] = useState(initialTab);
|
|
3945
|
+
const handleTabChange = (tab) => {
|
|
3946
|
+
setActiveTab(tab);
|
|
3947
|
+
onTabChange == null ? void 0 : onTabChange(tab);
|
|
3948
|
+
};
|
|
3949
|
+
if (isInitializing || isLocaleLoading) {
|
|
3950
|
+
return /* @__PURE__ */ jsx("div", { className: `taskon-user-center ${className}`, style, children: /* @__PURE__ */ jsx(LoadingState, { message: messages.loading }) });
|
|
3951
|
+
}
|
|
3952
|
+
if (enabledTabs.length === 0) {
|
|
3953
|
+
return /* @__PURE__ */ jsx("div", { className: `taskon-user-center ${className}`, style, children: /* @__PURE__ */ jsx("div", { className: "taskon-user-center-empty", children: messages.noData }) });
|
|
3954
|
+
}
|
|
3955
|
+
return /* @__PURE__ */ jsxs("div", { className: `taskon-user-center ${className}`, style, children: [
|
|
3956
|
+
/* @__PURE__ */ jsx(Tabs, { items: tabItems, activeKey: activeTab, onChange: handleTabChange }),
|
|
3957
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-user-center-content", children: [
|
|
3958
|
+
activeTab === UserCenterTabType.MyRewards && /* @__PURE__ */ jsx(
|
|
3959
|
+
MyRewardsContent,
|
|
3960
|
+
{
|
|
3961
|
+
config,
|
|
3962
|
+
messages,
|
|
3963
|
+
defaultRewardCard,
|
|
3964
|
+
defaultPointId
|
|
3965
|
+
}
|
|
3966
|
+
),
|
|
3967
|
+
activeTab === UserCenterTabType.Identity && /* @__PURE__ */ jsx(IdentityContent, { config, messages }),
|
|
3968
|
+
activeTab === UserCenterTabType.ActivityHistory && /* @__PURE__ */ jsx(
|
|
3969
|
+
ActivityHistoryContent,
|
|
3970
|
+
{
|
|
3971
|
+
messages
|
|
3972
|
+
}
|
|
3973
|
+
)
|
|
3974
|
+
] })
|
|
3975
|
+
] });
|
|
3976
|
+
}
|
|
3977
|
+
export {
|
|
3978
|
+
ActivityHistoryList as A,
|
|
3979
|
+
UserCenterWidget as U,
|
|
3980
|
+
AssetCard as a,
|
|
3981
|
+
AssetCarousel as b,
|
|
3982
|
+
useUserRewards as c,
|
|
3983
|
+
useGasFreeWithdraw as d,
|
|
3984
|
+
useIdentityData as e,
|
|
3985
|
+
useUnbindSocial as f,
|
|
3986
|
+
useBindWallet as g,
|
|
3987
|
+
useDisableUnlink as h,
|
|
3988
|
+
useActivityHistory as u
|
|
3989
|
+
};
|