Skip to content

Commit

Permalink
Merge pull request #104 from Agoric/feat/add-notice-banner
Browse files Browse the repository at this point in the history
feat: added banner for network-config notices
  • Loading branch information
frazarshad authored Jun 4, 2024
2 parents ed6e9ef + e6d9ee4 commit 0f710bd
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import NoticeBanner from 'components/NoticeBanner';
import { Menu, Transition } from '@headlessui/react';
import { Fragment, MouseEventHandler, useContext } from 'react';
import { FiChevronDown, FiExternalLink } from 'react-icons/fi';
Expand Down Expand Up @@ -81,6 +82,7 @@ const App = (_props: Props) => {

return (
<>
<NoticeBanner />
<ToastContainer
position={'bottom-right'}
closeOnClick={false}
Expand Down
54 changes: 54 additions & 0 deletions src/components/NoticeBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { FiX } from 'react-icons/fi';
import { GrAnnounce } from 'react-icons/gr';
import { motion, AnimatePresence } from 'framer-motion';
import { useContext, useState } from 'react';
import { activeNotices } from 'utils/networkConfig';
import { WalletContext } from 'lib/wallet';

const NoticeBanner = () => {
const [isDismissed, setIsDismissed] = useState(false);
const { chainKit } = useContext(WalletContext);
const config = chainKit.networkConfig;
const bannerContent = config && activeNotices(config).join(' • ');
const isVisible =
!isDismissed && bannerContent && bannerContent.trim().length;

return (
<AnimatePresence initial={false}>
{isVisible && (
<motion.div
initial="open"
animate="open"
exit="collapsed"
variants={{
open: { height: 'auto' },
collapsed: { height: 0 },
}}
className="bg-yellow-400 overflow-hidden"
>
<motion.div className="mx-auto max-w-7xl py-3 px-3 sm:px-6 lg:px-8">
<motion.div className="flex flex-wrap items-center justify-between">
<motion.div className="flex w-0 flex-1 items-center">
<span className="flex rounded-lgp-2">
<GrAnnounce className="h-6 w-6" aria-hidden="true" />
</span>
<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={() => 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"
>
<FiX className="h-6 w-6" />
</button>
</motion.div>
</motion.div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};

export default NoticeBanner;
16 changes: 6 additions & 10 deletions src/lib/chainInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,26 +61,22 @@ const makeChainInfo = (networkConfig, rpcAddr, chainId, caption) => {
};

/**
* @param {string} networkConfig URL
* @param {string} networkConfigURL URL
* @param {object} networkConfig
* @param {object} io
* @param {typeof fetch} io.fetch
* @param {import('@keplr-wallet/types').Keplr} io.keplr
* @param {string} [caption]
*/
export async function suggestChain(
networkConfigURL,
networkConfig,
{ fetch, keplr },
{ keplr },
caption = undefined,
) {
console.log('suggestChain: fetch', networkConfig); // log net IO
const res = await fetch(networkConfig);
if (!res.ok) {
throw Error(`Cannot fetch network: ${res.status}`);
}
const { chainName: chainId, rpcAddrs } = await res.json();
const { chainName: chainId, rpcAddrs } = networkConfig;
const rpcAddr = sample(rpcAddrs);

const chainInfo = makeChainInfo(networkConfig, rpcAddr, chainId, caption);
const chainInfo = makeChainInfo(networkConfigURL, rpcAddr, chainId, caption);
console.log('chainInfo', chainInfo);
await keplr.experimentalSuggestChain(chainInfo);
await keplr.enable(chainId);
Expand Down
9 changes: 7 additions & 2 deletions src/lib/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { marshal, RpcUtils } from './rpc';
import { makeRpcUtils } from './rpc.js';
import { accountInfoUrl, networkConfigUrl } from 'config.js';
import { AgoricChainStoragePathKind } from '@agoric/rpc/index.js';
import { fetchNetworkConfig } from 'utils/networkConfig.js';

export type RelativeTime = { timerBrand: Brand; relValue: bigint };

Expand All @@ -42,10 +43,13 @@ export const makeWalletUtils = async (rpcUtils: RpcUtils, keplr: Keplr) => {
};

const makeChainKit = async () => {
const netConfigURL = networkConfigUrl(agoricNet);
const networkConfig = await fetchNetworkConfig(netConfigURL);

const chainInfo: ChainInfo = await suggestChain(
networkConfigUrl(agoricNet),
netConfigURL,
networkConfig,
{
fetch: window.fetch,
keplr,
},
);
Expand All @@ -57,6 +61,7 @@ export const makeWalletUtils = async (rpcUtils: RpcUtils, keplr: Keplr) => {
);

return {
networkConfig,
chainInfo,
signer,
};
Expand Down
1 change: 0 additions & 1 deletion src/store/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Amount, Brand, DisplayInfo } from '@agoric/ertp/src/types';
import type { Ratio } from '@agoric/zoe/src/contractSupport';
import { atom } from 'jotai';

import { makeDisplayFunctions } from 'utils/displayFunctions';
import { mapAtom } from 'utils/helpers';

Expand Down
40 changes: 40 additions & 0 deletions src/utils/networkConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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 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);
};

export const fetchNetworkConfig = async url => {
console.log('fetchNetworkConfig: fetch', url); // log net IO
const res = await fetch(url);
if (!res.ok) {
throw Error(`Cannot fetch network: ${res.status}`);
}
return await res.json();
};

0 comments on commit 0f710bd

Please sign in to comment.