diff --git a/res/css/views/settings/tabs/_GeneralSettingsTab.scss b/res/css/views/settings/tabs/_GeneralSettingsTab.scss index 8bc3de8689..caacd0d3c7 100644 --- a/res/css/views/settings/tabs/_GeneralSettingsTab.scss +++ b/res/css/views/settings/tabs/_GeneralSettingsTab.scss @@ -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; } \ No newline at end of file diff --git a/res/img/feather-icons/upload.svg b/res/img/feather-icons/upload.svg new file mode 100644 index 0000000000..8ec5d95c2c --- /dev/null +++ b/res/img/feather-icons/upload.svg @@ -0,0 +1,5 @@ + diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 235eb02e3c..ed5620d30f 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -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; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 1522ee82a0..d7b135a74e 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -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; diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index c248ba23dd..993061eaa0 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -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 =
; + if (this.state.avatarUrl) { + showOverlayAnyways = false; + avatarElement = + } + + const avatarOverlayClasses = classNames({ + "mx_GeneralSettingsTab_profileAvatarOverlay": true, + "mx_GeneralSettingsTab_profileAvatarOverlay_show": showOverlayAnyways, + }); + let avatarHoverElement = ( +