From 7e37fc776c1379bde3b40281b3395805832a5306 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Wed, 8 Jun 2022 07:24:12 -0600 Subject: [PATCH] Fr comment ux improvements (#451) * Extend comment input box, only use airplane * Only 1 commentable bet, shrink input, fix feed lines * Pad sign in to comment button * Small changes --- web/components/feed/activity-items.ts | 62 +---- .../feed/feed-answer-comment-group.tsx | 135 ++++++---- web/components/feed/feed-comments.tsx | 230 ++++++++++-------- 3 files changed, 221 insertions(+), 206 deletions(-) diff --git a/web/components/feed/activity-items.ts b/web/components/feed/activity-items.ts index f40de1df..4af7d385 100644 --- a/web/components/feed/activity-items.ts +++ b/web/components/feed/activity-items.ts @@ -29,7 +29,6 @@ export type CommentInputItem = BaseActivityItem & { type: 'commentInput' betsByCurrentUser: Bet[] commentsByCurrentUser: Comment[] - answerOutcome?: string } export type DescriptionItem = BaseActivityItem & { @@ -74,10 +73,10 @@ export type BetGroupItem = BaseActivityItem & { export type AnswerGroupItem = BaseActivityItem & { type: 'answergroup' + user: User | undefined | null answer: Answer - items: ActivityItem[] - betsByCurrentUser?: Bet[] - commentsByCurrentUser?: Comment[] + comments: Comment[] + bets: Bet[] } export type CloseItem = BaseActivityItem & { @@ -232,31 +231,19 @@ function getAnswerGroups( const answerGroups = outcomes .map((outcome) => { - const answerBets = bets.filter((bet) => bet.outcome === outcome) - const answerComments = comments.filter((comment) => - answerBets.some((bet) => bet.id === comment.betId) - ) const answer = contract.answers?.find( (answer) => answer.id === outcome ) as Answer - let items = groupBets(answerBets, answerComments, contract, user?.id, { - hideOutcome: true, - abbreviated, - smallAvatar: true, - reversed, - }) - - if (abbreviated) - items = items.slice(-ABBREVIATED_NUM_COMMENTS_OR_BETS_TO_SHOW) - + // TODO: this doesn't abbreviate these groups for activity feed anymore return { id: outcome, type: 'answergroup' as const, contract, - answer, - items, user, + answer, + comments, + bets, } }) .filter((group) => group.answer) @@ -276,7 +263,6 @@ function getAnswerAndCommentInputGroups( outcomes = sortBy(outcomes, (outcome) => getOutcomeProbability(contract, outcome) ) - const betsByCurrentUser = bets.filter((bet) => bet.userId === user?.id) const answerGroups = outcomes .map((outcome) => { @@ -284,25 +270,14 @@ function getAnswerAndCommentInputGroups( (answer) => answer.id === outcome ) as Answer - const answerBets = bets.filter((bet) => bet.outcome === outcome) - const answerComments = comments.filter( - (comment) => - comment.answerOutcome === outcome || - answerBets.some((bet) => bet.id === comment.betId) - ) - const items = getCommentThreads(bets, answerComments, contract) - return { id: outcome, type: 'answergroup' as const, contract, - answer, - items, user, - betsByCurrentUser, - commentsByCurrentUser: answerComments.filter( - (comment) => comment.userId === user?.id - ), + answer, + comments, + bets, } }) .filter((group) => group.answer) as ActivityItem[] @@ -425,13 +400,6 @@ export function getAllContractActivityItems( } ) ) - items.push({ - type: 'commentInput' as const, - id: 'commentInput', - contract, - betsByCurrentUser: [], - commentsByCurrentUser: [], - }) } else { items.push( ...groupBetsAndComments(bets, comments, contract, user?.id, { @@ -450,16 +418,6 @@ export function getAllContractActivityItems( items.push({ type: 'resolve', id: `${contract.resolutionTime}`, contract }) } - if (outcomeType === 'BINARY') { - items.push({ - type: 'commentInput' as const, - id: 'commentInput', - contract, - betsByCurrentUser: [], - commentsByCurrentUser: [], - }) - } - if (reversed) items.reverse() return items diff --git a/web/components/feed/feed-answer-comment-group.tsx b/web/components/feed/feed-answer-comment-group.tsx index 1b976668..a745a8d9 100644 --- a/web/components/feed/feed-answer-comment-group.tsx +++ b/web/components/feed/feed-answer-comment-group.tsx @@ -1,8 +1,6 @@ import { Answer } from 'common/answer' -import { ActivityItem } from 'web/components/feed/activity-items' import { Bet } from 'common/bet' import { Comment } from 'common/comment' -import { useUser } from 'web/hooks/use-user' import { getDpmOutcomeProbability } from 'common/calculate-dpm' import { formatPercent } from 'common/util/format' import React, { useEffect, useState } from 'react' @@ -16,44 +14,80 @@ import { Linkify } from 'web/components/linkify' import clsx from 'clsx' import { tradingAllowed } from 'web/lib/firebase/contracts' import { BuyButton } from 'web/components/yes-no-selector' -import { FeedItem } from 'web/components/feed/feed-items' import { CommentInput, + CommentRepliesList, getMostRecentCommentableBet, } from 'web/components/feed/feed-comments' import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time' import { useRouter } from 'next/router' +import { groupBy } from 'lodash' +import { User } from 'common/user' export function FeedAnswerCommentGroup(props: { contract: any + user: User | undefined | null answer: Answer - items: ActivityItem[] - type: string - betsByCurrentUser?: Bet[] - commentsByCurrentUser?: Comment[] + comments: Comment[] + bets: Bet[] }) { - const { answer, items, contract, betsByCurrentUser, commentsByCurrentUser } = - props + const { answer, contract, comments, bets, user } = props const { username, avatarUrl, name, text } = answer - const answerElementId = `answer-${answer.id}` - const user = useUser() - const mostRecentCommentableBet = getMostRecentCommentableBet( - betsByCurrentUser ?? [], - commentsByCurrentUser ?? [], - user, - answer.number + '' - ) - const prob = getDpmOutcomeProbability(contract.totalShares, answer.id) - const probPercent = formatPercent(prob) + + const [replyToUsername, setReplyToUsername] = useState('') const [open, setOpen] = useState(false) const [showReply, setShowReply] = useState(false) - const isFreeResponseContractPage = !!commentsByCurrentUser - if (mostRecentCommentableBet && !showReply) setShowReplyAndFocus(true) const [inputRef, setInputRef] = useState(null) + const [highlighted, setHighlighted] = useState(false) + const router = useRouter() - // If they've already opened the input box, focus it once again - function setShowReplyAndFocus(show: boolean) { - setShowReply(show) + const answerElementId = `answer-${answer.id}` + const betsByUserId = groupBy(bets, (bet) => bet.userId) + const commentsByUserId = groupBy(comments, (comment) => comment.userId) + const answerComments = comments.filter( + (comment) => comment.answerOutcome === answer.number.toString() + ) + const commentReplies = comments.filter( + (comment) => + comment.replyToCommentId && + !comment.answerOutcome && + answerComments.map((c) => c.id).includes(comment.replyToCommentId) + ) + const commentsList = answerComments.concat(commentReplies) + + const prob = getDpmOutcomeProbability(contract.totalShares, answer.id) + const probPercent = formatPercent(prob) + const betsByCurrentUser = (user && betsByUserId[user.id]) ?? [] + const commentsByCurrentUser = (user && commentsByUserId[user.id]) ?? [] + const isFreeResponseContractPage = !!commentsByCurrentUser + useEffect(() => { + const mostRecentCommentableBet = getMostRecentCommentableBet( + betsByCurrentUser, + commentsByCurrentUser, + user, + answer.number.toString() + ) + if (mostRecentCommentableBet && !showReply) + scrollAndOpenReplyInput(undefined, answer) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [betsByCurrentUser]) + + useEffect(() => { + // Only show one comment input for a bet at a time + const usersMostRecentBet = bets + .filter((b) => b.userId === user?.id) + .sort((a, b) => b.createdTime - a.createdTime)[0] + if ( + usersMostRecentBet && + usersMostRecentBet.outcome !== answer.number.toString() + ) { + setShowReply(false) + } + }, [answer.number, bets, user]) + + function scrollAndOpenReplyInput(comment?: Comment, answer?: Answer) { + setReplyToUsername(comment?.userUsername ?? answer?.username ?? '') + setShowReply(true) inputRef?.focus() } @@ -61,8 +95,6 @@ export function FeedAnswerCommentGroup(props: { if (showReply && inputRef) inputRef.focus() }, [inputRef, showReply]) - const [highlighted, setHighlighted] = useState(false) - const router = useRouter() useEffect(() => { if (router.asPath.endsWith(`#${answerElementId}`)) { setHighlighted(true) @@ -70,7 +102,7 @@ export function FeedAnswerCommentGroup(props: { }, [answerElementId, router.asPath]) return ( - + setShowReplyAndFocus(true)} + onClick={() => scrollAndOpenReplyInput(undefined, answer)} > Reply @@ -143,7 +175,7 @@ export function FeedAnswerCommentGroup(props: {