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

UX enhancement #11

Merged
merged 9 commits into from
Aug 30, 2022
6 changes: 4 additions & 2 deletions src/components/InsightView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ interface data {
export function InsightView() {
const vote = useVote()
const dateFormat = 'MMMM D, YYYY'
const initialDate = new Date()
initialDate.setMonth(initialDate.getMonth() - 1)
const [data, setData] = useState<data>({
date: new Date(),
date: initialDate,
type: DateFilterTypes.month,
votes: []
votes: vote.backlogVotes.filter((i) => moment(i.timestamp).isAfter(initialDate))
})
const [events, setEvents] = useState<Array<InsightTimelineItem>>([])
const [order, setOrder] = useState<'address' | 'backlogItem'>('address')
Expand Down
101 changes: 101 additions & 0 deletions src/components/ItemCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Box, Dialog, Flex, Truncate } from '@primer/components'
import { ChevronUpIcon } from '@primer/styled-octicons'
import moment from 'moment'
import React, { useState } from 'react'

import { PRIMARY_COLOR } from 'src/utils/constants'
import { AddressAvatars } from './AddressAvatars'
import { Link } from './elements/Link'
import { ItemVote } from './ItemVote'
import { BacklogItem } from '../types/BacklogItem'

interface Props {
item:BacklogItem
length: number
index: number
}

export function ItemCard(props: Props) {
const { item, length, index } = props
const [showDialog, setShowDialog] = useState(false)
const returnFocusRef = React.useRef(null)
const lastItem = length === index + 1
const addresses = [...new Set(item.votes.flatMap((i) => i.address))]

return (
<React.Fragment key={item.number}>
<Dialog
css=""
returnFocusRef={returnFocusRef}
isOpen={showDialog}
onDismiss={() => setShowDialog(false)}
aria-labelledby="header-id"
>
<Dialog.Header id="header-id">
<Truncate
title={item.title}
inline
expandable={false}
maxWidth="100%"
className="mr-4"
>
#{item.number} {item.title}
</Truncate>
</Dialog.Header>
<Box p={3}>
<ItemVote number={item.number} />
</Box>
</Dialog>
<Box className={lastItem ? '' : 'border-bottom'}>
<Flex alignItems="flex-start" className="m-2">
<Flex
ref={returnFocusRef}
width={80}
flexShrink={0}
flexDirection="column"
justifyContent="center"
alignItems="center"
className="border"
onClick={() => setShowDialog(true)}
>
<ChevronUpIcon
size={24}
aria-label={'Vote on item #' + item.number}
color={PRIMARY_COLOR}
/>
<span>{item.totalVoteValue}</span>
</Flex>
<Flex flexGrow={1} className="mx-4" flexDirection="column">
<Flex justifyContent="space-between">
<Link className="f4 text-bold" to={item.url}>
<Truncate
title={item.title}
inline
expandable={false}
maxWidth="100%"
>
{item.title}
</Truncate>
</Link>
</Flex>
<Truncate title={item.description} inline maxWidth="100%">
{item.description}
</Truncate>
<p className="pt-1 mb-0 text-small color-text-tertiary">
#{item.number} opened {moment(item.created).fromNow()} by{' '}
{item.author}
</p>
{item.totalVoteCount > 0 && (
<div className="mt-3">
<AddressAvatars
addresses={addresses}
totalVoteCount={item.totalVoteCount}
/>
</div>
)}
</Flex>
</Flex>
</Box>
</React.Fragment>
)
}
86 changes: 2 additions & 84 deletions src/components/ItemsView.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
import {
Box,
ButtonPrimary,
Dialog,
Flex,
TextInput,
Tooltip,
Truncate,
} from '@primer/components'
import {
SearchIcon,
ChevronUpIcon,
VerifiedIcon,
} from '@primer/styled-octicons'
import moment from 'moment'
import React, { ChangeEvent, useState } from 'react'
import { useBacklog } from 'src/hooks/useBacklog'
import { BacklogItem } from 'src/types'
import { PRIMARY_COLOR } from 'src/utils/constants'
import { AddressAvatars } from './AddressAvatars'
import { Link } from './elements/Link'
import { ItemVote } from './ItemVote'
import { NoItemsFound } from './NoItemsFound'
import { NoOpenItems } from './NoOpenItems'
import { ItemCard } from './ItemCard'

export function ItemsView() {
const backlog = useBacklog()
Expand Down Expand Up @@ -76,81 +68,7 @@ export function ItemsView() {
{backlog.items.length === 0 && <NoOpenItems />}
{items.length === 0 && <NoItemsFound criteria={searchValue} />}
{items.map((i: BacklogItem, index: number) => {
const [showDialog, setShowDialog] = useState(false)
const returnFocusRef = React.useRef(null)
const lastItem = items.length === index + 1
const addresses = [...new Set(i.votes.flatMap(i => i.address))]

return (
<React.Fragment key={i.number}>
<Dialog
css=''
returnFocusRef={returnFocusRef}
isOpen={showDialog}
onDismiss={() => setShowDialog(false)}
aria-labelledby="header-id">
<Dialog.Header id="header-id">
<Truncate
title={i.title}
inline
expandable={false}
maxWidth="100%"
className='mr-4'>
#{i.number} {i.title}
</Truncate>
</Dialog.Header>
<Box p={3}>
<ItemVote number={i.number} />
</Box>
</Dialog>
<Box className={lastItem ? '' : 'border-bottom'}>
<Flex alignItems="flex-start" className="m-2">
<Flex ref={returnFocusRef}
width={80}
flexShrink={0}
flexDirection="column"
justifyContent="center"
alignItems="center"
className="border"
onClick={() => setShowDialog(true)}
>
<ChevronUpIcon
size={24}
aria-label={'Vote on item #' + i.number}
color={PRIMARY_COLOR}
/>
<span>{i.totalVoteValue}</span>
</Flex>
<Flex flexGrow={1} className="mx-4" flexDirection="column">
<Flex justifyContent="space-between">
<Link className="f4 text-bold" to={i.url}>
<Truncate
title={i.title}
inline
expandable={false}
maxWidth="100%"
>
{i.title}
</Truncate>
</Link>
</Flex>
<Truncate title={i.description} inline maxWidth="100%">
{i.description}
</Truncate>
<p className="pt-1 mb-0 text-small color-text-tertiary">
#{i.number} opened {moment(i.created).fromNow()} by{' '}
{i.author}
</p>
{i.totalVoteCount > 0 && (
<div className='mt-3'>
<AddressAvatars addresses={addresses} totalVoteCount={i.totalVoteCount} />
</div>
)}
</Flex>
</Flex>
</Box>
</React.Fragment>
)
return <ItemCard index={index} length={items.length} item={i} />
})}
</div>
</div>
Expand Down
31 changes: 23 additions & 8 deletions src/components/QuadraticVote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function QuadraticVote(props: Props) {
}
}

function onChange(type: 'MIN' | 'DOWN' | 'UP' | 'MAX') {
function onChangeStep(type: 'MIN' | 'DOWN' | 'UP' | 'MAX') {
let amount: number
let qc: number

Expand Down Expand Up @@ -62,6 +62,19 @@ export function QuadraticVote(props: Props) {
setQuadraticCost(qc)
}

function onChangeInput(event) {
const targetValue = event.target.value
const amount =
targetValue < 1
? 1
: targetValue > maxVotes
? Math.floor(maxVotes)
: parseInt(targetValue)
const qc = getQuadraticCost(amount)
setVoteAmount(amount)
setQuadraticCost(qc)
}

function disableLower() {
return voteAmount <= step
}
Expand All @@ -78,10 +91,11 @@ export function QuadraticVote(props: Props) {
return Number(Math.pow(value, 2).toFixed(2))
}


return (
<div>
<p>You can spend a maximum of {Math.max(maxVotes, 0)} voting power ('VP').</p>
<p>
You can spend a maximum of {Math.max(maxVotes, 0)} voting power ('VP').
</p>
<p>
You already have {itemCost} votes ({getQuadraticCost(itemCost)} VP) on
this item.
Expand All @@ -103,7 +117,7 @@ export function QuadraticVote(props: Props) {
className="mr-2"
variant="small"
disabled={disableLower()}
onClick={() => onChange('MIN')}
onClick={() => onChangeStep('MIN')}
>
MIN
</Button>
Expand All @@ -112,26 +126,27 @@ export function QuadraticVote(props: Props) {
className="mr-2"
variant="small"
disabled={disableLower()}
onClick={() => onChange('DOWN')}
onClick={() => onChangeStep('DOWN')}
>
&laquo;
</Button>
<TextInput
css=""
variant="small"
type="number"
width={100}
aria-label="Amount of votes"
name="votes"
placeholder="Amount of votes..."
value={voteAmount}
readOnly
onChange={onChangeInput}
/>
<Button
css=""
className="ml-2"
variant="small"
disabled={disableHigher()}
onClick={() => onChange('UP')}
onClick={() => onChangeStep('UP')}
>
&raquo;
</Button>
Expand All @@ -140,7 +155,7 @@ export function QuadraticVote(props: Props) {
className="ml-2"
variant="small"
disabled={disableHigher()}
onClick={() => onChange('MAX')}
onClick={() => onChangeStep('MAX')}
>
MAX
</Button>
Expand Down
30 changes: 3 additions & 27 deletions src/pages/[owner]/[repo].tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React from 'react'
import { GetStaticPaths, GetStaticProps } from 'next'
import { GetServerSideProps } from 'next'
import { ParsedUrlQuery } from 'querystring'
import { Backlog } from 'src/types'
import { DEFAULT_CACHE_REVALIDATE } from 'src/utils/constants'
import { GithubService } from 'src/services/github/service'
import { TokenlogService } from 'src/services/tokenlog'
import { Create } from 'src/repository/factory'
import { BacklogLayout } from 'src/components/layouts/Backlog'
import { BacklogContextProvider } from 'src/context/backlog-context'
Expand Down Expand Up @@ -33,36 +31,15 @@ export default function BacklogPage(data: Props) {
)
}

export const getStaticPaths: GetStaticPaths = async () => {
const repository = Create()
const service = new TokenlogService(repository)
const ids = await service.GetBacklogs()

const paths = ids
.map((backlog: Backlog) => {
if (backlog.type === 'github') {
const owner = backlog.id.replace('github:', '').split('/')[0]
const repo = backlog.id.replace('github:', '').split('/')[1]

return {
params: { owner: owner, repo: repo },
}
}
})
.filter((i) => !!i)

return { paths, fallback: 'blocking' }
}

export const getStaticProps: GetStaticProps<Props, Params> = async (
export const getServerSideProps: GetServerSideProps<Props, Params> = async (
context
) => {
const repository = Create()
const service = new GithubService(repository)
const id = `github:${context.params.owner}/${context.params.repo}`
const backlog = await service.GetBacklog(id)
backlog.items = await service.GetBacklogItems(id)

if (!backlog) {
return {
props: null,
Expand All @@ -74,6 +51,5 @@ export const getStaticProps: GetStaticProps<Props, Params> = async (
props: {
backlog: backlog,
},
revalidate: DEFAULT_CACHE_REVALIDATE,
}
}
2 changes: 1 addition & 1 deletion src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const IMAGE_URL = 'https://tokenlog.xyz/icon.png'

export const CREATOR_NOTICE = '2020 — wslyvh'

export const DEFAULT_CACHE_REVALIDATE = 10
export const DEFAULT_CACHE_REVALIDATE = 5

export const VOTE_VERSION = 2

Expand Down