From 940150a5c753a55683e5e6ec2060792a7051863f Mon Sep 17 00:00:00 2001 From: Aaron Boodman Date: Sun, 17 Aug 2014 00:31:12 -0700 Subject: [PATCH] Reimplement the Blob aspect using React. Change-Id: I819b4d1dda86fe80db203f10e1e8a1b23b0777d5 --- server/camlistored/ui/blob_detail.js | 107 ++++++++++++++++-- server/camlistored/ui/blobinfo.html | 38 ------- .../ui/{blobinfo.css => blobref.js} | 19 +--- server/camlistored/ui/index.js | 7 +- 4 files changed, 106 insertions(+), 65 deletions(-) delete mode 100644 server/camlistored/ui/blobinfo.html rename server/camlistored/ui/{blobinfo.css => blobref.js} (67%) diff --git a/server/camlistored/ui/blob_detail.js b/server/camlistored/ui/blob_detail.js index 6a3be6141..8c50c4929 100644 --- a/server/camlistored/ui/blob_detail.js +++ b/server/camlistored/ui/blob_detail.js @@ -16,9 +16,99 @@ limitations under the License. goog.provide('cam.BlobDetail'); -goog.require('cam.CacheBusterIframe'); +goog.require('cam.blobref'); +goog.require('cam.ServerConnection'); -cam.BlobDetail.getAspect = function(baseURL, onChildFrameClick, blobref, targetSearchSession) { +cam.BlobDetail = React.createClass({ + BLOBREF_PATTERN_: new RegExp(cam.blobref.PATTERN, 'g'), + propTypes: { + getDetailURL: React.PropTypes.func.isRequired, + meta: React.PropTypes.object.isRequired, + serverConnection: React.PropTypes.instanceOf(cam.ServerConnection).isRequired, + }, + + getInitialState: function() { + return { + content: null, + metadata: null, + claims: null, + }; + }, + + componentWillMount: function() { + this.props.serverConnection.getBlobContents(this.props.meta.blobRef, this.handleBlobContents_); + this.props.serverConnection.permanodeClaims(this.props.meta.blobRef, this.handleClaims_); + }, + + render: function() { + var children = [ + this.getHeader_("Blob content"), + this.getCodeBlock_(this.state.content), + this.getHeader_("Indexer metadata"), + this.getCodeBlock_(this.props.meta), + ]; + + // TODO(aa): This should really move to permanode detail. + if (this.state.claims) { + children.push(this.getHeader_("Mutation claims")); + children.push(this.getCodeBlock_(this.state.claims)); + } + + return React.DOM.div( + { + style: { + fontFamily: 'Open Sans', + margin: '1.5em 2em', + } + }, + children); + }, + + getHeader_: function(title) { + return React.DOM.h1( + { + style: { + fontSize: '1.5em', + } + }, + title + ); + }, + + getCodeBlock_: function(stuff) { + return React.DOM.pre( + { + style: { + overflowX: 'auto', + }, + }, + stuff ? this.linkify_(JSON.stringify(stuff, null, 2)) : null + ); + }, + + linkify_: function(code) { + var result = []; + var match; + var index = 0; + while ((match = this.BLOBREF_PATTERN_.exec(code)) !== null) { + result.push(code.substring(index, match.index)); + result.push(React.DOM.a({href: this.props.getDetailURL(match[0]).toString()}, match[0])); + index = match.index + match[0].length; + } + result.push(code.substring(index)); + return result; + }, + + handleBlobContents_: function(data) { + this.setState({content: JSON.parse(data)}); + }, + + handleClaims_: function(data) { + this.setState({claims: data}); + }, +}); + +cam.BlobDetail.getAspect = function(getDetailURL, serverConnection, blobref, targetSearchSession) { if(!targetSearchSession) { return; } @@ -32,15 +122,10 @@ cam.BlobDetail.getAspect = function(baseURL, onChildFrameClick, blobref, targetS fragment: 'blob', title: 'Blob', createContent: function(size) { - var url = baseURL.clone(); - url.setParameterValue('b', blobref); - return cam.CacheBusterIframe({ - baseURL: baseURL, - height: size.height, - onChildFrameClick: onChildFrameClick, - key: 'blob', - src: url, - width: size.width, + return cam.BlobDetail({ + getDetailURL: getDetailURL, + meta: m, + serverConnection: serverConnection, }); }, }; diff --git a/server/camlistored/ui/blobinfo.html b/server/camlistored/ui/blobinfo.html deleted file mode 100644 index f3373050e..000000000 --- a/server/camlistored/ui/blobinfo.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - Blob info - - - - - - - -
Home
-

Blob Contents

- -
- - - - - -

-
-	

Indexer Metadata

-

-
-	
-
-	
-
-
diff --git a/server/camlistored/ui/blobinfo.css b/server/camlistored/ui/blobref.js
similarity index 67%
rename from server/camlistored/ui/blobinfo.css
rename to server/camlistored/ui/blobref.js
index 7d5789739..13134972c 100644
--- a/server/camlistored/ui/blobinfo.css
+++ b/server/camlistored/ui/blobref.js
@@ -1,5 +1,5 @@
 /*
-Copyright 2011 The Camlistore Authors
+Copyright 2014 The Camlistore Authors
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-.cam-blobinfo-page {
-	font: 16px/1.4 normal Arial, sans-serif;
-}
-.cam-blobinfo-page #blobdata {
-	overflow: auto;
-	max-width: 800px;
-}
-.cam-blobinfo-nav:before {
-	content: "[";
-}
-.cam-blobinfo-nav:after {
-	content: "]";
-}
+goog.provide('cam.blobref');
+
+// TODO(aa): Need to eventually implement something like ref.go, which understands all the different types of hashes.
+cam.blobref.PATTERN = 'sha1-[0-9a-f]{40}';
diff --git a/server/camlistored/ui/index.js b/server/camlistored/ui/index.js
index c98da9f29..342073cf9 100644
--- a/server/camlistored/ui/index.js
+++ b/server/camlistored/ui/index.js
@@ -33,6 +33,7 @@ goog.require('cam.BlobItemGenericContent');
 goog.require('cam.BlobItemImageContent');
 goog.require('cam.BlobItemTwitterContent');
 goog.require('cam.BlobItemVideoContent');
+goog.require('cam.blobref');
 goog.require('cam.DetailView');
 goog.require('cam.DirectoryDetail');
 goog.require('cam.Header');
@@ -63,6 +64,8 @@ cam.IndexPage = React.createClass({
 		cam.BlobItemGenericContent.getHandler
 	],
 
+	BLOBREF_PATTERN_: new RegExp('^' + cam.blobref.PATTERN + '$'),
+
 	propTypes: {
 		availWidth: React.PropTypes.number.isRequired,
 		availHeight: React.PropTypes.number.isRequired,
@@ -147,7 +150,7 @@ cam.IndexPage = React.createClass({
 		var suffix = url.getPath().substr(this.baseURL_.getPath().length);
 
 		// TODO(aa): Need to implement something like ref.go that knows about the other hash types.
-		var match = suffix.match(/^sha1-[0-9a-f]{40}$/);
+		var match = suffix.match(this.BLOBREF_PATTERN_);
 		return match && match[0];
 	},
 
@@ -158,7 +161,7 @@ cam.IndexPage = React.createClass({
 			cam.ImageDetail.getAspect,
 			cam.PermanodeDetail.getAspect.bind(null, this.baseURL_, childFrameClickHandler),
 			cam.DirectoryDetail.getAspect.bind(null, this.baseURL_, childFrameClickHandler),
-			cam.BlobDetail.getAspect.bind(null, this.baseURL_, childFrameClickHandler),
+			cam.BlobDetail.getAspect.bind(null, this.getDetailURL_, this.props.serverConnection),
 		].map(function(f) {
 			return f(this.getTargetBlobref_(), this.targetSearchSession_);
 		}, this).filter(goog.functions.identity);