diff --git a/components/Loader/index.tsx b/components/Loader/index.tsx
index 4bfba2e..715ca26 100644
--- a/components/Loader/index.tsx
+++ b/components/Loader/index.tsx
@@ -1,12 +1,25 @@
+import { useEffect, useState } from "react";
import { keyframes, styled } from "../../stitches.config";
import Box from "../Box";
const gradientAnimation = keyframes({
"0%": {
+ backgroundPosition: "-400% 50%"
+ },
+ "50%": {
backgroundPosition: "0% 50%"
},
"100%": {
- backgroundPosition: "100% 50%"
+ backgroundPosition: "400% 50%"
+ }
+})
+
+const fadeInAnimation = keyframes({
+ "0%": {
+ opacity: "0"
+ },
+ "100%": {
+ opacity: "1"
}
})
@@ -18,9 +31,28 @@ const Loader = styled(Box, {
width: "100%",
minWidth: "150px",
minHeight: "1em",
- background: "linear-gradient(90deg, rgba(241,235,212,0.2), rgba(16,7,15,0.2), rgba(241,235,212,0.2))",
+ opacity: 0,
+ background: "linear-gradient(90deg, rgba(241,235,212,0.1), rgba(16,7,15,0.15), rgba(241,235,212,0.1))",
backgroundSize: "400% 400%",
- animation: `${gradientAnimation} 2s linear infinite`
+ animationName: `${gradientAnimation}, ${fadeInAnimation}`,
+ animationDuration: "12s, 1s",
+ animationTimingFunction: "linear, ease",
+ animationIterationCount: "infinite, 1",
+ animationFillMode: "forwards"
})
+export const LoaderWithDelay = () => {
+ const delay = 200;
+ const [show, setVisibility] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setVisibility(true), delay);
+ return () => {
+ clearTimeout(timer);
+ };
+ });
+
+ return show ? : null;
+};
+
export default Loader
diff --git a/components/MyStakes/index.tsx b/components/MyStakes/index.tsx
index ea2e6c6..1d1fa40 100644
--- a/components/MyStakes/index.tsx
+++ b/components/MyStakes/index.tsx
@@ -1,9 +1,9 @@
import { BigNumber, ethers } from "ethers";
import Image from "next/image";
+import { CaretDown } from "phosphor-react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Column, Row } from "react-table";
import { useSnapshot } from "valtio";
-import { CaretDown } from "phosphor-react";
import { useGetAssetPairsQuery } from "../../generated/graphql";
import { Stake, state } from "../../state";
import { fetchStakes } from "../../state/actions/stakes";
@@ -342,7 +342,7 @@ export const MyStakes = () => {
)}
- {tableData || (!tableData && stakesLoading) ? (
+ {tableData || stakesLoading ? (
({
{headerGroups[headerGroups.length - 1].headers.map((_column) => {
return (
|
-
+
|
);
})}
diff --git a/components/Wallet/ActiveWallet.tsx b/components/Wallet/ActiveWallet.tsx
index 37883f8..abf2307 100644
--- a/components/Wallet/ActiveWallet.tsx
+++ b/components/Wallet/ActiveWallet.tsx
@@ -2,23 +2,23 @@ import type * as Stitches from "@stitches/react";
import { isAddress, juiceDecimals } from "@vanilladefi/core-sdk";
import { getJuiceStakingContract } from "@vanilladefi/stake-sdk";
import { ContractTransaction } from "ethers";
-import Link from "../Link";
-import { Copy, ArrowUp, ArrowDown, XCircle } from "phosphor-react";
-import { useCallback, useState, useRef, useEffect } from "react";
-import { toast } from "react-toastify";
+import { formatUnits } from "ethers/lib/utils";
+import { ArrowDown, ArrowUp, Copy, XCircle } from "phosphor-react";
+import { useCallback, useEffect, useRef, useState } from "react";
import CountUp from "react-countup";
+import { toast } from "react-toastify";
+import { PolygonScanIcon } from "../../assets";
import { state, useSnapshot, VanillaEvents } from "../../state";
import { connectWallet, disconnect } from "../../state/actions/wallet";
import { emitEvent, getTransactionLink, parseJuice } from "../../utils/helpers";
import Box from "../Box";
import Button from "../Button";
import Input from "../Input";
-import Loader from "../Loader";
+import Link from "../Link";
+import { LoaderWithDelay } from "../Loader";
import Text from "../Text";
import Curtain from "./Curtain";
-import { PolygonScanIcon } from "../../assets";
-import { formatUnits } from "ethers/lib/utils";
enum TxTypes {
deposit,
@@ -420,7 +420,7 @@ const ActiveWallet: React.FC<{ css?: Stitches.CSS }> = ({ css }) => {
>
) : (
-
+
)}
= ({ css }) => {
>
) : (
-
+
)}
@@ -492,7 +492,7 @@ const ActiveWallet: React.FC<{ css?: Stitches.CSS }> = ({ css }) => {
In your wallet
>
) : (
-
+
)}
@@ -719,7 +719,7 @@ const ActiveWallet: React.FC<{ css?: Stitches.CSS }> = ({ css }) => {
Unstaked in Juicenet
>
) : (
-
+
)}
{rawBalances.unstakedJuice?.isZero() &&
diff --git a/components/Wallet/index.tsx b/components/Wallet/index.tsx
index e804ec9..d48d46e 100644
--- a/components/Wallet/index.tsx
+++ b/components/Wallet/index.tsx
@@ -2,10 +2,10 @@ import type * as Stitches from "@stitches/react";
import { state, useSnapshot } from "../../state";
import { connectWallet } from "../../state/actions/wallet";
import Box from "../Box";
-import Loader from "../Loader";
+import { LoaderWithDelay } from "../Loader";
const WalletButton: React.FC<{ css?: Stitches.CSS }> = ({ css }) => {
- const { walletAddress, truncatedWalletAddress, walletOpen } =
+ const { walletAddress, truncatedWalletAddress, walletOpen, online } =
useSnapshot(state);
const buttonStyles = {
@@ -19,6 +19,8 @@ const WalletButton: React.FC<{ css?: Stitches.CSS }> = ({ css }) => {
justifyContent: "center",
py: "$4",
px: "$3",
+ opacity: online ? 1 : 0.5,
+ cursor: online ? "pointer" : "not-allowed",
...css,
};
@@ -53,12 +55,12 @@ const WalletButton: React.FC<{ css?: Stitches.CSS }> = ({ css }) => {
{walletAddress ? (
<>
- {truncatedWalletAddress ? truncatedWalletAddress : }
+ {truncatedWalletAddress ? truncatedWalletAddress : }
>
) : (
- connectWallet()}>
- Connect
+ online && connectWallet()}>
+ { online ? "Connect" : "Offline" }
)}{" "}
diff --git a/generated/graphql.ts b/generated/graphql.ts
index a0d03e6..34dfb3a 100644
--- a/generated/graphql.ts
+++ b/generated/graphql.ts
@@ -141,7 +141,9 @@ export type HourlyPriceHistory = {
export type HourlyPriceHistory_Filter = {
assetPair?: Maybe;
assetPair_contains?: Maybe;
+ assetPair_contains_nocase?: Maybe;
assetPair_ends_with?: Maybe;
+ assetPair_ends_with_nocase?: Maybe;
assetPair_gt?: Maybe;
assetPair_gte?: Maybe;
assetPair_in?: Maybe>;
@@ -149,10 +151,14 @@ export type HourlyPriceHistory_Filter = {
assetPair_lte?: Maybe;
assetPair_not?: Maybe;
assetPair_not_contains?: Maybe;
+ assetPair_not_contains_nocase?: Maybe;
assetPair_not_ends_with?: Maybe;
+ assetPair_not_ends_with_nocase?: Maybe;
assetPair_not_in?: Maybe>;
assetPair_not_starts_with?: Maybe;
+ assetPair_not_starts_with_nocase?: Maybe;
assetPair_starts_with?: Maybe;
+ assetPair_starts_with_nocase?: Maybe;
closingPrice?: Maybe;
closingPrice_gt?: Maybe;
closingPrice_gte?: Maybe;
@@ -211,7 +217,9 @@ export type HourlyPriceHistory_Filter = {
openingPrice_not_in?: Maybe>;
test?: Maybe;
test_contains?: Maybe;
+ test_contains_nocase?: Maybe;
test_ends_with?: Maybe;
+ test_ends_with_nocase?: Maybe;
test_gt?: Maybe;
test_gte?: Maybe;
test_in?: Maybe>;
@@ -219,10 +227,14 @@ export type HourlyPriceHistory_Filter = {
test_lte?: Maybe;
test_not?: Maybe;
test_not_contains?: Maybe;
+ test_not_contains_nocase?: Maybe;
test_not_ends_with?: Maybe;
+ test_not_ends_with_nocase?: Maybe;
test_not_in?: Maybe>;
test_not_starts_with?: Maybe;
+ test_not_starts_with_nocase?: Maybe;
test_starts_with?: Maybe;
+ test_starts_with_nocase?: Maybe;
timestamp?: Maybe;
timestamp_gt?: Maybe;
timestamp_gte?: Maybe;
diff --git a/graphql.schema.json b/graphql.schema.json
index 58a2c2a..2c07998 100644
--- a/graphql.schema.json
+++ b/graphql.schema.json
@@ -1354,6 +1354,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "assetPair_contains_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "assetPair_ends_with",
"description": null,
@@ -1366,6 +1378,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "assetPair_ends_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "assetPair_gt",
"description": null,
@@ -1458,6 +1482,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "assetPair_not_contains_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "assetPair_not_ends_with",
"description": null,
@@ -1470,6 +1506,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "assetPair_not_ends_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "assetPair_not_in",
"description": null,
@@ -1502,6 +1550,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "assetPair_not_starts_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "assetPair_starts_with",
"description": null,
@@ -1514,6 +1574,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "assetPair_starts_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "closingPrice",
"description": null,
@@ -2322,6 +2394,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "test_contains_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "test_ends_with",
"description": null,
@@ -2334,6 +2418,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "test_ends_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "test_gt",
"description": null,
@@ -2426,6 +2522,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "test_not_contains_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "test_not_ends_with",
"description": null,
@@ -2438,6 +2546,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "test_not_ends_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "test_not_in",
"description": null,
@@ -2470,6 +2590,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "test_not_starts_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "test_starts_with",
"description": null,
@@ -2482,6 +2614,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "test_starts_with_nocase",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "timestamp",
"description": null,
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 2786075..957a41e 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -3,10 +3,10 @@ import type { AppProps } from "next/app";
import dynamic from "next/dynamic";
import Head from "next/head";
import { useRouter } from "next/router";
-import { useEffect } from "react";
-import { Provider } from "urql";
-import { ToastContainer } from "react-toastify";
+import { ReactText, useEffect, useRef } from "react";
+import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
+import { Provider } from "urql";
import Box from "../components/Box";
import Footer from "../components/Footer";
import Navigation from "../components/Navigation";
@@ -15,7 +15,7 @@ import { state, useSnapshot } from "../state";
import {
connectWallet,
ensureCorrectChain,
- initWalletSubscriptions,
+ initWalletSubscriptions
} from "../state/actions/wallet";
import { darkTheme } from "../stitches.config";
import "../styles/globals.css";
@@ -33,31 +33,48 @@ const WalletModal = dynamic(() => import("../components/Wallet/WalletModal"), {
});
function MyApp({ Component, pageProps }: AppProps) {
+ const shareImg = "/images/share-image.png";
+
if (pageProps.urqlState) {
ssrCache.restoreData(pageProps.urqlState);
}
+
const router = useRouter();
const origin = useOrigin();
- const shareImg = "/images/share-image.png";
+ const { signer, online } = useSnapshot(state);
+ const offlineToastId = useRef(null);
// One time initializations
useEffect(() => {
initWalletSubscriptions();
}, []);
- const { signer } = useSnapshot(state);
useEffect(() => {
if (!signer || !window.ethereum) return;
window.ethereum.on("accountsChanged", connectWallet);
window.ethereum.on("chainChanged", ensureCorrectChain);
+ window.addEventListener('offline', () => state.online = false);
+ window.addEventListener('online', () => state.online = true);
return () => {
window.ethereum.removeListener("accountsChanged", connectWallet);
window.ethereum.removeListener("chainChanged", ensureCorrectChain);
+ window.removeEventListener('offline', () => state.online = false);
+ window.removeEventListener('online', () => state.online = true);
};
}, [signer]);
+ // Offline indicator
+ useEffect(() => {
+ if (!online) {
+ offlineToastId.current = toast.error("You are offline", { autoClose: false, closeOnClick: false })
+ } else if (offlineToastId.current) {
+ toast.dismiss(offlineToastId.current.toString())
+ offlineToastId.current = null
+ }
+ }, [online])
+
return (
<>
diff --git a/state/index.ts b/state/index.ts
index 12914ae..73ecae9 100644
--- a/state/index.ts
+++ b/state/index.ts
@@ -4,7 +4,7 @@ import { subscribeKey } from "valtio/utils";
import Web3Modal from "web3modal";
import { defaultEthereumProvider, defaultPolygonProvider } from "../lib/config";
-type PrimitiveBalanceTypes = "eth" | "vnl" | "juice" | "matic"
+type PrimitiveBalanceTypes = "eth" | "vnl" | "juice" | "matic";
type JuicenetBalanceTypes = "unstakedJuice" | "stakedJuice" | "totalJuice";
export type Balances = {
@@ -12,8 +12,8 @@ export type Balances = {
};
export type RawBalances = {
- [key in PrimitiveBalanceTypes | JuicenetBalanceTypes]?: BigNumber
-}
+ [key in PrimitiveBalanceTypes | JuicenetBalanceTypes]?: BigNumber;
+};
export enum Sentiment {
long = "long",
@@ -33,29 +33,28 @@ export type Stake = {
export type AlertOpts = {
title: string;
body?: string | JSX.Element;
- onConfirm?: () => void | Promise,
- onCancel?: () => void,
- confirmDisabled?: boolean,
- confirmText?: string
- cancelText?: string
-}
-
+ onConfirm?: () => void | Promise;
+ onCancel?: () => void;
+ confirmDisabled?: boolean;
+ confirmText?: string;
+ cancelText?: string;
+};
type State = {
ethereumProvider:
- | providers.JsonRpcProvider
- | providers.Web3Provider
- | providers.WebSocketProvider
- | providers.Provider
- | providers.BaseProvider
- | null;
+ | providers.JsonRpcProvider
+ | providers.Web3Provider
+ | providers.WebSocketProvider
+ | providers.Provider
+ | providers.BaseProvider
+ | null;
polygonProvider:
- | providers.JsonRpcProvider
- | providers.Web3Provider
- | providers.WebSocketProvider
- | providers.Provider
- | providers.BaseProvider
- | null;
+ | providers.JsonRpcProvider
+ | providers.Web3Provider
+ | providers.WebSocketProvider
+ | providers.Provider
+ | providers.BaseProvider
+ | null;
providerName: string | null;
signer: Signer | null;
balances: Balances;
@@ -66,6 +65,7 @@ type State = {
stakes: Stake[] | null;
alert: AlertOpts | null;
walletOpen: boolean;
+ online: boolean;
};
export const initialState: State = {
@@ -81,6 +81,7 @@ export const initialState: State = {
alert: null,
stakes: null,
walletOpen: false,
+ online: true,
};
const persistedKeys = {
@@ -88,8 +89,8 @@ const persistedKeys = {
};
enum VanillaEvents {
- stakesChanged = 'vanilla-StakesChanged',
- balancesChanged = 'vanilla-BalancesChanged',
+ stakesChanged = "vanilla-StakesChanged",
+ balancesChanged = "vanilla-BalancesChanged",
}
const state = proxy(initialState);
@@ -102,5 +103,5 @@ export {
snapshot,
ref,
persistedKeys,
- VanillaEvents
+ VanillaEvents,
};