зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1142515: implement updating a room with changed context information. r=Standard8
This commit is contained in:
Родитель
7acc550f67
Коммит
18d196064c
|
@ -436,7 +436,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
|
||||
propTypes: {
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
roomUrls: React.PropTypes.object
|
||||
roomUrls: React.PropTypes.array
|
||||
},
|
||||
|
||||
handleClick: function(event) {
|
||||
|
|
|
@ -436,7 +436,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
|
||||
propTypes: {
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
roomUrls: React.PropTypes.object
|
||||
roomUrls: React.PropTypes.array
|
||||
},
|
||||
|
||||
handleClick: function(event) {
|
||||
|
|
|
@ -91,9 +91,9 @@ loop.store = loop.store || {};
|
|||
"getAllRooms",
|
||||
"getAllRoomsError",
|
||||
"openRoom",
|
||||
"renameRoom",
|
||||
"renameRoomError",
|
||||
"shareRoomUrl",
|
||||
"updateRoomContext",
|
||||
"updateRoomContextError",
|
||||
"updateRoomList"
|
||||
],
|
||||
|
||||
|
@ -469,29 +469,79 @@ loop.store = loop.store || {};
|
|||
},
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
* Updates the context data attached to a room.
|
||||
*
|
||||
* @param {sharedActions.RenameRoom} actionData
|
||||
* @param {sharedActions.UpdateRoomContext} actionData
|
||||
*/
|
||||
renameRoom: function(actionData) {
|
||||
var oldRoomName = this.getStoreState("roomName");
|
||||
var newRoomName = actionData.newRoomName.trim();
|
||||
updateRoomContext: function(actionData) {
|
||||
this._mozLoop.rooms.get(actionData.roomToken, function(err, room) {
|
||||
if (err) {
|
||||
this.dispatchAction(new sharedActions.UpdateRoomContextError({
|
||||
error: err
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip update if name is unchanged or empty.
|
||||
if (!newRoomName || oldRoomName === newRoomName) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setStoreState({error: null});
|
||||
this._mozLoop.rooms.rename(actionData.roomToken, newRoomName,
|
||||
function(err) {
|
||||
if (err) {
|
||||
this.dispatchAction(new sharedActions.RenameRoomError({error: err}));
|
||||
var roomData = {};
|
||||
var context = room.decryptedContext;
|
||||
var oldRoomName = context.roomName;
|
||||
var newRoomName = actionData.newRoomName.trim();
|
||||
if (newRoomName && oldRoomName != newRoomName) {
|
||||
roomData.roomName = newRoomName;
|
||||
}
|
||||
var oldRoomURLs = context.urls;
|
||||
var oldRoomURL = oldRoomURLs && oldRoomURLs[0];
|
||||
// Since we want to prevent storing falsy (i.e. empty) values for context
|
||||
// data, there's no need to send that to the server as an update.
|
||||
var newRoomURL = loop.shared.utils.stripFalsyValues({
|
||||
location: actionData.newRoomURL ? actionData.newRoomURL.trim() : "",
|
||||
thumbnail: actionData.newRoomURL ? actionData.newRoomThumbnail.trim() : "",
|
||||
description: actionData.newRoomDescription ?
|
||||
actionData.newRoomDescription.trim() : ""
|
||||
});
|
||||
// Only attach a context to the room when
|
||||
// 1) there was already a URL set,
|
||||
// 2) a new URL is provided as of now,
|
||||
// 3) the URL data has changed.
|
||||
var diff = loop.shared.utils.objectDiff(oldRoomURL, newRoomURL);
|
||||
if (diff.added.length || diff.updated.length) {
|
||||
newRoomURL = _.extend(oldRoomURL || {}, newRoomURL);
|
||||
var isValidURL = false;
|
||||
try {
|
||||
isValidURL = new URL(newRoomURL.location);
|
||||
} catch(ex) {}
|
||||
if (isValidURL) {
|
||||
roomData.urls = [newRoomURL];
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
// TODO: there currently is no clear UX defined on what to do when all
|
||||
// context data was cleared, e.g. when diff.removed contains all the
|
||||
// context properties. Until then, we can't deal with context removal here.
|
||||
|
||||
// When no properties have been set on the roomData object, there's nothing
|
||||
// to save.
|
||||
if (!Object.getOwnPropertyNames(roomData).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setStoreState({error: null});
|
||||
this._mozLoop.rooms.update(actionData.roomToken, roomData,
|
||||
function(err, data) {
|
||||
if (err) {
|
||||
this.dispatchAction(new sharedActions.UpdateRoomContextError({
|
||||
error: err
|
||||
}));
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
renameRoomError: function(actionData) {
|
||||
/**
|
||||
* Updating the context data attached to a room error.
|
||||
*
|
||||
* @param {sharedActions.UpdateRoomContextError} actionData
|
||||
*/
|
||||
updateRoomContextError: function(actionData) {
|
||||
this.setStoreState({error: actionData.error});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -170,11 +170,12 @@ loop.roomViews = (function(mozL10n) {
|
|||
* Desktop room invitation view (overlay).
|
||||
*/
|
||||
var DesktopRoomInvitationView = React.createClass({displayName: "DesktopRoomInvitationView",
|
||||
mixins: [React.addons.LinkedStateMixin, sharedMixins.DropdownMenuMixin],
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
|
@ -184,29 +185,10 @@ loop.roomViews = (function(mozL10n) {
|
|||
getInitialState: function() {
|
||||
return {
|
||||
copiedUrl: false,
|
||||
newRoomName: ""
|
||||
editMode: false
|
||||
};
|
||||
},
|
||||
|
||||
handleTextareaKeyDown: function(event) {
|
||||
// Submit the form as soon as the user press Enter in that field
|
||||
// Note: We're using a textarea instead of a simple text input to display
|
||||
// placeholder and entered text on two lines, to circumvent l10n
|
||||
// rendering/UX issues for some locales.
|
||||
if (event.which === 13) {
|
||||
this.handleFormSubmit(event);
|
||||
}
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: this.props.roomData.roomToken,
|
||||
newRoomName: this.state.newRoomName
|
||||
}));
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -229,27 +211,35 @@ loop.roomViews = (function(mozL10n) {
|
|||
this.toggleDropdownMenu();
|
||||
},
|
||||
|
||||
handleAddContextClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.handleEditModeChange(true);
|
||||
},
|
||||
|
||||
handleEditModeChange: function(newEditMode) {
|
||||
this.setState({ editMode: newEditMode });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.props.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var canAddContext = this.props.mozLoop.getLoopPref("contextInConversations.enabled") &&
|
||||
!this.props.showContext && !this.state.editMode;
|
||||
|
||||
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({"error": !!this.props.error,
|
||||
"error-display-area": true})},
|
||||
mozL10n.get("rooms_name_change_failed_label")
|
||||
React.createElement("p", {className: cx({hide: this.state.editMode})},
|
||||
mozL10n.get("invite_header_text")
|
||||
),
|
||||
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
||||
React.createElement("textarea", {rows: "2", type: "text", className: "input-room-name",
|
||||
valueLink: this.linkState("newRoomName"),
|
||||
onBlur: this.handleFormSubmit,
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("rooms_name_this_room_label")})
|
||||
React.createElement("a", {className: cx({hide: !canAddContext, "room-invitation-addcontext": true}),
|
||||
onClick: this.handleAddContextClick},
|
||||
mozL10n.get("context_add_some_label")
|
||||
),
|
||||
React.createElement("p", null, mozL10n.get("invite_header_text")),
|
||||
React.createElement("div", {className: "btn-group call-action-group"},
|
||||
React.createElement("button", {className: "btn btn-info btn-email",
|
||||
onClick: this.handleEmailButtonClick},
|
||||
|
@ -275,47 +265,257 @@ loop.roomViews = (function(mozL10n) {
|
|||
ref: "menu"})
|
||||
),
|
||||
React.createElement(DesktopRoomContextView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
editMode: this.state.editMode,
|
||||
error: this.props.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
onEditModeChange: this.handleEditModeChange,
|
||||
roomData: this.props.roomData,
|
||||
show: this.props.showContext})
|
||||
show: this.props.showContext || this.state.editMode})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DesktopRoomContextView = React.createClass({displayName: "DesktopRoomContextView",
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
editMode: React.PropTypes.bool,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
onEditModeChange: React.PropTypes.func,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
show: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
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) {
|
||||
this.setState({ show: nextProps.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.previews.length ? metadata.previews[0] : "";
|
||||
var description = metadata.description || metadata.title;
|
||||
var url = metadata.url;
|
||||
this.setState({
|
||||
availableContext: {
|
||||
previewImage: previewImage,
|
||||
description: description,
|
||||
url: url
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
// When we receive an update for the `roomData` property, make sure that
|
||||
// the current form fields reflect reality. This is necessary, because the
|
||||
// form state is maintained in the components' state.
|
||||
if (nextProps.roomData) {
|
||||
// Right now it's only necessary to update the form input states when
|
||||
// they contain no text yet.
|
||||
if (!this.state.newRoomName && nextProps.roomData.roomName) {
|
||||
newState.newRoomName = nextProps.roomData.roomName;
|
||||
}
|
||||
var url = this._getURL(nextProps.roomData);
|
||||
if (url) {
|
||||
if (!this.state.newRoomURL && url.location) {
|
||||
newState.newRoomURL = url.location;
|
||||
}
|
||||
if (!this.state.newRoomDescription && url.description) {
|
||||
newState.newRoomDescription = url.description;
|
||||
}
|
||||
if (!this.state.newRoomThumbnail && url.thumbnail) {
|
||||
newState.newRoomThumbnail = url.thumbnail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertyNames(newState).length) {
|
||||
this.setState(newState);
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return { editMode: false };
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return { show: this.props.show };
|
||||
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 || "",
|
||||
newRoomDescription: url && url.description || "",
|
||||
newRoomThumbnail: url && url.thumbnail || ""
|
||||
};
|
||||
},
|
||||
|
||||
handleCloseClick: function() {
|
||||
handleCloseClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.state.editMode) {
|
||||
this.setState({ editMode: false });
|
||||
if (this.props.onEditModeChange) {
|
||||
this.props.onEditModeChange(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.setState({ show: false });
|
||||
},
|
||||
|
||||
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
|
||||
// in `availableContext`.
|
||||
var context = this.state.availableContext;
|
||||
this.setState({
|
||||
newRoomURL: context.url,
|
||||
newRoomDescription: context.description,
|
||||
newRoomThumbnail: context.previewImage
|
||||
}, this.handleFormSubmit);
|
||||
} else {
|
||||
this.setState({
|
||||
newRoomURL: "",
|
||||
newRoomDescription: "",
|
||||
newRoomThumbnail: ""
|
||||
}, this.handleFormSubmit);
|
||||
}
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event && event.preventDefault();
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.UpdateRoomContext({
|
||||
roomToken: this.props.roomData.roomToken,
|
||||
newRoomName: this.state.newRoomName,
|
||||
newRoomURL: this.state.newRoomURL,
|
||||
newRoomDescription: this.state.newRoomDescription,
|
||||
newRoomThumbnail: this.state.newRoomThumbnail
|
||||
}));
|
||||
},
|
||||
|
||||
handleTextareaKeyDown: function(event) {
|
||||
// Submit the form as soon as the user press Enter in that field
|
||||
// Note: We're using a textarea instead of a simple text input to display
|
||||
// placeholder and entered text on two lines, to circumvent l10n
|
||||
// rendering/UX issues for some locales.
|
||||
if (event.which === 13) {
|
||||
this.handleFormSubmit(event);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to extract URL context data from the `roomData` property
|
||||
* that can also be supplied as an argument.
|
||||
*
|
||||
* @param {Object} roomData Optional room data object to use, equivalent to
|
||||
* the activeRoomStore state.
|
||||
* @return {Object} The first context URL found on the `roomData` object.
|
||||
*/
|
||||
_getURL: function(roomData) {
|
||||
roomData = roomData || this.props.roomData;
|
||||
return this.props.roomData.roomContextUrls &&
|
||||
this.props.roomData.roomContextUrls[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Truncate a string if it exceeds the length as defined in `maxLen`, which
|
||||
* is defined as '72' characters by default. If the string needs trimming,
|
||||
* it'll be suffixed with the unicode ellipsis char, \u2026.
|
||||
*
|
||||
* @param {String} str The string to truncate, if needed.
|
||||
* @param {Number} maxLen Maximum number of characters that the string is
|
||||
* allowed to contain. Optional, defaults to 72.
|
||||
* @return {String} Truncated version of `str`.
|
||||
*/
|
||||
_truncate: function(str, maxLen) {
|
||||
if (!maxLen) {
|
||||
maxLen = 72;
|
||||
}
|
||||
return (str.length > maxLen) ? str.substr(0, maxLen) + "…" : str;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.show)
|
||||
if (!this.state.show && !this.state.editMode)
|
||||
return null;
|
||||
|
||||
var URL = this.props.roomData.roomContextUrls && this.props.roomData.roomContextUrls[0];
|
||||
var thumbnail = URL && URL.thumbnail || "";
|
||||
var URLDescription = URL && URL.description || "";
|
||||
var location = URL && URL.location || "";
|
||||
var url = this._getURL();
|
||||
var thumbnail = url && url.thumbnail || "";
|
||||
var urlDescription = url && url.description || "";
|
||||
var location = url && url.location || "";
|
||||
var checkboxLabel = null;
|
||||
var locationData = null;
|
||||
if (location) {
|
||||
locationData = sharedUtils.formatURL(location);
|
||||
locationData = checkboxLabel = sharedUtils.formatURL(location);
|
||||
}
|
||||
if (!checkboxLabel) {
|
||||
checkboxLabel = sharedUtils.formatURL((this.state.availableContext ?
|
||||
this.state.availableContext.url : ""));
|
||||
}
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
if (this.state.editMode) {
|
||||
return (
|
||||
React.createElement("div", {className: "room-context"},
|
||||
React.createElement("div", {className: "room-context-content"},
|
||||
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, {
|
||||
checked: !!url,
|
||||
disabled: !!url || !checkboxLabel,
|
||||
label: mozL10n.get("context_edit_activate_label", {
|
||||
title: checkboxLabel ? checkboxLabel.hostname : ""
|
||||
}),
|
||||
onChange: this.handleCheckboxChange,
|
||||
value: location}),
|
||||
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
||||
React.createElement("textarea", {rows: "2", type: "text", className: "room-context-name",
|
||||
onBlur: this.handleFormSubmit,
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_name_placeholder"),
|
||||
valueLink: this.linkState("newRoomName")}),
|
||||
React.createElement("input", {type: "text", className: "room-context-url",
|
||||
onBlur: this.handleFormSubmit,
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: "https://",
|
||||
valueLink: this.linkState("newRoomURL")}),
|
||||
React.createElement("textarea", {rows: "4", type: "text", className: "room-context-comments",
|
||||
onBlur: this.handleFormSubmit,
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_comments_placeholder"),
|
||||
valueLink: this.linkState("newRoomDescription")})
|
||||
),
|
||||
React.createElement("button", {className: "room-context-btn-close",
|
||||
onClick: this.handleCloseClick})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!locationData) {
|
||||
|
@ -327,7 +527,8 @@ loop.roomViews = (function(mozL10n) {
|
|||
React.createElement("img", {className: "room-context-thumbnail", src: thumbnail}),
|
||||
React.createElement("div", {className: "room-context-content"},
|
||||
React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")),
|
||||
React.createElement("div", {className: "room-context-description"}, URLDescription),
|
||||
React.createElement("div", {className: "room-context-description",
|
||||
title: urlDescription}, this._truncate(urlDescription)),
|
||||
React.createElement("a", {className: "room-context-url",
|
||||
href: location,
|
||||
target: "_blank",
|
||||
|
@ -336,7 +537,9 @@ loop.roomViews = (function(mozL10n) {
|
|||
React.createElement("div", {className: "room-context-comment"}, this.props.roomData.roomDescription) :
|
||||
null,
|
||||
React.createElement("button", {className: "room-context-btn-close",
|
||||
onClick: this.handleCloseClick})
|
||||
onClick: this.handleCloseClick}),
|
||||
React.createElement("button", {className: "room-context-btn-edit",
|
||||
onClick: this.handleEditClick})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -457,6 +660,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
React.createElement(DesktopRoomInvitationView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
error: this.state.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
roomData: roomData,
|
||||
show: shouldRenderInvitationOverlay,
|
||||
showContext: shouldRenderContextView,
|
||||
|
@ -481,6 +685,9 @@ loop.roomViews = (function(mozL10n) {
|
|||
)
|
||||
),
|
||||
React.createElement(DesktopRoomContextView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
error: this.state.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
roomData: roomData,
|
||||
show: !shouldRenderInvitationOverlay && shouldRenderContextView})
|
||||
)
|
||||
|
|
|
@ -170,11 +170,12 @@ loop.roomViews = (function(mozL10n) {
|
|||
* Desktop room invitation view (overlay).
|
||||
*/
|
||||
var DesktopRoomInvitationView = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin, sharedMixins.DropdownMenuMixin],
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
|
@ -184,29 +185,10 @@ loop.roomViews = (function(mozL10n) {
|
|||
getInitialState: function() {
|
||||
return {
|
||||
copiedUrl: false,
|
||||
newRoomName: ""
|
||||
editMode: false
|
||||
};
|
||||
},
|
||||
|
||||
handleTextareaKeyDown: function(event) {
|
||||
// Submit the form as soon as the user press Enter in that field
|
||||
// Note: We're using a textarea instead of a simple text input to display
|
||||
// placeholder and entered text on two lines, to circumvent l10n
|
||||
// rendering/UX issues for some locales.
|
||||
if (event.which === 13) {
|
||||
this.handleFormSubmit(event);
|
||||
}
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: this.props.roomData.roomToken,
|
||||
newRoomName: this.state.newRoomName
|
||||
}));
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -229,27 +211,35 @@ loop.roomViews = (function(mozL10n) {
|
|||
this.toggleDropdownMenu();
|
||||
},
|
||||
|
||||
handleAddContextClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.handleEditModeChange(true);
|
||||
},
|
||||
|
||||
handleEditModeChange: function(newEditMode) {
|
||||
this.setState({ editMode: newEditMode });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.props.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var canAddContext = this.props.mozLoop.getLoopPref("contextInConversations.enabled") &&
|
||||
!this.props.showContext && !this.state.editMode;
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<div className="room-invitation-overlay">
|
||||
<div className="room-invitation-content">
|
||||
<p className={cx({"error": !!this.props.error,
|
||||
"error-display-area": true})}>
|
||||
{mozL10n.get("rooms_name_change_failed_label")}
|
||||
<p className={cx({hide: this.state.editMode})}>
|
||||
{mozL10n.get("invite_header_text")}
|
||||
</p>
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<textarea rows="2" type="text" className="input-room-name"
|
||||
valueLink={this.linkState("newRoomName")}
|
||||
onBlur={this.handleFormSubmit}
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("rooms_name_this_room_label")} />
|
||||
</form>
|
||||
<p>{mozL10n.get("invite_header_text")}</p>
|
||||
<a className={cx({hide: !canAddContext, "room-invitation-addcontext": true})}
|
||||
onClick={this.handleAddContextClick}>
|
||||
{mozL10n.get("context_add_some_label")}
|
||||
</a>
|
||||
<div className="btn-group call-action-group">
|
||||
<button className="btn btn-info btn-email"
|
||||
onClick={this.handleEmailButtonClick}>
|
||||
|
@ -275,47 +265,257 @@ loop.roomViews = (function(mozL10n) {
|
|||
ref="menu" />
|
||||
</div>
|
||||
<DesktopRoomContextView
|
||||
dispatcher={this.props.dispatcher}
|
||||
editMode={this.state.editMode}
|
||||
error={this.props.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
onEditModeChange={this.handleEditModeChange}
|
||||
roomData={this.props.roomData}
|
||||
show={this.props.showContext} />
|
||||
show={this.props.showContext || this.state.editMode} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DesktopRoomContextView = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
editMode: React.PropTypes.bool,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
onEditModeChange: React.PropTypes.func,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
show: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
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) {
|
||||
this.setState({ show: nextProps.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.previews.length ? metadata.previews[0] : "";
|
||||
var description = metadata.description || metadata.title;
|
||||
var url = metadata.url;
|
||||
this.setState({
|
||||
availableContext: {
|
||||
previewImage: previewImage,
|
||||
description: description,
|
||||
url: url
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
// When we receive an update for the `roomData` property, make sure that
|
||||
// the current form fields reflect reality. This is necessary, because the
|
||||
// form state is maintained in the components' state.
|
||||
if (nextProps.roomData) {
|
||||
// Right now it's only necessary to update the form input states when
|
||||
// they contain no text yet.
|
||||
if (!this.state.newRoomName && nextProps.roomData.roomName) {
|
||||
newState.newRoomName = nextProps.roomData.roomName;
|
||||
}
|
||||
var url = this._getURL(nextProps.roomData);
|
||||
if (url) {
|
||||
if (!this.state.newRoomURL && url.location) {
|
||||
newState.newRoomURL = url.location;
|
||||
}
|
||||
if (!this.state.newRoomDescription && url.description) {
|
||||
newState.newRoomDescription = url.description;
|
||||
}
|
||||
if (!this.state.newRoomThumbnail && url.thumbnail) {
|
||||
newState.newRoomThumbnail = url.thumbnail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertyNames(newState).length) {
|
||||
this.setState(newState);
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return { editMode: false };
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return { show: this.props.show };
|
||||
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 || "",
|
||||
newRoomDescription: url && url.description || "",
|
||||
newRoomThumbnail: url && url.thumbnail || ""
|
||||
};
|
||||
},
|
||||
|
||||
handleCloseClick: function() {
|
||||
handleCloseClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.state.editMode) {
|
||||
this.setState({ editMode: false });
|
||||
if (this.props.onEditModeChange) {
|
||||
this.props.onEditModeChange(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.setState({ show: false });
|
||||
},
|
||||
|
||||
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
|
||||
// in `availableContext`.
|
||||
var context = this.state.availableContext;
|
||||
this.setState({
|
||||
newRoomURL: context.url,
|
||||
newRoomDescription: context.description,
|
||||
newRoomThumbnail: context.previewImage
|
||||
}, this.handleFormSubmit);
|
||||
} else {
|
||||
this.setState({
|
||||
newRoomURL: "",
|
||||
newRoomDescription: "",
|
||||
newRoomThumbnail: ""
|
||||
}, this.handleFormSubmit);
|
||||
}
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event && event.preventDefault();
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.UpdateRoomContext({
|
||||
roomToken: this.props.roomData.roomToken,
|
||||
newRoomName: this.state.newRoomName,
|
||||
newRoomURL: this.state.newRoomURL,
|
||||
newRoomDescription: this.state.newRoomDescription,
|
||||
newRoomThumbnail: this.state.newRoomThumbnail
|
||||
}));
|
||||
},
|
||||
|
||||
handleTextareaKeyDown: function(event) {
|
||||
// Submit the form as soon as the user press Enter in that field
|
||||
// Note: We're using a textarea instead of a simple text input to display
|
||||
// placeholder and entered text on two lines, to circumvent l10n
|
||||
// rendering/UX issues for some locales.
|
||||
if (event.which === 13) {
|
||||
this.handleFormSubmit(event);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to extract URL context data from the `roomData` property
|
||||
* that can also be supplied as an argument.
|
||||
*
|
||||
* @param {Object} roomData Optional room data object to use, equivalent to
|
||||
* the activeRoomStore state.
|
||||
* @return {Object} The first context URL found on the `roomData` object.
|
||||
*/
|
||||
_getURL: function(roomData) {
|
||||
roomData = roomData || this.props.roomData;
|
||||
return this.props.roomData.roomContextUrls &&
|
||||
this.props.roomData.roomContextUrls[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Truncate a string if it exceeds the length as defined in `maxLen`, which
|
||||
* is defined as '72' characters by default. If the string needs trimming,
|
||||
* it'll be suffixed with the unicode ellipsis char, \u2026.
|
||||
*
|
||||
* @param {String} str The string to truncate, if needed.
|
||||
* @param {Number} maxLen Maximum number of characters that the string is
|
||||
* allowed to contain. Optional, defaults to 72.
|
||||
* @return {String} Truncated version of `str`.
|
||||
*/
|
||||
_truncate: function(str, maxLen) {
|
||||
if (!maxLen) {
|
||||
maxLen = 72;
|
||||
}
|
||||
return (str.length > maxLen) ? str.substr(0, maxLen) + "…" : str;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.show)
|
||||
if (!this.state.show && !this.state.editMode)
|
||||
return null;
|
||||
|
||||
var URL = this.props.roomData.roomContextUrls && this.props.roomData.roomContextUrls[0];
|
||||
var thumbnail = URL && URL.thumbnail || "";
|
||||
var URLDescription = URL && URL.description || "";
|
||||
var location = URL && URL.location || "";
|
||||
var url = this._getURL();
|
||||
var thumbnail = url && url.thumbnail || "";
|
||||
var urlDescription = url && url.description || "";
|
||||
var location = url && url.location || "";
|
||||
var checkboxLabel = null;
|
||||
var locationData = null;
|
||||
if (location) {
|
||||
locationData = sharedUtils.formatURL(location);
|
||||
locationData = checkboxLabel = sharedUtils.formatURL(location);
|
||||
}
|
||||
if (!checkboxLabel) {
|
||||
checkboxLabel = sharedUtils.formatURL((this.state.availableContext ?
|
||||
this.state.availableContext.url : ""));
|
||||
}
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
if (this.state.editMode) {
|
||||
return (
|
||||
<div className="room-context">
|
||||
<div className="room-context-content">
|
||||
<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
|
||||
checked={!!url}
|
||||
disabled={!!url || !checkboxLabel}
|
||||
label={mozL10n.get("context_edit_activate_label", {
|
||||
title: checkboxLabel ? checkboxLabel.hostname : ""
|
||||
})}
|
||||
onChange={this.handleCheckboxChange}
|
||||
value={location} />
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<textarea rows="2" type="text" className="room-context-name"
|
||||
onBlur={this.handleFormSubmit}
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_name_placeholder")}
|
||||
valueLink={this.linkState("newRoomName")} />
|
||||
<input type="text" className="room-context-url"
|
||||
onBlur={this.handleFormSubmit}
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder="https://"
|
||||
valueLink={this.linkState("newRoomURL")} />
|
||||
<textarea rows="4" type="text" className="room-context-comments"
|
||||
onBlur={this.handleFormSubmit}
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_comments_placeholder")}
|
||||
valueLink={this.linkState("newRoomDescription")} />
|
||||
</form>
|
||||
<button className="room-context-btn-close"
|
||||
onClick={this.handleCloseClick}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!locationData) {
|
||||
|
@ -327,7 +527,8 @@ loop.roomViews = (function(mozL10n) {
|
|||
<img className="room-context-thumbnail" src={thumbnail}/>
|
||||
<div className="room-context-content">
|
||||
<div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
|
||||
<div className="room-context-description">{URLDescription}</div>
|
||||
<div className="room-context-description"
|
||||
title={urlDescription}>{this._truncate(urlDescription)}</div>
|
||||
<a className="room-context-url"
|
||||
href={location}
|
||||
target="_blank"
|
||||
|
@ -337,6 +538,8 @@ loop.roomViews = (function(mozL10n) {
|
|||
null}
|
||||
<button className="room-context-btn-close"
|
||||
onClick={this.handleCloseClick}/>
|
||||
<button className="room-context-btn-edit"
|
||||
onClick={this.handleEditClick}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -457,6 +660,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
<DesktopRoomInvitationView
|
||||
dispatcher={this.props.dispatcher}
|
||||
error={this.state.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
roomData={roomData}
|
||||
show={shouldRenderInvitationOverlay}
|
||||
showContext={shouldRenderContextView}
|
||||
|
@ -481,6 +685,9 @@ loop.roomViews = (function(mozL10n) {
|
|||
</div>
|
||||
</div>
|
||||
<DesktopRoomContextView
|
||||
dispatcher={this.props.dispatcher}
|
||||
error={this.state.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
roomData={roomData}
|
||||
show={!shouldRenderInvitationOverlay && shouldRenderContextView} />
|
||||
</div>
|
||||
|
|
|
@ -874,7 +874,6 @@ html, .fx-embedded, #main,
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.room-invitation-overlay .error-display-area.error,
|
||||
.room-invitation-overlay input[type="text"] {
|
||||
display: block;
|
||||
background-color: rgba(0,0,0,.5);
|
||||
|
@ -882,35 +881,31 @@ html, .fx-embedded, #main,
|
|||
padding: .5em;
|
||||
}
|
||||
|
||||
.room-invitation-overlay .error-display-area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.room-invitation-overlay .error-display-area.error {
|
||||
position: absolute;
|
||||
top: 2em;
|
||||
left: 1em;
|
||||
right: 1em;
|
||||
text-align: start;
|
||||
width: calc(258px - 2em);
|
||||
color: #d74345;
|
||||
}
|
||||
|
||||
.room-invitation-overlay .btn-group {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.room-invitation-overlay textarea {
|
||||
display: block;
|
||||
background: rgba(0, 0, 0, .5);
|
||||
color: #fff;
|
||||
font-family: "Helvetica Neue", Arial, sans;
|
||||
font-size: 1.2em;
|
||||
border: none;
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
padding: .2em .4em;
|
||||
border-radius: .5em;
|
||||
.room-invitation-addcontext {
|
||||
color: #0095dd;
|
||||
padding-left: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
background-image: url("../img/icons-10x10.svg#edit-active");
|
||||
background-size: 1em 1em;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left top;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.room-invitation-addcontext:hover,
|
||||
.room-invitation-addcontext:hover:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
body[dir="rtl"] .room-invitation-addcontext {
|
||||
padding-left: auto;
|
||||
padding-right: 1.5em;
|
||||
background-position: right top;
|
||||
}
|
||||
|
||||
.share-service-dropdown {
|
||||
|
@ -975,7 +970,7 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
|
|||
border-top: 2px solid #444;
|
||||
border-bottom: 2px solid #444;
|
||||
padding: .5rem;
|
||||
max-height: 120px;
|
||||
max-height: 400px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
@ -985,6 +980,8 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
|
|||
flex-flow: row nowrap;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.room-invitation-overlay .room-context {
|
||||
|
@ -997,7 +994,6 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
|
|||
|
||||
.room-context-thumbnail {
|
||||
width: 100px;
|
||||
max-height: 200px;
|
||||
-moz-margin-end: 1ch;
|
||||
margin-bottom: 1em;
|
||||
order: 1;
|
||||
|
@ -1022,12 +1018,34 @@ body[dir=rtl] .room-context-content {
|
|||
order: 1;
|
||||
}
|
||||
|
||||
.room-context-content > .error-display-area.error {
|
||||
display: block;
|
||||
background-color: rgba(215,67,69,.8);
|
||||
border-radius: 3px;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.room-context-content > .error-display-area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.room-context-content > .error-display-area.error {
|
||||
margin: 1em 0 .5em 0;
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px 0 rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
.room-context-content > .checkbox-wrapper {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.room-context-label {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.room-context-label,
|
||||
.room-context-description {
|
||||
.room-context-description,
|
||||
.room-context-content > .checkbox-wrapper > label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@ -1051,7 +1069,27 @@ body[dir=rtl] .room-context-content {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.room-context-btn-close {
|
||||
.room-context-content > form > textarea,
|
||||
.room-context-content > form > input[type="text"] {
|
||||
display: block;
|
||||
background: rgba(0,0,0,.5);
|
||||
color: #fff;
|
||||
font-family: "Helvetica Neue", Arial, sans;
|
||||
font-size: 1em;
|
||||
border: 1px solid #999;
|
||||
width: 100%;
|
||||
padding: .2em .4em;
|
||||
border-radius: 3px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.room-context-content > form > textarea:not(:last-of-type),
|
||||
.room-context-content > form > input[type="text"] {
|
||||
margin: 0 0 .5em 0;
|
||||
}
|
||||
|
||||
.room-context-btn-close,
|
||||
.room-context-btn-edit {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
|
@ -1066,16 +1104,31 @@ body[dir=rtl] .room-context-content {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.room-context-btn-edit {
|
||||
right: 18px;
|
||||
background-image: url("../img/icons-10x10.svg#edit");
|
||||
}
|
||||
|
||||
.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");
|
||||
}
|
||||
|
||||
body[dir=rtl] .room-context-btn-close {
|
||||
body[dir=rtl] .room-context-btn-close,
|
||||
body[dir=rtl] .room-context-btn-edit {
|
||||
right: auto;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
body[dir=rtl] .room-context-btn-edit {
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
/* Standalone rooms */
|
||||
|
||||
.standalone .room-conversation-wrapper {
|
||||
|
|
|
@ -39,6 +39,11 @@ use[id$="-disabled"] {
|
|||
<path id="dropdown-shape" fill-rule="evenodd" clip-rule="evenodd" d="M9,3L4.984,7L1,3H9z"/>
|
||||
<polygon id="expand-shape" fill-rule="evenodd" clip-rule="evenodd" points="10,0 4.838,0 6.506,1.669 0,8.175 1.825,10 8.331,3.494
|
||||
10,5.162"/>
|
||||
<path id="edit-shape" d="M5.493,1.762l2.745,2.745L2.745,10H0V7.255L5.493,1.762z M2.397,9.155l0.601-0.601L1.446,7.002L0.845,7.603
|
||||
V8.31H1.69v0.845H2.397z M5.849,3.028c0-0.096-0.048-0.144-0.146-0.144c-0.044,0-0.081,0.015-0.112,0.046L2.014,6.508
|
||||
C1.983,6.538,1.968,6.577,1.968,6.619c0,0.098,0.048,0.146,0.144,0.146c0.044,0,0.081-0.015,0.112-0.046l3.579-3.577
|
||||
C5.834,3.111,5.849,3.073,5.849,3.028z M10,2.395c0,0.233-0.081,0.431-0.245,0.595L8.66,4.085L5.915,1.34L7.01,0.25
|
||||
C7.168,0.083,7.366,0,7.605,0c0.233,0,0.433,0.083,0.601,0.25l1.55,1.544C9.919,1.966,10,2.166,10,2.395z"/>
|
||||
<rect id="minimize-shape" y="3.6" fill-rule="evenodd" clip-rule="evenodd" width="10" height="2.8"/>
|
||||
</defs>
|
||||
<use id="close" xlink:href="#close-shape"/>
|
||||
|
@ -48,6 +53,9 @@ use[id$="-disabled"] {
|
|||
<use id="dropdown-white" xlink:href="#dropdown-shape"/>
|
||||
<use id="dropdown-active" xlink:href="#dropdown-shape"/>
|
||||
<use id="dropdown-disabled" xlink:href="#dropdown-shape"/>
|
||||
<use id="edit" xlink:href="#edit-shape"/>
|
||||
<use id="edit-active" xlink:href="#edit-shape"/>
|
||||
<use id="edit-disabled" xlink:href="#edit-shape"/>
|
||||
<use id="expand" xlink:href="#expand-shape"/>
|
||||
<use id="expand-active" xlink:href="#expand-shape"/>
|
||||
<use id="expand-disabled" xlink:href="#expand-shape"/>
|
||||
|
|
До Ширина: | Высота: | Размер: 2.0 KiB После Ширина: | Высота: | Размер: 2.8 KiB |
|
@ -330,19 +330,21 @@ loop.shared.actions = (function() {
|
|||
}),
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
* Updates the context data attached to a room.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
*/
|
||||
RenameRoom: Action.define("renameRoom", {
|
||||
UpdateRoomContext: Action.define("updateRoomContext", {
|
||||
roomToken: String,
|
||||
newRoomName: String
|
||||
// newRoomDescription: String, Optional.
|
||||
// newRoomThumbnail: String, Optional.
|
||||
// newRoomURL: String Optional.
|
||||
}),
|
||||
|
||||
/**
|
||||
* Renaming a room error.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
* Updating the context data attached to a room error.
|
||||
*/
|
||||
RenameRoomError: Action.define("renameRoomError", {
|
||||
UpdateRoomContextError: Action.define("updateRoomContextError", {
|
||||
error: [Error, Object]
|
||||
}),
|
||||
|
||||
|
|
|
@ -710,29 +710,36 @@ let LoopRoomsInternal = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
* Updates a room.
|
||||
*
|
||||
* @param {String} roomToken The room token
|
||||
* @param {String} newRoomName The new name for the room
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`.
|
||||
* @param {String} roomToken The room token
|
||||
* @param {Object} roomData Updated context data for the room. The following
|
||||
* properties are expected: `roomName` and `urls`.
|
||||
* IMPORTANT: Data in the `roomData::urls` array
|
||||
* will be stored as-is, so any data omitted therein
|
||||
* will be gone forever.
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`.
|
||||
*/
|
||||
rename: function(roomToken, newRoomName, callback) {
|
||||
update: function(roomToken, roomData, callback) {
|
||||
let room = this.rooms.get(roomToken);
|
||||
let url = "/rooms/" + encodeURIComponent(roomToken);
|
||||
|
||||
let roomData = this.rooms.get(roomToken);
|
||||
if (!roomData.decryptedContext) {
|
||||
roomData.decryptedContext = {
|
||||
roomName: newRoomName
|
||||
if (!room.decryptedContext) {
|
||||
room.decryptedContext = {
|
||||
roomName: roomData.roomName || room.roomName
|
||||
};
|
||||
} else {
|
||||
roomData.decryptedContext.roomName = newRoomName;
|
||||
room.decryptedContext.roomName = roomData.roomName || room.roomName;
|
||||
}
|
||||
if (roomData.urls && roomData.urls.length) {
|
||||
// For now we only support adding one URL to the room context.
|
||||
room.decryptedContext.urls = [roomData.urls[0]];
|
||||
}
|
||||
|
||||
Task.spawn(function* () {
|
||||
let {all, encrypted} = yield this.promiseEncryptRoomData(roomData);
|
||||
let {all, encrypted} = yield this.promiseEncryptRoomData(room);
|
||||
|
||||
// For patch, we only send the context data.
|
||||
let sendData = {
|
||||
|
@ -743,7 +750,7 @@ let LoopRoomsInternal = {
|
|||
// XXX This should go away once bug 1153788 is fixed.
|
||||
if (!sendData.context) {
|
||||
sendData = {
|
||||
roomName: newRoomName
|
||||
roomName: room.decryptedContext.roomName
|
||||
};
|
||||
} else {
|
||||
// This might be an upgrade to encrypted rename, so store the key
|
||||
|
@ -864,8 +871,8 @@ this.LoopRooms = {
|
|||
return LoopRoomsInternal.sendConnectionStatus(roomToken, sessionToken, status, callback);
|
||||
},
|
||||
|
||||
rename: function(roomToken, newRoomName, callback) {
|
||||
return LoopRoomsInternal.rename(roomToken, newRoomName, callback);
|
||||
update: function(roomToken, roomData, callback) {
|
||||
return LoopRoomsInternal.update(roomToken, roomData, callback);
|
||||
},
|
||||
|
||||
getGuestCreatedRoom: function() {
|
||||
|
|
|
@ -115,7 +115,6 @@ rooms_leave_button_label=Leave
|
|||
rooms_list_copy_url_tooltip=Copy Link
|
||||
rooms_list_delete_tooltip=Delete conversation
|
||||
rooms_list_deleteConfirmation_label=Are you sure?
|
||||
rooms_name_this_room_label=Name this conversation
|
||||
rooms_new_room_button_label=Start a conversation
|
||||
rooms_only_occupant_label=You're the first one here.
|
||||
rooms_panel_title=Choose a conversation or start a new one
|
||||
|
|
|
@ -164,9 +164,9 @@
|
|||
var SVGIcons = React.createClass({displayName: "SVGIcons",
|
||||
shapes: {
|
||||
"10x10": ["close", "close-active", "close-disabled", "dropdown",
|
||||
"dropdown-white", "dropdown-active", "dropdown-disabled", "expand",
|
||||
"expand-active", "expand-disabled", "minimize", "minimize-active",
|
||||
"minimize-disabled"
|
||||
"dropdown-white", "dropdown-active", "dropdown-disabled", "edit",
|
||||
"edit-active", "edit-disabled", "expand", "expand-active", "expand-disabled",
|
||||
"minimize", "minimize-active", "minimize-disabled"
|
||||
],
|
||||
"14x14": ["audio", "audio-active", "audio-disabled", "facemute",
|
||||
"facemute-active", "facemute-disabled", "hangup", "hangup-active",
|
||||
|
|
|
@ -164,9 +164,9 @@
|
|||
var SVGIcons = React.createClass({
|
||||
shapes: {
|
||||
"10x10": ["close", "close-active", "close-disabled", "dropdown",
|
||||
"dropdown-white", "dropdown-active", "dropdown-disabled", "expand",
|
||||
"expand-active", "expand-disabled", "minimize", "minimize-active",
|
||||
"minimize-disabled"
|
||||
"dropdown-white", "dropdown-active", "dropdown-disabled", "edit",
|
||||
"edit-active", "edit-disabled", "expand", "expand-active", "expand-disabled",
|
||||
"minimize", "minimize-active", "minimize-disabled"
|
||||
],
|
||||
"14x14": ["audio", "audio-active", "audio-disabled", "facemute",
|
||||
"facemute-active", "facemute-disabled", "hangup", "hangup-active",
|
||||
|
|
|
@ -306,8 +306,7 @@ rooms_list_current_conversations=Current conversation;Current conversations
|
|||
rooms_list_delete_tooltip=Delete conversation
|
||||
rooms_list_deleteConfirmation_label=Are you sure?
|
||||
rooms_list_no_current_conversations=No current conversations
|
||||
rooms_name_this_room_label=Name this conversation
|
||||
rooms_name_change_failed_label=Conversation cannot be renamed
|
||||
rooms_change_failed_label=Conversation cannot be updated
|
||||
rooms_new_room_button_label=Start a conversation
|
||||
rooms_only_occupant_label=You're the first one here.
|
||||
rooms_panel_title=Choose a conversation or start a new one
|
||||
|
@ -340,3 +339,4 @@ context_inroom_label=Let's talk about:
|
|||
context_edit_activate_label=Talk about "{{title}}"
|
||||
context_edit_name_placeholder=Conversation Name
|
||||
context_edit_comments_placeholder=Comments
|
||||
context_add_some_label=Add some context
|
||||
|
|
Загрузка…
Ссылка в новой задаче