diff --git a/typescript/lunargame/client/babel.config.js b/typescript/lunargame/client/babel.config.js index 620e090..5cf5c00 100644 --- a/typescript/lunargame/client/babel.config.js +++ b/typescript/lunargame/client/babel.config.js @@ -1,17 +1,17 @@ module.exports = { - presets: [ - "@babel/preset-env", - "@babel/preset-react", - "@babel/preset-typescript" - ], - plugins: [ - "@babel/plugin-syntax-dynamic-import", - ["@babel/plugin-proposal-decorators", { legacy: true }], - ["@babel/plugin-proposal-class-properties", { loose: true }], - ], - env: { - test: { - presets: [["@babel/preset-env", { targets: { node: "current" } }]], - }, - }, + presets: [ + '@babel/preset-env', + '@babel/preset-react', + '@babel/preset-typescript' + ], + plugins: [ + '@babel/plugin-syntax-dynamic-import', + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-proposal-class-properties', { loose: true }] + ], + env: { + test: { + presets: [['@babel/preset-env', { targets: { node: 'current' } }]] + } + } } diff --git a/typescript/lunargame/client/package-lock.json b/typescript/lunargame/client/package-lock.json index cd8aadc..f8b6c64 100644 --- a/typescript/lunargame/client/package-lock.json +++ b/typescript/lunargame/client/package-lock.json @@ -736,18 +736,6 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, - "@babel/plugin-transform-runtime": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.0.tgz", - "integrity": "sha512-LmPIZOAgTLl+86gR9KjLXex6P/lRz1fWEjTz6V6QZMmKie51ja3tvzdwORqhHc4RWR8TcZ5pClpRWs0mlaA2ng==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "resolve": "^1.8.1", - "semver": "^5.5.1" - } - }, "@babel/plugin-transform-shorthand-properties": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", @@ -1638,6 +1626,39 @@ "object.assign": "^4.1.0" } }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-regenerator-runtime": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.5.0.tgz", + "integrity": "sha1-DkHNHJ+ARCRm8BXHSf/4upj44RA=", + "dev": true + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2489,6 +2510,12 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-js": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", + "dev": true + }, "core-js-compat": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.4.tgz", diff --git a/typescript/lunargame/client/package.json b/typescript/lunargame/client/package.json index 43eae6b..6e9fa65 100644 --- a/typescript/lunargame/client/package.json +++ b/typescript/lunargame/client/package.json @@ -12,12 +12,13 @@ "@babel/plugin-proposal-class-properties": "^7.5.0", "@babel/plugin-proposal-decorators": "^7.4.4", "@babel/plugin-syntax-dynamic-import": "^7.2.0", - "@babel/plugin-transform-runtime": "^7.5.0", "@babel/preset-env": "^7.5.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", "@types/react-router-dom": "^4.3.4", "babel-loader": "^8.0.6", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-regenerator-runtime": "^6.5.0", "css-loader": "^3.0.0", "html-webpack-inline-source-plugin": "0.0.10", "html-webpack-plugin": "^3.2.0", diff --git a/typescript/lunargame/client/src/common/account/components/Login.tsx b/typescript/lunargame/client/src/common/account/components/Login.tsx deleted file mode 100644 index 6a79d1b..0000000 --- a/typescript/lunargame/client/src/common/account/components/Login.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react' - -export const Login = () => { - return

This is the login component

-} diff --git a/typescript/lunargame/client/src/common/account/components/Signup.tsx b/typescript/lunargame/client/src/common/account/components/Signup.tsx deleted file mode 100644 index 0560fc0..0000000 --- a/typescript/lunargame/client/src/common/account/components/Signup.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react' - -export const Signup = () => { - return

This is the signup component

-} diff --git a/typescript/lunargame/client/src/common/dom/dialogs/classes/DialogManager.ts b/typescript/lunargame/client/src/common/dom/dialogs/classes/DialogManager.ts new file mode 100644 index 0000000..be91c7e --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/dialogs/classes/DialogManager.ts @@ -0,0 +1,45 @@ +import { Singleton } from '@eix/utils' +import { BehaviorSubject } from 'rxjs' + +export interface DialogAction { + name: string + variant: 'standard' | 'contained' | 'outlined' + callback: Function +} + +export interface Dialog { + title: string + message: string + actions: DialogAction[] + onClose: (event: unknown) => void +} + +@Singleton +export class DialogManager { + public active = new BehaviorSubject(null) + public queue: Dialog[] = [] + + public add(dialog: Dialog) { + if (this.active.value !== null) { + this.queue.push(dialog) + } else { + this.activate(dialog) + } + } + + private activate(dialog: Dialog) { + this.active.next({ + ...dialog, + onClose: (event: unknown) => { + dialog.onClose(event) + + if (this.queue.length) { + const newDialog = this.queue.shift() + this.activate(newDialog) + } else { + this.active.next(null) + } + } + }) + } +} diff --git a/typescript/lunargame/client/src/common/dom/dialogs/components/BaseDialogRenderer.tsx b/typescript/lunargame/client/src/common/dom/dialogs/components/BaseDialogRenderer.tsx new file mode 100644 index 0000000..8cc3504 --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/dialogs/components/BaseDialogRenderer.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import Dialog from '@material-ui/core/Dialog' +import DialogTitle from '@material-ui/core/DialogTitle' +import { DialogManager } from '../classes/DialogManager' +import { useObservable } from 'rxjs-hooks' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogContentText from '@material-ui/core/DialogContentText' +import { Button } from '@material-ui/core' + +const manager = new DialogManager() + +export const BaseDialogRenderer = () => { + const activeDialog = useObservable(() => manager.active) + + if (activeDialog !== null) { + return ( + + + {activeDialog.title} + + + + + {activeDialog.message} + + + + + {activeDialog.actions.map((action, index) => { + return ( + + ) + })} + + + ) + } + + return <> +} diff --git a/typescript/lunargame/client/src/common/dom/forms/classes/FormField.ts b/typescript/lunargame/client/src/common/dom/forms/classes/FormField.ts new file mode 100644 index 0000000..6dce933 --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/forms/classes/FormField.ts @@ -0,0 +1,49 @@ +import { Subscription, BehaviorSubject } from 'rxjs' + +export interface FormFieldSnapshot { + passing: boolean + errorMessage?: string +} + +export interface FormValidator { + validate: (input: string) => FormFieldSnapshot +} + +export class FormField { + private subscription: Subscription + + public constructor( + public name: string, + public input: BehaviorSubject, + public output: BehaviorSubject, + private validators: FormValidator[] + ) { + this.subscription = this.input.subscribe((text: string) => { + for (const validator of this.validators) { + const result = validator.validate(text) + + if (!result.passing) { + return this.output.next(result) + } + } + + this.output.next({ + passing: true + }) + }) + } + + public dispose() { + if (this.subscription) { + this.subscription.unsubscribe() + } + } + + public passes() { + return this.output.value.passing + } + + public get value() { + return this.input.value + } +} diff --git a/typescript/lunargame/client/src/common/dom/forms/classes/FormManager.ts b/typescript/lunargame/client/src/common/dom/forms/classes/FormManager.ts new file mode 100644 index 0000000..bee6c1a --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/forms/classes/FormManager.ts @@ -0,0 +1,40 @@ +import { FormField } from './FormField' +import { BaseServer } from '../../../../modules/network/classes/BaseServer' + +const server = new BaseServer() + +export class FormManager { + constructor(public fields: FormField[]) {} + + public collect() { + const data: Record = {} + + for (const { name, value } of this.fields) { + data[name] = value + } + + return data + } + + public async submit(url: string) { + if (this.validate()) { + return server.request(url, 'POST', this.collect()) + } + } + + public validate() { + for (const field of this.fields) { + if (!field.passes()) return false + } + + return true + } + + public dispose() { + for (const field of this.fields) { + field.dispose() + } + + return this + } +} diff --git a/typescript/lunargame/client/src/common/dom/forms/components/TextFieldWithError.tsx b/typescript/lunargame/client/src/common/dom/forms/components/TextFieldWithError.tsx new file mode 100644 index 0000000..f754b15 --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/forms/components/TextFieldWithError.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import TextField from '@material-ui/core/TextField' +import { BehaviorSubject } from 'rxjs' +import { FormFieldSnapshot } from '../classes/FormField' +import { useObservable } from 'rxjs-hooks' + +export interface TextFieldWithErrorsProps { + name: string + type: string + input: BehaviorSubject + output: BehaviorSubject + className: string +} + +const good = '✅' + +export const TextFieldWithErrors = (props: TextFieldWithErrorsProps) => { + const outputSnapshot = useObservable(() => props.output, { + passing: true, + errorMessage: good + }) + + return ( + { + props.input.next(event.target.value) + }} + /> + ) +} diff --git a/typescript/lunargame/client/src/common/dom/forms/helpers/createFormModal.tsx b/typescript/lunargame/client/src/common/dom/forms/helpers/createFormModal.tsx new file mode 100644 index 0000000..42b2189 --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/forms/helpers/createFormModal.tsx @@ -0,0 +1,110 @@ +import React from 'react' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogContentText from '@material-ui/core/DialogContentText' +import DialogTitle from '@material-ui/core/DialogTitle' +import { FormValidator, FormField } from '../classes/FormField' +import { Button } from '@material-ui/core' +import { BehaviorSubject } from 'rxjs' +import { FormManager } from '../classes/FormManager' +import { TextFieldWithErrors } from '../components/TextFieldWithError' +import { makeStyles, Theme } from '@material-ui/core/styles' + +export interface TextFieldData { + name: string + type: string + validators: FormValidator[] +} + +export interface ModalProps { + open: boolean + onClose: Function +} + +const useStyles = makeStyles((theme: Theme) => ({ + field: { + marginTop: theme.spacing(2) + } +})) + +export const createFormModal = ( + title: string, + description: string, + url: string, + fields: TextFieldData[], + onSubmit?: (data: unknown) => void +) => { + const formFields = fields.map( + field => + new FormField( + field.name, + new BehaviorSubject(''), + new BehaviorSubject({ + passing: true + }), + field.validators + ) + ) + + const formManager = new FormManager(formFields) + + return (props: ModalProps) => { + const handleClose = (event: unknown) => { + props.onClose(event) + } + + const classes = useStyles() + + const textFields = fields.map((field, index) => { + const fieldObject = formFields[index] + + return ( + + ) + }) + + return ( + + {title} + + {description} + {textFields} + + + + + + + ) + } +} diff --git a/typescript/lunargame/client/src/common/dom/forms/helpers/createValidator.ts b/typescript/lunargame/client/src/common/dom/forms/helpers/createValidator.ts new file mode 100644 index 0000000..8b1c5cd --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/forms/helpers/createValidator.ts @@ -0,0 +1,26 @@ +import { FormValidator, FormFieldSnapshot } from '../classes/FormField' + +export interface ValidatorCondition { + regex: RegExp + message: string +} + +export const createValidator = ( + ...conditions: ValidatorCondition[] +): { new (): FormValidator } => + class implements FormValidator { + public validate(text: string): FormFieldSnapshot { + for (const condition of conditions) { + if (!condition.regex.test(text)) { + return { + passing: false, + errorMessage: condition.message + } + } + } + + return { + passing: true + } + } + } diff --git a/typescript/lunargame/client/src/common/dom/forms/validators/lengthValidator.ts b/typescript/lunargame/client/src/common/dom/forms/validators/lengthValidator.ts new file mode 100644 index 0000000..bc579ef --- /dev/null +++ b/typescript/lunargame/client/src/common/dom/forms/validators/lengthValidator.ts @@ -0,0 +1,7 @@ +import { createValidator } from '../helpers/createValidator' + +export const lengthValidator = (min: number, max: number) => + createValidator({ + regex: new RegExp(`^.{${min},${max}}$`), + message: `Must contain between ${min} and ${max} characters.` + }) diff --git a/typescript/lunargame/client/src/common/lang/objects/decorators/cacheInstances.ts b/typescript/lunargame/client/src/common/lang/objects/decorators/cacheInstances.ts new file mode 100644 index 0000000..b2a5043 --- /dev/null +++ b/typescript/lunargame/client/src/common/lang/objects/decorators/cacheInstances.ts @@ -0,0 +1,38 @@ +import { decorable } from '@eix/utils' +import { areEqual } from '../helpers/areEqual' + +export interface ObjectArgumentsRef { + instance: T + arguments: unknown[] +} + +export const cacheInstances = (argCount = Infinity) => { + const objectMemory: ObjectArgumentsRef[] = [] + + return void } }>( + toDecorate: T + ) => { + return class extends toDecorate { + constructor(...args: any[]) { + super(...args) + + const sliceParameters = + argCount === Infinity ? [0] : [0, argCount] + const argumentsToStore = args.slice(...sliceParameters) + + const reference = objectMemory.find(instance => + areEqual(argumentsToStore, instance.arguments) + ) + + if (reference) return reference.instance as this + else + objectMemory.push({ + instance: this, + arguments: argumentsToStore + }) + + if (super.init) super.init() + } + } + } +} diff --git a/typescript/lunargame/client/src/common/lang/objects/helpers/areEqual.ts b/typescript/lunargame/client/src/common/lang/objects/helpers/areEqual.ts new file mode 100644 index 0000000..f265e09 --- /dev/null +++ b/typescript/lunargame/client/src/common/lang/objects/helpers/areEqual.ts @@ -0,0 +1,10 @@ +export const areEqual = (first: T, last: T) => { + for (const key of Object.keys(first)) { + // for ts to shut up + const typedKey = key as keyof T + + if (first[typedKey] !== last[typedKey]) return false + } + + return true +} diff --git a/typescript/lunargame/client/src/common/network/classes/BaseServer.ts b/typescript/lunargame/client/src/common/network/classes/BaseServer.ts deleted file mode 100644 index cd16fce..0000000 --- a/typescript/lunargame/client/src/common/network/classes/BaseServer.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Account } from '../types/Account' -import { BehaviorSubject } from 'rxjs' -import { Singleton } from '@eix/utils' -import { defaultAvatar } from '../constants' - -@Singleton -export class BaseServer { - public account = new BehaviorSubject(null) - - constructor() { - // mock account for now - // this.account.next({ - // name: 'Mock', - // email: 'mock@somethng.io', - // avatar: defaultAvatar, - // description: 'Just a random mock account', - // uid: '1234', - // verified: true - // }) - } -} diff --git a/typescript/lunargame/client/src/main.tsx b/typescript/lunargame/client/src/main.tsx index 32938a9..85b3770 100644 --- a/typescript/lunargame/client/src/main.tsx +++ b/typescript/lunargame/client/src/main.tsx @@ -1,5 +1,5 @@ import { render } from 'react-dom' import React from 'react' -import { App } from './common/core/components/App' +import { App } from './modules/core/components/App' render(, document.querySelector('#app')) diff --git a/typescript/lunargame/client/src/modules/account/components/LoginModal.ts b/typescript/lunargame/client/src/modules/account/components/LoginModal.ts new file mode 100644 index 0000000..33cf9d1 --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/components/LoginModal.ts @@ -0,0 +1,26 @@ +import { createFormModal } from '../../../common/dom/forms/helpers/createFormModal' +import { + emailValidatorList, + passwordValidatorList +} from '../validators/authValidators' +import { Account } from '../../network/types/Account' +import { updateAccount } from '../../helpers/updateAccount' + +export const LoginModal = createFormModal( + 'Login', + `To subscribe to this website, please enter you r email address here. We will send updates occasionally.`, + 'auth/login', + [ + { + name: 'email', + type: 'email', + validators: emailValidatorList() + }, + { + name: 'password', + type: 'password', + validators: passwordValidatorList() + } + ], + updateAccount +) diff --git a/typescript/lunargame/client/src/modules/account/components/ModalButton.tsx b/typescript/lunargame/client/src/modules/account/components/ModalButton.tsx new file mode 100644 index 0000000..28e3008 --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/components/ModalButton.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react' +import Button from '@material-ui/core/Button' +import { ModalProps } from '../../../common/dom/forms/helpers/createFormModal' + +export interface ModalButtonProps { + modal: (props: ModalProps) => JSX.Element + children: string + className?: string + contained?: boolean +} + +export const ModalButton = (props: ModalButtonProps) => { + const [open, setOpen] = useState(false) + + return ( + <> + + setOpen(false)} /> + + ) +} diff --git a/typescript/lunargame/client/src/modules/account/components/SignupModal.ts b/typescript/lunargame/client/src/modules/account/components/SignupModal.ts new file mode 100644 index 0000000..3041a0b --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/components/SignupModal.ts @@ -0,0 +1,44 @@ +import { createFormModal } from '../../../common/dom/forms/helpers/createFormModal' +import { + usernameValidatorList, + emailValidatorList, + passwordValidatorList +} from '../validators/authValidators' +import { updateAccount } from '../../helpers/updateAccount' +import { Account } from '../../network/types/Account' +import { DialogManager } from '../../../common/dom/dialogs/classes/DialogManager' + +const dialogManager = new DialogManager() + +export const SignupModal = createFormModal( + 'Signup', + `To create an account you need to provide an username, email and a password.`, + 'auth/create', + [ + { + name: 'name', + type: 'text', + validators: usernameValidatorList() + }, + { + name: 'email', + type: 'email', + validators: emailValidatorList() + }, + { + name: 'password', + type: 'password', + validators: passwordValidatorList() + } + ], + (data: Account) => { + updateAccount(data) + dialogManager.add({ + title: 'Email verification', + message: + 'To unlock the full set of features offered by lunrabox, please verify your email', + actions: [], + onClose: () => {} + }) + } +) diff --git a/typescript/lunargame/client/src/common/account/components/TopbarAccount.tsx b/typescript/lunargame/client/src/modules/account/components/TopbarAccount.tsx similarity index 65% rename from typescript/lunargame/client/src/common/account/components/TopbarAccount.tsx rename to typescript/lunargame/client/src/modules/account/components/TopbarAccount.tsx index 69105e4..53ed2f3 100644 --- a/typescript/lunargame/client/src/common/account/components/TopbarAccount.tsx +++ b/typescript/lunargame/client/src/modules/account/components/TopbarAccount.tsx @@ -2,9 +2,10 @@ import React from 'react' import { useObservable } from 'rxjs-hooks' import { BaseServer } from '../../network/classes/BaseServer' import Avatar from '@material-ui/core/Avatar' -import Button from '@material-ui/core/Button' import { makeStyles, Theme } from '@material-ui/core/styles' -import { Link } from 'react-router-dom' +import { LoginModal } from './LoginModal' +import { ModalButton } from './ModalButton' +import { SignupModal } from './SignupModal' const { account } = new BaseServer() @@ -16,18 +17,18 @@ const useStyles = makeStyles((theme: Theme) => ({ export const TopbarAccount = (props: unknown) => { const accountSnapshot = useObservable(() => account, null) - const classes = useStyles(props) const signup = ( <> - - - + Sign up + + Log in + ) diff --git a/typescript/lunargame/client/src/modules/account/validators/alphaNumeric.ts b/typescript/lunargame/client/src/modules/account/validators/alphaNumeric.ts new file mode 100644 index 0000000..01a05a9 --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/validators/alphaNumeric.ts @@ -0,0 +1,6 @@ +import { createValidator } from '../../../common/dom/forms/helpers/createValidator' + +export const alphaNumericValidator = createValidator({ + regex: /^[a-zA-Z0-9_]*$/, + message: 'Must only contain alpha-numeric characters or underscores.' +}) diff --git a/typescript/lunargame/client/src/modules/account/validators/authValidators.ts b/typescript/lunargame/client/src/modules/account/validators/authValidators.ts new file mode 100644 index 0000000..551ab38 --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/validators/authValidators.ts @@ -0,0 +1,20 @@ +import { fieldLengthValidator } from './fieldLength' +import { emailValidator } from './emailValidator' +import { requiredValidator } from './requiredValidator' +import { alphaNumericValidator } from './alphaNumeric' + +export const usernameValidatorList = () => [ + new requiredValidator(), + new fieldLengthValidator(), + new alphaNumericValidator() +] +export const emailValidatorList = () => [ + new requiredValidator(), + new fieldLengthValidator(), + new emailValidator() +] +export const passwordValidatorList = () => [ + new requiredValidator(), + new fieldLengthValidator(), + new alphaNumericValidator() +] diff --git a/typescript/lunargame/client/src/modules/account/validators/emailValidator.ts b/typescript/lunargame/client/src/modules/account/validators/emailValidator.ts new file mode 100644 index 0000000..374db7c --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/validators/emailValidator.ts @@ -0,0 +1,6 @@ +import { createValidator } from '../../../common/dom/forms/helpers/createValidator' + +export const emailValidator = createValidator({ + regex: /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/, + message: 'Must be a valid email' +}) diff --git a/typescript/lunargame/client/src/modules/account/validators/fieldLength.ts b/typescript/lunargame/client/src/modules/account/validators/fieldLength.ts new file mode 100644 index 0000000..c020df6 --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/validators/fieldLength.ts @@ -0,0 +1,3 @@ +import { lengthValidator } from '../../../common/dom/forms/validators/lengthValidator' + +export const fieldLengthValidator = lengthValidator(3, 30) diff --git a/typescript/lunargame/client/src/modules/account/validators/requiredValidator.ts b/typescript/lunargame/client/src/modules/account/validators/requiredValidator.ts new file mode 100644 index 0000000..7545424 --- /dev/null +++ b/typescript/lunargame/client/src/modules/account/validators/requiredValidator.ts @@ -0,0 +1,6 @@ +import { createValidator } from '../../../common/dom/forms/helpers/createValidator' + +export const requiredValidator = createValidator({ + regex: /^.{1,}$/, + message: 'Field is required' +}) diff --git a/typescript/lunargame/client/src/common/core/components/App.tsx b/typescript/lunargame/client/src/modules/core/components/App.tsx similarity index 80% rename from typescript/lunargame/client/src/common/core/components/App.tsx rename to typescript/lunargame/client/src/modules/core/components/App.tsx index 19a9dc1..25fe7bc 100644 --- a/typescript/lunargame/client/src/common/core/components/App.tsx +++ b/typescript/lunargame/client/src/modules/core/components/App.tsx @@ -8,6 +8,7 @@ import { theme as MuiTheme } from '../data/Theme' import { ThemeProvider as Theme } from '@material-ui/styles' import { AppBar } from './AppBar' import { Body } from './Body' +import { BaseDialogRenderer } from '../../../common/dom/dialogs/components/BaseDialogRenderer' export const App = () => { return ( @@ -16,6 +17,7 @@ export const App = () => { + ) diff --git a/typescript/lunargame/client/src/common/core/components/AppBar.tsx b/typescript/lunargame/client/src/modules/core/components/AppBar.tsx similarity index 100% rename from typescript/lunargame/client/src/common/core/components/AppBar.tsx rename to typescript/lunargame/client/src/modules/core/components/AppBar.tsx diff --git a/typescript/lunargame/client/src/common/core/components/Body.tsx b/typescript/lunargame/client/src/modules/core/components/Body.tsx similarity index 65% rename from typescript/lunargame/client/src/common/core/components/Body.tsx rename to typescript/lunargame/client/src/modules/core/components/Body.tsx index deec3f8..890b5b6 100644 --- a/typescript/lunargame/client/src/common/core/components/Body.tsx +++ b/typescript/lunargame/client/src/modules/core/components/Body.tsx @@ -2,8 +2,6 @@ import React from 'react' import { SiddebarRoutes } from './SidebarRouteList' import { makeStyles } from '@material-ui/styles' import { Route } from 'react-router-dom' -import { Signup } from '../../account/components/Signup' -import { Login } from '../../account/components/Login' const useStyles = makeStyles({ root: { @@ -17,9 +15,6 @@ export const Body = (props: unknown) => { return (
- - -
) } diff --git a/typescript/lunargame/client/src/common/core/components/Home.tsx b/typescript/lunargame/client/src/modules/core/components/Home.tsx similarity index 51% rename from typescript/lunargame/client/src/common/core/components/Home.tsx rename to typescript/lunargame/client/src/modules/core/components/Home.tsx index 7b4375e..a89f6b1 100644 --- a/typescript/lunargame/client/src/common/core/components/Home.tsx +++ b/typescript/lunargame/client/src/modules/core/components/Home.tsx @@ -2,6 +2,7 @@ import React from 'react' import Typography from '@material-ui/core/Typography' import Divider from '@material-ui/core/Divider' import { makeStyles, Theme } from '@material-ui/core/styles' +import { BaseServer } from '../../network/classes/BaseServer' const useStyles = makeStyles((theme: Theme) => ({ root: { @@ -10,6 +11,9 @@ const useStyles = makeStyles((theme: Theme) => ({ divider: { marginBottom: theme.spacing(2), marginTop: theme.spacing(2) + }, + a: { + color: '#0000ff' } })) @@ -24,9 +28,34 @@ export const Home = (props: unknown) => { Lunarbox is a game streaming website for games made with the eix - game engine. The project is open source, and right now it's + game engine. The project is open source, and right now also unusable. + +
+ + + The project is open source on{' '} + + github + + . + + + {/* Todo: remove */} + ) } diff --git a/typescript/lunargame/client/src/common/core/components/Sidebar.tsx b/typescript/lunargame/client/src/modules/core/components/Sidebar.tsx similarity index 100% rename from typescript/lunargame/client/src/common/core/components/Sidebar.tsx rename to typescript/lunargame/client/src/modules/core/components/Sidebar.tsx diff --git a/typescript/lunargame/client/src/common/core/components/SidebarRouteData.tsx b/typescript/lunargame/client/src/modules/core/components/SidebarRouteData.tsx similarity index 100% rename from typescript/lunargame/client/src/common/core/components/SidebarRouteData.tsx rename to typescript/lunargame/client/src/modules/core/components/SidebarRouteData.tsx diff --git a/typescript/lunargame/client/src/common/core/components/SidebarRouteList.tsx b/typescript/lunargame/client/src/modules/core/components/SidebarRouteList.tsx similarity index 100% rename from typescript/lunargame/client/src/common/core/components/SidebarRouteList.tsx rename to typescript/lunargame/client/src/modules/core/components/SidebarRouteList.tsx diff --git a/typescript/lunargame/client/src/common/core/data/Theme.ts b/typescript/lunargame/client/src/modules/core/data/Theme.ts similarity index 100% rename from typescript/lunargame/client/src/common/core/data/Theme.ts rename to typescript/lunargame/client/src/modules/core/data/Theme.ts diff --git a/typescript/lunargame/client/src/common/core/styles/reset.scss b/typescript/lunargame/client/src/modules/core/styles/reset.scss similarity index 100% rename from typescript/lunargame/client/src/common/core/styles/reset.scss rename to typescript/lunargame/client/src/modules/core/styles/reset.scss diff --git a/typescript/lunargame/client/src/common/core/types/Route.ts b/typescript/lunargame/client/src/modules/core/types/Route.ts similarity index 100% rename from typescript/lunargame/client/src/common/core/types/Route.ts rename to typescript/lunargame/client/src/modules/core/types/Route.ts diff --git a/typescript/lunargame/client/src/modules/helpers/updateAccount.ts b/typescript/lunargame/client/src/modules/helpers/updateAccount.ts new file mode 100644 index 0000000..36473de --- /dev/null +++ b/typescript/lunargame/client/src/modules/helpers/updateAccount.ts @@ -0,0 +1,8 @@ +import { BaseServer } from '../network/classes/BaseServer' +import { Account } from '../network/types/Account' + +const server = new BaseServer() + +export const updateAccount = (data: Account | null) => { + server.account.next(data) +} diff --git a/typescript/lunargame/client/src/modules/network/classes/BaseServer.ts b/typescript/lunargame/client/src/modules/network/classes/BaseServer.ts new file mode 100644 index 0000000..b9c4844 --- /dev/null +++ b/typescript/lunargame/client/src/modules/network/classes/BaseServer.ts @@ -0,0 +1,55 @@ +import { Account } from '../types/Account' +import { BehaviorSubject } from 'rxjs' +import { Singleton } from '@eix/utils' +import { Response } from '../types/Response' + +@Singleton +export class BaseServer { + public account = new BehaviorSubject(null) + public path = 'http://localhost:8000' + + constructor() { + this.refreshAccount() + } + + public async refreshAccount(url = 'account', method = 'GET', body = {}) { + try { + const account = await this.request(url, method, body) + this.account.next(account) + return account + } catch (err) { + this.account.next(null) + return null + } + } + + public async request( + url: string, + method = 'GET', + body = {} + ): Promise { + const noBody = ['GET', 'DELETE'] + + const response = await fetch(`${this.path}/${url}`, { + ...(noBody.indexOf(method) === -1 + ? { body: JSON.stringify(body) } + : {}), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Access-Control-Allow-Credentials': 'true' + }, + method, + credentials: 'include' + }) + const parsed: Response = await response.json() + const status = response.status + + if (status !== 200) { + console.warn(parsed.message) + throw new Error(parsed.message) + } + + return parsed.data + } +} diff --git a/typescript/lunargame/client/src/modules/network/classes/PublicAccount.ts b/typescript/lunargame/client/src/modules/network/classes/PublicAccount.ts new file mode 100644 index 0000000..a8764ed --- /dev/null +++ b/typescript/lunargame/client/src/modules/network/classes/PublicAccount.ts @@ -0,0 +1,30 @@ +import { cacheInstances } from '../../../common/lang/objects/decorators/cacheInstances' +import { BehaviorSubject } from 'rxjs' +import { AccountPublicData } from '../types/AccountPublicData' +import { BaseServer } from './BaseServer' + +@cacheInstances() +export class PublicAccount { + public static server = new BaseServer() + public account = new BehaviorSubject(null) + + public constructor(public name: string) {} + + public init() { + this.refresh() + } + + public async refresh() { + try { + const account = await PublicAccount.server.request< + AccountPublicData + >(`user/name/${this.name}`) + + this.account.next(account) + return account + } catch { + this.account.next(null) + return null + } + } +} diff --git a/typescript/lunargame/client/src/common/network/constants.ts b/typescript/lunargame/client/src/modules/network/constants.ts similarity index 100% rename from typescript/lunargame/client/src/common/network/constants.ts rename to typescript/lunargame/client/src/modules/network/constants.ts diff --git a/typescript/lunargame/client/src/modules/network/types/Account.ts b/typescript/lunargame/client/src/modules/network/types/Account.ts new file mode 100644 index 0000000..75e0b80 --- /dev/null +++ b/typescript/lunargame/client/src/modules/network/types/Account.ts @@ -0,0 +1,6 @@ +import { AccountPublicData } from './AccountPublicData' + +export interface Account extends AccountPublicData { + uid: string + verified: boolean +} diff --git a/typescript/lunargame/client/src/common/network/types/Account.ts b/typescript/lunargame/client/src/modules/network/types/AccountPublicData.ts similarity index 55% rename from typescript/lunargame/client/src/common/network/types/Account.ts rename to typescript/lunargame/client/src/modules/network/types/AccountPublicData.ts index 503affa..c7840da 100644 --- a/typescript/lunargame/client/src/common/network/types/Account.ts +++ b/typescript/lunargame/client/src/modules/network/types/AccountPublicData.ts @@ -1,8 +1,6 @@ -export interface Account { - name: string +export interface AccountPublicData { email: string - avatar: string + name: string description: string - uid: string - verified: boolean + avatar: string } diff --git a/typescript/lunargame/client/src/modules/network/types/Response.ts b/typescript/lunargame/client/src/modules/network/types/Response.ts new file mode 100644 index 0000000..164848f --- /dev/null +++ b/typescript/lunargame/client/src/modules/network/types/Response.ts @@ -0,0 +1,4 @@ +export interface Response { + data: T + message: string +} diff --git a/typescript/lunargame/client/tsconfig.json b/typescript/lunargame/client/tsconfig.json index ef9eff8..a2ec574 100644 --- a/typescript/lunargame/client/tsconfig.json +++ b/typescript/lunargame/client/tsconfig.json @@ -4,7 +4,8 @@ "esModuleInterop": true, "jsx": "preserve", "noImplicitAny": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "target": "esnext" }, "exclude": ["node_modules"], "include": ["src"] diff --git a/typescript/lunargame/client/webpack.config.js b/typescript/lunargame/client/webpack.config.js index f9f0c56..970293f 100644 --- a/typescript/lunargame/client/webpack.config.js +++ b/typescript/lunargame/client/webpack.config.js @@ -1,115 +1,114 @@ -const { resolve } = require("path") -const HtmlWebpackPlugin = require("html-webpack-plugin") -const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin") -const MiniCssExtractPlugin = require("mini-css-extract-plugin") -const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin") -const webpackMerge = require("webpack-merge") +const { resolve } = require('path') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') +const webpackMerge = require('webpack-merge') -const isProduction = process.env.NODE_ENV === "production" +const isProduction = process.env.NODE_ENV === 'production' const projectRoot = resolve(__dirname) -const sourceFolder = resolve(projectRoot, "src") -const buildFolder = resolve(projectRoot, "dist") -const htmlTemplateFile = resolve(sourceFolder, "index.html") +const sourceFolder = resolve(projectRoot, 'src') +const buildFolder = resolve(projectRoot, 'dist') +const htmlTemplateFile = resolve(sourceFolder, 'index.html') const babelRule = { - test: /\.(js|tsx?)$/, - use: "babel-loader", + test: /\.(js|tsx?)$/, + use: 'babel-loader' } const sassRule = { - test: /\.scss$/, - use: [ - isProduction - ? MiniCssExtractPlugin.loader - : { - loader: "style-loader", - options: { - singleton: true, - }, - }, - { loader: "css-loader" }, - { - loader: "sass-loader", - options: { - includePaths: [sourceFolder], - }, - }, - ], + test: /\.scss$/, + use: [ + isProduction + ? MiniCssExtractPlugin.loader + : { + loader: 'style-loader', + options: { + singleton: true + } + }, + { loader: 'css-loader' }, + { + loader: 'sass-loader', + options: { + includePaths: [sourceFolder] + } + } + ] } const baseConfig = { - mode: "none", - entry: [ resolve(sourceFolder, "main")], - output: { - filename: "js/[name].js", - path: buildFolder, - publicPath: "/", - }, - module: { - rules: [ babelRule, sassRule], - }, - resolve: { - extensions: [".js", ".ts", ".tsx", ".scss"], - }, - plugins: [ - ] + mode: 'none', + entry: ['babel-regenerator-runtime', resolve(sourceFolder, 'main')], + output: { + filename: 'js/[name].js', + path: buildFolder, + publicPath: '/' + }, + module: { + rules: [babelRule, sassRule] + }, + resolve: { + extensions: ['.js', '.ts', '.tsx', '.scss'] + }, + plugins: [] } const devConfig = { - mode: "development", - plugins: [ - new HtmlWebpackPlugin({ - template: htmlTemplateFile, - chunksSortMode: "dependency", - }), - ], - devtool: "inline-source-map", - devServer: { - historyApiFallback: true - } + mode: 'development', + plugins: [ + new HtmlWebpackPlugin({ + template: htmlTemplateFile, + chunksSortMode: 'dependency' + }) + ], + devtool: 'inline-source-map', + devServer: { + historyApiFallback: true + } } const prodConfig = { - mode: "production", - optimization: { - minimize: true, - nodeEnv: "production", - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: "css/[name].min.css", - }), - new OptimizeCssAssetsWebpackPlugin(), - new HtmlWebpackPlugin({ - template: htmlTemplateFile, - minify: { - removeComments: true, - collapseWhitespace: true, - removeRedundantAttributes: true, - useShortDoctype: true, - removeEmptyAttributes: true, - removeStyleLinkTypeAttributes: true, - keepClosingSlash: true, - minifyJS: true, - minifyCSS: true, - minifyURLs: true - }, - inject: true - }), - new HtmlWebpackInlineSourcePlugin() - ], - devtool: "source-map" + mode: 'production', + optimization: { + minimize: true, + nodeEnv: 'production' + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: 'css/[name].min.css' + }), + new OptimizeCssAssetsWebpackPlugin(), + new HtmlWebpackPlugin({ + template: htmlTemplateFile, + minify: { + removeComments: true, + collapseWhitespace: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeStyleLinkTypeAttributes: true, + keepClosingSlash: true, + minifyJS: true, + minifyCSS: true, + minifyURLs: true + }, + inject: true + }), + new HtmlWebpackInlineSourcePlugin() + ], + devtool: 'source-map' } function getFinalConfig() { - if (process.env.NODE_ENV === "production") { - console.info("Running production config") - return webpackMerge(baseConfig, prodConfig) - } + if (process.env.NODE_ENV === 'production') { + console.info('Running production config') + return webpackMerge(baseConfig, prodConfig) + } - console.info("Running development config") - return webpackMerge(baseConfig, devConfig) + console.info('Running development config') + return webpackMerge(baseConfig, devConfig) } module.exports = getFinalConfig()