flowtyped everything, some functional, safety, and structural changes

This commit is contained in:
41666 2019-03-10 03:18:11 -05:00
parent 6f3eca7a64
commit d2aecb38ca
92 changed files with 17554 additions and 1440 deletions

5
UI/.babelrc Normal file
View file

@ -0,0 +1,5 @@
{
"presets": [
"next/babel", "@babel/preset-flow"
]
}

View file

@ -1,3 +1,6 @@
// @flow
import * as React from 'react'
export const colors = {
white: '#efefef',
c9: '#EBD6D4',
@ -12,7 +15,7 @@ export const colors = {
}
const getColors = () => {
Object.keys(colors).map(key => {
return Object.keys(colors).map(key => {
const nk = key.replace(/c([0-9])/, '$1')
return `--c-${nk}: ${colors[key]};`
}).join(' \n')
@ -26,6 +29,18 @@ body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* prevent FOUC */
transition: opacity 0.2s ease-in-out;
opacity: 0;
}
.wf-active body {
opacity: 1;
}
// FOUC guard if we take too long
.force-active body {
opacity: 1;
}
.font-sans-serif {

View file

@ -0,0 +1,11 @@
// @flow
import * as React from 'react'
import HeaderBarCommon from './common'
const HeaderBarAuth: React.StatelessFunctionalComponent<{}> = () => (
<HeaderBarCommon>
hi
</HeaderBarCommon>
)
export default HeaderBarAuth

View file

@ -0,0 +1,14 @@
// @flow
import * as React from 'react'
export type CommonProps = {
children: React.Element<any>
}
const HeaderBarCommon: React.StatelessFunctionalComponent<CommonProps> = ({ children }) => (
<div>
{ children }
</div>
)
export default HeaderBarCommon

View file

@ -0,0 +1,11 @@
// @flow
import * as React from 'react'
import HeaderBarCommon from './common'
const HeaderBarUnauth: React.StatelessFunctionalComponent<{}> = () => (
<HeaderBarCommon>
hi
</HeaderBarCommon>
)
export default HeaderBarUnauth

View file

@ -0,0 +1,36 @@
// @flow
import * as React from 'react'
import NextHead from 'next/head'
export type SocialCardProps = {
title?: string,
description?: string,
image?: string,
imageSize?: number
}
const defaultProps: SocialCardProps = {
title: 'Roleypoly',
description: 'Tame your Discord roles.',
image: 'https://rp.kat.cafe/static/social.png',
imageSize: 200
}
const SocialCards: React.StatelessFunctionalComponent<SocialCardProps> = (props) => {
props = {
...defaultProps,
...props
}
return <NextHead>
<meta key='og:title' property='og:title' content={props.title} />
<meta key='og:description' property='og:description' content={props.description} />
<meta key='twitter:card' name='twitter:card' content='summary_large_image' />
<meta key='twitter:image' name='twitter:image' content={props.image} />
<meta key='og:image' property='og:image' content={props.image} />
<meta key='og:image:width' property='og:image:width' content={props.imageSize} />
<meta key='og:image:height' property='og:image:height' content={props.imageSize} />
</NextHead>
}
export default SocialCards

15
UI/config/redux.js Normal file
View file

@ -0,0 +1,15 @@
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import withNextRedux from 'next-redux-wrapper'
import { rootReducer } from 'fast-redux'
export const initStore = (initialState = {}) => {
return createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
export const withRedux = (comp) => withNextRedux(initStore)(comp)

4
UI/config/rpc.js Normal file
View file

@ -0,0 +1,4 @@
// @flow
import RPCClient from '../rpc'
export default (new RPCClient({ forceDev: false })).rpc

View file

@ -0,0 +1,30 @@
// @flow
import * as React from 'react'
import dynamic from 'next/dynamic'
import { withRedux } from '../config/redux'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { namespaceConfig } from 'fast-redux'
import * as User from './user'
type Props = {
user: User.User
}
const HeaderBarAuth = dynamic(() => import('../components/header/auth'))
const HeaderBarUnauth = dynamic(() => import('../components/header/unauth'))
const HeaderBar: React.StatelessFunctionalComponent<Props> = () => {
// if ()
return null
}
const mapStateToProps = (state): Props => {
return {}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({ }, dispatch)
}
export default withRedux(connect(mapStateToProps, mapDispatchToProps)(HeaderBar))

32
UI/containers/user.js Normal file
View file

@ -0,0 +1,32 @@
// @flow
import { namespaceConfig } from 'fast-redux'
export type User = {
id: string,
username: string,
discriminator: string,
avatar: string,
nicknameCache: {
[server: string]: string
}
}
export type UserState = {
currentUser: User | null,
userCache: {
[id: string]: User
}
}
const DEFAULT_STATE: UserState = {
currentUser: null,
userCache: {}
}
export const {
action, getState: getUserStore
} = namespaceConfig('userStore', DEFAULT_STATE)
export const getCurrentUser = () => async (dispatch: Function) => {
}

View file

@ -1,6 +1,9 @@
import * as React from 'react'
import App, { Container } from 'next/app'
import Head from 'next/head'
import GlobalColors from '../components/global-colors'
import SocialCards from '../components/social-cards'
// import RPCClient from '../rpc'
class RoleypolyApp extends App {
static async getInitialProps ({ Component, ctx }) {
@ -15,6 +18,7 @@ class RoleypolyApp extends App {
componentDidMount () {
this.loadTypekit(document)
this.waitForFOUC()
}
loadTypekit (d) {
@ -42,14 +46,26 @@ class RoleypolyApp extends App {
s.parentNode.insertBefore(tk, s)
}
// wait one second, add FOUC de-protection.
waitForFOUC () {
setTimeout(() => {
document.documentElement.className += ' force-active'//
}, 2000)
}
render () {
const { Component, pageProps, router } = this.props
const { Component, pageProps, router, rpc } = this.props
return (
<Container>
<Head />
<Head>
<meta charSet='utf-8' />
<title key='title'>Roleypoly</title>
<meta name='viewport' content='width=device-width, initial-scale=1' />
</Head>
<GlobalColors />
<Component {...pageProps} router={router} />
<SocialCards />
<Component {...pageProps} router={router} rpc={rpc} />
</Container>
)
}

View file

@ -1,7 +1,19 @@
const Server = ({ router: { query: { id } } }) => (
<div>
{id}
</div>
)
// @flow
import * as React from 'react'
import Head from 'next/head'
import type { PageProps } from '../../types'
import SocialCards from '../../components/social-cards'
export default Server
export default class Server extends React.Component<PageProps> {
render () {
return (
<div>
<Head>
<title key='title'>server name!</title>
</Head>
<SocialCards title={'server test'} />
hello {this.props.router.query.id}
</div>
)
}
}

View file

@ -5,24 +5,24 @@ import Nav from '../components/nav'
const Home = () => (
<div>
<Head title="Home" />
<Head title='Home' />
<Nav />
<div className="hero">
<h1 className="title">Welcome to Next!</h1>
<p className="description">
<div className='hero'>
<h1 className='title'>Welcome to Next!</h1>
<p className='description'>
To get started, edit <code>pages/index.js</code> and save to reload.
</p>
<div className="row">
<Link href="https://github.com/zeit/next.js#getting-started">
<a className="card">
<div className='row'>
<Link href='https://github.com/zeit/next.js#getting-started'>
<a className='card'>
<h3>Getting Started &rarr;</h3>
<p>Learn more about Next on Github and in their examples</p>
</a>
</Link>
<Link href="https://open.segment.com/create-next-app">
<a className="card">
<Link href='https://open.segment.com/create-next-app'>
<a className='card'>
<h3>Examples &rarr;</h3>
<p>
Find other example boilerplates on the{' '}
@ -30,8 +30,8 @@ const Home = () => (
</p>
</a>
</Link>
<Link href="https://github.com/segmentio/create-next-app">
<a className="card">
<Link href='https://github.com/segmentio/create-next-app'>
<a className='card'>
<h3>Create Next App &rarr;</h3>
<p>Was this tool helpful? Let us know how we can improve it</p>
</a>

24
UI/pages/testrpc.js Normal file
View file

@ -0,0 +1,24 @@
import * as React from 'react'
import RPC from '../config/rpc'
export default class TestRPC extends React.Component {
static async getInitialProps (ctx) {
return {
// hello: await RPC.hello('world')
}
}
componentDidMount () {
window.$RPC = RPC
}
componentDidCatch (error, errorInfo) {
if (error) {
console.log(error, errorInfo)
}
}
render () {
return <div>hello, { this.props.hello }</div>
}
}

106
UI/rpc/index.js Normal file
View file

@ -0,0 +1,106 @@
// @flow
import superagent from 'superagent'
import RPCError from '../../rpc/_error'
export type RPCResponse = {
response?: mixed,
hash?: string,
// error stuff
error?: boolean,
msg?: string,
trace?: string
}
export type RPCRequest = {
fn: string,
args: mixed[]
}
export default class RPCClient {
dev: boolean = false
baseUrl: string
firstKnownHash: string
recentHash: string
rpc: {
[fn: string]: (...args: any[]) => Promise<mixed> | string
} = {}
__rpcAvailable: Array<{
name: string,
args: number
}> = []
constructor ({ forceDev, baseUrl = '/api/_rpc' }: { forceDev?: boolean, baseUrl?: string } = {}) {
this.baseUrl = (process.env.APP_URL || '') + baseUrl
if (forceDev != null) {
this.dev = forceDev
} else {
this.dev = process.env.NODE_ENV === 'development'
}
this.rpc = new Proxy({
toJSON () {
return '{}'
}
}, { get: this.__rpcCall, has: this.__checkCall, ownKeys: this.__listCalls, delete: () => {} })
if (this.dev) {
this.updateCalls()
}
}
async updateCalls () {
// this is for development only. doing in prod is probably dumb.
const rsp = await superagent.get(this.baseUrl)
if (rsp.status !== 200) {
console.error(rsp)
return
}
const { hash, available } = rsp.body
this.__rpcAvailable = available
if (this.firstKnownHash == null) {
this.firstKnownHash = hash
}
this.recentHash = hash
// just kinda prefill. none of these get called anyway.
// and don't matter in prod either.
for (let { name } of available) {
this.rpc[name] = async () => {}
}
}
async call (fn: string, ...args: any[]): mixed {
const req: RPCRequest = { fn, args }
const rsp = await superagent.post(this.baseUrl).send(req).ok(() => true)
const body: RPCResponse = rsp.body
if (body.error === true) {
throw RPCError.fromResponse(body, rsp.status)
}
if (body.hash != null) {
if (this.firstKnownHash == null) {
this.firstKnownHash = body.hash
}
this.recentHash = body.hash
if (this.firstKnownHash !== this.recentHash) {
this.updateCalls()
}
}
return body.response
}
// PROXY HANDLERS
__rpcCall = (_: {}, fn: string) => this.call.bind(this, fn)
__checkCall = (_: {}, fn: string) => this.dev ? this.__listCalls(_).includes(fn) : true
__listCalls = (_: {}): string[] => this.__rpcAvailable.map(x => x.name)
}

8
UI/types.js Normal file
View file

@ -0,0 +1,8 @@
// @flow
export type PageProps = {
router: {
query: {
[key: string]: string
}
}
}