From 1138db00cdc09a6f3102eb033a81332da392708c Mon Sep 17 00:00:00 2001 From: Aaron Boodman Date: Fri, 20 Jun 2014 05:24:44 -0700 Subject: [PATCH] start at twitter renderer.. pretty ugly right now. still working on it... Change-Id: I7e4940d05bf0eb03f2eb68fb330a8fbe77c38173 --- .../ui/blob_item_foursquare_content.js | 31 +---- server/camlistored/ui/blob_item_twitter.css | 63 +++++++++ .../ui/blob_item_twitter_content.js | 122 ++++++++++++++++++ server/camlistored/ui/date_utils.js | 47 +++++++ server/camlistored/ui/index.html | 1 + server/camlistored/ui/index.js | 9 +- server/camlistored/ui/twitter-logo.png | Bin 0 -> 3462 bytes 7 files changed, 241 insertions(+), 32 deletions(-) create mode 100644 server/camlistored/ui/blob_item_twitter.css create mode 100644 server/camlistored/ui/blob_item_twitter_content.js create mode 100644 server/camlistored/ui/date_utils.js create mode 100644 server/camlistored/ui/twitter-logo.png diff --git a/server/camlistored/ui/blob_item_foursquare_content.js b/server/camlistored/ui/blob_item_foursquare_content.js index 3d3ae35c7..8aaeb8d31 100644 --- a/server/camlistored/ui/blob_item_foursquare_content.js +++ b/server/camlistored/ui/blob_item_foursquare_content.js @@ -21,6 +21,7 @@ goog.require('goog.math.Size'); goog.require('goog.object'); goog.require('goog.string'); +goog.require('cam.dateUtils'); goog.require('cam.math'); goog.require('cam.permanodeUtils'); goog.require('cam.Thumber'); @@ -55,38 +56,10 @@ cam.BlobItemFoursquareContent = React.createClass({ ) ) ), - React.DOM.div({className:'cam-blobitem-fs-checkin-when'}, this.formatDate_()) + React.DOM.div({className:'cam-blobitem-fs-checkin-when'}, cam.dateUtils.formatDateShort(this.props.date)) ) ); }, - - formatDate_: function() { - var seconds = Math.floor((Date.now() - this.props.date) / 1000); - var interval = Math.floor(seconds / 31536000); - - return (function() { - if (interval > 1) { - return interval + ' years'; - } - interval = Math.floor(seconds / 2592000); - if (interval > 1) { - return interval + ' months'; - } - interval = Math.floor(seconds / 86400); - if (interval > 1) { - return interval + ' days'; - } - interval = Math.floor(seconds / 3600); - if (interval > 1) { - return interval + ' hours'; - } - interval = Math.floor(seconds / 60); - if (interval > 1) { - return interval + ' minutes'; - } - return Math.floor(seconds) + ' seconds'; - })() + ' ago'; - }, }); // Blech, we need this to prevent images from flashing when data changes server-side. diff --git a/server/camlistored/ui/blob_item_twitter.css b/server/camlistored/ui/blob_item_twitter.css new file mode 100644 index 000000000..a58eea73c --- /dev/null +++ b/server/camlistored/ui/blob_item_twitter.css @@ -0,0 +1,63 @@ +/* +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. +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 CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.cam-blobitem-twitter-tweet { + color: black; + display: block; + font-size: 90%; + position: relative; + overflow: hidden; + white-space: normal; + border-radius: 7%; +} + +.cam-blobitem-twitter-tweet table { + border-spacing: 0; + background-color: #e1e8ed; + width: 100%; + height: 100%; +} + +.cam-blobitem-twitter-tweet-icon { + position: absolute; + width: 100%; + bottom: 0; +} + +.cam-blobitem-twitter-tweet-icon img { + width: 4em; + height: 4em; + position: absolute; + bottom: 1em; + right: 1em; + opacity: 1; +} + +.cam-blobitem-twitter-tweet-meta { + text-align: left; + vertical-align: top; + padding: 0.8em; +} + +.cam-blobitem-twitter-tweet-date { + color: #aaa; +} + +.cam-blobitem-twitter-tweet-image { + background-position: top; + background-size: cover; + height: 100%; +} diff --git a/server/camlistored/ui/blob_item_twitter_content.js b/server/camlistored/ui/blob_item_twitter_content.js new file mode 100644 index 000000000..60f24453c --- /dev/null +++ b/server/camlistored/ui/blob_item_twitter_content.js @@ -0,0 +1,122 @@ +/* +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. +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 CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +goog.provide('cam.BlobItemTwitterContent'); + +goog.require('goog.math.Size'); + +goog.require('cam.dateUtils'); +goog.require('cam.math'); +goog.require('cam.permanodeUtils'); +goog.require('cam.Thumber'); + +cam.BlobItemTwitterContent = React.createClass({ + propTypes: { + date: React.PropTypes.number.isRequired, + href: React.PropTypes.string.isRequired, + image: React.PropTypes.string, + size: React.PropTypes.instanceOf(goog.math.Size).isRequired, + username: React.PropTypes.string.isRequired, + }, + + getImageRow_: function() { + if (!this.props.image) { + return null; + } + + return React.DOM.tr(null, + React.DOM.td({ + className: 'cam-blobitem-twitter-tweet-image', + colSpan: 2, + src: 'twitter-icon.png', + style: { + backgroundImage: 'url(' + this.props.image + ')', + }, + }) + ); + }, + + render: function() { + return React.DOM.a({ + href: this.props.href, + className: 'cam-blobitem-twitter-tweet', + style: { + width: this.props.size.width, + height: this.props.size.height, + }, + }, + React.DOM.table({height: this.props.image ? '100%' : ''}, + React.DOM.tr(null, + React.DOM.td({className: 'cam-blobitem-twitter-tweet-meta'}, + React.DOM.span({className: 'cam-blobitem-twitter-tweet-date'}, cam.dateUtils.formatDateShort(this.props.date)), + React.DOM.br(), + React.DOM.span({className: ' cam-blobitem-twitter-tweet-content'}, this.props.content) + ) + ), + this.getImageRow_(), + React.DOM.tr(null, + React.DOM.td({className: 'cam-blobitem-twitter-tweet-icon'}, + React.DOM.img({src: 'twitter-logo.png'}) + ) + ) + ) + ); + }, +}); + +cam.BlobItemTwitterContent.getHandler = function(blobref, searchSession, href) { + var m = searchSession.getMeta(blobref); + if (m.camliType != 'permanode') { + return null; + } + + if (cam.permanodeUtils.getSingleAttr(m.permanode, 'camliNodeType') != 'twitter.com:tweet') { + return null; + } + + var content = cam.permanodeUtils.getSingleAttr(m.permanode, 'content'); + var date = cam.permanodeUtils.getSingleAttr(m.permanode, 'startDate'); + var username = cam.permanodeUtils.getSingleAttr(m.permanode, 'url').match(/^https:\/\/twitter.com\/(.+?)\//)[1]; + var imageMeta = cam.permanodeUtils.getSingleAttr(m.permanode, 'camliContentImage'); + if (imageMeta) { + imageMeta = searchSession.getResolvedMeta(imageMeta); + } + + return new cam.BlobItemTwitterContent.Handler(content, Date.parse(date), href, imageMeta, username); +}; + +cam.BlobItemTwitterContent.Handler = function(content, date, href, imageMeta, username) { + this.content_ = content; + this.date_ = date; + this.href_ = href; + this.username_ = username; + this.thumber_ = imageMeta ? new cam.Thumber.fromImageMeta(imageMeta) : null; +}; + +cam.BlobItemTwitterContent.Handler.prototype.getAspectRatio = function() { + return 1.0; +}; + +cam.BlobItemTwitterContent.Handler.prototype.createContent = function(size) { + return cam.BlobItemTwitterContent({ + content: this.content_, + date: this.date_, + href: this.href_, + image: this.thumber_ ? this.thumber_.getSrc(size) : null, + size: size, + username: this.username_, + }); +}; diff --git a/server/camlistored/ui/date_utils.js b/server/camlistored/ui/date_utils.js new file mode 100644 index 000000000..7dc839d57 --- /dev/null +++ b/server/camlistored/ui/date_utils.js @@ -0,0 +1,47 @@ +/* +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. +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 CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +goog.provide('cam.dateUtils'); + +cam.dateUtils.formatDateShort = function(date) { + // TODO(aa): Do something better based on Closure date/i18n utils. + // I think I would prefer this to return (in en-us) either '11:18 PM', 'Jun 11', or 'June 11 1952', depending on how far back it is. I don't find '5 hours ago' that useful. + var seconds = Math.floor((Date.now() - date) / 1000); + var interval = Math.floor(seconds / 31536000); + + return (function() { + if (interval > 1) { + return interval + ' years'; + } + interval = Math.floor(seconds / 2592000); + if (interval > 1) { + return interval + ' months'; + } + interval = Math.floor(seconds / 86400); + if (interval > 1) { + return interval + ' days'; + } + interval = Math.floor(seconds / 3600); + if (interval > 1) { + return interval + ' hours'; + } + interval = Math.floor(seconds / 60); + if (interval > 1) { + return interval + ' minutes'; + } + return Math.floor(seconds) + ' seconds'; + })() + ' ago'; +}; diff --git a/server/camlistored/ui/index.html b/server/camlistored/ui/index.html index 6a279a1ca..4f761c557 100644 --- a/server/camlistored/ui/index.html +++ b/server/camlistored/ui/index.html @@ -40,6 +40,7 @@ limitations under the License. + diff --git a/server/camlistored/ui/index.js b/server/camlistored/ui/index.js index c7b72af41..7ca17fc7d 100644 --- a/server/camlistored/ui/index.js +++ b/server/camlistored/ui/index.js @@ -26,11 +26,12 @@ goog.require('goog.Uri'); goog.require('cam.BlobDetail'); goog.require('cam.BlobItemContainerReact'); +goog.require('cam.BlobItemDemoContent'); goog.require('cam.BlobItemFoursquareContent'); goog.require('cam.BlobItemGenericContent'); -goog.require('cam.BlobItemVideoContent'); goog.require('cam.BlobItemImageContent'); -goog.require('cam.BlobItemDemoContent'); +goog.require('cam.BlobItemTwitterContent'); +goog.require('cam.BlobItemVideoContent'); goog.require('cam.ContainerDetail'); goog.require('cam.DetailView'); goog.require('cam.DirectoryDetail'); @@ -53,12 +54,14 @@ cam.IndexPage = React.createClass({ RAW: 'raw' }, + // Note that these are ordered by priority. BLOB_ITEM_HANDLERS_: [ cam.BlobItemDemoContent.getHandler, cam.BlobItemFoursquareContent.getHandler, + cam.BlobItemTwitterContent.getHandler, cam.BlobItemImageContent.getHandler, cam.BlobItemVideoContent.getHandler, - cam.BlobItemGenericContent.getHandler // must be last + cam.BlobItemGenericContent.getHandler ], propTypes: { diff --git a/server/camlistored/ui/twitter-logo.png b/server/camlistored/ui/twitter-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1562d432cf21cd9fb6045ff22efd6963608899 GIT binary patch literal 3462 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 o`FiZ>