{"action":"created","comment":{"url":"https://api.github.com/repos/oppia/oppia/pulls/comments/39320743","id":39320743,"diff_hunk":"@@ -75,227 +218,425 @@ oppia.directive('conversationSkin', [function() {\n         }, 100);\n       };\n \n-      $window.addEventListener('beforeunload', function(e) {\n-        if (hasInteractedAtLeastOnce && !$scope.finished &&\n-            !$scope.isInPreviewMode) {\n-          oppiaPlayerService.registerMaybeLeaveEvent();\n-          var confirmationMessage = (\n-            'If you navigate away from this page, your progress on the ' +\n-            'exploration will be lost.');\n-          (e || $window.event).returnValue = confirmationMessage;\n-          return confirmationMessage;\n+      $scope.getThumbnailSrc = function(panelName) {\n+        if (panelName === $scope.PANEL_TUTOR) {\n+          return '/images/general/o_black_72px.png';\n+        } else if (panelName === $scope.PANEL_INTERACTION) {\n+          return oppiaPlayerService.getInteractionThumbnailSrc(\n+            $scope.activeCard.stateName);\n+        } else {\n+          throw Error(\n+            'Found a panel not corresponding to a tutor or interaction: ' +\n+            panelName);\n         }\n-      });\n+      };\n \n-      var _scrollToBottom = function(postScrollCallback) {\n-        $scope.adjustPageHeight(true, function() {\n-          var oppiaLastContentHeight = $('.conversation-skin-oppia-output:last')\n-            .offset().top;\n-          var newScrollTop = null;\n-          if ($(document).height() - oppiaLastContentHeight - 60 <=\n-              $(window).height() * 0.5) {\n-            // The -60 prevents the attribution guide from being scrolled into view.\n-            newScrollTop = $(document).height() - $(window).height() - 60;\n-            _learnerInputIsInView = true;\n-          } else {\n-            newScrollTop = oppiaLastContentHeight - $(window).height() * 0.5;\n-            _learnerInputIsInView = false;\n-          }\n+      var _recomputeAndResetPanels = function() {\n+        $scope.panels = [];\n+        if (!$scope.canWindowFitTwoCards()) {\n+          $scope.panels.push($scope.PANEL_TUTOR);\n+        }\n+        if (!$scope.interactionIsInline) {\n+          $scope.panels.push($scope.PANEL_INTERACTION);\n+        }\n+        $scope.resetVisiblePanel();\n+      };\n \n-          // Do not scroll up.\n-          // This occurs if Oppia gives no feedback for (e.g.) a supplemental\n-          // interaction. This leads to a scroll *up* to Oppia's last output,\n-          // which is rather disconcerting.\n-          if ($(document).scrollTop() >= newScrollTop) {\n-            newScrollTop = $(document).scrollTop();\n-          }\n+      $scope.currentVisiblePanelName = null;\n+\n+      $scope.isPanelVisible = function(panelName) {\n+        if (panelName === $scope.PANEL_TUTOR && $scope.canWindowFitTwoCards()) {\n+          return true;\n+        } else {\n+          return panelName == $scope.currentVisiblePanelName;\n+        }\n+      };\n \n-          var page = $('html, body, iframe');\n+      $scope.setVisiblePanel = function(panelName) {\n+        $scope.currentVisiblePanelName = panelName;\n+        if (panelName === $scope.PANEL_INTERACTION) {\n+          $scope.$broadcast('showInteraction');\n+        }\n+      };\n+\n+      $scope.resetVisiblePanel = function() {\n+        if ($scope.panels.length === 0) {\n+          $scope.currentVisiblePanelName = null;\n+        } else {\n+          $scope.currentVisiblePanelName = $scope.panels[0];\n+        }\n+      };\n \n-          page.on(\"scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove\", function() {\n-            page.stop();\n-          });\n+      // Changes the currently-active card, and resets the 'show previous\n+      // responses' setting.\n+      var _navigateToCard = function(index) {\n+        $scope.activeCard = $scope.transcript[index];\n+        $scope.arePreviousResponsesShown = false;\n+        $scope.interactionIsInline = oppiaPlayerService.isInteractionInline(\n+          $scope.activeCard.stateName);\n \n-          page.animate({\n-            'scrollTop': newScrollTop\n-          }, 1000, 'easeOutQuad', function() {\n-            page.off(\"scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove\");\n-          }).promise().done(postScrollCallback);\n-        });\n+        _recomputeAndResetPanels();\n       };\n \n-      var _addNewCard = function(stateName, contentHtml) {\n-        $scope.allResponseStates.push({\n+      var _addNewCard = function(stateName, contentHtml, interactionHtml) {\n+        var interactionIsInline = oppiaPlayerService.isInteractionInline(\n+          stateName);\n+        var interactionInstructions = (\n+          oppiaPlayerService.getInteractionInstructions(stateName));\n+\n+        var oldStateName = null;\n+        if ($scope.transcript.length >= 1) {\n+          oldStateName = $scope.transcript[$scope.transcript.length - 1].stateName;\n+        }\n+\n+        oppiaPlayerService.applyCachedParamUpdates();\n+        $scope.transcript.push({\n           stateName: stateName,\n           content: contentHtml,\n+          interaction: interactionHtml,\n+          interactionIsInline: interactionIsInline,\n+          interactionIsDisabled: false,\n+          interactionInstructions: interactionInstructions,\n           answerFeedbackPairs: []\n         });\n+\n+        $scope.numProgressDots++;\n+\n+        if ($scope.canWindowFitTwoCards() &&\n+            oldStateName &&\n+            oppiaPlayerService.isInteractionInline(oldStateName) &&\n+            !oppiaPlayerService.isInteractionInline(stateName)) {\n+          $scope.isAnimatingToTwoCards = true;\n+          $timeout(function() {\n+            $scope.isAnimatingToTwoCards = false;\n+            $scope.currentProgressDotIndex = $scope.numProgressDots - 1;\n+          }, TIME_NUM_CARDS_CHANGE_MSEC + TIME_FADEIN_MSEC + 500);\n+        } else if (\n+            $scope.canWindowFitTwoCards() &&\n+            oldStateName &&\n+            !oppiaPlayerService.isInteractionInline(oldStateName) &&\n+            oppiaPlayerService.isInteractionInline(stateName)) {\n+          $scope.isAnimatingToOneCard = true;\n+          $timeout(function() {\n+            $scope.isAnimatingToOneCard = false;\n+            $scope.currentProgressDotIndex = $scope.numProgressDots - 1;\n+          }, TIME_NUM_CARDS_CHANGE_MSEC + TIME_FADEIN_MSEC + 500);\n+        } else {\n+          $scope.currentProgressDotIndex = $scope.numProgressDots - 1;\n+        }\n       };\n \n-      var MIN_CARD_LOADING_DELAY_MILLISECS = 1000;\n+      $scope.toggleShowPreviousResponses = function() {\n+        $scope.arePreviousResponsesShown = !$scope.arePreviousResponsesShown;\n+      };\n \n       $scope.initializePage = function() {\n-        $scope.allResponseStates = [];\n-        $scope.inputTemplate = '';\n+        $scope.transcript = [];\n         $scope.interactionIsInline = false;\n         $scope.waitingForOppiaFeedback = false;\n-        $scope.waitingForNewCard = false;\n-\n-        // This is measured in milliseconds since the epoch.\n-        var timeAtServerCall = new Date().getTime();\n+        hasInteractedAtLeastOnce = false;\n \n-        oppiaPlayerService.init(function(stateName, initHtml, hasEditingRights, introCardImageUrl) {\n-          $scope.explorationId = oppiaPlayerService.getExplorationId();\n-          $scope.explorationTitle = oppiaPlayerService.getExplorationTitle();\n+        oppiaPlayerService.init(function(stateName, initHtml) {\n           $scope.isLoggedIn = oppiaPlayerService.isLoggedIn();\n-          $scope.introCardImageUrl = introCardImageUrl;\n-          oppiaPlayerService.getUserProfileImage().then(function(result) {\n-            // $scope.profilePicture contains a dataURI representation of the\n-            // user-uploaded profile image, or the path to the default image.\n-            $scope.profilePicture = result;\n-          });\n-          hasInteractedAtLeastOnce = false;\n-          $scope.finished = false;\n-          $scope.hasEditingRights = hasEditingRights;\n-          messengerService.sendMessage(\n-            messengerService.EXPLORATION_LOADED, null);\n-\n-          $scope.stateName = stateName;\n-          _labelForNextFocusTarget = Math.random().toString(36).slice(2);\n-          $scope.inputTemplate = oppiaPlayerService.getInteractionHtml(stateName, _labelForNextFocusTarget);\n-          $scope.interactionIsInline = oppiaPlayerService.isInteractionInline(stateName);\n-          $scope.gadgetPanelsContents = oppiaPlayerService.getGadgetPanelsContents();\n-\n-          // This $timeout prevents a 'flash of unstyled content' when the preview tab is loaded from\n-          // the editor tab.\n-          $timeout(function() {\n-            $rootScope.loadingMessage = '';\n-          }, 500);\n+          _nextFocusLabel = focusService.generateFocusLabel();\n+          $scope.gadgetPanelsContents = (\n+            oppiaPlayerService.getGadgetPanelsContents());\n+\n+          _addNewCard(\n+            stateName,\n+            initHtml,\n+            oppiaPlayerService.getInteractionHtml(stateName, _nextFocusLabel));\n+          $rootScope.loadingMessage = '';\n \n           $scope.adjustPageHeight(false, null);\n           $window.scrollTo(0, 0);\n+          focusService.setFocus(_nextFocusLabel);\n \n-          $scope.waitingForNewCard = true;\n-\n-          var millisecsLeftToWait = Math.max(\n-            MIN_CARD_LOADING_DELAY_MILLISECS - (new Date().getTime() - timeAtServerCall),\n-            1.0);\n-          $timeout(function() {\n-            _addNewCard($scope.stateName, initHtml);\n-            $scope.waitingForNewCard = false;\n-            _scrollToBottom(function() {\n-              if (_learnerInputIsInView) {\n-                focusService.setFocus(_labelForNextFocusTarget);\n-              }\n-            });\n-          }, millisecsLeftToWait);\n-        });\n-\n-        ratingService.init(function(userRating) {\n-          $scope.userRating = userRating;\n+          $scope.finished = oppiaPlayerService.isStateTerminal(stateName);\n         });\n       };\n \n-      $scope.submitUserRating = function(ratingValue) {\n-        ratingService.submitUserRating(ratingValue);\n-      };\n-      $scope.$on('ratingUpdated', function() {\n-        $scope.userRating = ratingService.getUserRating();\n-      });\n-\n-      $scope.initializePage();\n-\n       $scope.submitAnswer = function(answer) {\n         // For some reason, answers are getting submitted twice when the submit\n         // button is clicked. This guards against that.\n         if (_answerIsBeingProcessed) {\n           return;\n         }\n+\n+        $timeout(function() {\n+          _recomputeAndResetPanels();\n+        }, 250);\n+\n         _answerIsBeingProcessed = true;\n         hasInteractedAtLeastOnce = true;\n+        $scope.waitingForOppiaFeedback = true;\n \n-        $scope.allResponseStates[$scope.allResponseStates.length - 1].answerFeedbackPairs.push({\n+        var _oldStateName = (\n+          $scope.transcript[$scope.transcript.length - 1].stateName);\n+        $scope.transcript[$scope.transcript.length - 1].answerFeedbackPairs.push({\n           learnerAnswer: oppiaPlayerService.getAnswerAsHtml(answer),\n-          oppiaFeedback: ''\n+          shortLearnerAnswer: oppiaPlayerService.getShortAnswerAsHtml(answer),\n+          oppiaFeedback: null\n         });\n \n-        $scope.waitingForOppiaFeedback = true;\n-\n         // This is measured in milliseconds since the epoch.\n         var timeAtServerCall = new Date().getTime();\n \n         oppiaPlayerService.submitAnswer(answer, function(\n-            newStateName, refreshInteraction, feedbackHtml, questionHtml, newInteractionId) {\n-\n+            newStateName, refreshInteraction, feedbackHtml, contentHtml) {\n           var millisecsLeftToWait = Math.max(\n-            MIN_CARD_LOADING_DELAY_MILLISECS - (new Date().getTime() - timeAtServerCall),\n+            MIN_WAIT_TIME_MILLISECS - (new Date().getTime() - timeAtServerCall),\n             1.0);\n \n-          $timeout(function() {\n-            var oldStateName = $scope.stateName;\n-            $scope.stateName = newStateName;\n-\n-            $scope.finished = oppiaPlayerService.isStateTerminal(newStateName);\n-            if ($scope.finished) {\n-              messengerService.sendMessage(\n-                messengerService.EXPLORATION_COMPLETED, null);\n-            }\n-\n-            if (!newStateName) {\n-              $scope.inputTemplate = '';\n-            } else if (newStateName && refreshInteraction) {\n-              // The previous interaction should be replaced.\n-              _labelForNextFocusTarget = Math.random().toString(36).slice(2);\n-              $scope.inputTemplate = oppiaPlayerService.getInteractionHtml(\n-                newStateName, _labelForNextFocusTarget) + oppiaPlayerService.getRandomSuffix();\n-              $scope.interactionIsInline = oppiaPlayerService.isInteractionInline(\n-                newStateName);\n+          $scope.waitingForOppiaFeedback = false;\n+          var pairs = (\n+            $scope.transcript[$scope.transcript.length - 1].answerFeedbackPairs);\n+          var lastAnswerFeedbackPair = pairs[pairs.length - 1];\n+\n+          if (_oldStateName === newStateName) {\n+            // Stay on the same card.\n+            lastAnswerFeedbackPair.oppiaFeedback = feedbackHtml;\n+            if (refreshInteraction) {\n+              // Replace the previous interaction (even though it might be of\n+              // the same type).\n+              _nextFocusLabel = focusService.generateFocusLabel();\n+              $scope.transcript[$scope.transcript.length - 1].interaction = (\n+                oppiaPlayerService.getInteractionHtml(newStateName, _nextFocusLabel) +\n+                oppiaPlayerService.getRandomSuffix());\n             }\n-\n-            var pairs = $scope.allResponseStates[$scope.allResponseStates.length - 1].answerFeedbackPairs;\n-            pairs[pairs.length - 1].oppiaFeedback = feedbackHtml;\n-\n-            if (oldStateName === newStateName) {\n-              $scope.waitingForOppiaFeedback = false;\n-              _scrollToBottom(function() {\n-                if (_learnerInputIsInView) {\n-                  focusService.setFocus(_labelForNextFocusTarget);\n-                }\n-                _answerIsBeingProcessed = false;\n-              });\n+            focusService.setFocus(_nextFocusLabel);\n+            scrollToBottom();\n+          } else {\n+            // There is a new card. Disable the current interaction -- then, if\n+            // there is no feedback, move on immediately. Otherwise, give the\n+            // learner a chance to read the feedback, and display a 'Continue'\n+            // button.\n+            $scope.transcript[$scope.transcript.length - 1].interactionIsDisabled = true;\n+\n+            _nextFocusLabel = focusService.generateFocusLabel();\n+\n+            // These are used to compute the dimensions for the next card.\n+            $scope.upcomingStateName = newStateName;\n+            $scope.upcomingContentHtml = (\n+              contentHtml + oppiaPlayerService.getRandomSuffix());\n+            var _isNextInteractionInline = oppiaPlayerService.isInteractionInline(\n+              newStateName);\n+            $scope.upcomingInlineInteractionHtml = (\n+              _isNextInteractionInline ?\n+              oppiaPlayerService.getInteractionHtml(\n+                newStateName, _nextFocusLabel\n+              ) + oppiaPlayerService.getRandomSuffix() : '');\n+\n+            if (feedbackHtml) {\n+              lastAnswerFeedbackPair.oppiaFeedback = feedbackHtml;\n+              $scope.waitingForContinueButtonClick = true;\n+              focusService.setFocus($scope.CONTINUE_BUTTON_FOCUS_LABEL);\n+              scrollToBottom();\n             } else {\n-              if (feedbackHtml) {\n-                $scope.waitingForOppiaFeedback = false;\n-                $scope.waitingForNewCard = true;\n-                _scrollToBottom(function() {\n-                  $timeout(function() {\n-                    $scope.waitingForNewCard = false;\n-                    _addNewCard($scope.stateName, questionHtml);\n-                    _scrollToBottom(function() {\n-                      if (_learnerInputIsInView) {\n-                        focusService.setFocus(_labelForNextFocusTarget);\n-                      }\n-                      _answerIsBeingProcessed = false;\n-                    });\n-                  }, 1000);\n+              // Note that feedbackHtml is an empty string if no feedback has\n+              // been specified. This causes the answer-feedback pair to change\n+              // abruptly, so we make the change only after the animation has\n+              // completed.\n+              $scope.showPendingCard(\n+                newStateName,\n+                contentHtml + oppiaPlayerService.getRandomSuffix(),\n+                function() {\n+                  lastAnswerFeedbackPair.oppiaFeedback = feedbackHtml;\n                 });\n-              } else {\n-                $scope.waitingForOppiaFeedback = false;\n-                _addNewCard($scope.stateName, questionHtml);\n-                _scrollToBottom(function() {\n-                  if (_learnerInputIsInView) {\n-                    focusService.setFocus(_labelForNextFocusTarget);\n-                  }\n-                  _answerIsBeingProcessed = false;\n-                });\n-              }\n             }\n-          }, millisecsLeftToWait);\n+          }\n+\n+          $scope.finished = oppiaPlayerService.isStateTerminal(newStateName);\n+          _answerIsBeingProcessed = false;\n+        }, true);\n+      };\n+\n+      $scope.startCardChangeAnimation = false;\n+      $scope.showPendingCard = function(newStateName, newContentHtml, successCallback) {\n+        $scope.waitingForContinueButtonClick = false;\n+        $scope.startCardChangeAnimation = true;\n+\n+        $timeout(function() {\n+          _addNewCard(\n+            newStateName,\n+            newContentHtml,\n+            oppiaPlayerService.getInteractionHtml(\n+              newStateName, _nextFocusLabel) + oppiaPlayerService.getRandomSuffix());\n+\n+          $scope.upcomingStateName = null;\n+          $scope.upcomingContentHtml = null;\n+          $scope.upcomingInlineInteractionHtml = null;\n+        }, TIME_FADEOUT_MSEC + 0.1 * TIME_HEIGHT_CHANGE_MSEC);\n+\n+        $timeout(function() {\n+          focusService.setFocus(_nextFocusLabel);\n+          scrollToTop();\n+        }, TIME_FADEOUT_MSEC + TIME_HEIGHT_CHANGE_MSEC + 0.5 * TIME_FADEIN_MSEC);\n+\n+        $timeout(function() {\n+          $scope.startCardChangeAnimation = false;\n+          if (successCallback) {\n+            successCallback();\n+          }\n+        }, TIME_FADEOUT_MSEC + TIME_HEIGHT_CHANGE_MSEC + TIME_FADEIN_MSEC + 10);\n+      };\n+\n+      var scrollToBottom = function() {\n+        $timeout(function() {\n+          var tutorCard = $('.conversation-skin-tutor-card-active');\n+          var tutorCardBottom = tutorCard.offset().top + tutorCard.outerHeight();\n+          if ($(window).scrollTop() + $(window).height() < tutorCardBottom) {\n+            $('html, body').animate({\n+              scrollTop: tutorCardBottom - $(window).height() + 12\n+            }, {\n+              duration: 600,\n+              easing: 'easeOutQuad'\n+            });\n+          }\n+        }, 100);\n+      };\n+\n+      var scrollToTop = function() {\n+        $timeout(function() {\n+          $(window).scrollTop(0);\n         });\n       };\n \n+      $scope.submitUserRating = function(ratingValue) {\n+        ratingService.submitUserRating(ratingValue);\n+      };\n+      $scope.$on('ratingUpdated', function() {\n+        $scope.userRating = ratingService.getUserRating();\n+      });\n+\n+      $scope.$watch('currentProgressDotIndex', function(newValue) {\n+        if (newValue !== null) {\n+          _navigateToCard(newValue);\n+        }\n+      });\n+\n+      $scope.isWindowNarrow = function() {\n+        return $(window).width() < 700;\n+      };\n+\n+      $window.addEventListener('beforeunload', function(e) {\n+        if (hasInteractedAtLeastOnce && !$scope.finished &&\n+            !$scope.isInPreviewMode) {\n+          oppiaPlayerService.registerMaybeLeaveEvent();\n+          var confirmationMessage = (\n+            'If you navigate away from this page, your progress on the ' +\n+            'exploration will be lost.');\n+          (e || $window.event).returnValue = confirmationMessage;\n+          return confirmationMessage;\n+        }\n+      });\n+\n+      $scope.windowWidth = windowDimensionsService.getWidth();\n       $window.onresize = function() {\n         $scope.adjustPageHeight(false, null);\n+        $scope.windowWidth = windowDimensionsService.getWidth();\n+        _recomputeAndResetPanels();\n+      };\n+\n+      $window.addEventListener('scroll', function() {\n+        fadeDotsOnScroll();\n+        fixSupplementOnScroll();\n+      });\n+\n+      var fadeDotsOnScroll = function() {\n+        var progressDots = $('.conversation-skin-progress-dots');\n+        var progressDotsTop = progressDots.height();\n+        var newOpacity = Math.max(\n+          (progressDotsTop - $(window).scrollTop()) / progressDotsTop, 0);\n+        progressDots.css({opacity: newOpacity});\n+      };\n+\n+      var fixSupplementOnScroll = function() {\n+        var supplementCard = $('md-card.conversation-skin-supplemental-card');\n+        var topMargin = $('.navbar-container').height() - 20;\n+        if ($(window).scrollTop() > topMargin) {\n+          supplementCard.addClass('conversation-skin-supplemental-card-fixed');\n+        } else {\n+          supplementCard.removeClass('conversation-skin-supplemental-card-fixed');\n+        }\n+      };\n+\n+      $scope.canWindowFitTwoCards = function() {\n+        return $scope.windowWidth >= $scope.TWO_CARD_THRESHOLD_PX;\n+      };\n+\n+      $scope.initializePage();\n+      ratingService.init(function(userRating) {\n+        $scope.userRating = userRating;\n+      });\n+\n+      oppiaPlayerService.getUserProfileImage().then(function(result) {\n+        $scope.profilePicture = result;\n+      });\n+    }]\n+  };\n+}]);\n+\n+oppia.directive('answerFeedbackPair', [function() {\n+  return {\n+    restrict: 'E',\n+    scope: {\n+      answer: '&',\n+      feedback: '&',\n+      profilePicture: '&',\n+      shortAnswer: '&'\n+    },\n+    templateUrl: 'components/answerFeedbackPair'\n+  };\n+}]);\n+","path":"extensions/skins/conversation_v1/Conversation.js","position":744,"original_position":744,"commit_id":"c0b7d466a52a5fbaf5d5bdde8bee4eb18efedd90","original_commit_id":"c0b7d466a52a5fbaf5d5bdde8bee4eb18efedd90","user":{"login":"BenHenning","id":12983742,"avatar_url":"https://avatars.githubusercontent.com/u/12983742?v=3","gravatar_id":"","url":"https://api.github.com/users/BenHenning","html_url":"https://github.com/BenHenning","followers_url":"https://api.github.com/users/BenHenning/followers","following_url":"https://api.github.com/users/BenHenning/following{/other_user}","gists_url":"https://api.github.com/users/BenHenning/gists{/gist_id}","starred_url":"https://api.github.com/users/BenHenning/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BenHenning/subscriptions","organizations_url":"https://api.github.com/users/BenHenning/orgs","repos_url":"https://api.github.com/users/BenHenning/repos","events_url":"https://api.github.com/users/BenHenning/events{/privacy}","received_events_url":"https://api.github.com/users/BenHenning/received_events","type":"User","site_admin":false},"body":"Nit: newline","created_at":"2015-09-11T21:39:46Z","updated_at":"2015-09-11T21:39:46Z","html_url":"https://github.com/oppia/oppia/pull/943#discussion_r39320743","pull_request_url":"https://api.github.com/repos/oppia/oppia/pulls/943","_links":{"self":{"href":"https://api.github.com/repos/oppia/oppia/pulls/comments/39320743"},"html":{"href":"https://github.com/oppia/oppia/pull/943#discussion_r39320743"},"pull_request":{"href":"https://api.github.com/repos/oppia/oppia/pulls/943"}}},"pull_request":{"url":"https://api.github.com/repos/oppia/oppia/pulls/943","id":43688103,"html_url":"https://github.com/oppia/oppia/pull/943","diff_url":"https://github.com/oppia/oppia/pull/943.diff","patch_url":"https://github.com/oppia/oppia/pull/943.patch","issue_url":"https://api.github.com/repos/oppia/oppia/issues/943","number":943,"state":"open","locked":false,"title":"Redesign the learner view.","user":{"login":"seanlip","id":10575562,"avatar_url":"https://avatars.githubusercontent.com/u/10575562?v=3","gravatar_id":"","url":"https://api.github.com/users/seanlip","html_url":"https://github.com/seanlip","followers_url":"https://api.github.com/users/seanlip/followers","following_url":"https://api.github.com/users/seanlip/following{/other_user}","gists_url":"https://api.github.com/users/seanlip/gists{/gist_id}","starred_url":"https://api.github.com/users/seanlip/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seanlip/subscriptions","organizations_url":"https://api.github.com/users/seanlip/orgs","repos_url":"https://api.github.com/users/seanlip/repos","events_url":"https://api.github.com/users/seanlip/events{/privacy}","received_events_url":"https://api.github.com/users/seanlip/received_events","type":"User","site_admin":false},"body":"This is the commit for the redesign of the learner view that @amitdeutsch and I have been working on. Sending to @BenHenning for code review. @BenHenning also has authority to merge if he thinks this is ready.\r\n\r\n@amitdeutsch / @BenHenning, please feel free to address any review comments that you're able to. Where possible, it would probably be a good idea to leave some comments for separate work after this branch gets merged into 'develop', since this is blocking @anuzis (who is depending on this for upcoming work on gadgets). Thanks!","created_at":"2015-08-29T03:55:07Z","updated_at":"2015-09-11T21:39:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a0ac3dbf2290de54937009a1edf73eb5c618c589","assignee":{"login":"BenHenning","id":12983742,"avatar_url":"https://avatars.githubusercontent.com/u/12983742?v=3","gravatar_id":"","url":"https://api.github.com/users/BenHenning","html_url":"https://github.com/BenHenning","followers_url":"https://api.github.com/users/BenHenning/followers","following_url":"https://api.github.com/users/BenHenning/following{/other_user}","gists_url":"https://api.github.com/users/BenHenning/gists{/gist_id}","starred_url":"https://api.github.com/users/BenHenning/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BenHenning/subscriptions","organizations_url":"https://api.github.com/users/BenHenning/orgs","repos_url":"https://api.github.com/users/BenHenning/repos","events_url":"https://api.github.com/users/BenHenning/events{/privacy}","received_events_url":"https://api.github.com/users/BenHenning/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/oppia/oppia/pulls/943/commits","review_comments_url":"https://api.github.com/repos/oppia/oppia/pulls/943/comments","review_comment_url":"https://api.github.com/repos/oppia/oppia/pulls/comments{/number}","comments_url":"https://api.github.com/repos/oppia/oppia/issues/943/comments","statuses_url":"https://api.github.com/repos/oppia/oppia/statuses/c0b7d466a52a5fbaf5d5bdde8bee4eb18efedd90","head":{"label":"oppia:improved-skin","ref":"improved-skin","sha":"c0b7d466a52a5fbaf5d5bdde8bee4eb18efedd90","user":{"login":"oppia","id":11620230,"avatar_url":"https://avatars.githubusercontent.com/u/11620230?v=3","gravatar_id":"","url":"https://api.github.com/users/oppia","html_url":"https://github.com/oppia","followers_url":"https://api.github.com/users/oppia/followers","following_url":"https://api.github.com/users/oppia/following{/other_user}","gists_url":"https://api.github.com/users/oppia/gists{/gist_id}","starred_url":"https://api.github.com/users/oppia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oppia/subscriptions","organizations_url":"https://api.github.com/users/oppia/orgs","repos_url":"https://api.github.com/users/oppia/repos","events_url":"https://api.github.com/users/oppia/events{/privacy}","received_events_url":"https://api.github.com/users/oppia/received_events","type":"Organization","site_admin":false},"repo":{"id":40687563,"name":"oppia","full_name":"oppia/oppia","owner":{"login":"oppia","id":11620230,"avatar_url":"https://avatars.githubusercontent.com/u/11620230?v=3","gravatar_id":"","url":"https://api.github.com/users/oppia","html_url":"https://github.com/oppia","followers_url":"https://api.github.com/users/oppia/followers","following_url":"https://api.github.com/users/oppia/following{/other_user}","gists_url":"https://api.github.com/users/oppia/gists{/gist_id}","starred_url":"https://api.github.com/users/oppia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oppia/subscriptions","organizations_url":"https://api.github.com/users/oppia/orgs","repos_url":"https://api.github.com/users/oppia/repos","events_url":"https://api.github.com/users/oppia/events{/privacy}","received_events_url":"https://api.github.com/users/oppia/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/oppia/oppia","description":"Tool for creating interactive tutors.","fork":false,"url":"https://api.github.com/repos/oppia/oppia","forks_url":"https://api.github.com/repos/oppia/oppia/forks","keys_url":"https://api.github.com/repos/oppia/oppia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oppia/oppia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oppia/oppia/teams","hooks_url":"https://api.github.com/repos/oppia/oppia/hooks","issue_events_url":"https://api.github.com/repos/oppia/oppia/issues/events{/number}","events_url":"https://api.github.com/repos/oppia/oppia/events","assignees_url":"https://api.github.com/repos/oppia/oppia/assignees{/user}","branches_url":"https://api.github.com/repos/oppia/oppia/branches{/branch}","tags_url":"https://api.github.com/repos/oppia/oppia/tags","blobs_url":"https://api.github.com/repos/oppia/oppia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oppia/oppia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oppia/oppia/git/refs{/sha}","trees_url":"https://api.github.com/repos/oppia/oppia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oppia/oppia/statuses/{sha}","languages_url":"https://api.github.com/repos/oppia/oppia/languages","stargazers_url":"https://api.github.com/repos/oppia/oppia/stargazers","contributors_url":"https://api.github.com/repos/oppia/oppia/contributors","subscribers_url":"https://api.github.com/repos/oppia/oppia/subscribers","subscription_url":"https://api.github.com/repos/oppia/oppia/subscription","commits_url":"https://api.github.com/repos/oppia/oppia/commits{/sha}","git_commits_url":"https://api.github.com/repos/oppia/oppia/git/commits{/sha}","comments_url":"https://api.github.com/repos/oppia/oppia/comments{/number}","issue_comment_url":"https://api.github.com/repos/oppia/oppia/issues/comments{/number}","contents_url":"https://api.github.com/repos/oppia/oppia/contents/{+path}","compare_url":"https://api.github.com/repos/oppia/oppia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oppia/oppia/merges","archive_url":"https://api.github.com/repos/oppia/oppia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oppia/oppia/downloads","issues_url":"https://api.github.com/repos/oppia/oppia/issues{/number}","pulls_url":"https://api.github.com/repos/oppia/oppia/pulls{/number}","milestones_url":"https://api.github.com/repos/oppia/oppia/milestones{/number}","notifications_url":"https://api.github.com/repos/oppia/oppia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oppia/oppia/labels{/name}","releases_url":"https://api.github.com/repos/oppia/oppia/releases{/id}","created_at":"2015-08-14T00:16:14Z","updated_at":"2015-09-09T07:17:22Z","pushed_at":"2015-09-11T21:11:51Z","git_url":"git://github.com/oppia/oppia.git","ssh_url":"git@github.com:oppia/oppia.git","clone_url":"https://github.com/oppia/oppia.git","svn_url":"https://github.com/oppia/oppia","homepage":"https://www.oppia.org","size":42721,"stargazers_count":24,"watchers_count":24,"language":"Python","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":36,"mirror_url":null,"open_issues_count":163,"forks":36,"open_issues":163,"watchers":24,"default_branch":"develop"}},"base":{"label":"oppia:develop","ref":"develop","sha":"08260e8be33bcb38069d5e701dd98fd8309462ef","user":{"login":"oppia","id":11620230,"avatar_url":"https://avatars.githubusercontent.com/u/11620230?v=3","gravatar_id":"","url":"https://api.github.com/users/oppia","html_url":"https://github.com/oppia","followers_url":"https://api.github.com/users/oppia/followers","following_url":"https://api.github.com/users/oppia/following{/other_user}","gists_url":"https://api.github.com/users/oppia/gists{/gist_id}","starred_url":"https://api.github.com/users/oppia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oppia/subscriptions","organizations_url":"https://api.github.com/users/oppia/orgs","repos_url":"https://api.github.com/users/oppia/repos","events_url":"https://api.github.com/users/oppia/events{/privacy}","received_events_url":"https://api.github.com/users/oppia/received_events","type":"Organization","site_admin":false},"repo":{"id":40687563,"name":"oppia","full_name":"oppia/oppia","owner":{"login":"oppia","id":11620230,"avatar_url":"https://avatars.githubusercontent.com/u/11620230?v=3","gravatar_id":"","url":"https://api.github.com/users/oppia","html_url":"https://github.com/oppia","followers_url":"https://api.github.com/users/oppia/followers","following_url":"https://api.github.com/users/oppia/following{/other_user}","gists_url":"https://api.github.com/users/oppia/gists{/gist_id}","starred_url":"https://api.github.com/users/oppia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oppia/subscriptions","organizations_url":"https://api.github.com/users/oppia/orgs","repos_url":"https://api.github.com/users/oppia/repos","events_url":"https://api.github.com/users/oppia/events{/privacy}","received_events_url":"https://api.github.com/users/oppia/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/oppia/oppia","description":"Tool for creating interactive tutors.","fork":false,"url":"https://api.github.com/repos/oppia/oppia","forks_url":"https://api.github.com/repos/oppia/oppia/forks","keys_url":"https://api.github.com/repos/oppia/oppia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oppia/oppia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oppia/oppia/teams","hooks_url":"https://api.github.com/repos/oppia/oppia/hooks","issue_events_url":"https://api.github.com/repos/oppia/oppia/issues/events{/number}","events_url":"https://api.github.com/repos/oppia/oppia/events","assignees_url":"https://api.github.com/repos/oppia/oppia/assignees{/user}","branches_url":"https://api.github.com/repos/oppia/oppia/branches{/branch}","tags_url":"https://api.github.com/repos/oppia/oppia/tags","blobs_url":"https://api.github.com/repos/oppia/oppia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oppia/oppia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oppia/oppia/git/refs{/sha}","trees_url":"https://api.github.com/repos/oppia/oppia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oppia/oppia/statuses/{sha}","languages_url":"https://api.github.com/repos/oppia/oppia/languages","stargazers_url":"https://api.github.com/repos/oppia/oppia/stargazers","contributors_url":"https://api.github.com/repos/oppia/oppia/contributors","subscribers_url":"https://api.github.com/repos/oppia/oppia/subscribers","subscription_url":"https://api.github.com/repos/oppia/oppia/subscription","commits_url":"https://api.github.com/repos/oppia/oppia/commits{/sha}","git_commits_url":"https://api.github.com/repos/oppia/oppia/git/commits{/sha}","comments_url":"https://api.github.com/repos/oppia/oppia/comments{/number}","issue_comment_url":"https://api.github.com/repos/oppia/oppia/issues/comments{/number}","contents_url":"https://api.github.com/repos/oppia/oppia/contents/{+path}","compare_url":"https://api.github.com/repos/oppia/oppia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oppia/oppia/merges","archive_url":"https://api.github.com/repos/oppia/oppia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oppia/oppia/downloads","issues_url":"https://api.github.com/repos/oppia/oppia/issues{/number}","pulls_url":"https://api.github.com/repos/oppia/oppia/pulls{/number}","milestones_url":"https://api.github.com/repos/oppia/oppia/milestones{/number}","notifications_url":"https://api.github.com/repos/oppia/oppia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oppia/oppia/labels{/name}","releases_url":"https://api.github.com/repos/oppia/oppia/releases{/id}","created_at":"2015-08-14T00:16:14Z","updated_at":"2015-09-09T07:17:22Z","pushed_at":"2015-09-11T21:11:51Z","git_url":"git://github.com/oppia/oppia.git","ssh_url":"git@github.com:oppia/oppia.git","clone_url":"https://github.com/oppia/oppia.git","svn_url":"https://github.com/oppia/oppia","homepage":"https://www.oppia.org","size":42721,"stargazers_count":24,"watchers_count":24,"language":"Python","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":36,"mirror_url":null,"open_issues_count":163,"forks":36,"open_issues":163,"watchers":24,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/oppia/oppia/pulls/943"},"html":{"href":"https://github.com/oppia/oppia/pull/943"},"issue":{"href":"https://api.github.com/repos/oppia/oppia/issues/943"},"comments":{"href":"https://api.github.com/repos/oppia/oppia/issues/943/comments"},"review_comments":{"href":"https://api.github.com/repos/oppia/oppia/pulls/943/comments"},"review_comment":{"href":"https://api.github.com/repos/oppia/oppia/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/oppia/oppia/pulls/943/commits"},"statuses":{"href":"https://api.github.com/repos/oppia/oppia/statuses/c0b7d466a52a5fbaf5d5bdde8bee4eb18efedd90"}}}}