mirror of https://github.com/vector-im/riot-web
				
				
				
			Finish the box for displayname/avatar changes
							parent
							
								
									de81c8d768
								
							
						
					
					
						commit
						f643d7a143
					
				|  | @ -14,12 +14,78 @@ | |||
|   width: 88px; | ||||
|   height: 88px; | ||||
|   margin-left: 13px; | ||||
|   position: relative; | ||||
|   cursor: pointer; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatar div { | ||||
| .mx_GeneralSettingsTab_profileAvatar > * { | ||||
|   display: block; | ||||
|   width: 88px; | ||||
|   height: 88px; | ||||
|   border-radius: 4px; | ||||
|   background-color: #ccc; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatar .mx_GeneralSettingsTab_profileAvatarPlaceholder { | ||||
|   background-color: $settings-profile-placeholder-bg-color; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatarOverlay { | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   display: none; | ||||
|   text-align: center; | ||||
|   vertical-align: middle; | ||||
|   font-size: 10px; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatar:hover .mx_GeneralSettingsTab_profileAvatarOverlay { | ||||
|   display: inline-block; | ||||
|   opacity: 0.5 !important; | ||||
|   color: $settings-profile-overlay-fg-color !important; | ||||
|   background-color: $settings-profile-overlay-bg-color !important; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatarOverlay_show { | ||||
|   display: inline-block; | ||||
|   opacity: 1; | ||||
|   color: $settings-profile-overlay-placeholder-fg-color; | ||||
|   background-color: $settings-profile-overlay-placeholder-bg-color; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatarOverlayText { | ||||
|   display: block; | ||||
|   margin-top: 17px; | ||||
|   margin-bottom: 8px; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatarOverlayImgContainer { | ||||
|   position: relative; | ||||
|   width: 14px; | ||||
|   height: 14px; | ||||
|   margin: auto; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatarOverlayImg:before { | ||||
|   background-color: $settings-profile-overlay-placeholder-fg-color; | ||||
|   mask: url("$(res)/img/feather-icons/upload.svg"); | ||||
|   mask-repeat: no-repeat; | ||||
|   mask-size: 14px; | ||||
|   mask-position: center; | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatar:hover .mx_GeneralSettingsTab_profileAvatarOverlayImg:before { | ||||
|   background-color: $settings-profile-overlay-fg-color !important; | ||||
| } | ||||
| 
 | ||||
| .mx_GeneralSettingsTab_profileAvatarUpload { | ||||
|   display: none; | ||||
| } | ||||
|  | @ -0,0 +1,5 @@ | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"> | ||||
|     <g fill="none" fill-rule="evenodd" stroke="#454545" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"> | ||||
|         <path d="M13 9v2.667c0 .736-.597 1.333-1.333 1.333H2.333A1.333 1.333 0 0 1 1 11.667V9M10.667 4.333L7.333 1 4 4.333M7.333 1v8"/> | ||||
|     </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 359 B | 
|  | @ -120,6 +120,11 @@ $blockquote-bar-color: #ddd; | |||
| $blockquote-fg-color: #777; | ||||
| 
 | ||||
| $settings-grey-fg-color: #a2a2a2; | ||||
| $settings-profile-placeholder-bg-color: #e7e7e7; | ||||
| $settings-profile-overlay-bg-color: #000; | ||||
| $settings-profile-overlay-placeholder-bg-color: transparent; | ||||
| $settings-profile-overlay-fg-color: #fff; | ||||
| $settings-profile-overlay-placeholder-fg-color: #454545; | ||||
| 
 | ||||
| $voip-decline-color: #f48080; | ||||
| $voip-accept-color: #80f480; | ||||
|  |  | |||
|  | @ -113,6 +113,11 @@ $blockquote-bar-color: #ddd; | |||
| $blockquote-fg-color: #777; | ||||
| 
 | ||||
| $settings-grey-fg-color: #a2a2a2; | ||||
| $settings-profile-placeholder-bg-color: #e7e7e7; | ||||
| $settings-profile-overlay-bg-color: #000; | ||||
| $settings-profile-overlay-placeholder-bg-color: transparent; | ||||
| $settings-profile-overlay-fg-color: #fff; | ||||
| $settings-profile-overlay-placeholder-fg-color: #454545; | ||||
| 
 | ||||
| $voip-decline-color: #f48080; | ||||
| $voip-accept-color: #80f480; | ||||
|  |  | |||
|  | @ -19,19 +19,34 @@ import {_t} from "../../../../languageHandler"; | |||
| import MatrixClientPeg from "../../../../MatrixClientPeg"; | ||||
| import Field from "../../elements/Field"; | ||||
| import AccessibleButton from "../../elements/AccessibleButton"; | ||||
| import classNames from 'classnames'; | ||||
| 
 | ||||
| export default class GeneralSettingsTab extends React.Component { | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         const user = client.getUser(client.getUserId()); | ||||
|         let avatarUrl = user.avatarUrl; | ||||
|         if (avatarUrl) avatarUrl = client.mxcUrlToHttp(avatarUrl, 96, 96, 'crop', false); | ||||
|         this.state = { | ||||
|             userId: client.getUserId(), | ||||
|             displayName: client.getUser(client.getUserId()).displayName, | ||||
|             userId: user.userId, | ||||
|             originalDisplayName: user.displayName, | ||||
|             displayName: user.displayName, | ||||
|             originalAvatarUrl: avatarUrl, | ||||
|             avatarUrl: avatarUrl, | ||||
|             avatarFile: null, | ||||
|             enableProfileSave: false, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     _uploadAvatar = (e) => { | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
| 
 | ||||
|         this.refs.avatarUpload.click(); | ||||
|     }; | ||||
| 
 | ||||
|     _saveProfile = async (e) => { | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
|  | @ -39,12 +54,26 @@ export default class GeneralSettingsTab extends React.Component { | |||
|         if (!this.state.enableProfileSave) return; | ||||
|         this.setState({enableProfileSave: false}); | ||||
| 
 | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         const newState = {}; | ||||
| 
 | ||||
|         // TODO: What do we do about errors?
 | ||||
|         await MatrixClientPeg.get().setDisplayName(this.state.displayName); | ||||
| 
 | ||||
|         // TODO: Support avatars
 | ||||
|         if (this.state.originalDisplayName !== this.state.displayName) { | ||||
|             await client.setDisplayName(this.state.displayName); | ||||
|             newState.originalDisplayName = this.state.displayName; | ||||
|         } | ||||
| 
 | ||||
|         this.setState({enableProfileSave: true}); | ||||
|         if (this.state.avatarFile) { | ||||
|             const uri = await client.uploadContent(this.state.avatarFile); | ||||
|             await client.setAvatarUrl(uri); | ||||
|             newState.avatarUrl = client.mxcUrlToHttp(uri, 96, 96, 'crop', false); | ||||
|             newState.originalAvatarUrl = newState.avatarUrl; | ||||
|             newState.avatarFile = null; | ||||
|         } | ||||
| 
 | ||||
|         newState.enableProfileSave = true; | ||||
|         this.setState(newState); | ||||
|     }; | ||||
| 
 | ||||
|     _onDisplayNameChanged = (e) => { | ||||
|  | @ -54,19 +83,66 @@ export default class GeneralSettingsTab extends React.Component { | |||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     _onAvatarChanged = (e) => { | ||||
|         if (!e.target.files || !e.target.files.length) { | ||||
|             this.setState({ | ||||
|                 avatarUrl: this.state.originalAvatarUrl, | ||||
|                 avatarFile: null, | ||||
|                 enableProfileSave: false, | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const file = e.target.files[0]; | ||||
|         const reader = new FileReader(); | ||||
|         reader.onload = (ev) => { | ||||
|             this.setState({ | ||||
|                 avatarUrl: ev.target.result, | ||||
|                 avatarFile: file, | ||||
|                 enableProfileSave: true, | ||||
|             }); | ||||
|         }; | ||||
|         reader.readAsDataURL(file); | ||||
|     }; | ||||
| 
 | ||||
|     _renderProfileSection() { | ||||
|         // TODO: Ditch avatar placeholder and use the real thing
 | ||||
|         // TODO: Why is rendering a box with an overlay so complicated? Can the DOM be reduced?
 | ||||
| 
 | ||||
|         let showOverlayAnyways = true; | ||||
|         let avatarElement = <div className="mx_GeneralSettingsTab_profileAvatarPlaceholder" />; | ||||
|         if (this.state.avatarUrl) { | ||||
|             showOverlayAnyways = false; | ||||
|             avatarElement = <img src={this.state.avatarUrl} className="mx_GeneralSettingsTab_profileAvatarImg" | ||||
|                                  alt={_t("Profile picture")}/> | ||||
|         } | ||||
| 
 | ||||
|         const avatarOverlayClasses = classNames({ | ||||
|             "mx_GeneralSettingsTab_profileAvatarOverlay": true, | ||||
|             "mx_GeneralSettingsTab_profileAvatarOverlay_show": showOverlayAnyways, | ||||
|         }); | ||||
|         let avatarHoverElement = ( | ||||
|             <div className={avatarOverlayClasses} onClick={this._uploadAvatar}> | ||||
|                 <span className="mx_GeneralSettingsTab_profileAvatarOverlayText">{_t("Upload profile picture")}</span> | ||||
|                 <div className="mx_GeneralSettingsTab_profileAvatarOverlayImgContainer"> | ||||
|                     <div className="mx_GeneralSettingsTab_profileAvatarOverlayImg" /> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
| 
 | ||||
|         const form = ( | ||||
|             <form onSubmit={this._saveProfile} autoComplete={false} noValidate={true}> | ||||
|                 <input type="file" ref="avatarUpload" className="mx_GeneralSettingsTab_profileAvatarUpload" | ||||
|                        onChange={this._onAvatarChanged} accept="image/*" /> | ||||
|                 <div className="mx_GeneralSettingsTab_profile"> | ||||
|                     <div className="mx_GeneralSettingsTab_profileControls"> | ||||
|                         <p className="mx_GeneralSettingsTab_profileUsername">{this.state.userId}</p> | ||||
|                         <Field id="profileDisplayName" label={_t("Display Name")} | ||||
|                                type="text" value={this.state.displayName} autocomplete="off" | ||||
|                                type="text" value={this.state.displayName} autoComplete="off" | ||||
|                                onChange={this._onDisplayNameChanged} /> | ||||
|                     </div> | ||||
|                     <div className="mx_GeneralSettingsTab_profileAvatar"> | ||||
|                         <div /> | ||||
|                         {avatarElement} | ||||
|                         {avatarHoverElement} | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <AccessibleButton onClick={this._saveProfile} kind="primary" | ||||
|  |  | |||
|  | @ -402,6 +402,9 @@ | |||
|     "Off": "Off", | ||||
|     "On": "On", | ||||
|     "Noisy": "Noisy", | ||||
|     "Profile picture": "Profile picture", | ||||
|     "Upload profile picture": "Upload profile picture", | ||||
|     "Upload": "Upload", | ||||
|     "Display Name": "Display Name", | ||||
|     "Save": "Save", | ||||
|     "Profile": "Profile", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston