experiment with trying to turn UserSettings into a controller-less 'wiring component' which wires together a series of smaller components (in this case, so small they're mainly <input/s>
This commit is contained in:
parent
88c5a5e074
commit
7b3eea0b58
|
@ -94,7 +94,16 @@ limitations under the License.
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
margin-left: 63px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_simpleHeaderCancel {
|
||||||
|
float: right;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_name {
|
.mx_RoomHeader_name {
|
||||||
|
|
|
@ -19,3 +19,101 @@ limitations under the License.
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_spinner {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 12px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_button {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
border: 0px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 36px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #76cfa6;
|
||||||
|
width: auto;
|
||||||
|
margin: auto;
|
||||||
|
padding: 7px;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 1.5em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings h2 {
|
||||||
|
clear: both;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-left: 63px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_section {
|
||||||
|
margin-left: 63px;
|
||||||
|
margin-top: 28px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_profileTable,
|
||||||
|
.mx_UserSettings_notifTable
|
||||||
|
{
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_profileTableRow,
|
||||||
|
.mx_UserSettings_notifTableRow
|
||||||
|
{
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_profileLabelCell
|
||||||
|
{
|
||||||
|
padding-bottom: 21px;
|
||||||
|
display: table-cell;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_profileInputCell {
|
||||||
|
display: table-cell;
|
||||||
|
padding-bottom: 21px;
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_profileInputCell input {
|
||||||
|
border: 0px;
|
||||||
|
border-bottom: 1px solid rgba(151, 151, 151, 0.5);
|
||||||
|
padding: 0px;
|
||||||
|
width: 240px;
|
||||||
|
color: rgba(74, 74, 74, 0.9);
|
||||||
|
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_notifInputCell {
|
||||||
|
display: table-cell;
|
||||||
|
padding-bottom: 21px;
|
||||||
|
padding-right: 8px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_notifLabelCell
|
||||||
|
{
|
||||||
|
padding-bottom: 21px;
|
||||||
|
width: 270px;
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserSettings_logout,
|
||||||
|
.mx_UserSettings_save {
|
||||||
|
float: right;
|
||||||
|
margin-right: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
|
@ -48,10 +48,15 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var header;
|
var header;
|
||||||
if (this.props.simpleHeader) {
|
if (this.props.simpleHeader) {
|
||||||
|
var cancel;
|
||||||
|
if (this.props.onCancelClick) {
|
||||||
|
cancel = <img className="mx_RoomHeader_simpleHeaderCancel" src="img/cancel-black.png" onClick={ this.props.onCancelClick } alt="Close" width="18" height="18"/>
|
||||||
|
}
|
||||||
header =
|
header =
|
||||||
<div className="mx_RoomHeader_wrapper">
|
<div className="mx_RoomHeader_wrapper">
|
||||||
<div className="mx_RoomHeader_simpleHeader">
|
<div className="mx_RoomHeader_simpleHeader">
|
||||||
{ this.props.simpleHeader }
|
{ this.props.simpleHeader }
|
||||||
|
{ cancel }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,142 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher')
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var q = require('q');
|
||||||
|
|
||||||
var UserSettingsController = require('matrix-react-sdk/lib/controllers/organisms/UserSettings')
|
var version = require('../../../../../package.json').version;
|
||||||
|
|
||||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||||
|
var UserSettingsStore = require('matrix-react-sdk/lib/UserSettingsStore');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'UserSettings',
|
displayName: 'UserSettings',
|
||||||
mixins: [UserSettingsController],
|
|
||||||
|
Phases: {
|
||||||
|
Loading: "loading",
|
||||||
|
Saving: "saving",
|
||||||
|
Display: "display",
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
avatarUrl: null,
|
||||||
|
displayName: null,
|
||||||
|
threePids: [],
|
||||||
|
clientVersion: version,
|
||||||
|
phase: this.Phases.Loading,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var profilePromise = UserSettingsStore.loadProfileInfo();
|
||||||
|
var threepidPromise = UserSettingsStore.loadThreePids();
|
||||||
|
|
||||||
|
q.all([profilePromise, threepidPromise]).then(
|
||||||
|
function(resps) {
|
||||||
|
self.setState({
|
||||||
|
avatarUrl: resps[0].avatar_url,
|
||||||
|
displayName: resps[0].displayname,
|
||||||
|
threepids: resps[1].threepids,
|
||||||
|
phase: self.Phases.Display,
|
||||||
|
});
|
||||||
|
|
||||||
|
// keep a copy of the original state in order to track changes
|
||||||
|
self.setState({
|
||||||
|
originalState: self.state
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Can't load user settings",
|
||||||
|
description: error.toString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
dis.unregister(this.dispatcherRef);
|
||||||
|
},
|
||||||
|
|
||||||
|
onSaveClicked: function(ev) {
|
||||||
|
var self = this;
|
||||||
|
var savePromises = [];
|
||||||
|
|
||||||
|
// XXX: this is managed in ChangeAvatar.js, although could be moved out here in order
|
||||||
|
// to allow for the change to be staged alongside the rest of the form.
|
||||||
|
//
|
||||||
|
// if (this.state.originalState.avatarUrl !== this.state.avatarUrl) {
|
||||||
|
// savePromises.push( UserSettingsStore.saveAvatarUrl(this.state.avatarUrl) );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (this.state.originalState.displayName !== this.state.displayName) {
|
||||||
|
savePromises.push( UserSettingsStore.saveDisplayName(this.state.displayName) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.originalState.threepids.length !== this.state.threepids.length ||
|
||||||
|
this.state.originalState.threepids.every(function(element, index) {
|
||||||
|
return element === this.state.threepids[index];
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
savePromises.push( UserSettingsStore.saveThreePids(this.state.threepids) );
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setState({
|
||||||
|
phase: self.Phases.Saving,
|
||||||
|
});
|
||||||
|
|
||||||
|
q.all(savePromises).then(
|
||||||
|
function(resps) {
|
||||||
|
self.setState({
|
||||||
|
phase: self.Phases.Display,
|
||||||
|
});
|
||||||
|
self.onClose();
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
self.setState({
|
||||||
|
phase: self.Phases.Display,
|
||||||
|
});
|
||||||
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Can't save user settings",
|
||||||
|
description: error.toString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose: function(ev) {
|
||||||
|
// XXX: use browser history instead to find the previous room?
|
||||||
|
if (this.props.roomId) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
room_id: this.props.roomId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_indexed_room',
|
||||||
|
roomIndex: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onAction: function(payload) {
|
||||||
|
if (payload.action === "notifier_enabled") {
|
||||||
|
this.setState({
|
||||||
|
enableNotifications : UserSettingsStore.getEnableNotifications()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
editAvatar: function() {
|
editAvatar: function() {
|
||||||
var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl);
|
var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl);
|
||||||
|
@ -39,20 +166,11 @@ module.exports = React.createClass({
|
||||||
this.avatarDialog = Modal.createDialogWithElement(avatarDialog);
|
this.avatarDialog = Modal.createDialogWithElement(avatarDialog);
|
||||||
},
|
},
|
||||||
|
|
||||||
addEmail: function() {
|
onAvatarDialogCancel: function() {
|
||||||
|
this.avatarDialog.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
editDisplayName: function() {
|
onLogoutClicked: function(event) {
|
||||||
this.refs.displayname.edit();
|
|
||||||
},
|
|
||||||
|
|
||||||
changePassword: function() {
|
|
||||||
var ChangePassword = sdk.getComponent('molecules.ChangePassword');
|
|
||||||
Modal.createDialog(ChangePassword);
|
|
||||||
},
|
|
||||||
|
|
||||||
onLogoutClicked: function(ev) {
|
|
||||||
var LogoutPrompt = sdk.getComponent('organisms.LogoutPrompt');
|
var LogoutPrompt = sdk.getComponent('organisms.LogoutPrompt');
|
||||||
this.logoutModal = Modal.createDialog(LogoutPrompt, {onCancel: this.onLogoutPromptCancel});
|
this.logoutModal = Modal.createDialog(LogoutPrompt, {onCancel: this.onLogoutPromptCancel});
|
||||||
},
|
},
|
||||||
|
@ -61,62 +179,115 @@ module.exports = React.createClass({
|
||||||
this.logoutModal.closeDialog();
|
this.logoutModal.closeDialog();
|
||||||
},
|
},
|
||||||
|
|
||||||
onAvatarDialogCancel: function() {
|
onDisplayNameChange: function(event) {
|
||||||
this.avatarDialog.close();
|
this.setState({ displayName: event.target.value });
|
||||||
|
},
|
||||||
|
|
||||||
|
onEnableNotificationsChange: function(event) {
|
||||||
|
// don't bother waiting for Save to be clicked, as that'd be silly
|
||||||
|
UserSettingsStore.setEnableNotifications( this.refs.enableNotifications.value );
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
enableNotifications : UserSettingsStore.getEnableNotifications()
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var Loader = sdk.getComponent("atoms.Spinner");
|
var Loader = sdk.getComponent("atoms.Spinner");
|
||||||
|
var saving;
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case this.Phases.Loading:
|
case this.Phases.Loading:
|
||||||
return <Loader />
|
return <Loader />
|
||||||
|
case this.Phases.Saving:
|
||||||
|
saving = <Loader />
|
||||||
case this.Phases.Display:
|
case this.Phases.Display:
|
||||||
var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName');
|
var RoomHeader = sdk.getComponent('molecules.RoomHeader');
|
||||||
var EnableNotificationsButton = sdk.getComponent('atoms.EnableNotificationsButton');
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_UserSettings">
|
<div className="mx_UserSettings">
|
||||||
<div className="mx_UserSettings_User">
|
<RoomHeader simpleHeader="Settings" onCancelClick={ this.onClose } />
|
||||||
<h1>User Settings</h1>
|
|
||||||
<hr/>
|
<h2>Profile</h2>
|
||||||
<div className="mx_UserSettings_User_Inner">
|
|
||||||
<div className="mx_UserSettings_Avatar">
|
<div className="mx_UserSettings_section">
|
||||||
<div className="mx_UserSettings_Avatar_Text">Profile Photo</div>
|
<div className="mx_UserSettings_profileTable">
|
||||||
<div className="mx_UserSettings_Avatar_Edit" onClick={this.editAvatar}>Edit</div>
|
<div className="mx_UserSettings_profileTableRow">
|
||||||
|
<div className="mx_UserSettings_profileLabelCell">
|
||||||
|
<label htmlFor="displayName">Display name</label>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserSettings_profileInputCell">
|
||||||
|
<input id="displayName" ref="displayName" value={ this.state.displayName } onChange={ this.onDisplayNameChange } />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_UserSettings_DisplayName">
|
{this.state.threepids.map(function(val) {
|
||||||
<ChangeDisplayName ref="displayname" />
|
var id = "email-" + val.address;
|
||||||
<div className="mx_UserSettings_DisplayName_Edit" onClick={this.editDisplayName}>Edit</div>
|
return (
|
||||||
|
<div className="mx_UserSettings_profileTableRow">
|
||||||
|
<div className="mx_UserSettings_profileLabelCell">
|
||||||
|
<label htmlFor={ id }>Email</label>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserSettings_profileInputCell">
|
||||||
|
<input key={val.address} id={ id } value={ val.address } disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<div className="mx_UserSettings_profileTableRow">
|
||||||
|
<div className="mx_UserSettings_profileLabelCell">
|
||||||
|
<label htmlFor="password1">New password</label>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserSettings_profileInputCell">
|
||||||
|
<input id="password1" ref="password1" value={ this.state.password1 } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserSettings_profileTableRow">
|
||||||
|
<div className="mx_UserSettings_profileLabelCell">
|
||||||
|
<label htmlFor="password2">Confirm new password</label>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserSettings_profileInputCell">
|
||||||
|
<input id="password2" ref="password2" value={ this.state.password2 } />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_UserSettings_3pids">
|
</div>
|
||||||
{this.state.threepids.map(function(val) {
|
|
||||||
return <div key={val.address}>{val.address}</div>;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mx_UserSettings_Add3pid" onClick={this.addEmail}>Add email</div>
|
<div className="mx_UserSettings_avatarPicker">
|
||||||
|
<div className="mx_UserSettings_avatarPicker_edit" onClick={this.editAvatar}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_UserSettings_Global">
|
<div className="mx_UserSettings_logout">
|
||||||
<h1>Global Settings</h1>
|
<div className="mx_UserSettings_button" onClick={this.onLogoutClicked}>Log out</div>
|
||||||
<hr/>
|
</div>
|
||||||
<div className="mx_UserSettings_Global_Inner">
|
|
||||||
<div className="mx_UserSettings_ChangePassword" onClick={this.changePassword}>
|
<h2>Notifications</h2>
|
||||||
Change Password
|
|
||||||
</div>
|
<div className="mx_UserSettings_section">
|
||||||
<div className="mx_UserSettings_ClientVersion">
|
<div className="mx_UserSettings_notifTable">
|
||||||
Version {this.state.clientVersion}
|
<div className="mx_UserSettings_notifTableRow">
|
||||||
</div>
|
<div className="mx_UserSettings_notifInputCell">
|
||||||
<div className="mx_UserSettings_EnableNotifications">
|
<input id="enableNotifications" ref="enableNotifications" type="checkbox" checked={ this.state.enableNotifications } onChange={ this.onEnableNotificationsChange } />
|
||||||
<EnableNotificationsButton />
|
</div>
|
||||||
</div>
|
<div className="mx_UserSettings_notifLabelCell">
|
||||||
<div className="mx_UserSettings_Logout">
|
<label htmlFor="enableNotifications">Enable desktop notifications</label>
|
||||||
<button onClick={this.onLogoutClicked}>Sign Out</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2>Advanced</h2>
|
||||||
|
|
||||||
|
<div className="mx_UserSettings_section">
|
||||||
|
<div className="mx_UserSettings_advanced">
|
||||||
|
Version {this.state.clientVersion}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mx_UserSettings_save">
|
||||||
|
<div className="mx_UserSettings_spinner">{ saving }</div>
|
||||||
|
<div className="mx_UserSettings_button" onClick={this.onSaveClicked}>Save and close</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ module.exports = React.createClass({
|
||||||
right_panel = <RightPanel roomId={this.state.currentRoom} collapsed={this.state.collapse_rhs} />
|
right_panel = <RightPanel roomId={this.state.currentRoom} collapsed={this.state.collapse_rhs} />
|
||||||
break;
|
break;
|
||||||
case this.PageTypes.UserSettings:
|
case this.PageTypes.UserSettings:
|
||||||
page_element = <UserSettings />
|
page_element = <UserSettings roomId={this.state.currentRoom} />
|
||||||
right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
|
right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
|
||||||
break;
|
break;
|
||||||
case this.PageTypes.CreateRoom:
|
case this.PageTypes.CreateRoom:
|
||||||
|
|
Loading…
Reference in New Issue