From dd07f6740775d50888d15a4600827aea9065cf5c Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie Date: Mon, 6 Dec 2021 17:22:09 +0100 Subject: [PATCH 01/15] Add onDiscovery function --- server/services/alexa/index.js | 32 ++++++++ .../services/alexa/lib/alexa.onDiscovery.js | 34 ++++++++ server/services/alexa/lib/deviceMappings.js | 25 ++++++ server/services/alexa/lib/index.js | 17 ++++ .../services/alexa/lib/syncDeviceConverter.js | 51 ++++++++++++ server/services/alexa/package-lock.json | 26 +++++++ server/services/alexa/package.json | 21 +++++ server/services/index.js | 1 + .../alexa/lib/alexa.onDiscovery.test.js | 77 +++++++++++++++++++ 9 files changed, 284 insertions(+) create mode 100644 server/services/alexa/index.js create mode 100644 server/services/alexa/lib/alexa.onDiscovery.js create mode 100644 server/services/alexa/lib/deviceMappings.js create mode 100644 server/services/alexa/lib/index.js create mode 100644 server/services/alexa/lib/syncDeviceConverter.js create mode 100644 server/services/alexa/package-lock.json create mode 100644 server/services/alexa/package.json create mode 100644 server/test/services/alexa/lib/alexa.onDiscovery.test.js diff --git a/server/services/alexa/index.js b/server/services/alexa/index.js new file mode 100644 index 0000000000..9ce854b332 --- /dev/null +++ b/server/services/alexa/index.js @@ -0,0 +1,32 @@ +const logger = require('../../utils/logger'); +const AlexaHandler = require('./lib'); + +module.exports = function AlexaService(gladys, serviceId) { + const alexaHandler = new AlexaHandler(gladys, serviceId); + + /** + * @public + * @description This function starts service + * @example + * gladys.services['alexa'].start(); + */ + async function start() { + logger.info('starting Alexa service'); + } + + /** + * @public + * @description This function stops the service + * @example + * gladys.services['alexa'].stop(); + */ + async function stop() { + logger.info('stopping Alexa service'); + } + + return Object.freeze({ + start, + stop, + alexaHandler, + }); +}; diff --git a/server/services/alexa/lib/alexa.onDiscovery.js b/server/services/alexa/lib/alexa.onDiscovery.js new file mode 100644 index 0000000000..5df60e9793 --- /dev/null +++ b/server/services/alexa/lib/alexa.onDiscovery.js @@ -0,0 +1,34 @@ +const uuid = require('uuid'); +const { syncDeviceConverter } = require('./syncDeviceConverter'); + +/** + * @public + * @description Returns devices formatted the Amazon Alexa way. + * @example + * onDiscovery(); + */ +function onDiscovery() { + const gladysDevices = Object.values(this.gladys.stateManager.state.device).map((store) => store.get()); + // Convert it to Alexa devices + const endpoints = gladysDevices.map((d) => syncDeviceConverter(d)).filter((d) => d !== null); + + const response = { + event: { + header: { + namespace: 'Alexa.Discovery', + name: 'Discover.Response', + payloadVersion: '3', + messageId: uuid.v4(), + }, + payload: { + endpoints, + }, + }, + }; + + return response; +} + +module.exports = { + onDiscovery, +}; diff --git a/server/services/alexa/lib/deviceMappings.js b/server/services/alexa/lib/deviceMappings.js new file mode 100644 index 0000000000..68da9f0788 --- /dev/null +++ b/server/services/alexa/lib/deviceMappings.js @@ -0,0 +1,25 @@ +const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../../../utils/constants'); + +const mappings = { + [DEVICE_FEATURE_CATEGORIES.LIGHT]: { + category: 'LIGHT', + capabilities: { + [DEVICE_FEATURE_TYPES.LIGHT.BINARY]: { + type: 'AlexaInterface', + interface: 'Alexa.PowerController', + version: '3', + properties: { + supported: [ + { + name: 'powerState', + }, + ], + proactivelyReported: true, + retrievable: true, + }, + }, + }, + }, +}; + +module.exports = mappings; diff --git a/server/services/alexa/lib/index.js b/server/services/alexa/lib/index.js new file mode 100644 index 0000000000..d7a3501148 --- /dev/null +++ b/server/services/alexa/lib/index.js @@ -0,0 +1,17 @@ +const { onDiscovery } = require('./alexa.onDiscovery'); + +/** + * @description Add ability to connect to Alexa. + * @param {Object} gladys - Gladys instance. + * @param {string} serviceId - UUID of the service in DB. + * @example + * const alexaHandler = new AlexaHandler(gladys, serviceId); + */ +const AlexaHandler = function AlexaHandler(gladys, serviceId) { + this.gladys = gladys; + this.serviceId = serviceId; +}; + +AlexaHandler.prototype.onDiscovery = onDiscovery; + +module.exports = AlexaHandler; diff --git a/server/services/alexa/lib/syncDeviceConverter.js b/server/services/alexa/lib/syncDeviceConverter.js new file mode 100644 index 0000000000..e5cc34aa8a --- /dev/null +++ b/server/services/alexa/lib/syncDeviceConverter.js @@ -0,0 +1,51 @@ +const get = require('get-value'); +const mappings = require('./deviceMappings'); + +/** + * @description Format a Gladys device the Alexa way + * @example const deviceAlexa = syncDeviceConverter(device); + * @param {Object} device - The device to convert. + * @returns {Object|null} Return the alexa formatted device or null if not handled. + */ +function syncDeviceConverter(device) { + const endpoint = { + endpointId: device.selector, + friendlyName: device.name, + displayCategories: [], + capabilities: [], + connections: [], + }; + + // We create a unique map of device features + const uniqueDeviceFeatures = new Map(); + device.features.forEach((feature) => { + const key = `${feature.category}${feature.type}`; + if (!uniqueDeviceFeatures.has(key)) { + uniqueDeviceFeatures.set(key, feature); + } + }); + + uniqueDeviceFeatures.forEach((value, key) => { + const displayCategory = get(mappings, `${value.category}.category`); + // We add a display category if not already present + if (displayCategory && endpoint.displayCategories.indexOf(displayCategory) === -1) { + endpoint.displayCategories.push(displayCategory); + } + // we get the capability if handled + const capability = get(mappings, `${value.category}.capabilities.${value.type}`); + if (capability) { + endpoint.capabilities.push(capability); + } + }); + + // if nothing is handled in this device, return null + if (endpoint.capabilities.length === 0) { + return null; + } + // otherwise, return the full endpoint + return endpoint; +} + +module.exports = { + syncDeviceConverter, +}; diff --git a/server/services/alexa/package-lock.json b/server/services/alexa/package-lock.json new file mode 100644 index 0000000000..36234e65d3 --- /dev/null +++ b/server/services/alexa/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "gladys-alexa", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "get-value": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-3.0.1.tgz", + "integrity": "sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==", + "requires": { + "isobject": "^3.0.1" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } +} diff --git a/server/services/alexa/package.json b/server/services/alexa/package.json new file mode 100644 index 0000000000..de38bca2ae --- /dev/null +++ b/server/services/alexa/package.json @@ -0,0 +1,21 @@ +{ + "name": "gladys-alexa", + "version": "1.0.0", + "main": "index.js", + "os": [ + "darwin", + "linux", + "freebsd", + "win32" + ], + "cpu": [ + "x64", + "arm", + "arm64" + ], + "scripts": {}, + "dependencies": { + "get-value": "^3.0.1", + "uuid": "^8.3.2" + } +} diff --git a/server/services/index.js b/server/services/index.js index 7f5480b3f8..e5818b3d92 100644 --- a/server/services/index.js +++ b/server/services/index.js @@ -1,3 +1,4 @@ +module.exports.alexa = require('./alexa'); module.exports.example = require('./example'); module.exports.caldav = require('./caldav'); module.exports.openweather = require('./openweather'); diff --git a/server/test/services/alexa/lib/alexa.onDiscovery.test.js b/server/test/services/alexa/lib/alexa.onDiscovery.test.js new file mode 100644 index 0000000000..87e3083d86 --- /dev/null +++ b/server/test/services/alexa/lib/alexa.onDiscovery.test.js @@ -0,0 +1,77 @@ +const sinon = require('sinon'); +const { expect } = require('chai'); +const get = require('get-value'); + +const { assert, fake } = sinon; +const AlexaHandler = require('../../../../services/alexa/lib'); + +const gladys = { + stateManager: { + state: { + device: {}, + }, + }, +}; +const serviceId = 'd1e45425-fe25-4968-ac0f-bc695d5202d9'; + +describe('alexa.onDiscovery', () => { + beforeEach(() => { + sinon.reset(); + gladys.stateManager.state.device = {}; + }); + + it('return one light with on/off capability', async () => { + gladys.stateManager.state.device = { + device_1: { + get: fake.returns({ + name: 'Device 1', + selector: 'device-1', + external_id: 'device-1-external-id', + features: [ + { + category: 'light', + type: 'binary', + }, + ], + model: 'device-model', + room: { + name: 'living-room', + }, + }), + }, + }; + + const alexaHandler = new AlexaHandler(gladys, serviceId); + const result = alexaHandler.onDiscovery(); + const exptectedResult = { + event: { + header: { + namespace: 'Alexa.Discovery', + name: 'Discover.Response', + payloadVersion: '3', + messageId: get(result, 'event.header.messageId'), + }, + payload: { + endpoints: [ + { + endpointId: 'device-1', + friendlyName: 'Device 1', + displayCategories: ['LIGHT'], + capabilities: [ + { + type: 'AlexaInterface', + interface: 'Alexa.PowerController', + version: '3', + properties: { supported: [{ name: 'powerState' }], proactivelyReported: true, retrievable: true }, + }, + ], + connections: [], + }, + ], + }, + }, + }; + expect(result).to.deep.eq(exptectedResult); + assert.calledOnce(gladys.stateManager.state.device.device_1.get); + }); +}); From af375ce5b936356b2eb2b9181d8ac382e6f8bea8 Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie Date: Mon, 10 Jan 2022 13:21:56 +0100 Subject: [PATCH 02/15] add alexa message handler --- .../lib/gateway/gateway.handleAlexaMessage.js | 53 +++++++++++++++++++ .../lib/gateway/gateway.handleNewMessage.js | 5 ++ server/lib/gateway/index.js | 3 ++ server/utils/constants.js | 1 + 4 files changed, 62 insertions(+) create mode 100644 server/lib/gateway/gateway.handleAlexaMessage.js diff --git a/server/lib/gateway/gateway.handleAlexaMessage.js b/server/lib/gateway/gateway.handleAlexaMessage.js new file mode 100644 index 0000000000..992c2e9571 --- /dev/null +++ b/server/lib/gateway/gateway.handleAlexaMessage.js @@ -0,0 +1,53 @@ +const get = require('get-value'); +const logger = require('../../utils/logger'); +const { SYSTEM_VARIABLE_NAMES } = require('../../utils/constants'); + +/** + * @description Handle a new Gladys Alexa Gateway message. + * @param {Object} data - Gateway message. + * @param {Object} rawMessage - Message with metadata. + * @param {Function} cb - Callback. + * @returns {Promise} Resolve when finished. + * @example + * handleNewMessage({ + * type: 'gladys-api-call', + * }); + */ +async function handleAlexaMessage(data, rawMessage, cb) { + const service = this.serviceManager.getService('alexa'); + try { + const body = { + ...data.data, + user: { + id: rawMessage.sender_id, + local_user_id: rawMessage.local_user_id, + }, + }; + // save that the user is connected to gateway alexa + if (!this.alexaConnected) { + await this.variable.setValue( + SYSTEM_VARIABLE_NAMES.GLADYS_GATEWAY_ALEXA_USER_IS_CONNECTED_WITH_GATEWAY, + rawMessage.sender_id, + ); + this.alexaConnected = true; + } + const directiveNamespace = get(body, 'directive.header.namespace'); + let response; + if (directiveNamespace === 'Alexa.Discovery') { + response = service.alexaHandler.onDiscovery(); + } else { + response = { + status: 400, + }; + } + + cb(response); + } catch (e) { + logger.error(e); + cb({ status: 400 }); + } +} + +module.exports = { + handleAlexaMessage, +}; diff --git a/server/lib/gateway/gateway.handleNewMessage.js b/server/lib/gateway/gateway.handleNewMessage.js index 99c75a370a..d10b12c75c 100644 --- a/server/lib/gateway/gateway.handleNewMessage.js +++ b/server/lib/gateway/gateway.handleNewMessage.js @@ -84,6 +84,11 @@ async function handleNewMessage(data, rawMessage, cb) { if (data.type === 'gladys-open-api' && data.action === 'google-home-request') { this.handleGoogleHomeMessage(data, rawMessage, cb); } + + // if the message is a Alexa request + if (data.type === 'gladys-open-api' && data.action === 'alexa-request') { + this.handleAlexaMessage(data, rawMessage, cb); + } } module.exports = { diff --git a/server/lib/gateway/index.js b/server/lib/gateway/index.js index 12f2c07933..5142c55d44 100644 --- a/server/lib/gateway/index.js +++ b/server/lib/gateway/index.js @@ -12,6 +12,7 @@ const { backup } = require('./gateway.backup'); const { forwardDeviceStateToGoogleHome } = require('./gateway.forwardDeviceStateToGoogleHome'); const { checkIfBackupNeeded } = require('./gateway.checkIfBackupNeeded'); const { handleGoogleHomeMessage } = require('./gateway.handleGoogleHomeMessage'); +const { handleAlexaMessage } = require('./gateway.handleAlexaMessage'); const { handleNewMessage } = require('./gateway.handleNewMessage'); const { login } = require('./gateway.login'); const { loginTwoFactor } = require('./gateway.loginTwoFactor'); @@ -42,6 +43,7 @@ const Gateway = function Gateway(variable, event, system, sequelize, config, use this.restoreInProgress = false; this.usersKeys = []; this.googleHomeConnected = false; + this.alexaConnected = false; this.forwardStateToGoogleHomeTimeouts = new Map(); this.googleHomeForwardStateTimeout = 5 * 1000; this.GladysGatewayClient = GladysGatewayClient; @@ -59,6 +61,7 @@ const Gateway = function Gateway(variable, event, system, sequelize, config, use Gateway.prototype.backup = backup; Gateway.prototype.checkIfBackupNeeded = checkIfBackupNeeded; Gateway.prototype.handleGoogleHomeMessage = handleGoogleHomeMessage; +Gateway.prototype.handleAlexaMessage = handleAlexaMessage; Gateway.prototype.forwardDeviceStateToGoogleHome = forwardDeviceStateToGoogleHome; Gateway.prototype.handleNewMessage = handleNewMessage; Gateway.prototype.login = login; diff --git a/server/utils/constants.js b/server/utils/constants.js index f7d6d2181e..919f58f304 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -47,6 +47,7 @@ const SYSTEM_VARIABLE_NAMES = { GLADYS_GATEWAY_USERS_KEYS: 'GLADYS_GATEWAY_USERS_KEYS', GLADYS_GATEWAY_GOOGLE_HOME_USER_IS_CONNECTED_WITH_GATEWAY: 'GLADYS_GATEWAY_GOOGLE_HOME_USER_IS_CONNECTED_WITH_GATEWAY', + GLADYS_GATEWAY_ALEXA_USER_IS_CONNECTED_WITH_GATEWAY: 'GLADYS_GATEWAY_ALEXA_USER_IS_CONNECTED_WITH_GATEWAY', TIMEZONE: 'TIMEZONE', }; From fb50d5c4c88892f40215cda1240542d3e7767f09 Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie Date: Tue, 22 Mar 2022 13:13:14 +0000 Subject: [PATCH 03/15] Add Alexa Oauth UI --- front/package-lock.json | 6 +- front/package.json | 2 +- front/src/components/app.jsx | 2 + front/src/components/header/index.jsx | 3 +- front/src/config/i18n/en.json | 14 ++ front/src/config/i18n/fr.json | 14 ++ .../integration/all/alexa-gateway/Layout.jsx | 15 ++ .../integration/all/alexa-gateway/index.js | 135 ++++++++++++++++++ .../integration/all/alexa-gateway/style.css | 3 + 9 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 front/src/routes/integration/all/alexa-gateway/Layout.jsx create mode 100644 front/src/routes/integration/all/alexa-gateway/index.js create mode 100644 front/src/routes/integration/all/alexa-gateway/style.css diff --git a/front/package-lock.json b/front/package-lock.json index 8dddf23f54..4083399848 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -2571,9 +2571,9 @@ "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" }, "@gladysassistant/gladys-gateway-js": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@gladysassistant/gladys-gateway-js/-/gladys-gateway-js-3.5.0.tgz", - "integrity": "sha512-4nd1ueZW4izHnl7tx1zPHib2RDQDNIb9B2sF7sJPJ+4Kvj1CCAr/BlYnQbhT2vZsHvdspm7lUsE3foniS9hHTA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@gladysassistant/gladys-gateway-js/-/gladys-gateway-js-3.6.0.tgz", + "integrity": "sha512-bmcvQa79ks4DU5JyBQ9jOwzAg625lz4p1l3TEdkjlD3sPl5KDu83XS8PMML2k2EFIwnNG1B25Jb7sOLcHiNlvw==", "requires": { "@ctrlpanel/pbkdf2": "^1.0.0", "array-buffer-to-hex": "^1.0.0", diff --git a/front/package.json b/front/package.json index 70a4c71ca9..9ee9ad5227 100644 --- a/front/package.json +++ b/front/package.json @@ -43,7 +43,7 @@ "sirv-cli": "^1.0.12" }, "dependencies": { - "@gladysassistant/gladys-gateway-js": "^3.5.0", + "@gladysassistant/gladys-gateway-js": "^3.6.0", "@gladysassistant/theme-optimized": "^1.0.3", "@jaames/iro": "^5.5.2", "@yaireo/tagify": "^4.5.0", diff --git a/front/src/components/app.jsx b/front/src/components/app.jsx index 90b46113a3..0ab4944604 100644 --- a/front/src/components/app.jsx +++ b/front/src/components/app.jsx @@ -27,6 +27,7 @@ import GatewayForgotPassword from '../routes/gateway-forgot-password'; import GatewayResetPassword from '../routes/gateway-reset-password'; import GatewayConfirmEmail from '../routes/gateway-confirm-email'; import GoogleHomeGateway from '../routes/integration/all/google-home-gateway'; +import AlexaGateway from '../routes/integration/all/alexa-gateway'; import SignupWelcomePage from '../routes/signup/1-welcome'; import SignupCreateAccountLocal from '../routes/signup/2-create-account-local'; @@ -234,6 +235,7 @@ const AppRouter = connect( + diff --git a/front/src/components/header/index.jsx b/front/src/components/header/index.jsx index 9426c17525..1a6d438f5d 100644 --- a/front/src/components/header/index.jsx +++ b/front/src/components/header/index.jsx @@ -19,7 +19,8 @@ const PAGES_WITHOUT_HEADER = [ '/subscribe-gateway', '/gateway-configure-two-factor', '/confirm-email', - '/dashboard/integration/device/google-home/authorize' + '/dashboard/integration/device/google-home/authorize', + '/dashboard/integration/device/alexa/authorize' ]; const Header = ({ ...props }) => { diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 023361fd43..7dd7ea4d33 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -542,6 +542,20 @@ "cancelButton": "Cancel", "connectButton": "Link" }, + "alexa": { + "title": "Gladys Assistant", + "cardTitle": "Do you want to connect Gladys Assistant to Amazon Alexa?", + "description": "By signin in, you are authorizing Alexa to access your devices.", + "error": "An error occured. Please retry !", + "connectedAs": "Connected as", + "googleWillBeAble": "Alexa will be able:", + "seeDevices": "List all your devices", + "controlDevices": "Control all your devices", + "getNewDeviceValues": "Periodically refresh your device states", + "privacyPolicy": "Read Alexa's privacy policy.", + "cancelButton": "Cancel", + "connectButton": "Link" + }, "zwave": { "title": "Z-Wave", "description": "Control your Z-Wave devices.", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 9af6c0adc5..0e987ca398 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -669,6 +669,20 @@ "cancelButton": "Annuler", "connectButton": "Lier" }, + "alexa": { + "title": "Gladys Assistant", + "cardTitle": "Voulez-vous connecter Gladys Assistant à Amazon Alexa ?", + "description": "En vous connectant, vous authorizez Alexa à accéder à vos appareils.", + "error": "Une erreur est survenue. Merci de réessayer.", + "connectedAs": "Connecté en tant que", + "googleWillBeAble": "Amazon Alexa pourra :", + "seeDevices": "Lister les appareils présents chez vous", + "controlDevices": "Contrôler vos appareils", + "getNewDeviceValues": "Récupérer périodiquement les états de vos appareils", + "privacyPolicy": "Lire la politique de confidentialité d'Alexa.", + "cancelButton": "Annuler", + "connectButton": "Lier" + }, "zwave": { "title": "Z-Wave", "description": "Contrôlez vos appareils Z-Wave.", diff --git a/front/src/routes/integration/all/alexa-gateway/Layout.jsx b/front/src/routes/integration/all/alexa-gateway/Layout.jsx new file mode 100644 index 0000000000..ee758ce3d4 --- /dev/null +++ b/front/src/routes/integration/all/alexa-gateway/Layout.jsx @@ -0,0 +1,15 @@ +const Layout = ({ children, ...props }) => ( +
+
+
+
+
+
{children}
+
+
+
+
+
+); + +export default Layout; diff --git a/front/src/routes/integration/all/alexa-gateway/index.js b/front/src/routes/integration/all/alexa-gateway/index.js new file mode 100644 index 0000000000..7ecf81d5f7 --- /dev/null +++ b/front/src/routes/integration/all/alexa-gateway/index.js @@ -0,0 +1,135 @@ +import { Component } from 'preact'; +import { connect } from 'unistore/preact'; +import cx from 'classnames'; +import { Text, Localizer, MarkupText } from 'preact-i18n'; +import Layout from './Layout'; +import style from './style.css'; + +class AlexaGateway extends Component { + cancel = async e => { + e.preventDefault(); + await this.setState({ loading: true }); + if (this.props.redirect_uri && this.props.state) { + const redirectUrl = `${this.props.redirect_uri}?state=${this.props.state}&error=cancelled`; + window.location.replace(redirectUrl); + } else { + this.setState({ loading: false, error: true }); + } + }; + link = async e => { + e.preventDefault(); + try { + await this.setState({ loading: true, error: false }); + const responseAuthorize = await this.props.session.gatewayClient.alexaAuthorize({ + client_id: this.props.client_id, + redirect_uri: this.props.redirect_uri, + state: this.props.state + }); + window.location.replace(responseAuthorize.redirectUrl); + } catch (e) { + await this.setState({ loading: false, error: true }); + console.error(e); + if (this.props.redirect_uri && this.props.state) { + const redirectUrl = `${this.props.redirect_uri}?state=${this.props.state}&error=errored`; + window.location.replace(redirectUrl); + } + } + }; + + render(props, { loading, error }) { + return ( + +
+
+
+
+

+ + {<Text} + /> + + +

+
+
+
+
+

+ +

+
+ +
+
+
+ {error && ( +

+ +

+ )} +

+ +

+ +

+ {props.user && props.user.email} +

+ +
+

+ +

+
    +
  • +