mirror of https://github.com/vector-im/riot-web
				
				
				
			Move all of the UserMenu into the UserMenu component
							parent
							
								
									dafce40d1b
								
							
						
					
					
						commit
						bcfdd4d984
					
				|  | @ -54,7 +54,7 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations | |||
|             display: flex; | ||||
|             flex-direction: column; | ||||
| 
 | ||||
|             // There's 2 rows when breadcrumbs are present: the top bit and the breadcrumbs | ||||
|             // This is basically just breadcrumbs. The row above that is handled by the UserMenu | ||||
|             .mx_LeftPanel2_headerRow { | ||||
|                 // Create yet another flexbox, this time within the row, to ensure items stay | ||||
|                 // aligned correctly. This is also a row-based flexbox. | ||||
|  | @ -62,32 +62,6 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations | |||
|                 align-items: center; | ||||
|             } | ||||
| 
 | ||||
|             .mx_LeftPanel2_userAvatarContainer { | ||||
|                 position: relative; // to make default avatars work | ||||
|                 margin-right: 8px; | ||||
|                 height: 32px; // to remove the unknown 4px gap the browser puts below it | ||||
| 
 | ||||
|                 .mx_LeftPanel2_userAvatar { | ||||
|                     border-radius: 32px; // should match avatar size | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_LeftPanel2_userName { | ||||
|                 font-weight: 600; | ||||
|                 font-size: $font-15px; | ||||
|                 line-height: $font-20px; | ||||
|                 flex: 1; | ||||
| 
 | ||||
|                 // Ellipsize any text overflow | ||||
|                 text-overflow: ellipsis; | ||||
|                 overflow: hidden; | ||||
|                 white-space: nowrap; | ||||
|             } | ||||
| 
 | ||||
|             .mx_LeftPanel2_headerButtons { | ||||
|                 // No special styles: the rest of the layout happens to make it work. | ||||
|             } | ||||
| 
 | ||||
|             .mx_LeftPanel2_breadcrumbsContainer { | ||||
|                 width: 100%; | ||||
|                 overflow: hidden; | ||||
|  |  | |||
|  | @ -14,6 +14,38 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| .mx_UserMenu { | ||||
|     // Create a row-based flexbox to ensure items stay aligned correctly. | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
| 
 | ||||
|     .mx_UserMenu_userAvatarContainer { | ||||
|         position: relative; // to make default avatars work | ||||
|         margin-right: 8px; | ||||
|         height: 32px; // to remove the unknown 4px gap the browser puts below it | ||||
| 
 | ||||
|         .mx_UserMenu_userAvatar { | ||||
|             border-radius: 32px; // should match avatar size | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenu_userName { | ||||
|         font-weight: 600; | ||||
|         font-size: $font-15px; | ||||
|         line-height: $font-20px; | ||||
|         flex: 1; | ||||
| 
 | ||||
|         // Ellipsize any text overflow | ||||
|         text-overflow: ellipsis; | ||||
|         overflow: hidden; | ||||
|         white-space: nowrap; | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenu_headerButtons { | ||||
|         // No special styles: the rest of the layout happens to make it work. | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .mx_UserMenuButton { | ||||
|     > span { | ||||
|         width: 16px; | ||||
|  | @ -37,10 +69,10 @@ limitations under the License. | |||
|     } | ||||
| } | ||||
| 
 | ||||
| .mx_UserMenuButton_contextMenu { | ||||
| .mx_UserMenu_contextMenu { | ||||
|     width: 247px; | ||||
| 
 | ||||
|     .mx_UserMenuButton_contextMenu_redRow { | ||||
|     .mx_UserMenu_contextMenu_redRow { | ||||
|         .mx_AccessibleButton { | ||||
|             color: $warning-color !important; // !important to override styles from context menu | ||||
|         } | ||||
|  | @ -50,12 +82,12 @@ limitations under the License. | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_contextMenu_header { | ||||
|     .mx_UserMenu_contextMenu_header { | ||||
|         // Create a flexbox to organize the header a bit easier | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
| 
 | ||||
|         .mx_UserMenuButton_contextMenu_name { | ||||
|         .mx_UserMenu_contextMenu_name { | ||||
|             // Create another flexbox of columns to handle large user IDs | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|  | @ -72,19 +104,19 @@ limitations under the License. | |||
|                 white-space: nowrap; | ||||
|             } | ||||
| 
 | ||||
|             .mx_UserMenuButton_contextMenu_displayName { | ||||
|             .mx_UserMenu_contextMenu_displayName { | ||||
|                 font-weight: bold; | ||||
|                 font-size: $font-15px; | ||||
|                 line-height: $font-20px; | ||||
|             } | ||||
| 
 | ||||
|             .mx_UserMenuButton_contextMenu_userId { | ||||
|             .mx_UserMenu_contextMenu_userId { | ||||
|                 font-size: $font-15px; | ||||
|                 line-height: $font-24px; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         .mx_UserMenuButton_contextMenu_themeButton { | ||||
|         .mx_UserMenu_contextMenu_themeButton { | ||||
|             min-width: 32px; | ||||
|             max-width: 32px; | ||||
|             width: 32px; | ||||
|  | @ -118,31 +150,31 @@ limitations under the License. | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconHome::before { | ||||
|     .mx_UserMenu_iconHome::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/home.svg'); | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconBell::before { | ||||
|     .mx_UserMenu_iconBell::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/notifications.svg'); | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconLock::before { | ||||
|     .mx_UserMenu_iconLock::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/lock.svg'); | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconSettings::before { | ||||
|     .mx_UserMenu_iconSettings::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/settings.svg'); | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconArchive::before { | ||||
|     .mx_UserMenu_iconArchive::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/archive.svg'); | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconMessage::before { | ||||
|     .mx_UserMenu_iconMessage::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/message-circle.svg'); | ||||
|     } | ||||
| 
 | ||||
|     .mx_UserMenuButton_iconSignOut::before { | ||||
|     .mx_UserMenu_iconSignOut::before { | ||||
|         mask-image: url('$(res)/img/feather-customised/sign-out.svg'); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -22,18 +22,13 @@ import dis from "../../dispatcher/dispatcher"; | |||
| import { _t } from "../../languageHandler"; | ||||
| import RoomList2 from "../views/rooms/RoomList2"; | ||||
| import { Action } from "../../dispatcher/actions"; | ||||
| import { MatrixClientPeg } from "../../MatrixClientPeg"; | ||||
| import BaseAvatar from '../views/avatars/BaseAvatar'; | ||||
| import UserMenu from "./UserMenuButton"; | ||||
| import UserMenu from "./UserMenu"; | ||||
| import RoomSearch from "./RoomSearch"; | ||||
| import AccessibleButton from "../views/elements/AccessibleButton"; | ||||
| import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2"; | ||||
| import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore"; | ||||
| import { UPDATE_EVENT } from "../../stores/AsyncStore"; | ||||
| import ResizeNotifier from "../../utils/ResizeNotifier"; | ||||
| import { MatrixEvent } from "matrix-js-sdk/src/models/event"; | ||||
| import { throttle } from 'lodash'; | ||||
| import { OwnProfileStore } from "../../stores/OwnProfileStore"; | ||||
| 
 | ||||
| /******************************************************************* | ||||
|  *   CAUTION                                                       * | ||||
|  | @ -76,32 +71,13 @@ export default class LeftPanel2 extends React.Component<IProps, IState> { | |||
|         // We watch the middle panel because we don't actually get resized, the middle panel does.
 | ||||
|         // We listen to the noisy channel to avoid choppy reaction times.
 | ||||
|         this.props.resizeNotifier.on("middlePanelResizedNoisy", this.onResize); | ||||
| 
 | ||||
|         OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); | ||||
|     } | ||||
| 
 | ||||
|     public componentWillUnmount() { | ||||
|         BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); | ||||
|         this.props.resizeNotifier.off("middlePanelResizedNoisy", this.onResize); | ||||
|         OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); | ||||
|     } | ||||
| 
 | ||||
|     // TSLint wants this to be a member, but we don't want that.
 | ||||
|     // tslint:disable-next-line
 | ||||
|     private onRoomStateUpdate = throttle((ev: MatrixEvent) => { | ||||
|         const myUserId = MatrixClientPeg.get().getUserId(); | ||||
|         if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) { | ||||
|             // noinspection JSIgnoredPromiseFromCall
 | ||||
|             this.onProfileUpdate(); | ||||
|         } | ||||
|     }, 200, {trailing: true, leading: true}); | ||||
| 
 | ||||
|     private onProfileUpdate = async () => { | ||||
|         // the store triggered an update, so force a layout update. We don't
 | ||||
|         // have any state to store here for that to magically happen.
 | ||||
|         this.forceUpdate(); | ||||
|     }; | ||||
| 
 | ||||
|     private onSearch = (term: string): void => { | ||||
|         this.setState({searchFilter: term}); | ||||
|     }; | ||||
|  | @ -170,7 +146,6 @@ export default class LeftPanel2 extends React.Component<IProps, IState> { | |||
|         // TODO: Presence
 | ||||
|         // TODO: Breadcrumbs toggle
 | ||||
|         // TODO: Menu button
 | ||||
|         const avatarSize = 32; // should match border-radius of the avatar
 | ||||
| 
 | ||||
|         let breadcrumbs; | ||||
|         if (this.state.showBreadcrumbs) { | ||||
|  | @ -181,34 +156,9 @@ export default class LeftPanel2 extends React.Component<IProps, IState> { | |||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         let name = <span className="mx_LeftPanel2_userName">{OwnProfileStore.instance.displayName}</span>; | ||||
|         let buttons = ( | ||||
|             <span className="mx_LeftPanel2_headerButtons"> | ||||
|                 <UserMenu /> | ||||
|             </span> | ||||
|         ); | ||||
|         if (this.props.isMinimized) { | ||||
|             name = null; | ||||
|             buttons = null; | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_LeftPanel2_userHeader"> | ||||
|                 <div className="mx_LeftPanel2_headerRow"> | ||||
|                     <span className="mx_LeftPanel2_userAvatarContainer"> | ||||
|                         <BaseAvatar | ||||
|                             idName={MatrixClientPeg.get().getUserId()} | ||||
|                             name={OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId()} | ||||
|                             url={OwnProfileStore.instance.getHttpAvatarUrl(avatarSize)} | ||||
|                             width={avatarSize} | ||||
|                             height={avatarSize} | ||||
|                             resizeMethod="crop" | ||||
|                             className="mx_LeftPanel2_userAvatar" | ||||
|                         /> | ||||
|                     </span> | ||||
|                     {name} | ||||
|                     {buttons} | ||||
|                 </div> | ||||
|                 <UserMenu isMinimized={this.props.isMinimized} /> | ||||
|                 {breadcrumbs} | ||||
|             </div> | ||||
|         ); | ||||
|  |  | |||
|  | @ -35,8 +35,10 @@ import SdkConfig from "../../SdkConfig"; | |||
| import {getHomePageUrl} from "../../utils/pages"; | ||||
| import { OwnProfileStore } from "../../stores/OwnProfileStore"; | ||||
| import { UPDATE_EVENT } from "../../stores/AsyncStore"; | ||||
| import BaseAvatar from '../views/avatars/BaseAvatar'; | ||||
| 
 | ||||
| interface IProps { | ||||
|     isMinimized: boolean; | ||||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|  | @ -158,14 +160,14 @@ export default class UserMenu extends React.Component<IProps, IState> { | |||
|         defaultDispatcher.dispatch({action: 'view_home_page'}); | ||||
|     }; | ||||
| 
 | ||||
|     public render() { | ||||
|     private renderMenuButton(): React.ReactNode { | ||||
|         let contextMenu; | ||||
|         if (this.state.menuDisplayed) { | ||||
|             let hostingLink; | ||||
|             const signupLink = getHostingLink("user-context-menu"); | ||||
|             if (signupLink) { | ||||
|                 hostingLink = ( | ||||
|                     <div className="mx_UserMenuButton_contextMenu_header"> | ||||
|                     <div className="mx_UserMenu_contextMenu_header"> | ||||
|                         {_t( | ||||
|                             "<a>Upgrade</a> to your own domain", {}, | ||||
|                             { | ||||
|  | @ -188,7 +190,7 @@ export default class UserMenu extends React.Component<IProps, IState> { | |||
|                 homeButton = ( | ||||
|                     <li> | ||||
|                         <AccessibleButton onClick={this.onHomeClick}> | ||||
|                             <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconHome" /> | ||||
|                             <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconHome" /> | ||||
|                             <span>{_t("Home")}</span> | ||||
|                         </AccessibleButton> | ||||
|                     </li> | ||||
|  | @ -203,18 +205,18 @@ export default class UserMenu extends React.Component<IProps, IState> { | |||
|                     top={elementRect.top + elementRect.height} | ||||
|                     onFinished={this.onCloseMenu} | ||||
|                 > | ||||
|                     <div className="mx_IconizedContextMenu mx_UserMenuButton_contextMenu"> | ||||
|                         <div className="mx_UserMenuButton_contextMenu_header"> | ||||
|                             <div className="mx_UserMenuButton_contextMenu_name"> | ||||
|                                 <span className="mx_UserMenuButton_contextMenu_displayName"> | ||||
|                     <div className="mx_IconizedContextMenu mx_UserMenu_contextMenu"> | ||||
|                         <div className="mx_UserMenu_contextMenu_header"> | ||||
|                             <div className="mx_UserMenu_contextMenu_name"> | ||||
|                                 <span className="mx_UserMenu_contextMenu_displayName"> | ||||
|                                     {OwnProfileStore.instance.displayName} | ||||
|                                 </span> | ||||
|                                 <span className="mx_UserMenuButton_contextMenu_userId"> | ||||
|                                 <span className="mx_UserMenu_contextMenu_userId"> | ||||
|                                     {MatrixClientPeg.get().getUserId()} | ||||
|                                 </span> | ||||
|                             </div> | ||||
|                             <div | ||||
|                                 className="mx_UserMenuButton_contextMenu_themeButton" | ||||
|                                 className="mx_UserMenu_contextMenu_themeButton" | ||||
|                                 onClick={this.onSwitchThemeClick} | ||||
|                                 title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")} | ||||
|                             > | ||||
|  | @ -231,31 +233,31 @@ export default class UserMenu extends React.Component<IProps, IState> { | |||
|                                 {homeButton} | ||||
|                                 <li> | ||||
|                                     <AccessibleButton onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconBell" /> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconBell" /> | ||||
|                                         <span>{_t("Notification settings")}</span> | ||||
|                                     </AccessibleButton> | ||||
|                                 </li> | ||||
|                                 <li> | ||||
|                                     <AccessibleButton onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconLock" /> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconLock" /> | ||||
|                                         <span>{_t("Security & privacy")}</span> | ||||
|                                     </AccessibleButton> | ||||
|                                 </li> | ||||
|                                 <li> | ||||
|                                     <AccessibleButton onClick={(e) => this.onSettingsOpen(e, null)}> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconSettings" /> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconSettings" /> | ||||
|                                         <span>{_t("All settings")}</span> | ||||
|                                     </AccessibleButton> | ||||
|                                 </li> | ||||
|                                 <li> | ||||
|                                     <AccessibleButton onClick={this.onShowArchived}> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconArchive" /> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconArchive" /> | ||||
|                                         <span>{_t("Archived rooms")}</span> | ||||
|                                     </AccessibleButton> | ||||
|                                 </li> | ||||
|                                 <li> | ||||
|                                     <AccessibleButton onClick={this.onProvideFeedback}> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconMessage" /> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconMessage" /> | ||||
|                                         <span>{_t("Feedback")}</span> | ||||
|                                     </AccessibleButton> | ||||
|                                 </li> | ||||
|  | @ -263,9 +265,9 @@ export default class UserMenu extends React.Component<IProps, IState> { | |||
|                         </div> | ||||
|                         <div className="mx_IconizedContextMenu_optionList"> | ||||
|                             <ul> | ||||
|                                 <li className="mx_UserMenuButton_contextMenu_redRow"> | ||||
|                                 <li className="mx_UserMenu_contextMenu_redRow"> | ||||
|                                     <AccessibleButton onClick={this.onSignOutClick}> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenuButton_iconSignOut" /> | ||||
|                                         <span className="mx_IconizedContextMenu_icon mx_UserMenu_iconSignOut" /> | ||||
|                                         <span>{_t("Sign out")}</span> | ||||
|                                     </AccessibleButton> | ||||
|                                 </li> | ||||
|  | @ -291,4 +293,37 @@ export default class UserMenu extends React.Component<IProps, IState> { | |||
|             </React.Fragment> | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public render() { | ||||
|         const avatarSize = 32; // should match border-radius of the avatar
 | ||||
| 
 | ||||
|         let name = <span className="mx_UserMenu_userName">{OwnProfileStore.instance.displayName}</span>; | ||||
|         let buttons = ( | ||||
|             <span className="mx_UserMenu_headerButtons"> | ||||
|                 {this.renderMenuButton()} | ||||
|             </span> | ||||
|         ); | ||||
|         if (this.props.isMinimized) { | ||||
|             name = null; | ||||
|             buttons = null; | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_UserMenu"> | ||||
|                     <span className="mx_UserMenu_userAvatarContainer"> | ||||
|                         <BaseAvatar | ||||
|                             idName={MatrixClientPeg.get().getUserId()} | ||||
|                             name={OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId()} | ||||
|                             url={OwnProfileStore.instance.getHttpAvatarUrl(avatarSize)} | ||||
|                             width={avatarSize} | ||||
|                             height={avatarSize} | ||||
|                             resizeMethod="crop" | ||||
|                             className="mx_UserMenu_userAvatar" | ||||
|                         /> | ||||
|                     </span> | ||||
|                 {name} | ||||
|                 {buttons} | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston