|
| 1 | +import React, { useState, useEffect, useCallback } from 'react' |
| 2 | +import { useTranslation } from 'react-i18next' |
| 3 | +import { useState as useGlobalState } from 'states' |
| 4 | +import { bytes } from '@ckb-lumos/codec' |
| 5 | +import { blockchain } from '@ckb-lumos/base' |
| 6 | +import Dialog from 'widgets/Dialog' |
| 7 | +import Table, { TableProps } from 'widgets/Table' |
| 8 | +import { Download, Search, ArrowNext, Clean, ArrowUp, ArrowDown } from 'widgets/Icons/icon' |
| 9 | +import Button from 'widgets/Button' |
| 10 | +import Switch from 'widgets/Switch' |
| 11 | +import Tooltip from 'widgets/Tooltip' |
| 12 | +import PageContainer from 'components/PageContainer' |
| 13 | +import { |
| 14 | + PerunIcon, |
| 15 | + AddSimple, |
| 16 | + DetailIcon, |
| 17 | + CkbIcon, |
| 18 | + InfoCircleOutlined, |
| 19 | + DepositTimeSort, |
| 20 | + PerunSend, |
| 21 | + PerunClose, |
| 22 | + LineDownArrow, |
| 23 | +} from 'widgets/Icons/icon' |
| 24 | +import TableNoData from 'widgets/Icons/TableNoData.png' |
| 25 | +import { type CKBComponents } from '@ckb-lumos/lumos/rpc' |
| 26 | +import { |
| 27 | + SerializeOffChainParticipant, |
| 28 | + SerializeSEC1EncodedPubKey, |
| 29 | +} from '@ckb-connect/perun-wallet-wrapper/dist/ckb/serialization' |
| 30 | +import { channelIdToString, channelIdFromString } from '@ckb-connect/perun-wallet-wrapper/dist/translator' |
| 31 | +import * as wire from '@ckb-connect/perun-wallet-wrapper/dist/wire' |
| 32 | + |
| 33 | +import { ControllerResponse } from 'services/remote/remoteApiWrapper' |
| 34 | +import { |
| 35 | + OfflineSignStatus, |
| 36 | + OfflineSignType, |
| 37 | + getCurrentWalletAccountExtendedPubKey, |
| 38 | + perunServiceAction, |
| 39 | + respondPerunRequest, |
| 40 | + signRawMessage, |
| 41 | + signTransactionOnly, |
| 42 | + showErrorMessage, |
| 43 | +} from 'services/remote' |
| 44 | +import { |
| 45 | + addressToScript, |
| 46 | + scriptToAddress, |
| 47 | + bytesToHex, |
| 48 | + ErrorCode, |
| 49 | + errorFormatter, |
| 50 | + isSuccessResponse, |
| 51 | + clsx, |
| 52 | + getParticipantByAddressAndPubkey, |
| 53 | +} from 'utils' |
| 54 | +import { PasswordDialog } from 'components/SignAndVerify' |
| 55 | +import PerunCreationRequestList from 'components/PerunCreationRequestList' |
| 56 | +import PerunLockedInChannels from 'components/PerunLockedInChannels' |
| 57 | +import PerunCloseChannel from 'components/PerunCloseChannel' |
| 58 | +import PerunOpenChannel from 'components/PerunOpenChannel' |
| 59 | +import PerunSendPayment from 'components/PerunSendPayment' |
| 60 | +import { State } from '@ckb-connect/perun-wallet-wrapper/wire' |
| 61 | +import RowExtend from './RowExtend' |
| 62 | +import styles from './perun.module.scss' |
| 63 | + |
| 64 | +enum DialogType { |
| 65 | + creationRequest = 'creationRequest', |
| 66 | + lockedInChannels = 'lockedInChannels', |
| 67 | + closeChannel = 'closeChannel', |
| 68 | + send = 'send', |
| 69 | + openChannel = 'openChannel', |
| 70 | +} |
| 71 | + |
| 72 | +const PaymentChannel = () => { |
| 73 | + const { |
| 74 | + wallet, |
| 75 | + perun: { requests, channels }, |
| 76 | + } = useGlobalState() |
| 77 | + const [t, _] = useTranslation() |
| 78 | + const [dialogType, setDialogType] = useState<DialogType | undefined>(undefined) |
| 79 | + |
| 80 | + const [myPubKey, setMyPubKey] = useState<string>('') |
| 81 | + |
| 82 | + const [expandedRow, setExpandedRow] = useState<number | null>(null) |
| 83 | + |
| 84 | + const assets = ['CKB'] |
| 85 | + |
| 86 | + useEffect(() => { |
| 87 | + getCurrentWalletAccountExtendedPubKey({ type: 0, index: 0 }).then(res => { |
| 88 | + if (isSuccessResponse(res)) { |
| 89 | + setMyPubKey(res.result) |
| 90 | + } |
| 91 | + }) |
| 92 | + }, []) |
| 93 | + |
| 94 | + const handleExpandClick = (idx: number | null) => { |
| 95 | + setExpandedRow(prevIndex => (prevIndex === idx ? null : idx)) |
| 96 | + } |
| 97 | + |
| 98 | + const columns: TableProps<State.Transaction>['columns'] = [ |
| 99 | + { |
| 100 | + title: t('history.table.asset'), |
| 101 | + dataIndex: 'allocation', |
| 102 | + align: 'left', |
| 103 | + minWidth: '150px', |
| 104 | + render: (_, __, item) => JSON.stringify(item.allocation), |
| 105 | + }, |
| 106 | + { |
| 107 | + title: t('perun.creation-time'), |
| 108 | + dataIndex: 'createdAt', |
| 109 | + align: 'left', |
| 110 | + minWidth: '150px', |
| 111 | + render: (_, __, item) => item.createdAt, |
| 112 | + sortable: true, |
| 113 | + }, |
| 114 | + { |
| 115 | + title: t('history.table.status'), |
| 116 | + dataIndex: 'status', |
| 117 | + align: 'left', |
| 118 | + minWidth: '50px', |
| 119 | + render(_, __, item) { |
| 120 | + return 'status' |
| 121 | + }, |
| 122 | + }, |
| 123 | + { |
| 124 | + title: t('history.table.operation'), |
| 125 | + dataIndex: 'operation', |
| 126 | + align: 'center', |
| 127 | + minWidth: '72px', |
| 128 | + render(_, idx) { |
| 129 | + return <ArrowNext className={styles.arrow} data-is-expand-show={expandedRow === idx} /> |
| 130 | + }, |
| 131 | + }, |
| 132 | + ] |
| 133 | + |
| 134 | + return ( |
| 135 | + <PageContainer |
| 136 | + head={ |
| 137 | + <div className={styles.pageHeader}> |
| 138 | + <PerunIcon /> |
| 139 | + <p>{t('navbar.perun')}</p> |
| 140 | + </div> |
| 141 | + } |
| 142 | + > |
| 143 | + <div className={styles.container}> |
| 144 | + <div className={styles.topWrap}> |
| 145 | + <div className={clsx(styles.panel, styles.leftWrap)}> |
| 146 | + <h2>{t('perun.of-open-channels')}</h2> |
| 147 | + <h1>{channels.length}</h1> |
| 148 | + <Button type="primary" className={styles.createBtn} onClick={() => setDialogType(DialogType.openChannel)}> |
| 149 | + <AddSimple /> |
| 150 | + {t('perun.create-new-channel')} |
| 151 | + </Button> |
| 152 | + </div> |
| 153 | + <div className={clsx(styles.panel, styles.rightWrap)}> |
| 154 | + <h2> |
| 155 | + {t('perun.locked-in-channels')}{' '} |
| 156 | + <Button |
| 157 | + type="text" |
| 158 | + className={styles.detailBtn} |
| 159 | + onClick={() => setDialogType(DialogType.lockedInChannels)} |
| 160 | + > |
| 161 | + <DetailIcon /> |
| 162 | + </Button> |
| 163 | + </h2> |
| 164 | + <div className={styles.sliderWrap}> |
| 165 | + {assets.map(item => ( |
| 166 | + <div className={styles.sliderItem} key={item}> |
| 167 | + <h2> |
| 168 | + <CkbIcon /> |
| 169 | + {item} |
| 170 | + </h2> |
| 171 | + <p>0</p> |
| 172 | + </div> |
| 173 | + ))} |
| 174 | + </div> |
| 175 | + </div> |
| 176 | + </div> |
| 177 | + |
| 178 | + <div className={styles.panel}> |
| 179 | + <div className={styles.creationRequest}> |
| 180 | + <h2 className={styles.title}> |
| 181 | + {t('perun.channel-creation-request')}{' '} |
| 182 | + {requests.length > 0 && <span className={styles.badge}>{requests.length}</span>} |
| 183 | + </h2> |
| 184 | + <Button type="primary" onClick={() => setDialogType(DialogType.creationRequest)}> |
| 185 | + {t('perun.check')} |
| 186 | + </Button> |
| 187 | + </div> |
| 188 | + </div> |
| 189 | + |
| 190 | + <div className={clsx(styles.panel, styles.overview)}> |
| 191 | + <div className={styles.header}> |
| 192 | + <h2> |
| 193 | + {t('perun.channel-overview')} |
| 194 | + <Tooltip tip={t('perun.channel-overview-tooltip')} showTriangle placement="top"> |
| 195 | + <InfoCircleOutlined /> |
| 196 | + </Tooltip> |
| 197 | + </h2> |
| 198 | + </div> |
| 199 | + |
| 200 | + <div className={styles.overviewContent}> |
| 201 | + <Table |
| 202 | + columns={columns} |
| 203 | + dataSource={channels} |
| 204 | + noDataContent={t('overview.no-recent-activities')} |
| 205 | + rowExtendRender={channel => <RowExtend channel={channel} />} |
| 206 | + expandedRow={expandedRow} |
| 207 | + onRowClick={(_, __, idx) => handleExpandClick(idx)} |
| 208 | + /> |
| 209 | + </div> |
| 210 | + </div> |
| 211 | + |
| 212 | + {dialogType === DialogType.creationRequest && requests.length > 0 && ( |
| 213 | + <PerunCreationRequestList |
| 214 | + walletID={wallet?.id ?? ''} |
| 215 | + requests={requests} |
| 216 | + onCancel={() => setDialogType(undefined)} |
| 217 | + /> |
| 218 | + )} |
| 219 | + {dialogType === DialogType.lockedInChannels && ( |
| 220 | + <PerunLockedInChannels onClose={() => setDialogType(undefined)} /> |
| 221 | + )} |
| 222 | + {dialogType === DialogType.closeChannel && <PerunCloseChannel onClose={() => setDialogType(undefined)} />} |
| 223 | + |
| 224 | + <PerunOpenChannel |
| 225 | + show={dialogType === DialogType.openChannel && myPubKey} |
| 226 | + onClose={() => setDialogType(undefined)} |
| 227 | + myPubKey={myPubKey} |
| 228 | + /> |
| 229 | + |
| 230 | + {dialogType === DialogType.send && <PerunSendPayment onClose={() => setDialogType(undefined)} />} |
| 231 | + </div> |
| 232 | + </PageContainer> |
| 233 | + ) |
| 234 | +} |
| 235 | + |
| 236 | +PaymentChannel.displayName = 'PaymentChannel' |
| 237 | + |
| 238 | +export default PaymentChannel |
0 commit comments