@pollar/react 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +23 -0
- package/dist/index.css.map +1 -1
- package/dist/index.js +112 -130
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -131
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { PollarClient, StellarClient, StateStatus, STATE_VAR_CODES,
|
|
2
|
+
import { PollarClient, StellarClient, StateStatus, STATE_VAR_CODES, AUTH_ERROR_CODES, WalletType } from '@pollar/core';
|
|
3
3
|
import { createContext, useState, useEffect, useMemo, useContext, useRef, Component } from 'react';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -42,52 +42,21 @@ var PollarModalFooter = () => {
|
|
|
42
42
|
] })
|
|
43
43
|
] });
|
|
44
44
|
};
|
|
45
|
-
var
|
|
46
|
-
NONE:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
EMAIL_AUTH_CODE_ERROR: { text: "Invalid code \u2014 try again" },
|
|
55
|
-
EMAIL_AUTH_CODE_SUCCESS: { text: "Code verified!" },
|
|
56
|
-
WALLET_AUTH_START: { text: "Connecting wallet\u2026" },
|
|
57
|
-
WALLET_AUTH_FREIGHTER_NOT_INSTALLED: { text: "Freighter is not installed" },
|
|
58
|
-
WALLET_AUTH_ALBEDO_NOT_INSTALLED: { text: "Albedo is not installed" },
|
|
59
|
-
WALLET_AUTH_CONNECTED: { text: "Wallet connected" },
|
|
60
|
-
WALLET_AUTH_LOGIN_START: { text: "Signing in with wallet\u2026" },
|
|
61
|
-
WALLET_AUTH_LOGIN_START_SUCCESS: { text: "Wallet signed in" },
|
|
62
|
-
WALLET_AUTH_LOGIN_START_ERROR: { text: "Failed to sign in with wallet" },
|
|
63
|
-
WALLET_AUTH_ERROR: { text: "Unknow wallet error" },
|
|
64
|
-
STREAM_POLL_START: { text: "Waiting for authentication\u2026" },
|
|
65
|
-
STREAM_POLL_EVENT: { text: "Waiting for authentication\u2026" },
|
|
66
|
-
STREAM_POLL_READY: { text: "Authenticated!" },
|
|
67
|
-
FETCH_SESSION_START: { text: "Loading session\u2026" },
|
|
68
|
-
FETCH_SESSION_SUCCESS: { text: "Welcome back!" },
|
|
69
|
-
FETCH_SESSION_ERROR: { text: "Failed to load session" },
|
|
70
|
-
NO_RESTORED_SESSION: { text: "" },
|
|
71
|
-
RESTORED_SESSION_SUCCESS: { text: "Session restored" },
|
|
72
|
-
RESTORED_SESSION_ERROR: { text: "Failed to restore session" },
|
|
73
|
-
SESSION_STORED: { text: "Session saved" },
|
|
74
|
-
ERROR_UNKNOWN: { text: "Something went wrong" },
|
|
75
|
-
ABORTED: { text: "Login cancelled" },
|
|
76
|
-
// transaction
|
|
77
|
-
BUILD_TRANSACTION_START: { text: "Building transaction\u2026" },
|
|
78
|
-
BUILD_TRANSACTION_SUCCESS: { text: "Transaction built, ready to sign and send" },
|
|
79
|
-
BUILD_TRANSACTION_ERROR: { text: "Failed to build transaction" },
|
|
80
|
-
BUILD_TRANSACTION_ERROR_NO_WALLET: { text: "No wallet connected" },
|
|
81
|
-
SIGN_SEND_TRANSACTION_START: { text: "Signing and sending transaction\u2026" },
|
|
82
|
-
SIGN_SEND_TRANSACTION_SUCCESS: { text: "Transaction signed" },
|
|
83
|
-
SIGN_SEND_TRANSACTION_ERROR: { text: "Signing rejected" }
|
|
45
|
+
var TRANSACTION_CODE_MESSAGES = {
|
|
46
|
+
NONE: "",
|
|
47
|
+
BUILD_TRANSACTION_START: "Building transaction\u2026",
|
|
48
|
+
BUILD_TRANSACTION_SUCCESS: "Transaction built, ready to sign and send",
|
|
49
|
+
BUILD_TRANSACTION_ERROR: "Failed to build transaction",
|
|
50
|
+
BUILD_TRANSACTION_ERROR_NO_WALLET: "No wallet connected",
|
|
51
|
+
SIGN_SEND_TRANSACTION_START: "Signing and sending transaction\u2026",
|
|
52
|
+
SIGN_SEND_TRANSACTION_SUCCESS: "Transaction signed",
|
|
53
|
+
SIGN_SEND_TRANSACTION_ERROR: "Signing rejected"
|
|
84
54
|
};
|
|
85
|
-
function ModalStatusBanner({
|
|
86
|
-
if (!
|
|
55
|
+
function ModalStatusBanner({ message, status, onCancel, onRetry }) {
|
|
56
|
+
if (!message && status === StateStatus.NONE) {
|
|
87
57
|
return /* @__PURE__ */ jsx("div", { className: "pollar-status" });
|
|
88
58
|
}
|
|
89
|
-
const
|
|
90
|
-
const isLoading = status === "LOADING";
|
|
59
|
+
const isLoading = status === StateStatus.LOADING;
|
|
91
60
|
const icon = status === "ERROR" ? /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
|
|
92
61
|
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
|
|
93
62
|
/* @__PURE__ */ jsx("path", { d: "M4.5 4.5l5 5M9.5 4.5l-5 5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
@@ -97,7 +66,7 @@ function ModalStatusBanner({ code, status, onCancel, onRetry }) {
|
|
|
97
66
|
] }) : status === "LOADING" ? /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeDasharray: "22 10" }) }) : null;
|
|
98
67
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-status", "data-kind": status, children: [
|
|
99
68
|
icon,
|
|
100
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
69
|
+
/* @__PURE__ */ jsx("span", { children: message }),
|
|
101
70
|
isLoading && onCancel && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onCancel, children: "Cancel" }),
|
|
102
71
|
status === StateStatus.ERROR && onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onRetry, children: "Retry" })
|
|
103
72
|
] });
|
|
@@ -208,6 +177,38 @@ var GoogleButton = ({ disabled, onClick }) => {
|
|
|
208
177
|
] })
|
|
209
178
|
] });
|
|
210
179
|
};
|
|
180
|
+
var AUTH_STATE_MESSAGES = {
|
|
181
|
+
idle: "",
|
|
182
|
+
creating_session: "Initializing\u2026",
|
|
183
|
+
entering_email: "",
|
|
184
|
+
sending_email: "Sending\u2026",
|
|
185
|
+
entering_code: "Code sent \u2014 check your inbox",
|
|
186
|
+
verifying_email_code: "Verifying\u2026",
|
|
187
|
+
opening_oauth: "Redirecting\u2026",
|
|
188
|
+
connecting_wallet: "Connecting wallet\u2026",
|
|
189
|
+
wallet_not_installed: "Wallet not installed",
|
|
190
|
+
authenticating_wallet: "Signing in with wallet\u2026",
|
|
191
|
+
authenticating: "Authenticating\u2026",
|
|
192
|
+
authenticated: "Welcome!",
|
|
193
|
+
error: ""
|
|
194
|
+
};
|
|
195
|
+
function authStateToStatus(step) {
|
|
196
|
+
const loading = [
|
|
197
|
+
"creating_session",
|
|
198
|
+
"sending_email",
|
|
199
|
+
"verifying_email_code",
|
|
200
|
+
"opening_oauth",
|
|
201
|
+
"connecting_wallet",
|
|
202
|
+
"authenticating_wallet",
|
|
203
|
+
"authenticating"
|
|
204
|
+
];
|
|
205
|
+
const success = ["authenticated", "entering_code"];
|
|
206
|
+
const error = ["error", "wallet_not_installed"];
|
|
207
|
+
if (loading.includes(step)) return StateStatus.LOADING;
|
|
208
|
+
if (success.includes(step)) return StateStatus.SUCCESS;
|
|
209
|
+
if (error.includes(step)) return StateStatus.ERROR;
|
|
210
|
+
return StateStatus.NONE;
|
|
211
|
+
}
|
|
211
212
|
function LoginModalTemplate({
|
|
212
213
|
theme,
|
|
213
214
|
accentColor,
|
|
@@ -217,17 +218,16 @@ function LoginModalTemplate({
|
|
|
217
218
|
providers,
|
|
218
219
|
appName,
|
|
219
220
|
email = "",
|
|
220
|
-
status,
|
|
221
|
-
error,
|
|
222
221
|
onEmailChange,
|
|
223
222
|
onEmailSubmit,
|
|
224
223
|
onSocialLogin,
|
|
225
224
|
onFreighterConnect,
|
|
226
225
|
onAlbedoConnect,
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
authState,
|
|
227
|
+
codeInputKey,
|
|
229
228
|
onCodeSubmit,
|
|
230
|
-
|
|
229
|
+
onBack,
|
|
230
|
+
onCancel,
|
|
231
231
|
onRetry
|
|
232
232
|
}) {
|
|
233
233
|
const isDark = theme === "dark";
|
|
@@ -245,16 +245,22 @@ function LoginModalTemplate({
|
|
|
245
245
|
"--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
|
|
246
246
|
"--pollar-error-text": isDark ? "#f87171" : "#dc2626"
|
|
247
247
|
};
|
|
248
|
+
const status = authStateToStatus(authState.step);
|
|
248
249
|
const isLoading = status === StateStatus.LOADING;
|
|
250
|
+
const isEmailCodeError = authState.step === "error" && (authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED || authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID);
|
|
251
|
+
const awaitingEmailCode = authState.step === "entering_code" || authState.step === "verifying_email_code" || isEmailCodeError;
|
|
252
|
+
const statusMessage = authState.step === "error" ? authState.message : AUTH_STATE_MESSAGES[authState.step];
|
|
249
253
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
|
|
250
254
|
/* @__PURE__ */ jsxs("div", { className: "pollar-header", children: [
|
|
251
255
|
/* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
|
|
252
256
|
/* @__PURE__ */ jsx("h2", { className: "pollar-title", children: appName }),
|
|
253
257
|
/* @__PURE__ */ jsx("p", { className: "pollar-subtitle", children: "Log in or sign up" })
|
|
254
258
|
] }),
|
|
255
|
-
awaitingEmailCode ? /* @__PURE__ */
|
|
256
|
-
|
|
257
|
-
|
|
259
|
+
awaitingEmailCode ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
260
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-back-btn", onClick: onBack, children: "\u2190 Back" }),
|
|
261
|
+
/* @__PURE__ */ jsx(EmailCodeInput, { email, onSubmit: onCodeSubmit ?? (() => {
|
|
262
|
+
}) }, codeInputKey)
|
|
263
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
258
264
|
emailEnabled && /* @__PURE__ */ jsxs("div", { className: "pollar-email-section", children: [
|
|
259
265
|
/* @__PURE__ */ jsx(
|
|
260
266
|
"input",
|
|
@@ -290,74 +296,68 @@ function LoginModalTemplate({
|
|
|
290
296
|
] })
|
|
291
297
|
] })
|
|
292
298
|
] }),
|
|
293
|
-
/* @__PURE__ */ jsx(
|
|
299
|
+
/* @__PURE__ */ jsx(
|
|
300
|
+
ModalStatusBanner,
|
|
301
|
+
{
|
|
302
|
+
message: statusMessage,
|
|
303
|
+
status,
|
|
304
|
+
onCancel,
|
|
305
|
+
onRetry: isEmailCodeError ? void 0 : onRetry
|
|
306
|
+
}
|
|
307
|
+
),
|
|
294
308
|
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
295
309
|
] });
|
|
296
310
|
}
|
|
297
|
-
function isLoginCode(code) {
|
|
298
|
-
return Object.values(STATE_VAR_CODES[PollarStateVar.AUTHENTICATION]).some((c) => code.startsWith(c));
|
|
299
|
-
}
|
|
300
311
|
function LoginModal({ onClose }) {
|
|
301
312
|
const [email, setEmail] = useState("");
|
|
302
313
|
const { getClient, styles, config } = usePollar();
|
|
303
|
-
const [
|
|
304
|
-
const [
|
|
305
|
-
const
|
|
306
|
-
const [awaitingEmailCode, setAwaitingEmailCode] = useState(false);
|
|
307
|
-
const [clientSessionId, setClientSessionId] = useState(null);
|
|
314
|
+
const [authState, setAuthState] = useState(() => getClient().getAuthState());
|
|
315
|
+
const [codeInputKey, setCodeInputKey] = useState(0);
|
|
316
|
+
const pendingEmail = useRef(null);
|
|
308
317
|
useEffect(() => {
|
|
309
|
-
return getClient().
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
setAwaitingEmailCode(true);
|
|
321
|
-
setClientSessionId(data?.content?.clientSessionId);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (stateEntry.code === STATE_VAR_CODES[PollarStateVar.AUTHENTICATION].FETCH_SESSION_SUCCESS) {
|
|
325
|
-
setAwaitingEmailCode(false);
|
|
326
|
-
setTimeout(onClose, 1e3);
|
|
327
|
-
}
|
|
318
|
+
return getClient().onAuthStateChange((next) => {
|
|
319
|
+
setAuthState(next);
|
|
320
|
+
if (next.step === "entering_email" && pendingEmail.current) {
|
|
321
|
+
getClient().sendEmailCode(pendingEmail.current);
|
|
322
|
+
pendingEmail.current = null;
|
|
323
|
+
}
|
|
324
|
+
if (next.step === "error" && next.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID) {
|
|
325
|
+
setCodeInputKey((k) => k + 1);
|
|
326
|
+
}
|
|
327
|
+
if (next.step === "authenticated") {
|
|
328
|
+
setTimeout(onClose, 1e3);
|
|
328
329
|
}
|
|
329
330
|
});
|
|
330
331
|
}, []);
|
|
331
332
|
const { theme = "light", accentColor = "#005DB4", logoUrl, emailEnabled, embeddedWallets, providers } = styles;
|
|
332
333
|
function handleClose() {
|
|
333
334
|
setEmail("");
|
|
334
|
-
|
|
335
|
-
setAwaitingEmailCode(false);
|
|
336
|
-
setClientSessionId(null);
|
|
335
|
+
getClient().cancelLogin();
|
|
337
336
|
onClose();
|
|
338
337
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
const { cancelLogin } = getClient().login({ provider: "email", email });
|
|
345
|
-
cancelLoginRef.current = cancelLogin;
|
|
338
|
+
function handleEmailSubmit() {
|
|
339
|
+
if (!email) return;
|
|
340
|
+
pendingEmail.current = email;
|
|
341
|
+
getClient().beginEmailLogin();
|
|
346
342
|
}
|
|
347
343
|
function handleSocialLogin(provider) {
|
|
348
|
-
|
|
349
|
-
cancelLoginRef.current = cancelLogin;
|
|
344
|
+
getClient().loginOAuth(provider);
|
|
350
345
|
}
|
|
351
346
|
function handleWalletConnect(type) {
|
|
352
|
-
|
|
353
|
-
cancelLoginRef.current = cancelLogin;
|
|
347
|
+
getClient().loginWallet(type);
|
|
354
348
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
349
|
+
function handleVerifyCode(code) {
|
|
350
|
+
getClient().verifyEmailCode(code);
|
|
351
|
+
}
|
|
352
|
+
function handleBack() {
|
|
353
|
+
setEmail("");
|
|
354
|
+
getClient().cancelLogin();
|
|
358
355
|
}
|
|
359
356
|
function handleRetry() {
|
|
360
357
|
getClient().logout();
|
|
358
|
+
if (styles.emailEnabled) {
|
|
359
|
+
getClient().beginEmailLogin();
|
|
360
|
+
}
|
|
361
361
|
}
|
|
362
362
|
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: handleClose, children: /* @__PURE__ */ jsx(
|
|
363
363
|
LoginModalTemplate,
|
|
@@ -376,17 +376,16 @@ function LoginModal({ onClose }) {
|
|
|
376
376
|
},
|
|
377
377
|
appName: config.application?.name ?? "Pollar",
|
|
378
378
|
email,
|
|
379
|
-
status,
|
|
380
|
-
error,
|
|
381
379
|
onEmailChange: setEmail,
|
|
382
|
-
onEmailSubmit:
|
|
380
|
+
onEmailSubmit: handleEmailSubmit,
|
|
383
381
|
onSocialLogin: handleSocialLogin,
|
|
384
382
|
onFreighterConnect: () => handleWalletConnect(WalletType.FREIGHTER),
|
|
385
383
|
onAlbedoConnect: () => handleWalletConnect(WalletType.ALBEDO),
|
|
386
|
-
|
|
387
|
-
|
|
384
|
+
authState,
|
|
385
|
+
codeInputKey,
|
|
388
386
|
onCodeSubmit: handleVerifyCode,
|
|
389
|
-
|
|
387
|
+
onBack: handleBack,
|
|
388
|
+
onCancel: () => getClient().cancelLogin(),
|
|
390
389
|
onRetry: handleRetry
|
|
391
390
|
}
|
|
392
391
|
) });
|
|
@@ -534,7 +533,7 @@ function TransactionModalTemplate({
|
|
|
534
533
|
/* @__PURE__ */ jsx(
|
|
535
534
|
ModalStatusBanner,
|
|
536
535
|
{
|
|
537
|
-
|
|
536
|
+
message: TRANSACTION_CODE_MESSAGES[transactionStateCode] ?? "",
|
|
538
537
|
status
|
|
539
538
|
}
|
|
540
539
|
),
|
|
@@ -618,13 +617,6 @@ function PollarProvider({ config, styles: propStyles, children }) {
|
|
|
618
617
|
level: "info",
|
|
619
618
|
ts: 0
|
|
620
619
|
},
|
|
621
|
-
authentication: {
|
|
622
|
-
var: "authentication",
|
|
623
|
-
code: STATE_VAR_CODES.authentication.NONE,
|
|
624
|
-
status: StateStatus.NONE,
|
|
625
|
-
level: "info",
|
|
626
|
-
ts: 0
|
|
627
|
-
},
|
|
628
620
|
transaction: {
|
|
629
621
|
var: "transaction",
|
|
630
622
|
code: STATE_VAR_CODES.transaction.NONE,
|
|
@@ -639,25 +631,18 @@ function PollarProvider({ config, styles: propStyles, children }) {
|
|
|
639
631
|
return pollarClient.onStateChange((stateEntry) => {
|
|
640
632
|
setState((prevState) => {
|
|
641
633
|
if (JSON.stringify(prevState[stateEntry.var]) !== JSON.stringify(stateEntry)) {
|
|
642
|
-
return {
|
|
643
|
-
...prevState,
|
|
644
|
-
[stateEntry.var]: stateEntry
|
|
645
|
-
};
|
|
634
|
+
return { ...prevState, [stateEntry.var]: stateEntry };
|
|
646
635
|
}
|
|
647
636
|
return prevState;
|
|
648
637
|
});
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}
|
|
658
|
-
if (stateEntry.code === STATE_VAR_CODES.authentication.LOGOUT) {
|
|
659
|
-
setSessionState(null);
|
|
660
|
-
}
|
|
638
|
+
});
|
|
639
|
+
}, [pollarClient]);
|
|
640
|
+
useEffect(() => {
|
|
641
|
+
return pollarClient.onAuthStateChange((authState) => {
|
|
642
|
+
if (authState.step === "authenticated") {
|
|
643
|
+
setSessionState((prev) => JSON.stringify(prev) !== JSON.stringify(authState.session) ? authState.session : prev);
|
|
644
|
+
} else if (authState.step === "idle") {
|
|
645
|
+
setSessionState(null);
|
|
661
646
|
}
|
|
662
647
|
});
|
|
663
648
|
}, [pollarClient]);
|
|
@@ -679,14 +664,12 @@ function PollarProvider({ config, styles: propStyles, children }) {
|
|
|
679
664
|
() => ({
|
|
680
665
|
walletAddress: sessionState?.wallet?.publicKey || "",
|
|
681
666
|
getClient: () => pollarClient,
|
|
682
|
-
// client
|
|
683
667
|
state,
|
|
684
668
|
login: (options) => pollarClient.login(options),
|
|
685
669
|
logout: () => pollarClient.logout(),
|
|
686
670
|
isAuthenticated: pollarClient.isAuthenticated(),
|
|
687
671
|
buildTx: (operation, params, options) => pollarClient.buildTx(operation, params, options),
|
|
688
672
|
submitTx: (signedXdr) => pollarClient.submitTx(signedXdr),
|
|
689
|
-
// react
|
|
690
673
|
sendTransaction: (operation, params, options) => {
|
|
691
674
|
void pollarClient.buildTx(operation, params, options);
|
|
692
675
|
setTransactionModalOpen(true);
|
|
@@ -695,7 +678,6 @@ function PollarProvider({ config, styles: propStyles, children }) {
|
|
|
695
678
|
openLoginModal: () => setLoginModalOpen(true),
|
|
696
679
|
config: remoteConfig,
|
|
697
680
|
styles,
|
|
698
|
-
// stellar
|
|
699
681
|
async getBalance(publicKey) {
|
|
700
682
|
const pk = publicKey || sessionState?.wallet?.publicKey;
|
|
701
683
|
if (pk) {
|