Merge branch 'main' into group-adding-removing
This commit is contained in:
commit
7d39f27786
|
@ -20,6 +20,7 @@ import { Text } from '@tiptap/extension-text'
|
|||
// other tiptap extensions
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import { Link } from '@tiptap/extension-link'
|
||||
import Iframe from './tiptap-iframe'
|
||||
|
||||
export function parseTags(text: string) {
|
||||
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
|
||||
|
@ -80,6 +81,7 @@ export const exhibitExts = [
|
|||
|
||||
Image,
|
||||
Link,
|
||||
Iframe,
|
||||
]
|
||||
// export const exhibitExts = [StarterKit as unknown as Extension, Image]
|
||||
|
||||
|
|
92
common/util/tiptap-iframe.ts
Normal file
92
common/util/tiptap-iframe.ts
Normal 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
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
|
@ -17,6 +17,7 @@ initAdmin()
|
|||
|
||||
const adminFirestore = admin.firestore()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const addGroupIdToContracts = async () => {
|
||||
const groups = await getValues<Group>(adminFirestore.collection('groups'))
|
||||
const contracts = await getValues<Contract>(
|
||||
|
|
|
@ -39,11 +39,12 @@ const indexPrefix = ENV === 'DEV' ? 'dev-' : ''
|
|||
|
||||
const sortIndexes = [
|
||||
{ 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 traded', value: indexPrefix + 'contracts-most-traded' },
|
||||
{ label: '24h volume', value: indexPrefix + 'contracts-24-hour-vol' },
|
||||
{ label: 'Last updated', value: indexPrefix + 'contracts-last-updated' },
|
||||
{ label: 'Subsidy', value: indexPrefix + 'contracts-liquidity' },
|
||||
{ label: 'Close date', value: indexPrefix + 'contracts-close-date' },
|
||||
{ label: 'Resolve date', value: indexPrefix + 'contracts-resolve-date' },
|
||||
]
|
||||
|
|
|
@ -49,7 +49,7 @@ export function ContractLeaderboard(props: {
|
|||
|
||||
return users && users.length > 0 ? (
|
||||
<Leaderboard
|
||||
title="🏅 Top bettors"
|
||||
title="🏅 Top traders"
|
||||
users={users || []}
|
||||
columns={[
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ import { useMutation } from 'react-query'
|
|||
import { exhibitExts } from 'common/util/parse'
|
||||
import { FileUploadButton } from './file-upload-button'
|
||||
import { linkClass } from './site-link'
|
||||
import Iframe from 'common/util/tiptap-iframe'
|
||||
|
||||
const proseClass = clsx(
|
||||
'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),
|
||||
},
|
||||
}),
|
||||
Iframe,
|
||||
],
|
||||
content: defaultValue,
|
||||
})
|
||||
|
@ -69,12 +71,20 @@ export function useTextEditor(props: {
|
|||
(file) => file.type.startsWith('image')
|
||||
)
|
||||
|
||||
if (!imageFiles.length) {
|
||||
return // if no files pasted, use default paste handler
|
||||
if (imageFiles.length) {
|
||||
event.preventDefault()
|
||||
upload.mutate(imageFiles)
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
upload.mutate(imageFiles)
|
||||
// If the pasted content is iframe code, directly inject it
|
||||
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
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { sortBy, sumBy, uniqBy } from 'lodash'
|
||||
import clsx from 'clsx'
|
||||
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 { Row } from 'web/components/layout/row'
|
||||
import { Page } from 'web/components/page'
|
||||
|
@ -16,11 +19,11 @@ import { useRouter } from 'next/router'
|
|||
import Custom404 from '../404'
|
||||
import { useCharityTxns } from 'web/hooks/use-charity-txns'
|
||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||
import Confetti from 'react-confetti'
|
||||
import { Donation } from 'web/components/charity/feed-items'
|
||||
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 { SEO } from 'web/components/SEO'
|
||||
|
||||
export default function CharityPageWrapper() {
|
||||
const router = useRouter()
|
||||
|
@ -63,6 +66,11 @@ function CharityPage(props: { charity: Charity }) {
|
|||
/>
|
||||
}
|
||||
>
|
||||
<SEO
|
||||
title={name}
|
||||
description={description}
|
||||
url="/groups"
|
||||
/>
|
||||
{showConfetti && (
|
||||
<Confetti
|
||||
width={width ? width : 500}
|
||||
|
|
|
@ -23,6 +23,7 @@ import { searchInAny } from 'common/util/parse'
|
|||
import { getUser } from 'web/lib/firebase/users'
|
||||
import { SiteLink } from 'web/components/site-link'
|
||||
import { User } from 'common/user'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
|
||||
export async function getStaticProps() {
|
||||
const txns = await getAllCharityTxns()
|
||||
|
@ -114,6 +115,11 @@ export default function Charity(props: {
|
|||
|
||||
return (
|
||||
<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="">
|
||||
<Title className="!mt-0" text="Manifold for Charity" />
|
||||
|
@ -128,6 +134,9 @@ export default function Charity(props: {
|
|||
</SiteLink>
|
||||
!
|
||||
</span> */}
|
||||
<span className="text-gray-600">
|
||||
Convert your M$ earnings into real charitable donations.
|
||||
</span>
|
||||
<DonatedStats
|
||||
stats={[
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@ import { TextEditor, useTextEditor } from 'web/components/editor'
|
|||
import { Checkbox } from 'web/components/checkbox'
|
||||
import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
|
||||
import { Title } from 'web/components/title'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
|
||||
export const getServerSideProps = redirectIfLoggedOut('/')
|
||||
|
||||
|
@ -63,6 +64,11 @@ export default function Create() {
|
|||
|
||||
return (
|
||||
<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="rounded-lg px-6 py-4 sm:py-0">
|
||||
<Title className="!mt-0" text="Create a market" />
|
||||
|
|
|
@ -492,7 +492,7 @@ function GroupLeaderboards(props: {
|
|||
<SortedLeaderboard
|
||||
users={members}
|
||||
scoreFunction={(user) => traderScores[user.id] ?? 0}
|
||||
title="🏅 Top bettors"
|
||||
title="🏅 Top traders"
|
||||
header="Profit"
|
||||
maxToShow={maxToShow}
|
||||
/>
|
||||
|
@ -508,7 +508,7 @@ function GroupLeaderboards(props: {
|
|||
<>
|
||||
<Leaderboard
|
||||
className="max-w-xl"
|
||||
title="🏅 Top bettors"
|
||||
title="🏅 Top traders"
|
||||
users={topTraders}
|
||||
columns={[
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@ import { Avatar } from 'web/components/avatar'
|
|||
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
|
||||
import { UserLink } from 'web/components/user-page'
|
||||
import { searchInAny } from 'common/util/parse'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
|
||||
export async function getStaticProps() {
|
||||
const groups = await listAllGroups().catch((_) => [])
|
||||
|
@ -100,6 +101,11 @@ export default function Groups(props: {
|
|||
|
||||
return (
|
||||
<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="w-full max-w-2xl px-4 sm:px-2">
|
||||
<Row className="items-center justify-between">
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Col } from 'web/components/layout/col'
|
|||
import { ManifoldLogo } from 'web/components/nav/manifold-logo'
|
||||
import { redirectIfLoggedIn } from 'web/lib/firebase/server-auth'
|
||||
import { useSaveReferral } from 'web/hooks/use-save-referral'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
|
||||
export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => {
|
||||
// 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 (
|
||||
<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">
|
||||
<ManifoldLogo />
|
||||
</div>
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useEffect, useState } from 'react'
|
|||
import { Title } from 'web/components/title'
|
||||
import { Tabs } from 'web/components/layout/tabs'
|
||||
import { useTracking } from 'web/hooks/use-tracking'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
|
||||
export async function getStaticProps() {
|
||||
const props = await fetchProps()
|
||||
|
@ -78,7 +79,7 @@ export default function Leaderboards(_props: {
|
|||
<>
|
||||
<Col className="mx-4 items-center gap-10 lg:flex-row">
|
||||
<Leaderboard
|
||||
title="🏅 Top bettors"
|
||||
title="🏅 Top traders"
|
||||
users={topTraders}
|
||||
columns={[
|
||||
{
|
||||
|
@ -123,6 +124,11 @@ export default function Leaderboards(_props: {
|
|||
|
||||
return (
|
||||
<Page>
|
||||
<SEO
|
||||
title="Leaderboards"
|
||||
description="Manifold's leaderboards show the top traders and market creators."
|
||||
url="/leaderboards"
|
||||
/>
|
||||
<Title text={'Leaderboards'} className={'hidden md:block'} />
|
||||
<Tabs
|
||||
currentPageForAnalytics={'leaderboards'}
|
||||
|
|
|
@ -8,7 +8,7 @@ export default function Markets() {
|
|||
<Page>
|
||||
<SEO
|
||||
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"
|
||||
/>
|
||||
<ContractSearch />
|
||||
|
|
|
@ -391,7 +391,7 @@ function IncomeNotificationItem(props: {
|
|||
reasonText = !simple
|
||||
? `Bonus for ${
|
||||
parseInt(sourceText) / UNIQUE_BETTOR_BONUS_AMOUNT
|
||||
} unique bettors`
|
||||
} unique traders`
|
||||
: 'bonus on'
|
||||
} else if (sourceType === 'tip') {
|
||||
reasonText = !simple ? `tipped you` : `in tips on`
|
||||
|
|
|
@ -21,7 +21,12 @@ export default function ReferralsPage() {
|
|||
|
||||
return (
|
||||
<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="h-full rounded bg-white p-4 py-8 sm:p-8 sm:shadow-md">
|
||||
|
|
Loading…
Reference in New Issue
Block a user