terry-wallet-login #15
@@ -215,6 +215,14 @@ export const enDict: Dict = {
|
||||
walletDesktopHelpRetry: "Come back here and click “Reconnect {wallet}”.",
|
||||
walletReconnectWallet: "Reconnect {wallet}",
|
||||
walletInstallWallet: "Install {wallet} extension",
|
||||
walletConfirmAddressTitle: "Log in with this {wallet} address?",
|
||||
walletConfirmAddressDesc:
|
||||
"Confirm this is the {wallet} address you want to use.",
|
||||
walletConfirmLogin: "Confirm login",
|
||||
walletCancelLogin: "Cancel",
|
||||
walletDesktopImTokenTitle: "Open this site in the mobile {wallet} app",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet} does not provide a desktop browser extension. Open https://arkie-library-stag.com inside the mobile {wallet} app to log in.",
|
||||
walletOpen: "Open",
|
||||
walletQrLogin: "QR login",
|
||||
walletMobileQrDesc:
|
||||
|
||||
@@ -217,6 +217,14 @@ export const idDict: Dict = {
|
||||
"Kembali ke sini dan klik “Hubungkan ulang {wallet}”.",
|
||||
walletReconnectWallet: "Hubungkan ulang {wallet}",
|
||||
walletInstallWallet: "Pasang ekstensi {wallet}",
|
||||
walletConfirmAddressTitle: "Masuk dengan alamat {wallet} ini?",
|
||||
walletConfirmAddressDesc:
|
||||
"Pastikan ini adalah alamat dompet {wallet} yang ingin Anda gunakan.",
|
||||
walletConfirmLogin: "Konfirmasi masuk",
|
||||
walletCancelLogin: "Batal",
|
||||
walletDesktopImTokenTitle: "Buka situs ini di aplikasi {wallet} seluler",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet} tidak menyediakan ekstensi browser desktop. Buka https://arkie-library-stag.com di dalam aplikasi {wallet} seluler untuk masuk.",
|
||||
walletOpen: "Buka",
|
||||
walletQrLogin: "Login QR",
|
||||
walletMobileQrDesc:
|
||||
|
||||
@@ -240,6 +240,15 @@ export const jaDict: Dict = {
|
||||
walletDesktopHelpRetry: "ここに戻って「{wallet} に再接続」をクリックします。",
|
||||
walletReconnectWallet: "{wallet} に再接続",
|
||||
walletInstallWallet: "{wallet} 拡張機能をインストール",
|
||||
walletConfirmAddressTitle: "この {wallet} アドレスでログインしますか?",
|
||||
walletConfirmAddressDesc:
|
||||
"使用する {wallet} ウォレットアドレスであることを確認してください。",
|
||||
walletConfirmLogin: "ログインを確認",
|
||||
walletCancelLogin: "キャンセル",
|
||||
walletDesktopImTokenTitle:
|
||||
"モバイル {wallet} アプリでこのサイトを開いてください",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet} にはデスクトップ用ブラウザ拡張機能がありません。モバイル {wallet} アプリ内で https://arkie-library-stag.com を開いてログインしてください。",
|
||||
walletOpen: "開く",
|
||||
walletQrLogin: "QR ログイン",
|
||||
walletMobileQrDesc:
|
||||
|
||||
@@ -214,6 +214,14 @@ export const koDict: Dict = {
|
||||
walletDesktopHelpRetry: "여기로 돌아와 ‘{wallet} 다시 연결’을 클릭하세요.",
|
||||
walletReconnectWallet: "{wallet} 다시 연결",
|
||||
walletInstallWallet: "{wallet} 확장 프로그램 설치",
|
||||
walletConfirmAddressTitle: "이 {wallet} 주소로 로그인할까요?",
|
||||
walletConfirmAddressDesc:
|
||||
"사용하려는 {wallet} 지갑 주소가 맞는지 확인하세요.",
|
||||
walletConfirmLogin: "로그인 확인",
|
||||
walletCancelLogin: "취소",
|
||||
walletDesktopImTokenTitle: "모바일 {wallet} 앱에서 이 사이트를 여세요",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet}은 데스크톱 브라우저 확장 프로그램을 제공하지 않습니다. 모바일 {wallet} 앱 안에서 https://arkie-library-stag.com 을 열어 로그인하세요.",
|
||||
walletOpen: "열기",
|
||||
walletQrLogin: "QR 로그인",
|
||||
walletMobileQrDesc:
|
||||
|
||||
@@ -215,6 +215,15 @@ export const msDict: Dict = {
|
||||
walletDesktopHelpRetry: "Kembali ke sini dan klik “Sambung semula {wallet}”.",
|
||||
walletReconnectWallet: "Sambung semula {wallet}",
|
||||
walletInstallWallet: "Pasang sambungan {wallet}",
|
||||
walletConfirmAddressTitle: "Log masuk dengan alamat {wallet} ini?",
|
||||
walletConfirmAddressDesc:
|
||||
"Sahkan ini ialah alamat dompet {wallet} yang ingin anda gunakan.",
|
||||
walletConfirmLogin: "Sahkan log masuk",
|
||||
walletCancelLogin: "Batal",
|
||||
walletDesktopImTokenTitle:
|
||||
"Buka laman ini dalam aplikasi mudah alih {wallet}",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet} tidak menyediakan sambungan pelayar desktop. Buka https://arkie-library-stag.com dalam aplikasi mudah alih {wallet} untuk log masuk.",
|
||||
walletOpen: "Buka",
|
||||
walletQrLogin: "Log masuk QR",
|
||||
walletMobileQrDesc:
|
||||
|
||||
@@ -211,6 +211,15 @@ export const viDict: Dict = {
|
||||
walletDesktopHelpRetry: "Quay lại đây và nhấn “Kết nối lại {wallet}”.",
|
||||
walletReconnectWallet: "Kết nối lại {wallet}",
|
||||
walletInstallWallet: "Cài tiện ích {wallet}",
|
||||
walletConfirmAddressTitle: "Đăng nhập bằng địa chỉ {wallet} này?",
|
||||
walletConfirmAddressDesc:
|
||||
"Vui lòng xác nhận đây là địa chỉ ví {wallet} bạn muốn dùng.",
|
||||
walletConfirmLogin: "Xác nhận đăng nhập",
|
||||
walletCancelLogin: "Hủy",
|
||||
walletDesktopImTokenTitle:
|
||||
"Mở trang này trong ứng dụng {wallet} trên điện thoại",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet} không có tiện ích mở rộng trình duyệt cho máy tính. Hãy mở https://arkie-library-stag.com trong ứng dụng {wallet} trên điện thoại để đăng nhập.",
|
||||
walletOpen: "Mở",
|
||||
walletQrLogin: "Đăng nhập QR",
|
||||
walletMobileQrDesc:
|
||||
|
||||
@@ -200,6 +200,13 @@ export const zhDict: Dict = {
|
||||
walletDesktopHelpRetry: "回到这里点击“重新连接 {wallet}”。",
|
||||
walletReconnectWallet: "重新连接 {wallet}",
|
||||
walletInstallWallet: "安装 {wallet} 插件",
|
||||
walletConfirmAddressTitle: "确认使用这个 {wallet} 地址登录?",
|
||||
walletConfirmAddressDesc: "请确认这是你要使用的 {wallet} 钱包地址。",
|
||||
walletConfirmLogin: "确认登录",
|
||||
walletCancelLogin: "取消",
|
||||
walletDesktopImTokenTitle: "请用手机 {wallet} App 打开本站",
|
||||
walletDesktopImTokenDesc:
|
||||
"{wallet} 没有电脑浏览器插件。请在手机 {wallet} App 内打开 https://arkie-library-stag.com 后登录。",
|
||||
walletOpen: "打开",
|
||||
walletQrLogin: "扫码登录",
|
||||
walletMobileQrDesc: "适合用另一台设备扫描二维码登录当前浏览器。",
|
||||
|
||||
@@ -16,6 +16,10 @@ function supportsDirectPull(kind: WalletKind): boolean {
|
||||
return kind === "tokenPocket" || kind === "imToken";
|
||||
}
|
||||
|
||||
function supportsDesktopExtension(kind: WalletKind): boolean {
|
||||
return kind === "tokenPocket";
|
||||
}
|
||||
|
||||
function buildAutoLoginDappUrl(kind: WalletKind): string {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set(AUTO_LOGIN_PARAM, kind);
|
||||
@@ -26,6 +30,11 @@ const wallets: WalletKind[] = ["tokenPocket", "imToken"];
|
||||
|
||||
type LoginState = "idle" | "connecting";
|
||||
|
||||
type PendingLogin = {
|
||||
kind: WalletKind;
|
||||
address: string;
|
||||
};
|
||||
|
||||
type Translate = (key: string) => string;
|
||||
|
||||
function walletErrorMessage(error: unknown, t: Translate): string {
|
||||
@@ -47,6 +56,7 @@ export function WalletLoginModal() {
|
||||
const [mobileDevice, setMobileDevice] = useState(false);
|
||||
const [state, setState] = useState<LoginState>("idle");
|
||||
const [error, setError] = useState("");
|
||||
const [pendingLogin, setPendingLogin] = useState<PendingLogin | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loginModalOpen) return;
|
||||
@@ -54,13 +64,14 @@ export function WalletLoginModal() {
|
||||
setSelected(null);
|
||||
setState("idle");
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
}, [loginModalOpen]);
|
||||
|
||||
if (!loginModalOpen) return null;
|
||||
|
||||
const walletName = (kind: WalletKind) => t(walletNameKey(kind));
|
||||
const walletText = (key: string, kind: WalletKind) =>
|
||||
t(key).replace("{wallet}", walletName(kind));
|
||||
t(key).replaceAll("{wallet}", walletName(kind));
|
||||
const walletHint = () =>
|
||||
mobileDevice ? t("walletChooseMobile") : t("walletDesktopHint");
|
||||
const busy = state !== "idle";
|
||||
@@ -70,27 +81,49 @@ export function WalletLoginModal() {
|
||||
setSelected(null);
|
||||
setState("idle");
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
};
|
||||
|
||||
const selectWallet = (kind: WalletKind) => {
|
||||
setSelected(kind);
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
};
|
||||
|
||||
const loginInjected = async (kind: WalletKind) => {
|
||||
setSelected(kind);
|
||||
setState("connecting");
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
try {
|
||||
const address = await connectInjectedWallet(kind);
|
||||
completeLogin(localWalletToken(address), address);
|
||||
setPendingLogin({ kind, address });
|
||||
setState("idle");
|
||||
} catch (err) {
|
||||
setState("idle");
|
||||
setError(walletErrorMessage(err, t));
|
||||
}
|
||||
};
|
||||
|
||||
const confirmPendingLogin = () => {
|
||||
if (!pendingLogin) return;
|
||||
completeLogin(localWalletToken(pendingLogin.address), pendingLogin.address);
|
||||
};
|
||||
|
||||
const cancelPendingLogin = () => {
|
||||
setPendingLogin(null);
|
||||
setSelected(null);
|
||||
setState("idle");
|
||||
setError("");
|
||||
};
|
||||
|
||||
const openWalletAppDirect = (kind: WalletKind) => {
|
||||
if (!mobileDevice && !supportsDesktopExtension(kind)) {
|
||||
setSelected(kind);
|
||||
setPendingLogin(null);
|
||||
setError("");
|
||||
return;
|
||||
}
|
||||
if (getInjectedWallet(kind)) {
|
||||
void loginInjected(kind);
|
||||
return;
|
||||
@@ -103,6 +136,7 @@ export function WalletLoginModal() {
|
||||
return;
|
||||
}
|
||||
setSelected(kind);
|
||||
setPendingLogin(null);
|
||||
setError(walletText("walletInstallSelected", kind));
|
||||
};
|
||||
|
||||
@@ -192,6 +226,39 @@ export function WalletLoginModal() {
|
||||
|
||||
{!mobileDevice && active ? (
|
||||
<div className="mt-3 rounded-2xl border border-white/10 bg-black/20 p-3 text-sm text-neutral-300">
|
||||
{pendingLogin?.kind === kind ? (
|
||||
<>
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletConfirmAddressTitle", kind)}
|
||||
</p>
|
||||
<p className="mt-2 text-xs leading-5 text-neutral-400">
|
||||
{walletText("walletConfirmAddressDesc", kind)}
|
||||
</p>
|
||||
<p
|
||||
className="mt-3 break-all rounded-xl border border-ark-gold/30 bg-ark-gold/10 px-3 py-2 font-mono text-sm text-ark-gold"
|
||||
title={pendingLogin.address}
|
||||
>
|
||||
{pendingLogin.address}
|
||||
</p>
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={confirmPendingLogin}
|
||||
className="rounded-full bg-ark-gold px-3 py-2 text-sm font-bold text-black transition hover:bg-ark-gold2"
|
||||
>
|
||||
{t("walletConfirmLogin")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={cancelPendingLogin}
|
||||
className="rounded-full border border-white/20 px-3 py-2 text-sm font-semibold text-neutral-200 transition hover:border-ark-gold/50 hover:text-ark-gold"
|
||||
>
|
||||
{t("walletCancelLogin")}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : supportsDesktopExtension(kind) ? (
|
||||
<>
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletDesktopHelpTitle", kind)}
|
||||
</p>
|
||||
@@ -217,6 +284,24 @@ export function WalletLoginModal() {
|
||||
{walletText("walletInstallWallet", kind)}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletDesktopImTokenTitle", kind)}
|
||||
</p>
|
||||
<p className="mt-2 text-sm leading-6 text-neutral-400">
|
||||
{walletText("walletDesktopImTokenDesc", kind)}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openWalletInstall(kind)}
|
||||
className="mt-3 w-full rounded-full border border-ark-gold/50 px-3 py-2 text-sm font-semibold text-ark-gold transition hover:bg-ark-gold/10"
|
||||
>
|
||||
{walletText("walletDownloadApp", kind)}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function openWalletDeepLink(kind: WalletKind): void {
|
||||
}
|
||||
|
||||
const downloadUrls: Record<WalletKind, string> = {
|
||||
tokenPocket: "https://www.tokenpocket.pro/en/download/app",
|
||||
tokenPocket: "https://extension.tokenpocket.pro/",
|
||||
metaMask: "https://metamask.io/download/",
|
||||
imToken: "https://token.im/download",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user