Bug 1171940: remove the mode-switching and have only one-mode Edit Context view inside the Hello conversation window. r=Standard8

This commit is contained in:
Mike de Boer 2015-07-03 19:00:25 +02:00
parent 06878cbdf4
commit 6bb8d833b9
3 changed files with 243 additions and 372 deletions

View File

@ -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
};

View File

@ -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 (
<div className="room-invitation-overlay">
<div className="room-invitation-content">
<p className={cx({hide: this.state.editMode})}>
<p className={cx({hide: this.props.showEditContext})}>
{mozL10n.get("invite_header_text")}
</p>
<a className={cx({hide: !canAddContext, "room-invitation-addcontext": true})}
@ -237,7 +245,7 @@ loop.roomViews = (function(mozL10n) {
<div className={cx({
"btn-group": true,
"call-action-group": true,
hide: this.state.editMode
hide: this.props.showEditContext
})}>
<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} />
<DesktopRoomContextView
<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} />
</div>
);
}
});
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 (
<div className="room-context editMode">
<p className={cx({"error": !!this.props.error,
"error-display-area": true})}>
{mozL10n.get("rooms_change_failed_label")}
</p>
<div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
<sharedViews.Checkbox
additionalClass={cx({ hide: !checkboxLabel })}
checked={checked}
disabled={checked}
label={checkboxLabel}
onChange={this.handleCheckboxChange}
value={location} />
<form onSubmit={this.handleFormSubmit}>
<input className="room-context-name"
onKeyDown={this.handleTextareaKeyDown}
placeholder={mozL10n.get("context_edit_name_placeholder")}
type="text"
valueLink={this.linkState("newRoomName")} />
<input className="room-context-url"
disabled={availableContext && availableContext.url === this.state.newRoomURL}
onKeyDown={this.handleTextareaKeyDown}
placeholder="https://"
type="text"
valueLink={this.linkState("newRoomURL")} />
<textarea className="room-context-comments"
onKeyDown={this.handleTextareaKeyDown}
placeholder={mozL10n.get("context_edit_comments_placeholder")}
rows="3" type="text"
valueLink={this.linkState("newRoomDescription")} />
</form>
<button className="btn btn-info"
disabled={this.props.savingContext}
onClick={this.handleFormSubmit}>
{mozL10n.get("context_save_label2")}
</button>
<button className="room-context-btn-close"
onClick={this.handleCloseClick}
title={mozL10n.get("cancel_button")}/>
</div>
);
}
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 (
<div className="room-context">
<p className={cx({"error": !!this.props.error,
"error-display-area": true})}>
{mozL10n.get("rooms_change_failed_label")}
</p>
<div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
<div className="room-context-content"
onClick={this.handleContextClick}>
<img className="room-context-thumbnail" src={thumbnail} />
<div className="room-context-description"
title={urlDescription}>
{this._truncate(urlDescription)}
<a className="room-context-url"
title={locationData.location}>{locationData.hostname}</a>
</div>
</div>
<sharedViews.Checkbox
additionalClass={cx({ hide: !checkboxLabel })}
checked={checked}
disabled={checked}
label={checkboxLabel}
onChange={this.handleCheckboxChange}
value={location} />
<form onSubmit={this.handleFormSubmit}>
<input className="room-context-name"
onKeyDown={this.handleTextareaKeyDown}
placeholder={mozL10n.get("context_edit_name_placeholder")}
type="text"
valueLink={this.linkState("newRoomName")} />
<input className="room-context-url"
disabled={availableContext && availableContext.url === this.state.newRoomURL}
onKeyDown={this.handleTextareaKeyDown}
placeholder="https://"
type="text"
valueLink={this.linkState("newRoomURL")} />
<textarea className="room-context-comments"
onKeyDown={this.handleTextareaKeyDown}
placeholder={mozL10n.get("context_edit_comments_placeholder")}
rows="3" type="text"
valueLink={this.linkState("newRoomDescription")} />
</form>
<button className="btn btn-info"
disabled={this.props.savingContext}
onClick={this.handleFormSubmit}>
{mozL10n.get("context_save_label2")}
</button>
<button className="room-context-btn-close"
onClick={this.handleCloseClick}
title={mozL10n.get("context_hide_tooltip")}/>
<button className="room-context-btn-edit"
onClick={this.handleEditClick}
title={mozL10n.get("context_edit_tooltip")}/>
title={mozL10n.get("cancel_button")}/>
</div>
);
}
@ -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 (
<div className="room-conversation-wrapper">
<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} />
<div className="video-layout-wrapper">
<div className="conversation room-conversation">
<div className="media nested">
<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} />
<div className="video_wrapper remote_wrapper">
<div className="video_inner remote focus-stream">
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
@ -798,13 +754,14 @@ loop.roomViews = (function(mozL10n) {
video={{enabled: !this.state.videoMuted, visible: true}} />
</div>
</div>
<DesktopRoomContextView
<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} />
<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
};

View File

@ -930,7 +930,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
}
.room-context {
background: rgba(0,0,0,.6);
background: rgba(0,0,0,.8);
border-top: 2px solid #444;
border-bottom: 2px solid #444;
padding: .5rem;
@ -939,6 +939,9 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
left: 0;
bottom: 0;
width: 100%;
/* Stretch to the maximum available space whilst not covering the conversation
toolbar (26px). */
height: calc(100% - 26px);
font-size: .9em;
display: flex;
flex-flow: column nowrap;
@ -950,42 +953,14 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
z-index: 2;
}
.room-context.editMode {
/* Stretch to the maximum available space whilst not covering the conversation
toolbar (26px). */
height: calc(100% - 26px);
}
.room-invitation-overlay .room-context {
position: relative;
left: auto;
bottom: auto;
flex: 0 1 auto;
}
.room-invitation-overlay .room-context.editMode {
height: 100%;
}
.room-context-content {
flex: 1 1 auto;
text-align: start;
display: flex;
flex-flow: row nowrap;
font-size: .9em;
}
.room-context-thumbnail {
/* 16px icon size + 3px border width. */
width: 19px;
max-height: 19px;
border: 3px solid #fff;
border-radius: 3px;
background-color: #fff;
-moz-margin-end: 1ch;
flex: 0 1 auto;
}
.room-context > .error-display-area.error {
display: block;
background-color: rgba(215,67,69,.8);
@ -1011,7 +986,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
margin-bottom: 1em;
}
.room-context-description,
.room-context > .checkbox-wrapper > label {
color: #fff;
}
@ -1020,7 +994,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
color: #707070;
}
.room-context-description,
.room-context-comment {
word-wrap: break-word;
}
@ -1079,8 +1052,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
align-self: flex-end;
}
.room-context-btn-close,
.room-context-btn-edit {
.room-context-btn-close {
position: absolute;
right: 8px;
/* 8px offset + 2px border-top */
@ -1096,31 +1068,16 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
cursor: pointer;
}
.room-context-btn-edit {
right: 20px;
background-image: url("../img/icons-10x10.svg#edit-darkergrey");
}
.room-context-btn-edit:hover,
.room-context-btn-edit:hover:active {
background-image: url("../img/icons-10x10.svg#edit-active");
}
.room-context-btn-close:hover,
.room-context-btn-close:hover:active {
background-image: url("../img/icons-10x10.svg#close-active");
}
html[dir="rtl"] .room-context-btn-close,
html[dir="rtl"] .room-context-btn-edit {
html[dir="rtl"] .room-context-btn-close {
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);