Skip to content

Commit

Permalink
merge main into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
samsiegart committed Sep 19, 2023
2 parents 1129a89 + d7d3cd9 commit 69a2a79
Show file tree
Hide file tree
Showing 7 changed files with 2,136 additions and 1,856 deletions.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ChainConnection from 'components/ChainConnection';
import { INTER_LOGO } from 'assets/assets';
import { MdBarChart } from 'react-icons/md';
import { FiExternalLink } from 'react-icons/fi';
import NoticeBanner from 'components/NoticeBanner';

import 'styles/globals.css';

Expand All @@ -26,6 +27,7 @@ const App = () => {
}}
></ToastContainer>
<motion.div className="flex flex-col min-h-screen">
<NoticeBanner />
<motion.div className="min-w-screen container p-4 mx-auto flex justify-between items-center">
<a href="https://inter.trade/" target="inter.trade">
<img
Expand Down
93 changes: 52 additions & 41 deletions src/components/ChainConnection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import {
governedParamsIndexAtom,
metricsIndexAtom,
chainConnectionAtom,
networkConfigAtom,
termsIndexAgreedUponAtom,
smartWalletProvisionedAtom,
provisionToastIdAtom,
ChainConnection as ChainConnectionStore,
networkConfigPAtom,
} from 'store/app';
import {
watchContract,
Expand All @@ -30,12 +30,12 @@ import TermsDialog, { currentTermsIndex } from './TermsDialog';
import clsx from 'clsx';
import { makeAgoricChainStorageWatcher } from '@agoric/rpc';
import { sample } from 'lodash-es';
import { loadNetworkConfig } from 'utils/networkConfig';
import ProvisionSmartWalletDialog from './ProvisionSmartWalletDialog';
import { querySwingsetParams } from 'utils/swingsetParams';

import 'react-toastify/dist/ReactToastify.css';
import 'styles/globals.css';
import { querySwingsetParams } from 'utils/swingsetParams';
import { loadable } from 'jotai/utils';

const autoCloseDelayMs = 7000;

Expand Down Expand Up @@ -81,13 +81,12 @@ const ChainConnection = () => {
const [isSmartWalletProvisioned, setSmartWalletProvisioned] = useAtom(
smartWalletProvisionedAtom
);
const networkConfig = useAtomValue(networkConfigAtom);
const termsAgreed = useAtomValue(termsIndexAgreedUponAtom);
const [isTermsDialogOpen, setIsTermsDialogOpen] = useState(false);
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState(false);
const { smartWalletFee, error: smartWalletFeeError } =
useSmartWalletFeeQuery(chainConnection);

const networkConfig = useAtomValue(loadable(networkConfigPAtom));
const areLatestTermsAgreed = termsAgreed === currentTermsIndex;

const handleTermsDialogClose = () => {
Expand Down Expand Up @@ -169,46 +168,58 @@ const ChainConnection = () => {
setIsTermsDialogOpen(true);
return;
}

let connection;
setConnectionInProgress(true);
try {
const { rpcAddrs, chainName } = await loadNetworkConfig(
networkConfig.url
);
const rpc = sample(rpcAddrs);
if (!rpc) {
throw new Error(Errors.networkConfig);
};

useEffect(() => {
const connect = async () => {
if (networkConfig.state === 'loading') {
return;
}
const watcher = makeAgoricChainStorageWatcher(rpc, chainName, e => {
console.error(e);
throw e;
});
connection = await makeAgoricWalletConnection(watcher);
setChainConnection({
...connection,
watcher,
chainId: chainName,
});
} catch (e: any) {
switch (e.message) {
case Errors.enableKeplr:
toast.error('Enable the connection in Keplr to continue.', {
hideProgressBar: false,
autoClose: autoCloseDelayMs,
});
break;
case Errors.networkConfig:
toast.error('Network not found.');
break;
default:
toast.error('Error connecting to network:' + e.message);
break;
try {
if (networkConfig.state === 'hasError') {
throw new Error(Errors.networkConfig);
}
const config = networkConfig.data;
const rpc = sample(config.rpcAddrs);
if (!rpc) {
throw new Error(Errors.networkConfig);
}
const chainId = config.chainName;
const watcher = makeAgoricChainStorageWatcher(rpc, chainId, e => {
console.error(e);
throw e;
});
const connection = await makeAgoricWalletConnection(watcher);
setChainConnection({
...connection,
watcher,
chainId,
});
} catch (e: any) {
switch (e.message) {
case Errors.enableKeplr:
toast.error('Enable the connection in Keplr to continue.', {
hideProgressBar: false,
autoClose: autoCloseDelayMs,
});
break;
case Errors.networkConfig:
toast.error('Network not found.');
break;
default:
toast.error('Error connecting to network:' + e.message);
break;
}
} finally {
setConnectionInProgress(false);
}
} finally {
setConnectionInProgress(false);
};

if (connectionInProgress) {
connect();
}
};
}, [connectionInProgress, networkConfig, setChainConnection]);

const status = (() => {
if (connectionInProgress) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { useAtom } from 'jotai';
import { FiX } from 'react-icons/fi';
import { GrAnnounce } from 'react-icons/gr';
import { motion, AnimatePresence } from 'framer-motion';
import { bannerIndexDismissedAtom } from 'store/app';
import { useState } from 'react';
import { activeNotices } from 'utils/networkConfig';
import { useAtomValue } from 'jotai';
import { networkConfigPAtom } from 'store/app';
import { loadable } from 'jotai/utils';

// UPDATE WHENEVER COMPONENT IS MODIFIED.
export const currentBannerIndex = 3;

const AnnouncementBanner = () => {
const [bannerIndexDismissed, setBannerIndexDismissed] = useAtom(
bannerIndexDismissedAtom
);

const isVisible = currentBannerIndex > bannerIndexDismissed;
const NoticeBanner = () => {
const [isDismissed, setIsDismissed] = useState(false);
const config = useAtomValue(loadable(networkConfigPAtom));
const bannerContent =
config.state === 'hasData' && activeNotices(config.data).join(' • ');
const isVisible =
!isDismissed && bannerContent && bannerContent.trim().length;

return (
<AnimatePresence initial={false}>
Expand All @@ -33,15 +34,11 @@ const AnnouncementBanner = () => {
<span className="flex rounded-lgp-2">
<GrAnnounce className="h-6 w-6" aria-hidden="true" />
</span>
<p className="ml-3 font-medium text-black">
Note: A chain upgrade is planned to take place on Monday June
19th 2023 at approximately 17:00 UTC. The PSM will not be
functional during this time.
</p>
<p className="ml-3 font-medium text-black">{bannerContent}</p>
</motion.div>
<motion.div className="order-2 flex-shrink-0 sm:order-3 sm:ml-3">
<button
onClick={() => setBannerIndexDismissed(currentBannerIndex)}
onClick={() => setIsDismissed(true)}
type="button"
className="-mr-1 flex rounded-md p-2 hover:bg-black hover:bg-opacity-10 focus:outline-none focus:ring-2 focus:ring-white sm:-mr-2"
>
Expand All @@ -56,4 +53,4 @@ const AnnouncementBanner = () => {
);
};

export default AnnouncementBanner;
export default NoticeBanner;
10 changes: 5 additions & 5 deletions src/store/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { makeAgoricWalletConnection } from '@agoric/web-components';
import type { Brand, DisplayInfo, Amount } from '@agoric/ertp/src/types';
import type { Id as ToastId } from 'react-toastify';
import { ChainStorageWatcher } from '@agoric/rpc';
import { loadNetworkConfig } from 'utils/networkConfig';

type Ratio = ReturnType<typeof makeRatio>;

Expand All @@ -20,11 +21,6 @@ export type BrandInfo = DisplayInfo<'nat'> & {
petname: string;
};

export const bannerIndexDismissedAtom = atomWithStorage(
'banner-index-dismissed',
-1
);

export const brandToInfoAtom = mapAtom<Brand, BrandInfo>();

export const chainConnectionAtom = atom<ChainConnection | null>(null);
Expand All @@ -38,6 +34,10 @@ export const networkConfigAtom = atomWithStorage(
networkConfigs.mainnet
);

export const networkConfigPAtom = atom(async get =>
loadNetworkConfig(get(networkConfigAtom).url)
);

export const termsIndexAgreedUponAtom = atomWithStorage('terms-agreed', -1);

/** A map of anchor brand petnames to their instance ids. */
Expand Down
32 changes: 31 additions & 1 deletion src/utils/networkConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
type MinimalNetworkConfig = { rpcAddrs: string[]; chainName: string };
type NetworkNotice = {
start: string;
// In the future this might be optional to indicate that it's user-dismissable.
// In that case the client would need some persistent state, perhaps keyed by `message`.
end: string;
message: string;
};

export type MinimalNetworkConfig = {
rpcAddrs: string[];
chainName: string;
notices?: NetworkNotice[];
};

export const loadNetworkConfig = (url: string): Promise<MinimalNetworkConfig> =>
fetch(url).then(res => res.json());

export const activeNotices = (
config: Pick<MinimalNetworkConfig, 'notices'>
) => {
const { notices } = config;
if (!notices) return [];

const now = Date.now();
const active = notices.filter(n => {
const startD = Date.parse(n.start);
if (startD > now) {
return false;
}
const endD = Date.parse(n.end);
return startD < endD;
});
return active.map(n => n.message);
};
40 changes: 40 additions & 0 deletions test/utils/networkConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, it, describe } from 'vitest';

import { activeNotices } from '../../src/utils/networkConfig';

describe('activeNotices', () => {
it('handles empty', () => {
const notices = [];
expect(activeNotices({ notices })).toEqual([]);
});
it('handles future', () => {
const notices = [
{
start: '2030-01-01',
end: '2040-01-01',
message: 'hello from the future',
},
];
expect(activeNotices({ notices })).toEqual([]);
});
it('handles past', () => {
const notices = [
{
start: '2000-01-01',
end: '2000-01-01',
message: 'hello from the past',
},
];
expect(activeNotices({ notices })).toEqual([]);
});
it('handles started', () => {
const notices = [
{
start: '2020-01-01',
end: '2040-01-01',
message: 'hello now',
},
];
expect(activeNotices({ notices })).toEqual(['hello now']);
});
});
Loading

0 comments on commit 69a2a79

Please sign in to comment.