From 61f192434fade43af81dc74602aef218d5c5be19 Mon Sep 17 00:00:00 2001 From: Clemens Date: Thu, 21 Jul 2016 11:50:02 +0200 Subject: moved editor to raw_view mode --- web/src/js/components/ContentView/ContentViews.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index 82ee0adc..0eaf3ea8 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -1,6 +1,7 @@ import React, { PropTypes } from 'react' import ContentLoader from './ContentLoader' import { MessageUtils } from '../../flow/utils.js' +import CodeEditor from '../common/CodeEditor' const views = [ViewAuto, ViewImage, ViewJSON, ViewRaw] @@ -28,8 +29,9 @@ ViewRaw.propTypes = { content: React.PropTypes.string.isRequired, } -export function ViewRaw({ content }) { - return
{content}
+export function ViewRaw({ content, isFlowEditorOpen, readonly }) { + let showEditor = isFlowEditorOpen && !readonly + return showEditor ? alert(content)}/> :
{content}
} ViewJSON.textView = true -- cgit v1.2.3 From 70ca10b423de4a57a395798aa94189ba4da7840f Mon Sep 17 00:00:00 2001 From: Clemens Date: Thu, 21 Jul 2016 19:13:16 +0200 Subject: moved editor to raw_view mode, add content_file_upload --- web/src/js/components/ContentView/ContentViews.jsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index 0eaf3ea8..6c7f4900 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -29,9 +29,8 @@ ViewRaw.propTypes = { content: React.PropTypes.string.isRequired, } -export function ViewRaw({ content, isFlowEditorOpen, readonly }) { - let showEditor = isFlowEditorOpen && !readonly - return showEditor ? alert(content)}/> :
{content}
+export function ViewRaw({ content, readonly, onChange }) { + return readonly ?
{content}
: } ViewJSON.textView = true @@ -61,12 +60,12 @@ ViewAuto.propTypes = { flow: React.PropTypes.object.isRequired, } -export function ViewAuto({ message, flow }) { +export function ViewAuto({ message, flow, readonly }) { const View = ViewAuto.findView(message) if (View.textView) { - return + return } else { - return + return } } -- cgit v1.2.3 From f578bf512248c609296d2ff0ea2007a6feac561f Mon Sep 17 00:00:00 2001 From: Clemens Date: Fri, 22 Jul 2016 19:07:53 +0200 Subject: file upload updates contentview, editable contentloader, diffs on upload --- web/src/js/components/ContentView/ContentLoader.jsx | 8 ++++++-- web/src/js/components/ContentView/ContentViews.jsx | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index 1a23325c..503ea758 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -46,9 +46,13 @@ export default class ContentLoader extends Component { } componentWillReceiveProps(nextProps) { - if (nextProps.message !== this.props.message) { + let reload = nextProps.message !== this.props.message || nextProps.lastFileUpload !== this.props.lastFileUpload + let isUserEdit = !nextProps.readonly && nextProps.message.content + + if (isUserEdit) + this.setState({content: nextProps.message.content}) + else if(reload) this.requestContent(nextProps) - } } componentWillUnmount() { diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index 6c7f4900..4ae6044b 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -60,10 +60,10 @@ ViewAuto.propTypes = { flow: React.PropTypes.object.isRequired, } -export function ViewAuto({ message, flow, readonly }) { +export function ViewAuto({ message, flow, readonly, lastFileUpload }) { const View = ViewAuto.findView(message) if (View.textView) { - return + return } else { return } -- cgit v1.2.3 From 0aedea6e60f22f78f3523b860027550159ba6dd8 Mon Sep 17 00:00:00 2001 From: Clemens Date: Mon, 25 Jul 2016 09:05:24 +0200 Subject: remove lastFileUpload field --- web/src/js/components/ContentView/ContentViews.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index 4ae6044b..6c7f4900 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -60,10 +60,10 @@ ViewAuto.propTypes = { flow: React.PropTypes.object.isRequired, } -export function ViewAuto({ message, flow, readonly, lastFileUpload }) { +export function ViewAuto({ message, flow, readonly }) { const View = ViewAuto.findView(message) if (View.textView) { - return + return } else { return } -- cgit v1.2.3 From 61ef7ca91b0cfc072b60e173104e01bd0145a6a1 Mon Sep 17 00:00:00 2001 From: Clemens Date: Mon, 25 Jul 2016 10:56:12 +0200 Subject: added contentHash --- web/src/js/components/ContentView/ContentLoader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index 503ea758..eff82d05 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -46,7 +46,7 @@ export default class ContentLoader extends Component { } componentWillReceiveProps(nextProps) { - let reload = nextProps.message !== this.props.message || nextProps.lastFileUpload !== this.props.lastFileUpload + let reload = nextProps.message !== this.props.message let isUserEdit = !nextProps.readonly && nextProps.message.content if (isUserEdit) -- cgit v1.2.3 From 1ea094e9dcf187a409dab9ca9a2b3a0b10f243f7 Mon Sep 17 00:00:00 2001 From: Clemens Date: Mon, 25 Jul 2016 15:47:05 +0200 Subject: remove auto change to raw mode on edit --- web/src/js/components/ContentView/ContentViews.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index 6c7f4900..b39e545a 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -60,10 +60,10 @@ ViewAuto.propTypes = { flow: React.PropTypes.object.isRequired, } -export function ViewAuto({ message, flow, readonly }) { +export function ViewAuto({ message, flow, readonly, onChange }) { const View = ViewAuto.findView(message) if (View.textView) { - return + return } else { return } -- cgit v1.2.3 From 70dbd1b32d13d30e15c03ee91b0fab7bfdf429b3 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 25 Jul 2016 17:03:50 -0700 Subject: web: refactor ContentLoader --- web/src/js/components/ContentView/CodeEditor.jsx | 21 +++++ .../js/components/ContentView/ContentLoader.jsx | 97 ++++++++++++---------- web/src/js/components/ContentView/ContentViews.jsx | 44 ++++------ .../ContentView/DownloadContentButton.jsx | 18 ++++ .../components/ContentView/UploadContentButton.jsx | 28 +++++++ web/src/js/components/ContentView/ViewSelector.jsx | 51 ++++++++---- 6 files changed, 172 insertions(+), 87 deletions(-) create mode 100644 web/src/js/components/ContentView/CodeEditor.jsx create mode 100644 web/src/js/components/ContentView/DownloadContentButton.jsx create mode 100644 web/src/js/components/ContentView/UploadContentButton.jsx (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/CodeEditor.jsx b/web/src/js/components/ContentView/CodeEditor.jsx new file mode 100644 index 00000000..95f1b98b --- /dev/null +++ b/web/src/js/components/ContentView/CodeEditor.jsx @@ -0,0 +1,21 @@ +import React, { Component, PropTypes } from 'react' +import { render } from 'react-dom'; +import Codemirror from 'react-codemirror'; + + +CodeEditor.propTypes = { + content: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, +} + +export default function CodeEditor ( { content, onChange} ){ + + let options = { + lineNumbers: true + }; + return ( +
e.stopPropagation()}> + +
+ ) +} diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index eff82d05..fb022df6 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -1,58 +1,34 @@ import React, { Component, PropTypes } from 'react' import { MessageUtils } from '../../flow/utils.js' -// This is the only place where we use jQuery. -// Remove when possible. -import $ from "jquery" -export default class ContentLoader extends Component { +export default View => class extends React.Component { + + static displayName = View.displayName || View.name + static matches = View.matches static propTypes = { + ...View.propTypes, + content: PropTypes.string, // mark as non-required flow: PropTypes.object.isRequired, message: PropTypes.object.isRequired, } - constructor(props, context) { - super(props, context) - this.state = { content: null, request: null } - } - - requestContent(nextProps) { - if (this.state.request) { - this.state.request.abort() + constructor(props) { + super(props) + this.state = { + content: undefined, + request: undefined, } - - const requestUrl = MessageUtils.getContentURL(nextProps.flow, nextProps.message) - const request = $.get(requestUrl) - - this.setState({ content: null, request }) - - request - .done(content => { - this.setState({ content }) - }) - .fail((xhr, textStatus, errorThrown) => { - if (textStatus === 'abort') { - return - } - this.setState({ content: `AJAX Error: ${textStatus}\r\n${errorThrown}` }) - }) - .always(() => { - this.setState({ request: null }) - }) } componentWillMount() { - this.requestContent(this.props) + this.startRequest(this.props) } componentWillReceiveProps(nextProps) { - let reload = nextProps.message !== this.props.message - let isUserEdit = !nextProps.readonly && nextProps.message.content - - if (isUserEdit) - this.setState({content: nextProps.message.content}) - else if(reload) - this.requestContent(nextProps) + if (nextProps.message.contentHash !== this.props.message.contentHash) { + this.startRequest(nextProps) + } } componentWillUnmount() { @@ -61,15 +37,50 @@ export default class ContentLoader extends Component { } } + startRequest(props) { + if (this.state.request) { + this.state.request.abort() + } + let requestUrl = MessageUtils.getContentURL(props.flow, props.message) + + // We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable. + let request = new XMLHttpRequest(); + request.addEventListener("load", this.requestComplete.bind(this, request)); + request.addEventListener("error", this.requestFailed.bind(this, request)); + request.open("GET", requestUrl); + request.send(); + this.setState({ request, content: undefined }) + } + + requestComplete(request, e) { + if (request !== this.state.request) { + return // Stale request + } + this.setState({ + content: request.responseText, + request: undefined + }) + } + + requestFailed(request, e) { + if (request !== this.state.request) { + return // Stale request + } + console.error(e) + // FIXME: Better error handling + this.setState({ + content: "Error getting content.", + request: undefined + }) + } + render() { return this.state.content ? ( - React.cloneElement(this.props.children, { - content: this.state.content - }) + ) : (
) } -} +}; diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index b39e545a..a1adebea 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -1,20 +1,16 @@ import React, { PropTypes } from 'react' import ContentLoader from './ContentLoader' -import { MessageUtils } from '../../flow/utils.js' -import CodeEditor from '../common/CodeEditor' +import { MessageUtils } from '../../flow/utils' +import CodeEditor from './CodeEditor' -const views = [ViewAuto, ViewImage, ViewJSON, ViewRaw] - -ViewImage.regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i -ViewImage.matches = msg => ViewImage.regex.test(MessageUtils.getContentType(msg)) - +const isImage = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i +ViewImage.matches = msg => isImage.test(MessageUtils.getContentType(msg)) ViewImage.propTypes = { flow: PropTypes.object.isRequired, message: PropTypes.object.isRequired, } - -export function ViewImage({ flow, message }) { +function ViewImage({ flow, message }) { return (
preview @@ -22,26 +18,23 @@ export function ViewImage({ flow, message }) { ) } -ViewRaw.textView = true -ViewRaw.matches = () => true +ViewRaw.matches = () => true ViewRaw.propTypes = { content: React.PropTypes.string.isRequired, } - -export function ViewRaw({ content, readonly, onChange }) { +function ViewRaw({ content, readonly, onChange }) { return readonly ?
{content}
: } +ViewRaw = ContentLoader(ViewRaw) -ViewJSON.textView = true -ViewJSON.regex = /^application\/json$/i -ViewJSON.matches = msg => ViewJSON.regex.test(MessageUtils.getContentType(msg)) +const isJSON = /^application\/json$/i +ViewJSON.matches = msg => isJSON.test(MessageUtils.getContentType(msg)) ViewJSON.propTypes = { content: React.PropTypes.string.isRequired, } - -export function ViewJSON({ content }) { +function ViewJSON({ content }) { let json = content try { json = JSON.stringify(JSON.parse(content), null, 2); @@ -50,23 +43,18 @@ export function ViewJSON({ content }) { } return
{json}
} +ViewJSON = ContentLoader(ViewJSON) ViewAuto.matches = () => false -ViewAuto.findView = msg => views.find(v => v.matches(msg)) || views[views.length - 1] - +ViewAuto.findView = msg => [ViewImage, ViewJSON, ViewRaw].find(v => v.matches(msg)) || ViewRaw ViewAuto.propTypes = { message: React.PropTypes.object.isRequired, flow: React.PropTypes.object.isRequired, } - -export function ViewAuto({ message, flow, readonly, onChange }) { +function ViewAuto({ message, flow, readonly, onChange }) { const View = ViewAuto.findView(message) - if (View.textView) { - return - } else { - return - } + return } -export default views +export { ViewImage, ViewRaw, ViewAuto, ViewJSON } diff --git a/web/src/js/components/ContentView/DownloadContentButton.jsx b/web/src/js/components/ContentView/DownloadContentButton.jsx new file mode 100644 index 00000000..3f11f909 --- /dev/null +++ b/web/src/js/components/ContentView/DownloadContentButton.jsx @@ -0,0 +1,18 @@ +import { MessageUtils } from "../../flow/utils" +import { PropTypes } from 'react' + +DownloadContentButton.propTypes = { + flow: PropTypes.object.isRequired, + message: PropTypes.object.isRequired, +} + +export default function DownloadContentButton({ flow, message }) { + + return ( + + + + ) +} diff --git a/web/src/js/components/ContentView/UploadContentButton.jsx b/web/src/js/components/ContentView/UploadContentButton.jsx new file mode 100644 index 00000000..0652b584 --- /dev/null +++ b/web/src/js/components/ContentView/UploadContentButton.jsx @@ -0,0 +1,28 @@ +import { PropTypes } from 'react' + +UploadContentButton.propTypes = { + uploadContent: PropTypes.func.isRequired, +} + +export default function UploadContentButton({ uploadContent }) { + + let fileInput; + + return ( + fileInput.click()} + title="Upload a file to replace the content."> + + fileInput = ref} + className="hidden" + type="file" + onChange={e => { + if (e.target.files.length > 0) uploadContent(e.target.files[0]) + }} + /> + + + ) +} + diff --git a/web/src/js/components/ContentView/ViewSelector.jsx b/web/src/js/components/ContentView/ViewSelector.jsx index 9b151a5b..973d2333 100644 --- a/web/src/js/components/ContentView/ViewSelector.jsx +++ b/web/src/js/components/ContentView/ViewSelector.jsx @@ -1,28 +1,47 @@ import React, { PropTypes } from 'react' import classnames from 'classnames' -import views, { ViewAuto } from './ContentViews' +import { connect } from 'react-redux' +import * as ContentViews from './ContentViews' +import { setContentView } from "../../ducks/ui/flow"; + + +function ViewButton({ name, setContentView, children, activeView }) { + return ( + + ) +} +ViewButton = connect(state => ({ + activeView: state.ui.flow.contentView +}), { + setContentView +})(ViewButton) + ViewSelector.propTypes = { - active: PropTypes.func.isRequired, message: PropTypes.object.isRequired, - onSelectView: PropTypes.func.isRequired, } +export default function ViewSelector({ message }) { + + let autoView = ContentViews.ViewAuto.findView(message) + let autoViewName = (autoView.displayName || autoView.name) + .toLowerCase() + .replace('view', '') + .replace(/ContentLoader\((.+)\)/,"$1") -export default function ViewSelector({ active, message, onSelectView }) { return (
- {views.map(View => ( - - ))} + + auto: {autoViewName} + + {Object.keys(ContentViews).map(name => + name !== "ViewAuto" && + {name.toLowerCase().replace('view', '')} + )} +
) } -- cgit v1.2.3 From e1587b2bc1b425a162af30d6c271bcd392047b02 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 25 Jul 2016 17:12:39 -0700 Subject: make empty buttons editable --- web/src/js/components/ContentView/ContentLoader.jsx | 6 +++++- web/src/js/components/ContentView/ViewSelector.jsx | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index fb022df6..697085a9 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -41,6 +41,10 @@ export default View => class extends React.Component { if (this.state.request) { this.state.request.abort() } + if(props.message.contentLength === 0 || props.message.contentLength === null){ + return this.setState({request: undefined, content: ""}) + } + let requestUrl = MessageUtils.getContentURL(props.flow, props.message) // We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable. @@ -75,7 +79,7 @@ export default View => class extends React.Component { } render() { - return this.state.content ? ( + return this.state.content !== undefined ? ( ) : (
diff --git a/web/src/js/components/ContentView/ViewSelector.jsx b/web/src/js/components/ContentView/ViewSelector.jsx index 973d2333..89b36231 100644 --- a/web/src/js/components/ContentView/ViewSelector.jsx +++ b/web/src/js/components/ContentView/ViewSelector.jsx @@ -35,7 +35,7 @@ export default function ViewSelector({ message }) { return (
- auto: {autoViewName} + auto: {autoViewName} {Object.keys(ContentViews).map(name => name !== "ViewAuto" && -- cgit v1.2.3 From 67bfc1df147f8a6daa847146625d8887a44ac338 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 25 Jul 2016 17:31:37 -0700 Subject: fix flow edit --- web/src/js/components/ContentView/ContentLoader.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index 697085a9..094ce18b 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -22,12 +22,15 @@ export default View => class extends React.Component { } componentWillMount() { - this.startRequest(this.props) + this.updateContent(this.props) } componentWillReceiveProps(nextProps) { - if (nextProps.message.contentHash !== this.props.message.contentHash) { - this.startRequest(nextProps) + if ( + nextProps.message.content !== this.props.message.content || + nextProps.message.contentHash !== this.props.message.contentHash + ) { + this.updateContent(nextProps) } } @@ -37,13 +40,17 @@ export default View => class extends React.Component { } } - startRequest(props) { + updateContent(props) { if (this.state.request) { this.state.request.abort() } + // We have a few special cases where we do not need to make an HTTP request. if(props.message.contentLength === 0 || props.message.contentLength === null){ return this.setState({request: undefined, content: ""}) } + if(props.message.content !== undefined) { + return this.setState({request: undefined, content: props.message.content}) + } let requestUrl = MessageUtils.getContentURL(props.flow, props.message) -- cgit v1.2.3 From 03a596d1d92e12125f0624626b040dc2919e24c6 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 25 Jul 2016 17:45:42 -0700 Subject: simplify stop edit --- web/src/js/components/ContentView/ContentLoader.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index 094ce18b..ba6702ca 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -45,12 +45,12 @@ export default View => class extends React.Component { this.state.request.abort() } // We have a few special cases where we do not need to make an HTTP request. - if(props.message.contentLength === 0 || props.message.contentLength === null){ - return this.setState({request: undefined, content: ""}) - } if(props.message.content !== undefined) { return this.setState({request: undefined, content: props.message.content}) } + if(props.message.contentLength === 0 || props.message.contentLength === null){ + return this.setState({request: undefined, content: ""}) + } let requestUrl = MessageUtils.getContentURL(props.flow, props.message) -- cgit v1.2.3 From 3ebb58f641612a4c512c045187ffe40879720fa7 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 25 Jul 2016 17:50:59 -0700 Subject: add up/download button to ContentTooLarge view --- web/src/js/components/ContentView/MetaViews.jsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'web/src/js/components/ContentView') diff --git a/web/src/js/components/ContentView/MetaViews.jsx b/web/src/js/components/ContentView/MetaViews.jsx index 2d064b54..b926738e 100644 --- a/web/src/js/components/ContentView/MetaViews.jsx +++ b/web/src/js/components/ContentView/MetaViews.jsx @@ -1,5 +1,7 @@ import React from 'react' import { formatSize } from '../../utils.js' +import UploadContentButton from './UploadContentButton' +import DownloadContentButton from './DownloadContentButton' export function ContentEmpty({ flow, message }) { return ( @@ -17,11 +19,19 @@ export function ContentMissing({ flow, message }) { ) } -export function ContentTooLarge({ message, onClick }) { +export function ContentTooLarge({ message, onClick, uploadContent, flow }) { return ( -
- - {formatSize(message.contentLength)} content size. +
+
+ + + {formatSize(message.contentLength)} content size. +
+
+ +   + +
) } -- cgit v1.2.3