Twitch prerelease (#882)
* Bot linking button functional. * Implemented initial prototype of new Twitch signup page. * Removed old Twitch signup page. * Moved new Twitch page to correct URL. * Twitch account linking functional. * Fixed charity link. * Changed to point to live bot server. * Slightly improve spacing and alignment on Twitch page * Tidy up, handle some errors when talking to bot * Seriously do the thing where Twitch link is hidden by default Co-authored-by: Marshall Polaris <marshall@pol.rs>
This commit is contained in:
parent
1321b95eb1
commit
833ec518b4
|
@ -6,38 +6,101 @@ import { LinkIcon } from '@heroicons/react/solid'
|
||||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import { updatePrivateUser } from 'web/lib/firebase/users'
|
import { updatePrivateUser } from 'web/lib/firebase/users'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import { linkTwitchAccountRedirect } from 'web/lib/twitch/link-twitch-account'
|
import {
|
||||||
|
linkTwitchAccountRedirect,
|
||||||
|
updateBotEnabledForUser,
|
||||||
|
} from 'web/lib/twitch/link-twitch-account'
|
||||||
import { copyToClipboard } from 'web/lib/util/copy'
|
import { copyToClipboard } from 'web/lib/util/copy'
|
||||||
import { Button, ColorType } from './../button'
|
import { Button, ColorType } from './../button'
|
||||||
import { Row } from './../layout/row'
|
import { Row } from './../layout/row'
|
||||||
import { LoadingIndicator } from './../loading-indicator'
|
import { LoadingIndicator } from './../loading-indicator'
|
||||||
|
import { PrivateUser } from 'common/user'
|
||||||
|
|
||||||
function BouncyButton(props: {
|
function BouncyButton(props: {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
onClick?: MouseEventHandler<any>
|
onClick?: MouseEventHandler<any>
|
||||||
color?: ColorType
|
color?: ColorType
|
||||||
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { children, onClick, color } = props
|
const { children, onClick, color, className } = props
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
color={color}
|
color={color}
|
||||||
size="lg"
|
size="lg"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="btn h-[inherit] flex-shrink-[inherit] border-none font-normal normal-case"
|
className={clsx(
|
||||||
|
'btn h-[inherit] flex-shrink-[inherit] border-none font-normal normal-case',
|
||||||
|
className
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BotConnectButton(props: {
|
||||||
|
privateUser: PrivateUser | null | undefined
|
||||||
|
}) {
|
||||||
|
const { privateUser } = props
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
const updateBotConnected = (connected: boolean) => async () => {
|
||||||
|
if (!privateUser) return
|
||||||
|
const twitchInfo = privateUser.twitchInfo
|
||||||
|
if (!twitchInfo) return
|
||||||
|
|
||||||
|
const error = connected
|
||||||
|
? 'Failed to add bot to your channel'
|
||||||
|
: 'Failed to remove bot from your channel'
|
||||||
|
const success = connected
|
||||||
|
? 'Added bot to your channel'
|
||||||
|
: 'Removed bot from your channel'
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
toast.promise(
|
||||||
|
updateBotEnabledForUser(privateUser, connected).then(() =>
|
||||||
|
updatePrivateUser(privateUser.id, {
|
||||||
|
twitchInfo: { ...twitchInfo, botEnabled: connected },
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{ loading: 'Updating bot settings...', error, success }
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{privateUser?.twitchInfo?.botEnabled ? (
|
||||||
|
<BouncyButton
|
||||||
|
color="red"
|
||||||
|
onClick={updateBotConnected(false)}
|
||||||
|
className={clsx(loading && 'btn-disabled')}
|
||||||
|
>
|
||||||
|
Remove bot from your channel
|
||||||
|
</BouncyButton>
|
||||||
|
) : (
|
||||||
|
<BouncyButton
|
||||||
|
color="green"
|
||||||
|
onClick={updateBotConnected(true)}
|
||||||
|
className={clsx(loading && 'btn-disabled')}
|
||||||
|
>
|
||||||
|
Add bot to your channel
|
||||||
|
</BouncyButton>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function TwitchPanel() {
|
export function TwitchPanel() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const privateUser = usePrivateUser()
|
const privateUser = usePrivateUser()
|
||||||
|
|
||||||
const twitchInfo = privateUser?.twitchInfo
|
const twitchInfo = privateUser?.twitchInfo
|
||||||
const twitchName = privateUser?.twitchInfo?.twitchName
|
const twitchName = twitchInfo?.twitchName
|
||||||
const twitchToken = privateUser?.twitchInfo?.controlToken
|
const twitchToken = twitchInfo?.controlToken
|
||||||
const twitchBotConnected = privateUser?.twitchInfo?.botEnabled
|
|
||||||
|
|
||||||
const linkIcon = <LinkIcon className="mr-2 h-6 w-6" aria-hidden="true" />
|
const linkIcon = <LinkIcon className="mr-2 h-6 w-6" aria-hidden="true" />
|
||||||
|
|
||||||
|
@ -55,13 +118,6 @@ export function TwitchPanel() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateBotConnected = (connected: boolean) => async () => {
|
|
||||||
if (user && twitchInfo) {
|
|
||||||
twitchInfo.botEnabled = connected
|
|
||||||
await updatePrivateUser(user.id, { twitchInfo })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [twitchLoading, setTwitchLoading] = useState(false)
|
const [twitchLoading, setTwitchLoading] = useState(false)
|
||||||
|
|
||||||
const createLink = async () => {
|
const createLink = async () => {
|
||||||
|
@ -115,17 +171,12 @@ export function TwitchPanel() {
|
||||||
<BouncyButton color="indigo" onClick={copyDockLink}>
|
<BouncyButton color="indigo" onClick={copyDockLink}>
|
||||||
Copy dock link
|
Copy dock link
|
||||||
</BouncyButton>
|
</BouncyButton>
|
||||||
{twitchBotConnected ? (
|
|
||||||
<BouncyButton color="red" onClick={updateBotConnected(false)}>
|
|
||||||
Remove bot from your channel
|
|
||||||
</BouncyButton>
|
|
||||||
) : (
|
|
||||||
<BouncyButton color="green" onClick={updateBotConnected(true)}>
|
|
||||||
Add bot to your channel
|
|
||||||
</BouncyButton>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-4" />
|
||||||
|
<div className="flex w-full">
|
||||||
|
<BotConnectButton privateUser={privateUser} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -3,29 +3,33 @@ import { generateNewApiKey } from '../api/api-key'
|
||||||
|
|
||||||
const TWITCH_BOT_PUBLIC_URL = 'https://king-prawn-app-5btyw.ondigitalocean.app' // TODO: Add this to env config appropriately
|
const TWITCH_BOT_PUBLIC_URL = 'https://king-prawn-app-5btyw.ondigitalocean.app' // TODO: Add this to env config appropriately
|
||||||
|
|
||||||
|
async function postToBot(url: string, body: unknown) {
|
||||||
|
const result = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
const json = await result.json()
|
||||||
|
if (!result.ok) {
|
||||||
|
throw new Error(json.message)
|
||||||
|
} else {
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function initLinkTwitchAccount(
|
export async function initLinkTwitchAccount(
|
||||||
manifoldUserID: string,
|
manifoldUserID: string,
|
||||||
manifoldUserAPIKey: string
|
manifoldUserAPIKey: string
|
||||||
): Promise<[string, Promise<{ twitchName: string; controlToken: string }>]> {
|
): Promise<[string, Promise<{ twitchName: string; controlToken: string }>]> {
|
||||||
const response = await fetch(`${TWITCH_BOT_PUBLIC_URL}/api/linkInit`, {
|
const response = await postToBot(`${TWITCH_BOT_PUBLIC_URL}/api/linkInit`, {
|
||||||
method: 'POST',
|
manifoldID: manifoldUserID,
|
||||||
headers: {
|
apiKey: manifoldUserAPIKey,
|
||||||
'Content-Type': 'application/json',
|
redirectURL: window.location.href,
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
manifoldID: manifoldUserID,
|
|
||||||
apiKey: manifoldUserAPIKey,
|
|
||||||
redirectURL: window.location.href,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
const responseData = await response.json()
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(responseData.message)
|
|
||||||
}
|
|
||||||
const responseFetch = fetch(
|
const responseFetch = fetch(
|
||||||
`${TWITCH_BOT_PUBLIC_URL}/api/linkResult?userID=${manifoldUserID}`
|
`${TWITCH_BOT_PUBLIC_URL}/api/linkResult?userID=${manifoldUserID}`
|
||||||
)
|
)
|
||||||
return [responseData.twitchAuthURL, responseFetch.then((r) => r.json())]
|
return [response.twitchAuthURL, responseFetch.then((r) => r.json())]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function linkTwitchAccountRedirect(
|
export async function linkTwitchAccountRedirect(
|
||||||
|
@ -39,3 +43,22 @@ export async function linkTwitchAccountRedirect(
|
||||||
|
|
||||||
window.location.href = twitchAuthURL
|
window.location.href = twitchAuthURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateBotEnabledForUser(
|
||||||
|
privateUser: PrivateUser,
|
||||||
|
botEnabled: boolean
|
||||||
|
) {
|
||||||
|
if (botEnabled) {
|
||||||
|
return postToBot(`${TWITCH_BOT_PUBLIC_URL}/registerchanneltwitch`, {
|
||||||
|
apiKey: privateUser.apiKey,
|
||||||
|
}).then((r) => {
|
||||||
|
if (!r.success) throw new Error(r.message)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return postToBot(`${TWITCH_BOT_PUBLIC_URL}/unregisterchanneltwitch`, {
|
||||||
|
apiKey: privateUser.apiKey,
|
||||||
|
}).then((r) => {
|
||||||
|
if (!r.success) throw new Error(r.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { RefreshIcon } from '@heroicons/react/outline'
|
import { RefreshIcon } from '@heroicons/react/outline'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
import { AddFundsButton } from 'web/components/add-funds-button'
|
import { AddFundsButton } from 'web/components/add-funds-button'
|
||||||
import { Page } from 'web/components/page'
|
import { Page } from 'web/components/page'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
|
@ -64,6 +64,7 @@ function EditUserField(props: {
|
||||||
export default function ProfilePage(props: {
|
export default function ProfilePage(props: {
|
||||||
auth: { user: User; privateUser: PrivateUser }
|
auth: { user: User; privateUser: PrivateUser }
|
||||||
}) {
|
}) {
|
||||||
|
const router = useRouter()
|
||||||
const { user, privateUser } = props.auth
|
const { user, privateUser } = props.auth
|
||||||
const [avatarUrl, setAvatarUrl] = useState(user.avatarUrl || '')
|
const [avatarUrl, setAvatarUrl] = useState(user.avatarUrl || '')
|
||||||
const [avatarLoading, setAvatarLoading] = useState(false)
|
const [avatarLoading, setAvatarLoading] = useState(false)
|
||||||
|
@ -237,8 +238,7 @@ export default function ProfilePage(props: {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{router.query.twitch && <TwitchPanel />}
|
||||||
<TwitchPanel />
|
|
||||||
</Col>
|
</Col>
|
||||||
</Col>
|
</Col>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
@ -1,29 +1,34 @@
|
||||||
|
import { PrivateUser, User } from 'common/user'
|
||||||
|
import Link from 'next/link'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import { Page } from 'web/components/page'
|
import toast from 'react-hot-toast'
|
||||||
|
import { Button } from 'web/components/button'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import { ManifoldLogo } from 'web/components/nav/manifold-logo'
|
import { Row } from 'web/components/layout/row'
|
||||||
import { useSaveReferral } from 'web/hooks/use-save-referral'
|
|
||||||
import { SEO } from 'web/components/SEO'
|
|
||||||
import { Spacer } from 'web/components/layout/spacer'
|
import { Spacer } from 'web/components/layout/spacer'
|
||||||
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
|
import { ManifoldLogo } from 'web/components/nav/manifold-logo'
|
||||||
|
import { Page } from 'web/components/page'
|
||||||
|
import { SEO } from 'web/components/SEO'
|
||||||
|
import { Title } from 'web/components/title'
|
||||||
|
import { useSaveReferral } from 'web/hooks/use-save-referral'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import { firebaseLogin, getUserAndPrivateUser } from 'web/lib/firebase/users'
|
import { firebaseLogin, getUserAndPrivateUser } from 'web/lib/firebase/users'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import { Row } from 'web/components/layout/row'
|
|
||||||
import { Button } from 'web/components/button'
|
|
||||||
import { useTracking } from 'web/hooks/use-tracking'
|
|
||||||
import { linkTwitchAccountRedirect } from 'web/lib/twitch/link-twitch-account'
|
import { linkTwitchAccountRedirect } from 'web/lib/twitch/link-twitch-account'
|
||||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
|
||||||
import toast from 'react-hot-toast'
|
|
||||||
|
|
||||||
export default function TwitchLandingPage() {
|
function TwitchPlaysManifoldMarkets(props: {
|
||||||
useSaveReferral()
|
user?: User | null
|
||||||
useTracking('view twitch landing page')
|
privateUser?: PrivateUser | null
|
||||||
|
}) {
|
||||||
|
const { user, privateUser } = props
|
||||||
|
|
||||||
const user = useUser()
|
|
||||||
const privateUser = usePrivateUser()
|
|
||||||
const twitchUser = privateUser?.twitchInfo?.twitchName
|
const twitchUser = privateUser?.twitchInfo?.twitchName
|
||||||
|
|
||||||
|
const [isLoading, setLoading] = useState(false)
|
||||||
|
|
||||||
const callback =
|
const callback =
|
||||||
user && privateUser
|
user && privateUser
|
||||||
? () => linkTwitchAccountRedirect(user, privateUser)
|
? () => linkTwitchAccountRedirect(user, privateUser)
|
||||||
|
@ -37,8 +42,6 @@ export default function TwitchLandingPage() {
|
||||||
await linkTwitchAccountRedirect(user, privateUser)
|
await linkTwitchAccountRedirect(user, privateUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [isLoading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
const getStarted = async () => {
|
const getStarted = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
@ -53,6 +56,191 @@ export default function TwitchLandingPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row className="mb-4">
|
||||||
|
<img
|
||||||
|
src="/twitch-glitch.svg"
|
||||||
|
className="mb-[0.4rem] mr-4 inline h-10 w-10"
|
||||||
|
></img>
|
||||||
|
<Title
|
||||||
|
text={'Twitch plays Manifold Markets'}
|
||||||
|
className={'!-my-0 md:block'}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
<Col className="gap-4">
|
||||||
|
<div>
|
||||||
|
Similar to Twitch channel point predictions, Manifold Markets allows
|
||||||
|
you to create and feature on stream any question you like with users
|
||||||
|
predicting to earn play money.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
The key difference is that Manifold's questions function more like a
|
||||||
|
stock market and viewers can buy and sell shares over the course of
|
||||||
|
the event and not just at the start. The market will eventually
|
||||||
|
resolve to yes or no at which point the winning shareholders will
|
||||||
|
receive their profit.
|
||||||
|
</div>
|
||||||
|
Start playing now by logging in with Google and typing commands in chat!
|
||||||
|
{twitchUser ? (
|
||||||
|
<Button size="xl" color="green" className="btn-disabled self-center">
|
||||||
|
Account connected: {twitchUser}
|
||||||
|
</Button>
|
||||||
|
) : isLoading ? (
|
||||||
|
<LoadingIndicator spinnerClassName="!w-11 !h-11" />
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
size="xl"
|
||||||
|
color="gradient"
|
||||||
|
className="my-4 self-center !px-16"
|
||||||
|
onClick={getStarted}
|
||||||
|
>
|
||||||
|
Start playing
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
Instead of Twitch channel points we use our play money, mana (m$). All
|
||||||
|
viewers start with M$1000 and more can be earned for free and then{' '}
|
||||||
|
<Link href="/charity" className="underline">
|
||||||
|
donated to a charity
|
||||||
|
</Link>{' '}
|
||||||
|
of their choice at no cost!
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Subtitle(props: { text: string }) {
|
||||||
|
const { text } = props
|
||||||
|
return <div className="text-2xl">{text}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command(props: { command: string; desc: string }) {
|
||||||
|
const { command, desc } = props
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p className="inline font-bold">{'!' + command}</p>
|
||||||
|
{' - '}
|
||||||
|
<p className="inline">{desc}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TwitchChatCommands() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Title text="Twitch Chat Commands" className="md:block" />
|
||||||
|
<Col className="gap-4">
|
||||||
|
<Subtitle text="For Chat" />
|
||||||
|
<Command command="bet yes#" desc="Bets a # of Mana on yes." />
|
||||||
|
<Command command="bet no#" desc="Bets a # of Mana on no." />
|
||||||
|
<Command
|
||||||
|
command="sell"
|
||||||
|
desc="Sells all shares you own. Using this command causes you to
|
||||||
|
cash out early before the market resolves. This could be profitable
|
||||||
|
(if the probability has moved towards the direction you bet) or cause
|
||||||
|
a loss, although at least you keep some Mana. For maximum profit (but
|
||||||
|
also risk) it is better to not sell and wait for a favourable
|
||||||
|
resolution."
|
||||||
|
/>
|
||||||
|
<Command command="balance" desc="Shows how much Mana you own." />
|
||||||
|
<Command command="allin yes" desc="Bets your entire balance on yes." />
|
||||||
|
<Command command="allin no" desc="Bets your entire balance on no." />
|
||||||
|
|
||||||
|
<Subtitle text="For Mods/Streamer" />
|
||||||
|
<Command
|
||||||
|
command="create <question>"
|
||||||
|
desc="Creates and features the question. Be careful... this will override any question that is currently featured."
|
||||||
|
/>
|
||||||
|
<Command command="resolve yes" desc="Resolves the market as 'Yes'." />
|
||||||
|
<Command command="resolve no" desc="Resolves the market as 'No'." />
|
||||||
|
<Command
|
||||||
|
command="resolve n/a"
|
||||||
|
desc="Resolves the market as 'N/A' and refunds everyone their Mana."
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function BotSetupStep(props: {
|
||||||
|
stepNum: number
|
||||||
|
buttonName?: string
|
||||||
|
text: string
|
||||||
|
}) {
|
||||||
|
const { stepNum, buttonName, text } = props
|
||||||
|
return (
|
||||||
|
<Col className="flex-1">
|
||||||
|
{buttonName && (
|
||||||
|
<>
|
||||||
|
<Button color="green">{buttonName}</Button>
|
||||||
|
<Spacer h={4} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<p className="inline font-bold">Step {stepNum}. </p>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetUpBot(props: { privateUser?: PrivateUser | null }) {
|
||||||
|
const { privateUser } = props
|
||||||
|
const twitchLinked = privateUser?.twitchInfo?.twitchName
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title
|
||||||
|
text={'Set up the bot for your own stream'}
|
||||||
|
className={'!mb-4 md:block'}
|
||||||
|
/>
|
||||||
|
<Col className="gap-4">
|
||||||
|
<img
|
||||||
|
src="https://raw.githubusercontent.com/PhilBladen/ManifoldTwitchIntegration/master/docs/OBS.png"
|
||||||
|
className="!-my-2"
|
||||||
|
></img>
|
||||||
|
To add the bot to your stream make sure you have logged in then follow
|
||||||
|
the steps below.
|
||||||
|
{!twitchLinked && (
|
||||||
|
<Button
|
||||||
|
size="xl"
|
||||||
|
color="gradient"
|
||||||
|
className="my-4 self-center !px-16"
|
||||||
|
// onClick={getStarted}
|
||||||
|
>
|
||||||
|
Start playing
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col gap-6 sm:flex-row">
|
||||||
|
<BotSetupStep
|
||||||
|
stepNum={1}
|
||||||
|
buttonName={twitchLinked && 'Add bot to channel'}
|
||||||
|
text="Use the button above to add the bot to your channel. Then mod it by typing in your Twitch chat: /mod ManifoldBot (or whatever you named the bot) If the bot is modded it will not work properly on the backend."
|
||||||
|
/>
|
||||||
|
<BotSetupStep
|
||||||
|
stepNum={2}
|
||||||
|
buttonName={twitchLinked && 'Overlay link'}
|
||||||
|
text="Create a new browser source in your streaming software such as OBS. Paste in the above link and resize it to your liking. We recommend setting the size to 400x400."
|
||||||
|
/>
|
||||||
|
<BotSetupStep
|
||||||
|
stepNum={3}
|
||||||
|
buttonName={twitchLinked && 'Control dock link'}
|
||||||
|
text="The bot can be controlled entirely through chat. But we made an easy to use control panel. Share the link with your mods or embed it into your OBS as a custom dock."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TwitchLandingPage() {
|
||||||
|
useSaveReferral()
|
||||||
|
useTracking('view twitch landing page')
|
||||||
|
|
||||||
|
const user = useUser()
|
||||||
|
const privateUser = usePrivateUser()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<SEO
|
<SEO
|
||||||
|
@ -62,7 +250,7 @@ export default function TwitchLandingPage() {
|
||||||
<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>
|
||||||
<Col className="items-center">
|
{/* <Col className="items-center">
|
||||||
<Col className="max-w-3xl">
|
<Col className="max-w-3xl">
|
||||||
<Col className="mb-6 rounded-xl sm:m-12 sm:mt-0">
|
<Col className="mb-6 rounded-xl sm:m-12 sm:mt-0">
|
||||||
<Row className="self-center">
|
<Row className="self-center">
|
||||||
|
@ -114,6 +302,12 @@ export default function TwitchLandingPage() {
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
</Col>
|
</Col>
|
||||||
|
</Col> */}
|
||||||
|
|
||||||
|
<Col className="max-w-3xl gap-8 rounded bg-white p-10 text-gray-600 shadow-md sm:mx-auto">
|
||||||
|
<TwitchPlaysManifoldMarkets user={user} privateUser={privateUser} />
|
||||||
|
<TwitchChatCommands />
|
||||||
|
<SetUpBot privateUser={privateUser} />
|
||||||
</Col>
|
</Col>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
|
|
21
web/public/twitch-glitch.svg
Normal file
21
web/public/twitch-glitch.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 2400 2800" style="enable-background:new 0 0 2400 2800;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#9146FF;}
|
||||||
|
</style>
|
||||||
|
<title>Asset 2</title>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="2200,1300 1800,1700 1400,1700 1050,2050 1050,1700 600,1700 600,200 2200,200 "/>
|
||||||
|
<g>
|
||||||
|
<g id="Layer_1-2">
|
||||||
|
<path class="st1" d="M500,0L0,500v1800h600v500l500-500h400l900-900V0H500z M2200,1300l-400,400h-400l-350,350v-350H600V200h1600
|
||||||
|
V1300z"/>
|
||||||
|
<rect x="1700" y="550" class="st1" width="200" height="600"/>
|
||||||
|
<rect x="1150" y="550" class="st1" width="200" height="600"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 890 B |
Loading…
Reference in New Issue
Block a user