From 182ec6064d53858e21e703b57524039374a95da3 Mon Sep 17 00:00:00 2001
From: Alex Liebowitz <alex@alexliebowitz.com>
Date: Fri, 6 Jan 2017 06:27:43 -0500
Subject: [PATCH] FileTile improvements and refactoring

 - Now accepts a single metadata object for all metadata fields so
   the surrounding components don't have to break out the individual
   fields into props.
 - Now tracks whether the file was published by the user, and if
   there's a copy on their machine (will look up using API calls if
   needed)
 - Use the new "state" prop for DownloadLink
 - General refactoring and cleanup
---
 js/component/file-tile.js | 81 ++++++++++++++++++++++++++++++---------
 1 file changed, 62 insertions(+), 19 deletions(-)

diff --git a/js/component/file-tile.js b/js/component/file-tile.js
index 67bbf2962..caa4e374c 100644
--- a/js/component/file-tile.js
+++ b/js/component/file-tile.js
@@ -8,16 +8,18 @@ let FileTile = React.createClass({
   _statusCheckInterval: 5000,
 
   propTypes: {
-    name: React.PropTypes.string.isRequired,
-    mediaType: React.PropTypes.string.isRequired, 
-    title: React.PropTypes.string.isRequired,
-    description: React.PropTypes.string,
-    compact: React.PropTypes.boolean,
+    metadata: React.PropTypes.object.isRequired,
+    name: React.PropTypes.string,
+    sdHash: React.PropTypes.string,
+    available: React.PropTypes.bool,
+    isMine: React.PropTypes.bool,
+    local: React.PropTypes.bool,
+    path: React.PropTypes.string,
     cost: React.PropTypes.number,
-    costIncludesData: React.PropTypes.boolean,
+    costIncludesData: React.PropTypes.bool,
   },
   updateFileInfo: function(progress=null) {
-    const updateStatusCallback = ((result) => {
+    const updateStatusCallback = ((fileInfo) => {
       if (!this._isMounted || 'fileInfo' in this.props) {
         /**
          * The component was unmounted, or a file info data structure has now been provided by the
@@ -27,7 +29,8 @@ let FileTile = React.createClass({
       }
 
       this.setState({
-        fileInfo: result || null,
+        fileInfo: fileInfo || null,
+        local: !!fileInfo,
       });
 
       setTimeout(() => { this.updateFileInfo() }, this._statusCheckInterval);
@@ -35,12 +38,38 @@ let FileTile = React.createClass({
 
     if ('sdHash' in this.props) {
       lbry.getFileInfoBySdHash(this.props.sdHash, updateStatusCallback);
+      this.getIsMineIfNeeded(this.props.sdHash);
     } else if ('name' in this.props) {
-      lbry.getFileInfoByName(this.props.name, updateStatusCallback);
+      lbry.getFileInfoByName(this.props.name, (fileInfo) => {
+        this.getIsMineIfNeeded(fileInfo.sd_hash);
+
+        updateStatusCallback(fileInfo);
+      });
     } else {
       throw new Error("No progress, stream name or sd hash passed to FileTile");
     }
   },
+  getIsMineIfNeeded: function(sdHash) {
+    if (this.state.isMine !== null) {
+      // The info was already provided by this.props.isMine
+      return;
+    }
+
+    lbry.getMyClaims((claimsInfo) => {
+      for (let {value} of claimsInfo) {
+        if (JSON.parse(value).sources.lbry_sd_hash == sdHash) {
+          this.setState({
+            isMine: true,
+          });
+          return;
+        }
+      }
+
+      this.setState({
+        isMine: false,
+      });
+    });
+  },
   getInitialState: function() {
     return {
       downloading: false,
@@ -48,6 +77,8 @@ let FileTile = React.createClass({
       cost: null,
       costIncludesData: null,
       fileInfo: 'fileInfo' in this.props ? this.props.fileInfo : null,
+      isMine: 'isMine' in this.props ? this.props.isMine : null,
+      local: 'local' in this.props ? this.props.local : null,
     }
   },
   getDefaultProps: function() {
@@ -66,6 +97,8 @@ let FileTile = React.createClass({
     });
   },
   componentWillMount: function() {
+    this.updateFileInfo();
+
     if ('cost' in this.props) {
       this.setState({
         cost: this.props.cost,
@@ -82,29 +115,39 @@ let FileTile = React.createClass({
   },
   componentDidMount: function() {
     this._isMounted = true;
-    this.updateFileInfo();
   },
   componentWillUnmount: function() {
     this._isMounted = false;
   },
   render: function() {
+    if (this.state.isMine === null || this.state.local === null) {
+      // Can't render until we know whether we own the file and if we have a local copy
+      return null;
+    }
+
     const obscureNsfw = !lbry.getClientSetting('showNsfw') && this.props.nsfw;
 
     let downloadLinkExtraProps = {};
-    if (this.state.fileInfo !== null) {
-      const {written_bytes, total_bytes, completed} = this.state.fileInfo;
-      downloadLinkExtraProps['progress'] = written_bytes / total_bytes;
-      downloadLinkExtraProps['downloading'] = !completed;
+    if (this.state.fileInfo === null) {
+      downloadLinkExtraProps.state = 'not-started';
+    } else if (!this.state.fileInfo.completed) {
+      downloadLinkExtraProps.state = 'downloading';
+
+      const {written_bytes, total_bytes, path} = this.state.fileInfo;
+      downloadLinkExtraProps.progress = written_bytes / total_bytes;
+    } else {
+      downloadLinkExtraProps.state = 'done';
+      downloadLinkExtraProps.path = this.state.fileInfo.download_path;
     }
 
     return (
       <section className={ 'file-tile card ' + (obscureNsfw ? 'card-obscured ' : '') + (this.props.compact ? 'file-tile--compact' : '')} onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
         <div className="row-fluid card-content file-tile__row">
           <div className="span3">
-            <a href={'/?show=' + this.props.name}><Thumbnail className="file-tile__thumbnail" src={this.props.imgUrl} alt={'Photo for ' + (this.props.title || this.props.name)} /></a>
+            <a href={'/?show=' + this.props.name}><Thumbnail className="file-tile__thumbnail" src={this.props.metadata.thumbnail} alt={'Photo for ' + (this.props.metadata.title || this.props.name)} /></a>
           </div>
           <div className="span9">
-            {this.state.cost !== null
+            {this.state.cost !== null && !this.state.local
               ? <span className="file-tile__cost">
                   <CreditAmount amount={this.state.cost} isEstimate={!this.state.costIncludesData}/>
                 </span>
@@ -113,19 +156,19 @@ let FileTile = React.createClass({
             <h3 className={'file-tile__title ' + (this.props.compact ? 'file-tile__title--compact' : '')}>
               <a href={'/?show=' + this.props.name}>
                 <TruncatedText lines={3}>
-                  {this.props.title}
+                  {this.props.metadata.title}
                 </TruncatedText>
               </a>
             </h3>
             <div>
-              {this.props.mediaType == 'video' ? <WatchLink streamName={this.props.name} button="primary" /> : null}
+              {this.props.metadata.content_type.startsWith('video/') ? <WatchLink streamName={this.props.name} button="primary" /> : null}
               {!this.props.isMine
                 ? <DownloadLink streamName={this.props.name} button="text" {... downloadLinkExtraProps}/>
                 : null}
              </div>
             <p className="file-tile__description">
               <TruncatedText lines={3}>
-                {this.props.description}
+                {this.props.metadata.description}
               </TruncatedText>
             </p>
           </div>