From 6bb8d833b9f99e6b4794a8a14445312311c42b5a Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Fri, 3 Jul 2015 19:00:25 +0200 Subject: [PATCH] Bug 1171940: remove the mode-switching and have only one-mode Edit Context view inside the Hello conversation window. r=Standard8 --- .../components/loop/content/js/roomViews.js | 279 ++++++++--------- .../components/loop/content/js/roomViews.jsx | 281 ++++++++---------- .../loop/content/shared/css/conversation.css | 55 +--- 3 files changed, 243 insertions(+), 372 deletions(-) diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index d71978cdfec8..088f90277672 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -153,18 +153,19 @@ loop.roomViews = (function(mozL10n) { dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, error: React.PropTypes.object, mozLoop: React.PropTypes.object.isRequired, + onAddContextClick: React.PropTypes.func, + onEditContextClose: React.PropTypes.func, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, savingContext: React.PropTypes.bool, show: React.PropTypes.bool.isRequired, - showContext: React.PropTypes.bool.isRequired, + showEditContext: React.PropTypes.bool.isRequired, socialShareProviders: React.PropTypes.array }, getInitialState: function() { return { copiedUrl: false, - editMode: false, newRoomName: "" }; }, @@ -207,11 +208,15 @@ loop.roomViews = (function(mozL10n) { handleAddContextClick: function(event) { event.preventDefault(); - this.handleEditModeChange(true); + if (this.props.onAddContextClick) { + this.props.onAddContextClick(); + } }, - handleEditModeChange: function(newEditMode) { - this.setState({ editMode: newEditMode }); + handleEditContextClose: function() { + if (this.props.onEditContextClose) { + this.props.onEditContextClose(); + } }, render: function() { @@ -220,13 +225,16 @@ loop.roomViews = (function(mozL10n) { } var canAddContext = this.props.mozLoop.getLoopPref("contextInConversations.enabled") && - !this.props.showContext && !this.state.editMode; + // Don't show the link when we're showing the edit form already: + !this.props.showEditContext && + // Don't show the link when there's already context data available: + !(this.props.roomData.roomContextUrls || this.props.roomData.roomDescription); var cx = React.addons.classSet; return ( React.createElement("div", {className: "room-invitation-overlay"}, React.createElement("div", {className: "room-invitation-content"}, - React.createElement("p", {className: cx({hide: this.state.editMode})}, + React.createElement("p", {className: cx({hide: this.props.showEditContext})}, mozL10n.get("invite_header_text") ), React.createElement("a", {className: cx({hide: !canAddContext, "room-invitation-addcontext": true}), @@ -237,7 +245,7 @@ loop.roomViews = (function(mozL10n) { React.createElement("div", {className: cx({ "btn-group": true, "call-action-group": true, - hide: this.state.editMode + hide: this.props.showEditContext })}, React.createElement("button", {className: "btn btn-info btn-email", onClick: this.handleEmailButtonClick}, @@ -260,62 +268,47 @@ loop.roomViews = (function(mozL10n) { roomUrl: this.props.roomData.roomUrl, show: this.state.showMenu, socialShareProviders: this.props.socialShareProviders}), - React.createElement(DesktopRoomContextView, { + React.createElement(DesktopRoomEditContextView, { dispatcher: this.props.dispatcher, - editMode: this.state.editMode, error: this.props.error, mozLoop: this.props.mozLoop, - onEditModeChange: this.handleEditModeChange, + onClose: this.handleEditContextClose, roomData: this.props.roomData, savingContext: this.props.savingContext, - show: this.props.showContext || this.state.editMode}) + show: this.props.showEditContext}) ) ); } }); - var DesktopRoomContextView = React.createClass({displayName: "DesktopRoomContextView", + var DesktopRoomEditContextView = React.createClass({displayName: "DesktopRoomEditContextView", mixins: [React.addons.LinkedStateMixin], propTypes: { // Only used for tests. availableContext: React.PropTypes.object, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, - editMode: React.PropTypes.bool, error: React.PropTypes.object, mozLoop: React.PropTypes.object.isRequired, - onEditModeChange: React.PropTypes.func, + onClose: React.PropTypes.func, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, savingContext: React.PropTypes.bool.isRequired, show: React.PropTypes.bool.isRequired }, + componentWillMount: function() { + this._fetchMetadata(); + }, + componentWillReceiveProps: function(nextProps) { var newState = {}; // When the 'show' prop is changed from outside this component, we do need // to update the state. if (("show" in nextProps) && nextProps.show !== this.props.show) { newState.show = nextProps.show; - } - if (("editMode" in nextProps && nextProps.editMode !== this.props.editMode)) { - newState.editMode = nextProps.editMode; - // If we're switching to edit mode, fetch the metadata of the current tab. - // But _only_ if there's no context currently attached to the room; the - // checkbox will be disabled in that case. - if (nextProps.editMode) { - this.props.mozLoop.getSelectedTabMetadata(function(metadata) { - var previewImage = metadata.favicon || ""; - var description = metadata.title || metadata.description; - var metaUrl = metadata.url; - this.setState({ - availableContext: { - previewImage: previewImage, - description: description, - url: metaUrl - } - }); - }.bind(this)); + if (nextProps.show) { + this._fetchMetadata(); } } // When we receive an update for the `roomData` property, make sure that @@ -341,28 +334,16 @@ loop.roomViews = (function(mozL10n) { } } - // Make sure we do not show the edit-mode when we just successfully saved - // context. - if (this.props.savingContext && nextProps.savingContext !== this.props.savingContext && - !nextProps.error && this.state.editMode) { - newState.editMode = false; - } - if (Object.getOwnPropertyNames(newState).length) { this.setState(newState); } }, - getDefaultProps: function() { - return { editMode: false }; - }, - getInitialState: function() { var url = this._getURL(); return { // `availableContext` prop only used in tests. availableContext: this.props.availableContext, - editMode: this.props.editMode, show: this.props.show, newRoomName: this.props.roomData.roomName || "", newRoomURL: url && url.location || "", @@ -371,17 +352,29 @@ loop.roomViews = (function(mozL10n) { }; }, + _fetchMetadata: function() { + this.props.mozLoop.getSelectedTabMetadata(function(metadata) { + var previewImage = metadata.favicon || ""; + var description = metadata.title || metadata.description; + var metaUrl = metadata.url; + this.setState({ + availableContext: { + previewImage: previewImage, + description: description, + url: metaUrl + } + }); + }.bind(this)); + }, + handleCloseClick: function(event) { + event.stopPropagation(); event.preventDefault(); - if (this.state.editMode) { - this.setState({ editMode: false }); - if (this.props.onEditModeChange) { - this.props.onEditModeChange(false); - } - return; - } this.setState({ show: false }); + if (this.props.onClose) { + this.props.onClose(); + } }, handleContextClick: function(event) { @@ -396,15 +389,6 @@ loop.roomViews = (function(mozL10n) { this.props.mozLoop.openURL(url.location); }, - handleEditClick: function(event) { - event.preventDefault(); - - this.setState({ editMode: true }); - if (this.props.onEditModeChange) { - this.props.onEditModeChange(true); - } - }, - handleCheckboxChange: function(state) { if (state.checked) { // The checkbox was checked, prefill the fields with the values available @@ -478,7 +462,7 @@ loop.roomViews = (function(mozL10n) { }, render: function() { - if (!this.state.show && !this.state.editMode) { + if (!this.state.show) { return null; } @@ -486,93 +470,55 @@ loop.roomViews = (function(mozL10n) { var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe"; var urlDescription = url && url.description || ""; var location = url && url.location || ""; - var locationData = null; - if (location) { - locationData = checkboxLabel = sharedUtils.formatURL(location); - } - if (!checkboxLabel) { - try { - checkboxLabel = sharedUtils.formatURL((this.state.availableContext ? - this.state.availableContext.url : "")); - } catch (ex) {} - } var cx = React.addons.classSet; - if (this.state.editMode) { - var availableContext = this.state.availableContext; - // The checkbox shows as checked when there's already context data - // attached to this room. - var checked = !!urlDescription; - var checkboxLabel = urlDescription || (availableContext && availableContext.url ? - availableContext.description : ""); - - return ( - React.createElement("div", {className: "room-context editMode"}, - React.createElement("p", {className: cx({"error": !!this.props.error, - "error-display-area": true})}, - mozL10n.get("rooms_change_failed_label") - ), - React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), - React.createElement(sharedViews.Checkbox, { - additionalClass: cx({ hide: !checkboxLabel }), - checked: checked, - disabled: checked, - label: checkboxLabel, - onChange: this.handleCheckboxChange, - value: location}), - React.createElement("form", {onSubmit: this.handleFormSubmit}, - React.createElement("input", {className: "room-context-name", - onKeyDown: this.handleTextareaKeyDown, - placeholder: mozL10n.get("context_edit_name_placeholder"), - type: "text", - valueLink: this.linkState("newRoomName")}), - React.createElement("input", {className: "room-context-url", - disabled: availableContext && availableContext.url === this.state.newRoomURL, - onKeyDown: this.handleTextareaKeyDown, - placeholder: "https://", - type: "text", - valueLink: this.linkState("newRoomURL")}), - React.createElement("textarea", {className: "room-context-comments", - onKeyDown: this.handleTextareaKeyDown, - placeholder: mozL10n.get("context_edit_comments_placeholder"), - rows: "3", type: "text", - valueLink: this.linkState("newRoomDescription")}) - ), - React.createElement("button", {className: "btn btn-info", - disabled: this.props.savingContext, - onClick: this.handleFormSubmit}, - mozL10n.get("context_save_label2") - ), - React.createElement("button", {className: "room-context-btn-close", - onClick: this.handleCloseClick, - title: mozL10n.get("cancel_button")}) - ) - ); - } - - if (!locationData) { - return null; - } + var availableContext = this.state.availableContext; + // The checkbox shows as checked when there's already context data + // attached to this room. + var checked = !!urlDescription; + var checkboxLabel = urlDescription || (availableContext && availableContext.url ? + availableContext.description : ""); return ( React.createElement("div", {className: "room-context"}, + React.createElement("p", {className: cx({"error": !!this.props.error, + "error-display-area": true})}, + mozL10n.get("rooms_change_failed_label") + ), React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), - React.createElement("div", {className: "room-context-content", - onClick: this.handleContextClick}, - React.createElement("img", {className: "room-context-thumbnail", src: thumbnail}), - React.createElement("div", {className: "room-context-description", - title: urlDescription}, - this._truncate(urlDescription), - React.createElement("a", {className: "room-context-url", - title: locationData.location}, locationData.hostname) - ) + React.createElement(sharedViews.Checkbox, { + additionalClass: cx({ hide: !checkboxLabel }), + checked: checked, + disabled: checked, + label: checkboxLabel, + onChange: this.handleCheckboxChange, + value: location}), + React.createElement("form", {onSubmit: this.handleFormSubmit}, + React.createElement("input", {className: "room-context-name", + onKeyDown: this.handleTextareaKeyDown, + placeholder: mozL10n.get("context_edit_name_placeholder"), + type: "text", + valueLink: this.linkState("newRoomName")}), + React.createElement("input", {className: "room-context-url", + disabled: availableContext && availableContext.url === this.state.newRoomURL, + onKeyDown: this.handleTextareaKeyDown, + placeholder: "https://", + type: "text", + valueLink: this.linkState("newRoomURL")}), + React.createElement("textarea", {className: "room-context-comments", + onKeyDown: this.handleTextareaKeyDown, + placeholder: mozL10n.get("context_edit_comments_placeholder"), + rows: "3", type: "text", + valueLink: this.linkState("newRoomDescription")}) + ), + React.createElement("button", {className: "btn btn-info", + disabled: this.props.savingContext, + onClick: this.handleFormSubmit}, + mozL10n.get("context_save_label2") ), React.createElement("button", {className: "room-context-btn-close", onClick: this.handleCloseClick, - title: mozL10n.get("context_hide_tooltip")}), - React.createElement("button", {className: "room-context-btn-edit", - onClick: this.handleEditClick, - title: mozL10n.get("context_edit_tooltip")}) + title: mozL10n.get("cancel_button")}) ) ); } @@ -599,6 +545,13 @@ loop.roomViews = (function(mozL10n) { roomStore: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired }, + getInitialState: function() { + return { + contextEnabled: this.props.mozLoop.getLoopPref("contextInConversations.enabled"), + showEditContext: false + }; + }, + componentWillUpdate: function(nextProps, nextState) { // The SDK needs to know about the configuration and the elements to use // for display. So the best way seems to pass the information here - ideally @@ -642,13 +595,6 @@ loop.roomViews = (function(mozL10n) { return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS); }, - _shouldRenderContextView: function() { - return !!( - this.props.mozLoop.getLoopPref("contextInConversations.enabled") && - (this.state.roomContextUrls || this.state.roomDescription) - ); - }, - /** * Works out if remote video should be rended or not, depending on the * room state and other flags. @@ -718,6 +664,14 @@ loop.roomViews = (function(mozL10n) { !this.state.mediaConnected; }, + handleAddContextClick: function() { + this.setState({ showEditContext: true }); + }, + + handleEditContextClose: function() { + this.setState({ showEditContext: false }); + }, + render: function() { if (this.state.roomName) { this.setTitle(this.state.roomName); @@ -736,7 +690,7 @@ loop.roomViews = (function(mozL10n) { }; var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay(); - var shouldRenderContextView = this._shouldRenderContextView(); + var shouldRenderEditContextView = this.state.contextEnabled && this.state.showEditContext; var roomData = this.props.roomStore.getStoreState("activeRoom"); switch(this.state.roomState) { @@ -760,18 +714,20 @@ loop.roomViews = (function(mozL10n) { return ( React.createElement("div", {className: "room-conversation-wrapper"}, - React.createElement(DesktopRoomInvitationView, { - dispatcher: this.props.dispatcher, - error: this.state.error, - mozLoop: this.props.mozLoop, - roomData: roomData, - savingContext: this.state.savingContext, - show: shouldRenderInvitationOverlay, - showContext: shouldRenderContextView, - socialShareProviders: this.state.socialShareProviders}), React.createElement("div", {className: "video-layout-wrapper"}, React.createElement("div", {className: "conversation room-conversation"}, React.createElement("div", {className: "media nested"}, + React.createElement(DesktopRoomInvitationView, { + dispatcher: this.props.dispatcher, + error: this.state.error, + mozLoop: this.props.mozLoop, + onAddContextClick: this.handleAddContextClick, + onEditContextClose: this.handleEditContextClose, + roomData: roomData, + savingContext: this.state.savingContext, + show: shouldRenderInvitationOverlay, + showEditContext: shouldRenderInvitationOverlay && shouldRenderEditContextView, + socialShareProviders: this.state.socialShareProviders}), React.createElement("div", {className: "video_wrapper remote_wrapper"}, React.createElement("div", {className: "video_inner remote focus-stream"}, React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), @@ -798,13 +754,14 @@ loop.roomViews = (function(mozL10n) { video: {enabled: !this.state.videoMuted, visible: true}}) ) ), - React.createElement(DesktopRoomContextView, { + React.createElement(DesktopRoomEditContextView, { dispatcher: this.props.dispatcher, error: this.state.error, mozLoop: this.props.mozLoop, + onClose: this.handleEditContextClose, roomData: roomData, savingContext: this.state.savingContext, - show: !shouldRenderInvitationOverlay && shouldRenderContextView}), + show: !shouldRenderInvitationOverlay && shouldRenderEditContextView}), React.createElement(sharedViews.chat.TextChatView, { dispatcher: this.props.dispatcher, showAlways: false, @@ -820,7 +777,7 @@ loop.roomViews = (function(mozL10n) { return { ActiveRoomStoreMixin: ActiveRoomStoreMixin, SocialShareDropdown: SocialShareDropdown, - DesktopRoomContextView: DesktopRoomContextView, + DesktopRoomEditContextView: DesktopRoomEditContextView, DesktopRoomConversationView: DesktopRoomConversationView, DesktopRoomInvitationView: DesktopRoomInvitationView }; diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index b616231321c8..f7c4afd9ebb8 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -153,18 +153,19 @@ loop.roomViews = (function(mozL10n) { dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, error: React.PropTypes.object, mozLoop: React.PropTypes.object.isRequired, + onAddContextClick: React.PropTypes.func, + onEditContextClose: React.PropTypes.func, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, savingContext: React.PropTypes.bool, show: React.PropTypes.bool.isRequired, - showContext: React.PropTypes.bool.isRequired, + showEditContext: React.PropTypes.bool.isRequired, socialShareProviders: React.PropTypes.array }, getInitialState: function() { return { copiedUrl: false, - editMode: false, newRoomName: "" }; }, @@ -207,11 +208,15 @@ loop.roomViews = (function(mozL10n) { handleAddContextClick: function(event) { event.preventDefault(); - this.handleEditModeChange(true); + if (this.props.onAddContextClick) { + this.props.onAddContextClick(); + } }, - handleEditModeChange: function(newEditMode) { - this.setState({ editMode: newEditMode }); + handleEditContextClose: function() { + if (this.props.onEditContextClose) { + this.props.onEditContextClose(); + } }, render: function() { @@ -220,13 +225,16 @@ loop.roomViews = (function(mozL10n) { } var canAddContext = this.props.mozLoop.getLoopPref("contextInConversations.enabled") && - !this.props.showContext && !this.state.editMode; + // Don't show the link when we're showing the edit form already: + !this.props.showEditContext && + // Don't show the link when there's already context data available: + !(this.props.roomData.roomContextUrls || this.props.roomData.roomDescription); var cx = React.addons.classSet; return (
-

+

{mozL10n.get("invite_header_text")}

); } }); - var DesktopRoomContextView = React.createClass({ + var DesktopRoomEditContextView = React.createClass({ mixins: [React.addons.LinkedStateMixin], propTypes: { // Only used for tests. availableContext: React.PropTypes.object, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, - editMode: React.PropTypes.bool, error: React.PropTypes.object, mozLoop: React.PropTypes.object.isRequired, - onEditModeChange: React.PropTypes.func, + onClose: React.PropTypes.func, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, savingContext: React.PropTypes.bool.isRequired, show: React.PropTypes.bool.isRequired }, + componentWillMount: function() { + this._fetchMetadata(); + }, + componentWillReceiveProps: function(nextProps) { var newState = {}; // When the 'show' prop is changed from outside this component, we do need // to update the state. if (("show" in nextProps) && nextProps.show !== this.props.show) { newState.show = nextProps.show; - } - if (("editMode" in nextProps && nextProps.editMode !== this.props.editMode)) { - newState.editMode = nextProps.editMode; - // If we're switching to edit mode, fetch the metadata of the current tab. - // But _only_ if there's no context currently attached to the room; the - // checkbox will be disabled in that case. - if (nextProps.editMode) { - this.props.mozLoop.getSelectedTabMetadata(function(metadata) { - var previewImage = metadata.favicon || ""; - var description = metadata.title || metadata.description; - var metaUrl = metadata.url; - this.setState({ - availableContext: { - previewImage: previewImage, - description: description, - url: metaUrl - } - }); - }.bind(this)); + if (nextProps.show) { + this._fetchMetadata(); } } // When we receive an update for the `roomData` property, make sure that @@ -341,28 +334,16 @@ loop.roomViews = (function(mozL10n) { } } - // Make sure we do not show the edit-mode when we just successfully saved - // context. - if (this.props.savingContext && nextProps.savingContext !== this.props.savingContext && - !nextProps.error && this.state.editMode) { - newState.editMode = false; - } - if (Object.getOwnPropertyNames(newState).length) { this.setState(newState); } }, - getDefaultProps: function() { - return { editMode: false }; - }, - getInitialState: function() { var url = this._getURL(); return { // `availableContext` prop only used in tests. availableContext: this.props.availableContext, - editMode: this.props.editMode, show: this.props.show, newRoomName: this.props.roomData.roomName || "", newRoomURL: url && url.location || "", @@ -371,17 +352,29 @@ loop.roomViews = (function(mozL10n) { }; }, + _fetchMetadata: function() { + this.props.mozLoop.getSelectedTabMetadata(function(metadata) { + var previewImage = metadata.favicon || ""; + var description = metadata.title || metadata.description; + var metaUrl = metadata.url; + this.setState({ + availableContext: { + previewImage: previewImage, + description: description, + url: metaUrl + } + }); + }.bind(this)); + }, + handleCloseClick: function(event) { + event.stopPropagation(); event.preventDefault(); - if (this.state.editMode) { - this.setState({ editMode: false }); - if (this.props.onEditModeChange) { - this.props.onEditModeChange(false); - } - return; - } this.setState({ show: false }); + if (this.props.onClose) { + this.props.onClose(); + } }, handleContextClick: function(event) { @@ -396,15 +389,6 @@ loop.roomViews = (function(mozL10n) { this.props.mozLoop.openURL(url.location); }, - handleEditClick: function(event) { - event.preventDefault(); - - this.setState({ editMode: true }); - if (this.props.onEditModeChange) { - this.props.onEditModeChange(true); - } - }, - handleCheckboxChange: function(state) { if (state.checked) { // The checkbox was checked, prefill the fields with the values available @@ -478,7 +462,7 @@ loop.roomViews = (function(mozL10n) { }, render: function() { - if (!this.state.show && !this.state.editMode) { + if (!this.state.show) { return null; } @@ -486,93 +470,55 @@ loop.roomViews = (function(mozL10n) { var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe"; var urlDescription = url && url.description || ""; var location = url && url.location || ""; - var locationData = null; - if (location) { - locationData = checkboxLabel = sharedUtils.formatURL(location); - } - if (!checkboxLabel) { - try { - checkboxLabel = sharedUtils.formatURL((this.state.availableContext ? - this.state.availableContext.url : "")); - } catch (ex) {} - } var cx = React.addons.classSet; - if (this.state.editMode) { - var availableContext = this.state.availableContext; - // The checkbox shows as checked when there's already context data - // attached to this room. - var checked = !!urlDescription; - var checkboxLabel = urlDescription || (availableContext && availableContext.url ? - availableContext.description : ""); - - return ( -
-

- {mozL10n.get("rooms_change_failed_label")} -

-
{mozL10n.get("context_inroom_label")}
- -
- - -