diff --git a/lib/utils/wagmi-utils.js b/lib/utils/wagmi-utils.js index befcd98a1..bd85ad0e1 100644 --- a/lib/utils/wagmi-utils.js +++ b/lib/utils/wagmi-utils.js @@ -1,38 +1,81 @@ // @flow -import { configureChains, createClient } from 'wagmi'; +import { connectorsForWallets } from '@rainbow-me/rainbowkit'; +import { + injectedWallet, + metaMaskWallet, + rainbowWallet, + walletConnectWallet, + // eslint-disable-next-line import/extensions +} from '@rainbow-me/rainbowkit/wallets'; +import * as React from 'react'; +import { configureChains, createClient, useProvider } from 'wagmi'; // eslint-disable-next-line import/extensions import { mainnet } from 'wagmi/chains'; // eslint-disable-next-line import/extensions import { alchemyProvider } from 'wagmi/providers/alchemy'; // eslint-disable-next-line import/extensions import { publicProvider } from 'wagmi/providers/public'; +import { ENSCacheProvider } from '../components/ens-cache-provider.react.js'; + // details can be found at https://wagmi.sh/core/providers/configuring-chains type WagmiChainConfiguration = { +chains: mixed, +provider: mixed, }; function configureWagmiChains(alchemyKey: ?string): WagmiChainConfiguration { const availableProviders = alchemyKey ? [alchemyProvider({ apiKey: alchemyKey })] : [publicProvider()]; const { chains, provider } = configureChains([mainnet], availableProviders); return { chains, provider }; } type WagmiClientInput = { +provider: mixed, +connectors?: mixed, }; function createWagmiClient(input: WagmiClientInput): mixed { const { provider, connectors } = input; return createClient({ autoConnect: true, connectors, provider, }); } -export { configureWagmiChains, createWagmiClient }; +const { chains, provider } = configureWagmiChains(process.env.COMM_ALCHEMY_KEY); +const connectors = connectorsForWallets([ + { + groupName: 'Recommended', + wallets: [ + injectedWallet({ chains }), + rainbowWallet({ chains }), + metaMaskWallet({ chains }), + walletConnectWallet({ chains }), + ], + }, +]); +const wagmiClient: mixed = createWagmiClient({ connectors, provider }); +const wagmiChains: mixed = chains; + +type Props = { + +children: React.Node, +}; +function WagmiENSCacheProvider(props: Props): React.Node { + const { children } = props; + const wagmiProvider = useProvider(); + return ( + {children} + ); +} + +export { + configureWagmiChains, + createWagmiClient, + wagmiClient, + wagmiChains, + WagmiENSCacheProvider, +}; diff --git a/web/app.react.js b/web/app.react.js index 52993dee8..9fadc7335 100644 --- a/web/app.react.js +++ b/web/app.react.js @@ -1,348 +1,348 @@ // @flow import 'basscss/css/basscss.min.css'; import './theme.css'; import { config as faConfig } from '@fortawesome/fontawesome-svg-core'; import classnames from 'classnames'; import _isEqual from 'lodash/fp/isEqual.js'; import * as React from 'react'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { useDispatch } from 'react-redux'; import { WagmiConfig } from 'wagmi'; import { fetchEntriesActionTypes, updateCalendarQueryActionTypes, } from 'lib/actions/entry-actions.js'; import { ModalProvider, useModalContext, } from 'lib/components/modal-provider.react.js'; import { createLoadingStatusSelector, combineLoadingStatuses, } from 'lib/selectors/loading-selectors.js'; import { unreadCount } from 'lib/selectors/thread-selectors.js'; import { isLoggedIn } from 'lib/selectors/user-selectors.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { registerConfig } from 'lib/utils/config.js'; +import { WagmiENSCacheProvider, wagmiClient } from 'lib/utils/wagmi-utils.js'; import AppsDirectory from './apps/apps-directory.react.js'; import Calendar from './calendar/calendar.react.js'; import Chat from './chat/chat.react.js'; import { TooltipProvider } from './chat/tooltip-provider.js'; import NavigationArrows from './components/navigation-arrows.react.js'; import electron from './electron.js'; import InputStateContainer from './input/input-state-container.react.js'; import LoadingIndicator from './loading-indicator.react.js'; import { MenuProvider } from './menu-provider.react.js'; import UpdateModalHandler from './modals/update-modal.react.js'; import SettingsSwitcher from './navigation-panels/settings-switcher.react.js'; import Topbar from './navigation-panels/topbar.react.js'; import { updateNavInfoActionType } from './redux/action-types.js'; import DeviceIDUpdater from './redux/device-id-updater.js'; import DisconnectedBarVisibilityHandler from './redux/disconnected-bar-visibility-handler.js'; import DisconnectedBar from './redux/disconnected-bar.js'; import FocusHandler from './redux/focus-handler.react.js'; import PolicyAcknowledgmentHandler from './redux/policy-acknowledgment-handler.js'; import { useSelector } from './redux/redux-utils.js'; import VisibilityHandler from './redux/visibility-handler.react.js'; import history from './router-history.js'; import AccountSettings from './settings/account-settings.react.js'; import DangerZone from './settings/danger-zone.react.js'; import CommunityPicker from './sidebar/community-picker.react.js'; import Splash from './splash/splash.react.js'; import './typography.css'; import css from './style.css'; import getTitle from './title/getTitle.js'; import { type NavInfo } from './types/nav-types.js'; import { canonicalURLFromReduxState, navInfoFromURL } from './url-utils.js'; -import { WagmiENSCacheProvider, wagmiClient } from './utils/wagmi-utils.js'; // We want Webpack's css-loader and style-loader to handle the Fontawesome CSS, // so we disable the autoAddCss logic and import the CSS file. Otherwise every // icon flashes huge for a second before the CSS is loaded. import '@fortawesome/fontawesome-svg-core/styles.css'; faConfig.autoAddCss = false; registerConfig({ // We can't securely cache credentials on web, so we have no way to recover // from a cookie invalidation resolveInvalidatedCookie: null, // We use httponly cookies on web to protect against XSS attacks, so we have // no access to the cookies from JavaScript setCookieOnRequest: false, setSessionIDOnRequest: true, // Never reset the calendar range calendarRangeInactivityLimit: null, platformDetails: { platform: 'web' }, }); type BaseProps = { +location: { +pathname: string, ... }, }; type Props = { ...BaseProps, // Redux state +navInfo: NavInfo, +entriesLoadingStatus: LoadingStatus, +loggedIn: boolean, +activeThreadCurrentlyUnread: boolean, // Redux dispatch functions +dispatch: Dispatch, +modals: $ReadOnlyArray, }; class App extends React.PureComponent { componentDidMount() { const { navInfo, location: { pathname }, loggedIn, } = this.props; const newURL = canonicalURLFromReduxState(navInfo, pathname, loggedIn); if (pathname !== newURL) { history.replace(newURL); } } componentDidUpdate(prevProps: Props) { const { navInfo, location: { pathname }, loggedIn, } = this.props; if (!_isEqual(navInfo)(prevProps.navInfo)) { const newURL = canonicalURLFromReduxState(navInfo, pathname, loggedIn); if (newURL !== pathname) { history.push(newURL); } } else if (pathname !== prevProps.location.pathname) { const newNavInfo = navInfoFromURL(pathname, { navInfo }); if (!_isEqual(newNavInfo)(navInfo)) { this.props.dispatch({ type: updateNavInfoActionType, payload: newNavInfo, }); } } else if (loggedIn !== prevProps.loggedIn) { const newURL = canonicalURLFromReduxState(navInfo, pathname, loggedIn); if (newURL !== pathname) { history.replace(newURL); } } if (loggedIn !== prevProps.loggedIn) { electron?.clearHistory(); } } onWordmarkClicked = () => { this.props.dispatch({ type: updateNavInfoActionType, payload: { tab: 'chat' }, }); }; render() { let content; if (this.props.loggedIn) { content = this.renderMainContent(); } else { content = ; } return ( {content} {this.props.modals} ); } onHeaderDoubleClick = () => electron?.doubleClickTopBar(); stopDoubleClickPropagation = electron ? e => e.stopPropagation() : null; renderMainContent() { const mainContent = this.getMainContentWithSwitcher(); let navigationArrows = null; if (electron) { navigationArrows = ; } const headerClasses = classnames({ [css.header]: true, [css['electron-draggable']]: electron, }); const wordmarkClasses = classnames({ [css.wordmark]: true, [css['electron-non-draggable']]: electron, }); return (

Comm

{navigationArrows}
{mainContent}
); } getMainContentWithSwitcher() { const { tab, settingsSection } = this.props.navInfo; let mainContent; if (tab === 'settings') { if (settingsSection === 'account') { mainContent = ; } else if (settingsSection === 'danger-zone') { mainContent = ; } return (
{mainContent}
); } if (tab === 'calendar') { mainContent = ; } else if (tab === 'chat') { mainContent = ; } else if (tab === 'apps') { mainContent = ; } const mainContentClass = classnames( css['main-content-container'], css['main-content-container-column'], ); return (
{mainContent}
); } } const fetchEntriesLoadingStatusSelector = createLoadingStatusSelector( fetchEntriesActionTypes, ); const updateCalendarQueryLoadingStatusSelector = createLoadingStatusSelector( updateCalendarQueryActionTypes, ); const ConnectedApp: React.ComponentType = React.memo( function ConnectedApp(props) { const activeChatThreadID = useSelector( state => state.navInfo.activeChatThreadID, ); const navInfo = useSelector(state => state.navInfo); const fetchEntriesLoadingStatus = useSelector( fetchEntriesLoadingStatusSelector, ); const updateCalendarQueryLoadingStatus = useSelector( updateCalendarQueryLoadingStatusSelector, ); const entriesLoadingStatus = combineLoadingStatuses( fetchEntriesLoadingStatus, updateCalendarQueryLoadingStatus, ); const loggedIn = useSelector(isLoggedIn); const activeThreadCurrentlyUnread = useSelector( state => !activeChatThreadID || !!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread, ); const boundUnreadCount = useSelector(unreadCount); React.useEffect(() => { document.title = getTitle(boundUnreadCount); electron?.setBadge(boundUnreadCount === 0 ? null : boundUnreadCount); }, [boundUnreadCount]); const dispatch = useDispatch(); const modalContext = useModalContext(); const modals = React.useMemo( () => modalContext.modals.map(([modal, key]) => ( {modal} )), [modalContext.modals], ); return ( ); }, ); function AppWithProvider(props: BaseProps): React.Node { return ( ); } export default AppWithProvider; diff --git a/web/splash/splash.react.js b/web/splash/splash.react.js index 57240a715..6e3817820 100644 --- a/web/splash/splash.react.js +++ b/web/splash/splash.react.js @@ -1,39 +1,40 @@ // @flow import { darkTheme, RainbowKitProvider } from '@rainbow-me/rainbowkit'; import _merge from 'lodash/fp/merge.js'; import * as React from 'react'; +import { wagmiChains } from 'lib/utils/wagmi-utils.js'; + import css from './splash.css'; import LoginForm from '../account/log-in-form.react.js'; -import { wagmiChains } from '../utils/wagmi-utils.js'; function Splash(): React.Node { const rainbowKitTheme = React.useMemo(() => { return _merge(darkTheme())({ radii: { modal: 0, modalMobile: 0, }, colors: { modalBackdrop: '#242529', }, }); }, []); return (
); } export default Splash; diff --git a/web/utils/wagmi-utils.js b/web/utils/wagmi-utils.js deleted file mode 100644 index 0ed96d1eb..000000000 --- a/web/utils/wagmi-utils.js +++ /dev/null @@ -1,46 +0,0 @@ -// @flow - -import { connectorsForWallets } from '@rainbow-me/rainbowkit'; -import { - injectedWallet, - rainbowWallet, - metaMaskWallet, - walletConnectWallet, - // eslint-disable-next-line import/extensions -} from '@rainbow-me/rainbowkit/wallets'; -import * as React from 'react'; -import { useProvider } from 'wagmi'; - -import { ENSCacheProvider } from 'lib/components/ens-cache-provider.react.js'; -import { - configureWagmiChains, - createWagmiClient, -} from 'lib/utils/wagmi-utils.js'; - -const { chains, provider } = configureWagmiChains(process.env.COMM_ALCHEMY_KEY); -const connectors = connectorsForWallets([ - { - groupName: 'Recommended', - wallets: [ - injectedWallet({ chains }), - rainbowWallet({ chains }), - metaMaskWallet({ chains }), - walletConnectWallet({ chains }), - ], - }, -]); -const wagmiClient: mixed = createWagmiClient({ connectors, provider }); -const wagmiChains: mixed = chains; - -type Props = { - +children: React.Node, -}; -function WagmiENSCacheProvider(props: Props): React.Node { - const { children } = props; - const wagmiProvider = useProvider(); - return ( - {children} - ); -} - -export { wagmiClient, wagmiChains, WagmiENSCacheProvider };