Bug 1168829 - Part 2. Adjust Loop's Standalone UI to include text chat for conversations. r=mikedeboer
authorMark Banner <standard8@mozilla.com>
Thu, 18 Jun 2015 14:18:02 +0100
changeset 249534 f160bb5abec102a8b0272a6bb77c73c5c3c39f15
parent 249533 ec9c9d0b9d753547870ccca2c845d700ea40ee97
child 249535 5442bb7741fc445b6c5aa72fb8bed66ace5f37a8
push id28929
push userryanvm@gmail.com
push dateThu, 18 Jun 2015 19:51:41 +0000
treeherdermozilla-central@d56a1257088e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer
bugs1168829
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1168829 - Part 2. Adjust Loop's Standalone UI to include text chat for conversations. r=mikedeboer
browser/components/loop/content/shared/css/conversation.css
browser/components/loop/standalone/content/css/webapp.css
browser/components/loop/standalone/content/js/standaloneRoomViews.js
browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
browser/components/loop/test/standalone/standaloneRoomViews_test.js
browser/components/loop/ui/ui-showcase.js
browser/components/loop/ui/ui-showcase.jsx
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -255,18 +255,17 @@
 }
 
 .fx-embedded .no-video {
   background: black none repeat scroll 0% 0%;
   height: 100%;
   width: 100%;
 }
 
-.standalone .local-stream,
-.standalone .remote-inset-stream {
+.standalone .local-stream {
   /* required to have it superimposed to the control toolbar */
   z-index: 1001;
 }
 
 /* Side by side video elements */
 
 .conversation .media.side-by-side .focus-stream {
   width: 50%;
@@ -549,17 +548,17 @@
   /*
    * Expand to fill the available space, since there is no video any
    * intrinsic width. XXX should really change to an <img> for clarity
    */
   height: 100%;
   width: 100%;
 }
 
-.local .avatar {
+.conversation .local .avatar {
   position: absolute;
   z-index: 1;
 }
 
 .remote .avatar {
   /* make visually distinct from local avatar */
   opacity: 0.25;
 }
@@ -642,26 +641,25 @@
  * */
 html, .fx-embedded, #main,
 .video-layout-wrapper,
 .conversation {
   height: 100%;
 }
 
 /* We use 641px rather than 640, as min-width and max-width are inclusive */
-@media screen and (min-width:641px) {
-  .standalone .conversation-toolbar {
+@media screen and (min-width: 641px) {
+  .standalone .conversation .conversation-toolbar {
     position: absolute;
     bottom: 0;
     left: 0;
     right: 0;
   }
 
-  .standalone .local-stream,
-  .standalone .remote-inset-stream {
+  .standalone .conversation .local-stream {
     position: absolute;
     right: 15px;
     bottom: 15px;
     width: 20%;
     height: 20%;
     max-width: 400px;
     max-height: 300px;
   }
@@ -696,21 +694,17 @@ html, .fx-embedded, #main,
   .standalone .media {
     height: 90%;
   }
 
   .standalone .media.nested {
     min-height: 500px;
   }
 
-  .standalone .remote-inset-stream {
-    display: none;
-  }
-
-  .standalone .local-stream {
+  .standalone .conversation .local-stream {
     flex: 1;
     min-width: 120px;
     min-height: 150px;
     width: 100%;
   }
 
   /* Nested video elements */
   .standalone .conversation .media.nested {
@@ -748,39 +742,40 @@ html, .fx-embedded, #main,
   position: relative;
   height: 100%;
 }
 
 .room-conversation-wrapper header {
   background: #000;
   height: 50px;
   text-align: left;
-  width: 75%;
+  margin: 0 10px;
 }
 
 .room-conversation-wrapper header h1 {
   font-size: 1.5em;
   color: #fff;
   line-height: 50px;
-  text-indent: 60px;
+  text-indent: 40px;
   background-image: url("../img/firefox-logo.png");
   background-size: 30px;
-  background-position: 20px;
+  background-position: 0 center;
   background-repeat: no-repeat;
   display: inline-block;
+  margin: 0 10px;
 }
 
 .room-conversation-wrapper header a {
   float: right;
 }
 
 .room-conversation-wrapper header .icon-help {
   display: inline-block;
   background-size: contain;
-  margin-top: 20px;
+  margin-top: 15px;
   width: 20px;
   height: 20px;
   background: transparent url("../img/svg/glyph-help-16x16.svg") no-repeat;
 }
 
 .fx-embedded .room-conversation .conversation-toolbar .btn-hangup {
   background-image: url("../img/icons-16x16.svg#leave");
 }
@@ -880,16 +875,17 @@ body[platform="win"] .share-service-drop
 .dropdown-menu-item:hover:active > .icon-add-share-service {
   background-image: url("../img/icons-16x16.svg#add-active");
 }
 
 .context-url-view-wrapper {
   padding-left: 1em;
   padding-right: 1em;
   padding-bottom: 0.5em;
+  margin-bottom: 0.5em;
   background-color: #E8F6FE;
 }
 
 .room-context {
   background: rgba(0,0,0,.6);
   border-top: 2px solid #444;
   border-bottom: 2px solid #444;
   padding: .5rem;
@@ -1075,16 +1071,208 @@ html[dir="rtl"] .room-context-btn-edit {
   right: auto;
   left: 8px;
 }
 
 html[dir="rtl"] .room-context-btn-edit {
   left: 20px;
 }
 
+.media-layout {
+  /* 50px is the header, 3em is the footer. */
+  height: calc(100% - 50px - 3em);
+}
+
+.media-layout > .media-wrapper {
+  display: flex;
+  flex-flow: column wrap;
+  /* 64px for .conversation-toolbar */
+  height: calc(100% - 64px);
+  margin: 0 10px;
+}
+
+.media-wrapper > .focus-stream {
+  /* We want this to be the width, minus 200px which is for the right-side text
+     chat and video displays. */
+  width: calc(100% - 200px);
+  /* 100% height to fill up media-layout, thus forcing other elements into the
+     second column that's 200px wide */
+  height: 100%;
+  background-color: #4E4E4E;
+}
+
+.media-wrapper > .remote {
+  /* Works around an issue with object-fit: cover in Google Chrome - it doesn't
+     currently crop but overlaps the surrounding elements.
+     https://code.google.com/p/chromium/issues/detail?id=400829 */
+  overflow: hidden;
+}
+
+.media-wrapper > .remote > .remote-video {
+  object-fit: cover;
+}
+
+/* Note: we can't use flex for the text-chat-view as this lets it overflow
+   the expected column heights, and we ca't fix its height. */
+.media-wrapper > .text-chat-view {
+  flex: 0 0 auto;
+  /* Text chat is a fixed 200px width for normal displays. */
+  width: 200px;
+  height: 100%;
+}
+
+.media-wrapper.showing-local-streams > .text-chat-view {
+  /* When we're displaying the local streams, then we need to make the text
+     chat view a bit shorter to give room. */
+  height: calc(100% - 150px);
+}
+
+.media-wrapper.showing-local-streams.receiving-screen-share > .text-chat-view {
+  /* When we're displaying the local streams, then we need to make the text
+     chat view a bit shorter to give room. */
+  height: calc(100% - 300px);
+}
+
+.media-wrapper > .text-chat-view > .text-chat-entries {
+  /* 40px is the height of .text-chat-box. */
+  height: calc(100% - 40px);
+}
+
+.media-wrapper > .local {
+  flex: 0 1 auto;
+  width: 200px;
+  height: 150px;
+}
+
+.media-wrapper.receiving-screen-share > .screen {
+  order: 1;
+}
+
+.media-wrapper.receiving-screen-share > .text-chat-view {
+  order: 2;
+}
+
+.media-wrapper.receiving-screen-share > .remote {
+  order: 3;
+  flex: 0 1 auto;
+  width: 200px;
+  height: 150px;
+}
+
+.media-wrapper.receiving-screen-share > .local {
+  order: 4;
+}
+
+@media screen and (max-width:640px) {
+  .media-layout {
+    /* 50px is height of header, 25px is height of footer. */
+    height: calc(100% - 50px - 25px);
+  }
+
+  .media-layout > .media-wrapper {
+    flex-direction: row;
+    margin: 0;
+    width: 100%;
+    /* conversation toolbar is 38px in narrow mode */
+    height: calc(100% - 38px);
+  }
+
+  .media-wrapper > .focus-stream {
+    width: 100%;
+    /* A reasonable height */
+    height: 70%;
+  }
+
+  .media-wrapper.receiving-screen-share > .focus-stream {
+    height: 50%;
+  }
+
+  .media-wrapper > .text-chat-view > .text-chat-entries {
+    /* 40px is the height of .text-chat-box. */
+    height: calc(100% - 40px);
+    width: 100%;
+  }
+
+  .media-wrapper > .local {
+    /* Position over the remote video */
+    position: absolute;
+    /* Make sure its on top */
+    z-index: 1001;
+    margin: 3px;
+    right: 0;
+    /* 29px is (30% of 50px high header) + (height toolbar (38px) +
+       height footer (25px) - height header (50px)) */
+    bottom: calc(30% + 29px);
+    width: 120px;
+    height: 120px;
+  }
+
+  html[dir="rtl"] .media-wrapper > .local {
+    right: auto;
+    left: 0;
+  }
+
+  .media-wrapper > .text-chat-view {
+    order: 3;
+    flex: 1 1 auto;
+    width: 100%;
+  }
+
+  .media-wrapper > .text-chat-view,
+  .media-wrapper.showing-local-streams > .text-chat-view,
+  .media-wrapper.showing-local-streams.receiving-screen-share > .text-chat-view {
+    /* The remaining 30% that the .focus-stream doesn't use. */
+    height: 30%;
+  }
+
+  .media-wrapper.receiving-screen-share > .screen {
+    order: 1;
+  }
+
+  .media-wrapper.receiving-screen-share > .remote {
+    /* Screen shares have remote & local video side-by-side on narrow screens */
+    order: 2;
+    flex: 1 1 auto;
+    height: 20%;
+    /* Ensure no previously specified widths take effect, and we take up no more
+       than half the width. */
+    width: auto;
+    max-width: 50%;
+  }
+
+  .media-wrapper.receiving-screen-share > .remote > .remote-video {
+      /* Reset the object-fit for this. */
+    object-fit: contain;
+  }
+
+  .media-wrapper.receiving-screen-share > .local {
+    /* Screen shares have remote & local video side-by-side on narrow screens */
+    order: 3;
+    flex: 1 1 auto;
+    height: 20%;
+    /* Ensure no previously specified widths take effect, and we take up no more
+       than half the width. */
+    width: auto;
+    max-width: 50%;
+    /* This cancels out the absolute positioning when it's just remote video. */
+    position: relative;
+    bottom: auto;
+    right: auto;
+    margin: 0;
+  }
+
+  .media-wrapper.receiving-screen-share > .text-chat-view {
+    order: 4;
+  }
+}
+
+.standalone > .room-conversation-wrapper > .media-layout > .conversation-toolbar {
+  border: none;
+}
+
 /* Standalone rooms */
 
 .standalone .room-conversation-wrapper {
   position: relative;
   height: 100%;
   background: #000;
 }
 
@@ -1148,33 +1336,16 @@ html[dir="rtl"] .room-context-btn-edit {
 
 .standalone .room-conversation-wrapper .room-inner-info-area a.btn {
   padding: .5em 3em .3em 3em;
   border-radius: 3px;
   font-weight: normal;
   max-width: 400px;
 }
 
-.standalone-room-info {
-  position: absolute;
-  display: block;
-  top: 0;
-  right: 10px;
-  /* 20px is 10px for left and right margins. */
-  width: calc(25% - 20px);
-  z-index: 2000000;
-  font-size: 1.2em;
-  padding: .4em;
-  height: 100%;
-}
-
-.standalone-room-info > h2 {
-  color: #fff;
-}
-
 .standalone-context-url {
   color: #fff;
   /* Try and keep clear of local video */
   height: 40%;
 }
 
 .standalone-context-url.screen-share-active {
   /* Try and keep clear of remote video when screensharing */
@@ -1238,17 +1409,17 @@ html[dir="rtl"] .room-context-btn-edit {
 
 .fx-embedded .text-chat-entries {
   flex: 1 1 auto;
   max-height: 120px;
   min-height: 60px;
   padding: .7em .5em 0;
 }
 
-.fx-embedded .text-chat-box {
+.text-chat-box {
   flex: 0 0 auto;
   max-height: 40px;
   min-height: 40px;
   width: 100%;
 }
 
 .text-chat-entries {
   overflow: scroll;
@@ -1304,43 +1475,27 @@ html[dir="rtl"] .room-context-btn-edit {
 }
 
 .fx-embedded .text-chat-box > form > input {
   border: 0;
   border-top: 1px solid #999;
 }
 
 @media screen and (max-width:640px) {
-  .standalone-room-info {
-    /* This isn't perfect, we just center the heading for now. Bug 1141493
-       should fix this. */
-    position: absolute;
-    width: 100%;
-    right: 0px;
-
-    /* Override the 100% specified in the .standalone-room-info selector
-       block so that this div doesn't take over the _whole_ screen and
-       transparently occlude UI widgetry (like the Join button), making
-       it unusable. */
-    height: auto;
-  }
-
   .standalone-context-url {
     /* XXX We haven't got UX for standalone yet, so temporarily not displaying
        on narrow window widths. See bug 1153827. */
     display: none;
   }
 
   /* Rooms specific responsive styling */
   .standalone .room-conversation {
     background: #000;
   }
-  .room-conversation-wrapper header {
-    width: 100%;
-  }
+
   .standalone .room-conversation-wrapper .room-inner-info-area {
     right: 0;
     margin: auto;
     width: 100%;
     left: 0;
   }
   .standalone .room-conversation-wrapper .video-layout-wrapper {
     /* 50px: header's height; 25px: footer's height */
@@ -1352,17 +1507,17 @@ html[dir="rtl"] .room-context-btn-edit {
   .standalone .room-conversation .video_wrapper.remote_wrapper.not-joined {
     width: 100%;
   }
 
   .standalone .conversation-toolbar {
     height: 38px;
     padding: 8px;
   }
-  .standalone .focus-stream {
+  .standalone .conversation .focus-stream {
     /* Set at maximum height, minus height of conversation toolbar */
     height: 100%;
   }
 
   .standalone .media.nested {
     /* This forces the remote video stream to fit within wrapper's height */
     min-height: 0px;
   }
@@ -1413,16 +1568,14 @@ html[dir="rtl"] .room-context-btn-edit {
      convention in video conferencing systems. */
   transform: scale(-1, 1);
   transform-origin: 50% 50% 0;
 }
 
 .remote-video {
   width: 100%;
   height: 100%;
-  display: block;
-  position: absolute;
 }
 
 .screen-share-video {
   width: 100%;
   height: 100%;
 }
--- a/browser/components/loop/standalone/content/css/webapp.css
+++ b/browser/components/loop/standalone/content/css/webapp.css
@@ -124,17 +124,17 @@ body,
   background-image: url("../shared/img/mozilla-logo.png");
   background-repeat: no-repeat;
 }
 
 /* Rooms Footer */
 
 .rooms-footer {
   background: #000;
-  margin: 0 20px;
+  margin: 0 10px;
   text-align: left;
   height: 3em;
   position: relative;
 }
 
 html[dir="rtl"] .rooms-footer {
   text-align: right;
 }
@@ -354,39 +354,16 @@ p.standalone-btn-label {
   right: 35%;
 }
 
 .standalone .ended-conversation .local-stream {
   /* Hide  local media stream when feedback form is shown. */
   display: none;
 }
 
-/**
- * The .text-chat-* styles are very temporarily whilst we work on text chat
- * (bug 1108892 and dependencies).
- */
-.text-chat-view {
-  height: 60px;
-  color: black;
-}
-
-.text-chat-entries {
-  /* XXX Should use flex, this is just for the initial implementation. */
-  height: calc(100% - 2em);
-}
-
-.text-chat-box {
-  width: 30%;
-  margin: auto;
-}
-
-.text-chat-box > form > input {
-  width: 100%;
-}
-
 @media screen and (max-width:640px) {
   .standalone .ended-conversation .feedback {
     width: 92%;
     top: 10%;
     left: 5px;
     right: 5px;
   }
 }
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -350,89 +350,84 @@ loop.standaloneRoomViews = (function(moz
           console.warn("StandaloneRoomView.shouldRenderRemoteVideo:" +
             " unexpected roomState: ", this.state.roomState);
           return true;
 
       }
     },
 
     render: function() {
-      var localStreamClasses = React.addons.classSet({
-        local: true,
-        "local-stream": true,
-        "local-stream-audio": this.state.videoMuted
-      });
+      var displayScreenShare = this.state.receivingScreenShare ||
+        this.props.screenSharePosterUrl;
 
       var remoteStreamClasses = React.addons.classSet({
-        "video_inner": true,
         "remote": true,
-        "focus-stream": !this.state.receivingScreenShare,
-        "remote-inset-stream": this.state.receivingScreenShare
+        "focus-stream": !displayScreenShare
       });
 
       var screenShareStreamClasses = React.addons.classSet({
         "screen": true,
-        "focus-stream": this.state.receivingScreenShare,
-        hide: !this.state.receivingScreenShare
+        "focus-stream": displayScreenShare
       });
 
-      // XXX Temporarily showAlways = showRoomName = false for TextChatView
-      // until bug 1168829 is completed.
+      var mediaWrapperClasses = React.addons.classSet({
+        "media-wrapper": true,
+        "receiving-screen-share": displayScreenShare,
+        "showing-local-streams": this.state.localSrcVideoObject ||
+          this.props.localPosterUrl
+      });
+
       return (
         React.createElement("div", {className: "room-conversation-wrapper"}, 
           React.createElement("div", {className: "beta-logo"}), 
-          React.createElement(sharedViews.TextChatView, {
-            dispatcher: this.props.dispatcher, 
-            showAlways: false, 
-            showRoomName: false}), 
           React.createElement(StandaloneRoomHeader, {dispatcher: this.props.dispatcher}), 
           React.createElement(StandaloneRoomInfoArea, {roomState: this.state.roomState, 
                                   failureReason: this.state.failureReason, 
                                   joinRoom: this.joinRoom, 
                                   isFirefox: this.props.isFirefox, 
                                   activeRoomStore: this.props.activeRoomStore, 
                                   roomUsed: this.state.used}), 
-          React.createElement("div", {className: "video-layout-wrapper"}, 
-            React.createElement("div", {className: "conversation room-conversation"}, 
-              React.createElement("div", {className: "media nested"}, 
-                React.createElement("span", {className: "self-view-hidden-message"}, 
-                  mozL10n.get("self_view_hidden_message")
-                ), 
-                React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
-                  React.createElement("div", {className: remoteStreamClasses}, 
-                    React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), 
-                      posterUrl: this.props.remotePosterUrl, 
-                      mediaType: "remote", 
-                      srcVideoObject: this.state.remoteSrcVideoObject})
-                  ), 
-                  React.createElement("div", {className: screenShareStreamClasses}, 
-                    React.createElement(sharedViews.MediaView, {displayAvatar: false, 
-                      posterUrl: this.props.screenSharePosterUrl, 
-                      mediaType: "screen-share", 
-                      srcVideoObject: this.state.screenShareVideoObject})
-                  )
-                ), 
-                React.createElement("div", {className: localStreamClasses}, 
-                  React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted, 
-                    posterUrl: this.props.localPosterUrl, 
-                    mediaType: "local", 
-                    srcVideoObject: this.state.localSrcVideoObject})
-                )
+          React.createElement("div", {className: "media-layout"}, 
+            React.createElement("div", {className: mediaWrapperClasses}, 
+              React.createElement("span", {className: "self-view-hidden-message"}, 
+                mozL10n.get("self_view_hidden_message")
+              ), 
+              React.createElement("div", {className: remoteStreamClasses}, 
+                React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), 
+                  posterUrl: this.props.remotePosterUrl, 
+                  mediaType: "remote", 
+                  srcVideoObject: this.state.remoteSrcVideoObject})
+              ), 
+              React.createElement("div", {className: screenShareStreamClasses}, 
+                React.createElement(sharedViews.MediaView, {displayAvatar: false, 
+                  posterUrl: this.props.screenSharePosterUrl, 
+                  mediaType: "screen-share", 
+                  srcVideoObject: this.state.screenShareVideoObject})
               ), 
-              React.createElement(sharedViews.ConversationToolbar, {
+              React.createElement(sharedViews.TextChatView, {
                 dispatcher: this.props.dispatcher, 
-                video: {enabled: !this.state.videoMuted,
-                        visible: this._roomIsActive()}, 
-                audio: {enabled: !this.state.audioMuted,
-                        visible: this._roomIsActive()}, 
-                publishStream: this.publishStream, 
-                hangup: this.leaveRoom, 
-                hangupButtonLabel: mozL10n.get("rooms_leave_button_label"), 
-                enableHangup: this._roomIsActive()})
-            )
+                showAlways: true, 
+                showRoomName: true}), 
+              React.createElement("div", {className: "local"}, 
+                React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted, 
+                  posterUrl: this.props.localPosterUrl, 
+                  mediaType: "local", 
+                  srcVideoObject: this.state.localSrcVideoObject})
+              )
+            ), 
+            React.createElement(sharedViews.ConversationToolbar, {
+              dispatcher: this.props.dispatcher, 
+              video: {enabled: !this.state.videoMuted,
+                      visible: this._roomIsActive()}, 
+              audio: {enabled: !this.state.audioMuted,
+                      visible: this._roomIsActive()}, 
+              publishStream: this.publishStream, 
+              hangup: this.leaveRoom, 
+              hangupButtonLabel: mozL10n.get("rooms_leave_button_label"), 
+              enableHangup: this._roomIsActive()})
           ), 
           React.createElement(loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView, {
             marketplaceSrc: this.state.marketplaceSrc, 
             onMarketplaceMessage: this.state.onMarketplaceMessage}), 
           React.createElement(StandaloneRoomFooter, {dispatcher: this.props.dispatcher})
         )
       );
     }
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -350,89 +350,84 @@ loop.standaloneRoomViews = (function(moz
           console.warn("StandaloneRoomView.shouldRenderRemoteVideo:" +
             " unexpected roomState: ", this.state.roomState);
           return true;
 
       }
     },
 
     render: function() {
-      var localStreamClasses = React.addons.classSet({
-        local: true,
-        "local-stream": true,
-        "local-stream-audio": this.state.videoMuted
-      });
+      var displayScreenShare = this.state.receivingScreenShare ||
+        this.props.screenSharePosterUrl;
 
       var remoteStreamClasses = React.addons.classSet({
-        "video_inner": true,
         "remote": true,
-        "focus-stream": !this.state.receivingScreenShare,
-        "remote-inset-stream": this.state.receivingScreenShare
+        "focus-stream": !displayScreenShare
       });
 
       var screenShareStreamClasses = React.addons.classSet({
         "screen": true,
-        "focus-stream": this.state.receivingScreenShare,
-        hide: !this.state.receivingScreenShare
+        "focus-stream": displayScreenShare
       });
 
-      // XXX Temporarily showAlways = showRoomName = false for TextChatView
-      // until bug 1168829 is completed.
+      var mediaWrapperClasses = React.addons.classSet({
+        "media-wrapper": true,
+        "receiving-screen-share": displayScreenShare,
+        "showing-local-streams": this.state.localSrcVideoObject ||
+          this.props.localPosterUrl
+      });
+
       return (
         <div className="room-conversation-wrapper">
           <div className="beta-logo" />
-          <sharedViews.TextChatView
-            dispatcher={this.props.dispatcher}
-            showAlways={false}
-            showRoomName={false} />
           <StandaloneRoomHeader dispatcher={this.props.dispatcher} />
           <StandaloneRoomInfoArea roomState={this.state.roomState}
                                   failureReason={this.state.failureReason}
                                   joinRoom={this.joinRoom}
                                   isFirefox={this.props.isFirefox}
                                   activeRoomStore={this.props.activeRoomStore}
                                   roomUsed={this.state.used} />
-          <div className="video-layout-wrapper">
-            <div className="conversation room-conversation">
-              <div className="media nested">
-                <span className="self-view-hidden-message">
-                  {mozL10n.get("self_view_hidden_message")}
-                </span>
-                <div className="video_wrapper remote_wrapper">
-                  <div className={remoteStreamClasses}>
-                    <sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
-                      posterUrl={this.props.remotePosterUrl}
-                      mediaType="remote"
-                      srcVideoObject={this.state.remoteSrcVideoObject} />
-                  </div>
-                  <div className={screenShareStreamClasses}>
-                    <sharedViews.MediaView displayAvatar={false}
-                      posterUrl={this.props.screenSharePosterUrl}
-                      mediaType="screen-share"
-                      srcVideoObject={this.state.screenShareVideoObject} />
-                  </div>
-                </div>
-                <div className={localStreamClasses}>
-                  <sharedViews.MediaView displayAvatar={this.state.videoMuted}
-                    posterUrl={this.props.localPosterUrl}
-                    mediaType="local"
-                    srcVideoObject={this.state.localSrcVideoObject} />
-                </div>
+          <div className="media-layout">
+            <div className={mediaWrapperClasses}>
+              <span className="self-view-hidden-message">
+                {mozL10n.get("self_view_hidden_message")}
+              </span>
+              <div className={remoteStreamClasses}>
+                <sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
+                  posterUrl={this.props.remotePosterUrl}
+                  mediaType="remote"
+                  srcVideoObject={this.state.remoteSrcVideoObject} />
+              </div>
+              <div className={screenShareStreamClasses}>
+                <sharedViews.MediaView displayAvatar={false}
+                  posterUrl={this.props.screenSharePosterUrl}
+                  mediaType="screen-share"
+                  srcVideoObject={this.state.screenShareVideoObject} />
               </div>
-              <sharedViews.ConversationToolbar
+              <sharedViews.TextChatView
                 dispatcher={this.props.dispatcher}
-                video={{enabled: !this.state.videoMuted,
-                        visible: this._roomIsActive()}}
-                audio={{enabled: !this.state.audioMuted,
-                        visible: this._roomIsActive()}}
-                publishStream={this.publishStream}
-                hangup={this.leaveRoom}
-                hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
-                enableHangup={this._roomIsActive()} />
+                showAlways={true}
+                showRoomName={true} />
+              <div className="local">
+                <sharedViews.MediaView displayAvatar={this.state.videoMuted}
+                  posterUrl={this.props.localPosterUrl}
+                  mediaType="local"
+                  srcVideoObject={this.state.localSrcVideoObject} />
+              </div>
             </div>
+            <sharedViews.ConversationToolbar
+              dispatcher={this.props.dispatcher}
+              video={{enabled: !this.state.videoMuted,
+                      visible: this._roomIsActive()}}
+              audio={{enabled: !this.state.audioMuted,
+                      visible: this._roomIsActive()}}
+              publishStream={this.publishStream}
+              hangup={this.leaveRoom}
+              hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
+              enableHangup={this._roomIsActive()} />
           </div>
           <loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView
             marketplaceSrc={this.state.marketplaceSrc}
             onMarketplaceMessage={this.state.onMarketplaceMessage} />
           <StandaloneRoomFooter dispatcher={this.props.dispatcher} />
         </div>
       );
     }
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -420,24 +420,24 @@ describe("loop.standaloneRoomViews", fun
           function() {
             activeRoomStore.setStoreState({used: false});
             expect(view.getDOMNode().querySelector(".faces")).eql(null);
           });
 
       });
 
       describe("Mute", function() {
-        it("should render local media as audio-only if video is muted",
+        it("should render a local avatar if video is muted",
           function() {
             activeRoomStore.setStoreState({
               roomState: ROOM_STATES.SESSION_CONNECTED,
               videoMuted: true
             });
 
-            expect(view.getDOMNode().querySelector(".local-stream-audio"))
+            expect(view.getDOMNode().querySelector(".local .avatar"))
               .not.eql(null);
           });
 
         it("should render a local avatar if the room HAS_PARTICIPANTS and" +
           " .videoMuted is true",
           function() {
             activeRoomStore.setStoreState({
               roomState: ROOM_STATES.HAS_PARTICIPANTS,
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -967,16 +967,31 @@
                   React.createElement(StandaloneRoomView, {
                     dispatcher: dispatcher, 
                     activeRoomStore: updatingActiveRoomStore, 
                     roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
                     remotePosterUrl: "sample-img/video-screen-remote.png"})
                 )
+            ), 
+
+            React.createElement(FramedExample, {width: 600, height: 480, 
+                           onContentsRendered: updatingSharingRoomStore.forcedUpdate, 
+              summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)"}, 
+                React.createElement("div", {className: "standalone", cssClass: "standalone"}, 
+                  React.createElement(StandaloneRoomView, {
+                    dispatcher: dispatcher, 
+                    activeRoomStore: updatingSharingRoomStore, 
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
+                    isFirefox: true, 
+                    localPosterUrl: "sample-img/video-screen-local.png", 
+                    remotePosterUrl: "sample-img/video-screen-remote.png", 
+                    screenSharePosterUrl: "sample-img/video-screen-terminal.png"})
+                )
             )
           ), 
 
           React.createElement(Section, {name: "TextChatView (standalone)"}, 
             React.createElement(FramedExample, {width: 200, height: 400, cssClass: "standalone", 
                           summary: "Standalone Text Chat conversation (200 x 400)"}, 
               React.createElement("div", {className: "standalone text-chat-example"}, 
                 React.createElement(TextChatView, {
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -968,16 +968,31 @@
                     dispatcher={dispatcher}
                     activeRoomStore={updatingActiveRoomStore}
                     roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
                     remotePosterUrl="sample-img/video-screen-remote.png" />
                 </div>
             </FramedExample>
+
+            <FramedExample width={600} height={480}
+                           onContentsRendered={updatingSharingRoomStore.forcedUpdate}
+              summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)">
+                <div className="standalone" cssClass="standalone">
+                  <StandaloneRoomView
+                    dispatcher={dispatcher}
+                    activeRoomStore={updatingSharingRoomStore}
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
+                    isFirefox={true}
+                    localPosterUrl="sample-img/video-screen-local.png"
+                    remotePosterUrl="sample-img/video-screen-remote.png"
+                    screenSharePosterUrl="sample-img/video-screen-terminal.png" />
+                </div>
+            </FramedExample>
           </Section>
 
           <Section name="TextChatView (standalone)">
             <FramedExample width={200} height={400} cssClass="standalone"
                           summary="Standalone Text Chat conversation (200 x 400)">
               <div className="standalone text-chat-example">
                 <TextChatView
                   dispatcher={dispatcher}