Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show active offers #461

Merged
merged 11 commits into from
Aug 24, 2022
101 changes: 101 additions & 0 deletions src/components/Earn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { EarnReportOverlay } from './EarnReport'
import * as Api from '../libs/JmWalletApi'
import styles from './Earn.module.css'
import { OrderbookOverlay } from './Orderbook'
import Balance from './Balance'

// In order to prevent state mismatch, the 'maker stop' response is delayed shortly.
// Even though the API response suggests that the maker has started or stopped immediately, it seems that this is not always the case.
Expand Down Expand Up @@ -83,6 +84,99 @@ const factorToPercentage = (val, precision = 6) => {
return Number((val * 100).toFixed(precision))
}

const renderOrderType = (val, t) => {
if (val.includes('absoffer')) {
return <rb.Badge bg="info">{t('earn.current.text_offer_type_absolute')}</rb.Badge>
}
if (val.includes('reloffer')) {
return <rb.Badge bg="primary">{t('earn.current.text_offer_type_relative')}</rb.Badge>
}
return <rb.Badge bg="secondary">{val}</rb.Badge>
}

function CurrentOffer({ offer, nickname }) {
const { t } = useTranslation()
const settings = useSettings()

return (
<div className={styles.offerContainer}>
<div className="d-flex justify-content-between align-items-center">
<div className={styles.offerTitle}>{t('earn.current.title', { id: offer.oid })}</div>
<div className="d-flex align-items-center gap-1">{renderOrderType(offer.ordertype, t)}</div>
</div>
<rb.Container className="mt-2">
<rb.Row className="mb-2">
<rb.Col xs={12}>
<div className={styles.offerLabel}>{t('earn.current.text_nickname')}</div>
<div>{nickname}</div>
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={6}>
<div className="d-flex flex-column">
<div className={styles.offerLabel}>{t('earn.current.text_cjfee')}</div>
<div>
{offer.ordertype.includes('reloffer') ? (
<>{offer.cjfee}%</>
) : (
<>
<Balance
valueString={String(offer.cjfee)}
convertToUnit={settings.unit}
showBalance={settings.showBalance}
/>
</>
)}
</div>
</div>
</rb.Col>

<rb.Col xs={6}>
<div className="d-flex flex-column">
<div className={styles.offerLabel}>{t('earn.current.text_minsize')}</div>
<div>
<Balance
valueString={String(offer.minsize)}
convertToUnit={settings.unit}
showBalance={settings.showBalance}
/>
</div>
</div>
</rb.Col>
</rb.Row>

<rb.Row>
<rb.Col xs={6}>
<div className="d-flex flex-column">
<div className={styles.offerLabel}>{t('earn.current.text_txfee')}</div>
<div>
<Balance
valueString={String(offer.txfee)}
convertToUnit={settings.unit}
showBalance={settings.showBalance}
/>
</div>
</div>
</rb.Col>

<rb.Col xs={6}>
<div className="d-flex flex-column">
<div className={styles.offerLabel}>{t('earn.current.text_maxsize')}</div>
<div>
<Balance
valueString={String(offer.maxsize)}
convertToUnit={settings.unit}
showBalance={settings.showBalance}
/>
</div>
</div>
</rb.Col>
</rb.Row>
</rb.Container>
</div>
)
}

export default function Earn() {
const { t } = useTranslation()
const settings = useSettings()
Expand Down Expand Up @@ -294,6 +388,13 @@ export default function Earn() {
!serviceInfo?.makerRunning &&
!isWaitingMakerStart &&
!isWaitingMakerStop && <p className="text-secondary mb-4">{t('earn.market_explainer')}</p>}
{serviceInfo?.makerRunning && serviceInfo?.offers && serviceInfo?.nickname && (
<>
{serviceInfo.offers.map((offer, index) => (
<CurrentOffer key={index} offer={offer} nickname={serviceInfo.nickname} />
))}
</>
)}
{!serviceInfo?.coinjoinInProgress && (
<>
<PageTitle
Expand Down
27 changes: 27 additions & 0 deletions src/components/Earn.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,30 @@
border-color: var(--bs-gray-800);
opacity: 100%;
}

.offerContainer {
border: 1px solid var(--bs-gray-200);
border-radius: 0.3rem;
padding: 1.25rem;
margin-bottom: 1.5rem;
}

:root[data-theme='dark'] .offerContainer {
border-color: var(--bs-gray-700);
}

.offerContainer .offerTitle {
width: 100%;
font-size: 1.2rem;
color: var(--bs-body-color);
}

.offerContainer .offerLabel {
color: var(--bs-gray-600);
font-size: 0.8rem;
}

.offerContainer .offerContent {
font-size: 0.8rem;
word-break: break-all;
}
Comment on lines +56 to +81
Copy link

Choose a reason for hiding this comment

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

That's the same styles as the existing FB box right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes.

35 changes: 30 additions & 5 deletions src/context/ServiceInfoContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,35 @@ import * as Api from '../libs/JmWalletApi'
// interval in milliseconds for periodic session requests
const SESSION_REQUEST_INTERVAL = 10_000

interface Offer {
oid: number
ordertype: string
minsize: Api.AmountSats
maxsize: Api.AmountSats
txfee: Api.AmountSats
cjfee: string
}

interface JmSessionData {
session: boolean
maker_running: boolean
coinjoin_in_process: boolean
wallet_name: string
wallet_name: Api.WalletName | 'None'
offer_list: Offer[] | null
nickname: string | null
}

type SessionFlag = { sessionActive: boolean }
type MakerRunningFlag = { makerRunning: boolean }
type CoinjoinInProgressFlag = { coinjoinInProgress: boolean }
type WalletName = { walletName: string | null }

type ServiceInfo = SessionFlag & MakerRunningFlag & CoinjoinInProgressFlag & WalletName
type ServiceInfo = SessionFlag &
MakerRunningFlag &
CoinjoinInProgressFlag & {
walletName: Api.WalletName | null
offers: Offer[] | null
nickname: string | null
}
type ServiceInfoUpdate = ServiceInfo | MakerRunningFlag | CoinjoinInProgressFlag

interface ServiceInfoContextEntry {
Expand Down Expand Up @@ -74,17 +90,26 @@ const ServiceInfoProvider = ({ children }: React.PropsWithChildren<{}>) => {
}
}

const fetch = Api.getSession({ signal })
const fetch = Api.getSession({ signal, token: currentWallet?.token })
.then((res) => (res.ok ? res.json() : Api.Helper.throwError(res)))
.then((data: JmSessionData) => {
const {
session: sessionActive,
maker_running: makerRunning,
coinjoin_in_process: coinjoinInProgress,
wallet_name: walletNameOrNoneString,
offer_list: offers,
nickname,
} = data
const activeWalletName = walletNameOrNoneString !== 'None' ? walletNameOrNoneString : null
return { sessionActive, makerRunning, coinjoinInProgress, walletName: activeWalletName } as ServiceInfo
return {
walletName: activeWalletName,
sessionActive,
makerRunning,
coinjoinInProgress,
offers,
nickname,
} as ServiceInfo
})

fetchSessionInProgress.current = fetch
Expand Down
4 changes: 2 additions & 2 deletions src/context/WalletContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import * as Api from '../libs/JmWalletApi'
import { WalletBalanceSummary, toBalanceSummary } from './BalanceSummary'

export interface CurrentWallet {
name: string
token: string
name: Api.WalletName
token: Api.ApiToken
}

// TODO: move these interfaces to JmWalletApi, once distinct types are used as return value instead of plain "Response"
Expand Down
10 changes: 10 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,16 @@
"text_stopping": "Stopping",
"button_show_report": "Show earnings report",
"button_show_orderbook": "Show orderbook",
"current": {
"title": "Active Offer #{{ id }}",
"text_nickname": "Nym",
"text_cjfee": "Fee",
"text_minsize": "Minimum Size",
"text_maxsize": "Maximum Size",
"text_txfee": "Transaction Fee",
"text_offer_type_absolute": "absolute",
"text_offer_type_relative": "relative"
},
"report": {
"title": "Earnings Report",
"text_report_summary_one": "{{ count }} entry",
Expand Down
4 changes: 2 additions & 2 deletions src/libs/JmWalletApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
*/
const basePath = () => `${window.JM.PUBLIC_PATH}/api`

type ApiToken = string
type WalletName = string
export type ApiToken = string
export type WalletName = `${string}.jmdat`

type Mixdepth = number
export type AmountSats = number // TODO: should be BigInt! Remove once every caller migrated to TypeScript.
Expand Down
6 changes: 4 additions & 2 deletions src/session.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { WalletName, ApiToken } from './libs/JmWalletApi'

const SESSION_KEY = 'joinmarket'

export interface SessionItem {
name: string
token: string
name: WalletName
token: ApiToken
}

export const setSession = (session: SessionItem) => sessionStorage.setItem(SESSION_KEY, JSON.stringify(session))
Expand Down