diff --git a/CHANGELOG_UNRELEASED.md b/CHANGELOG_UNRELEASED.md index d995def357..9278e4b347 100644 --- a/CHANGELOG_UNRELEASED.md +++ b/CHANGELOG_UNRELEASED.md @@ -23,7 +23,7 @@ - (bugs) [\#2295](https://github.com/bandprotocol/bandchain/pull/2295) Truncate `accumulated_commission` precision. ### Scan - +- (impv) [\#2310](https://github.com/bandprotocol/bandchain/pull/2310) Implemented the TxIndexpage layout for mobile view - (impv) [\#2305](https://github.com/bandprotocol/bandchain/pull/2305) Implement the TxHomepage layout for mobile view and adjusted the pagination on mobile view. - (feat) [\#2294](https://github.com/bandprotocol/bandchain/pull/2294) Implemented top part of `ValidatorIndexPage` for mobile - (impv) [\#2299](https://github.com/bandprotocol/bandchain/pull/2299) Update the latest transactions table for mobile version. diff --git a/scan/src/components/AmountRender.re b/scan/src/components/AmountRender.re index 950023d86f..72f2aefb35 100644 --- a/scan/src/components/AmountRender.re +++ b/scan/src/components/AmountRender.re @@ -1,6 +1,7 @@ type pos = | Msg - | TxIndex; + | TxIndex + | Fee; module Styles = { open Css; @@ -22,6 +23,7 @@ let make = (~coins, ~pos=Msg) => { {switch (pos) { | Msg => | TxIndex => + | Fee => React.null }} ; }; diff --git a/scan/src/components/TxIndexPageTable.re b/scan/src/components/TxIndexPageTable.re index 720a0b0ef3..385ada59b3 100644 --- a/scan/src/components/TxIndexPageTable.re +++ b/scan/src/components/TxIndexPageTable.re @@ -953,7 +953,7 @@ let renderUnknownMessage = () => { ; }; -let renderBody = (msg: TxSub.Msg.t) => { +let renderBody = (msg: TxSub.Msg.t) => switch (msg) { | SendMsg(send) => renderSend(send) | CreateDataSourceMsg(dataSource) => renderCreateDataSource(dataSource) @@ -997,7 +997,6 @@ let renderBody = (msg: TxSub.Msg.t) => { | FailMsg(_) => renderFailMessage() | UnknownMsg => renderUnknownMessage() }; -}; module THead = { [@react.component] @@ -1052,6 +1051,7 @@ let make = (~messages: list(TxSub.Msg.t)) => { {messages ->Belt.List.mapWithIndex((index, msg) => { let theme = msg |> TxSub.Msg.getBadgeTheme; + //TODO: Change index to be uniqe something string_of_int}> diff --git a/scan/src/components/TxMobileIndexPageTable.re b/scan/src/components/TxMobileIndexPageTable.re new file mode 100644 index 0000000000..66f8db8d52 --- /dev/null +++ b/scan/src/components/TxMobileIndexPageTable.re @@ -0,0 +1,160 @@ +let addressWidth = 160; +let renderMuitisendList = (tx: TxSub.Msg.MultiSend.t) => + InfoMobileCard.[("INPUTS", Nothing)] + ->Belt.List.concat( + { + let%IterList {address, coins} = tx.inputs; + [ + ("FROM", InfoMobileCard.Address(address, addressWidth, false)), + ("AMOUNT", Coin({value: coins, hasDenom: false})), + ]; + }, + ) + ->Belt.List.concat([("OUTPUT", Nothing)]) + ->Belt.List.concat( + { + let%IterList {address, coins} = tx.outputs; + [ + ("TO", InfoMobileCard.Address(address, addressWidth, false)), + ("AMOUNT", Coin({value: coins, hasDenom: false})), + ]; + }, + ); + +let renderDetailMobile = + //TODO: implement Guan Yu's message later + fun + | TxSub.Msg.SendMsg({fromAddress, toAddress, amount}) => + InfoMobileCard.[ + ("FROM", Address(fromAddress, addressWidth, false)), + ("TO", Address(toAddress, addressWidth, false)), + ("AMOUNT", Coin({value: amount, hasDenom: true})), + ] + | DelegateMsg({validatorAddress, delegatorAddress, amount}) => [ + ("DELEGATOR ADDRESS", Address(delegatorAddress, addressWidth, false)), + ("VALIDATOR ADDRESS", Address(validatorAddress, addressWidth, true)), + ("AMOUNT", Coin({value: [amount], hasDenom: true})), + ] + | UndelegateMsg({validatorAddress, delegatorAddress, amount}) => [ + ("DELEGATOR ADDRESS", Address(delegatorAddress, addressWidth, false)), + ("VALIDATOR ADDRESS", Address(validatorAddress, addressWidth, true)), + ("AMOUNT", Coin({value: [amount], hasDenom: true})), + ] + | MultiSendMsg(tx) => renderMuitisendList(tx) + | WithdrawRewardMsg({validatorAddress, delegatorAddress, amount}) => [ + ("DELEGATOR ADDRESS", Address(delegatorAddress, addressWidth, false)), + ("VALIDATOR ADDRESS", Address(validatorAddress, addressWidth, true)), + ("AMOUNT", Coin({value: amount, hasDenom: true})), + ] + | RedelegateMsg({validatorSourceAddress, validatorDestinationAddress, delegatorAddress, amount}) => [ + ("DELEGATOR ADDRESS", Address(delegatorAddress, addressWidth, false)), + ("SOURCE ADDRESS", Address(validatorSourceAddress, addressWidth, true)), + ("DESTINATION ADDRESS", Address(validatorDestinationAddress, addressWidth, true)), + ("AMOUNT", Coin({value: [amount], hasDenom: true})), + ] + | SetWithdrawAddressMsg({delegatorAddress, withdrawAddress}) => [ + ("DELEGATOR ADDRESS", Address(delegatorAddress, addressWidth, false)), + ("WITHDRAW ADDRESS", Address(withdrawAddress, addressWidth, false)), + ] + | CreateValidatorMsg({ + moniker, + identity, + website, + details, + commissionRate, + commissionMaxRate, + commissionMaxChange, + delegatorAddress, + validatorAddress, + publicKey, + minSelfDelegation, + selfDelegation, + }) => [ + ("MONIKER", Text(moniker)), + ("IDENTITY", Text(identity)), + ("WEBSITE", Text(website)), + ("DETAIL", Text(details)), + ("COMMISSION RATE", Percentage(commissionRate, Some(4))), + ("COMMISSION MAX RATE", Percentage(commissionMaxRate, Some(4))), + ("COMMISSION MAX CHANGE", Percentage(commissionMaxChange, Some(4))), + ("DELEGATOR ADDRESS", Address(delegatorAddress, addressWidth, false)), + ("VALIDATOR ADDRESS", Address(validatorAddress, addressWidth, true)), + ("PUBLIC KEY", PubKey(publicKey)), + ("MIN SELF DELEGATION", Coin({value: [minSelfDelegation], hasDenom: true})), + ("SELF DELEGATION", Coin({value: [selfDelegation], hasDenom: true})), + ] + | EditValidatorMsg({ + moniker, + identity, + website, + details, + commissionRate, + sender, + minSelfDelegation, + }) => [ + ("MONIKER", moniker == Config.doNotModify ? Text("Unchanged") : Text(moniker)), + ("IDENTITY", identity == Config.doNotModify ? Text("Unchanged") : Text(identity)), + ("WEBSITE", website == Config.doNotModify ? Text("Unchanged") : Text(website)), + ("DETAIL", details == Config.doNotModify ? Text("Unchanged") : Text(details)), + ( + "COMMISSION RATE", + switch (commissionRate) { + | Some(rate) => Percentage(rate, Some(4)) + | None => Text("Unchanged") + }, + ), + ("VALIDATOR ADDRESS", Address(sender, addressWidth, true)), + ( + "MIN SELF DELEGATION", + switch (minSelfDelegation) { + | Some(amount) => Coin({value: [amount], hasDenom: true}) + | None => Text("Unchanged") + }, + ), + ] + | WithdrawCommissionMsg({validatorAddress, amount}) => [ + ("VALIDATOR ADDRESS", Address(validatorAddress, addressWidth, true)), + ("AMOUNT", Coin({value: amount, hasDenom: true})), + ] + | UnjailMsg({address}) => [("VALIDATOR ADDRESS", Address(address, addressWidth, true))] + | _ => []; + +[@react.component] +let make = (~messages: list(TxSub.Msg.t)) => { + <> + //TODO: Change index to be uniqe something + {messages + ->Belt.List.mapWithIndex((index, msg) => { + let renderList = msg |> renderDetailMobile; + let theme = msg |> TxSub.Msg.getBadgeTheme; + let creator = msg |> TxSub.Msg.getCreator; + Belt.List.concat(renderList) + } + key={index |> string_of_int} + idx={index |> string_of_int} + />; + }) + ->Array.of_list + ->React.array} + ; +}; + +module Loading = { + [@react.component] + let make = () => { + ; + }; +}; diff --git a/scan/src/components/TxsTable.re b/scan/src/components/TxsTable.re index c6441c9e15..50a66e4607 100644 --- a/scan/src/components/TxsTable.re +++ b/scan/src/components/TxsTable.re @@ -105,7 +105,7 @@ let renderBodyMobile = (reserveIndex, txSub: ApolloHooks.Subscription.variant(Tx values=InfoMobileCard.[ ("TX HASH", TxHash(txHash, 200)), ("BLOCK", Height(blockHeight)), - ("GAS FEE\n(BAND)", Coin(gasFee)), + ("GAS FEE\n(BAND)", Coin({value: gasFee, hasDenom: false})), ("ACTIONS", Messages(txHash, messages, success, errMsg)), ] key={blockHeight |> ID.Block.toString} diff --git a/scan/src/components/validator/DelegatorsTable.re b/scan/src/components/validator/DelegatorsTable.re index 1b669a7002..67ba84acd9 100644 --- a/scan/src/components/validator/DelegatorsTable.re +++ b/scan/src/components/validator/DelegatorsTable.re @@ -109,9 +109,9 @@ let renderBodyMobile = | Data({amount, sharePercentage, delegatorAddress}) => Coin.getBandAmountFromCoin)), + ("DELEGATOR", Address(delegatorAddress, 149, false)), + ("SHARES (%)", Float(sharePercentage, Some(4))), + ("AMOUNT\n(BAND)", Coin({value: [amount], hasDenom: false})), ] key={delegatorAddress |> Address.toBech32} idx={delegatorAddress |> Address.toBech32} diff --git a/scan/src/pages/TxIndexPage.re b/scan/src/pages/TxIndexPage.re index 0ed8c23a48..75862276f1 100644 --- a/scan/src/pages/TxIndexPage.re +++ b/scan/src/pages/TxIndexPage.re @@ -22,6 +22,7 @@ module Styles = { alignItems(`center), marginTop(`px(25)), marginBottom(`px(44)), + Media.mobile([marginBottom(`px(25))]), ]); let correctLogo = style([width(`px(20)), marginLeft(`px(10))]); @@ -43,6 +44,25 @@ module Styles = { boxShadow(Shadow.box(~x=`zero, ~y=`px(2), ~blur=`px(4), rgba(0, 0, 0, 0.1))), ]); let notfoundLogo = style([width(`px(180)), marginRight(`px(10))]); + let infoContainerFullwidth = + style([ + Media.mobile([ + selector("> div", [flexBasis(`percent(100.))]), + selector("> div + div", [marginTop(`px(15))]), + selector("> div > div > div", [display(`block)]), + ]), + ]); + let infoContainerHalfwidth = + style([ + Media.mobile([ + selector( + "> div", + [flexGrow(0.), flexShrink(0.), flexBasis(`calc((`sub, `percent(50.), `px(20))))], + ), + selector("> div + div + div", [marginTop(`px(15))]), + selector("> div *", [alignItems(`flexStart)]), + ]), + ]); }; module TxNotFound = { @@ -71,8 +91,8 @@ module TxNotFound = { [@react.component] let make = (~txHash) => { + let isMobile = Media.isMobile(); let txSub = TxSub.get(txHash); - switch (txSub) { | Loading | Data(_) => @@ -115,21 +135,33 @@ let make = (~txHash) => { {switch (txSub) { | Data(_) => <> - Hash.toHex(~upper=true)} - size=Text.Xxl - weight=Text.Bold - nowrap=true - code=true - color=Colors.gray7 - /> - - Hash.toHex(~upper=true)} /> + {isMobile + ? Hash.toHex(~upper=true)} + size=Text.Lg + weight=Text.Bold + nowrap=false + breakAll=true + code=true + color=Colors.gray7 + /> + : <> + Hash.toHex(~upper=true)} + size=Text.Xxl + weight=Text.Bold + nowrap=true + code=true + color=Colors.gray7 + /> + + Hash.toHex(~upper=true)} /> + } | _ => }} - + {switch (txSub) { | Data({blockHeight}) => @@ -140,7 +172,17 @@ let make = (~txHash) => { {switch (txSub) { | Data({timestamp}) => - | _ => + | _ => + }} @@ -151,7 +193,7 @@ let make = (~txHash) => { - + {switch (txSub) { | Data({gasUsed}) => @@ -207,7 +249,7 @@ let make = (~txHash) => { - + {isMobile ? : } | _ => <> @@ -217,7 +259,7 @@ let make = (~txHash) => { - + {isMobile ? : } }} diff --git a/scan/src/reusable/InfoMobileCard.re b/scan/src/reusable/InfoMobileCard.re index 99a2ff6c45..33c2699d6a 100644 --- a/scan/src/reusable/InfoMobileCard.re +++ b/scan/src/reusable/InfoMobileCard.re @@ -1,14 +1,24 @@ +type coin_amount_t = { + value: list(Coin.t), + hasDenom: bool, +}; + type t = - | Address(Address.t, int) + | Address(Address.t, int, bool) | Height(ID.Block.t) - | Coin(list(Coin.t)) + | Coin(coin_amount_t) | Count(int) - | Float(float) + | Float(float, option(int)) + | Percentage(float, option(int)) | Timestamp(MomentRe.Moment.t) | TxHash(Hash.t, int) | Validator(Address.t, string, string) | Messages(Hash.t, list(TxSub.Msg.t), bool, string) - | Loading(int); + | PubKey(PubKey.t) + | Badge(TxSub.Msg.badge_theme_t) + | Loading(int) + | Text(string) + | Nothing; module Styles = { open Css; @@ -16,14 +26,21 @@ module Styles = { let addressContainer = w => { style([width(`px(w))]); }; + let badge = color => + style([ + display(`inlineFlex), + padding2(~v=`px(5), ~h=`px(10)), + backgroundColor(color), + borderRadius(`px(15)), + ]); }; [@react.component] let make = (~info) => { switch (info) { - | Address(address, width) => + | Address(address, width, isValidator) =>
- +
| Height(height) =>
@@ -35,15 +52,31 @@ let make = (~info) => { spacing={Text.Em(0.02)} code=true /> - | Float(value) => - Format.fPretty} size=Text.Md spacing={Text.Em(0.02)} code=true /> - | Coin(value) => + | Float(value, digits) => + Format.fPretty(~digits?)} + size=Text.Md + spacing={Text.Em(0.02)} + code=true + /> + | Percentage(value, digits) => Format.fPercent(~digits?)} + size=Text.Md + spacing={Text.Em(0.02)} + code=true + /> + | Coin({value, hasDenom}) => + + | Text(text) => + Coin.getBandAmountFromCoins->Format.fPretty} - weight=Text.Medium + nowrap=true + ellipsis=true /> | Timestamp(time) => | Validator(address, moniker, identity) => @@ -54,9 +87,15 @@ let make = (~info) => { identity width={`percent(100.)} /> + | PubKey(publicKey) => | TxHash(txHash, width) => | Messages(txHash, messages, success, errMsg) => + | Badge({text, textColor, bgColor}) => +
+ +
| Loading(width) => + | Nothing => React.null }; }; diff --git a/scan/src/reusable/MobileCard.re b/scan/src/reusable/MobileCard.re index aa22caaf80..4feea3c91b 100644 --- a/scan/src/reusable/MobileCard.re +++ b/scan/src/reusable/MobileCard.re @@ -19,6 +19,7 @@ module Styles = { flexBasis(`percent(25.)), ]); let logo = style([width(`px(20)), position(`absolute), top(`px(5)), right(`px(12))]); + let cardItemHeadingLg = style([padding2(~v=`px(10), ~h=`zero)]); }; [@react.component] @@ -35,20 +36,35 @@ let make = (~values, ~idx, ~status=?) => { | InfoMobileCard.Messages(_) => `baseline | _ => `center }; +
string_of_int)}>
{heading ->Js.String2.split("\n") - ->Belt.Array.map(each => - - ) + ->Belt.Array.map(each => { + switch (value) { + | InfoMobileCard.Nothing => +
+ +
+ | _ => + + } + }) ->React.array}