mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-06-16 18:29:08 +00:00
lerna: split up bulk of packages
This commit is contained in:
parent
cb0b1d2410
commit
47a2e5694e
90 changed files with 0 additions and 0 deletions
112
packages/roleypoly-ui/pages/_app.js
Normal file
112
packages/roleypoly-ui/pages/_app.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import App, { Container } from 'next/app'
|
||||
import Head from 'next/head'
|
||||
import Layout from '../components/layout'
|
||||
import { withCookies } from '../config/rpc'
|
||||
import { Provider } from 'react-redux'
|
||||
import ErrorP, { Overlay } from './_error'
|
||||
import styled from 'styled-components'
|
||||
import { withRedux } from '../config/redux'
|
||||
import type { UserPartial } from '../../services/discord'
|
||||
|
||||
type NextPage = React.Component<any> & React.StatelessFunctionalComponent<any> & {
|
||||
getInitialProps: (ctx: any, ...args: any) => any
|
||||
}
|
||||
|
||||
const MissingJS = styled.noscript`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-size: 1.3em;
|
||||
padding: 1em;
|
||||
`
|
||||
|
||||
class RoleypolyApp extends App {
|
||||
static async getInitialProps ({ Component, ctx, router }: { Component: NextPage, router: any, ctx: {[x:string]: any}}) {
|
||||
// Fix for next/error rendering instead of our error page.
|
||||
// Who knows why this would ever happen.
|
||||
if (Component.displayName === 'ErrorPage' || Component.constructor.name === 'ErrorPage') {
|
||||
Component = ErrorP
|
||||
}
|
||||
|
||||
// console.log({ Component })
|
||||
|
||||
let pageProps = {}
|
||||
const rpc = withCookies(ctx)
|
||||
let user: ?UserPartial
|
||||
try {
|
||||
user = await rpc.getCurrentUser()
|
||||
ctx.user = user
|
||||
} catch (e) {
|
||||
if (e.code === 403) {
|
||||
ctx.user = null
|
||||
} else {
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
ctx.robots = 'INDEX, FOLLOW'
|
||||
|
||||
ctx.layout = {
|
||||
noBackground: false
|
||||
}
|
||||
|
||||
if (Component.getInitialProps) {
|
||||
pageProps = await Component.getInitialProps(ctx, rpc, router)
|
||||
}
|
||||
|
||||
// console.log({ pageProps })
|
||||
|
||||
return { pageProps, user, layout: ctx.layout, robots: ctx.robots }
|
||||
}
|
||||
|
||||
catchFOUC () {
|
||||
setTimeout(() => {
|
||||
if (document.documentElement) document.documentElement.className += ' force-active'
|
||||
}, 700)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.catchFOUC()
|
||||
}
|
||||
|
||||
render () {
|
||||
const { Component, pageProps, router, user, layout, robots, store } = this.props
|
||||
// Fix for next/error rendering instead of our error page.
|
||||
// Who knows why this would ever happen.
|
||||
const ErrorCaughtComponent = (Component.displayName === 'ErrorPage' || Component.constructor.name === 'ErrorPage') ? ErrorP : Component
|
||||
return <Container>
|
||||
<MissingJS>
|
||||
<Overlay />
|
||||
Hey there... Unfortunately, we require JS for this app to work. Please take this rose as retribution. 🌹
|
||||
</MissingJS>
|
||||
<Head>
|
||||
<meta charSet='utf-8' />
|
||||
<title key='title'>Roleypoly</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<link rel='icon' href='/static/favicon.png' />
|
||||
<meta key='robots' name='robots' content={robots} />
|
||||
<script key='typekit' dangerouslySetInnerHTML={{ __html: `
|
||||
(function(d) {
|
||||
var config = {
|
||||
kitId: 'bck0pci',
|
||||
scriptTimeout: 1500,
|
||||
async: true
|
||||
},
|
||||
h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
|
||||
})(document);//
|
||||
` }} />
|
||||
</Head>
|
||||
<Provider store={store}>
|
||||
<Layout user={user} {...layout} router={router}>
|
||||
<ErrorCaughtComponent {...pageProps} router={router} originalName={Component.displayName || Component.constructor.name} />
|
||||
</Layout>
|
||||
</Provider>
|
||||
</Container>
|
||||
}
|
||||
}
|
||||
|
||||
export default withRedux(RoleypolyApp)
|
24
packages/roleypoly-ui/pages/_document.js
Normal file
24
packages/roleypoly-ui/pages/_document.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import Document from 'next/document'
|
||||
import { ServerStyleSheet } from 'styled-components'
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static async getInitialProps (ctx) {
|
||||
const sheet = new ServerStyleSheet()
|
||||
const originalRenderPage = ctx.renderPage
|
||||
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
|
||||
})
|
||||
|
||||
const initialProps = await Document.getInitialProps(ctx)
|
||||
return {
|
||||
...initialProps,
|
||||
styles: <>{initialProps.styles}{sheet.getStyleElement()}</>
|
||||
}
|
||||
} finally {
|
||||
sheet.seal()
|
||||
}
|
||||
}
|
||||
}
|
110
packages/roleypoly-ui/pages/_error.js
Normal file
110
packages/roleypoly-ui/pages/_error.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import MediaQuery from '../kit/media'
|
||||
|
||||
export const Overlay = styled.div`
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: -10;
|
||||
background-image: radial-gradient(circle, var(--c-dark), var(--c-dark) 1px, transparent 1px, transparent);
|
||||
background-size: 27px 27px;
|
||||
`
|
||||
|
||||
const ResponsiveSplitter = styled.div`
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1.6;
|
||||
font-size: 1.3em;
|
||||
flex-direction: column;
|
||||
|
||||
${() => MediaQuery({
|
||||
md: `flex-direction: row; min-height: 100vh; position: relative; top: -50px;`
|
||||
})}
|
||||
|
||||
& > div {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
& section {
|
||||
text-align: center;
|
||||
${() => MediaQuery({
|
||||
md: `text-align: left;`
|
||||
})}
|
||||
}
|
||||
`
|
||||
|
||||
const JapaneseFlair = styled.section`
|
||||
color: var(--c-3);
|
||||
font-size: 0.9rem;
|
||||
`
|
||||
|
||||
const Code = styled.h1`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 4em;
|
||||
${() => MediaQuery({
|
||||
md: `font-size: 2em;`
|
||||
})}
|
||||
`
|
||||
|
||||
export default class CustomErrorPage extends React.Component {
|
||||
static getInitialProps ({ res, err, robots }) {
|
||||
const statusCode = res ? res.statusCode : err ? err.statusCode : null
|
||||
robots = 'NOINDEX, NOFOLLOW'
|
||||
return { statusCode }
|
||||
}
|
||||
|
||||
render400 = () => this.out('400', `Your client sent me something weird...`, '((((;゜Д゜)))')
|
||||
render403 = () => this.out('403', `You weren't allowed to access this.`, 'あなたはこの点に合格しないかもしれません')
|
||||
render404 = () => this.out('404', 'This page is in another castle.', 'お探しのページは見つかりませんでした')
|
||||
render419 = () => this.out('419', 'Something went too slowly...', 'おやすみなさい〜')
|
||||
render500 = () => this.out('500', `The server doesn't like you right now. Feed it a cookie.`, 'クッキーを送ってください〜 クッキーを送ってください〜')
|
||||
renderDefault = () => this.out('Oops', 'Something went bad. How could this happen?', 'おねがい?')
|
||||
renderServer = () => this.out('Oops.', 'Server was unhappy about this render. Try reloading or changing page.', 'クッキーを送ってください〜')
|
||||
renderAuthExpired = () => this.out('Woah.', 'That magic login link was expired.', 'What are you trying to do?')
|
||||
|
||||
out (code, description, flair) {
|
||||
return <div>
|
||||
<Overlay />
|
||||
<ResponsiveSplitter>
|
||||
<div>
|
||||
<Code>{code}</Code>
|
||||
</div>
|
||||
<div>
|
||||
<section>
|
||||
{description}
|
||||
</section>
|
||||
<JapaneseFlair>{flair}</JapaneseFlair>
|
||||
</div>
|
||||
</ResponsiveSplitter>
|
||||
</div>
|
||||
}
|
||||
|
||||
handlers = {
|
||||
400: this.render400,
|
||||
403: this.render403,
|
||||
404: this.render404,
|
||||
419: this.render419,
|
||||
500: this.render500,
|
||||
1001: this.renderAuthExpired
|
||||
}
|
||||
|
||||
render () {
|
||||
// if (this.props.originalName === 'ErrorPage') {
|
||||
// return this.renderServer()
|
||||
// }
|
||||
|
||||
if (this.props.statusCode in this.handlers) {
|
||||
return this.handlers[this.props.statusCode]()
|
||||
}
|
||||
|
||||
return this.renderDefault()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import SocialCards from '../../../components/social-cards'
|
||||
|
||||
export default () => <>
|
||||
<SocialCards title='Sign in on Roleypoly' />
|
||||
</>
|
|
@ -0,0 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import SocialCards from '../../../components/social-cards'
|
||||
|
||||
export default () => <>
|
||||
<SocialCards title='Sign in on Roleypoly' description="Click this link to log in. It's magic!" />
|
||||
</>
|
111
packages/roleypoly-ui/pages/_internal/_server.js
Normal file
111
packages/roleypoly-ui/pages/_internal/_server.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Head from 'next/head'
|
||||
import type { PageProps } from '../../types'
|
||||
import SocialCards from '../../components/social-cards'
|
||||
import redirect from '../../lib/redirect'
|
||||
import { connect } from 'react-redux'
|
||||
import { fetchServerIfNeed, getCurrentServerState, type ServerState } from '../../stores/currentServer'
|
||||
import { renderRoles, getCategoryViewState, toggleRole, type ViewState } from '../../stores/roles'
|
||||
import styled from 'styled-components'
|
||||
import Role from '../../components/role'
|
||||
|
||||
type ServerPageProps = PageProps & {
|
||||
currentServer: ServerState,
|
||||
view: ViewState,
|
||||
isDiscordBot: boolean
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, { router: { query: { id } } }) => {
|
||||
return {
|
||||
currentServer: getCurrentServerState(state, id),
|
||||
view: getCategoryViewState(state)
|
||||
}
|
||||
}
|
||||
|
||||
const Category = styled.div``
|
||||
|
||||
const Hider = styled.div`
|
||||
/* opacity: ${(props: any) => props.visible ? '1' : '0'}; */
|
||||
/* opacity: 1; */
|
||||
/* transition: opacity 0.15s ease-out; */
|
||||
/* ${(props: any) => props.visible ? '' : 'display: none;'} */
|
||||
`
|
||||
|
||||
const RoleHolder = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`
|
||||
class Server extends React.PureComponent<ServerPageProps> {
|
||||
static async getInitialProps (ctx: *, rpc: *, router: *) {
|
||||
const isDiscordBot = ctx.req && ctx.req.headers['user-agent'].includes('Discordbot')
|
||||
if (ctx.user == null) {
|
||||
if (!isDiscordBot) {
|
||||
redirect(ctx, `/auth/login?r=${router.asPath}`)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.robots = 'NOINDEX, NOFOLLOW'
|
||||
await ctx.store.dispatch(fetchServerIfNeed(router.query.id, rpc))
|
||||
|
||||
if (!isDiscordBot) {
|
||||
await ctx.store.dispatch(renderRoles(router.query.id))
|
||||
}
|
||||
return { isDiscordBot }
|
||||
}
|
||||
|
||||
async componentDidMount () {
|
||||
const { currentServer, router: { query: { id } }, dispatch } = this.props
|
||||
if (currentServer == null) {
|
||||
this.props.router.push('/s/add')
|
||||
}
|
||||
|
||||
await dispatch(fetchServerIfNeed(id))
|
||||
await dispatch(renderRoles(id))
|
||||
}
|
||||
|
||||
onToggle = (role) => (nextState) => {
|
||||
if (role.safe) {
|
||||
this.props.dispatch(toggleRole(role.id, nextState))
|
||||
}
|
||||
}
|
||||
|
||||
renderSocial () {
|
||||
const { currentServer } = this.props
|
||||
return <SocialCards title={`${currentServer.server.name} on Roleypoly`} description='Manage your roles here.' />
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isDiscordBot, currentServer, view } = this.props
|
||||
// console.log({ currentServer })
|
||||
if (currentServer == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isDiscordBot) {
|
||||
return this.renderSocial()
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title key='title'>{currentServer.server.name} - Roleypoly</title>
|
||||
</Head>
|
||||
{ this.renderSocial() }
|
||||
hello <span style={{ color: currentServer.gm?.color }}>{currentServer.gm?.nickname}</span> on {currentServer.server.name} ({ view.dirty ? 'dirty' : 'clean' })
|
||||
<Hider visible={true || currentServer.id !== null}>
|
||||
{ !view.invalidated && view.categories.map(c => <Category key={`cat__${c.name}__${c.id}`}>
|
||||
<div>{ c.name }</div>
|
||||
<RoleHolder>
|
||||
{
|
||||
c._roles && c._roles.map(r => <Role key={`role__${r.name}__${r.id}`} role={r} active={view.selected.includes(r.id)} onToggle={this.onToggle(r)} disabled={!r.safe} />)
|
||||
}
|
||||
</RoleHolder>
|
||||
</Category>) }
|
||||
</Hider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Server)
|
1
packages/roleypoly-ui/pages/_internal/_server_add.js
Normal file
1
packages/roleypoly-ui/pages/_internal/_server_add.js
Normal file
|
@ -0,0 +1 @@
|
|||
export default () => <h1>s/add</h1>
|
2
packages/roleypoly-ui/pages/_test/error.js
Normal file
2
packages/roleypoly-ui/pages/_test/error.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Error from '../_error'
|
||||
export default ({ router: { query: { code = 404 } } }) => <Error statusCode={code} />
|
23
packages/roleypoly-ui/pages/_test/landing-mock.js
Normal file
23
packages/roleypoly-ui/pages/_test/landing-mock.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import type { PageProps } from '../../types'
|
||||
|
||||
export default class LandingTest extends React.Component<PageProps> {
|
||||
render () {
|
||||
return <div>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Suscipit accusantium quidem adipisci excepturi, delectus iure omnis, eos corrupti, ea ex iusto magnam! Incidunt accusamus repellat natus esse facilis animi aut.</p>
|
||||
</div>
|
||||
}
|
||||
}
|
3
packages/roleypoly-ui/pages/auth/expired.js
Normal file
3
packages/roleypoly-ui/pages/auth/expired.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import ErrorPage from '../_error'
|
||||
|
||||
export default (props) => <ErrorPage {...props} statusCode={1001} />
|
192
packages/roleypoly-ui/pages/auth/login.js
Normal file
192
packages/roleypoly-ui/pages/auth/login.js
Normal file
|
@ -0,0 +1,192 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import MediaQuery from '../../kit/media'
|
||||
import DiscordButton from '../../components/discord-button'
|
||||
import RPC from '../../config/rpc'
|
||||
import redirect from '../../lib/redirect'
|
||||
import dynamic from 'next/dynamic'
|
||||
import type { PageProps, ServerSlug } from '../../types'
|
||||
import getConfig from 'next/config'
|
||||
const { publicRuntimeConfig: { BOT_HANDLE } } = getConfig()
|
||||
|
||||
type AuthLoginState = {
|
||||
humanCode: string,
|
||||
waiting: boolean
|
||||
}
|
||||
|
||||
type AuthLoginProps = PageProps & {
|
||||
redirect: ?string,
|
||||
redirectSlug: ?ServerSlug
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 3em;
|
||||
width: 400px;
|
||||
max-width: calc(98vw - 10px);
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
${() => MediaQuery({
|
||||
md: `
|
||||
padding-top: 0;
|
||||
align-items: center;
|
||||
min-height: 80vh;
|
||||
`
|
||||
})}
|
||||
`
|
||||
|
||||
const Line = styled.div`
|
||||
height: 1px;
|
||||
background-color: var(--c-9);
|
||||
margin: 1em 0.3em;
|
||||
`
|
||||
|
||||
const SecretCode = styled.input`
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
padding: 1em;
|
||||
color: var(--c-9);
|
||||
margin: 0.5rem 0;
|
||||
width: 100%;
|
||||
font-size: 0.9em;
|
||||
appearance: none;
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
&:focus, &:active, &:hover {
|
||||
background-color: var(--c-3);
|
||||
}
|
||||
|
||||
&:focus, &:active {
|
||||
& ::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
& ::placeholder {
|
||||
transition: all 0.3s ease-in-out;
|
||||
color: var(--c-7);
|
||||
text-align: center;
|
||||
}
|
||||
`
|
||||
|
||||
const HiderButton = styled.button`
|
||||
appearance: none;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
background-color: var(--c-3);
|
||||
color: var(--c-white);
|
||||
border: none;
|
||||
padding: 1em;
|
||||
font-size: 0.9em;
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
&[disabled] {
|
||||
cursor: default;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
`
|
||||
const SlugWrapper = styled.div`
|
||||
padding-bottom: 2em;
|
||||
text-align: center;
|
||||
`
|
||||
|
||||
const DiscordGuildPic = dynamic(() => import('../../components/discord-guild-pic'))
|
||||
const StyledDGP = styled(DiscordGuildPic)`
|
||||
border-radius: 100%;
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
height: 4em;
|
||||
margin-top: 1em;
|
||||
`
|
||||
|
||||
const ServerName = styled.span`
|
||||
font-weight: bold;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: 370px;
|
||||
display: block;
|
||||
`
|
||||
|
||||
const Slug = (slug: ServerSlug) => <SlugWrapper>
|
||||
<StyledDGP {...slug} />
|
||||
<br />Hey there.<br /><ServerName>{slug.name}</ServerName> uses Roleypoly to manage its roles.
|
||||
</SlugWrapper>
|
||||
|
||||
export default class AuthLogin extends React.Component<AuthLoginProps, AuthLoginState> {
|
||||
state = {
|
||||
humanCode: '',
|
||||
waiting: false
|
||||
}
|
||||
|
||||
static async getInitialProps (ctx: *, rpc: typeof RPC, router: *) {
|
||||
let { r } = (router.query: { r: string })
|
||||
|
||||
if (ctx.user != null) {
|
||||
redirect(ctx, r || '/')
|
||||
}
|
||||
|
||||
ctx.robots = 'NOINDEX, NOFOLLOW'
|
||||
|
||||
if (r != null) {
|
||||
let redirectSlug = null
|
||||
if (r.startsWith('/s/') && r !== '/s/add') {
|
||||
redirectSlug = await rpc.getServerSlug(r.replace('/s/', ''))
|
||||
}
|
||||
return { redirect: r, redirectSlug }
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
if (this.props.redirect != null) {
|
||||
this.props.router.replace(this.props.router.pathname)
|
||||
}
|
||||
}
|
||||
|
||||
onChange = (event: any) => {
|
||||
this.setState({ humanCode: event.target.value })
|
||||
}
|
||||
|
||||
onSubmit = async () => {
|
||||
this.setState({ waiting: true })
|
||||
try {
|
||||
const result = await RPC.checkAuthChallenge(this.state.humanCode)
|
||||
if (result === true) {
|
||||
redirect(null, this.props.redirect || '/')
|
||||
}
|
||||
} catch (e) {
|
||||
this.setState({ waiting: false })
|
||||
}
|
||||
}
|
||||
|
||||
get dm () {
|
||||
if (BOT_HANDLE) {
|
||||
const [username, discrim] = BOT_HANDLE.split('#')
|
||||
return <><b>{ username }</b>#{discrim}</>
|
||||
}
|
||||
|
||||
return <><b>roleypoly</b>#3712</>
|
||||
}
|
||||
|
||||
render () {
|
||||
return <Wrapper>
|
||||
<div>
|
||||
{(this.props.redirectSlug != null) ? <Slug {...this.props.redirectSlug} /> : null}
|
||||
<DiscordButton href={`/api/auth/redirect?r=${this.props.redirect || '/'}`}>Sign in with Discord</DiscordButton>
|
||||
<Line />
|
||||
<div>
|
||||
<i>Or, send a DM to {this.dm} saying: login</i>
|
||||
</div>
|
||||
<div>
|
||||
<SecretCode placeholder='click to enter super secret code' onChange={this.onChange} value={this.state.humanCode} />
|
||||
<HiderButton onClick={this.onSubmit} disabled={this.state.humanCode === ''}>{
|
||||
(this.state.waiting) ? 'One sec...' : 'Submit Code →'
|
||||
}</HiderButton>
|
||||
</div>
|
||||
</div>
|
||||
</Wrapper>
|
||||
}
|
||||
}
|
113
packages/roleypoly-ui/pages/help/why-no-roles.js
Normal file
113
packages/roleypoly-ui/pages/help/why-no-roles.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import demoRoles from '../../config/demo'
|
||||
import MediaQuery from '../../kit/media'
|
||||
|
||||
const admin = { name: 'admin', color: '#db2828' }
|
||||
const bot = { name: 'roleypoly', color: 'var(--c-5)' }
|
||||
|
||||
const exampleGood = [
|
||||
admin,
|
||||
bot,
|
||||
...demoRoles
|
||||
]
|
||||
|
||||
const exampleBad = [
|
||||
admin,
|
||||
...demoRoles,
|
||||
bot
|
||||
]
|
||||
|
||||
const DiscordOuter = styled.div`
|
||||
background-color: var(--dark-but-not-black);
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
color: var(--c-white);
|
||||
border: 1px solid rgba(0,0,0,0.25);
|
||||
width: 250px;
|
||||
margin: 0 auto;
|
||||
user-select: none;
|
||||
`
|
||||
|
||||
const Collapser = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
${() => MediaQuery({
|
||||
md: `flex-direction: row;`
|
||||
})}
|
||||
`
|
||||
|
||||
const DiscordRole = styled.div`
|
||||
color: ${({ role: { color } }) => color};
|
||||
position: relative;
|
||||
padding: 0.3em 0.6em;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transition: opacity 0.02s ease-in-out;
|
||||
opacity: 0;
|
||||
background-color: ${({ role: { color } }) => color};
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
${
|
||||
({ role }) => (role.name === 'roleypoly')
|
||||
? `
|
||||
background-color: ${role.color};
|
||||
color: var(--c-white);
|
||||
`
|
||||
: ``
|
||||
}
|
||||
`
|
||||
|
||||
const MicroHeader = styled.div`
|
||||
font-size: 0.7em;
|
||||
font-weight: bold;
|
||||
color: #72767d;
|
||||
padding: 0.3rem 0.6rem;
|
||||
`
|
||||
|
||||
const Center = styled.div`
|
||||
text-align: center;
|
||||
margin: 2em;
|
||||
`
|
||||
|
||||
const Example = ({ data }) => (
|
||||
<DiscordOuter>
|
||||
<MicroHeader>
|
||||
ROLES
|
||||
</MicroHeader>
|
||||
{
|
||||
data.map(role => <DiscordRole role={role} key={role.name}>{role.name}</DiscordRole>)
|
||||
}
|
||||
</DiscordOuter>
|
||||
)
|
||||
|
||||
export default () => <div>
|
||||
<h3>How come I can't see any of my roles?!</h3>
|
||||
<p>Discord doesn't let us act upon roles that are "higher" than Roleypoly's in the list. You must keep it's role higher than any role you may want to assign.</p>
|
||||
<Collapser>
|
||||
<Center>
|
||||
<h4 style={{ color: 'var(--c-red)' }}>Bad ❌</h4>
|
||||
<Example data={exampleBad} />
|
||||
</Center>
|
||||
<Center>
|
||||
<h4 style={{ color: 'var(--c-green)' }}>Good ✅</h4>
|
||||
<Example data={exampleGood} />
|
||||
</Center>
|
||||
</Collapser>
|
||||
</div>
|
119
packages/roleypoly-ui/pages/index.js
Normal file
119
packages/roleypoly-ui/pages/index.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
import * as React from 'react'
|
||||
import redirect from '../lib/redirect'
|
||||
// import Link from 'next/link'
|
||||
// import Head from '../components/head'
|
||||
// import Nav from '../components/nav'
|
||||
import TypingDemo from '../components/demos/typing'
|
||||
import TapDemo from '../components/demos/tap'
|
||||
import styled from 'styled-components'
|
||||
import MediaQuery from '../kit/media'
|
||||
|
||||
const HeroBig = styled.h1`
|
||||
color: var(--c-7);
|
||||
font-size: 1.8em;
|
||||
`
|
||||
|
||||
const HeroSmol = styled.h1`
|
||||
color: var(--c-5);
|
||||
font-size: 1.1em;
|
||||
`
|
||||
|
||||
const Hero = styled.div`
|
||||
padding: 2em 0;
|
||||
text-align: center;
|
||||
`
|
||||
|
||||
const Footer = styled.p`
|
||||
text-align: center;
|
||||
font-size: 0.7em;
|
||||
opacity: 0.3;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 1;
|
||||
}
|
||||
`
|
||||
|
||||
const FooterLink = styled.a`
|
||||
font-style: none;
|
||||
color: var(--c-7);
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--c-5);
|
||||
}
|
||||
`
|
||||
|
||||
const DemoArea = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
${() => MediaQuery({ md: `flex-direction: row;` })}
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
& > div > p {
|
||||
text-align: center;
|
||||
}
|
||||
`
|
||||
|
||||
const Wrapper = styled.div`
|
||||
flex-wrap: wrap;
|
||||
${() => MediaQuery({
|
||||
md: `
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80vh;
|
||||
min-height: 500px;
|
||||
`
|
||||
})}
|
||||
`
|
||||
|
||||
export default class Home extends React.Component {
|
||||
static async getInitialProps (ctx, rpc) {
|
||||
if (ctx.user != null) {
|
||||
redirect(ctx, '/s/add')
|
||||
}
|
||||
|
||||
ctx.layout.noBackground = true
|
||||
}
|
||||
|
||||
render () {
|
||||
return <div>
|
||||
<Wrapper>
|
||||
<div>
|
||||
<Hero>
|
||||
<HeroBig>Discord roles for humans.</HeroBig>
|
||||
<HeroSmol>Ditch bot commands once and for all.</HeroSmol>
|
||||
</Hero>
|
||||
<DemoArea>
|
||||
<div>
|
||||
<TypingDemo />
|
||||
<p>What is this? 2005?</p>
|
||||
</div>
|
||||
<div>
|
||||
<TapDemo />
|
||||
<p>Just click or tap.</p>
|
||||
</div>
|
||||
</DemoArea>
|
||||
</div>
|
||||
</Wrapper>
|
||||
<Footer>
|
||||
© {new Date().getFullYear()}<br />
|
||||
Made with ♡
|
||||
<img src='/static/flags.svg' style={{ height: '1em', opacity: 0.5 }} /><br />
|
||||
<FooterLink target='_blank' href='https://ko-fi.com/roleypoly'>Ko-Fi</FooterLink> -
|
||||
<FooterLink target='_blank' href='https://github.com/kayteh/roleypoly'>GitHub</FooterLink> -
|
||||
<FooterLink target='_blank' href='https://discord.gg/PWQUVsd'>Discord</FooterLink>
|
||||
</Footer>
|
||||
</div>
|
||||
}
|
||||
}
|
31
packages/roleypoly-ui/pages/testrpc.js
Normal file
31
packages/roleypoly-ui/pages/testrpc.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import * as React from 'react'
|
||||
import RPC, { withCookies } from '../config/rpc'
|
||||
|
||||
export default class TestRPC extends React.Component {
|
||||
static async getInitialProps (ctx) {
|
||||
const user = await withCookies(ctx).getCurrentUser()
|
||||
console.log(user)
|
||||
return {
|
||||
user
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount () {
|
||||
window.$RPC = RPC
|
||||
}
|
||||
|
||||
componentDidCatch (error, errorInfo) {
|
||||
if (error) {
|
||||
console.log(error, errorInfo)
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.props.user == null) {
|
||||
return <div>hello stranger OwO</div>
|
||||
}
|
||||
|
||||
const { username, avatar, discriminator } = this.props.user
|
||||
return <div>hello, {username}#{discriminator} <img src={avatar} width={50} height={50} /></div>
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue