Skip to content
This repository was archived by the owner on Apr 4, 2022. It is now read-only.

notifications #992

Merged
merged 18 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react'

import { Order } from 'api/operator'
import { Network } from 'types'
import { Network, UiError } from 'types'
import { TableState, TableStateSetters } from 'apps/explorer/components/OrdersTableWidget/useTable'

export type BlockchainNetwork = Network | undefined

type CommonState = {
addressAccountParams: { networkId: BlockchainNetwork; ownerAddress: string }
orders: Order[] | undefined
error: string
error?: UiError
isOrdersLoading: boolean
tableState: TableState
} & TableStateSetters
Expand Down
10 changes: 9 additions & 1 deletion src/apps/explorer/components/OrdersTableWidget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ import { OrdersTableContext, BlockchainNetwork } from './context/OrdersTableCont
import PaginationOrdersTable from './PaginationOrdersTable'
import { useGetAccountOrders } from 'hooks/useGetOrders'
import Spinner from 'components/common/Spinner'
import { ConnectionStatus } from 'components/ConnectionStatus'
import { Notification } from 'components/Notification'

const StyledTabLoader = styled.span`
padding-left: 4px;
`

const StyledExplorerTabs = styled(ExplorerTabs)`
margin: 1.6rem auto 0;
`

const tabItems = (isLoadingOrders: boolean): TabItemInterface[] => {
return [
{
Expand Down Expand Up @@ -75,7 +81,9 @@ const OrdersTableWidget: React.FC<Props> = ({ ownerAddress, networkId }) => {
handlePreviousPage,
}}
>
<ExplorerTabs tabItems={tabItems(isOrdersLoading)} extra={ExtraComponentNode} />
<ConnectionStatus />
{error && <Notification type={error.type} message={error.message} />}
<StyledExplorerTabs tabItems={tabItems(isOrdersLoading)} extra={ExtraComponentNode} />
</OrdersTableContext.Provider>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react'

import { Network } from 'types'
import { Network, UiError } from 'types'
import { Order } from 'api/operator'

export type BlockchainNetwork = Network | undefined

type CommonState = {
txHashParams: { networkId: BlockchainNetwork; txHash: string }
error: string
error?: UiError
orders: Order[] | undefined
isTxLoading: boolean
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import ExplorerTabs from '../common/ExplorerTabs/ExplorerTab'
import styled from 'styled-components'
import { TitleAddress } from 'apps/explorer/pages/styled'
import { BlockExplorerLink } from 'components/common/BlockExplorerLink'
import { ConnectionStatus } from 'components/ConnectionStatus'
import { Notification } from 'components/Notification'
import { useTxBatchTrades } from 'hooks/useTxBatchTrades'

interface Props {
Expand Down Expand Up @@ -82,6 +84,8 @@ export const TransactionsTableWidget: React.FC<Props> = ({ txHash }) => {
contentsToDisplay={<BlockExplorerLink type="tx" networkId={networkId} identifier={txHash} showLogo />}
/>
</h1>
<ConnectionStatus />
{error && <Notification type={error.type} message={error.message} />}
<TransactionsTableContext.Provider
value={{
orders,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type ExplorerTabsProps = Omit<TabsProps, 'tabTheme'>

const ExplorerTabs: React.FC<ExplorerTabsProps> = (props) => {
return (
<StyledTabs>
<StyledTabs className={props.className}>
<Tabs tabTheme={tabCustomThemeConfig} {...props} />
</StyledTabs>
)
Expand Down
9 changes: 8 additions & 1 deletion src/apps/explorer/pages/UserDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import React from 'react'
import { useParams } from 'react-router'
import styled from 'styled-components'

import OrdersTableWidget from '../components/OrdersTableWidget'
import { useNetworkId } from 'state/network'
import { BlockExplorerLink } from 'components/common/BlockExplorerLink'
import RedirectToSearch from 'components/RedirectToSearch'
import { useResolveEns } from 'hooks/useResolveEns'
import Spinner from 'components/common/Spinner'
import { TitleAddress, Wrapper } from 'apps/explorer/pages/styled'
import { TitleAddress, Wrapper as WrapperMod } from 'apps/explorer/pages/styled'

const Wrapper = styled(WrapperMod)`
> h1 {
padding: 2.4rem 0 0.75rem;
}
`

const UserDetails: React.FC = () => {
const { address } = useParams<{ address: string }>()
Expand Down
24 changes: 24 additions & 0 deletions src/components/ConnectionStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useCallback, useEffect, useState } from 'react'
import { Notification } from './Notification'

export const ConnectionStatus: React.FC = () => {
const [online, setOnline] = useState(true)

const handleConnectionChange = useCallback((e: Event) => {
setOnline(e.type === 'online')
}, [])

useEffect(() => {
window.addEventListener('online', handleConnectionChange)
window.addEventListener('offline', handleConnectionChange)

return (): void => {
window.removeEventListener('online', handleConnectionChange)
window.removeEventListener('offline', handleConnectionChange)
}
}, [handleConnectionChange])

return online ? null : (
<Notification type="warn" message="You may have lost your network connection." appendMessage={false} />
)
}
100 changes: 100 additions & 0 deletions src/components/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState } from 'react'
import styled from 'styled-components'
import { transparentize } from 'polished'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faExclamationEllipsis, faExclamationTriangle } from './icons'
import { BASE_COLOURS } from 'theme'

export interface NotificationProps {
type: 'warn' | 'error'
message: string
appendMessage?: boolean
}

export const NotificationWrap = styled.p<{ isActive?: boolean; type: string }>`
border-radius: 6px;
padding: 10px 16px;
background-color: ${({ theme, type }): string =>
type === 'error' ? transparentize(0.8, theme.red4) : transparentize(0.8, theme.orange)};
font-size: 12px;
display: ${({ isActive }): string => (isActive ? 'flex' : 'none')};
align-items: center;
margin: 0;

span {
flex-grow: 1;
margin: 0 16px;
line-height: 1.2;
max-width: calc(100% - 90px);
a {
color: ${BASE_COLOURS.orange1};
}
}

.svg-inline--fa {
color: ${({ theme, type }): string => (type === 'error' ? theme.red4 : theme.orange)};
width: 16px;
height: 16px;
}

&:not(:last-of-type) {
margin-bottom: 16px;
}
`

const CloseButton = styled.button`
cursor: pointer;
border: 0;
background-color: transparent;
background-image: none;
padding: 0;
width: 40px;
min-height: 20px;
display: flex;
align-items: center;
justify-content: flex-end;
position: relative;
&:before,
&:after {
content: '';
display: block;
width: 20px;
height: 2px;
background-color: ${({ theme }): string => theme.textPrimary1};
position: absolute;
}
&:after {
transform: rotate(-45deg);
left: 50%;
}
&:before {
left: 50%;
transform: rotate(45deg);
}
`

export const Notification: React.FC<NotificationProps> = ({
type,
message,
appendMessage = true,
}: NotificationProps) => {
const [isNoteActive, setIsNoteActive] = useState(true)
const isError = type === 'error'
const icon = isError ? faExclamationEllipsis : faExclamationTriangle
return (
<NotificationWrap type={type} isActive={isNoteActive}>
<FontAwesomeIcon icon={icon} />
<span>
{message}
{appendMessage && (
<>
. Please&nbsp;
<a onClick={(): void => window.location.reload()}>{isError ? 'try again ' : 'refresh '}</a>
{isError ? 'later.' : 'to get the latest.'}
</>
)}
</span>
<CloseButton onClick={(): void => setIsNoteActive(false)} />
</NotificationWrap>
)
}
1 change: 1 addition & 0 deletions src/components/common/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface TabTheme {
readonly indicatorTabSize: IndicatorTabSize
}
export interface Props {
readonly className?: string
readonly tabItems: TabItemInterface[]
readonly tabTheme: TabTheme
readonly defaultTab?: TabId
Expand Down
25 changes: 25 additions & 0 deletions src/components/icons/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'

export const faExclamationTriangle: IconDefinition = {
prefix: 'fas',
iconName: 'exclamation-triangle',
icon: [
512,
512,
[],
'',
'M248.747 204.705l6.588 112c.373 6.343 5.626 11.295 11.979 11.295h41.37a12 12 0 0 0 11.979-11.295l6.588-112c.405-6.893-5.075-12.705-11.979-12.705h-54.547c-6.903 0-12.383 5.812-11.978 12.705zM330 384c0 23.196-18.804 42-42 42s-42-18.804-42-42 18.804-42 42-42 42 18.804 42 42zm-.423-360.015c-18.433-31.951-64.687-32.009-83.154 0L6.477 440.013C-11.945 471.946 11.118 512 48.054 512H527.94c36.865 0 60.035-39.993 41.577-71.987L329.577 23.985zM53.191 455.002L282.803 57.008c2.309-4.002 8.085-4.002 10.394 0l229.612 397.993c2.308 4-.579 8.998-5.197 8.998H58.388c-4.617.001-7.504-4.997-5.197-8.997z',
],
}

export const faExclamationEllipsis: IconDefinition = {
prefix: 'fas',
iconName: 'exclamation-circle',
icon: [
512,
512,
[],
'',
'M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 448c-110.532 0-200-89.431-200-200 0-110.495 89.472-200 200-200 110.491 0 200 89.471 200 200 0 110.53-89.431 200-200 200zm42-104c0 23.159-18.841 42-42 42s-42-18.841-42-42 18.841-42 42-42 42 18.841 42 42zm-81.37-211.401l6.8 136c.319 6.387 5.591 11.401 11.985 11.401h41.17c6.394 0 11.666-5.014 11.985-11.401l6.8-136c.343-6.854-5.122-12.599-11.985-12.599h-54.77c-6.863 0-12.328 5.745-11.985 12.599z',
],
}
9 changes: 8 additions & 1 deletion src/components/orders/OrderDetails/OrderDetails.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ export const TokensNotLoaded = Template.bind({})
TokensNotLoaded.args = { ...defaultProps, order: { ...RICH_ORDER, buyToken: undefined } }

export const WithErrors = Template.bind({})
WithErrors.args = { ...defaultProps, errors: { error1: 'Failed something something', error2: 'Something else failed' } }
WithErrors.args = {
...defaultProps,
isOrderLoading: true,
errors: {
error1: { message: 'Failed something something', type: 'error' },
error2: { message: 'Something else failed', type: 'error' },
},
}
13 changes: 8 additions & 5 deletions src/components/orders/OrderDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { Order, Trade } from 'api/operator'
import { DetailsTable } from 'components/orders/DetailsTable'
import { RowWithCopyButton } from 'components/common/RowWithCopyButton'
import RedirectToSearch from 'components/RedirectToSearch'
import { Notification } from 'components/Notification'
import { Errors } from 'types'
import { ConnectionStatus } from 'components/ConnectionStatus'

const TitleUid = styled(RowWithCopyButton)`
color: ${({ theme }): string => theme.grey};
Expand All @@ -24,7 +27,7 @@ export type Props = {
trades: Trade[]
isOrderLoading: boolean
areTradesLoading: boolean
errors: Record<string, string>
errors: Errors
}

export const OrderDetails: React.FC<Props> = (props) => {
Expand Down Expand Up @@ -58,14 +61,14 @@ export const OrderDetails: React.FC<Props> = (props) => {
{order && 'Order details'}
{order && <TitleUid textToCopy={order.uid} contentsToDisplay={order.shortId} />}
</h1>
<ConnectionStatus />
{Object.keys(errors).map((key) => (
<Notification key={key} type={errors[key].type} message={errors[key].message} />
))}
{/* TODO: add tabs (overview/fills) */}
{order && areTokensLoaded && <DetailsTable order={{ ...order, txHash }} areTradesLoading={areTradesLoading} />}
{/* TODO: add fills tab for partiallyFillable orders */}
{!isOrderLoading && order && !areTokensLoaded && <p>Not able to load tokens</p>}
{/* TODO: do a better error display. Toast notification maybe? */}
{Object.keys(errors).map((key) => (
<p key={key}>{errors[key]}</p>
))}
{/* TODO: create common loading indicator */}
{isLoadingForTheFirstTime && <FontAwesomeIcon icon={faSpinner} spin size="3x" />}
</>
Expand Down
Loading