elements.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import React, { useEffect, useState } from 'react'
  2. import moment from 'moment'
  3. import { Header, Card, Icon, Image, Label, Statistic } from 'semantic-ui-react';
  4. import { Link } from 'react-router-dom';
  5. import { Balance } from '@polkadot/types/interfaces';
  6. import { formatBalance } from '@polkadot/util';
  7. import Identicon from '@polkadot/react-identicon';
  8. import { Actor } from '@joystream/types/lib/roles';
  9. import { IProfile, MemberId } from '@joystream/types/lib/members';
  10. import { Text, GenericAccountId } from '@polkadot/types';
  11. import { LeadRoleState } from '@joystream/types/lib/content-working-group';
  12. type ActorProps = {
  13. actor: Actor
  14. }
  15. type BalanceProps = {
  16. balance?: Balance
  17. }
  18. export function BalanceView(props: BalanceProps) {
  19. return (
  20. <div className="balance">
  21. <span>Balance:</span> {formatBalance(props.balance)}
  22. </div>
  23. )
  24. }
  25. type MemoProps = ActorProps & {
  26. memo?: Text
  27. }
  28. export function MemoView(props: MemoProps) {
  29. if (typeof props.memo === "undefined") {
  30. return null
  31. }
  32. return (
  33. <div className="memo">
  34. <span>Memo:</span> {props.memo.toString()}
  35. <Link to={`/addressbook/memo/${props.actor.account.toString()}`}>{' view full memo'}</Link>
  36. </div>
  37. )
  38. }
  39. type ProfileProps = {
  40. profile: IProfile
  41. }
  42. export function HandleView(props: ProfileProps) {
  43. if (typeof props.profile === "undefined") {
  44. return null
  45. }
  46. return (
  47. <Link to={`/members/${props.profile.handle.toString()}`}>{props.profile.handle.toString()}</Link>
  48. )
  49. }
  50. type MemberProps = ActorProps & BalanceProps & ProfileProps
  51. export function MemberView(props: MemberProps) {
  52. let avatar = <Identicon value={props.actor.account.toString()} size={50} />
  53. if (typeof props.profile.avatar_uri !== "undefined" && props.profile.avatar_uri.toString() != "") {
  54. avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
  55. }
  56. return (
  57. <Header as='h4' image>
  58. {avatar}
  59. <Header.Content>
  60. <HandleView profile={props.profile} />
  61. <BalanceView balance={props.balance} />
  62. </Header.Content>
  63. </Header>
  64. )
  65. }
  66. type ActorDetailsProps = MemoProps & BalanceProps
  67. export function ActorDetailsView(props: ActorDetailsProps) {
  68. return (
  69. <div className="actor-summary" id={props.actor.account.toString()}>
  70. {props.actor.account.toString()}
  71. <MemoView actor={props.actor} memo={props.memo} />
  72. </div>
  73. )
  74. }
  75. export type GroupMember = {
  76. memberId: MemberId
  77. roleAccount: GenericAccountId
  78. profile: IProfile
  79. title: string
  80. stake?: Balance
  81. earned?: Balance
  82. }
  83. export type GroupLead = {
  84. memberId: MemberId
  85. roleAccount: GenericAccountId
  86. profile: IProfile
  87. title: string
  88. stage: LeadRoleState
  89. }
  90. type inset = {
  91. inset?: boolean
  92. }
  93. export function GroupLeadView(props: GroupLead & inset) {
  94. let fluid = false
  95. if (typeof props.inset !== "undefined") {
  96. fluid = props.inset
  97. }
  98. let avatar = <Identicon value={props.roleAccount.toString()} size={50} />
  99. if (typeof props.profile.avatar_uri !== "undefined" && props.profile.avatar_uri.toString() != "") {
  100. avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
  101. }
  102. return (
  103. <Card color='grey' className="staked-card" fluid={fluid}>
  104. <Card.Content>
  105. <Image floated='right'>
  106. {avatar}
  107. </Image>
  108. <Card.Header><HandleView profile={props.profile} /></Card.Header>
  109. <Card.Meta>{props.title}</Card.Meta>
  110. <Card.Description>
  111. <Label color='teal' ribbon={fluid}>
  112. <Icon name="shield" />
  113. Content Lead
  114. <Label.Detail>{/* ... */}</Label.Detail>
  115. </Label>
  116. </Card.Description>
  117. </Card.Content>
  118. {/* <Card.Content extra>
  119. <Label>Something about <Label.Detail> the lead </Label.Detail></Label>
  120. </Card.Content> */}
  121. </Card>
  122. )
  123. }
  124. export function GroupMemberView(props: GroupMember & inset) {
  125. let fluid = false
  126. if (typeof props.inset !== "undefined") {
  127. fluid = props.inset
  128. }
  129. let stake = null
  130. if (typeof props.stake !== "undefined" && props.stake.toNumber() !== 0) {
  131. stake = (
  132. <Label color='green' ribbon={fluid}>
  133. <Icon name="shield" />
  134. Staked
  135. <Label.Detail>{formatBalance(props.stake)}</Label.Detail>
  136. </Label>
  137. )
  138. }
  139. let avatar = <Identicon value={props.roleAccount.toString()} size={50} />
  140. if (typeof props.profile.avatar_uri !== "undefined" && props.profile.avatar_uri.toString() != "") {
  141. avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
  142. }
  143. let earned = null
  144. if (typeof props.earned !== "undefined" &&
  145. props.earned.toNumber() > 0 &&
  146. !fluid) {
  147. earned = (
  148. <Card.Content extra>
  149. <Label>Earned <Label.Detail>{formatBalance(props.earned)}</Label.Detail></Label>
  150. </Card.Content>
  151. )
  152. }
  153. return (
  154. <Card color='grey' className="staked-card" fluid={fluid}>
  155. <Card.Content>
  156. <Image floated='right'>
  157. {avatar}
  158. </Image>
  159. <Card.Header><HandleView profile={props.profile} /></Card.Header>
  160. <Card.Meta>{props.title}</Card.Meta>
  161. <Card.Description>
  162. {stake}
  163. </Card.Description>
  164. </Card.Content>
  165. {earned}
  166. </Card>
  167. )
  168. }
  169. type CountdownProps = {
  170. end: Date
  171. }
  172. export function Countdown(props: CountdownProps) {
  173. let interval: number = -1
  174. const [days, setDays] = useState<number | undefined>(undefined)
  175. const [hours, setHours] = useState<number | undefined>(undefined)
  176. const [minutes, setMinutes] = useState<number | undefined>(undefined)
  177. const [seconds, setSeconds] = useState<number | undefined>(undefined)
  178. const update = () => {
  179. const then = moment(props.end)
  180. const now = moment()
  181. const d = moment.duration(then.diff(now))
  182. setDays(d.days())
  183. setHours(d.hours())
  184. setMinutes(d.minutes())
  185. setSeconds(d.seconds())
  186. }
  187. interval = window.setInterval(update, 1000);
  188. useEffect(() => {
  189. update()
  190. return () => {
  191. clearInterval(interval);
  192. };
  193. }, []);
  194. if (!seconds) {
  195. return null;
  196. }
  197. return (
  198. <div className='countdown wrapper'>
  199. <Statistic size="tiny">
  200. <Statistic.Value>{days}</Statistic.Value>
  201. <Statistic.Label>Days</Statistic.Label>
  202. </Statistic>
  203. <Statistic size="tiny">
  204. <Statistic.Value>{hours}</Statistic.Value>
  205. <Statistic.Label>hours</Statistic.Label>
  206. </Statistic>
  207. <Statistic size="tiny">
  208. <Statistic.Value>{minutes}</Statistic.Value>
  209. <Statistic.Label>minutes</Statistic.Label>
  210. </Statistic>
  211. <Statistic size="tiny">
  212. <Statistic.Value>{seconds}</Statistic.Value>
  213. <Statistic.Label>seconds</Statistic.Label>
  214. </Statistic>
  215. </div>
  216. )
  217. }