Merge branch 'main' into group-adding-removing

This commit is contained in:
Ian Philips 2022-07-22 11:27:26 -06:00 committed by GitHub
commit 7d39f27786
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 166 additions and 14 deletions

View File

@ -20,6 +20,7 @@ import { Text } from '@tiptap/extension-text'
// other tiptap extensions // other tiptap extensions
import { Image } from '@tiptap/extension-image' import { Image } from '@tiptap/extension-image'
import { Link } from '@tiptap/extension-link' import { Link } from '@tiptap/extension-link'
import Iframe from './tiptap-iframe'
export function parseTags(text: string) { export function parseTags(text: string) {
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
@ -80,6 +81,7 @@ export const exhibitExts = [
Image, Image,
Link, Link,
Iframe,
] ]
// export const exhibitExts = [StarterKit as unknown as Extension, Image] // export const exhibitExts = [StarterKit as unknown as Extension, Image]

View File

@ -0,0 +1,92 @@
// Adopted from https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/iframe.ts
import { Node } from '@tiptap/core'
export interface IframeOptions {
allowFullscreen: boolean
HTMLAttributes: {
[key: string]: any
}
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
iframe: {
setIframe: (options: { src: string }) => ReturnType
}
}
}
// These classes style the outer wrapper and the inner iframe;
// Adopted from css in https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/index.vue
const wrapperClasses = 'relative h-auto w-full overflow-hidden'
const iframeClasses = 'absolute top-0 left-0 h-full w-full'
export default Node.create<IframeOptions>({
name: 'iframe',
group: 'block',
atom: true,
addOptions() {
return {
allowFullscreen: true,
HTMLAttributes: {
class: 'iframe-wrapper' + ' ' + wrapperClasses,
// Tailwind JIT doesn't seem to pick up `pb-[20rem]`, so we hack this in:
style: 'padding-bottom: 20rem;',
},
}
},
addAttributes() {
return {
src: {
default: null,
},
frameborder: {
default: 0,
},
allowfullscreen: {
default: this.options.allowFullscreen,
parseHTML: () => this.options.allowFullscreen,
},
}
},
parseHTML() {
return [{ tag: 'iframe' }]
},
renderHTML({ HTMLAttributes }) {
return [
'div',
this.options.HTMLAttributes,
[
'iframe',
{
...HTMLAttributes,
class: HTMLAttributes.class + ' ' + iframeClasses,
},
],
]
},
addCommands() {
return {
setIframe:
(options: { src: string }) =>
({ tr, dispatch }) => {
const { selection } = tr
const node = this.type.create(options)
if (dispatch) {
tr.replaceRangeWith(selection.from, selection.to, node)
}
return true
},
}
},
})

View File

@ -17,6 +17,7 @@ initAdmin()
const adminFirestore = admin.firestore() const adminFirestore = admin.firestore()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const addGroupIdToContracts = async () => { const addGroupIdToContracts = async () => {
const groups = await getValues<Group>(adminFirestore.collection('groups')) const groups = await getValues<Group>(adminFirestore.collection('groups'))
const contracts = await getValues<Contract>( const contracts = await getValues<Contract>(

View File

@ -39,11 +39,12 @@ const indexPrefix = ENV === 'DEV' ? 'dev-' : ''
const sortIndexes = [ const sortIndexes = [
{ label: 'Newest', value: indexPrefix + 'contracts-newest' }, { label: 'Newest', value: indexPrefix + 'contracts-newest' },
{ label: 'Oldest', value: indexPrefix + 'contracts-oldest' }, // { label: 'Oldest', value: indexPrefix + 'contracts-oldest' },
{ label: 'Most popular', value: indexPrefix + 'contracts-score' }, { label: 'Most popular', value: indexPrefix + 'contracts-score' },
{ label: 'Most traded', value: indexPrefix + 'contracts-most-traded' }, { label: 'Most traded', value: indexPrefix + 'contracts-most-traded' },
{ label: '24h volume', value: indexPrefix + 'contracts-24-hour-vol' }, { label: '24h volume', value: indexPrefix + 'contracts-24-hour-vol' },
{ label: 'Last updated', value: indexPrefix + 'contracts-last-updated' }, { label: 'Last updated', value: indexPrefix + 'contracts-last-updated' },
{ label: 'Subsidy', value: indexPrefix + 'contracts-liquidity' },
{ label: 'Close date', value: indexPrefix + 'contracts-close-date' }, { label: 'Close date', value: indexPrefix + 'contracts-close-date' },
{ label: 'Resolve date', value: indexPrefix + 'contracts-resolve-date' }, { label: 'Resolve date', value: indexPrefix + 'contracts-resolve-date' },
] ]

View File

@ -49,7 +49,7 @@ export function ContractLeaderboard(props: {
return users && users.length > 0 ? ( return users && users.length > 0 ? (
<Leaderboard <Leaderboard
title="🏅 Top bettors" title="🏅 Top traders"
users={users || []} users={users || []}
columns={[ columns={[
{ {

View File

@ -19,6 +19,7 @@ import { useMutation } from 'react-query'
import { exhibitExts } from 'common/util/parse' import { exhibitExts } from 'common/util/parse'
import { FileUploadButton } from './file-upload-button' import { FileUploadButton } from './file-upload-button'
import { linkClass } from './site-link' import { linkClass } from './site-link'
import Iframe from 'common/util/tiptap-iframe'
const proseClass = clsx( const proseClass = clsx(
'prose prose-p:my-0 prose-li:my-0 prose-blockquote:not-italic max-w-none prose-quoteless leading-relaxed', 'prose prose-p:my-0 prose-li:my-0 prose-blockquote:not-italic max-w-none prose-quoteless leading-relaxed',
@ -56,6 +57,7 @@ export function useTextEditor(props: {
class: clsx('no-underline !text-indigo-700', linkClass), class: clsx('no-underline !text-indigo-700', linkClass),
}, },
}), }),
Iframe,
], ],
content: defaultValue, content: defaultValue,
}) })
@ -69,12 +71,20 @@ export function useTextEditor(props: {
(file) => file.type.startsWith('image') (file) => file.type.startsWith('image')
) )
if (!imageFiles.length) { if (imageFiles.length) {
return // if no files pasted, use default paste handler event.preventDefault()
upload.mutate(imageFiles)
} }
event.preventDefault() // If the pasted content is iframe code, directly inject it
upload.mutate(imageFiles) const text = event.clipboardData?.getData('text/plain').trim() ?? ''
const isValidIframe = /^<iframe.*<\/iframe>$/.test(text)
if (isValidIframe) {
editor.chain().insertContent(text).run()
return true // Prevent the code from getting pasted as text
}
return // Otherwise, use default paste handler
}, },
}, },
}) })

View File

@ -1,6 +1,9 @@
import { sortBy, sumBy, uniqBy } from 'lodash' import { sortBy, sumBy, uniqBy } from 'lodash'
import clsx from 'clsx' import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import Image from 'next/image'
import Confetti from 'react-confetti'
import { Col } from 'web/components/layout/col' import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row' import { Row } from 'web/components/layout/row'
import { Page } from 'web/components/page' import { Page } from 'web/components/page'
@ -16,11 +19,11 @@ import { useRouter } from 'next/router'
import Custom404 from '../404' import Custom404 from '../404'
import { useCharityTxns } from 'web/hooks/use-charity-txns' import { useCharityTxns } from 'web/hooks/use-charity-txns'
import { useWindowSize } from 'web/hooks/use-window-size' import { useWindowSize } from 'web/hooks/use-window-size'
import Confetti from 'react-confetti'
import { Donation } from 'web/components/charity/feed-items' import { Donation } from 'web/components/charity/feed-items'
import Image from 'next/image' import Image from 'next/image'
import { manaToUSD } from 'common/util/format' import { manaToUSD } from 'common/util/format'>>>>>>> main
import { track } from 'web/lib/service/analytics' import { track } from 'web/lib/service/analytics'
import { SEO } from 'web/components/SEO'
export default function CharityPageWrapper() { export default function CharityPageWrapper() {
const router = useRouter() const router = useRouter()
@ -63,6 +66,11 @@ function CharityPage(props: { charity: Charity }) {
/> />
} }
> >
<SEO
title={name}
description={description}
url="/groups"
/>
{showConfetti && ( {showConfetti && (
<Confetti <Confetti
width={width ? width : 500} width={width ? width : 500}

View File

@ -23,6 +23,7 @@ import { searchInAny } from 'common/util/parse'
import { getUser } from 'web/lib/firebase/users' import { getUser } from 'web/lib/firebase/users'
import { SiteLink } from 'web/components/site-link' import { SiteLink } from 'web/components/site-link'
import { User } from 'common/user' import { User } from 'common/user'
import { SEO } from 'web/components/SEO'
export async function getStaticProps() { export async function getStaticProps() {
const txns = await getAllCharityTxns() const txns = await getAllCharityTxns()
@ -114,6 +115,11 @@ export default function Charity(props: {
return ( return (
<Page> <Page>
<SEO
title="Manifold for Charity"
description="Donate your prediction market earnings to charity on Manifold."
url="/charity"
/>
<Col className="w-full rounded px-4 py-6 sm:px-8 xl:w-[125%]"> <Col className="w-full rounded px-4 py-6 sm:px-8 xl:w-[125%]">
<Col className=""> <Col className="">
<Title className="!mt-0" text="Manifold for Charity" /> <Title className="!mt-0" text="Manifold for Charity" />
@ -128,6 +134,9 @@ export default function Charity(props: {
</SiteLink> </SiteLink>
! !
</span> */} </span> */}
<span className="text-gray-600">
Convert your M$ earnings into real charitable donations.
</span>
<DonatedStats <DonatedStats
stats={[ stats={[
{ {

View File

@ -30,6 +30,7 @@ import { TextEditor, useTextEditor } from 'web/components/editor'
import { Checkbox } from 'web/components/checkbox' import { Checkbox } from 'web/components/checkbox'
import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth' import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
import { Title } from 'web/components/title' import { Title } from 'web/components/title'
import { SEO } from 'web/components/SEO'
export const getServerSideProps = redirectIfLoggedOut('/') export const getServerSideProps = redirectIfLoggedOut('/')
@ -63,6 +64,11 @@ export default function Create() {
return ( return (
<Page> <Page>
<SEO
title="Create a market"
description="Create a play-money prediction market on any question."
url="/create"
/>
<div className="mx-auto w-full max-w-2xl"> <div className="mx-auto w-full max-w-2xl">
<div className="rounded-lg px-6 py-4 sm:py-0"> <div className="rounded-lg px-6 py-4 sm:py-0">
<Title className="!mt-0" text="Create a market" /> <Title className="!mt-0" text="Create a market" />

View File

@ -492,7 +492,7 @@ function GroupLeaderboards(props: {
<SortedLeaderboard <SortedLeaderboard
users={members} users={members}
scoreFunction={(user) => traderScores[user.id] ?? 0} scoreFunction={(user) => traderScores[user.id] ?? 0}
title="🏅 Top bettors" title="🏅 Top traders"
header="Profit" header="Profit"
maxToShow={maxToShow} maxToShow={maxToShow}
/> />
@ -508,7 +508,7 @@ function GroupLeaderboards(props: {
<> <>
<Leaderboard <Leaderboard
className="max-w-xl" className="max-w-xl"
title="🏅 Top bettors" title="🏅 Top traders"
users={topTraders} users={topTraders}
columns={[ columns={[
{ {

View File

@ -18,6 +18,7 @@ import { Avatar } from 'web/components/avatar'
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button' import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
import { UserLink } from 'web/components/user-page' import { UserLink } from 'web/components/user-page'
import { searchInAny } from 'common/util/parse' import { searchInAny } from 'common/util/parse'
import { SEO } from 'web/components/SEO'
export async function getStaticProps() { export async function getStaticProps() {
const groups = await listAllGroups().catch((_) => []) const groups = await listAllGroups().catch((_) => [])
@ -100,6 +101,11 @@ export default function Groups(props: {
return ( return (
<Page> <Page>
<SEO
title="Groups"
description="Manifold Groups are communities centered around a collection of prediction markets. Discuss and compete on questions with your friends."
url="/groups"
/>
<Col className="items-center"> <Col className="items-center">
<Col className="w-full max-w-2xl px-4 sm:px-2"> <Col className="w-full max-w-2xl px-4 sm:px-2">
<Row className="items-center justify-between"> <Row className="items-center justify-between">

View File

@ -5,6 +5,7 @@ import { Col } from 'web/components/layout/col'
import { ManifoldLogo } from 'web/components/nav/manifold-logo' import { ManifoldLogo } from 'web/components/nav/manifold-logo'
import { redirectIfLoggedIn } from 'web/lib/firebase/server-auth' import { redirectIfLoggedIn } from 'web/lib/firebase/server-auth'
import { useSaveReferral } from 'web/hooks/use-save-referral' import { useSaveReferral } from 'web/hooks/use-save-referral'
import { SEO } from 'web/components/SEO'
export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => { export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => {
// These hardcoded markets will be shown in the frontpage for signed-out users: // These hardcoded markets will be shown in the frontpage for signed-out users:
@ -30,6 +31,11 @@ export default function Home(props: { hotContracts: Contract[] }) {
return ( return (
<Page> <Page>
<SEO
title="Manifold Markets"
description="Create a play-money prediction market on any topic you care about
and bet with your friends on what will happen!"
/>
<div className="px-4 pt-2 md:mt-0 lg:hidden"> <div className="px-4 pt-2 md:mt-0 lg:hidden">
<ManifoldLogo /> <ManifoldLogo />
</div> </div>

View File

@ -13,6 +13,7 @@ import { useEffect, useState } from 'react'
import { Title } from 'web/components/title' import { Title } from 'web/components/title'
import { Tabs } from 'web/components/layout/tabs' import { Tabs } from 'web/components/layout/tabs'
import { useTracking } from 'web/hooks/use-tracking' import { useTracking } from 'web/hooks/use-tracking'
import { SEO } from 'web/components/SEO'
export async function getStaticProps() { export async function getStaticProps() {
const props = await fetchProps() const props = await fetchProps()
@ -78,7 +79,7 @@ export default function Leaderboards(_props: {
<> <>
<Col className="mx-4 items-center gap-10 lg:flex-row"> <Col className="mx-4 items-center gap-10 lg:flex-row">
<Leaderboard <Leaderboard
title="🏅 Top bettors" title="🏅 Top traders"
users={topTraders} users={topTraders}
columns={[ columns={[
{ {
@ -123,6 +124,11 @@ export default function Leaderboards(_props: {
return ( return (
<Page> <Page>
<SEO
title="Leaderboards"
description="Manifold's leaderboards show the top traders and market creators."
url="/leaderboards"
/>
<Title text={'Leaderboards'} className={'hidden md:block'} /> <Title text={'Leaderboards'} className={'hidden md:block'} />
<Tabs <Tabs
currentPageForAnalytics={'leaderboards'} currentPageForAnalytics={'leaderboards'}

View File

@ -8,7 +8,7 @@ export default function Markets() {
<Page> <Page>
<SEO <SEO
title="Explore" title="Explore"
description="Discover what's new, trending, or soon-to-close. Or search among our hundreds of markets." description="Discover what's new, trending, or soon-to-close. Or search thousands of prediction markets."
url="/markets" url="/markets"
/> />
<ContractSearch /> <ContractSearch />

View File

@ -391,7 +391,7 @@ function IncomeNotificationItem(props: {
reasonText = !simple reasonText = !simple
? `Bonus for ${ ? `Bonus for ${
parseInt(sourceText) / UNIQUE_BETTOR_BONUS_AMOUNT parseInt(sourceText) / UNIQUE_BETTOR_BONUS_AMOUNT
} unique bettors` } unique traders`
: 'bonus on' : 'bonus on'
} else if (sourceType === 'tip') { } else if (sourceType === 'tip') {
reasonText = !simple ? `tipped you` : `in tips on` reasonText = !simple ? `tipped you` : `in tips on`

View File

@ -21,7 +21,12 @@ export default function ReferralsPage() {
return ( return (
<Page> <Page>
<SEO title="Referrals" description="" url="/add-funds" /> <SEO
title="Referrals"
description={`Manifold's referral program. Invite new users to Manifold and get M${REFERRAL_AMOUNT} if they
sign up!`}
url="/referrals"
/>
<Col className="items-center"> <Col className="items-center">
<Col className="h-full rounded bg-white p-4 py-8 sm:p-8 sm:shadow-md"> <Col className="h-full rounded bg-white p-4 py-8 sm:p-8 sm:shadow-md">