mirror of https://github.com/vector-im/riot-web
Merge pull request #2597 from matrix-org/jryans/add-welcome-page
SDK support for welcome pagepull/21833/head
commit
f99c56fedb
|
@ -36,6 +36,7 @@
|
|||
@import "./views/auth/_LanguageSelector.scss";
|
||||
@import "./views/auth/_ServerConfig.scss";
|
||||
@import "./views/auth/_ServerTypeSelector.scss";
|
||||
@import "./views/auth/_Welcome.scss";
|
||||
@import "./views/avatars/_BaseAvatar.scss";
|
||||
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||
@import "./views/context_menus/_MessageContextMenu.scss";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
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.
|
||||
|
@ -22,10 +23,3 @@ limitations under the License.
|
|||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.mx_HomePage iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
.mx_Welcome {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mx_Welcome .mx_AuthBody_language {
|
||||
width: 120px;
|
||||
margin-bottom: 10px;
|
||||
}
|
|
@ -27,6 +27,10 @@ limitations under the License.
|
|||
margin: 5px 0;
|
||||
padding: 0;
|
||||
|
||||
li.mx_TopLeftMenu_icon_home::after {
|
||||
mask-image: url('$(res)/img/feather-icons/home.svg');
|
||||
}
|
||||
|
||||
li.mx_TopLeftMenu_icon_settings::after {
|
||||
mask-image: url('$(res)/img/feather-icons/settings.svg');
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
After Width: | Height: | Size: 332 B |
|
@ -35,8 +35,10 @@ export const SAFE_LOCALPART_REGEX = /^[a-z0-9=_\-./]+$/;
|
|||
* on what the HS supports
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {bool} options.go_home_on_cancel If true, goes to
|
||||
* the hame page if the user cancels the action
|
||||
* @param {bool} options.go_home_on_cancel
|
||||
* If true, goes to the home page if the user cancels the action
|
||||
* @param {bool} options.go_welcome_on_cancel
|
||||
* If true, goes to the welcome page if the user cancels the action
|
||||
*/
|
||||
export async function startAnyRegistrationFlow(options) {
|
||||
if (options === undefined) options = {};
|
||||
|
@ -73,6 +75,8 @@ export async function startAnyRegistrationFlow(options) {
|
|||
dis.dispatch({action: 'start_registration'});
|
||||
} else if (options.go_home_on_cancel) {
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
} else if (options.go_welcome_on_cancel) {
|
||||
dis.dispatch({action: 'view_welcome_page'});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
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.
|
||||
|
@ -26,22 +27,27 @@ import sdk from '../../index';
|
|||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import classnames from 'classnames';
|
||||
|
||||
class HomePage extends React.Component {
|
||||
static displayName = 'HomePage';
|
||||
|
||||
export default class EmbeddedPage extends React.PureComponent {
|
||||
static propTypes = {
|
||||
// URL to use as the iFrame src. Defaults to /home.html.
|
||||
homePageUrl: PropTypes.string,
|
||||
// URL to request embedded page content from
|
||||
url: PropTypes.string,
|
||||
// Class name prefix to apply for a given instance
|
||||
className: PropTypes.string,
|
||||
// Whether to wrap the page in a scrollbar
|
||||
scrollbar: PropTypes.bool,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||
};
|
||||
|
||||
state = {
|
||||
iframeSrc: '',
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
page: '',
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
translate(s) {
|
||||
// default implementation - skins may wish to extend this
|
||||
|
@ -51,22 +57,24 @@ class HomePage extends React.Component {
|
|||
componentWillMount() {
|
||||
this._unmounted = false;
|
||||
|
||||
// we use request() to inline the homepage into the react component
|
||||
if (!this.props.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we use request() to inline the page into the react component
|
||||
// so that it can inherit CSS and theming easily rather than mess around
|
||||
// with iframes and trying to synchronise document.stylesheets.
|
||||
|
||||
const src = this.props.homePageUrl || 'home.html';
|
||||
|
||||
request(
|
||||
{ method: "GET", url: src },
|
||||
{ method: "GET", url: this.props.url },
|
||||
(err, response, body) => {
|
||||
if (this._unmounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err || response.status < 200 || response.status >= 300) {
|
||||
console.warn(`Error loading home page: ${err}`);
|
||||
this.setState({ page: _t("Couldn't load home page") });
|
||||
console.warn(`Error loading page: ${err}`);
|
||||
this.setState({ page: _t("Couldn't load page") });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,28 +89,28 @@ class HomePage extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const isGuest = this.context.matrixClient.isGuest();
|
||||
const client = this.context.matrixClient;
|
||||
const isGuest = client ? client.isGuest() : true;
|
||||
const className = this.props.className;
|
||||
const classes = classnames({
|
||||
mx_HomePage: true,
|
||||
mx_HomePage_guest: isGuest,
|
||||
[className]: true,
|
||||
[`${className}_guest`]: isGuest,
|
||||
});
|
||||
|
||||
if (this.state.iframeSrc) {
|
||||
return (
|
||||
<div className={classes}>
|
||||
<iframe src={ this.state.iframeSrc } />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const content = <div className={`${className}_body`}
|
||||
dangerouslySetInnerHTML={{ __html: this.state.page }}
|
||||
>
|
||||
</div>;
|
||||
|
||||
if (this.props.scrollbar) {
|
||||
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
||||
return (
|
||||
<GeminiScrollbarWrapper autoshow={true} className={classes}>
|
||||
<div className="mx_HomePage_body" dangerouslySetInnerHTML={{ __html: this.state.page }}>
|
||||
</div>
|
||||
</GeminiScrollbarWrapper>
|
||||
);
|
||||
return <GeminiScrollbarWrapper autoshow={true} className={classes}>
|
||||
{content}
|
||||
</GeminiScrollbarWrapper>;
|
||||
} else {
|
||||
return <div className={classes}>
|
||||
{content}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HomePage;
|
|
@ -420,7 +420,7 @@ const LoggedInView = React.createClass({
|
|||
render: function() {
|
||||
const LeftPanel = sdk.getComponent('structures.LeftPanel');
|
||||
const RoomView = sdk.getComponent('structures.RoomView');
|
||||
const HomePage = sdk.getComponent('structures.HomePage');
|
||||
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
|
||||
const GroupView = sdk.getComponent('structures.GroupView');
|
||||
const MyGroups = sdk.getComponent('structures.MyGroups');
|
||||
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
|
||||
|
@ -459,8 +459,20 @@ const LoggedInView = React.createClass({
|
|||
|
||||
case PageTypes.HomePage:
|
||||
{
|
||||
pageElement = <HomePage
|
||||
homePageUrl={this.props.config.welcomePageUrl}
|
||||
const pagesConfig = this.props.config.embeddedPages;
|
||||
let pageUrl = null;
|
||||
if (pagesConfig) {
|
||||
pageUrl = pagesConfig.homeUrl;
|
||||
}
|
||||
if (!pageUrl) {
|
||||
// This is a deprecated config option for the home page
|
||||
// (despite the name, given we also now have a welcome
|
||||
// page, which is not the same).
|
||||
pageUrl = this.props.config.welcomePageUrl;
|
||||
}
|
||||
pageElement = <EmbeddedPage className="mx_HomePage"
|
||||
url={pageUrl}
|
||||
scrollbar={true}
|
||||
/>;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -60,27 +60,30 @@ const VIEWS = {
|
|||
// trying to re-animate a matrix client or register as a guest.
|
||||
LOADING: 0,
|
||||
|
||||
// we are showing the welcome view
|
||||
WELCOME: 1,
|
||||
|
||||
// we are showing the login view
|
||||
LOGIN: 1,
|
||||
LOGIN: 2,
|
||||
|
||||
// we are showing the registration view
|
||||
REGISTER: 2,
|
||||
REGISTER: 3,
|
||||
|
||||
// completeing the registration flow
|
||||
POST_REGISTRATION: 3,
|
||||
POST_REGISTRATION: 4,
|
||||
|
||||
// showing the 'forgot password' view
|
||||
FORGOT_PASSWORD: 4,
|
||||
FORGOT_PASSWORD: 5,
|
||||
|
||||
// we have valid matrix credentials (either via an explicit login, via the
|
||||
// initial re-animation/guest registration, or via a registration), and are
|
||||
// now setting up a matrixclient to talk to it. This isn't an instant
|
||||
// process because we need to clear out indexeddb. While it is going on we
|
||||
// show a big spinner.
|
||||
LOGGING_IN: 5,
|
||||
LOGGING_IN: 6,
|
||||
|
||||
// we are logged in with an active matrix client.
|
||||
LOGGED_IN: 6,
|
||||
LOGGED_IN: 7,
|
||||
};
|
||||
|
||||
// Actions that are redirected through the onboarding process prior to being
|
||||
|
@ -354,8 +357,8 @@ export default React.createClass({
|
|||
});
|
||||
}).then((loadedSession) => {
|
||||
if (!loadedSession) {
|
||||
// fall back to showing the login screen
|
||||
dis.dispatch({action: "start_login"});
|
||||
// fall back to showing the welcome screen
|
||||
dis.dispatch({action: "view_welcome_page"});
|
||||
}
|
||||
});
|
||||
// Note we don't catch errors from this: we catch everything within
|
||||
|
@ -606,6 +609,9 @@ export default React.createClass({
|
|||
case 'view_group':
|
||||
this._viewGroup(payload);
|
||||
break;
|
||||
case 'view_welcome_page':
|
||||
this._viewWelcome();
|
||||
break;
|
||||
case 'view_home_page':
|
||||
this._viewHome();
|
||||
break;
|
||||
|
@ -881,6 +887,13 @@ export default React.createClass({
|
|||
this.notifyNewScreen('group/' + groupId);
|
||||
},
|
||||
|
||||
_viewWelcome() {
|
||||
this.setStateForNewView({
|
||||
view: VIEWS.WELCOME,
|
||||
});
|
||||
this.notifyNewScreen('welcome');
|
||||
},
|
||||
|
||||
_viewHome: function() {
|
||||
// The home page requires the "logged in" view, so we'll set that.
|
||||
this.setStateForNewView({
|
||||
|
@ -954,11 +967,11 @@ export default React.createClass({
|
|||
}
|
||||
dis.dispatch({
|
||||
action: 'require_registration',
|
||||
// If the set_mxid dialog is cancelled, view /home because if the browser
|
||||
// was pointing at /user/@someone:domain?action=chat, the URL needs to be
|
||||
// reset so that they can revisit /user/.. // (and trigger
|
||||
// If the set_mxid dialog is cancelled, view /welcome because if the
|
||||
// browser was pointing at /user/@someone:domain?action=chat, the URL
|
||||
// needs to be reset so that they can revisit /user/.. // (and trigger
|
||||
// `_chatCreateOrReuse` again)
|
||||
go_home_on_cancel: true,
|
||||
go_welcome_on_cancel: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -1180,7 +1193,11 @@ export default React.createClass({
|
|||
room_id: localStorage.getItem('mx_last_room_id'),
|
||||
});
|
||||
} else {
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
if (MatrixClientPeg.get().isGuest) {
|
||||
dis.dispatch({action: 'view_welcome_page'});
|
||||
} else {
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1466,6 +1483,10 @@ export default React.createClass({
|
|||
dis.dispatch({
|
||||
action: 'view_user_settings',
|
||||
});
|
||||
} else if (screen == 'welcome') {
|
||||
dis.dispatch({
|
||||
action: 'view_welcome_page',
|
||||
});
|
||||
} else if (screen == 'home') {
|
||||
dis.dispatch({
|
||||
action: 'view_home_page',
|
||||
|
@ -1849,6 +1870,11 @@ export default React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
if (this.state.view === VIEWS.WELCOME) {
|
||||
const Welcome = sdk.getComponent('auth.Welcome');
|
||||
return <Welcome />;
|
||||
}
|
||||
|
||||
if (this.state.view === VIEWS.REGISTER) {
|
||||
const Registration = sdk.getComponent('structures.auth.Registration');
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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 React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
|
||||
export default class Welcome extends React.PureComponent {
|
||||
render() {
|
||||
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
|
||||
const LanguageSelector = sdk.getComponent('auth.LanguageSelector');
|
||||
|
||||
const pagesConfig = SdkConfig.get().embeddedPages;
|
||||
let pageUrl = null;
|
||||
if (pagesConfig) {
|
||||
pageUrl = pagesConfig.welcomeUrl;
|
||||
}
|
||||
if (!pageUrl) {
|
||||
pageUrl = 'welcome.html';
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthPage>
|
||||
<div className="mx_Welcome">
|
||||
<EmbeddedPage className="mx_WelcomePage"
|
||||
url={pageUrl}
|
||||
/>
|
||||
<LanguageSelector />
|
||||
</div>
|
||||
</AuthPage>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2018, 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.
|
||||
|
@ -19,6 +19,7 @@ import dis from '../../../dispatcher';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import LogoutDialog from "../dialogs/LogoutDialog";
|
||||
import Modal from "../../../Modal";
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
|
||||
export class TopLeftMenu extends React.Component {
|
||||
constructor() {
|
||||
|
@ -27,8 +28,28 @@ export class TopLeftMenu extends React.Component {
|
|||
this.signOut = this.signOut.bind(this);
|
||||
}
|
||||
|
||||
hasHomePage() {
|
||||
const config = SdkConfig.get();
|
||||
const pagesConfig = config.embeddedPages;
|
||||
if (pagesConfig && pagesConfig.homeUrl) {
|
||||
return true;
|
||||
}
|
||||
// This is a deprecated config option for the home page
|
||||
// (despite the name, given we also now have a welcome
|
||||
// page, which is not the same).
|
||||
return !!config.welcomePageUrl;
|
||||
}
|
||||
|
||||
render() {
|
||||
let homePageSection = null;
|
||||
if (this.hasHomePage()) {
|
||||
homePageSection = <ul className="mx_TopLeftMenu_section">
|
||||
<li className="mx_TopLeftMenu_icon_home" onClick={this.viewHomePage}>{_t("Home")}</li>
|
||||
</ul>;
|
||||
}
|
||||
|
||||
return <div className="mx_TopLeftMenu">
|
||||
{homePageSection}
|
||||
<ul className="mx_TopLeftMenu_section">
|
||||
<li className="mx_TopLeftMenu_icon_settings" onClick={this.openSettings}>{_t("Settings")}</li>
|
||||
</ul>
|
||||
|
@ -38,6 +59,11 @@ export class TopLeftMenu extends React.Component {
|
|||
</div>;
|
||||
}
|
||||
|
||||
viewHomePage() {
|
||||
dis.dispatch({action: 'view_home_page'});
|
||||
this.closeMenu();
|
||||
}
|
||||
|
||||
openSettings() {
|
||||
dis.dispatch({action: 'view_user_settings'});
|
||||
this.closeMenu();
|
||||
|
|
|
@ -1279,6 +1279,7 @@
|
|||
"<safariLink>Safari</safariLink> and <operaLink>Opera</operaLink> work too.": "<safariLink>Safari</safariLink> and <operaLink>Opera</operaLink> work too.",
|
||||
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!",
|
||||
"I understand the risks and wish to continue": "I understand the risks and wish to continue",
|
||||
"Couldn't load page": "Couldn't load page",
|
||||
"You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality",
|
||||
"You must join the room to see its files": "You must join the room to see its files",
|
||||
"There are no visible files in this room": "There are no visible files in this room",
|
||||
|
@ -1323,7 +1324,6 @@
|
|||
"Community %(groupId)s not found": "Community %(groupId)s not found",
|
||||
"This homeserver does not support communities": "This homeserver does not support communities",
|
||||
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
|
||||
"Couldn't load home page": "Couldn't load home page",
|
||||
"Invalid configuration: Cannot supply a default homeserver URL and a default server name": "Invalid configuration: Cannot supply a default homeserver URL and a default server name",
|
||||
"Failed to reject invitation": "Failed to reject invitation",
|
||||
"This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.",
|
||||
|
|
Loading…
Reference in New Issue