Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fileverse-dev/fortune-core",
"version": "1.0.88",
"version": "1.0.88-patch-10",
"main": "lib/index.js",
"module": "es/index.js",
"typings": "lib/index.d.ts",
Expand All @@ -15,7 +15,7 @@
"dev": "father-build --watch"
},
"dependencies": {
"@fileverse-dev/formula-parser": "0.2.50",
"@fileverse-dev/formula-parser": "0.2.50-patch-7",
"dayjs": "^1.11.0",
"immer": "^9.0.12",
"lodash": "^4.17.21",
Expand Down
4 changes: 2 additions & 2 deletions packages/formula-parser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fileverse-dev/formula-parser",
"version": "0.2.50",
"version": "0.2.50-patch-7",
"description": "Formula parser",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down Expand Up @@ -48,7 +48,7 @@
"webpack-cli": "^4.2.0"
},
"dependencies": {
"@fileverse-dev/formulajs": "4.4.11-mod-89",
"@fileverse-dev/formulajs": "4.4.11-mod-93",
"tiny-emitter": "^2.1.0"
},
"jest": {
Expand Down
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fileverse-dev/fortune-react",
"version": "1.0.88",
"version": "1.0.88-patch-10",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"module": "es/index.js",
Expand All @@ -16,7 +16,7 @@
"tsc": "tsc"
},
"dependencies": {
"@fileverse-dev/fortune-core": "1.0.88",
"@fileverse-dev/fortune-core": "1.0.88-patch-9",
"@fileverse/ui": "^4.1.7-patch-21",
"@tippyjs/react": "^4.2.6",
"@types/regenerator-runtime": "^0.13.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const GNOSIS_PAY_ACCESS = "GNOSIS_PAY_ACCESS";
85 changes: 84 additions & 1 deletion packages/react/src/components/SheetOverlay/FormulaHint/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import WorkbookContext from "../../../context";
import "./index.css";
import { timeFromNowMessage } from "./utils/utils";
import useGnosisPay from "./use-gnosis-pay";

const FormulaHint: React.FC<React.HTMLAttributes<HTMLDivElement>> = (props) => {
const { context } = useContext(WorkbookContext);
Expand All @@ -18,13 +20,25 @@
);
const [showFunctionBody, setShouldShowFunctionBody] = useState(true);

const {
grantAccess,
handleGnosisPayToken,
hasGnosisPayToken,
isWrongGnosisPayConnector,
isLoading,
accessTokenCreatedAt,
timeLeft,
} = useGnosisPay(fn);

useEffect(() => {
if (fn) {
setApiKeyAdded(!!localStorage.getItem(fn?.API_KEY));
setAPI_KEY(localStorage.getItem(fn?.API_KEY) || "");
setShowAPInput(!localStorage.getItem(fn?.API_KEY));

handleGnosisPayToken();
}
}, [fn]);

Check warning on line 41 in packages/react/src/components/SheetOverlay/FormulaHint/index.tsx

View workflow job for this annotation

GitHub Actions / test

React Hook useEffect has a missing dependency: 'handleGnosisPayToken'. Either include it or remove the dependency array

Check warning on line 41 in packages/react/src/components/SheetOverlay/FormulaHint/index.tsx

View workflow job for this annotation

GitHub Actions / test

React Hook useEffect has a missing dependency: 'handleGnosisPayToken'. Either include it or remove the dependency array
const apiKeyPlaceholder: Record<string, string> = {
ETHERSCAN_API_KEY: "Etherscan API key",
};
Expand Down Expand Up @@ -89,6 +103,7 @@
if (el && handleWheel) el.removeEventListener("wheel", handleWheel);
};
}, []);

if (!fn) return null;

return (
Expand Down Expand Up @@ -213,7 +228,7 @@
style={{
backgroundColor: `${fn.BRAND_COLOR ? fn.BRAND_COLOR : "#F8F9FA"}`,
maxHeight: "318px",
overflowY: "scroll",
overflowY: "auto",
}}
>
{fn.API_KEY && (
Expand Down Expand Up @@ -291,6 +306,74 @@
</div>
)}

{fn.n === "GNOSISPAY" && (
<div
id="gnosis-pay-area"
style={{
borderLeft: `4px solid ${
hasGnosisPayToken ? "#177E23" : "#fb923c"
}`,
backgroundColor: "white",
padding: "8px",
margin: "4px 4px 0px 4px",
borderRadius: "4px",
}}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
cursor: "pointer",
}}
onClick={() => {}}
>
<h3
style={{
margin: "0 0 8px 0",
}}
className="text-heading-xsm color-text-default"
>
{hasGnosisPayToken
? "Access granted"
: "Connect your Gnosis Pay account"}
</h3>
</div>
<div>
<p
style={{
margin: "0 0 8px 0",
}}
className="text-body-sm color-text-default"
>
{!hasGnosisPayToken
? "Grant access to your Gnosis Pay account and ensure you're using the same wallet for both Gnosis Pay and dSheet."
: ` You can now interact with your Gnosis Pay account for the next ${timeFromNowMessage(
timeLeft
)}. When the timer’s up, just re-grant access and you're good to go!`}
</p>
<Button
onClick={grantAccess}
disabled={
hasGnosisPayToken || isWrongGnosisPayConnector || isLoading
}
className="w-full items-center flex gap-1"
>
{isLoading && (
<div>
<LucideIcon
name="LoaderCircle"
className="animate-spin"
size="sm"
/>
</div>
)}
<p>Grant access </p>{" "}
{accessTokenCreatedAt > 0 && <div>{timeLeft}</div>}
</Button>
</div>
</div>
)}

<div
style={{
backgroundColor: "white",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useEffect, useRef, useState } from "react";
import {
isGnosisPayAccessExpired,
// @ts-ignore
} from "@fileverse-dev/formulajs/crypto-constants";
import { GNOSIS_PAY_ACCESS } from "./constants";
import { formatTimeLeft, getJwtExpiry } from "./utils/utils";

const useGnosisPay = (fn: any) => {
const gnosisTokenTokenIntervalRef = useRef<any>(null);
const [isLoading, setIsLoading] = useState(false);
const [hasGnosisPayToken, setHasGnosisPayToken] = useState(false);
const [timeLeft, setTimeLeft] = useState("00:00");
const [accessTokenCreatedAt, setAccessTokenCreatedAt] = useState(0);
const isWrongGnosisPayConnector =
localStorage.getItem("LOGIN_METHOD") !== "walletAddress";

const handleGnosisPayToken = (onDone?: () => void) => {
if (localStorage.getItem(GNOSIS_PAY_ACCESS)) {
const access = localStorage.getItem(GNOSIS_PAY_ACCESS) || "";
if (!access || isGnosisPayAccessExpired(access)) {
if (hasGnosisPayToken) {
setHasGnosisPayToken(false);
}
if (accessTokenCreatedAt) {
setAccessTokenCreatedAt(0);
}
localStorage.removeItem(GNOSIS_PAY_ACCESS);
return;
}
setHasGnosisPayToken(!!access);
setAccessTokenCreatedAt(getJwtExpiry(access));
onDone?.();
} else {
const urlParams = new URLSearchParams(window.location.search);
const isRejected = urlParams.has("reject");
if (isRejected) {
const url = new URL(window.location.href);
url.searchParams.delete("reject");
window.history.replaceState({}, "", url.toString());
onDone?.();
}
}
};

useEffect(() => {
return () => {
if (gnosisTokenTokenIntervalRef.current)
clearInterval(gnosisTokenTokenIntervalRef.current);
};
}, [gnosisTokenTokenIntervalRef]);

useEffect(() => {
if (accessTokenCreatedAt <= 0) return () => {};

const interval = setInterval(() => {
const access = localStorage.getItem(GNOSIS_PAY_ACCESS) || "";
const expiryTimestamp = getJwtExpiry(access) * 1000;
const newTimeLeft = expiryTimestamp - Date.now();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token already contains an expiry field, use that instead

setTimeLeft(formatTimeLeft(newTimeLeft));
if (
isGnosisPayAccessExpired(localStorage.getItem(GNOSIS_PAY_ACCESS)) ||
!document.getElementById("gnosis-pay-area")
) {
localStorage.removeItem(GNOSIS_PAY_ACCESS);
setHasGnosisPayToken(false);
setAccessTokenCreatedAt(0);
clearInterval(interval);
}
}, 1000);

return () => {
clearInterval(interval);
};
}, [accessTokenCreatedAt, fn]);

const grantAccess = () => {
const button = document.getElementById("grant-gnosispay-access");
if (!button) return;
button.click();
setIsLoading(true);
const interval = setInterval(() => {
handleGnosisPayToken(() => {
clearInterval(interval);
setIsLoading(false);
});
}, 5000);

gnosisTokenTokenIntervalRef.current = interval;
};

return {
grantAccess,
handleGnosisPayToken,
hasGnosisPayToken,
isWrongGnosisPayConnector,
isLoading,
accessTokenCreatedAt,
timeLeft,
};
};

export default useGnosisPay;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export function formatTimeLeft(msLeft: number): string {
const totalSeconds = Math.max(0, Math.floor(msLeft / 1000));
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, "0");
const seconds = String(totalSeconds % 60).padStart(2, "0");
return `${minutes}:${seconds}`;
}
export function timeFromNowMessage(expiryStr: string): string {
if (!expiryStr) {
return "0 minute";
}
const [mm] = expiryStr.split(":").map(Number);

return `${mm} minute${mm !== 1 ? "s" : ""}`;
}
export function getJwtExpiry(token: string): number {
try {
const payloadBase64 = token?.split(".")?.[1];
if (!payloadBase64) return 0;

const payloadJson = atob(payloadBase64);
const payload = JSON.parse(payloadJson);

return typeof payload.exp === "number" ? payload.exp : 0;
} catch (error) {
return 0;
}
}
Loading
Loading