From c68d20e5a49fb0059e59b81d06b8d23581d7517f Mon Sep 17 00:00:00 2001 From: Dwayne Harris Date: Thu, 24 Oct 2019 18:24:49 -0400 Subject: [PATCH] WIP --- etc/communicator/index.ts | 81 ++++++++++++ etc/communicator/webpack.config.ts | 27 ++++ package-lock.json | 181 ++++++++++++++------------- package.json | 31 +++-- src/actions/composer.ts | 22 +++- src/components/composer.tsx | 108 ++++++++++++++-- src/components/create-group-step.tsx | 2 +- src/reducers/composer.ts | 12 ++ src/selectors/composer.ts | 2 + src/types/communicator.ts | 17 +++ src/types/store.ts | 2 + src/utils/index.ts | 6 + tsconfig.json | 1 - webpack.config.ts | 5 - 14 files changed, 371 insertions(+), 126 deletions(-) create mode 100644 etc/communicator/index.ts create mode 100644 etc/communicator/webpack.config.ts create mode 100644 src/types/communicator.ts diff --git a/etc/communicator/index.ts b/etc/communicator/index.ts new file mode 100644 index 0000000..1a96bdb --- /dev/null +++ b/etc/communicator/index.ts @@ -0,0 +1,81 @@ +import { OutgoingMessageData, MessageContent } from 'src/types/communicator' + +declare global { + interface Window { NewFlexorCommunicator: (publicKey: string) => Communicator } +} + +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 }) + } +} + +window.NewFlexorCommunicator = publicKey => new Communicator(publicKey) diff --git a/etc/communicator/webpack.config.ts b/etc/communicator/webpack.config.ts new file mode 100644 index 0000000..94a5f28 --- /dev/null +++ b/etc/communicator/webpack.config.ts @@ -0,0 +1,27 @@ +import { resolve } from 'path' +import { Configuration } from 'webpack' + +const config: Configuration = { + mode: 'production', + entry: { + communicator: `${__dirname}/index.ts` + }, + output: { + path: resolve(__dirname, '../../dist'), + filename: '[name].js', + }, + resolve: { + extensions: ['.ts'], + }, + module: { + rules: [ + { + test: /\.ts(x?)$/, + exclude: /node_modules/, + use: 'ts-loader', + }, + ], + }, +} + +export default config diff --git a/package-lock.json b/package-lock.json index 6c04487..dfb1765 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,9 +83,9 @@ } }, "@fortawesome/react-fontawesome": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.5.tgz", - "integrity": "sha512-WYDKTgyAWOncujWhhzhW7k8sgO5Eo2pZTUL51yNzSQNBUwwr6rNKg/JUSE3iebaU1XShHw74aKc1kJ+jvtRNew==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.7.tgz", + "integrity": "sha512-AHWSzOsHBe5vqOkrvs+CKw+8eLl+0XZsVixOWhTPpGpOA8WQUbVU6J9cmtAvTaxUU5OIf+rgxxF8ZKc3BVldxg==", "requires": { "prop-types": "^15.5.10" } @@ -158,9 +158,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.16.9", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", - "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "version": "4.16.10", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.10.tgz", + "integrity": "sha512-gM6evDj0OvTILTRKilh9T5dTaGpv1oYiFcJAfgSejuMJgGJUsD9hKEU2lB4aiTNy4WwChxRnjfYFuBQsULzsJw==", "dev": true, "requires": { "@types/node": "*", @@ -237,9 +237,9 @@ } }, "@types/lodash": { - "version": "4.14.141", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.141.tgz", - "integrity": "sha512-v5NYIi9qEbFEUpCyikmnOYe4YlP8BMUdTcNCAquAKzu+FA7rZ1onj9x80mbnDdOW/K5bFf3Tv5kJplP33+gAbQ==", + "version": "4.14.144", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.144.tgz", + "integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg==", "dev": true }, "@types/mime": { @@ -289,37 +289,28 @@ "dev": true }, "@types/react": { - "version": "16.9.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.5.tgz", - "integrity": "sha512-jQ12VMiFOWYlp+j66dghOWcmDDwhca0bnlcTxS4Qz/fh5gi6wpaZDthPEu/Gc/YlAuO87vbiUXL8qKstFvuOaA==", + "version": "16.9.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.9.tgz", + "integrity": "sha512-L+AudFJkDukk+ukInYvpoAPyJK5q1GanFOINOJnM0w6tUgITuWvJ4jyoBPFL7z4/L8hGLd+K/6xR5uUjXu0vVg==", "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^2.2.0" } }, - "@types/react-avatar-editor": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/@types/react-avatar-editor/-/react-avatar-editor-10.3.4.tgz", - "integrity": "sha512-yulle6pZw+7jCxq156WbIqT0qwuLtzxt53fCIm1op0IFUotChAYYSZmKQR2+4jjhToHrX53nR83Slvr8H6ar5Q==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, "@types/react-dom": { - "version": "16.9.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.1.tgz", - "integrity": "sha512-1S/akvkKr63qIUWVu5IKYou2P9fHLb/P2VAwyxVV85JGaGZTcUniMiTuIqM3lXFB25ej6h+CYEQ27ERVwi6eGA==", + "version": "16.9.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.2.tgz", + "integrity": "sha512-hgPbBoI1aTSTvZwo8HYw35UaTldW6n2ETLvHAcfcg1FaOuBV3olmyCe5eMpx2WybWMBPv0MdU2t5GOcQhP+3zA==", "dev": true, "requires": { "@types/react": "*" } }, "@types/react-redux": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.4.tgz", - "integrity": "sha512-SUV/7d+4L7C1Db/D4pqASgN1V1U2HnDEhEol9lYpPSguS76xFboZzf5ha2hTz6v31cUewyC7WksMh1q8JxhebQ==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.5.tgz", + "integrity": "sha512-ZoNGQMDxh5ENY7PzU7MVonxDzS1l/EWiy8nUhDqxFqUZn4ovboCyvk4Djf68x6COb7vhGTKjyjxHxtFdAA5sUA==", "dev": true, "requires": { "@types/hoist-non-react-statics": "^3.3.0", @@ -435,9 +426,9 @@ } }, "@types/webpack": { - "version": "4.39.2", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.2.tgz", - "integrity": "sha512-3c7+vcmyyIi3RBoOdXs8k3E9rQVIy6yOBqK0DFk6lnJ76JUfbDBWbEf1JflzyPQf56W4ToE+2YPnbxbucniW5w==", + "version": "4.39.5", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.5.tgz", + "integrity": "sha512-9twG6D97ao13MBLvigwfBJe6rxtb04UY3TcYHBYkW5sXZjUrNhqIRxLYg74VzK/YAE8xlVhOyd+3Whr7E5RrBA==", "dev": true, "requires": { "@types/anymatch": "*", @@ -458,9 +449,9 @@ } }, "@types/webpack-dev-server": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.1.7.tgz", - "integrity": "sha512-VIRkDkBDuOkYRXQ1EG/etisQ3odo6pcjSmA1Si4VYANuNhSBsLxfuPGeGERwCx1nDKxK3aaXnicPzi0gUvxUaw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.4.0.tgz", + "integrity": "sha512-cNxiXfGnMxVgXOjmo/SQdsIX2Muan0A44AvPgVfz9y1PfWogOJGEy+/nFkrF/luvFxykJXT+fZYPpyuIGZtRZg==", "dev": true, "requires": { "@types/connect-history-api-fallback": "*", @@ -1025,10 +1016,13 @@ "dev": true }, "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } }, "async-each": { "version": "1.0.3", @@ -1187,9 +1181,9 @@ } }, "bluebird": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz", - "integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "dev": true }, "bn.js": { @@ -1376,9 +1370,9 @@ "dev": true }, "bulma": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz", - "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.0.tgz", + "integrity": "sha512-nhf3rGyiZh/VM7FrSJ/5KeLlfaFkXz0nYcXriynfPH4vVpnxnqyEwaNGdNCVzHyyCA3cHgkQAMpdF/SFbFGZfA==", "dev": true }, "bytes": { @@ -1705,9 +1699,9 @@ } }, "commander": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", - "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "commondir": { @@ -5923,14 +5917,31 @@ } }, "portfinder": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.24.tgz", - "integrity": "sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg==", + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", "dev": true, "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "posix-character-classes": { @@ -6225,32 +6236,24 @@ "integrity": "sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg==" }, "react": { - "version": "16.10.2", - "resolved": "https://registry.npmjs.org/react/-/react-16.10.2.tgz", - "integrity": "sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw==", + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.11.0.tgz", + "integrity": "sha512-M5Y8yITaLmU0ynd0r1Yvfq98Rmll6q8AxaEe88c8e7LxO8fZ2cNgmFt0aGAS9wzf1Ao32NKXtCl+/tVVtkxq6g==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2" } }, - "react-avatar-editor": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/react-avatar-editor/-/react-avatar-editor-11.0.7.tgz", - "integrity": "sha512-GbNYBd1/L1QyuU9VRvOW0hSkW1R0XSneOWZFgqI5phQf6dX+dF/G3/AjiJ0hv3JWh2irMQ7DL0oYDKzwtTnNBQ==", - "requires": { - "prop-types": "^15.5.8" - } - }, "react-dom": { - "version": "16.10.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.10.2.tgz", - "integrity": "sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw==", + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.11.0.tgz", + "integrity": "sha512-nrRyIUE1e7j8PaXSPtyRKtz+2y9ubW/ghNgqKFHHAHaeP0fpF5uXR+sq8IMRHC+ZUxw7W9NyCDTBtwWxvkb0iA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.16.2" + "scheduler": "^0.17.0" } }, "react-is": { @@ -7031,9 +7034,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.17.0.tgz", + "integrity": "sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -7815,9 +7818,9 @@ } }, "terser": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.8.tgz", - "integrity": "sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz", + "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==", "dev": true, "requires": { "commander": "^2.20.0", @@ -7853,9 +7856,9 @@ } }, "thunky": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", - "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, "timers-browserify": { @@ -8049,9 +8052,9 @@ "dev": true }, "typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", - "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", + "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", "dev": true }, "uglify-js": { @@ -8335,9 +8338,9 @@ } }, "webpack": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.0.tgz", - "integrity": "sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g==", + "version": "4.41.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz", + "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", @@ -8471,9 +8474,9 @@ } }, "webpack-bundle-analyzer": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.5.2.tgz", - "integrity": "sha512-g9spCNe25QYUVqHRDkwG414GTok2m7pTTP0wr6l0J50Z3YLS04+BGodTqqoVBL7QfU/U/9p/oiI5XFOyfZ7S/A==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz", + "integrity": "sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g==", "dev": true, "requires": { "acorn": "^6.0.7", @@ -8543,9 +8546,9 @@ } }, "webpack-dev-server": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz", - "integrity": "sha512-0xxogS7n5jHDQWy0WST0q6Ykp7UGj4YvWh+HVN71JoE7BwPxMZrwgraBvmdEMbDVMBzF0u+mEzn8TQzBm5NYJQ==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", + "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -8566,7 +8569,7 @@ "loglevel": "^1.6.4", "opn": "^5.5.0", "p-retry": "^3.0.1", - "portfinder": "^1.0.24", + "portfinder": "^1.0.25", "schema-utils": "^1.0.0", "selfsigned": "^1.10.7", "semver": "^6.3.0", diff --git a/package.json b/package.json index e2ed22a..aa72456 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "npm run build:dev", "build:dev": "webpack --config webpack.config.ts", "build:prod": "webpack --mode production --config webpack.config.ts", + "build:communicator": "webpack --config etc/communicator/webpack.config.ts", "deploy:batch:dev": "az storage blob upload-batch -s ./dist/ -d $web --account-name flexordev", "deploy:batch:prod": "az storage blob upload-batch -s ./dist/ -d $web --account-name flexor", "deploy:config:dev": "az storage blob upload -f ./config/dev.json -c flexordev -n config.json", @@ -18,20 +19,19 @@ "devDependencies": { "@types/classnames": "^2.2.9", "@types/html-webpack-plugin": "^3.2.1", - "@types/lodash": "^4.14.141", + "@types/lodash": "^4.14.144", "@types/mini-css-extract-plugin": "^0.8.0", - "@types/react": "^16.9.5", - "@types/react-avatar-editor": "^10.3.4", - "@types/react-dom": "^16.9.1", - "@types/react-redux": "^7.1.4", + "@types/react": "^16.9.9", + "@types/react-dom": "^16.9.2", + "@types/react-redux": "^7.1.5", "@types/react-router-dom": "^5.1.0", "@types/redux-logger": "^3.0.7", "@types/uuid": "^3.4.5", - "@types/webpack": "^4.39.2", + "@types/webpack": "^4.39.5", "@types/webpack-bundle-analyzer": "^2.13.3", - "@types/webpack-dev-server": "^3.1.7", + "@types/webpack-dev-server": "^3.4.0", "@types/zxcvbn": "^4.4.0", - "bulma": "^0.7.5", + "bulma": "^0.8.0", "css-loader": "^3.2.0", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.8.0", @@ -41,26 +41,25 @@ "style-loader": "^1.0.0", "ts-loader": "^6.2.0", "ts-node": "^8.4.1", - "typescript": "^3.6.3", - "webpack": "^4.41.0", - "webpack-bundle-analyzer": "^3.5.2", + "typescript": "^3.6.4", + "webpack": "^4.41.2", + "webpack-bundle-analyzer": "^3.6.0", "webpack-cli": "^3.3.9", - "webpack-dev-server": "^3.8.2" + "webpack-dev-server": "^3.9.0" }, "dependencies": { "@azure/storage-blob": "^10.5.0", "@fortawesome/fontawesome-common-types": "^0.2.25", "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-solid-svg-icons": "^5.11.2", - "@fortawesome/react-fontawesome": "^0.1.5", + "@fortawesome/react-fontawesome": "^0.1.7", "classnames": "^2.2.6", "history": "^4.10.1", "lodash": "^4.17.15", "moment": "^2.24.0", "re-reselect": "^3.4.0", - "react": "^16.10.2", - "react-avatar-editor": "^11.0.7", - "react-dom": "^16.10.2", + "react": "^16.11.0", + "react-dom": "^16.11.0", "react-redux": "^7.1.1", "react-router-dom": "^5.1.2", "redux": "^4.0.4", diff --git a/src/actions/composer.ts b/src/actions/composer.ts index 62a796a..2017f0f 100644 --- a/src/actions/composer.ts +++ b/src/actions/composer.ts @@ -16,7 +16,17 @@ export interface SetSelectedInstallationAction extends Action { payload?: string } -export type ComposerActions = SetInstallationsAction | SetSelectedInstallationAction +export interface SetHeightAction extends Action { + type: 'COMPOSER_SET_HEIGHT', + payload: number +} + +export interface SetErrorAction extends Action { + type: 'COMPOSER_SET_ERROR', + payload?: string +} + +export type ComposerActions = SetInstallationsAction | SetSelectedInstallationAction | SetHeightAction | SetErrorAction export const setInstallations = (installations: string[]): SetInstallationsAction => ({ type: 'COMPOSER_SET_INSTALLATIONS', @@ -28,6 +38,16 @@ export const setSelectedInstallation = (installation?: string): SetSelectedInsta payload: installation, }) +export const setHeight = (height: number): SetHeightAction => ({ + type: 'COMPOSER_SET_HEIGHT', + payload: height, +}) + +export const setError = (error?: string): SetErrorAction => ({ + type: 'COMPOSER_SET_ERROR', + payload: error, +}) + interface FetchInstallationsResponse { installations: Installation[] } diff --git a/src/components/composer.tsx b/src/components/composer.tsx index be99eac..eb07190 100644 --- a/src/components/composer.tsx +++ b/src/components/composer.tsx @@ -1,23 +1,101 @@ -import React, { FC, useEffect } from 'react' +import React, { FC, useEffect, useRef } from 'react' import { useSelector, useDispatch } from 'react-redux' +import classNames from 'classnames' -import { useConfig } from 'src/hooks' -import { fetchInstallations, setSelectedInstallation } from 'src/actions/composer' -import { getInstallations, getSelectedInstallation } from 'src/selectors/composer' -import { AppState, Installation } from 'src/types' +import { getOrigin } from 'src/utils' +import { useConfig, useDeepCompareEffect } from 'src/hooks' +import { fetchInstallations, setSelectedInstallation, setHeight as setComposerHeight, setError as setComposerError } from 'src/actions/composer' +import { getInstallations, getSelectedInstallation, getError, getHeight as getComposerHeight } from 'src/selectors/composer' +import { AppState, Installation, ClassDictionary } from 'src/types' +import { IncomingMessageData, OutgoingMessageData } from 'src/types/communicator' const Composer: FC = () => { const installations = useSelector(getInstallations) - const selected = useSelector(getSelectedInstallation) + const installation = useSelector(getSelectedInstallation) + const height = useSelector(getComposerHeight) + const error = useSelector(getError) const config = useConfig() const dispatch = useDispatch() + const ref = useRef(null) + + const composerUrl = installation ? installation.app.composerUrl : undefined + const showComposer = !!composerUrl && !error + + const composerClasses: ClassDictionary = { + composer: true, + 'composer-empty': !showComposer, + } useEffect(() => { dispatch(fetchInstallations()) }, []) + useDeepCompareEffect(() => { + if (!composerUrl) return + if (!installation) return + if (error) return + + const listener = async (event: MessageEvent) => { + const origin = getOrigin(composerUrl) + if (event.origin !== origin) return + + const postMessage = (message: OutgoingMessageData) => { + if (ref.current && ref.current.contentWindow) { + ref.current.contentWindow.postMessage(JSON.stringify(message), origin) + } + } + + let data: IncomingMessageData | undefined + + try { + data = JSON.parse(event.data) + } catch (err) { + dispatch(setComposerError('Invalid payload')) + return + } + + if (!data) return + + if (data.publicKey !== installation.app.publicKey) { + const message = 'Invalid publicKey' + + dispatch(setComposerError(message)) + + postMessage({ + name: data.name, + error: message, + }) + } + + switch (data.name) { + case 'init': + postMessage({ + name: data.name, + settings: installation.settings, + }) + + break + case 'setHeight': + const { height = 100 } = data.content + dispatch(setComposerHeight(Math.max(Math.min(height, 400), 100))) + + postMessage({ + name: data.name, + }) + + break + } + } + + window.addEventListener('message', listener, false) + + return () => { + window.removeEventListener('message', listener, false) + } + }, [installation, error]) + const handleClick = (id: string) => { - if (selected && selected.id === id) { + if (installation && installation.id === id) { dispatch(setSelectedInstallation()) return } @@ -27,15 +105,19 @@ const Composer: FC = () => { return (
-
-

{selected ? selected.app.name : 'Choose an app.'}

+
+ {showComposer && +