diff --git a/CHANGELOG_UNRELEASED.md b/CHANGELOG_UNRELEASED.md index 91ecd07628..0cd5063e58 100644 --- a/CHANGELOG_UNRELEASED.md +++ b/CHANGELOG_UNRELEASED.md @@ -29,6 +29,7 @@ ### Scan - (impv) [\#2330](https://github.com/bandprotocol/bandchain/pull/2330) Fixed share_percentage decoder in DelegationSub +- (impv) [\#2317](https://github.com/bandprotocol/bandchain/pull/2317) Implemented account Index Page (Mobile) - (impv) [\#2315](https://github.com/bandprotocol/bandchain/pull/2315) Improved how to pass account type on the AddressRender component - (impv) [\#2312](https://github.com/bandprotocol/bandchain/pull/2312) Implemented the BlockIndexPage layout for mobile view - (impv) [\#2316](https://github.com/bandprotocol/bandchain/pull/2316) Implemented the ValidatorHomePage layout for mobile view diff --git a/scan/graphql_schema.json b/scan/graphql_schema.json index eb728adeae..5f40f3f6be 100644 --- a/scan/graphql_schema.json +++ b/scan/graphql_schema.json @@ -34220,7 +34220,7 @@ "args": [], "isDeprecated": false, "deprecationReason": null, - "name": "validator", + "name": "validatorByValidatorDstId", "type": { "kind": "NON_NULL", "name": null, @@ -34846,7 +34846,7 @@ "description": null }, { - "name": "validator", + "name": "validatorByValidatorDstId", "defaultValue": null, "type": { "kind": "INPUT_OBJECT", @@ -34978,7 +34978,7 @@ "description": null }, { - "name": "validator", + "name": "validatorByValidatorDstId", "defaultValue": null, "type": { "kind": "INPUT_OBJECT", @@ -35365,7 +35365,7 @@ "description": null }, { - "name": "validator", + "name": "validatorByValidatorDstId", "defaultValue": null, "type": { "kind": "INPUT_OBJECT", diff --git a/scan/src/components/account/AccountIndexDelegations.re b/scan/src/components/account/AccountIndexDelegations.re index e4afa2e06f..4845d94685 100644 --- a/scan/src/components/account/AccountIndexDelegations.re +++ b/scan/src/components/account/AccountIndexDelegations.re @@ -8,9 +8,58 @@ module Styles = { let alignRight = style([display(`flex), justifyContent(`flexEnd)]); }; +let renderBody = (delegation: DelegationSub.stake_t) => { + Address.toBech32} minHeight=50> + + + +
+ +
+ + +
+ Coin.getBandAmountFromCoin |> Format.fPretty} + code=true + /> +
+ + +
+ Coin.getBandAmountFromCoin |> Format.fPretty} + code=true + /> +
+ + +
+ ; +}; + +let renderBodyMobile = + ({operatorAddress, moniker, identity, amount, reward}: DelegationSub.stake_t) => { + Address.toHex} + idx={operatorAddress |> Address.toHex} + />; +}; + [@react.component] let make = (~address) => { + let isMobile = Media.isMobile(); let (page, setPage) = React.useState(_ => 1); let pageSize = 10; let delegationsCountSub = DelegationSub.getStakeCountByDelegator(address); @@ -31,80 +80,51 @@ let make = (~address) => <> - - - - - - - -
- -
- - -
- -
- - -
- - {delegations - ->Belt.Array.map(delegation => { - Address.toBech32} minHeight=50> + {isMobile + ? React.null + : -
- -
+
Coin.getBandAmountFromCoin |> Format.fPretty} - code=true + block=true + value="AMOUNT (BAND)" + size=Text.Sm + weight=Text.Bold + spacing={Text.Em(0.05)} + color=Colors.gray6 />
Coin.getBandAmountFromCoin |> Format.fPretty} - code=true + block=true + value="REWARD (BAND)" + size=Text.Sm + spacing={Text.Em(0.05)} + weight=Text.Bold + color=Colors.gray6 />
- + } + {delegations + ->Belt.Array.map(delegation => { + isMobile ? renderBodyMobile(delegation) : renderBody(delegation) }) ->React.array} diff --git a/scan/src/components/account/AccountIndexRedelegate.re b/scan/src/components/account/AccountIndexRedelegate.re index 69e7476294..787600573b 100644 --- a/scan/src/components/account/AccountIndexRedelegate.re +++ b/scan/src/components/account/AccountIndexRedelegate.re @@ -9,9 +9,88 @@ module Styles = { let alignLeft = style([display(`flex), justifyContent(`flexStart)]); }; +let renderBody = + ({srcValidator, dstValidator, completionTime, amount}: RedelegateSub.redelegate_list_t) => { + Address.toBech32) + ++ (dstValidator.operatorAddress |> Address.toBech32) + ++ (completionTime |> MomentRe.Moment.toISOString) + ++ (amount |> Coin.getBandAmountFromCoin |> Js.Float.toString) + } + minHeight=50> + + + + + + +
+ +
+ + +
+ Coin.getBandAmountFromCoin |> Format.fPretty} code=true /> +
+ + +
+ MomentRe.Moment.format(Config.timestampDisplayFormat) + |> String.uppercase_ascii + } + code=true + /> +
+ + +
+ ; +}; + +let renderBodyMobile = + ({amount, completionTime, dstValidator, srcValidator}: RedelegateSub.redelegate_list_t) => { + let key_ = + (srcValidator.operatorAddress |> Address.toBech32) + ++ (dstValidator.operatorAddress |> Address.toBech32) + ++ (completionTime |> MomentRe.Moment.toISOString) + ++ (amount |> Coin.getBandAmountFromCoin |> Js.Float.toString); + + ; +}; + [@react.component] let make = (~address) => { + let isMobile = Media.isMobile(); let currentTime = React.useContext(TimeContext.context) |> MomentRe.Moment.format(Config.timestampUseFormat); @@ -36,108 +115,64 @@ let make = (~address) => <> - - - - - - - -
- -
- - -
- -
- - -
- -
- - -
- - {redelegateList - ->Belt.Array.map(redelegateEntry => { - Address.toBech32} minHeight=50> + {isMobile + ? React.null + : -
-
Coin.getBandAmountFromCoin |> Format.fPretty - } - code=true + block=true + value="AMOUNT (BAND)" + size=Text.Sm + spacing={Text.Em(0.05)} + weight=Text.Bold + color=Colors.gray6 />
MomentRe.Moment.format(Config.timestampDisplayFormat) - |> String.uppercase_ascii - } - code=true + block=true + value="REDELEGATE COMPLETE AT" + size=Text.Sm + spacing={Text.Em(0.05)} + weight=Text.Bold + color=Colors.gray6 />
- - }) + } + {redelegateList + ->Belt.Array.map(redelegateEntry => + isMobile ? renderBodyMobile(redelegateEntry) : renderBody(redelegateEntry) + ) ->React.array} setPage(_ => newPage)} /> diff --git a/scan/src/components/account/AccountIndexUnbonding.re b/scan/src/components/account/AccountIndexUnbonding.re index 8106b00c79..8590b3679b 100644 --- a/scan/src/components/account/AccountIndexUnbonding.re +++ b/scan/src/components/account/AccountIndexUnbonding.re @@ -8,9 +8,75 @@ module Styles = { let alignRight = style([display(`flex), justifyContent(`flexEnd)]); }; +let renderBody = (unbondingEntry: UnbondingSub.unbonding_list_t) => { + Address.toBech32) + ++ (unbondingEntry.completionTime |> MomentRe.Moment.toISOString) + ++ (unbondingEntry.amount |> Coin.getBandAmountFromCoin |> Js.Float.toString) + } + minHeight=50> + + + +
+ +
+ + +
+ Coin.getBandAmountFromCoin |> Format.fPretty} + code=true + /> +
+ + +
+ MomentRe.Moment.format(Config.timestampDisplayFormat) + |> String.uppercase_ascii + } + code=true + /> +
+ + +
+ ; +}; + +let renderBodyMobile = + ( + {validator: {operatorAddress, moniker, identity}, amount, completionTime}: UnbondingSub.unbonding_list_t, + ) => { + let key_ = + (operatorAddress |> Address.toBech32) + ++ (completionTime |> MomentRe.Moment.toISOString) + ++ (amount |> Coin.getBandAmountFromCoin |> Js.Float.toString); + + ; +}; + [@react.component] let make = (~address) => { + let isMobile = Media.isMobile(); let currentTime = React.useContext(TimeContext.context) |> MomentRe.Moment.format(Config.timestampUseFormat); @@ -36,88 +102,52 @@ let make = (~address) => <> - - - - - - - -
- -
- - -
- -
- - -
- - {unbondingList - ->Belt.Array.map(unbondingEntry => { - Address.toBech32} minHeight=50> + {isMobile + ? React.null + : -
- -
+
Coin.getBandAmountFromCoin |> Format.fPretty - } - code=true + block=true + value="AMOUNT (BAND)" + size=Text.Sm + weight=Text.Bold + spacing={Text.Em(0.05)} + color=Colors.gray6 />
MomentRe.Moment.format(Config.timestampDisplayFormat) - |> String.uppercase_ascii - } - code=true + block=true + value="UNBONDED AT" + size=Text.Sm + spacing={Text.Em(0.05)} + weight=Text.Bold + color=Colors.gray6 />
- - }) + } + {unbondingList + ->Belt.Array.map(unbondingEntry => + isMobile ? renderBodyMobile(unbondingEntry) : renderBody(unbondingEntry) + ) ->React.array} setPage(_ => newPage)} /> diff --git a/scan/src/components/modal/ValidatorStakingInfo.re b/scan/src/components/modal/ValidatorStakingInfo.re index 166d8a24cd..0e7d2d4a59 100644 --- a/scan/src/components/modal/ValidatorStakingInfo.re +++ b/scan/src/components/modal/ValidatorStakingInfo.re @@ -150,7 +150,11 @@ module StakingInfo = { let balanceAtStakeSub = DelegationSub.getStakeByValiator(delegatorAddress, validatorAddress); let unbondingSub = - UnbondingSub.getUnbondingBalanceByValidator(delegatorAddress, validatorAddress); + UnbondingSub.getUnbondingBalanceByValidator( + delegatorAddress, + validatorAddress, + currentTime, + ); let unbondingListSub = UnbondingSub.getUnbondingList(delegatorAddress, validatorAddress, currentTime); diff --git a/scan/src/components/msgs/Msg.re b/scan/src/components/msgs/Msg.re index 3cd0426571..54e410ccb7 100644 --- a/scan/src/components/msgs/Msg.re +++ b/scan/src/components/msgs/Msg.re @@ -82,7 +82,7 @@ let make = (~msg: TxSub.Msg.t, ~width: int) => { -
+
| CreateDataSourceMsg({id, sender, name}) =>
diff --git a/scan/src/pages/AccountIndexPage.re b/scan/src/pages/AccountIndexPage.re index af915ac415..ee2cf61981 100644 --- a/scan/src/pages/AccountIndexPage.re +++ b/scan/src/pages/AccountIndexPage.re @@ -7,10 +7,13 @@ module Styles = { let logo = style([width(`px(50)), marginRight(`px(10))]); - let cFlex = style([display(`flex), flexDirection(`column), alignItems(`flexEnd)]); + let cFlex = alignItems_ => + style([display(`flex), flexDirection(`column), alignItems(alignItems_)]); let rFlex = style([display(`flex), flexDirection(`row)]); + let innerCenter = style([Media.mobile([display(`flex), justifyContent(`center)])]); + let separatorLine = style([ width(`px(1)), @@ -18,6 +21,7 @@ module Styles = { backgroundColor(Colors.gray7), marginLeft(`px(20)), opacity(0.3), + Media.mobile([marginLeft(`zero), width(`percent(100.)), height(`px(1))]), ]); let ovalIcon = color => @@ -38,9 +42,29 @@ module Styles = { alignItems(`flexEnd), height(`px(200)), padding2(~v=`px(12), ~h=`zero), + Media.mobile([height(`px(100))]), ]); - let totalBalance = style([display(`flex), flexDirection(`column), alignItems(`flexEnd)]); + let infoContainerFullwidth = + style([ + Media.mobile([ + selector("> div", [flexBasis(`percent(100.))]), + selector("> div + div", [marginTop(`px(15))]), + ]), + ]); + + let totalBalance = + style([ + display(`flex), + flexDirection(`column), + alignItems(`flexEnd), + Media.mobile([ + flexDirection(`row), + justifyContent(`spaceBetween), + alignItems(`center), + width(`percent(100.)), + ]), + ]); let button = style([ @@ -69,7 +93,7 @@ let balanceDetail = (~title, ~description, ~amount, ~usdPrice, ~color, ~isCountu /> -
+
{isCountup ? ; }; -let totalBalanceRender = (title, amount, symbol) => { +let totalBalanceRender = (isMobile, rawTitle, amount, symbol) => { + let titles = isMobile ? rawTitle->Js.String2.split("\n") : [|rawTitle|]; +
- +
+ {titles + ->Belt_Array.mapWithIndex((i, title) => + string_of_int ++ title} + value=title + size={isMobile ? Text.Sm : Text.Md} + spacing={Text.Em(0.03)} + height={Text.Px(18)} + /> + ) + ->React.array} +
- + - +
; }; @@ -143,10 +192,13 @@ let totalBalanceRender = (title, amount, symbol) => { [@react.component] let make = (~address, ~hashtag: Route.account_tab_t) => { + let currentTime = + React.useContext(TimeContext.context) |> MomentRe.Moment.format(Config.timestampUseFormat); + let isMobile = Media.isMobile(); let accountSub = AccountSub.get(address); let trackingSub = TrackingSub.use(); let balanceAtStakeSub = DelegationSub.getTotalStakeByDelegator(address); - let unbondingSub = UnbondingSub.getUnbondingBalance(address); + let unbondingSub = UnbondingSub.getUnbondingBalance(address, currentTime); let infoSub = React.useContext(GlobalContext.context); let (_, dispatchModal) = React.useContext(ModalContext.context); let (accountOpt, _) = React.useContext(AccountContext.context); @@ -209,29 +261,35 @@ let make = (~address, ~hashtag: Route.account_tab_t) =>
- -
{send()}}> - -
+ {isMobile + ? React.null + : <> + +
{send()}}> + +
+ }
- - + + - +
+ +
@@ -294,9 +352,10 @@ let make = (~address, ~hashtag: Route.account_tab_t) =>
- {totalBalanceRender("TOTAL BAND BALANCE", totalBalance, "BAND")} + {totalBalanceRender(isMobile, "TOTAL BAND BALANCE", totalBalance, "BAND")} {totalBalanceRender( - "TOTAL BAND IN USD ($" ++ (usdPrice |> Format.fPretty) ++ " / BAND)", + isMobile, + "TOTAL BAND IN USD \n($" ++ (usdPrice |> Format.fPretty(~digits=2)) ++ " / BAND)", totalBalance *. usdPrice, "USD", )} diff --git a/scan/src/reusable/AddressRender.re b/scan/src/reusable/AddressRender.re index 58d472119d..e8d3de580c 100644 --- a/scan/src/reusable/AddressRender.re +++ b/scan/src/reusable/AddressRender.re @@ -16,7 +16,12 @@ module Styles = { let font = fun - | Title => style([fontSize(`px(18)), lineHeight(`px(24))]) + | Title => + style([ + fontSize(`px(18)), + lineHeight(`px(24)), + Media.mobile([fontSize(px(14)), lineHeight(`px(18))]), + ]) | Subtitle => style([ fontSize(`px(14)), @@ -38,6 +43,11 @@ module Styles = { active([color(Colors.gray7)]), ]); + let wordBreak = + fun + | Title => style([Media.mobile([wordBreak(`breakAll), whiteSpace(`unset)])]) + | _ => ""; + let copy = style([width(`px(15)), marginLeft(`px(10)), cursor(`pointer)]); }; @@ -59,7 +69,13 @@ let make = (~address, ~position=Text, ~accountType=`account, ~copy=false, ~click ? Route.ValidatorIndexPage(address, Route.ProposedBlocks) : Route.AccountIndexPage(address, Route.AccountTransactions) }> - + {prefix |> React.string} {noPrefixAddress |> React.string} diff --git a/scan/src/subscriptions/RedelegateSub.re b/scan/src/subscriptions/RedelegateSub.re index 1f771cd906..5b40f5f953 100644 --- a/scan/src/subscriptions/RedelegateSub.re +++ b/scan/src/subscriptions/RedelegateSub.re @@ -25,7 +25,7 @@ module RedelegationByDelegatorConfig = [%graphql moniker identity } - dstValidator: validator @bsRecord{ + dstValidator: validatorByValidatorDstId @bsRecord{ operatorAddress: operator_address @bsDecoder(fn: "Address.fromBech32") moniker identity diff --git a/scan/src/subscriptions/UnbondingSub.re b/scan/src/subscriptions/UnbondingSub.re index 7bbc629066..3e0552b176 100644 --- a/scan/src/subscriptions/UnbondingSub.re +++ b/scan/src/subscriptions/UnbondingSub.re @@ -19,9 +19,9 @@ type unbonding_list_t = { module SingleConfig = [%graphql {| - subscription Unbonding($delegator_address: String!) { + subscription Unbonding($delegator_address: String!, $current_time: timestamp) { accounts_by_pk(address: $delegator_address){ - unbonding_delegations_aggregate { + unbonding_delegations_aggregate(where: {completion_time: {_gte: $current_time}}) { aggregate { sum { amount @bsDecoder(fn: "GraphQLParser.coinWithDefault") @@ -48,9 +48,9 @@ module MultiConfig = [%graphql module UnbondingByValidatorConfig = [%graphql {| - subscription Unbonding($delegator_address: String!, $operator_address: String!) { + subscription Unbonding($delegator_address: String!, $operator_address: String!, $current_time: timestamp) { accounts_by_pk(address: $delegator_address) { - unbonding_delegations_aggregate(where: {validator: {operator_address: {_eq: $operator_address}}}) { + unbonding_delegations_aggregate(where: {validator: {operator_address: {_eq: $operator_address}}, completion_time: {_gte: $current_time}}) { aggregate { sum { amount @bsDecoder(fn: "GraphQLParser.coinWithDefault") @@ -94,12 +94,16 @@ module UnbondingCountByDelegatorConfig = [%graphql |} ]; -let getUnbondingBalance = delegatorAddress => { +let getUnbondingBalance = (delegatorAddress, currentTime) => { let (result, _) = ApolloHooks.useSubscription( SingleConfig.definition, ~variables= - SingleConfig.makeVariables(~delegator_address=delegatorAddress |> Address.toBech32, ()), + SingleConfig.makeVariables( + ~delegator_address=delegatorAddress |> Address.toBech32, + ~current_time=currentTime |> Js.Json.string, + (), + ), ); let unbondingInfoSub = @@ -119,7 +123,7 @@ let getUnbondingBalance = delegatorAddress => { unbondingInfo |> Sub.resolve; }; -let getUnbondingBalanceByValidator = (delegatorAddress, operatorAddress) => { +let getUnbondingBalanceByValidator = (delegatorAddress, operatorAddress, currentTime) => { let (result, _) = ApolloHooks.useSubscription( UnbondingByValidatorConfig.definition, @@ -127,6 +131,7 @@ let getUnbondingBalanceByValidator = (delegatorAddress, operatorAddress) => { UnbondingByValidatorConfig.makeVariables( ~delegator_address=delegatorAddress |> Address.toBech32, ~operator_address=operatorAddress |> Address.toOperatorBech32, + ~current_time=currentTime |> Js.Json.string, (), ), );