Misc comment ux improvements (#103)
* Separate comments and bets via tabs * Normalcase comment button * Note about abbreviated and all mode * Revese,abbreviate,limit comments in feed
This commit is contained in:
parent
f9f226aceb
commit
1db1a739cf
|
@ -21,13 +21,24 @@ export function ContractTabs(props: {
|
||||||
bets.sort((bet1, bet2) => bet2.createdTime - bet1.createdTime)
|
bets.sort((bet1, bet2) => bet2.createdTime - bet1.createdTime)
|
||||||
const userBets = user && bets.filter((bet) => bet.userId === user.id)
|
const userBets = user && bets.filter((bet) => bet.userId === user.id)
|
||||||
|
|
||||||
const activity = (
|
const betActivity = (
|
||||||
<ContractActivity
|
<ContractActivity
|
||||||
contract={contract}
|
contract={contract}
|
||||||
bets={bets}
|
bets={bets}
|
||||||
comments={comments}
|
comments={comments}
|
||||||
user={user}
|
user={user}
|
||||||
mode="all"
|
mode="bets"
|
||||||
|
betRowClassName="!mt-0 xl:hidden"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const commentActivity = (
|
||||||
|
<ContractActivity
|
||||||
|
contract={contract}
|
||||||
|
bets={bets}
|
||||||
|
comments={comments}
|
||||||
|
user={user}
|
||||||
|
mode="comments"
|
||||||
betRowClassName="!mt-0 xl:hidden"
|
betRowClassName="!mt-0 xl:hidden"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -48,7 +59,8 @@ export function ContractTabs(props: {
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
tabs={[
|
tabs={[
|
||||||
{ title: 'Timeline', content: activity },
|
{ title: 'Comments', content: commentActivity },
|
||||||
|
{ title: 'Bets', content: betActivity },
|
||||||
...(!user || !userBets?.length
|
...(!user || !userBets?.length
|
||||||
? []
|
? []
|
||||||
: [{ title: 'Your bets', content: yourTrades }]),
|
: [{ title: 'Your bets', content: yourTrades }]),
|
||||||
|
|
|
@ -31,8 +31,6 @@ type BaseActivityItem = {
|
||||||
|
|
||||||
export type CommentInputItem = BaseActivityItem & {
|
export type CommentInputItem = BaseActivityItem & {
|
||||||
type: 'commentInput'
|
type: 'commentInput'
|
||||||
bets: Bet[]
|
|
||||||
commentsByBetId: Record<string, Comment>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DescriptionItem = BaseActivityItem & {
|
export type DescriptionItem = BaseActivityItem & {
|
||||||
|
@ -82,6 +80,7 @@ export type ResolveItem = BaseActivityItem & {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DAY_IN_MS = 24 * 60 * 60 * 1000
|
const DAY_IN_MS = 24 * 60 * 60 * 1000
|
||||||
|
const ABBREVIATED_NUM_COMMENTS_OR_BETS_TO_SHOW = 3
|
||||||
|
|
||||||
// Group together bets that are:
|
// Group together bets that are:
|
||||||
// - Within a day of the first in the group
|
// - Within a day of the first in the group
|
||||||
|
@ -173,7 +172,9 @@ function groupBets(
|
||||||
if (group.length > 0) {
|
if (group.length > 0) {
|
||||||
pushGroup()
|
pushGroup()
|
||||||
}
|
}
|
||||||
const abbrItems = abbreviated ? items.slice(-3) : items
|
const abbrItems = abbreviated
|
||||||
|
? items.slice(-ABBREVIATED_NUM_COMMENTS_OR_BETS_TO_SHOW)
|
||||||
|
: items
|
||||||
if (reversed) abbrItems.reverse()
|
if (reversed) abbrItems.reverse()
|
||||||
return abbrItems
|
return abbrItems
|
||||||
}
|
}
|
||||||
|
@ -240,7 +241,8 @@ function getAnswerGroups(
|
||||||
reversed,
|
reversed,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (abbreviated) items = items.slice(-2)
|
if (abbreviated)
|
||||||
|
items = items.slice(-ABBREVIATED_NUM_COMMENTS_OR_BETS_TO_SHOW)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: outcome,
|
id: outcome,
|
||||||
|
@ -253,6 +255,8 @@ function getAnswerGroups(
|
||||||
})
|
})
|
||||||
.filter((group) => group.answer)
|
.filter((group) => group.answer)
|
||||||
|
|
||||||
|
if (reversed) answerGroups.reverse()
|
||||||
|
|
||||||
return answerGroups
|
return answerGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +272,7 @@ function groupBetsAndComments(
|
||||||
reversed: boolean
|
reversed: boolean
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
const { smallAvatar, abbreviated, reversed } = options
|
||||||
const commentsWithoutBets = comments
|
const commentsWithoutBets = comments
|
||||||
.filter((comment) => !comment.betId)
|
.filter((comment) => !comment.betId)
|
||||||
.map((comment) => ({
|
.map((comment) => ({
|
||||||
|
@ -276,16 +281,16 @@ function groupBetsAndComments(
|
||||||
contract: contract,
|
contract: contract,
|
||||||
comment,
|
comment,
|
||||||
bet: undefined,
|
bet: undefined,
|
||||||
truncate: false,
|
truncate: abbreviated,
|
||||||
hideOutcome: true,
|
hideOutcome: true,
|
||||||
smallAvatar: false,
|
smallAvatar,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const groupedBets = groupBets(bets, comments, contract, userId, options)
|
const groupedBets = groupBets(bets, comments, contract, userId, options)
|
||||||
|
|
||||||
// iterate through the bets and comment activity items and add them to the items in order of comment creation time:
|
// iterate through the bets and comment activity items and add them to the items in order of comment creation time:
|
||||||
const unorderedBetsAndComments = [...commentsWithoutBets, ...groupedBets]
|
const unorderedBetsAndComments = [...commentsWithoutBets, ...groupedBets]
|
||||||
const sortedBetsAndComments = _.sortBy(unorderedBetsAndComments, (item) => {
|
let sortedBetsAndComments = _.sortBy(unorderedBetsAndComments, (item) => {
|
||||||
if (item.type === 'comment') {
|
if (item.type === 'comment') {
|
||||||
return item.comment.createdTime
|
return item.comment.createdTime
|
||||||
} else if (item.type === 'bet') {
|
} else if (item.type === 'bet') {
|
||||||
|
@ -294,7 +299,13 @@ function groupBetsAndComments(
|
||||||
return item.bets[0].createdTime
|
return item.bets[0].createdTime
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return sortedBetsAndComments
|
|
||||||
|
const abbrItems = abbreviated
|
||||||
|
? sortedBetsAndComments.slice(-ABBREVIATED_NUM_COMMENTS_OR_BETS_TO_SHOW)
|
||||||
|
: sortedBetsAndComments
|
||||||
|
|
||||||
|
if (reversed) abbrItems.reverse()
|
||||||
|
return abbrItems
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllContractActivityItems(
|
export function getAllContractActivityItems(
|
||||||
|
@ -308,8 +319,7 @@ export function getAllContractActivityItems(
|
||||||
) {
|
) {
|
||||||
const { abbreviated } = options
|
const { abbreviated } = options
|
||||||
const { outcomeType } = contract
|
const { outcomeType } = contract
|
||||||
|
const reversed = true
|
||||||
const reversed = !abbreviated
|
|
||||||
|
|
||||||
bets =
|
bets =
|
||||||
outcomeType === 'BINARY'
|
outcomeType === 'BINARY'
|
||||||
|
@ -347,12 +357,9 @@ export function getAllContractActivityItems(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
const commentsByBetId = mapCommentsByBetId(comments)
|
|
||||||
items.push({
|
items.push({
|
||||||
type: 'commentInput',
|
type: 'commentInput',
|
||||||
id: 'commentInput',
|
id: 'commentInput',
|
||||||
bets,
|
|
||||||
commentsByBetId,
|
|
||||||
contract,
|
contract,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -374,12 +381,9 @@ export function getAllContractActivityItems(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcomeType === 'BINARY') {
|
if (outcomeType === 'BINARY') {
|
||||||
const commentsByBetId = mapCommentsByBetId(comments)
|
|
||||||
items.push({
|
items.push({
|
||||||
type: 'commentInput',
|
type: 'commentInput',
|
||||||
id: 'commentInput',
|
id: 'commentInput',
|
||||||
bets,
|
|
||||||
commentsByBetId,
|
|
||||||
contract,
|
contract,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -412,25 +416,95 @@ export function getRecentContractActivityItems(
|
||||||
contractPath,
|
contractPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
const items =
|
const items = []
|
||||||
contract.outcomeType === 'FREE_RESPONSE'
|
if (contract.outcomeType === 'FREE_RESPONSE') {
|
||||||
? getAnswerGroups(
|
items.push(
|
||||||
contract as FullContract<DPM, FreeResponse>,
|
...getAnswerGroups(
|
||||||
bets,
|
contract as FullContract<DPM, FreeResponse>,
|
||||||
comments,
|
bets,
|
||||||
user,
|
comments,
|
||||||
{
|
user,
|
||||||
sortByProb: false,
|
{
|
||||||
abbreviated: true,
|
sortByProb: false,
|
||||||
reversed: false,
|
abbreviated: true,
|
||||||
}
|
reversed: true,
|
||||||
)
|
}
|
||||||
: groupBetsAndComments(bets, comments, contract, user?.id, {
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const onlyUsersBetsOrBetsWithComments = bets.filter((bet) =>
|
||||||
|
comments.some(
|
||||||
|
(comment) => comment.betId === bet.id || bet.userId === user?.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
items.push(
|
||||||
|
...groupBetsAndComments(
|
||||||
|
onlyUsersBetsOrBetsWithComments,
|
||||||
|
comments,
|
||||||
|
contract,
|
||||||
|
user?.id,
|
||||||
|
{
|
||||||
hideOutcome: false,
|
hideOutcome: false,
|
||||||
abbreviated: true,
|
abbreviated: true,
|
||||||
smallAvatar: false,
|
smallAvatar: false,
|
||||||
reversed: false,
|
reversed: true,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return [questionItem, ...items]
|
return [questionItem, ...items]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSpecificContractActivityItems(
|
||||||
|
contract: Contract,
|
||||||
|
bets: Bet[],
|
||||||
|
comments: Comment[],
|
||||||
|
user: User | null | undefined,
|
||||||
|
options: {
|
||||||
|
mode: 'comments' | 'bets'
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const { mode } = options
|
||||||
|
let items = [] as ActivityItem[]
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 'bets':
|
||||||
|
items.push(
|
||||||
|
...groupBets(bets, comments, contract, user?.id, {
|
||||||
|
hideOutcome: false,
|
||||||
|
abbreviated: false,
|
||||||
|
smallAvatar: false,
|
||||||
|
reversed: false,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'comments':
|
||||||
|
const onlyBetsWithComments = bets.filter((bet) =>
|
||||||
|
comments.some((comment) => comment.betId === bet.id)
|
||||||
|
)
|
||||||
|
items.push(
|
||||||
|
...groupBetsAndComments(
|
||||||
|
onlyBetsWithComments,
|
||||||
|
comments,
|
||||||
|
contract,
|
||||||
|
user?.id,
|
||||||
|
{
|
||||||
|
hideOutcome: false,
|
||||||
|
abbreviated: false,
|
||||||
|
smallAvatar: false,
|
||||||
|
reversed: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
items.push({
|
||||||
|
type: 'commentInput',
|
||||||
|
id: 'commentInput',
|
||||||
|
contract,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.reverse()
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
import { Contract } from '../../lib/firebase/contracts'
|
import { Contract } from '../../lib/firebase/contracts'
|
||||||
import { Comment } from '../../lib/firebase/comments'
|
import { Comment } from '../../lib/firebase/comments'
|
||||||
import { Bet } from '../../../common/bet'
|
import { Bet } from '../../../common/bet'
|
||||||
|
@ -8,6 +6,7 @@ import { useComments } from '../../hooks/use-comments'
|
||||||
import {
|
import {
|
||||||
getAllContractActivityItems,
|
getAllContractActivityItems,
|
||||||
getRecentContractActivityItems,
|
getRecentContractActivityItems,
|
||||||
|
getSpecificContractActivityItems,
|
||||||
} from './activity-items'
|
} from './activity-items'
|
||||||
import { FeedItems } from './feed-items'
|
import { FeedItems } from './feed-items'
|
||||||
import { User } from '../../../common/user'
|
import { User } from '../../../common/user'
|
||||||
|
@ -17,7 +16,7 @@ export function ContractActivity(props: {
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
comments: Comment[]
|
comments: Comment[]
|
||||||
user: User | null | undefined
|
user: User | null | undefined
|
||||||
mode: 'only-recent' | 'abbreviated' | 'all'
|
mode: 'only-recent' | 'abbreviated' | 'all' | 'comments' | 'bets'
|
||||||
contractPath?: string
|
contractPath?: string
|
||||||
className?: string
|
className?: string
|
||||||
betRowClassName?: string
|
betRowClassName?: string
|
||||||
|
@ -39,7 +38,12 @@ export function ContractActivity(props: {
|
||||||
? getRecentContractActivityItems(contract, bets, comments, user, {
|
? getRecentContractActivityItems(contract, bets, comments, user, {
|
||||||
contractPath,
|
contractPath,
|
||||||
})
|
})
|
||||||
: getAllContractActivityItems(contract, bets, comments, user, {
|
: mode === 'comments' || mode === 'bets'
|
||||||
|
? getSpecificContractActivityItems(contract, bets, comments, user, {
|
||||||
|
mode,
|
||||||
|
})
|
||||||
|
: // only used in abbreviated mode with folds/communities, all mode isn't used
|
||||||
|
getAllContractActivityItems(contract, bets, comments, user, {
|
||||||
abbreviated: mode === 'abbreviated',
|
abbreviated: mode === 'abbreviated',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -191,31 +191,12 @@ function RelativeTimestamp(props: { time: number }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CommentInput(props: {
|
export function CommentInput(props: { contract: Contract }) {
|
||||||
contract: Contract
|
|
||||||
commentsByBetId: Record<string, Comment>
|
|
||||||
bets: Bet[]
|
|
||||||
}) {
|
|
||||||
// see if we can comment input on any bet:
|
// see if we can comment input on any bet:
|
||||||
const { contract, bets, commentsByBetId } = props
|
const { contract } = props
|
||||||
const { outcomeType } = contract
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const [comment, setComment] = useState('')
|
const [comment, setComment] = useState('')
|
||||||
|
|
||||||
let canCommentOnABet = false
|
|
||||||
bets.some((bet) => {
|
|
||||||
// make sure there is not already a comment with a matching bet id:
|
|
||||||
const matchingComment = commentsByBetId[bet.id]
|
|
||||||
if (matchingComment) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const { createdTime, userId } = bet
|
|
||||||
canCommentOnABet = canCommentOnBet(userId, createdTime, user)
|
|
||||||
return canCommentOnABet
|
|
||||||
})
|
|
||||||
|
|
||||||
if (canCommentOnABet) return <div />
|
|
||||||
|
|
||||||
async function submitComment() {
|
async function submitComment() {
|
||||||
if (!comment) return
|
if (!comment) return
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
@ -342,7 +323,7 @@ export function FeedBet(props: {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline btn-sm mt-1"
|
className="btn btn-outline btn-sm text-transform: mt-1 capitalize"
|
||||||
onClick={submitComment}
|
onClick={submitComment}
|
||||||
disabled={!canComment}
|
disabled={!canComment}
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user