terry-staging #16

Merged
terry merged 96 commits from terry-staging into main 2026-06-05 16:33:12 +00:00
9 changed files with 171 additions and 28 deletions
Showing only changes of commit 4059ec3f20 - Show all commits

View File

@@ -215,6 +215,14 @@ export const enDict: Dict = {
walletDesktopHelpRetry: "Come back here and click “Reconnect {wallet}”.", walletDesktopHelpRetry: "Come back here and click “Reconnect {wallet}”.",
walletReconnectWallet: "Reconnect {wallet}", walletReconnectWallet: "Reconnect {wallet}",
walletInstallWallet: "Install {wallet} extension", 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", walletOpen: "Open",
walletQrLogin: "QR login", walletQrLogin: "QR login",
walletMobileQrDesc: walletMobileQrDesc:

View File

@@ -217,6 +217,14 @@ export const idDict: Dict = {
"Kembali ke sini dan klik “Hubungkan ulang {wallet}”.", "Kembali ke sini dan klik “Hubungkan ulang {wallet}”.",
walletReconnectWallet: "Hubungkan ulang {wallet}", walletReconnectWallet: "Hubungkan ulang {wallet}",
walletInstallWallet: "Pasang ekstensi {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", walletOpen: "Buka",
walletQrLogin: "Login QR", walletQrLogin: "Login QR",
walletMobileQrDesc: walletMobileQrDesc:

View File

@@ -240,6 +240,15 @@ export const jaDict: Dict = {
walletDesktopHelpRetry: "ここに戻って「{wallet} に再接続」をクリックします。", walletDesktopHelpRetry: "ここに戻って「{wallet} に再接続」をクリックします。",
walletReconnectWallet: "{wallet} に再接続", walletReconnectWallet: "{wallet} に再接続",
walletInstallWallet: "{wallet} 拡張機能をインストール", walletInstallWallet: "{wallet} 拡張機能をインストール",
walletConfirmAddressTitle: "この {wallet} アドレスでログインしますか?",
walletConfirmAddressDesc:
"使用する {wallet} ウォレットアドレスであることを確認してください。",
walletConfirmLogin: "ログインを確認",
walletCancelLogin: "キャンセル",
walletDesktopImTokenTitle:
"モバイル {wallet} アプリでこのサイトを開いてください",
walletDesktopImTokenDesc:
"{wallet} にはデスクトップ用ブラウザ拡張機能がありません。モバイル {wallet} アプリ内で https://arkie-library-stag.com を開いてログインしてください。",
walletOpen: "開く", walletOpen: "開く",
walletQrLogin: "QR ログイン", walletQrLogin: "QR ログイン",
walletMobileQrDesc: walletMobileQrDesc:

View File

@@ -214,6 +214,14 @@ export const koDict: Dict = {
walletDesktopHelpRetry: "여기로 돌아와 {wallet} 다시 연결’을 클릭하세요.", walletDesktopHelpRetry: "여기로 돌아와 {wallet} 다시 연결’을 클릭하세요.",
walletReconnectWallet: "{wallet} 다시 연결", walletReconnectWallet: "{wallet} 다시 연결",
walletInstallWallet: "{wallet} 확장 프로그램 설치", walletInstallWallet: "{wallet} 확장 프로그램 설치",
walletConfirmAddressTitle: "이 {wallet} 주소로 로그인할까요?",
walletConfirmAddressDesc:
"사용하려는 {wallet} 지갑 주소가 맞는지 확인하세요.",
walletConfirmLogin: "로그인 확인",
walletCancelLogin: "취소",
walletDesktopImTokenTitle: "모바일 {wallet} 앱에서 이 사이트를 여세요",
walletDesktopImTokenDesc:
"{wallet}은 데스크톱 브라우저 확장 프로그램을 제공하지 않습니다. 모바일 {wallet} 앱 안에서 https://arkie-library-stag.com 을 열어 로그인하세요.",
walletOpen: "열기", walletOpen: "열기",
walletQrLogin: "QR 로그인", walletQrLogin: "QR 로그인",
walletMobileQrDesc: walletMobileQrDesc:

View File

@@ -215,6 +215,15 @@ export const msDict: Dict = {
walletDesktopHelpRetry: "Kembali ke sini dan klik “Sambung semula {wallet}”.", walletDesktopHelpRetry: "Kembali ke sini dan klik “Sambung semula {wallet}”.",
walletReconnectWallet: "Sambung semula {wallet}", walletReconnectWallet: "Sambung semula {wallet}",
walletInstallWallet: "Pasang sambungan {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", walletOpen: "Buka",
walletQrLogin: "Log masuk QR", walletQrLogin: "Log masuk QR",
walletMobileQrDesc: walletMobileQrDesc:

View File

@@ -211,6 +211,15 @@ export const viDict: Dict = {
walletDesktopHelpRetry: "Quay lại đây và nhấn “Kết nối lại {wallet}”.", walletDesktopHelpRetry: "Quay lại đây và nhấn “Kết nối lại {wallet}”.",
walletReconnectWallet: "Kết nối lại {wallet}", walletReconnectWallet: "Kết nối lại {wallet}",
walletInstallWallet: "Cài tiện ích {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ở", walletOpen: "Mở",
walletQrLogin: "Đăng nhập QR", walletQrLogin: "Đăng nhập QR",
walletMobileQrDesc: walletMobileQrDesc:

View File

@@ -200,6 +200,13 @@ export const zhDict: Dict = {
walletDesktopHelpRetry: "回到这里点击“重新连接 {wallet}”。", walletDesktopHelpRetry: "回到这里点击“重新连接 {wallet}”。",
walletReconnectWallet: "重新连接 {wallet}", walletReconnectWallet: "重新连接 {wallet}",
walletInstallWallet: "安装 {wallet} 插件", walletInstallWallet: "安装 {wallet} 插件",
walletConfirmAddressTitle: "确认使用这个 {wallet} 地址登录?",
walletConfirmAddressDesc: "请确认这是你要使用的 {wallet} 钱包地址。",
walletConfirmLogin: "确认登录",
walletCancelLogin: "取消",
walletDesktopImTokenTitle: "请用手机 {wallet} App 打开本站",
walletDesktopImTokenDesc:
"{wallet} 没有电脑浏览器插件。请在手机 {wallet} App 内打开 https://arkie-library-stag.com 后登录。",
walletOpen: "打开", walletOpen: "打开",
walletQrLogin: "扫码登录", walletQrLogin: "扫码登录",
walletMobileQrDesc: "适合用另一台设备扫描二维码登录当前浏览器。", walletMobileQrDesc: "适合用另一台设备扫描二维码登录当前浏览器。",

View File

@@ -16,6 +16,10 @@ function supportsDirectPull(kind: WalletKind): boolean {
return kind === "tokenPocket" || kind === "imToken"; return kind === "tokenPocket" || kind === "imToken";
} }
function supportsDesktopExtension(kind: WalletKind): boolean {
return kind === "tokenPocket";
}
function buildAutoLoginDappUrl(kind: WalletKind): string { function buildAutoLoginDappUrl(kind: WalletKind): string {
const url = new URL(window.location.href); const url = new URL(window.location.href);
url.searchParams.set(AUTO_LOGIN_PARAM, kind); url.searchParams.set(AUTO_LOGIN_PARAM, kind);
@@ -26,6 +30,11 @@ const wallets: WalletKind[] = ["tokenPocket", "imToken"];
type LoginState = "idle" | "connecting"; type LoginState = "idle" | "connecting";
type PendingLogin = {
kind: WalletKind;
address: string;
};
type Translate = (key: string) => string; type Translate = (key: string) => string;
function walletErrorMessage(error: unknown, t: Translate): string { function walletErrorMessage(error: unknown, t: Translate): string {
@@ -47,6 +56,7 @@ export function WalletLoginModal() {
const [mobileDevice, setMobileDevice] = useState(false); const [mobileDevice, setMobileDevice] = useState(false);
const [state, setState] = useState<LoginState>("idle"); const [state, setState] = useState<LoginState>("idle");
const [error, setError] = useState(""); const [error, setError] = useState("");
const [pendingLogin, setPendingLogin] = useState<PendingLogin | null>(null);
useEffect(() => { useEffect(() => {
if (!loginModalOpen) return; if (!loginModalOpen) return;
@@ -54,13 +64,14 @@ export function WalletLoginModal() {
setSelected(null); setSelected(null);
setState("idle"); setState("idle");
setError(""); setError("");
setPendingLogin(null);
}, [loginModalOpen]); }, [loginModalOpen]);
if (!loginModalOpen) return null; if (!loginModalOpen) return null;
const walletName = (kind: WalletKind) => t(walletNameKey(kind)); const walletName = (kind: WalletKind) => t(walletNameKey(kind));
const walletText = (key: string, kind: WalletKind) => const walletText = (key: string, kind: WalletKind) =>
t(key).replace("{wallet}", walletName(kind)); t(key).replaceAll("{wallet}", walletName(kind));
const walletHint = () => const walletHint = () =>
mobileDevice ? t("walletChooseMobile") : t("walletDesktopHint"); mobileDevice ? t("walletChooseMobile") : t("walletDesktopHint");
const busy = state !== "idle"; const busy = state !== "idle";
@@ -70,27 +81,49 @@ export function WalletLoginModal() {
setSelected(null); setSelected(null);
setState("idle"); setState("idle");
setError(""); setError("");
setPendingLogin(null);
}; };
const selectWallet = (kind: WalletKind) => { const selectWallet = (kind: WalletKind) => {
setSelected(kind); setSelected(kind);
setError(""); setError("");
setPendingLogin(null);
}; };
const loginInjected = async (kind: WalletKind) => { const loginInjected = async (kind: WalletKind) => {
setSelected(kind); setSelected(kind);
setState("connecting"); setState("connecting");
setError(""); setError("");
setPendingLogin(null);
try { try {
const address = await connectInjectedWallet(kind); const address = await connectInjectedWallet(kind);
completeLogin(localWalletToken(address), address); setPendingLogin({ kind, address });
setState("idle");
} catch (err) { } catch (err) {
setState("idle"); setState("idle");
setError(walletErrorMessage(err, t)); 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) => { const openWalletAppDirect = (kind: WalletKind) => {
if (!mobileDevice && !supportsDesktopExtension(kind)) {
setSelected(kind);
setPendingLogin(null);
setError("");
return;
}
if (getInjectedWallet(kind)) { if (getInjectedWallet(kind)) {
void loginInjected(kind); void loginInjected(kind);
return; return;
@@ -103,6 +136,7 @@ export function WalletLoginModal() {
return; return;
} }
setSelected(kind); setSelected(kind);
setPendingLogin(null);
setError(walletText("walletInstallSelected", kind)); setError(walletText("walletInstallSelected", kind));
}; };
@@ -192,6 +226,39 @@ export function WalletLoginModal() {
{!mobileDevice && active ? ( {!mobileDevice && active ? (
<div className="mt-3 rounded-2xl border border-white/10 bg-black/20 p-3 text-sm text-neutral-300"> <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"> <p className="font-semibold text-neutral-100">
{walletText("walletDesktopHelpTitle", kind)} {walletText("walletDesktopHelpTitle", kind)}
</p> </p>
@@ -217,6 +284,24 @@ export function WalletLoginModal() {
{walletText("walletInstallWallet", kind)} {walletText("walletInstallWallet", kind)}
</button> </button>
</div> </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> </div>
) : null} ) : null}
</div> </div>

View File

@@ -31,7 +31,7 @@ export function openWalletDeepLink(kind: WalletKind): void {
} }
const downloadUrls: Record<WalletKind, string> = { const downloadUrls: Record<WalletKind, string> = {
tokenPocket: "https://www.tokenpocket.pro/en/download/app", tokenPocket: "https://extension.tokenpocket.pro/",
metaMask: "https://metamask.io/download/", metaMask: "https://metamask.io/download/",
imToken: "https://token.im/download", imToken: "https://token.im/download",
}; };