From 63226bb4c449c73d2abf30da0429c254c8722017 Mon Sep 17 00:00:00 2001 From: mantikoros <95266179+mantikoros@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:13:12 -0600 Subject: [PATCH] New feed improvements (#3218) * truncate if too long * add limit orders * add to sidebar * shrink imgs on mobile * activity in bottom bar * max w * comment divider * styling * Only hide img on desktop if single bet * infinite scroll --- backend/api/src/get-site-activity.ts | 18 ++-- common/src/api/schema.ts | 1 + web/components/nav/bottom-nav-bar.tsx | 8 +- web/components/nav/sidebar.tsx | 1 + web/components/site-activity.tsx | 135 ++++++++++++++++++-------- 5 files changed, 109 insertions(+), 54 deletions(-) diff --git a/backend/api/src/get-site-activity.ts b/backend/api/src/get-site-activity.ts index 93425de01d..aeec2bf454 100644 --- a/backend/api/src/get-site-activity.ts +++ b/backend/api/src/get-site-activity.ts @@ -11,7 +11,7 @@ import { convertContractComment } from 'common/supabase/comments' export const getSiteActivity: APIHandler<'get-site-activity'> = async ( props ) => { - const { limit, blockedGroupSlugs = [], blockedContractIds = [] } = props + const { limit, offset = 0, blockedGroupSlugs = [], blockedContractIds = [] } = props log('getSiteActivity called', { limit }) const blockedUserIds = [ @@ -35,8 +35,8 @@ export const getSiteActivity: APIHandler<'get-site-activity'> = async ( and is_api is not true and user_id != all($1) and contract_id != all($2) - order by created_time desc limit $3`, - [blockedUserIds, blockedContractIds, limit] + order by created_time desc limit $3 offset $4`, + [blockedUserIds, blockedContractIds, limit, offset] ), pg.manyOrNone( `select * from contract_bets @@ -54,16 +54,16 @@ export const getSiteActivity: APIHandler<'get-site-activity'> = async ( and (data->>'isCancelled')::boolean = false and user_id != all($1) and contract_id != all($2) - order by created_time desc limit $3`, - [blockedUserIds, blockedContractIds, limit] + order by created_time desc limit $3 offset $4`, + [blockedUserIds, blockedContractIds, limit, offset] ), pg.manyOrNone( `select * from contract_comments where (likes - coalesce(dislikes, 0)) >= 2 and user_id != all($1) and contract_id != all($2) - order by created_time desc limit $3`, - [blockedUserIds, blockedContractIds, limit] + order by created_time desc limit $3 offset $4`, + [blockedUserIds, blockedContractIds, limit, offset] ), pg.manyOrNone( `select * from contracts @@ -77,8 +77,8 @@ export const getSiteActivity: APIHandler<'get-site-activity'> = async ( where gc.contract_id = contracts.id and g.slug = any($3) ) - order by created_time desc limit $4`, - [blockedUserIds, blockedContractIds, blockedGroupSlugs, limit] + order by created_time desc limit $4 offset $5`, + [blockedUserIds, blockedContractIds, blockedGroupSlugs, limit, offset] ), ]) diff --git a/common/src/api/schema.ts b/common/src/api/schema.ts index cc136edbe9..d1e609be88 100644 --- a/common/src/api/schema.ts +++ b/common/src/api/schema.ts @@ -2000,6 +2000,7 @@ export const API = (_apiTypeCheck = { props: z .object({ limit: z.coerce.number().default(10), + offset: z.coerce.number().default(0), blockedUserIds: z.array(z.string()).optional(), blockedGroupSlugs: z.array(z.string()).optional(), blockedContractIds: z.array(z.string()).optional(), diff --git a/web/components/nav/bottom-nav-bar.tsx b/web/components/nav/bottom-nav-bar.tsx index 5765557e13..416363ad5f 100644 --- a/web/components/nav/bottom-nav-bar.tsx +++ b/web/components/nav/bottom-nav-bar.tsx @@ -7,7 +7,7 @@ import { TransitionChild, } from '@headlessui/react' import { - GlobeAltIcon, + LightningBoltIcon, NewspaperIcon, QuestionMarkCircleIcon, SearchIcon, @@ -48,9 +48,9 @@ function getNavigation(user: User) { icon: SearchIcon, }, { - name: 'Explore', - href: '/explore', - icon: GlobeAltIcon, + name: 'Activity', + href: '/activity', + icon: LightningBoltIcon, }, { name: 'Profile', diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index d541faf9c4..c024b7d614 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -211,6 +211,7 @@ const getMobileNav = ( return buildArray( { name: 'Activity', href: '/activity', icon: LightningBoltIcon }, { name: 'Leagues', href: '/leagues', icon: TrophyIcon }, + { name: 'Explore', href: '/explore', icon: GlobeAltIcon }, // { // name: 'AI', // href: '/ai', diff --git a/web/components/site-activity.tsx b/web/components/site-activity.tsx index 29de4b43d4..e492aab663 100644 --- a/web/components/site-activity.tsx +++ b/web/components/site-activity.tsx @@ -1,8 +1,8 @@ import clsx from 'clsx' import { ContractComment } from 'common/comment' import { Contract } from 'common/contract' -import { groupBy, keyBy, orderBy } from 'lodash' -import { memo } from 'react' +import { groupBy, keyBy, orderBy, uniqBy } from 'lodash' +import { memo, useEffect, useState } from 'react' import { usePrivateUser } from 'web/hooks/use-user' import { ContractMention } from './contract/contract-mention' import { FeedBet } from './feed/feed-bets' @@ -15,6 +15,7 @@ import { LoadingIndicator } from './widgets/loading-indicator' import { UserLink } from './widgets/user-link' import { UserHovercard } from './user/user-hovercard' import { useAPIGetter } from 'web/hooks/use-api-getter' +import { VisibilityObserver } from './widgets/visibility-observer' export function SiteActivity(props: { className?: string @@ -29,16 +30,36 @@ export function SiteActivity(props: { props.blockedUserIds ?? [] ) + const [offset, setOffset] = useState(0) + const limit = 10 + const { data, loading } = useAPIGetter('get-site-activity', { - limit: 10, + limit, + offset, blockedUserIds, blockedGroupSlugs, blockedContractIds, }) - if (loading || !data) return + const [allData, setAllData] = useState() - const { bets, comments, newContracts, relatedContracts } = data + useEffect(() => { + if (data) { + setAllData((prev) => { + if (!prev) return data + return { + bets: uniqBy([...prev.bets, ...data.bets], 'id'), + comments: uniqBy([...prev.comments, ...data.comments], 'id'), + newContracts: uniqBy([...prev.newContracts, ...data.newContracts], 'id'), + relatedContracts: uniqBy([...prev.relatedContracts, ...data.relatedContracts], 'id') + } + }) + } + }, [data]) + + if (!allData) return + + const { bets, comments, newContracts, relatedContracts } = allData const contracts = [...newContracts, ...relatedContracts] const contractsById = keyBy(contracts, 'id') @@ -76,7 +97,7 @@ export function SiteActivity(props: {
- {items.map((item) => + {items.map((item, i) => 'amount' in item ? ( ) : 'channelId' in item ? null : ( - + ) )}
+ {contract.coverImageUrl && ( )} @@ -109,6 +142,17 @@ export function SiteActivity(props: { ) })} +
+ {loading && } + { + if (visible && !loading) { + setOffset((prev: number) => prev + limit) + } + }} + /> +
) } @@ -126,29 +170,31 @@ const MarketCreatedLog = memo( return ( - - - - - - created - + + + + + + +
created this market
+ + -
+ {showDescription && props.contract.description && ( -
+
- + - - - - + + - {' '} - commented - - + + +
commented
+ + +
+ {showDivider &&
} ) })