@adobe/generator-aio-app
Version:
Adobe I/O application yeoman code generator
230 lines (217 loc) • 8.87 kB
JavaScript
/*<% if (false) { %>
Copyright 2019 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
<% } %>
* <license header>
*/
// react imports
import React from 'react'
import PropTypes from 'prop-types'
import ErrorBoundary from 'react-error-boundary'
// react spectrum components
import { Provider } from '@react-spectrum/provider'
import { theme } from '@react-spectrum/theme-default'
import { Button } from '@react-spectrum/button'
import { TextField } from '@react-spectrum/textfield'
import { Link } from '@react-spectrum/link'
import { Picker, Item } from '@react-spectrum/picker'
import { Form } from '@react-spectrum/form'
import { Flex } from '@react-spectrum/layout'
import { ProgressCircle } from '@react-spectrum/progress'
import { Heading, Text } from '@react-spectrum/text';
// local imports
import './App.css'
<% if (hasBackend) { %>import { actionWebInvoke } from './utils'
import actions from './config.json'<% } %>
/* Here is your entry point React Component, this class has access to the Adobe Experience Cloud Shell runtime object */
export default class App extends React.Component {
constructor (props) {
super(props)
// error handler on UI rendering failure
this.onError = (e, componentStack) => {}
// component to show if UI fails rendering
this.fallbackComponent = ({ componentStack, error }) => (
<React.Fragment>
<h1 style={{ textAlign: 'center', marginTop: '20px' }}>Something went wrong :(</h1>
<pre>{ componentStack + '\n' + error.message }</pre>
</React.Fragment>
)
<% if (hasBackend) { %>
this.state = {
actionSelected: null,
actionResponse: null,
actionResponseError: null,
actionHeaders: null,
actionHeadersValid: null,
actionParams: null,
actionParamsValid: null,
actionInvokeInProgress: false
}
<% } else { %>
this.state = {}
<% } %>
console.log('runtime object:', this.props.runtime)
console.log('ims object:', this.props.ims)
// use exc runtime event handlers
// respond to configuration change events (e.g. user switches org)
this.props.runtime.on('configuration', ({ imsOrg, imsToken, locale }) => {
console.log('configuration change', { imsOrg, imsToken, locale })
})
// respond to history change events
this.props.runtime.on('history', ({ type, path }) => {
console.log('history change', { type, path })
})
}
static get propTypes () {
return {
runtime: PropTypes.any,
ims: PropTypes.any
}
}
<% if (hasBackend) { %>
// parses a JSON input and adds it to the state
async setJSONInput (input, stateJSON, stateValid) {
let content
let validStr = null
if (input) {
try {
content = JSON.parse(input)
validStr = 'valid'
} catch (e) {
content = null
validStr = 'invalid'
}
}
this.setState({ [stateJSON]: content, [stateValid]: validStr })
}
// invokes a the selected backend actions with input headers and params
async invokeAction () {
this.setState({ actionInvokeInProgress: true })
const action = this.state.actionSelected
const headers = this.state.actionHeaders || {}
const params = this.state.actionParams || {}
// all headers to lowercase
Object.keys(headers).forEach(h => {
const lowercase = h.toLowerCase()
if (lowercase !== h) {
headers[lowercase] = headers[h]
headers[h] = undefined
delete headers[h]
}
})
// set the authorization header and org from the ims props object
if (this.props.ims.token && !headers.authorization) {
headers.authorization = 'Bearer ' + this.props.ims.token
}
if (this.props.ims.org && !headers['x-gw-ims-org-id']) {
headers['x-gw-ims-org-id'] = this.props.ims.org
}
try {
// invoke backend action
const actionResponse = await actionWebInvoke(action, headers, params)
// store the response
this.setState({ actionResponse, actionResponseError: null, actionInvokeInProgress: false })
console.log(`Response from ${action}:`, actionResponse)
} catch (e) {
// log and store any error message
console.error(e)
this.setState({ actionResponse: null, actionResponseError: e.message, actionInvokeInProgress: false })
}
}
<% } %>
render () {
return (
// ErrorBoundary wraps child components to handle eventual rendering errors
<ErrorBoundary onError={ this.onError } FallbackComponent={ this.fallbackComponent } >
<Provider UNSAFE_className='provider' theme={ theme }>
<Flex UNSAFE_className='main'>
<Heading UNSAFE_className='main-title'>Welcome to <%= projectName %>!</Heading>
<% if (hasBackend) { %>
<Flex UNSAFE_className='main-actions'>
<h3 className='actions-title'>Run your application backend actions</h3>
{ Object.keys(actions).length > 0 &&
<Form UNSAFE_className='actions-form' necessityIndicator='label'>
<Picker
placeholder='select an action'
aria-label='select an action'
items={ Object.keys(actions).map(k => ({ name: k })) }
itemKey='name'
onSelectionChange={ name => this.setState({ actionSelected: name, actionResponseError: null, actionResponse: null }) }>
{ item => <Item key={item.name}>{ item.name }</Item> }
</Picker>
<TextField
label='headers'
placeholder='{ "key": "value" }'
validationState={ this.state.actionHeadersValid }
onChange={ input => this.setJSONInput(input, 'actionHeaders', 'actionHeadersValid' ) }/>
<TextField
label='params'
placeholder='{ "key": "value" }'
validationState={ this.state.actionParamsValid }
onChange={ input => this.setJSONInput(input, 'actionParams', 'actionParamsValid' ) }/>
<Flex UNSAFE_className='actions-invoke'>
<Button
UNSAFE_className='actions-invoke-button'
variant='primary'
onPress={ this.invokeAction.bind(this) }
isDisabled={ !this.state.actionSelected }>
Invoke
</Button>
<ProgressCircle
UNSAFE_className='actions-invoke-progress'
aria-label='loading'
isIndeterminate
isHidden={ !this.state.actionInvokeInProgress }/>
</Flex>
</Form>
}
{ Object.keys(actions).length === 0 &&
<Text>You have no actions !</Text>
}
{ this.state.actionResponseError &&
<Text UNSAFE_className='actions-invoke-error'>
Failure! See the error in your browser console.
</Text>
}
{ !this.state.actionError && this.state.actionResponse &&
<Text UNSAFE_className='actions-invoke-success'>
Success! See the response content in your browser console.
</Text>
}
</Flex>
<% } %>
<Flex UNSAFE_className='main-doc'>
<h3 className='doc-title'>Useful documentation for your app</h3>
<Link UNSAFE_className='doc-item'>
<a href='https://github.com/AdobeDocs/project-firefly/blob/master/README.md#project-firefly-developer-guide' target='_blank'>
Firefly Apps
</a>
</Link>
<Link UNSAFE_className='doc-item'>
<a href='https://github.com/adobe/aio-sdk#adobeaio-sdk' target='_blank'>
Adobe I/O SDK
</a>
</Link>
<% if (hasBackend) { %> <Link UNSAFE_className='doc-item'>
<a href='https://adobedocs.github.io/adobeio-runtime/' target='_blank'>
Adobe I/O Runtime
</a>
</Link><% } %>
<Link UNSAFE_className='doc-item'>
<a href='https://react-spectrum.adobe.com/react-spectrum/index.html' target='_blank'>
React Spectrum
</a>
</Link>
</Flex>
</Flex>
</Provider>
</ErrorBoundary>
)
}
}