From 77732341384f4a6e816c3a91c6e4984ebcac5f8a Mon Sep 17 00:00:00 2001 From: James Grugett Date: Thu, 25 Aug 2022 17:21:51 -0500 Subject: [PATCH 001/239] Add debug console.log --- web/components/auth-context.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/components/auth-context.tsx b/web/components/auth-context.tsx index f62c10a2..6957d062 100644 --- a/web/components/auth-context.tsx +++ b/web/components/auth-context.tsx @@ -47,6 +47,7 @@ export function AuthProvider(props: { useEffect(() => { return onIdTokenChanged(auth, async (fbUser) => { + console.log('onIdTokenChanged', fbUser) if (fbUser) { setTokenCookies({ id: await fbUser.getIdToken(), From 0f49effade2f3de61acdb68cb89cb7e98bc2b8bc Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Thu, 25 Aug 2022 19:17:50 -0700 Subject: [PATCH 002/239] Tweak Featured badge design --- web/components/contract/FeaturedContractBadge.tsx | 9 --------- web/components/contract/contract-details.tsx | 2 +- web/components/contract/featured-contract-badge.tsx | 9 +++++++++ 3 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 web/components/contract/FeaturedContractBadge.tsx create mode 100644 web/components/contract/featured-contract-badge.tsx diff --git a/web/components/contract/FeaturedContractBadge.tsx b/web/components/contract/FeaturedContractBadge.tsx deleted file mode 100644 index 5ef34f4a..00000000 --- a/web/components/contract/FeaturedContractBadge.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { SparklesIcon } from '@heroicons/react/solid' - -export function FeaturedContractBadge() { - return ( - - - ) -} diff --git a/web/components/contract/contract-details.tsx b/web/components/contract/contract-details.tsx index 56407c4d..4b73a227 100644 --- a/web/components/contract/contract-details.tsx +++ b/web/components/contract/contract-details.tsx @@ -33,7 +33,7 @@ import { insertContent } from '../editor/utils' import clsx from 'clsx' import { contractMetrics } from 'common/contract-details' import { User } from 'common/user' -import { FeaturedContractBadge } from 'web/components/contract/FeaturedContractBadge' +import { FeaturedContractBadge } from 'web/components/contract/featured-contract-badge' export type ShowTime = 'resolve-date' | 'close-date' diff --git a/web/components/contract/featured-contract-badge.tsx b/web/components/contract/featured-contract-badge.tsx new file mode 100644 index 00000000..fe31ecb9 --- /dev/null +++ b/web/components/contract/featured-contract-badge.tsx @@ -0,0 +1,9 @@ +import { BadgeCheckIcon } from '@heroicons/react/solid' + +export function FeaturedContractBadge() { + return ( + + + ) +} From 4faab4fcdc33abd262ad5a051904c660a0237dd3 Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Thu, 25 Aug 2022 19:42:40 -0700 Subject: [PATCH 003/239] Clean up Featured code --- .../contract/contract-info-dialog.tsx | 58 +++++++------------ web/components/widgets/short-toggle.tsx | 8 ++- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/web/components/contract/contract-info-dialog.tsx b/web/components/contract/contract-info-dialog.tsx index 7c35a071..5c66aa4c 100644 --- a/web/components/contract/contract-info-dialog.tsx +++ b/web/components/contract/contract-info-dialog.tsx @@ -17,6 +17,7 @@ import { useAdmin, useDev } from 'web/hooks/use-admin' import { SiteLink } from '../site-link' import { firestoreConsolePath } from 'common/envs/constants' import { deleteField } from 'firebase/firestore' +import ShortToggle from '../widgets/short-toggle' export const contractDetailsButtonClassName = 'group flex items-center rounded-md px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-100 text-gray-400 hover:text-gray-500' @@ -50,6 +51,21 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) { ? 'Multiple choice' : 'Numeric' + const onFeaturedToggle = async (enabled: boolean) => { + if ( + enabled && + (contract.featuredOnHomeRank === 0 || !contract?.featuredOnHomeRank) + ) { + await updateContract(id, { featuredOnHomeRank: 1 }) + setFeatured(true) + } else if (!enabled && (contract?.featuredOnHomeRank ?? 0) > 0) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await updateContract(id, { featuredOnHomeRank: deleteField() }) + setFeatured(false) + } + } + return ( <> From 31db33031968cad55daa13e0ce57a85837a38e4c Mon Sep 17 00:00:00 2001 From: James Grugett Date: Fri, 26 Aug 2022 11:38:08 -0500 Subject: [PATCH 011/239] Show "(max)" on streak payout if it is the max payout --- web/pages/notifications.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/pages/notifications.tsx b/web/pages/notifications.tsx index ca5e1827..bfd18f7f 100644 --- a/web/pages/notifications.tsx +++ b/web/pages/notifications.tsx @@ -400,7 +400,8 @@ function IncomeNotificationItem(props: { } else if (sourceType === 'tip') { reasonText = !simple ? `tipped you on` : `in tips on` } else if (sourceType === 'betting_streak_bonus') { - reasonText = 'for your' + if (sourceText && +sourceText === 50) reasonText = '(max) for your' + else reasonText = 'for your' } else if (sourceType === 'loan' && sourceText) { reasonText = `of your invested bets returned as a` } From 803091db066c07b296cff1bee43fb0d8333f97b0 Mon Sep 17 00:00:00 2001 From: Sinclair Chen Date: Fri, 26 Aug 2022 10:31:25 -0700 Subject: [PATCH 012/239] Add tournament home page (#797) * Add tournament home page * Preload markets, follow count * organize imports * Fix card width * Make entire title clickable * plural /tournament -> /tournaments * prettier * Fix /tournaments when groupIds are invalid * Restyle /tournaments page * Reintroduce Salem, tweak styles Co-authored-by: Austin Chen --- web/components/contract/contract-card.tsx | 15 +- web/pages/tournaments/index.tsx | 236 ++++++++++++++++++++++ 2 files changed, 244 insertions(+), 7 deletions(-) create mode 100644 web/pages/tournaments/index.tsx diff --git a/web/components/contract/contract-card.tsx b/web/components/contract/contract-card.tsx index 090020e0..056801eb 100644 --- a/web/components/contract/contract-card.tsx +++ b/web/components/contract/contract-card.tsx @@ -38,6 +38,7 @@ export function ContractCard(props: { showHotVolume?: boolean showTime?: ShowTime className?: string + questionClass?: string onClick?: () => void hideQuickBet?: boolean hideGroupLink?: boolean @@ -46,6 +47,7 @@ export function ContractCard(props: { showHotVolume, showTime, className, + questionClass, onClick, hideQuickBet, hideGroupLink, @@ -68,7 +70,7 @@ export function ContractCard(props: { return ( @@ -105,7 +107,10 @@ export function ContractCard(props: { className={'hidden md:inline-flex'} />

{question} @@ -165,11 +170,7 @@ export function ContractCard(props: { showQuickBet ? 'w-[85%]' : 'w-full' )} > - + dayjs(d, 'MMM D, YYYY').tz('America/Los_Angeles') + +type Tourney = { + title: string + url?: string + blurb: string // actual description in the click-through + award?: string + endTime?: Dayjs + groupId: string +} + +const Salem = { + title: 'CSPI/Salem Forecasting Tournament', + blurb: 'Top 5 traders qualify for a UT Austin research fellowship.', + url: 'https://salemcenter.manifold.markets/', + award: '$25,000', + endTime: toDate('Jul 31, 2023'), +} as const + +const tourneys: Tourney[] = [ + { + title: 'Cause Exploration Prizes', + blurb: + 'Which new charity ideas will Open Philanthropy find most promising?', + award: 'M$100k', + endTime: toDate('Sep 9, 2022'), + groupId: 'cMcpBQ2p452jEcJD2SFw', + }, + { + title: 'Fantasy Football Stock Exchange', + blurb: 'How many points will each NFL player score this season?', + award: '$2,500', + endTime: toDate('Jan 6, 2023'), + groupId: 'SxGRqXRpV3RAQKudbcNb', + }, + // { + // title: 'Clearer Thinking Regrant Project', + // blurb: 'Something amazing', + // award: '$10,000', + // endTime: toDate('Sep 22, 2022'), + // groupId: '2VsVVFGhKtIdJnQRAXVb', + // }, +] + +export async function getStaticProps() { + const groupIds = tourneys + .map((data) => data.groupId) + .filter((id) => id != undefined) as string[] + const groups = (await Promise.all(groupIds.map(getGroup))) + // Then remove undefined groups + .filter(Boolean) as Group[] + + const contracts = await Promise.all( + groups.map((g) => listContractsByGroupSlug(g?.slug ?? '')) + ) + + const markets = Object.fromEntries(groups.map((g, i) => [g.id, contracts[i]])) + + const groupMap = keyBy(groups, 'id') + const numPeople = mapValues(groupMap, (g) => g?.memberIds.length) + const slugs = mapValues(groupMap, 'slug') + + return { props: { markets, numPeople, slugs }, revalidate: 60 * 10 } +} + +export default function TournamentPage(props: { + markets: { [groupId: string]: Contract[] } + numPeople: { [groupId: string]: number } + slugs: { [groupId: string]: string } +}) { + const { markets = {}, numPeople = {}, slugs = {} } = props + + return ( + + + + {tourneys.map(({ groupId, ...data }) => ( +

+ ))} +
+ + + ) +} + +function Section(props: { + title: string + url: string + blurb: string + award?: string + ppl?: number + endTime?: Dayjs + markets: Contract[] +}) { + const { title, url, blurb, award, ppl, endTime, markets } = props + + return ( +
+ + +

+ {title} +

+ + {!!award && 🏆 {award}} + {!!ppl && ( + + + {ppl} + + )} + {endTime && ( + + + + {endTime.format('MMM D')} + + + )} + +
+ + {blurb} + +
+ {markets.length ? ( + markets.map((m) => ( + + )) + ) : ( +
+ Coming Soon... +
+ )} + +
+ ) +} + +function Carousel(props: { children: ReactNode; className?: string }) { + const { children, className } = props + + const ref = useRef(null) + + const th = (f: () => any) => throttle(f, 500, { trailing: false }) + const scrollLeft = th(() => + ref.current?.scrollBy({ left: -ref.current.clientWidth }) + ) + const scrollRight = th(() => + ref.current?.scrollBy({ left: ref.current.clientWidth }) + ) + + const [atFront, setAtFront] = useState(true) + const [atBack, setAtBack] = useState(false) + const onScroll = throttle(() => { + if (ref.current) { + const { scrollLeft, clientWidth, scrollWidth } = ref.current + setAtFront(scrollLeft < 80) + setAtBack(scrollWidth - (clientWidth + scrollLeft) < 80) + } + }, 500) + + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(onScroll, []) + + return ( +
+ + {children} + + {!atFront && ( +
+ +
+ )} + {!atBack && ( +
+ +
+ )} +
+ ) +} From 490115d8901fd4ea60720a94a01b80afd848d3b1 Mon Sep 17 00:00:00 2001 From: Sinclair Chen Date: Fri, 26 Aug 2022 10:45:01 -0700 Subject: [PATCH 013/239] Add tournaments to sidebar (#802) * Add tournaments to sidebar * Remove unused import * Reposition tournaments tab Co-authored-by: Austin Chen --- web/components/nav/sidebar.tsx | 29 +++++++++++------------------ web/lib/icons/trophy-icon.tsx | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 web/lib/icons/trophy-icon.tsx diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index e16a502e..915ceea1 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -6,7 +6,6 @@ import { CashIcon, HeartIcon, UserGroupIcon, - TrendingUpIcon, ChatIcon, } from '@heroicons/react/outline' import clsx from 'clsx' @@ -29,6 +28,7 @@ import { Spacer } from '../layout/spacer' import { useWindowSize } from 'web/hooks/use-window-size' import { CHALLENGES_ENABLED } from 'common/challenge' import { buildArray } from 'common/util/array' +import TrophyIcon from 'web/lib/icons/trophy-icon' const logout = async () => { // log out, and then reload the page, in case SSR wants to boot them out @@ -46,11 +46,12 @@ function getNavigation() { icon: NotificationsIcon, }, - { name: 'Leaderboards', href: '/leaderboards', icon: TrendingUpIcon }, - ...(IS_PRIVATE_MANIFOLD ? [] - : [{ name: 'Get M$', href: '/add-funds', icon: CashIcon }]), + : [ + { name: 'Get M$', href: '/add-funds', icon: CashIcon }, + { name: 'Tournaments', href: '/tournaments', icon: TrophyIcon }, + ]), ] } @@ -70,11 +71,9 @@ function getMoreNavigation(user?: User | null) { return buildArray( CHALLENGES_ENABLED && { name: 'Challenges', href: '/challenges' }, [ + { name: 'Leaderboards', href: '/leaderboards' }, + { name: 'Tournaments', href: '/tournaments' }, { name: 'Charity', href: '/charity' }, - { - name: 'Salem tournament', - href: 'https://salemcenter.manifold.markets/', - }, { name: 'Blog', href: 'https://news.manifold.markets' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, { name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' }, @@ -86,12 +85,9 @@ function getMoreNavigation(user?: User | null) { CHALLENGES_ENABLED && { name: 'Challenges', href: '/challenges' }, [ { name: 'Referrals', href: '/referrals' }, + { name: 'Leaderboards', href: '/leaderboards' }, { name: 'Charity', href: '/charity' }, { name: 'Send M$', href: '/links' }, - { - name: 'Salem tournament', - href: 'https://salemcenter.manifold.markets/', - }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, { name: 'Help & About', href: 'https://help.manifold.markets/' }, { @@ -120,12 +116,12 @@ const signedOutMobileNavigation = [ icon: BookOpenIcon, }, { name: 'Charity', href: '/charity', icon: HeartIcon }, - { name: 'Leaderboards', href: '/leaderboards', icon: TrendingUpIcon }, + { name: 'Tournaments', href: '/tournaments', icon: TrophyIcon }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh', icon: ChatIcon }, ] const signedInMobileNavigation = [ - { name: 'Leaderboards', href: '/leaderboards', icon: TrendingUpIcon }, + { name: 'Tournaments', href: '/tournaments', icon: TrophyIcon }, ...(IS_PRIVATE_MANIFOLD ? [] : [{ name: 'Get M$', href: '/add-funds', icon: CashIcon }]), @@ -148,10 +144,7 @@ function getMoreMobileNav() { CHALLENGES_ENABLED && { name: 'Challenges', href: '/challenges' }, [ { name: 'Referrals', href: '/referrals' }, - { - name: 'Salem tournament', - href: 'https://salemcenter.manifold.markets/', - }, + { name: 'Leaderboards', href: '/leaderboards' }, { name: 'Charity', href: '/charity' }, { name: 'Send M$', href: '/links' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, diff --git a/web/lib/icons/trophy-icon.tsx b/web/lib/icons/trophy-icon.tsx new file mode 100644 index 00000000..c845a0af --- /dev/null +++ b/web/lib/icons/trophy-icon.tsx @@ -0,0 +1,27 @@ +export default function TrophyIcon(props: React.SVGProps) { + return ( + + + + + + + + ) +} From ba7d0f45db1427d23597feab26903ce16e3d7213 Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Fri, 26 Aug 2022 12:41:29 -0700 Subject: [PATCH 014/239] Close Add Market modal on Cancel --- web/components/editor/market-modal.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/web/components/editor/market-modal.tsx b/web/components/editor/market-modal.tsx index ea62b960..a81953de 100644 --- a/web/components/editor/market-modal.tsx +++ b/web/components/editor/market-modal.tsx @@ -48,8 +48,17 @@ export function MarketModal(props: { {contracts.length > 1 && 's'} )} - )} From 325580689124048772039e1a8e9b5a240cbb062c Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Fri, 26 Aug 2022 12:41:39 -0700 Subject: [PATCH 015/239] Support Figma embeds --- web/components/editor/embed-modal.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/components/editor/embed-modal.tsx b/web/components/editor/embed-modal.tsx index 6acfd8f0..48ccbfbb 100644 --- a/web/components/editor/embed-modal.tsx +++ b/web/components/editor/embed-modal.tsx @@ -40,6 +40,11 @@ const embedPatterns: EmbedPattern[] = [ rewrite: (id) => ``, }, + { + regex: /^(https?:\/\/www\.figma\.com\/(?:file|proto)\/[^\/]+\/[^\/]+)/, + rewrite: (url) => + ``, + }, // Twitch is a bit annoying, since it requires the `&parent=DOMAIN` to match { // Twitch: https://www.twitch.tv/videos/1445087149 From 8903b1ef95d1a5f54075467240cb4f75517ce40a Mon Sep 17 00:00:00 2001 From: Sinclair Chen Date: Fri, 26 Aug 2022 14:23:06 -0700 Subject: [PATCH 016/239] Replace style props with tailwind classes (#800) * add utility class for `word-break: break-word` * refactor visuallyHidden style out of Page * refactor out ref sizing hack in sidebar * replace style props with tailwind classes --- web/components/advanced-panel.tsx | 38 ----------------------- web/components/analytics/charts.tsx | 13 +++++--- web/components/contract/contract-card.tsx | 3 +- web/components/leaderboard.tsx | 2 +- web/components/linkify.tsx | 5 +-- web/components/nav/sidebar.tsx | 24 ++++++-------- web/components/outcome-label.tsx | 5 +-- web/components/page.tsx | 22 ++----------- web/components/site-link.tsx | 3 +- web/components/user-page.tsx | 5 +-- web/pages/home.tsx | 2 +- web/pages/simulator.tsx | 5 ++- web/pages/stats.tsx | 6 ++-- web/tailwind.config.js | 4 +++ 14 files changed, 35 insertions(+), 102 deletions(-) delete mode 100644 web/components/advanced-panel.tsx diff --git a/web/components/advanced-panel.tsx b/web/components/advanced-panel.tsx deleted file mode 100644 index 51caba67..00000000 --- a/web/components/advanced-panel.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import clsx from 'clsx' -import { useState, ReactNode } from 'react' - -export function AdvancedPanel(props: { children: ReactNode }) { - const { children } = props - const [collapsed, setCollapsed] = useState(true) - - return ( -
-
setCollapsed((collapsed) => !collapsed)} - className="cursor-pointer" - > -
- Advanced -
-
-
- -
- {children} -
-
- ) -} diff --git a/web/components/analytics/charts.tsx b/web/components/analytics/charts.tsx index 2f987d58..56e71257 100644 --- a/web/components/analytics/charts.tsx +++ b/web/components/analytics/charts.tsx @@ -1,4 +1,5 @@ import { Point, ResponsiveLine } from '@nivo/line' +import clsx from 'clsx' import dayjs from 'dayjs' import { zip } from 'lodash' import { useWindowSize } from 'web/hooks/use-window-size' @@ -26,8 +27,10 @@ export function DailyCountChart(props: { return (
= 800) ? 400 : 250 }} + className={clsx( + 'h-[250px] w-full overflow-hidden', + !small && 'md:h-[400px]' + )} > = 800) ? 400 : 250 }} + className={clsx( + 'h-[250px] w-full overflow-hidden', + !small && 'md:h-[400px]' + )} >

{question}

diff --git a/web/components/leaderboard.tsx b/web/components/leaderboard.tsx index b8c725e0..a0670795 100644 --- a/web/components/leaderboard.tsx +++ b/web/components/leaderboard.tsx @@ -40,7 +40,7 @@ export function Leaderboard(props: { {users.map((user, index) => ( {index + 1} - + diff --git a/web/components/linkify.tsx b/web/components/linkify.tsx index f33b2bf5..b4f05165 100644 --- a/web/components/linkify.tsx +++ b/web/components/linkify.tsx @@ -38,10 +38,7 @@ export function Linkify(props: { text: string; gray?: boolean }) { ) }) return ( - + {text.split(regex).map((part, i) => ( {part} diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index 915ceea1..995378ee 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -17,7 +17,7 @@ import { ManifoldLogo } from './manifold-logo' import { MenuButton } from './menu' import { ProfileSummary } from './profile-menu' import NotificationsIcon from 'web/components/notifications-icon' -import React, { useState } from 'react' +import React from 'react' import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants' import { CreateQuestionButton } from 'web/components/create-question-button' import { useMemberGroups } from 'web/hooks/use-group' @@ -25,7 +25,6 @@ import { groupPath } from 'web/lib/firebase/groups' import { trackCallback, withTracking } from 'web/lib/service/analytics' import { Group } from 'common/group' import { Spacer } from '../layout/spacer' -import { useWindowSize } from 'web/hooks/use-window-size' import { CHALLENGES_ENABLED } from 'common/challenge' import { buildArray } from 'common/util/array' import TrophyIcon from 'web/lib/icons/trophy-icon' @@ -235,19 +234,22 @@ export default function Sidebar(props: { className?: string }) { })) return ( -