Show blame in diff

With this change a blame column is added to the left side of diff
tables. The column is empty and hidden until blame is loaded. A button
is added to the change view to trigger a load of the blame for that
diff, as well as a unload it if already loaded. In this stage, the blame
information is non-interactive and only displays the SHA, date and
commit author.

Feature: Issue 6075
Change-Id: Ifcb951265d0e6339094e6b7c9574ec9c69e60b51
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 3bb373f..fbd845a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -18,6 +18,8 @@
   const MERGE_LIST_PATH = '/MERGE_LIST';
 
   const ERR_REVIEW_STATUS = 'Couldn’t change file review status.';
+  const MSG_LOADING_BLAME = 'Loading blame...';
+  const MSG_LOADED_BLAME = 'Blame loaded';
 
   const PARENT = 'PARENT';
 
@@ -125,6 +127,16 @@
         type: Boolean,
         computed: '_computeEditLoaded(_patchRange.*)',
       },
+
+      _isBlameSupported: {
+        type: Boolean,
+        value: false,
+      },
+      _isBlameLoaded: Boolean,
+      _isBlameLoading: {
+        type: Boolean,
+        value: false,
+      },
     },
 
     behaviors: [
@@ -160,6 +172,10 @@
         this._loggedIn = loggedIn;
       });
 
+      this.$.restAPI.getConfig().then(config => {
+        this._isBlameSupported = config.change.allow_blame;
+      });
+
       this.$.cursor.push('diffs', this.$.diff);
     },
 
@@ -805,5 +821,36 @@
     _computeContainerClass(editLoaded) {
       return editLoaded ? 'editLoaded' : '';
     },
+
+    _computeBlameToggleLabel(loaded, loading) {
+      if (loaded) { return 'Hide blame'; }
+      return 'Show blame';
+    },
+
+    /**
+     * Load and display blame information if it has not already been loaded.
+     * Otherwise hide it.
+     */
+    _toggleBlame() {
+      if (this._isBlameLoaded) {
+        this.$.diff.clearBlame();
+        return;
+      }
+
+      this._isBlameLoading = true;
+      this.fire('show-alert', {message: MSG_LOADING_BLAME});
+      this.$.diff.loadBlame()
+          .then(() => {
+            this._isBlameLoading = false;
+            this.fire('show-alert', {message: MSG_LOADED_BLAME});
+          })
+          .catch(() => {
+            this._isBlameLoading = false;
+          });
+    },
+
+    _computeBlameLoaderClass(isImageDiff, supported) {
+      return !isImageDiff && supported ? 'show' : '';
+    },
   });
 })();