Merge pull request #2748 from matrix-org/dbkr/shameless_plugging
Support linking to hosting providerspull/21833/head
						commit
						c1056025ed
					
				|  | @ -62,6 +62,10 @@ limitations under the License. | |||
|     mask-image: url('$(res)/img/icons-share.svg'); | ||||
| } | ||||
| 
 | ||||
| .mx_GroupView_hostingSignup img { | ||||
|     margin-left: 5px; | ||||
| } | ||||
| 
 | ||||
| .mx_GroupView_editable { | ||||
|     border-bottom: 1px solid $strong-input-border-color ! important; | ||||
|     min-width: 150px; | ||||
|  |  | |||
|  | @ -15,17 +15,39 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| .mx_TopLeftMenu { | ||||
|     min-width: 180px; | ||||
|     min-width: 210px; | ||||
|     border-radius: 4px; | ||||
| 
 | ||||
|     .mx_TopLeftMenu_greyedText { | ||||
|         font-size: 12px; | ||||
|         opacity: 0.5; | ||||
|     } | ||||
| 
 | ||||
|     .mx_TopLeftMenu_upgradeLink { | ||||
|         font-size: 12px; | ||||
| 
 | ||||
|         img { | ||||
|             margin-left: 5px; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_TopLeftMenu_section:not(:last-child) { | ||||
|         border-bottom: 1px solid $menu-border-color; | ||||
|     } | ||||
| 
 | ||||
|     .mx_TopLeftMenu_section { | ||||
|         list-style: none; | ||||
|     .mx_TopLeftMenu_section_noIcon { | ||||
|         margin: 5px 0; | ||||
|         padding: 5px 20px 5px 15px; | ||||
| 
 | ||||
|         div:not(:first-child) { | ||||
|             margin-top: 5px; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_TopLeftMenu_section_withIcon { | ||||
|         margin: 5px 0; | ||||
|         padding: 0; | ||||
|         list-style: none; | ||||
| 
 | ||||
|         li.mx_TopLeftMenu_icon_home::after { | ||||
|             mask-image: url('$(res)/img/feather-customised/home.svg'); | ||||
|  |  | |||
|  | @ -35,6 +35,14 @@ limitations under the License. | |||
|     margin-top: 0; | ||||
| } | ||||
| 
 | ||||
| .mx_ProfileSettings_hostingSignup { | ||||
|     margin-left: 20px; | ||||
| 
 | ||||
|     img { | ||||
|         margin-left: 5px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .mx_ProfileSettings_avatar { | ||||
|     width: 88px; | ||||
|     height: 88px; | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="11" height="10" viewBox="0 0 11 10"> | ||||
|     <g fill="none" fill-rule="evenodd" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"> | ||||
|         <path d="M8.5 5.5v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3M7 .5h3v3M4.5 6L10 .5"/> | ||||
|     </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 304 B | 
|  | @ -21,6 +21,7 @@ import Promise from 'bluebird'; | |||
| import MatrixClientPeg from '../../MatrixClientPeg'; | ||||
| import sdk from '../../index'; | ||||
| import dis from '../../dispatcher'; | ||||
| import { getHostingLink } from '../../utils/HostingLink'; | ||||
| import { sanitizedHtmlNode } from '../../HtmlUtils'; | ||||
| import { _t, _td } from '../../languageHandler'; | ||||
| import AccessibleButton from '../views/elements/AccessibleButton'; | ||||
|  | @ -816,6 +817,23 @@ export default React.createClass({ | |||
|         }); | ||||
| 
 | ||||
|         const header = this.state.editing ? <h2> { _t('Community Settings') } </h2> : <div />; | ||||
| 
 | ||||
|         const hostingSignupLink = getHostingLink('community-settings'); | ||||
|         let hostingSignup = null; | ||||
|         if (hostingSignupLink) { | ||||
|             hostingSignup = <div className="mx_GroupView_hostingSignup"> | ||||
|                 {_t( | ||||
|                     "Want more than a community? <a>Get your own server</a>", {}, | ||||
|                     { | ||||
|                         a: sub => <a href={hostingSignupLink} target="_blank" rel="noopener">{sub}</a>, | ||||
|                     }, | ||||
|                 )} | ||||
|                 <a href={hostingSignupLink} target="_blank" rel="noopener"> | ||||
|                     <img src={require("../../../res/img/external-link.svg")} width="11" height="10" alt='' /> | ||||
|                 </a> | ||||
|             </div>; | ||||
|         } | ||||
| 
 | ||||
|         const changeDelayWarning = this.state.editing && this.state.isUserPrivileged ? | ||||
|             <div className="mx_GroupView_changeDelayWarning"> | ||||
|                 { _t( | ||||
|  | @ -830,6 +848,7 @@ export default React.createClass({ | |||
|             </div> : <div />; | ||||
|         return <div className={groupSettingsSectionClasses}> | ||||
|             { header } | ||||
|             { hostingSignup } | ||||
|             { changeDelayWarning } | ||||
|             { this._getJoinableNode() } | ||||
|             { this._getLongDescriptionNode() } | ||||
|  |  | |||
|  | @ -68,17 +68,18 @@ export default class TopLeftMenuButton extends React.Component { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const fallbackUserId = MatrixClientPeg.get().getUserId(); | ||||
|         const profileInfo = this.state.profileInfo; | ||||
|         let name; | ||||
|     _getDisplayName() { | ||||
|         if (MatrixClientPeg.get().isGuest()) { | ||||
|             name = _t("Guest"); | ||||
|         } else if (profileInfo) { | ||||
|             name = profileInfo.name; | ||||
|             return _t("Guest"); | ||||
|         } else if (this.state.profileInfo) { | ||||
|             return this.state.profileInfo.name; | ||||
|         } else { | ||||
|             name = fallbackUserId; | ||||
|             return MatrixClientPeg.get().getUserId(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const name = this._getDisplayName(); | ||||
|         let nameElement; | ||||
|         if (!this.props.collapsed) { | ||||
|             nameElement = <div className="mx_TopLeftMenuButton_name"> | ||||
|  | @ -89,9 +90,9 @@ export default class TopLeftMenuButton extends React.Component { | |||
|         return ( | ||||
|             <AccessibleButton className="mx_TopLeftMenuButton" onClick={this.onToggleMenu}> | ||||
|                 <BaseAvatar | ||||
|                     idName={fallbackUserId} | ||||
|                     idName={MatrixClientPeg.get().getUserId()} | ||||
|                     name={name} | ||||
|                     url={profileInfo && profileInfo.avatarUrl} | ||||
|                     url={this.state.profileInfo && this.state.profileInfo.avatarUrl} | ||||
|                     width={AVATAR_SIZE} | ||||
|                     height={AVATAR_SIZE} | ||||
|                     resizeMethod="crop" | ||||
|  | @ -114,6 +115,8 @@ export default class TopLeftMenuButton extends React.Component { | |||
|             chevronFace: "none", | ||||
|             left: x, | ||||
|             top: y, | ||||
|             userId: MatrixClientPeg.get().getUserId(), | ||||
|             displayName: this._getDisplayName(), | ||||
|             onFinished: () => { | ||||
|                 this.setState({ menuDisplayed: false }); | ||||
|             }, | ||||
|  |  | |||
|  | @ -15,14 +15,22 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import dis from '../../../dispatcher'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import LogoutDialog from "../dialogs/LogoutDialog"; | ||||
| import Modal from "../../../Modal"; | ||||
| import SdkConfig from '../../../SdkConfig'; | ||||
| import { getHostingLink } from '../../../utils/HostingLink'; | ||||
| import MatrixClientPeg from '../../../MatrixClientPeg'; | ||||
| 
 | ||||
| export class TopLeftMenu extends React.Component { | ||||
|     static propTypes = { | ||||
|         displayName: PropTypes.string.isRequired, | ||||
|         userId: PropTypes.string.isRequired, | ||||
|         onFinished: PropTypes.func, | ||||
|     }; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
|         this.viewHomePage = this.viewHomePage.bind(this); | ||||
|  | @ -46,27 +54,48 @@ export class TopLeftMenu extends React.Component { | |||
|     render() { | ||||
|         const isGuest = MatrixClientPeg.get().isGuest(); | ||||
| 
 | ||||
|         const hostingSignupLink = getHostingLink('user-context-menu'); | ||||
|         let hostingSignup = null; | ||||
|         if (hostingSignupLink) { | ||||
|             hostingSignup = <div className="mx_TopLeftMenu_upgradeLink"> | ||||
|                 {_t( | ||||
|                     "<a>Upgrade</a> to your own domain", {}, | ||||
|                     { | ||||
|                         a: sub => <a href={hostingSignupLink} target="_blank" rel="noopener">{sub}</a>, | ||||
|                     }, | ||||
|                 )} | ||||
|                 <a href={hostingSignupLink} target="_blank" rel="noopener"> | ||||
|                     <img src={require("../../../../res/img/external-link.svg")} width="11" height="10" alt='' /> | ||||
|                 </a> | ||||
|             </div>; | ||||
|         } | ||||
| 
 | ||||
|         let homePageSection = null; | ||||
|         if (this.hasHomePage()) { | ||||
|             homePageSection = <ul className="mx_TopLeftMenu_section"> | ||||
|             homePageSection = <ul className="mx_TopLeftMenu_section_withIcon"> | ||||
|                 <li className="mx_TopLeftMenu_icon_home" onClick={this.viewHomePage}>{_t("Home")}</li> | ||||
|             </ul>; | ||||
|         } | ||||
| 
 | ||||
|         let signInOutSection; | ||||
|         if (isGuest) { | ||||
|             signInOutSection = <ul className="mx_TopLeftMenu_section"> | ||||
|             signInOutSection = <ul className="mx_TopLeftMenu_section_withIcon"> | ||||
|                 <li className="mx_TopLeftMenu_icon_signin" onClick={this.signIn}>{_t("Sign in")}</li> | ||||
|             </ul>; | ||||
|         } else { | ||||
|             signInOutSection = <ul className="mx_TopLeftMenu_section"> | ||||
|             signInOutSection = <ul className="mx_TopLeftMenu_section_withIcon"> | ||||
|                 <li className="mx_TopLeftMenu_icon_signout" onClick={this.signOut}>{_t("Sign out")}</li> | ||||
|             </ul>; | ||||
|         } | ||||
| 
 | ||||
|         return <div className="mx_TopLeftMenu"> | ||||
|             <div className="mx_TopLeftMenu_section_noIcon"> | ||||
|                 <div>{this.props.displayName}</div> | ||||
|                 <div className="mx_TopLeftMenu_greyedText">{this.props.userId}</div> | ||||
|                 {hostingSignup} | ||||
|             </div> | ||||
|             {homePageSection} | ||||
|             <ul className="mx_TopLeftMenu_section"> | ||||
|             <ul className="mx_TopLeftMenu_section_withIcon"> | ||||
|                 <li className="mx_TopLeftMenu_icon_settings" onClick={this.openSettings}>{_t("Settings")}</li> | ||||
|             </ul> | ||||
|             {signInOutSection} | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import Field from "../elements/Field"; | |||
| import AccessibleButton from "../elements/AccessibleButton"; | ||||
| import classNames from 'classnames'; | ||||
| import {User} from "matrix-js-sdk"; | ||||
| import { getHostingLink } from '../../../utils/HostingLink'; | ||||
| 
 | ||||
| export default class ProfileSettings extends React.Component { | ||||
|     constructor() { | ||||
|  | @ -137,13 +138,32 @@ export default class ProfileSettings extends React.Component { | |||
|             </div> | ||||
|         ); | ||||
| 
 | ||||
|         const hostingSignupLink = getHostingLink('user-settings'); | ||||
|         let hostingSignup = null; | ||||
|         if (hostingSignupLink) { | ||||
|             hostingSignup = <span className="mx_ProfileSettings_hostingSignup"> | ||||
|                 {_t( | ||||
|                     "<a>Upgrade</a> to your own domain", {}, | ||||
|                     { | ||||
|                         a: sub => <a href={hostingSignupLink} target="_blank" rel="noopener">{sub}</a>, | ||||
|                     }, | ||||
|                 )} | ||||
|                 <a href={hostingSignupLink} target="_blank" rel="noopener"> | ||||
|                     <img src={require("../../../../res/img/external-link.svg")} width="11" height="10" alt='' /> | ||||
|                 </a> | ||||
|             </span>; | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <form onSubmit={this._saveProfile} autoComplete={false} noValidate={true}> | ||||
|                 <input type="file" ref="avatarUpload" className="mx_ProfileSettings_avatarUpload" | ||||
|                        onChange={this._onAvatarChanged} accept="image/*" /> | ||||
|                 <div className="mx_ProfileSettings_profile"> | ||||
|                     <div className="mx_ProfileSettings_controls"> | ||||
|                         <p>{this.state.userId}</p> | ||||
|                         <p> | ||||
|                             {this.state.userId} | ||||
|                             {hostingSignup} | ||||
|                         </p> | ||||
|                         <Field id="profileDisplayName" label={_t("Display Name")} | ||||
|                                type="text" value={this.state.displayName} autoComplete="off" | ||||
|                                onChange={this._onDisplayNameChanged} /> | ||||
|  |  | |||
|  | @ -502,6 +502,7 @@ | |||
|     "Phone Number": "Phone Number", | ||||
|     "Profile picture": "Profile picture", | ||||
|     "Upload profile picture": "Upload profile picture", | ||||
|     "<a>Upgrade</a> to your own domain": "<a>Upgrade</a> to your own domain", | ||||
|     "Display Name": "Display Name", | ||||
|     "Save": "Save", | ||||
|     "Flair": "Flair", | ||||
|  | @ -1317,6 +1318,7 @@ | |||
|     "Leave %(groupName)s?": "Leave %(groupName)s?", | ||||
|     "Unable to leave community": "Unable to leave community", | ||||
|     "Community Settings": "Community Settings", | ||||
|     "Want more than a community? <a>Get your own server</a>": "Want more than a community? <a>Get your own server</a>", | ||||
|     "Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.": "Changes made to your community <bold1>name</bold1> and <bold2>avatar</bold2> might not be seen by other users for up to 30 minutes.", | ||||
|     "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.", | ||||
|     "Featured Rooms:": "Featured Rooms:", | ||||
|  |  | |||
|  | @ -0,0 +1,37 @@ | |||
| /* | ||||
| Copyright 2019 New Vector Ltd. | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import url from 'url'; | ||||
| import qs from 'qs'; | ||||
| 
 | ||||
| import SdkConfig from '../SdkConfig'; | ||||
| 
 | ||||
| export function getHostingLink(campaign) { | ||||
|     const hostingLink = SdkConfig.get().hosting_signup_link; | ||||
|     if (!hostingLink) return null; | ||||
|     if (!campaign) return hostingLink; | ||||
| 
 | ||||
|     try { | ||||
|         const hostingUrl = url.parse(hostingLink); | ||||
|         const params = qs.parse(hostingUrl.query); | ||||
|         params.utm_campaign = campaign; | ||||
|         hostingUrl.search = undefined; | ||||
|         hostingUrl.query = params; | ||||
|         return hostingUrl.format(); | ||||
|     } catch (e) { | ||||
|         return hostingLink; | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker