Track seen contracts in feed. Order feed by seen time priority.
This commit is contained in:
parent
3b933923e2
commit
addc883440
|
@ -1,5 +1,5 @@
|
|||
// From https://tailwindui.com/components/application-ui/lists/feeds
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Fragment, useRef, useState } from 'react'
|
||||
import * as _ from 'lodash'
|
||||
import {
|
||||
BanIcon,
|
||||
|
@ -44,10 +44,9 @@ import { Answer } from '../../../common/answer'
|
|||
import { ActivityItem } from './activity-items'
|
||||
import { FreeResponse, FullContract } from '../../../common/contract'
|
||||
import { BuyButton } from '../yes-no-selector'
|
||||
import { AnswerItem } from '../answers/answer-item'
|
||||
import { getDpmOutcomeProbability } from '../../../common/calculate-dpm'
|
||||
import { BetPanel } from '../bet-panel'
|
||||
import { AnswerBetPanel } from '../answers/answer-bet-panel'
|
||||
import { useSaveSeenContract } from '../../hooks/use-seen-contracts'
|
||||
|
||||
export function FeedItems(props: {
|
||||
contract: Contract
|
||||
|
@ -57,8 +56,11 @@ export function FeedItems(props: {
|
|||
const { contract, items, betRowClassName } = props
|
||||
const { outcomeType } = contract
|
||||
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
useSaveSeenContract(ref, contract)
|
||||
|
||||
return (
|
||||
<div className="flow-root pr-2 md:pr-0">
|
||||
<div className="flow-root pr-2 md:pr-0" ref={ref}>
|
||||
<div className={clsx(tradingAllowed(contract) ? '' : '-mb-6')}>
|
||||
{items.map((item, activityItemIdx) => (
|
||||
<div key={item.id} className="relative pb-6">
|
||||
|
|
|
@ -24,7 +24,8 @@ function lastActivityTime(contract: Contract) {
|
|||
export function findActiveContracts(
|
||||
allContracts: Contract[],
|
||||
recentComments: Comment[],
|
||||
recentBets: Bet[]
|
||||
recentBets: Bet[],
|
||||
seenContracts: { [contractId: string]: number }
|
||||
) {
|
||||
const idToActivityTime = new Map<string, number>()
|
||||
function record(contractId: string, time: number) {
|
||||
|
@ -64,5 +65,34 @@ export function findActiveContracts(
|
|||
activeContracts,
|
||||
(c) => -(idToActivityTime.get(c.id) ?? 0)
|
||||
)
|
||||
return activeContracts.slice(0, MAX_ACTIVE_CONTRACTS)
|
||||
|
||||
const contractComments = _.groupBy(
|
||||
recentComments,
|
||||
(comment) => comment.contractId
|
||||
)
|
||||
const contractMostRecentComment = _.mapValues(
|
||||
contractComments,
|
||||
(comments) => _.maxBy(comments, (c) => c.createdTime) as Comment
|
||||
)
|
||||
|
||||
const prioritizedContracts = _.sortBy(activeContracts, (c) => {
|
||||
const seenTime = seenContracts[c.id]
|
||||
if (!seenTime) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const lastCommentTime = contractMostRecentComment[c.id]?.createdTime
|
||||
if (lastCommentTime && lastCommentTime > seenTime) {
|
||||
return 1
|
||||
}
|
||||
|
||||
const lastBetTime = contractMostRecentBet[c.id]?.createdTime
|
||||
if (lastBetTime && lastBetTime > seenTime) {
|
||||
return 2
|
||||
}
|
||||
|
||||
return seenTime
|
||||
})
|
||||
|
||||
return prioritizedContracts.slice(0, MAX_ACTIVE_CONTRACTS)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { Contract, getActiveContracts } from '../lib/firebase/contracts'
|
|||
import { listAllFolds } from '../lib/firebase/folds'
|
||||
import { useInactiveContracts } from './use-contracts'
|
||||
import { useFollowedFolds } from './use-fold'
|
||||
import { useSeenContracts } from './use-seen-contracts'
|
||||
import { useUserBetContracts } from './use-user-bets'
|
||||
|
||||
// used in static props
|
||||
|
@ -95,10 +96,13 @@ export const useFindActiveContracts = (props: {
|
|||
}) => {
|
||||
const { contracts, recentBets, recentComments } = props
|
||||
|
||||
const seenContracts = useSeenContracts()
|
||||
|
||||
const activeContracts = findActiveContracts(
|
||||
contracts,
|
||||
recentComments,
|
||||
recentBets
|
||||
recentBets,
|
||||
seenContracts
|
||||
)
|
||||
|
||||
const betsByContract = _.groupBy(recentBets, (bet) => bet.contractId)
|
||||
|
|
29
web/hooks/use-is-visible.ts
Normal file
29
web/hooks/use-is-visible.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { RefObject, useEffect, useState } from 'react'
|
||||
|
||||
export function useIsVisible(elementRef: RefObject<Element>) {
|
||||
return !!useIntersectionObserver(elementRef)?.isIntersecting
|
||||
}
|
||||
|
||||
function useIntersectionObserver(
|
||||
elementRef: RefObject<Element>
|
||||
): IntersectionObserverEntry | undefined {
|
||||
const [entry, setEntry] = useState<IntersectionObserverEntry>()
|
||||
|
||||
const updateEntry = ([entry]: IntersectionObserverEntry[]): void => {
|
||||
setEntry(entry)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const node = elementRef?.current
|
||||
const hasIOSupport = !!window.IntersectionObserver
|
||||
|
||||
if (!hasIOSupport || !node) return
|
||||
|
||||
const observer = new IntersectionObserver(updateEntry, {})
|
||||
observer.observe(node)
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [elementRef])
|
||||
|
||||
return entry
|
||||
}
|
42
web/hooks/use-seen-contracts.ts
Normal file
42
web/hooks/use-seen-contracts.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import _ from 'lodash'
|
||||
import { useEffect, RefObject, useState } from 'react'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { useIsVisible } from './use-is-visible'
|
||||
|
||||
export const useSeenContracts = () => {
|
||||
const [seenContracts, setSeenContracts] = useState<{
|
||||
[contractId: string]: number
|
||||
}>({})
|
||||
|
||||
useEffect(() => {
|
||||
setSeenContracts(getSeenContracts())
|
||||
}, [])
|
||||
|
||||
return seenContracts
|
||||
}
|
||||
|
||||
export const useSaveSeenContract = (
|
||||
ref: RefObject<Element>,
|
||||
contract: Contract
|
||||
) => {
|
||||
const isVisible = useIsVisible(ref)
|
||||
|
||||
useEffect(() => {
|
||||
if (isVisible) {
|
||||
const newSeenContracts = {
|
||||
...getSeenContracts(),
|
||||
[contract.id]: Date.now(),
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(newSeenContracts))
|
||||
}
|
||||
}, [isVisible, contract])
|
||||
}
|
||||
|
||||
const key = 'feed-seen-contracts'
|
||||
|
||||
const getSeenContracts = () => {
|
||||
return _.mapValues(
|
||||
JSON.parse(localStorage.getItem(key) ?? '{}'),
|
||||
(time) => +time
|
||||
)
|
||||
}
|
|
@ -55,7 +55,7 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) {
|
|||
contracts.map((contract) => listAllBets(contract.id))
|
||||
)
|
||||
|
||||
let activeContracts = findActiveContracts(contracts, [], _.flatten(bets))
|
||||
let activeContracts = findActiveContracts(contracts, [], _.flatten(bets), {})
|
||||
const [resolved, unresolved] = _.partition(
|
||||
activeContracts,
|
||||
({ isResolved }) => isResolved
|
||||
|
|
Loading…
Reference in New Issue
Block a user