aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js/components/ContentView
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/js/components/ContentView')
-rw-r--r--web/src/js/components/ContentView/CodeEditor.jsx2
-rw-r--r--web/src/js/components/ContentView/ContentLoader.jsx5
-rw-r--r--web/src/js/components/ContentView/ContentViewOptions.jsx31
-rw-r--r--web/src/js/components/ContentView/ContentViews.jsx89
-rw-r--r--web/src/js/components/ContentView/ShowFullContentButton.jsx36
-rw-r--r--web/src/js/components/ContentView/ViewSelector.jsx95
6 files changed, 193 insertions, 65 deletions
diff --git a/web/src/js/components/ContentView/CodeEditor.jsx b/web/src/js/components/ContentView/CodeEditor.jsx
index 95f1b98b..d0430e6f 100644
--- a/web/src/js/components/ContentView/CodeEditor.jsx
+++ b/web/src/js/components/ContentView/CodeEditor.jsx
@@ -14,7 +14,7 @@ export default function CodeEditor ( { content, onChange} ){
lineNumbers: true
};
return (
- <div onKeyDown={e => e.stopPropagation()}>
+ <div className="codeeditor" onKeyDown={e => e.stopPropagation()}>
<Codemirror value={content} onChange={onChange} options={options}/>
</div>
)
diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx
index ba6702ca..e7a6f379 100644
--- a/web/src/js/components/ContentView/ContentLoader.jsx
+++ b/web/src/js/components/ContentView/ContentLoader.jsx
@@ -28,7 +28,8 @@ export default View => class extends React.Component {
componentWillReceiveProps(nextProps) {
if (
nextProps.message.content !== this.props.message.content ||
- nextProps.message.contentHash !== this.props.message.contentHash
+ nextProps.message.contentHash !== this.props.message.contentHash ||
+ nextProps.contentView !== this.props.contentView
) {
this.updateContent(nextProps)
}
@@ -52,7 +53,7 @@ export default View => class extends React.Component {
return this.setState({request: undefined, content: ""})
}
- let requestUrl = MessageUtils.getContentURL(props.flow, props.message)
+ let requestUrl = MessageUtils.getContentURL(props.flow, props.message, (View.name == 'ViewServer' ? props.contentView : undefined))
// We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable.
let request = new XMLHttpRequest();
diff --git a/web/src/js/components/ContentView/ContentViewOptions.jsx b/web/src/js/components/ContentView/ContentViewOptions.jsx
new file mode 100644
index 00000000..fed3a088
--- /dev/null
+++ b/web/src/js/components/ContentView/ContentViewOptions.jsx
@@ -0,0 +1,31 @@
+import React, { PropTypes } from 'react'
+import { connect } from 'react-redux'
+import ViewSelector from './ViewSelector'
+import UploadContentButton from './UploadContentButton'
+import DownloadContentButton from './DownloadContentButton'
+
+ContentViewOptions.propTypes = {
+ flow: React.PropTypes.object.isRequired,
+ message: React.PropTypes.object.isRequired,
+}
+
+function ContentViewOptions(props) {
+ const { flow, message, uploadContent, readonly, contentViewDescription } = props
+ return (
+ <div className="view-options">
+ <ViewSelector message={message}/>
+ &nbsp;
+ <DownloadContentButton flow={flow} message={message}/>
+ &nbsp;
+ <UploadContentButton uploadContent={uploadContent}/>
+ &nbsp;
+ <span>{contentViewDescription}</span>
+ </div>
+ )
+}
+
+export default connect(
+ state => ({
+ contentViewDescription: state.ui.flow.viewDescription
+ })
+)(ContentViewOptions)
diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx
index a1adebea..cd593023 100644
--- a/web/src/js/components/ContentView/ContentViews.jsx
+++ b/web/src/js/components/ContentView/ContentViews.jsx
@@ -1,4 +1,6 @@
-import React, { PropTypes } from 'react'
+import React, { PropTypes, Component } from 'react'
+import { connect } from 'react-redux'
+import { setContentViewDescription, setContent } from '../../ducks/ui/flow'
import ContentLoader from './ContentLoader'
import { MessageUtils } from '../../flow/utils'
import CodeEditor from './CodeEditor'
@@ -18,43 +20,68 @@ function ViewImage({ flow, message }) {
)
}
-
-ViewRaw.matches = () => true
-ViewRaw.propTypes = {
+Edit.propTypes = {
content: React.PropTypes.string.isRequired,
}
-function ViewRaw({ content, readonly, onChange }) {
- return readonly ? <pre>{content}</pre> : <CodeEditor content={content} onChange={onChange}/>
+
+function Edit({ content, onChange }) {
+ return <CodeEditor content={content} onChange={onChange}/>
}
-ViewRaw = ContentLoader(ViewRaw)
+Edit = ContentLoader(Edit)
+class ViewServer extends Component {
-const isJSON = /^application\/json$/i
-ViewJSON.matches = msg => isJSON.test(MessageUtils.getContentType(msg))
-ViewJSON.propTypes = {
- content: React.PropTypes.string.isRequired,
-}
-function ViewJSON({ content }) {
- let json = content
- try {
- json = JSON.stringify(JSON.parse(content), null, 2);
- } catch (e) {
- // @noop
+ componentWillMount(){
+ this.setContentView(this.props)
}
- return <pre>{json}</pre>
-}
-ViewJSON = ContentLoader(ViewJSON)
+ componentWillReceiveProps(nextProps){
+ if (nextProps.content != this.props.content) {
+ this.setContentView(nextProps)
+ }
+ }
+ setContentView(props){
+ try {
+ this.data = JSON.parse(props.content)
+ }catch(err) {
+ this.data = {lines: [], description: err.message}
+ }
+
+ props.setContentViewDescription(props.contentView != this.data.description ? this.data.description : '')
+ props.setContent(this.data.lines)
+ }
+ render() {
+ const {content, contentView, message, maxLines} = this.props
+ let lines = this.props.showFullContent ? this.data.lines : this.data.lines.slice(0, maxLines)
+ return <div>
+ <pre>
+ {lines.map((line, i) =>
+ <div key={`line${i}`}>
+ {line.map((tuple, j) =>
+ <span key={`tuple${j}`} className={tuple[0]}>
+ {tuple[1]}
+ </span>
+ )}
+ </div>
+ )}
+ </pre>
+ {ViewImage.matches(message) &&
+ <ViewImage {...this.props} />
+ }
+ </div>
+ }
-ViewAuto.matches = () => false
-ViewAuto.findView = msg => [ViewImage, ViewJSON, ViewRaw].find(v => v.matches(msg)) || ViewRaw
-ViewAuto.propTypes = {
- message: React.PropTypes.object.isRequired,
- flow: React.PropTypes.object.isRequired,
-}
-function ViewAuto({ message, flow, readonly, onChange }) {
- const View = ViewAuto.findView(message)
- return <View message={message} flow={flow} readonly={readonly} onChange={onChange}/>
}
-export { ViewImage, ViewRaw, ViewAuto, ViewJSON }
+ViewServer = connect(
+ state => ({
+ showFullContent: state.ui.flow.showFullContent,
+ maxLines: state.ui.flow.maxContentLines
+ }),
+ {
+ setContentViewDescription,
+ setContent
+ }
+)(ContentLoader(ViewServer))
+
+export { Edit, ViewServer, ViewImage }
diff --git a/web/src/js/components/ContentView/ShowFullContentButton.jsx b/web/src/js/components/ContentView/ShowFullContentButton.jsx
new file mode 100644
index 00000000..676068e9
--- /dev/null
+++ b/web/src/js/components/ContentView/ShowFullContentButton.jsx
@@ -0,0 +1,36 @@
+import React, { Component, PropTypes } from 'react'
+import { connect } from 'react-redux'
+import { render } from 'react-dom';
+import Button from '../common/Button';
+import { setShowFullContent } from '../../ducks/ui/flow'
+
+
+
+ShowFullContentButton.propTypes = {
+ setShowFullContent: PropTypes.func.isRequired,
+ showFullContent: PropTypes.bool.isRequired
+}
+
+function ShowFullContentButton ( {setShowFullContent, showFullContent, visibleLines, contentLines} ){
+
+ return (
+ !showFullContent &&
+ <div>
+ <Button className="view-all-content-btn btn-xs" onClick={() => setShowFullContent(true)} text="Show full content"/>
+ <span className="pull-right"> {visibleLines}/{contentLines} are visible &nbsp; </span>
+ </div>
+ )
+}
+
+export default connect(
+ state => ({
+ showFullContent: state.ui.flow.showFullContent,
+ visibleLines: state.ui.flow.maxContentLines,
+ contentLines: state.ui.flow.content.length
+
+ }),
+ {
+ setShowFullContent
+ }
+)(ShowFullContentButton)
+
diff --git a/web/src/js/components/ContentView/ViewSelector.jsx b/web/src/js/components/ContentView/ViewSelector.jsx
index 89b36231..59ec4276 100644
--- a/web/src/js/components/ContentView/ViewSelector.jsx
+++ b/web/src/js/components/ContentView/ViewSelector.jsx
@@ -1,47 +1,80 @@
-import React, { PropTypes } from 'react'
+import React, { PropTypes, Component } from 'react'
import classnames from 'classnames'
import { connect } from 'react-redux'
import * as ContentViews from './ContentViews'
import { setContentView } from "../../ducks/ui/flow";
-
-function ViewButton({ name, setContentView, children, activeView }) {
+function ViewItem({ name, setContentView, children }) {
return (
- <button
- onClick={() => setContentView(name)}
- className={classnames('btn btn-default', { active: name === activeView })}>
- {children}
- </button>
+ <li>
+ <a href="#" onClick={() => setContentView(name)}>
+ {children}
+ </a>
+ </li>
)
}
-ViewButton = connect(state => ({
- activeView: state.ui.flow.contentView
-}), {
- setContentView
-})(ViewButton)
-ViewSelector.propTypes = {
- message: PropTypes.object.isRequired,
-}
-export default function ViewSelector({ message }) {
+/*ViewSelector.propTypes = {
+ contentViews: PropTypes.array.isRequired,
+ activeView: PropTypes.string.isRequired,
+ isEdit: PropTypes.bool.isRequired,
+ isContentViewSelectorOpen: PropTypes.bool.isRequired,
+ setContentViewSelectorOpen: PropTypes.func.isRequired
+}*/
- let autoView = ContentViews.ViewAuto.findView(message)
- let autoViewName = (autoView.displayName || autoView.name)
- .toLowerCase()
- .replace('view', '')
- .replace(/ContentLoader\((.+)\)/,"$1")
- return (
- <div className="view-selector btn-group btn-group-xs">
+class ViewSelector extends Component {
+ constructor(props, context) {
+ super(props, context)
+ this.close = this.close.bind(this)
+ this.state = {open: false}
+ }
+ close() {
+ this.setState({open: false})
+ document.removeEventListener('click', this.close)
+ }
- <ViewButton name="ViewAuto">auto: {autoViewName}</ViewButton>
+ onDropdown(e){
+ e.preventDefault()
+ this.setState({open: !this.state.open})
+ document.addEventListener('click', this.close)
+ }
- {Object.keys(ContentViews).map(name =>
- name !== "ViewAuto" &&
- <ViewButton key={name} name={name}>{name.toLowerCase().replace('view', '')}</ViewButton>
- )}
+ render() {
+ const {contentViews, activeView, isEdit, setContentView} = this.props
+ let edit = ContentViews.Edit.displayName
- </div>
- )
+ return (
+ <div className={classnames('dropup pull-left', { open: this.state.open })}>
+ <a className="btn btn-default btn-xs"
+ onClick={ e => this.onDropdown(e) }
+ href="#">
+ <b>View:</b> {activeView}<span className="caret"></span>
+ </a>
+ <ul className="dropdown-menu" role="menu">
+ {contentViews.map(name =>
+ <ViewItem key={name} setContentView={setContentView} name={name}>
+ {name.toLowerCase().replace('_', ' ')}
+ </ViewItem>
+ )}
+ {isEdit &&
+ <ViewItem key={edit} setContentView={setContentView} name={edit}>
+ {edit.toLowerCase()}
+ </ViewItem>
+ }
+ </ul>
+ </div>
+ )
+ }
}
+
+export default connect (
+ state => ({
+ contentViews: state.settings.contentViews,
+ activeView: state.ui.flow.contentView,
+ isEdit: !!state.ui.flow.modifiedFlow,
+ }), {
+ setContentView,
+ }
+)(ViewSelector)