Skip to content
Merged
1 change: 1 addition & 0 deletions frontend/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"prettier": "^2.7.1",
"react": "^17.0.2",
"react-blockies": "^1.4.1",
"react-copy-to-clipboard": "^5.1.0",
"react-datepicker": "^4.6.0",
"react-dom": "^17.0.2",
"react-draft-wysiwyg": "^1.14.7",
Expand Down
13 changes: 12 additions & 1 deletion frontend/packages/client/src/App.sass
Original file line number Diff line number Diff line change
Expand Up @@ -703,4 +703,15 @@ span[data-tooltip]
transform: scale(1)

.rdw-editor-toolbar
border-radius: 8px !important
border-radius: 8px !important

.wallet-connect-background
z-index: 31
background: rgba(0, 0, 0, 0.55)
position: fixed
width: 100vw
height: 100vh
top: 0
right: 0
.wallet-connect-content
z-index: 32
2 changes: 1 addition & 1 deletion frontend/packages/client/src/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function Header(props) {
</div>
<div className="navbar-end">
<span className="navbar-item p-0">
<WalletConnect />
<WalletConnect expandContainer />
</span>
</div>
</nav>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ import Blockies from 'react-blockies';
import { useVotingResults, useWindowDimensions } from 'hooks';
import useMediaQuery, { mediaMatchers } from 'hooks/useMediaQuery';
import { parseDateFromServer } from 'utils';
import { truncateAddress as truncate } from 'utils';
import { LinkOut } from './Svg';
import Tooltip from './Tooltip';

function truncate(str) {
return str.substr(0, 3) + '...' + str.substr(10, str.length);
}

const BlockieWithAddress = React.forwardRef(
({ creatorAddr, isCoreCreator }, ref) => {
const [addr, setAdd] = useState(creatorAddr);
Expand Down
23 changes: 23 additions & 0 deletions frontend/packages/client/src/components/Svg.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 15 additions & 6 deletions frontend/packages/client/src/components/Tooltip.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import React from 'react';

export default function Tooltip({ position, text, children, classNames = '' }) {
export default function Tooltip({
enabled = true,
position,
text,
children,
classNames = '',
alwaysVisible = false,
}) {
const positionConfig = {
left: 'has-tooltip-left',
right: 'has-tooltip-right',
top: 'has-tooltip-top',
bottom: 'has-tooltip-bottom',
};
const className = positionConfig[position] ?? '';
const className = `${positionConfig[position] ?? ''}${
alwaysVisible ? ' has-tooltip-active' : ''
}`;

const props = enabled ? { 'data-tooltip': text } : {};
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

by removing data-tooltip' tooltip gets disabled


return (
<span
className={`has-tooltip-arrow ${className} ${classNames}`}
data-tooltip={text}
>
<span className={`has-tooltip-arrow ${className} ${classNames}`} {...props}>
{children}
</span>
);
Expand Down
195 changes: 169 additions & 26 deletions frontend/packages/client/src/components/WalletConnect.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,188 @@
import React from 'react';
import React, { useEffect, useRef, useState } from 'react';
import Blockies from 'react-blockies';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { Web3Consumer } from '../contexts/Web3';
import { Copy } from 'components/Svg';
import { useMediaQuery, useOnClickOutside } from 'hooks';
import { truncateAddress } from 'utils';
import classnames from 'classnames';
import Tooltip from './Tooltip';

const SignInOutButton = ({
user: { loggedIn, addr },
openWalletModal,
injectedProvider,
closeModal,
expandContainer = false,
}) => {
const signInOrOut = async (event) => {
const notMobile = useMediaQuery();

const [dropDownClass, setDropDownClass] = useState('');
const [addressCopied, setAddressCopied] = useState(false);

const dropdownRef = useRef();

const closeDropdown = (e) => {
setDropDownClass('');
};

useOnClickOutside(dropdownRef, closeDropdown);

const signOut = (event) => {
event.preventDefault();
event.stopPropagation();
injectedProvider.unauthenticate();
setDropDownClass('');
};

const connectWallet = (event) => {
event.preventDefault();
event.stopPropagation();
if (closeModal) {
closeModal();
}
if (loggedIn) {
injectedProvider.unauthenticate();
} else {
openWalletModal();
}
openWalletModal();
};

const openDropdown = (event) => {
event.preventDefault();
event.stopPropagation();
setDropDownClass('is-active');
};

const markAddressCopied = () => setAddressCopied(true);

useEffect(() => {
let timeout;
if (addressCopied) {
timeout = setTimeout(() => {
setAddressCopied(false);
}, 500);
}
return () => clearTimeout(timeout);
}, [addressCopied]);

const dropdownBackground = classnames('', {
'wallet-connect-background': dropDownClass && !notMobile,
});

const buttonClass = classnames(
'wallet-connect button is-uppercase transition-all small-text rounded-sm',
{ 'is-primary': !loggedIn },
{ 'px-2': !notMobile }
);
const addressStyle = classnames('', { 'smaller-text': !notMobile });

const containerAddressStyle = classnames('is-flex', { 'pl-2': !notMobile });

const containerButtonStyle = classnames('is-flex is-align-items-center', {
'flex-1 is-justify-content-space-between': notMobile,
});

return (
<button
onClick={signInOrOut}
className="wallet-connect button is-primary is-uppercase transition-all small-text"
>
{loggedIn ? (
<>
<span className="is-hidden-mobile is-hidden-connect">
{addr} -&nbsp;
</span>
<span>Disconnect</span>
</>
) : (
<>
<span>Connect</span>
<span className="is-hidden-mobile">&nbsp;Wallet</span>
</>
)}
</button>
<>
<div className={dropdownBackground} />
<div
className={`dropdown ${dropDownClass}`}
aria-haspopup="true"
aria-controls="dropdown-menu"
style={{ position: 'relative' }}
>
<div
className="dropdown-trigger is-flex is-justify-content-flex-end"
style={notMobile && expandContainer ? { width: '280px' } : {}}
>
<button
onClick={loggedIn ? openDropdown : connectWallet}
className={buttonClass}
style={
notMobile
? {
...(loggedIn ? { width: '147px' } : { width: '206px' }),
height: '40px',
}
: { width: '119px', height: '32px' }
}
>
{loggedIn ? (
<div className={containerButtonStyle}>
<Blockies
seed={addr}
size={notMobile ? 6.5 : 5}
scale={4}
className="blockies"
/>
<div className={containerAddressStyle}>
<p className={addressStyle}>{truncateAddress(addr, 4, 4)}</p>
</div>
</div>
) : (
<>
<span>Connect</span>
<span className="is-hidden-mobile">&nbsp;Wallet</span>
</>
)}
</button>
</div>

<div
className="dropdown-menu wallet-connect-content"
id="dropdown-menu"
role="menu"
ref={dropdownRef}
style={!notMobile ? { left: '-160px' } : {}}
>
<div className="dropdown-content p-0" style={{ width: '277px' }}>
<div className="px-4 pt-4 pb-2">
<Tooltip
classNames="is-flex is-flex-grow-1 is-align-items-center transition-all"
position="top"
text="Copied!"
alwaysVisible={true}
enabled={addressCopied}
>
<CopyToClipboard text={addr} onCopy={markAddressCopied}>
<div
className="columns flex-1 is-mobile m-0 px-4 py-0 rounded-sm button is-white border-light"
style={{
borderColor: 'hsl(0deg, 0%, 86%)',
height: '32px',
}}
>
<div className="column p-0 is-flex is-align-items-center">
<span className="small-text">{addr}</span>
</div>
<div className="column p-0 is-flex is-align-items-center is-narrow">
<div
className="is-flex is-align-items-center py-0 px-1"
style={{ height: '23px' }}
>
<Copy />
</div>
</div>
</div>
</CopyToClipboard>
</Tooltip>
</div>

<hr className="dropdown-divider" />
<div className="px-4 pb-4 pt-2">
<div
className="button is-fullwidth rounded-sm is-uppercase is-flex small-text has-text-white has-background-black"
style={{ height: '32px' }}
onClick={signOut}
>
disconnect
</div>
</div>
</div>
</div>
</div>
</>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

there's a upcoming update for this component, I thought about making it more modular but decided to wait for the update and see how it can be split into more components

);
};

const CurrentUser = ({ web3, closeModal }) => {
const CurrentUser = ({ web3, closeModal, expandContainer }) => {
const { user, injectedProvider, openWalletModal } = web3;
if (!user) {
return null;
Expand All @@ -53,6 +195,7 @@ const CurrentUser = ({ web3, closeModal }) => {
injectedProvider={injectedProvider}
openWalletModal={openWalletModal}
closeModal={closeModal}
expandContainer={expandContainer}
/>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/client/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export { default as useStarAnimation } from './useStarAnimation';
export { default as useAddFungibleToken } from './useAddFungibleToken';
export { default as useBrowserName } from './useBrowserName';
export { default as useLocalStorage } from './useLocalStorage';
export { default as useOnClickOutside } from './useOnOutsideClick';
28 changes: 28 additions & 0 deletions frontend/packages/client/src/hooks/useOnOutsideClick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect } from 'react';

export default function useOnClickOutside(ref, handler) {
useEffect(
() => {
const listener = (event) => {
// Do nothing if clicking ref's element or descendent elements
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
},
// Add ref and handler to effect dependencies
// It's worth noting that because passed in handler is a new
// function on every render that will cause this effect
// callback/cleanup to run every render. It's not a big deal
// but to optimize you can wrap handler in useCallback before
// passing it into this hook.
[ref, handler]
);
}
8 changes: 8 additions & 0 deletions frontend/packages/client/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,11 @@ export const formatTime = (date) => {
minutes = minutes < 10 ? '0' + minutes : minutes;
return hours + ':' + minutes + ' ' + ampm;
};

export function truncateAddress(str, initial = 3, tail = 10) {
return (
str.substr(0, initial) +
'...' +
str.substr((tail - str.length) * -1, str.length)
);
}
Loading