Send only the collapsed activity feed items to client instead of all bets and comments
This commit is contained in:
parent
1c8c2a4126
commit
e2657c75a3
|
@ -6,7 +6,6 @@ import {
|
|||
CheckIcon,
|
||||
DotsVerticalIcon,
|
||||
LockClosedIcon,
|
||||
StarIcon,
|
||||
UserIcon,
|
||||
UsersIcon,
|
||||
XIcon,
|
||||
|
@ -43,6 +42,7 @@ import { fromNow } from '../lib/util/time'
|
|||
import BetRow from './bet-row'
|
||||
import { parseTags } from '../../common/util/parse'
|
||||
import { Avatar } from './avatar'
|
||||
import { User } from '../../common/user'
|
||||
|
||||
function FeedComment(props: {
|
||||
activityItem: any
|
||||
|
@ -470,7 +470,12 @@ function groupBets(
|
|||
if (group.length == 1) {
|
||||
items.push(toActivityItem(group[0]))
|
||||
} else if (group.length > 1) {
|
||||
items.push({ type: 'betgroup', bets: [...group], id: group[0].id })
|
||||
items.push({
|
||||
type: 'betgroup',
|
||||
bets: [...group],
|
||||
id: group[0].id,
|
||||
createdTime: group[group.length - 1].createdTime,
|
||||
})
|
||||
}
|
||||
group = []
|
||||
}
|
||||
|
@ -504,6 +509,52 @@ function groupBets(
|
|||
return items as ActivityItem[]
|
||||
}
|
||||
|
||||
export function getContractFeedItems(
|
||||
contract: Contract,
|
||||
bets: Bet[],
|
||||
comments: Comment[],
|
||||
user: User | null | undefined,
|
||||
options: { feedType: 'activity' | 'market'; expanded: boolean }
|
||||
) {
|
||||
const { feedType, expanded } = options
|
||||
const groupWindow = feedType == 'activity' ? 10 * DAY_IN_MS : DAY_IN_MS
|
||||
|
||||
const allItems: ActivityItem[] = [
|
||||
{ type: 'start', id: '0', createdTime: contract.createdTime },
|
||||
...groupBets(
|
||||
bets.filter((bet) => !bet.isAnte),
|
||||
comments,
|
||||
groupWindow,
|
||||
user?.id
|
||||
),
|
||||
]
|
||||
if (contract.closeTime && contract.closeTime <= Date.now()) {
|
||||
allItems.push({
|
||||
type: 'close',
|
||||
id: `${contract.closeTime}`,
|
||||
createdTime: contract.closeTime,
|
||||
})
|
||||
}
|
||||
if (contract.resolution) {
|
||||
allItems.push({
|
||||
type: 'resolve',
|
||||
id: `${contract.resolutionTime}`,
|
||||
createdTime: contract.resolutionTime,
|
||||
})
|
||||
}
|
||||
|
||||
// If there are more than 5 items, only show the first, an expand item, and last 3
|
||||
let items = allItems
|
||||
if (!expanded && allItems.length > 5 && feedType == 'activity') {
|
||||
items = [
|
||||
allItems[0],
|
||||
{ type: 'expand', id: 'expand' },
|
||||
...allItems.slice(-3),
|
||||
]
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
function BetGroupSpan(props: { bets: Bet[]; outcome: 'YES' | 'NO' }) {
|
||||
const { bets, outcome } = props
|
||||
|
||||
|
@ -583,9 +634,7 @@ function FeedExpand(props: { setExpanded: (expanded: boolean) => void }) {
|
|||
)
|
||||
}
|
||||
|
||||
// Missing feed items:
|
||||
// - Bet sold?
|
||||
type ActivityItem = {
|
||||
export type ActivityItem = {
|
||||
id: string
|
||||
type:
|
||||
| 'bet'
|
||||
|
@ -595,48 +644,34 @@ type ActivityItem = {
|
|||
| 'close'
|
||||
| 'resolve'
|
||||
| 'expand'
|
||||
|
||||
createdTime?: number
|
||||
}
|
||||
|
||||
export function ContractFeed(props: {
|
||||
contract: Contract
|
||||
bets: Bet[]
|
||||
comments: Comment[]
|
||||
activityItems: ActivityItem[]
|
||||
// Feed types: 'activity' = Activity feed, 'market' = Comments feed on a market
|
||||
feedType: 'activity' | 'market'
|
||||
betRowClassName?: string
|
||||
}) {
|
||||
const { contract, feedType, betRowClassName } = props
|
||||
const { contract, activityItems, feedType, betRowClassName } = props
|
||||
const { id } = contract
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const user = useUser()
|
||||
|
||||
let bets = useBets(id) ?? props.bets
|
||||
let bets = useBets(id)
|
||||
bets = withoutAnteBets(contract, bets)
|
||||
|
||||
const comments = useComments(id) ?? props.comments
|
||||
const comments = useComments(id)
|
||||
|
||||
const groupWindow = feedType == 'activity' ? 10 * DAY_IN_MS : DAY_IN_MS
|
||||
|
||||
const allItems = [
|
||||
{ type: 'start', id: 0 },
|
||||
...groupBets(bets, comments, groupWindow, user?.id),
|
||||
]
|
||||
if (contract.closeTime && contract.closeTime <= Date.now()) {
|
||||
allItems.push({ type: 'close', id: `${contract.closeTime}` })
|
||||
}
|
||||
if (contract.resolution) {
|
||||
allItems.push({ type: 'resolve', id: `${contract.resolutionTime}` })
|
||||
}
|
||||
|
||||
// If there are more than 5 items, only show the first, an expand item, and last 3
|
||||
let items = allItems
|
||||
if (!expanded && allItems.length > 5 && feedType == 'activity') {
|
||||
items = [
|
||||
allItems[0],
|
||||
{ type: 'expand', id: 'expand' },
|
||||
...allItems.slice(-3),
|
||||
]
|
||||
}
|
||||
const items =
|
||||
bets && comments
|
||||
? getContractFeedItems(contract, bets, comments, user, {
|
||||
feedType,
|
||||
expanded,
|
||||
})
|
||||
: activityItems
|
||||
|
||||
return (
|
||||
<div className="flow-root pr-2 md:pr-0">
|
||||
|
|
|
@ -14,7 +14,7 @@ import { Row } from './layout/row'
|
|||
import { Linkify } from './linkify'
|
||||
import clsx from 'clsx'
|
||||
import { ContractDetails, ResolutionOrChance } from './contract-card'
|
||||
import { ContractFeed } from './contract-feed'
|
||||
import { ContractFeed, getContractFeedItems } from './contract-feed'
|
||||
import { TweetButton } from './tweet-button'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { Comment } from '../../common/comment'
|
||||
|
@ -46,6 +46,11 @@ export const ContractOverview = (props: {
|
|||
const url = `https://manifold.markets${contractPath(contract)}`
|
||||
const tweetText = `${tweetQuestion}\n\n${tweetDescription}\n\n${url}`
|
||||
|
||||
const activityItems = getContractFeedItems(contract, bets, comments, user, {
|
||||
feedType: 'market',
|
||||
expanded: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<Col className={clsx('mb-6', className)}>
|
||||
<Row className="justify-between gap-4 px-2">
|
||||
|
@ -131,8 +136,7 @@ export const ContractOverview = (props: {
|
|||
|
||||
<ContractFeed
|
||||
contract={contract}
|
||||
bets={bets}
|
||||
comments={comments}
|
||||
activityItems={activityItems}
|
||||
feedType="market"
|
||||
betRowClassName="md:hidden !mt-0"
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import _ from 'lodash'
|
||||
import { ContractFeed } from '../components/contract-feed'
|
||||
import {
|
||||
ActivityItem,
|
||||
ContractFeed,
|
||||
getContractFeedItems,
|
||||
} from '../components/contract-feed'
|
||||
import { Page } from '../components/page'
|
||||
import { Contract } from '../lib/firebase/contracts'
|
||||
import { Comment } from '../lib/firebase/comments'
|
||||
|
@ -85,27 +89,86 @@ export function findActiveContracts(
|
|||
return contracts.slice(0, MAX_ACTIVE_CONTRACTS)
|
||||
}
|
||||
|
||||
export function getActivity(
|
||||
contracts: Contract[],
|
||||
contractBets: Bet[][],
|
||||
contractComments: Comment[][]
|
||||
) {
|
||||
const contractActivityItems = contracts.map((contract, i) => {
|
||||
const bets = contractBets[i]
|
||||
const comments = contractComments[i]
|
||||
return getContractFeedItems(contract, bets, comments, undefined, {
|
||||
expanded: false,
|
||||
feedType: 'activity',
|
||||
})
|
||||
})
|
||||
|
||||
const hotMarketTimes: { [contractId: string]: number } = {}
|
||||
|
||||
// Add recent top-trading contracts, ordered by last bet.
|
||||
const DAY_IN_MS = 24 * 60 * 60 * 1000
|
||||
const contractTotalBets = _.map(contractBets, (bets) => {
|
||||
const recentBets = bets.filter(
|
||||
(bet) => bet.createdTime > Date.now() - DAY_IN_MS
|
||||
)
|
||||
return _.sumBy(recentBets, (bet) => bet.amount)
|
||||
})
|
||||
const topTradedContracts = _.sortBy(
|
||||
contractTotalBets.map((total, index) => [total, index] as [number, number]),
|
||||
([total, _]) => -1 * total
|
||||
).slice(0, MAX_HOT_MARKETS)
|
||||
|
||||
for (const [_total, index] of topTradedContracts) {
|
||||
const lastBet = _.last(contractBets[index])
|
||||
if (lastBet) {
|
||||
hotMarketTimes[contracts[index].id] = lastBet.createdTime
|
||||
}
|
||||
}
|
||||
|
||||
const orderedContracts = _.sortBy(
|
||||
contracts.map((c, index) => [c, index] as const),
|
||||
([contract, index]) => {
|
||||
const activeTypes = ['start', 'comment', 'close', 'resolve']
|
||||
const { createdTime } = _.last(
|
||||
contractActivityItems[index].filter((item) =>
|
||||
activeTypes.includes(item.type)
|
||||
)
|
||||
) as ActivityItem
|
||||
const activeTime = createdTime ?? 0
|
||||
const hotTime = hotMarketTimes[contract.id] ?? 0
|
||||
return -1 * Math.max(activeTime, hotTime)
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
contracts: orderedContracts.map(([_, index]) => contracts[index]),
|
||||
contractActivityItems: orderedContracts.map(
|
||||
([_, index]) => contractActivityItems[index]
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function ActivityFeed(props: {
|
||||
contracts: Contract[]
|
||||
contractBets: Bet[][]
|
||||
contractComments: Comment[][]
|
||||
contractActivityItems: ActivityItem[][]
|
||||
}) {
|
||||
const { contracts, contractBets, contractComments } = props
|
||||
const { contracts, contractActivityItems } = props
|
||||
|
||||
return contracts.length > 0 ? (
|
||||
<Col className="items-center">
|
||||
<Col className="w-full max-w-3xl">
|
||||
<Col className="w-full bg-white self-center divide-gray-300 divide-y">
|
||||
{contracts.map((contract, i) => (
|
||||
{contracts.map((contract, i) => {
|
||||
return (
|
||||
<div key={contract.id} className="py-6 px-2 sm:px-4">
|
||||
<ContractFeed
|
||||
contract={contract}
|
||||
bets={contractBets[i]}
|
||||
comments={contractComments[i]}
|
||||
activityItems={contractActivityItems[i]}
|
||||
feedType="activity"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</Col>
|
||||
</Col>
|
||||
</Col>
|
||||
|
@ -117,7 +180,7 @@ export function ActivityFeed(props: {
|
|||
export default function ActivityPage() {
|
||||
return (
|
||||
<Page>
|
||||
<ActivityFeed contracts={[]} contractBets={[]} contractComments={[]} />
|
||||
<ActivityFeed contracts={[]} contractActivityItems={[]} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import { FollowFoldButton } from '../../../components/follow-fold-button'
|
|||
import FeedCreate from '../../../components/feed-create'
|
||||
import { SEO } from '../../../components/SEO'
|
||||
import { useTaggedContracts } from '../../../hooks/use-contracts'
|
||||
import { getContractFeedItems } from '../../../components/contract-feed'
|
||||
|
||||
export async function getStaticProps(props: { params: { slugs: string[] } }) {
|
||||
const { slugs } = props.params
|
||||
|
@ -175,6 +176,16 @@ export default function FoldPage(props: {
|
|||
(contract) => contractsMap[contract.id]
|
||||
)
|
||||
|
||||
const contractActivityItems = activeContracts.map((contract, index) =>
|
||||
getContractFeedItems(
|
||||
contract,
|
||||
activeContractBets[index],
|
||||
activeContractComments[index],
|
||||
user,
|
||||
{ feedType: 'activity', expanded: false }
|
||||
)
|
||||
)
|
||||
|
||||
if (fold === null || !foldSubpages.includes(page) || slugs[2]) {
|
||||
return <Custom404 />
|
||||
}
|
||||
|
@ -260,8 +271,7 @@ export default function FoldPage(props: {
|
|||
<>
|
||||
<ActivityFeed
|
||||
contracts={activeContracts}
|
||||
contractBets={activeContractBets}
|
||||
contractComments={activeContractComments}
|
||||
contractActivityItems={contractActivityItems}
|
||||
/>
|
||||
{activeContracts.length === 0 && (
|
||||
<div className="text-gray-500 mt-4 mx-2 lg:mx-0">
|
||||
|
|
|
@ -4,9 +4,9 @@ import _ from 'lodash'
|
|||
|
||||
import { Contract, listAllContracts } from '../lib/firebase/contracts'
|
||||
import { Page } from '../components/page'
|
||||
import { ActivityFeed, findActiveContracts } from './activity'
|
||||
import { Comment, listAllComments } from '../lib/firebase/comments'
|
||||
import { Bet, listAllBets } from '../lib/firebase/bets'
|
||||
import { ActivityFeed, getActivity } from './activity'
|
||||
import { listAllComments } from '../lib/firebase/comments'
|
||||
import { listAllBets } from '../lib/firebase/bets'
|
||||
import FeedCreate from '../components/feed-create'
|
||||
import { Spacer } from '../components/layout/spacer'
|
||||
import { Col } from '../components/layout/col'
|
||||
|
@ -22,6 +22,7 @@ import { SearchIcon } from '@heroicons/react/outline'
|
|||
import { Row } from '../components/layout/row'
|
||||
import { SparklesIcon } from '@heroicons/react/solid'
|
||||
import { useFollowedFolds } from '../hooks/use-fold'
|
||||
import { ActivityItem } from '../components/contract-feed'
|
||||
|
||||
export async function getStaticProps() {
|
||||
const [contracts, folds] = await Promise.all([
|
||||
|
@ -36,9 +37,7 @@ export async function getStaticProps() {
|
|||
|
||||
return {
|
||||
props: {
|
||||
contracts,
|
||||
contractBets,
|
||||
contractComments,
|
||||
...getActivity(contracts, contractBets, contractComments),
|
||||
folds,
|
||||
},
|
||||
|
||||
|
@ -48,18 +47,14 @@ export async function getStaticProps() {
|
|||
|
||||
const Home = (props: {
|
||||
contracts: Contract[]
|
||||
contractBets: Bet[][]
|
||||
contractComments: Comment[][]
|
||||
contractActivityItems: ActivityItem[][]
|
||||
folds: Fold[]
|
||||
}) => {
|
||||
const { contractBets, contractComments, folds } = props
|
||||
const { contractActivityItems, folds } = props
|
||||
|
||||
const user = useUser()
|
||||
|
||||
const contracts = useUpdatedContracts(props.contracts)
|
||||
const contractIdToIndex = _.fromPairs(
|
||||
contracts.map((contract, index) => [contract.id, index])
|
||||
)
|
||||
|
||||
const followedFoldIds = useFollowedFolds(user)
|
||||
const followedFolds = filterDefined(
|
||||
|
@ -76,36 +71,20 @@ const Home = (props: {
|
|||
|
||||
const feedContracts =
|
||||
followedFoldIds && yourBetContracts
|
||||
? contracts.filter(
|
||||
? contracts
|
||||
.filter(
|
||||
(contract) =>
|
||||
contract.lowercaseTags.some((tag) => tagSet.has(tag)) ||
|
||||
yourBetContracts.has(contract.id)
|
||||
)
|
||||
.slice(0, 75)
|
||||
: undefined
|
||||
|
||||
const oneDayMS = 24 * 60 * 60 * 1000
|
||||
const recentBets = (feedContracts ?? [])
|
||||
.map((c) => contractBets[contractIdToIndex[c.id]])
|
||||
.flat()
|
||||
.filter((bet) => bet.createdTime > Date.now() - oneDayMS)
|
||||
const feedComments = (feedContracts ?? [])
|
||||
.map((c) => contractComments[contractIdToIndex[c.id]])
|
||||
.flat()
|
||||
const feedContractSet = new Set(feedContracts?.map((contract) => contract.id))
|
||||
|
||||
const activeContracts =
|
||||
feedContracts &&
|
||||
findActiveContracts(feedContracts, feedComments, recentBets, 365)
|
||||
|
||||
const activeBets = activeContracts
|
||||
? activeContracts.map(
|
||||
(contract) => contractBets[contractIdToIndex[contract.id]]
|
||||
const feedActivityItems = contractActivityItems.filter((_, index) =>
|
||||
feedContractSet.has(contracts[index].id)
|
||||
)
|
||||
: []
|
||||
const activeComments = activeContracts
|
||||
? activeContracts.map(
|
||||
(contract) => contractComments[contractIdToIndex[contract.id]]
|
||||
)
|
||||
: []
|
||||
|
||||
if (user === null) {
|
||||
Router.replace('/')
|
||||
|
@ -143,11 +122,10 @@ const Home = (props: {
|
|||
<SparklesIcon className="inline w-5 h-5" aria-hidden="true" />
|
||||
Recent activity
|
||||
</Row>
|
||||
{activeContracts ? (
|
||||
{feedContracts ? (
|
||||
<ActivityFeed
|
||||
contracts={activeContracts}
|
||||
contractBets={activeBets}
|
||||
contractComments={activeComments}
|
||||
contracts={feedContracts}
|
||||
contractActivityItems={feedActivityItems}
|
||||
/>
|
||||
) : (
|
||||
<LoadingIndicator className="mt-4" />
|
||||
|
|
Loading…
Reference in New Issue
Block a user