Dwayne Harris
5 years ago
10 changed files with 7727 additions and 0 deletions
-
7375package-lock.json
-
40package.json
-
67src/apps/text-app/composer/app.tsx
-
12src/apps/text-app/composer/index.ejs
-
8src/apps/text-app/composer/index.tsx
-
60src/apps/text-app/composer/webpack.config.ts
-
91src/communicator/index.ts
-
25src/server/index.ts
-
33src/styles/default.scss
-
16tsconfig.json
7375
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,40 @@ |
|||||
|
{ |
||||
|
"name": "flexor-apps", |
||||
|
"description": "Default set of Flexor Apps.", |
||||
|
"version": "1.0.0", |
||||
|
"private": true, |
||||
|
"scripts": { |
||||
|
"start": "node dist/server/index.js", |
||||
|
"build": "tsc", |
||||
|
"build:text-app": "webpack --config src/apps/text-app/composer/webpack.config.ts" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@types/html-webpack-plugin": "^3.2.1", |
||||
|
"@types/mini-css-extract-plugin": "^0.8.0", |
||||
|
"@types/react": "^16.9.9", |
||||
|
"@types/react-dom": "^16.9.2", |
||||
|
"@types/webpack": "^4.39.5", |
||||
|
"bulma": "^0.8.0", |
||||
|
"css-loader": "^3.2.0", |
||||
|
"html-webpack-plugin": "^3.2.0", |
||||
|
"mini-css-extract-plugin": "^0.8.0", |
||||
|
"node-sass": "^4.12.0", |
||||
|
"pino-pretty": "^3.2.2", |
||||
|
"sass-loader": "^8.0.0", |
||||
|
"style-loader": "^1.0.0", |
||||
|
"ts-loader": "^6.2.0", |
||||
|
"ts-node": "^8.4.1", |
||||
|
"typescript": "^3.6.4", |
||||
|
"webpack": "^4.41.2", |
||||
|
"webpack-cli": "^3.3.9" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@fortawesome/fontawesome-svg-core": "^1.2.25", |
||||
|
"@fortawesome/free-solid-svg-icons": "^5.11.2", |
||||
|
"@fortawesome/react-fontawesome": "^0.1.7", |
||||
|
"fastify": "^2.10.0", |
||||
|
"fastify-static": "^2.5.0", |
||||
|
"react": "^16.10.2", |
||||
|
"react-dom": "^16.10.2" |
||||
|
} |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
import React, { FC, useState, useEffect } from 'react' |
||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' |
||||
|
import { faEyeSlash } from '@fortawesome/free-solid-svg-icons' |
||||
|
import { Communicator } from '../../../communicator' |
||||
|
import '../../../styles/default.scss' |
||||
|
|
||||
|
interface Props { |
||||
|
communicator: Communicator |
||||
|
} |
||||
|
|
||||
|
const App: FC<Props> = ({ communicator }) => { |
||||
|
const maxCharacters = 256 |
||||
|
const [content, setContent] = useState('') |
||||
|
const [cover, setCover] = useState('') |
||||
|
|
||||
|
const charactersLeft = maxCharacters - content.length |
||||
|
const showCharactersLeft = charactersLeft < (maxCharacters / 2) |
||||
|
const showCharactersStyle = charactersLeft > (maxCharacters * 0.1) ? 'has-text-danger' : '' |
||||
|
const buttonStyle = 'button is-primary is-small' + (charactersLeft < 1 ? ' is-disabled' : '') |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const init = async () => { |
||||
|
try { |
||||
|
await communicator.setHeight(document.body.offsetHeight) |
||||
|
} catch (err) { |
||||
|
console.error('App Component: ', err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
init() |
||||
|
}, []) |
||||
|
|
||||
|
return ( |
||||
|
<div> |
||||
|
<div className="field"> |
||||
|
<div className="control"> |
||||
|
<textarea className="textarea" placeholder="What it do?" value={content} onChange={(e) => setContent(e.target.value)} /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className="field"> |
||||
|
<p className="control has-icons-left"> |
||||
|
<input className="input" type="text" placeholder="Cover Text" value={cover} onChange={(e) => setCover(e.target.value)} /> |
||||
|
<span className="icon is-small is-left"> |
||||
|
<FontAwesomeIcon icon={faEyeSlash} /> |
||||
|
</span> |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
<nav className="level"> |
||||
|
<div className="level-left"> |
||||
|
<div className="level-item"> |
||||
|
{showCharactersLeft && <p className={showCharactersStyle}>{charactersLeft} Chars Left</p>} |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className="level-right"> |
||||
|
<div className="level-item"> |
||||
|
<button className={buttonStyle}>Post!</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</nav> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default App |
@ -0,0 +1,12 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<title>Flexor</title> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
|
</head> |
||||
|
|
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,8 @@ |
|||||
|
import React from 'react' |
||||
|
import { render } from 'react-dom' |
||||
|
import App from './app' |
||||
|
import { Communicator } from '../../../communicator' |
||||
|
|
||||
|
const communicator = new Communicator('f279a4e81056b0db2d0f') |
||||
|
|
||||
|
render(<App communicator={communicator} />, document.getElementById('app')) |
@ -0,0 +1,60 @@ |
|||||
|
import { resolve } from 'path' |
||||
|
import { Configuration } from 'webpack' |
||||
|
import HtmlWebpackPlugin from 'html-webpack-plugin' |
||||
|
import MiniCssExtractPlugin from 'mini-css-extract-plugin' |
||||
|
|
||||
|
const config: Configuration = { |
||||
|
mode: 'development', |
||||
|
devtool: 'eval-source-map', |
||||
|
entry: { |
||||
|
app: resolve(__dirname, './index.tsx'), |
||||
|
}, |
||||
|
output: { |
||||
|
path: resolve(__dirname, '../../../../dist/apps/text-app/'), |
||||
|
// publicPath: '/',
|
||||
|
filename: '[name].js', |
||||
|
}, |
||||
|
optimization: { |
||||
|
splitChunks: { |
||||
|
chunks: 'all', |
||||
|
}, |
||||
|
}, |
||||
|
resolve: { |
||||
|
extensions: ['.ts', '.tsx', '.js'], |
||||
|
}, |
||||
|
module: { |
||||
|
rules: [ |
||||
|
{ |
||||
|
test: /\.ts(x?)$/, |
||||
|
exclude: /node_modules/, |
||||
|
use: 'ts-loader', |
||||
|
}, |
||||
|
{ |
||||
|
test: /\.scss$/, |
||||
|
use: [ |
||||
|
MiniCssExtractPlugin.loader, |
||||
|
'css-loader', |
||||
|
{ |
||||
|
loader: 'sass-loader', |
||||
|
options: { |
||||
|
sourceMap: true, |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
plugins: [ |
||||
|
new HtmlWebpackPlugin({ |
||||
|
title: 'Text App', |
||||
|
hash: true, |
||||
|
template: resolve(__dirname, './index.ejs'), |
||||
|
filename: 'composer.html', |
||||
|
}), |
||||
|
new MiniCssExtractPlugin({ |
||||
|
filename: '[name].css', |
||||
|
}), |
||||
|
], |
||||
|
} |
||||
|
|
||||
|
export default config |
@ -0,0 +1,91 @@ |
|||||
|
export interface MessageContent { |
||||
|
[key: string]: any |
||||
|
height?: number |
||||
|
} |
||||
|
|
||||
|
export interface IncomingMessageData { |
||||
|
name: string |
||||
|
content: MessageContent |
||||
|
publicKey: string |
||||
|
} |
||||
|
|
||||
|
export interface OutgoingMessageData { |
||||
|
name: string |
||||
|
content?: MessageContent |
||||
|
error?: string |
||||
|
settings?: object |
||||
|
} |
||||
|
|
||||
|
interface Listener { |
||||
|
resolve: (value: unknown) => void |
||||
|
reject: (reason: any) => void |
||||
|
once: boolean |
||||
|
} |
||||
|
|
||||
|
interface ListenerCollection { |
||||
|
[name: string]: Listener |
||||
|
} |
||||
|
|
||||
|
interface AppSettings { |
||||
|
[key: string]: any |
||||
|
} |
||||
|
|
||||
|
export class Communicator { |
||||
|
private origin = 'http://localhost:8080' |
||||
|
private publicKey: string |
||||
|
private listeners: ListenerCollection |
||||
|
private settings?: AppSettings |
||||
|
|
||||
|
constructor(publicKey: string) { |
||||
|
this.publicKey = publicKey |
||||
|
this.listeners = {} |
||||
|
|
||||
|
window.addEventListener('message', (event: MessageEvent) => { |
||||
|
if (event.origin !== this.origin) return |
||||
|
|
||||
|
try { |
||||
|
const data = JSON.parse(event.data) as OutgoingMessageData |
||||
|
this.emit(data) |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
return |
||||
|
} |
||||
|
}, false) |
||||
|
} |
||||
|
|
||||
|
private emit(data: OutgoingMessageData) { |
||||
|
const listener = this.listeners[data.name] |
||||
|
|
||||
|
if (listener) { |
||||
|
if (data.content) listener.resolve(data.content) |
||||
|
if (data.error) listener.reject(data.error) |
||||
|
if (listener.once) delete this.listeners[data.name] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async postAndReceive(name: string, content?: MessageContent) { |
||||
|
if (window.parent) { |
||||
|
window.parent.postMessage(JSON.stringify({ |
||||
|
name, |
||||
|
content, |
||||
|
publicKey: this.publicKey, |
||||
|
}), this.origin) |
||||
|
|
||||
|
return new Promise((resolve, reject) => { |
||||
|
this.listeners[name] = { |
||||
|
resolve, |
||||
|
reject, |
||||
|
once: true, |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async init() { |
||||
|
return this.postAndReceive('init') |
||||
|
} |
||||
|
|
||||
|
async setHeight(height: number) { |
||||
|
return this.postAndReceive('setHeight', { height }) |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
import { resolve } from 'path' |
||||
|
import fastify from 'fastify' |
||||
|
import fastifyStatic from 'fastify-static' |
||||
|
|
||||
|
const port = 8082 |
||||
|
const server = fastify({ |
||||
|
logger: { |
||||
|
level: 'info', |
||||
|
prettyPrint: true, |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
server.register(fastifyStatic, { |
||||
|
root: resolve(__dirname, '..', 'apps'), |
||||
|
}) |
||||
|
|
||||
|
const start = async () => { |
||||
|
try { |
||||
|
await server.listen(port) |
||||
|
} catch (err) { |
||||
|
process.exit(1) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
start() |
@ -0,0 +1,33 @@ |
|||||
|
@charset "utf-8"; |
||||
|
|
||||
|
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700&display=swap'); |
||||
|
|
||||
|
// Colors |
||||
|
$orange: hsl(14, 100%, 53%); |
||||
|
$yellow: hsl(48, 100%, 67%); |
||||
|
$green: hsl(141, 65%, 31%); |
||||
|
$turquoise: hsl(171, 100%, 41%); |
||||
|
$cyan: hsl(204, 86%, 53%); |
||||
|
$blue: hsl(217, 72%, 30%); |
||||
|
$purple: hsl(271, 63%, 32%); |
||||
|
$red: hsl(348, 71%, 42%); |
||||
|
$grey: hsl(0, 0%, 48%); |
||||
|
$grey-light: hsl(0, 0%, 71%); |
||||
|
$grey-lighter: hsl(0, 0%, 86%); |
||||
|
$white-ter: hsl(0, 0%, 96%); |
||||
|
$white-bis: hsl(0, 0%, 98%); |
||||
|
|
||||
|
$family-sans-serif: "Open Sans", sans-serif; |
||||
|
$primary: $blue; |
||||
|
$body-size: 14px; |
||||
|
|
||||
|
@import "../../node_modules/bulma/sass/utilities/_all.sass"; |
||||
|
@import "../../node_modules/bulma/sass/base/_all.sass"; |
||||
|
@import "../../node_modules/bulma/sass/form/_all.sass"; |
||||
|
@import "../../node_modules/bulma/sass/elements/button.sass"; |
||||
|
@import "../../node_modules/bulma/sass/elements/icon.sass"; |
||||
|
@import "../../node_modules/bulma/sass/components/level.sass"; |
||||
|
|
||||
|
body { |
||||
|
padding: 10px; |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"outDir": "./dist/server/", |
||||
|
"baseUrl": ".", |
||||
|
"sourceMap": true, |
||||
|
"strict": true, |
||||
|
"module": "commonjs", |
||||
|
"esModuleInterop": true, |
||||
|
"target": "es6", |
||||
|
"jsx": "react", |
||||
|
"resolveJsonModule": true |
||||
|
}, |
||||
|
"include": [ |
||||
|
"src/server/*" |
||||
|
] |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue