Rename konnectd to IDP
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import Routes from './Routes';
|
||||
|
||||
class App extends PureComponent {
|
||||
render() {
|
||||
const { classes, hello, pathPrefix } = this.props;
|
||||
|
||||
return (
|
||||
<BrowserRouter basename={pathPrefix}>
|
||||
<Routes hello={hello}/>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
reload(event) {
|
||||
event.preventDefault();
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
hello: PropTypes.object,
|
||||
updateAvailable: PropTypes.bool.isRequired,
|
||||
pathPrefix: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hello, updateAvailable, pathPrefix } = state.common;
|
||||
|
||||
return {
|
||||
hello,
|
||||
updateAvailable,
|
||||
pathPrefix
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(App);
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import store from './store';
|
||||
import App from './Main';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Provider store={store}><App/></Provider>, div);
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
all: images
|
||||
|
||||
.PHONY: images
|
||||
images:
|
||||
@$(MAKE) -C images
|
||||
|
||||
clean:
|
||||
@$(MAKE) -C images clean
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import AsyncComponent from 'kpop/es/AsyncComponent';
|
||||
|
||||
import PrivateRoute from './components/PrivateRoute';
|
||||
|
||||
const AsyncLogin = AsyncComponent(() =>
|
||||
import(/* webpackChunkName: "containers-login" */ './containers/Login'));
|
||||
const AsyncWelcome = AsyncComponent(() =>
|
||||
import(/* webpackChunkName: "containers-welcome" */ './containers/Welcome'));
|
||||
const AsyncGoodbye = AsyncComponent(() =>
|
||||
import(/* webpackChunkName: "containers-goodbye" */ './containers/Goodbye'));
|
||||
|
||||
const Routes = ({ hello }) => (
|
||||
<Switch>
|
||||
<PrivateRoute
|
||||
path="/welcome"
|
||||
exact
|
||||
component={AsyncWelcome}
|
||||
hello={hello}
|
||||
/>
|
||||
<Route
|
||||
path="/goodbye"
|
||||
exact
|
||||
component={AsyncGoodbye}
|
||||
/>
|
||||
<Route
|
||||
path="/"
|
||||
component={AsyncLogin}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
|
||||
Routes.propTypes = {
|
||||
hello: PropTypes.object
|
||||
};
|
||||
|
||||
export default Routes;
|
||||
@@ -0,0 +1,133 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import { newHelloRequest } from '../models/hello';
|
||||
import { withClientRequestState } from '../utils';
|
||||
import {
|
||||
ExtendedError,
|
||||
ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS,
|
||||
ERROR_HTTP_UNEXPECTED_RESPONSE_STATE
|
||||
} from '../errors';
|
||||
|
||||
import { handleAxiosError } from './utils';
|
||||
import * as types from './types';
|
||||
|
||||
export function receiveError(error) {
|
||||
return {
|
||||
type: types.RECEIVE_ERROR,
|
||||
error
|
||||
};
|
||||
}
|
||||
|
||||
export function resetHello() {
|
||||
return {
|
||||
type: types.RESET_HELLO
|
||||
};
|
||||
}
|
||||
|
||||
export function receiveHello(hello) {
|
||||
const { success, username, displayName } = hello;
|
||||
|
||||
return {
|
||||
type: types.RECEIVE_HELLO,
|
||||
state: success === true,
|
||||
username,
|
||||
displayName,
|
||||
hello
|
||||
};
|
||||
}
|
||||
|
||||
export function executeHello() {
|
||||
return function(dispatch, getState) {
|
||||
dispatch(resetHello());
|
||||
|
||||
const { flow, query } = getState().common;
|
||||
|
||||
const r = withClientRequestState(newHelloRequest(flow, query));
|
||||
return axios.post('./identifier/_/hello', r, {
|
||||
headers: {
|
||||
'Kopano-Konnect-XSRF': '1'
|
||||
}
|
||||
}).then(response => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
// success.
|
||||
return response.data;
|
||||
case 204:
|
||||
// not signed-in.
|
||||
return {
|
||||
success: false,
|
||||
state: response.headers['kopano-konnect-state']
|
||||
};
|
||||
default:
|
||||
// error.
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS, response);
|
||||
}
|
||||
}).then(response => {
|
||||
if (response.state !== r.state) {
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATE, response);
|
||||
}
|
||||
|
||||
dispatch(receiveHello(response));
|
||||
return Promise.resolve(response);
|
||||
}).catch(error => {
|
||||
error = handleAxiosError(error);
|
||||
|
||||
dispatch(receiveError(error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function retryHello() {
|
||||
return function(dispatch) {
|
||||
dispatch(receiveError(null));
|
||||
|
||||
return dispatch(executeHello());
|
||||
};
|
||||
}
|
||||
|
||||
export function requestLogoff() {
|
||||
return {
|
||||
type: types.REQUEST_LOGOFF
|
||||
};
|
||||
}
|
||||
|
||||
export function receiveLogoff(state) {
|
||||
return {
|
||||
type: types.RECEIVE_LOGOFF,
|
||||
state
|
||||
};
|
||||
}
|
||||
|
||||
export function executeLogoff() {
|
||||
return function(dispatch) {
|
||||
dispatch(resetHello());
|
||||
dispatch(requestLogoff());
|
||||
|
||||
const r = withClientRequestState({});
|
||||
return axios.post('./identifier/_/logoff', r, {
|
||||
headers: {
|
||||
'Kopano-Konnect-XSRF': '1'
|
||||
}
|
||||
}).then(response => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
// success.
|
||||
return response.data;
|
||||
default:
|
||||
// error.
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS, response);
|
||||
}
|
||||
}).then(response => {
|
||||
if (response.state !== r.state) {
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATE, response);
|
||||
}
|
||||
|
||||
dispatch(receiveLogoff(response.success === true));
|
||||
return Promise.resolve(response);
|
||||
}).catch(error => {
|
||||
error = handleAxiosError(error);
|
||||
|
||||
dispatch(receiveError(error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
import axios from 'axios';
|
||||
import queryString from 'query-string';
|
||||
|
||||
import { newHelloRequest } from '../models/hello';
|
||||
import { withClientRequestState } from '../utils';
|
||||
import {
|
||||
ExtendedError,
|
||||
ERROR_LOGIN_VALIDATE_MISSINGUSERNAME,
|
||||
ERROR_LOGIN_VALIDATE_MISSINGPASSWORD,
|
||||
ERROR_LOGIN_FAILED,
|
||||
ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS,
|
||||
ERROR_HTTP_UNEXPECTED_RESPONSE_STATE
|
||||
} from '../errors';
|
||||
|
||||
import * as types from './types';
|
||||
import { receiveHello } from './common';
|
||||
import { handleAxiosError } from './utils';
|
||||
|
||||
// Modes for logon.
|
||||
export const ModeLogonUsernameEmptyPasswordCookie = '0';
|
||||
export const ModeLogonUsernamePassword = '1';
|
||||
|
||||
export function updateInput(name, value) {
|
||||
return {
|
||||
type: types.UPDATE_INPUT,
|
||||
name,
|
||||
value
|
||||
};
|
||||
}
|
||||
|
||||
export function receiveValidateLogon(errors) {
|
||||
return {
|
||||
type: types.RECEIVE_VALIDATE_LOGON,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
export function requestLogon(username, password) {
|
||||
return {
|
||||
type: types.REQUEST_LOGON,
|
||||
username,
|
||||
password
|
||||
};
|
||||
}
|
||||
|
||||
export function receiveLogon(logon) {
|
||||
const { success, errors } = logon;
|
||||
|
||||
return {
|
||||
type: types.RECEIVE_LOGON,
|
||||
success,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
export function requestConsent(allow=false) {
|
||||
return {
|
||||
type: allow ? types.REQUEST_CONSENT_ALLOW : types.REQUEST_CONSENT_CANCEL
|
||||
};
|
||||
}
|
||||
|
||||
export function receiveConsent(logon) {
|
||||
const { success, errors } = logon;
|
||||
|
||||
return {
|
||||
type: types.RECEIVE_CONSENT,
|
||||
success,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
export function executeLogon(username, password, mode=ModeLogonUsernamePassword) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch(requestLogon(username, password));
|
||||
dispatch(receiveHello({
|
||||
username
|
||||
})); // Reset any hello state on logon.
|
||||
|
||||
const { flow, query } = getState().common;
|
||||
|
||||
// Prepare params based on mode.
|
||||
const params = [];
|
||||
switch (mode) {
|
||||
case ModeLogonUsernamePassword:
|
||||
// Username with password.
|
||||
params.push(username, password, mode);
|
||||
break;
|
||||
|
||||
case ModeLogonUsernameEmptyPasswordCookie:
|
||||
// Username with empty password - this only works when the user is already signed in.
|
||||
params.push(username, '', mode);
|
||||
break;
|
||||
}
|
||||
|
||||
const r = withClientRequestState({
|
||||
params: params,
|
||||
hello: newHelloRequest(flow, query)
|
||||
});
|
||||
return axios.post('./identifier/_/logon', r, {
|
||||
headers: {
|
||||
'Kopano-Konnect-XSRF': '1'
|
||||
}
|
||||
}).then(response => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
// success.
|
||||
return response.data;
|
||||
case 204:
|
||||
// login failed.
|
||||
return {
|
||||
success: false,
|
||||
state: response.headers['kopano-konnect-state'],
|
||||
errors: {
|
||||
http: new Error(ERROR_LOGIN_FAILED)
|
||||
}
|
||||
};
|
||||
default:
|
||||
// error.
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS, response);
|
||||
}
|
||||
}).then(response => {
|
||||
if (response.state !== r.state) {
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATE, response);
|
||||
}
|
||||
|
||||
let { hello } = response;
|
||||
if (!hello) {
|
||||
hello = {
|
||||
success: response.success,
|
||||
username
|
||||
};
|
||||
}
|
||||
dispatch(receiveHello(hello));
|
||||
dispatch(receiveLogon(response));
|
||||
return Promise.resolve(response);
|
||||
}).catch(error => {
|
||||
error = handleAxiosError(error);
|
||||
const errors = {
|
||||
http: error
|
||||
};
|
||||
|
||||
dispatch(receiveValidateLogon(errors));
|
||||
return {
|
||||
success: false,
|
||||
errors: errors
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function executeConsent(allow=false, scope='') {
|
||||
return function(dispatch, getState) {
|
||||
dispatch(requestConsent(allow));
|
||||
|
||||
const { query } = getState().common;
|
||||
|
||||
const r = withClientRequestState({
|
||||
allow,
|
||||
scope,
|
||||
client_id: query.client_id || '', // eslint-disable-line camelcase
|
||||
redirect_uri: query.redirect_uri || '', // eslint-disable-line camelcase
|
||||
ref: query.state || '',
|
||||
flow_nonce: query.nonce || '' // eslint-disable-line camelcase
|
||||
});
|
||||
return axios.post('./identifier/_/consent', r, {
|
||||
headers: {
|
||||
'Kopano-Konnect-XSRF': '1'
|
||||
}
|
||||
}).then(response => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
// success.
|
||||
return response.data;
|
||||
case 204:
|
||||
// cancel reply.
|
||||
return {
|
||||
success: true,
|
||||
state: response.headers['kopano-konnect-state']
|
||||
};
|
||||
default:
|
||||
// error.
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS, response);
|
||||
}
|
||||
}).then(response => {
|
||||
if (response.state !== r.state) {
|
||||
throw new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATE, response);
|
||||
}
|
||||
|
||||
dispatch(receiveConsent(response));
|
||||
return Promise.resolve(response);
|
||||
}).catch(error => {
|
||||
error = handleAxiosError(error);
|
||||
const errors = {
|
||||
http: error
|
||||
};
|
||||
|
||||
dispatch(receiveValidateLogon(errors));
|
||||
return {
|
||||
success: false,
|
||||
errors: errors
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function validateUsernamePassword(username, password, isSignedIn) {
|
||||
return function(dispatch) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const errors = {};
|
||||
|
||||
if (!username) {
|
||||
errors.username = new Error(ERROR_LOGIN_VALIDATE_MISSINGUSERNAME);
|
||||
}
|
||||
if (!password && !isSignedIn) {
|
||||
errors.password = new Error(ERROR_LOGIN_VALIDATE_MISSINGPASSWORD);
|
||||
}
|
||||
|
||||
dispatch(receiveValidateLogon(errors));
|
||||
if (Object.keys(errors).length === 0) {
|
||||
resolve(errors);
|
||||
} else {
|
||||
reject(errors);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function executeLogonIfFormValid(username, password, isSignedIn) {
|
||||
return (dispatch) => {
|
||||
return dispatch(
|
||||
validateUsernamePassword(username, password, isSignedIn)
|
||||
).then(() => {
|
||||
const mode = isSignedIn ? ModeLogonUsernameEmptyPasswordCookie : ModeLogonUsernamePassword;
|
||||
return dispatch(executeLogon(username, password, mode));
|
||||
}).catch((errors) => {
|
||||
return {
|
||||
success: false,
|
||||
errors: errors
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function advanceLogonFlow(success, history, done=false, extraQuery={}) {
|
||||
return (dispatch, getState) => {
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { flow, query, hello } = getState().common;
|
||||
const q = Object.assign({}, query, extraQuery);
|
||||
|
||||
switch (flow) {
|
||||
case 'oauth':
|
||||
case 'consent':
|
||||
case 'oidc':
|
||||
if (hello.details.flow !== flow) {
|
||||
// Ignore requested flow if hello flow does not match.
|
||||
break;
|
||||
}
|
||||
|
||||
if (!done && hello.details.next === 'consent') {
|
||||
history.replace(`/consent${history.location.search}${history.location.hash}`);
|
||||
return;
|
||||
}
|
||||
if (hello.details.continue_uri) {
|
||||
q.prompt = 'none';
|
||||
window.location.replace(hello.details.continue_uri + '?' + queryString.stringify(q));
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Legacy stupid modes.
|
||||
if (q.continue && q.continue.indexOf(document.location.origin) === 0) {
|
||||
window.location.replace(q.continue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Default action.
|
||||
let target = '/welcome';
|
||||
if (history.action === 'REPLACE') {
|
||||
target = target + history.location.search + history.location.hash;
|
||||
}
|
||||
|
||||
dispatch(receiveValidateLogon({})); // XXX(longsleep): hack to reset loading and errors.
|
||||
history.push(target);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export const RECEIVE_ERROR = 'RECEIVE_ERROR';
|
||||
|
||||
export const RESET_HELLO = 'RESET_HELLO';
|
||||
export const EXECUTE_HELLO = 'EXECUTE_HELLO';
|
||||
export const RECEIVE_HELLO = 'RECEIVE_HELLO';
|
||||
|
||||
export const RECEIVE_VALIDATE_LOGON = 'RECEIVE_VALIDATE_LOGON';
|
||||
export const REQUEST_LOGON = 'REQUEST_LOGON';
|
||||
export const EXECUTE_LOGON = 'EXECUTE_LOGON';
|
||||
export const RECEIVE_LOGON = 'RECEIVE_LOGON';
|
||||
export const UPDATE_INPUT = 'UPDATE_INPUT';
|
||||
|
||||
export const REQUEST_CONSENT_ALLOW = 'REQUEST_CONSENT_ALLOW';
|
||||
export const REQUEST_CONSENT_CANCEL = 'REQUEST_CONSENT_CANCEL';
|
||||
export const EXECUTE_CONSENT = 'EXECUTE_CONSENT';
|
||||
export const RECEIVE_CONSENT = 'RECEIVE_CONSENT';
|
||||
|
||||
export const REQUEST_LOGOFF = 'REQUEST_LOGOFF';
|
||||
export const EXECUTE_LOGOFF = 'EXECUTE_LOGOFF';
|
||||
export const RECEIVE_LOGOFF = 'RECEIVE_LOGOFF';
|
||||
|
||||
export const SERVICE_WORKER_NEW_CONTENT = 'SERVICE_WORKER_NEW_CONTENT';
|
||||
export const SERVICE_WORKER_READY = 'SERVICE_WORKER_READY';
|
||||
export const SERVICE_WORKER_ERROR = 'SERVICE_WORKER_ERROR';
|
||||
export const SERVICE_WORKER_OFFLINE = 'SERVICE_WORKER_OFFLINE';
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
ExtendedError,
|
||||
ERROR_HTTP_NETWORK_ERROR,
|
||||
ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS
|
||||
} from '../errors';
|
||||
|
||||
export function handleAxiosError(error) {
|
||||
if (error.request) {
|
||||
// Axios errors.
|
||||
if (error.response) {
|
||||
error = new ExtendedError(ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS, error.response);
|
||||
} else {
|
||||
error = new ExtendedError(ERROR_HTTP_NETWORK_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/* additional css on top of kpop */
|
||||
html {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.oc-login-bg {
|
||||
background-image: url(./images/background.jpg);
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#loader {
|
||||
/* NOTE(longsleep): White here needed because of the background image */
|
||||
color: white;
|
||||
text-shadow: #000 0 0 1px;
|
||||
}
|
||||
|
||||
.oc-logo {
|
||||
position: absolute;
|
||||
top: -130px;
|
||||
left: 50%;
|
||||
height: 80px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.oc-progress {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: rgba(78, 133, 200, 0.8) !important;
|
||||
height: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.oc-progress > div {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: #4e85c8 !important;
|
||||
}
|
||||
|
||||
.oc-input {
|
||||
background-color: #042047;
|
||||
border: 1px solid rgba(78, 133, 200, 0.8);
|
||||
border-radius: 3px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
height: 40px;
|
||||
width: 300px;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.oc-input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.oc-input::placeholder {
|
||||
color: rgba(78, 133, 200, 0.8);
|
||||
}
|
||||
|
||||
.oc-input + .oc-input {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.oc-button {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
font-size: 1.0625rem !important;
|
||||
}
|
||||
|
||||
.oc-button-primary {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: #4e85c8 !important;
|
||||
}
|
||||
|
||||
.oc-button-primary:hover,
|
||||
.oc-button-primary:focus {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: #306db5 !important;
|
||||
}
|
||||
|
||||
.oc-checkbox-dark svg {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
fill: white !important;
|
||||
}
|
||||
|
||||
.oc-footer-message {
|
||||
color: white;
|
||||
padding: 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.oc-logo {
|
||||
height: 60px;
|
||||
top: -90px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
.oc-mt-l {
|
||||
margin-top: 30px !important;
|
||||
}
|
||||
|
||||
.oc-mb-m {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.oc-light {
|
||||
color: #fff !important;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Loadable from 'react-loadable';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles';
|
||||
|
||||
import { defaultTheme as theme } from 'kpop/es/theme';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import Loading from 'kpop/es/Loading';
|
||||
import { unregister } from 'kpop/es/serviceWorker';
|
||||
|
||||
import store from './store';
|
||||
import translations from './locales';
|
||||
|
||||
// NOTE(longsleep): Load async with loader, this enables code splitting via Webpack.
|
||||
const LoadableApp = Loadable({
|
||||
loader: () => import(/* webpackChunkName: "identifier-main" */ './Main'),
|
||||
loading: Loading,
|
||||
timeout: 20000
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<IntlProvider messages={translations} locale="en" defaultLocale="en">
|
||||
<LoadableApp />
|
||||
</IntlProvider>
|
||||
</MuiThemeProvider>
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
unregister();
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ClientDisplayName = ({ client, ...rest }) => (
|
||||
<span {...rest}>{client.display_name ? client.display_name : client.id}</span>
|
||||
);
|
||||
|
||||
ClientDisplayName.propTypes = {
|
||||
client: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default ClientDisplayName;
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import renderIf from 'render-if';
|
||||
|
||||
import { retryHello } from '../actions/common';
|
||||
import { ErrorMessage } from '../errors';
|
||||
|
||||
function Loading({ error, dispatch }) {
|
||||
const retry = (event) => {
|
||||
event.preventDefault();
|
||||
dispatch(retryHello());
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid item align="center">
|
||||
{renderIf(error === null)(() => (
|
||||
<LinearProgress className="oc-progress" />
|
||||
))}
|
||||
{renderIf(error !== null)(() => (
|
||||
<div>
|
||||
<Typography className="oc-light" variant="h5" gutterBottom align="center">
|
||||
<FormattedMessage id="konnect.loading.error.headline" defaultMessage="Failed to connect to server" />
|
||||
</Typography>
|
||||
<Typography align="center" color="error">
|
||||
<ErrorMessage error={error} />
|
||||
</Typography>
|
||||
<Button
|
||||
autoFocus
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary oc-mt-l"
|
||||
onClick={(event) => retry(event)}
|
||||
>
|
||||
<FormattedMessage id="konnect.login.retryButton.label" defaultMessage="Retry" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Loading.propTypes = {
|
||||
error: PropTypes.object,
|
||||
dispatch: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { error } = state.common;
|
||||
|
||||
return {
|
||||
error
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(Loading);
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Route } from 'react-router-dom';
|
||||
|
||||
import RedirectWithQuery from './RedirectWithQuery';
|
||||
|
||||
const PrivateRoute = ({ component: Target, hello, ...rest }) => (
|
||||
<Route {...rest} render={props => (
|
||||
hello ? (
|
||||
<Target {...props}/>
|
||||
) : (
|
||||
<RedirectWithQuery target='/identifier' />
|
||||
)
|
||||
)}/>
|
||||
);
|
||||
|
||||
PrivateRoute.propTypes = {
|
||||
component: PropTypes.func.isRequired,
|
||||
hello: PropTypes.object
|
||||
};
|
||||
|
||||
export default PrivateRoute;
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
const RedirectWithQuery = ({target, location, ...rest}) => {
|
||||
const to = {
|
||||
pathname: target,
|
||||
search: location.search,
|
||||
hash: location.hash
|
||||
};
|
||||
|
||||
return (
|
||||
<Redirect to={to} {...rest}></Redirect>
|
||||
);
|
||||
};
|
||||
|
||||
RedirectWithQuery.propTypes = {
|
||||
target: PropTypes.string.isRequired,
|
||||
location: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default withRouter(RedirectWithQuery);
|
||||
@@ -0,0 +1,95 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
|
||||
import Loading from './Loading';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flex: 1
|
||||
},
|
||||
content: {
|
||||
position: 'relative',
|
||||
width: '100%'
|
||||
},
|
||||
actions: {
|
||||
marginTop: -40,
|
||||
justifyContent: 'flex-start',
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingRight: theme.spacing(3)
|
||||
},
|
||||
wrapper: {
|
||||
width: '100%',
|
||||
maxWidth: 300,
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
const footerProductName = name => <strong>{name}</strong>;
|
||||
|
||||
const ResponsiveScreen = (props) => {
|
||||
const {
|
||||
classes,
|
||||
withoutLogo,
|
||||
withoutPadding,
|
||||
loading,
|
||||
children,
|
||||
className,
|
||||
DialogProps,
|
||||
PaperProps,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const logo = withoutLogo ? null :
|
||||
<img src={process.env.PUBLIC_URL + '/static/logo.svg'} className="oc-logo" alt="ownCloud"/>;
|
||||
|
||||
const content = loading ? <Loading/> : (withoutPadding ? children : <DialogContent>{children}</DialogContent>);
|
||||
|
||||
return (
|
||||
<Grid container justify="center" alignItems="center" direction="column" spacing={0}
|
||||
className={classNames(classes.root, className)} {...other}>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.content}>
|
||||
{logo}
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
<footer className="oc-footer-message">
|
||||
<FormattedMessage
|
||||
id="konnect.footer.slogan"
|
||||
defaultMessage="<name>ownCloud</name> - a safe home for all your data"
|
||||
values={{
|
||||
name: chunks => footerProductName(chunks)
|
||||
}}
|
||||
/>
|
||||
</footer>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
ResponsiveScreen.defaultProps = {
|
||||
withoutLogo: false,
|
||||
withoutPadding: false,
|
||||
loading: false
|
||||
};
|
||||
|
||||
ResponsiveScreen.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
withoutLogo: PropTypes.bool,
|
||||
withoutPadding: PropTypes.bool,
|
||||
loading: PropTypes.bool,
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
PaperProps: PropTypes.object,
|
||||
DialogProps: PropTypes.object
|
||||
};
|
||||
|
||||
export default withStyles(styles)(ResponsiveScreen);
|
||||
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import Checkbox from '@material-ui/core/Checkbox';
|
||||
|
||||
import { injectIntl, useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
|
||||
const styles = () => ({
|
||||
row: {
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
}
|
||||
});
|
||||
|
||||
const scopeIDTranslations = defineMessages({
|
||||
'scope_alias_basic': {
|
||||
id: 'konnect.scopeDescription.aliasBasic',
|
||||
defaultMessage: 'Access your basic account information'
|
||||
},
|
||||
'scope_offline_access': {
|
||||
id: 'konnect.scopeDescription.offlineAccess',
|
||||
defaultMessage: 'Keep the allowed access persistently and forever'
|
||||
}
|
||||
});
|
||||
|
||||
const ScopesList = ({scopes, meta, classes, ...rest}) => {
|
||||
const { mapping, definitions } = meta;
|
||||
const intl = useIntl()
|
||||
|
||||
const rows = [];
|
||||
const known = {};
|
||||
|
||||
// TODO(longsleep): Sort scopes according to priority.
|
||||
for (let scope in scopes) {
|
||||
if (!scopes[scope]) {
|
||||
continue;
|
||||
}
|
||||
let id = mapping[scope];
|
||||
if (id) {
|
||||
if (known[id]) {
|
||||
continue;
|
||||
}
|
||||
known[id] = true;
|
||||
} else {
|
||||
id = scope;
|
||||
}
|
||||
let definition = definitions[id];
|
||||
let label ;
|
||||
if (definition) {
|
||||
if (definition.id) {
|
||||
const translation = scopeIDTranslations[definition.id];
|
||||
if (translation) {
|
||||
label = intl.formatMessage(translation);
|
||||
}
|
||||
}
|
||||
if (!label) {
|
||||
label = definition.description;
|
||||
}
|
||||
}
|
||||
if (!label) {
|
||||
label = <FormattedMessage
|
||||
id="konnect.scopeDescription.scope"
|
||||
defaultMessage="Scope: {scope}"
|
||||
values={{scope}}
|
||||
/>;
|
||||
}
|
||||
|
||||
rows.push(
|
||||
<ListItem
|
||||
disableGutters
|
||||
dense
|
||||
key={id}
|
||||
className={classes.row}
|
||||
><Checkbox
|
||||
checked
|
||||
disableRipple
|
||||
disabled
|
||||
className="oc-checkbox-dark"
|
||||
/>
|
||||
<ListItemText primary={label} className="oc-light" />
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<List {...rest}>
|
||||
{rows}
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
ScopesList.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
scopes: PropTypes.object.isRequired,
|
||||
meta: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default withStyles(styles)(injectIntl(ScopesList));
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
const TextInput = (props) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return <input className="oc-input" {...props} placeholder={props.placeholder ? intl.formatMessage(props.placeholder) : null} />;
|
||||
};
|
||||
|
||||
TextInput.propTypes = {
|
||||
placeholder: PropTypes.object,
|
||||
}
|
||||
|
||||
export default TextInput;
|
||||
@@ -0,0 +1,125 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import renderIf from 'render-if';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
|
||||
import ResponsiveScreen from '../../components/ResponsiveScreen';
|
||||
import { executeHello, executeLogoff } from '../../actions/common';
|
||||
|
||||
const styles = theme => ({
|
||||
subHeader: {
|
||||
marginBottom: theme.spacing(5)
|
||||
},
|
||||
wrapper: {
|
||||
marginTop: theme.spacing(5),
|
||||
position: 'relative',
|
||||
display: 'inline-block'
|
||||
}
|
||||
});
|
||||
|
||||
class Goodbyescreen extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
this.props.dispatch(executeHello());
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, hello } = this.props;
|
||||
|
||||
const loading = hello === null;
|
||||
return (
|
||||
<ResponsiveScreen loading={loading}>
|
||||
{renderIf(hello !== null && !hello.state)(() => (
|
||||
<div>
|
||||
<Typography variant="h5" component="h3">
|
||||
<FormattedMessage id="konnect.goodbye.headline" defaultMessage="Goodbye"></FormattedMessage>
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader}>
|
||||
<FormattedMessage id="konnect.goodbye.subHeader"
|
||||
defaultMessage="you have been signed out from your Kopano account">
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
<Typography gutterBottom>
|
||||
<FormattedMessage id="konnect.goodbye.message.close"
|
||||
defaultMessage="You can close this window now.">
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
</div>
|
||||
))}
|
||||
{renderIf(hello !== null && hello.state === true)(() => (
|
||||
<div>
|
||||
<Typography variant="h5" component="h3">
|
||||
<FormattedMessage
|
||||
id="konnect.goodbye.confirm.headline"
|
||||
defaultMessage="Hello {displayName}"
|
||||
values={{displayName: hello.displayName}}>
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader}>
|
||||
<FormattedMessage id="konnect.goodbye.confirm.subHeader"
|
||||
defaultMessage="please confirm sign out">
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
|
||||
<Typography gutterBottom>
|
||||
<FormattedMessage id="konnect.goodbye.message.confirm"
|
||||
defaultMessage="Press the button below, to sign out from your Kopano account now.">
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
|
||||
<DialogActions>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
onClick={(event) => this.logoff(event)}
|
||||
>
|
||||
<FormattedMessage id="konnect.goodbye.signoutButton.label"
|
||||
defaultMessage="Sign out"></FormattedMessage>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogActions>
|
||||
</div>
|
||||
))}
|
||||
</ResponsiveScreen>
|
||||
);
|
||||
}
|
||||
|
||||
logoff(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatch(executeLogoff()).then((response) => {
|
||||
const { history } = this.props;
|
||||
|
||||
if (response.success) {
|
||||
this.props.dispatch(executeHello());
|
||||
history.push('/goodbye');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Goodbyescreen.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
hello: PropTypes.object,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hello } = state.common;
|
||||
|
||||
return {
|
||||
hello
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Goodbyescreen));
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './Goodbyescreen';
|
||||
@@ -0,0 +1,144 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import { executeLogonIfFormValid, advanceLogonFlow } from '../../actions/login';
|
||||
import { ErrorMessage } from '../../errors';
|
||||
|
||||
const styles = theme => ({
|
||||
subHeader: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
message: {
|
||||
marginTop: theme.spacing(2)
|
||||
},
|
||||
accountList: {
|
||||
marginLeft: theme.spacing(-3),
|
||||
marginRight: theme.spacing(-3)
|
||||
},
|
||||
accountListItem: {
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingRight: theme.spacing(3)
|
||||
}
|
||||
});
|
||||
|
||||
function Chooseaccount({ loading, errors, classes, hello, history, dispatch }) {
|
||||
useEffect(() => {
|
||||
if ((!hello || !hello.state) && history.action !== 'PUSH') {
|
||||
history.replace(`/identifier${history.location.search}${history.location.hash}`);
|
||||
}
|
||||
});
|
||||
|
||||
let errorMessage = null;
|
||||
|
||||
if (errors.http) {
|
||||
errorMessage = <Typography color="error" className={classes.message}>
|
||||
<ErrorMessage error={errors.http}></ErrorMessage>
|
||||
</Typography>;
|
||||
}
|
||||
|
||||
let username = '';
|
||||
|
||||
if (hello && hello.state) {
|
||||
username = hello.username;
|
||||
}
|
||||
|
||||
const logon = (event) => {
|
||||
event.preventDefault();
|
||||
dispatch(executeLogonIfFormValid(hello.username, '', true)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const logoff = (event) => {
|
||||
event.preventDefault();
|
||||
history.push(`/identifier${history.location.search}${history.location.hash}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography variant="h5" component="h3" className="oc-light">
|
||||
<FormattedMessage id="konnect.chooseaccount.headline" defaultMessage="Choose an account"></FormattedMessage>
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader + " oc-light"}>
|
||||
<FormattedMessage id="konnect.chooseaccount.subHeader" defaultMessage="to sign in to Kopano">
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
|
||||
<form action="" onSubmit={(event) => logon(event)}>
|
||||
<List disablePadding className={classes.accountList}>
|
||||
<ListItem
|
||||
button
|
||||
disableGutters
|
||||
className={classes.accountListItem}
|
||||
disabled={!!loading}
|
||||
onClick={(event) => logon(event)}
|
||||
><ListItemAvatar><Avatar>{username.substr(0, 1)}</Avatar></ListItemAvatar>
|
||||
<ListItemText className="oc-light" primary={username} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
disableGutters
|
||||
className={classes.accountListItem}
|
||||
disabled={!!loading}
|
||||
onClick={(event) => logoff(event)}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<FormattedMessage id="konnect.chooseaccount.useOther.persona.label" defaultMessage="?">
|
||||
</FormattedMessage>
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
className="oc-light"
|
||||
primary={
|
||||
<FormattedMessage
|
||||
id="konnect.chooseaccount.useOther.label"
|
||||
defaultMessage="Use another account">
|
||||
</FormattedMessage>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
{errorMessage}
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Chooseaccount.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
loading: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
hello: PropTypes.object,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { loading, errors } = state.login;
|
||||
const { hello } = state.common;
|
||||
|
||||
return {
|
||||
loading,
|
||||
errors,
|
||||
hello
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Chooseaccount));
|
||||
@@ -0,0 +1,187 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import renderIf from 'render-if';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import green from '@material-ui/core/colors/green';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
|
||||
import { executeConsent, advanceLogonFlow, receiveValidateLogon } from '../../actions/login';
|
||||
import { ErrorMessage } from '../../errors';
|
||||
import { REQUEST_CONSENT_ALLOW } from '../../actions/types';
|
||||
import ClientDisplayName from '../../components/ClientDisplayName';
|
||||
import ScopesList from '../../components/ScopesList';
|
||||
|
||||
const styles = theme => ({
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: -12,
|
||||
marginLeft: -12
|
||||
},
|
||||
scopesList: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
wrapper: {
|
||||
marginTop: theme.spacing(2),
|
||||
position: 'relative',
|
||||
display: 'inline-block'
|
||||
},
|
||||
message: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2)
|
||||
}
|
||||
});
|
||||
|
||||
class Consent extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
const { dispatch, hello, history, client } = this.props;
|
||||
if ((!hello || !hello.state || !client) && history.action !== 'PUSH') {
|
||||
history.replace(`/identifier${history.location.search}${history.location.hash}`);
|
||||
}
|
||||
|
||||
dispatch(receiveValidateLogon({})); // XXX(longsleep): hack to reset loading and errors.
|
||||
}
|
||||
|
||||
action = (allow=false, scopes={}) => (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (allow === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert all scopes which are true to a scope value.
|
||||
const scope = Object.keys(scopes).filter(scope => {
|
||||
return !!scopes[scope];
|
||||
}).join(' ');
|
||||
|
||||
const { dispatch, history } = this.props;
|
||||
dispatch(executeConsent(allow, scope)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history, true, {konnect: response.state}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, loading, hello, errors, client } = this.props;
|
||||
|
||||
const scopes = hello.details.scopes || {};
|
||||
const meta = hello.details.meta || {};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography variant="h5" component="h3" className="oc-light">
|
||||
<FormattedMessage
|
||||
id="konnect.consent.headline"
|
||||
defaultMessage="Hi {displayName}"
|
||||
values={{displayName: hello.displayName}}
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className="oc-light oc-mb-m">
|
||||
{hello.username}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="subtitle1" gutterBottom className="oc-light">
|
||||
<FormattedMessage
|
||||
id="konnect.consent.message"
|
||||
defaultMessage="{clientDisplayName} wants to"
|
||||
values={{clientDisplayName:
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={<FormattedMessage
|
||||
id="konnect.consent.tooltip.client"
|
||||
defaultMessage='Clicking "Allow" will redirect you to: {redirectURI}'
|
||||
values={{
|
||||
redirectURI: client.redirect_uri
|
||||
}}
|
||||
></FormattedMessage>}
|
||||
>
|
||||
<em><ClientDisplayName client={client}/></em>
|
||||
</Tooltip>
|
||||
}}
|
||||
></FormattedMessage>
|
||||
</Typography>
|
||||
<ScopesList dense disablePadding className={classes.scopesList} scopes={scopes} meta={meta.scopes}></ScopesList>
|
||||
|
||||
<Typography className="oc-light">
|
||||
<FormattedMessage
|
||||
id="konnect.consent.consequence"
|
||||
defaultMessage="By clicking Allow, you allow this app to use your information.">
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
|
||||
<form action="" onSubmit={this.action(undefined, scopes)}>
|
||||
<DialogActions>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
disabled={!!loading}
|
||||
onClick={this.action(false, scopes)}
|
||||
>
|
||||
<FormattedMessage id="konnect.consent.cancelButton.label" defaultMessage="Cancel"></FormattedMessage>
|
||||
</Button>
|
||||
{(loading && loading !== REQUEST_CONSENT_ALLOW) &&
|
||||
<CircularProgress size={24} className={classes.buttonProgress} />}
|
||||
</div>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary"
|
||||
disabled={!!loading}
|
||||
onClick={this.action(true, scopes)}
|
||||
>
|
||||
<FormattedMessage id="konnect.consent.allowButton.label" defaultMessage="Allow"></FormattedMessage>
|
||||
</Button>
|
||||
{loading === REQUEST_CONSENT_ALLOW && <CircularProgress size={24} className={classes.buttonProgress} />}
|
||||
</div>
|
||||
</DialogActions>
|
||||
|
||||
{renderIf(errors.http)(() => (
|
||||
<Typography variant="subtitle2" color="error" className={classes.message}>
|
||||
<ErrorMessage error={errors.http}></ErrorMessage>
|
||||
</Typography>
|
||||
))}
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Consent.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
loading: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
hello: PropTypes.object,
|
||||
client: PropTypes.object.isRequired,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hello } = state.common;
|
||||
const { loading, errors } = state.login;
|
||||
|
||||
return {
|
||||
loading: loading,
|
||||
errors,
|
||||
hello,
|
||||
client: hello.details.client || {}
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Consent));
|
||||
@@ -0,0 +1,148 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import green from '@material-ui/core/colors/green';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import TextInput from '../../components/TextInput'
|
||||
|
||||
import { updateInput, executeLogonIfFormValid, advanceLogonFlow } from '../../actions/login';
|
||||
import { ErrorMessage } from '../../errors';
|
||||
|
||||
const styles = theme => ({
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: -12,
|
||||
marginLeft: -12
|
||||
},
|
||||
subHeader: {
|
||||
marginBottom: theme.spacing(3)
|
||||
},
|
||||
wrapper: {
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
textAlign: 'center'
|
||||
},
|
||||
message: {
|
||||
marginTop: 5,
|
||||
marginBottom: 5
|
||||
}
|
||||
});
|
||||
|
||||
class Login extends React.PureComponent {
|
||||
state = {};
|
||||
|
||||
componentDidMount() {
|
||||
const { hello, query, dispatch, history } = this.props;
|
||||
if (hello && hello.state && history.action !== 'PUSH') {
|
||||
if (!query.prompt || query.prompt.indexOf('select_account') == -1) {
|
||||
dispatch(advanceLogonFlow(true, history));
|
||||
return;
|
||||
}
|
||||
|
||||
history.replace(`/chooseaccount${history.location.search}${history.location.hash}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, errors, classes, username } = this.props;
|
||||
const hasError = errors.http || errors.username || errors.password;
|
||||
const errorMessage = errors.http
|
||||
? <ErrorMessage error={errors.http}></ErrorMessage>
|
||||
: (errors.username
|
||||
? <ErrorMessage error={errors.username}></ErrorMessage>
|
||||
: <ErrorMessage error={errors.password}></ErrorMessage>);
|
||||
|
||||
return (
|
||||
<form action="" onSubmit={(event) => this.logon(event)}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={username}
|
||||
onChange={this.handleChange('username')}
|
||||
autoComplete="kopano-account username"
|
||||
placeholder={({ id: "konnect.login.usernameField.label", defaultMessage: "Username" })}
|
||||
/>
|
||||
<TextInput
|
||||
type="password"
|
||||
margin="normal"
|
||||
onChange={this.handleChange('password')}
|
||||
autoComplete="kopano-account current-password"
|
||||
placeholder={({ id: "konnect.login.usernameField.label", defaultMessage: "Password" })}
|
||||
/>
|
||||
{hasError && <Typography variant="subtitle2" color="error" className={classes.message}>{errorMessage}</Typography>}
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary oc-mt-l"
|
||||
disabled={!!loading}
|
||||
onClick={(event) => this.logon(event)}
|
||||
>
|
||||
<FormattedMessage id="konnect.login.nextButton.label" defaultMessage="Log in"></FormattedMessage>
|
||||
</Button>
|
||||
{loading && <CircularProgress size={24} className={classes.buttonProgress} />}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
handleChange(name) {
|
||||
return event => {
|
||||
this.props.dispatch(updateInput(name, event.target.value));
|
||||
};
|
||||
}
|
||||
|
||||
logon(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const { username, password, dispatch, history } = this.props;
|
||||
dispatch(executeLogonIfFormValid(username, password, false)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Login.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
loading: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
password: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
hello: PropTypes.object,
|
||||
query: PropTypes.object.isRequired,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { loading, username, password, errors} = state.login;
|
||||
const { hello, query } = state.common;
|
||||
|
||||
return {
|
||||
loading,
|
||||
username,
|
||||
password,
|
||||
errors,
|
||||
hello,
|
||||
query
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Login));
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
import ResponsiveScreen from '../../components/ResponsiveScreen';
|
||||
import RedirectWithQuery from '../../components/RedirectWithQuery';
|
||||
import { executeHello } from '../../actions/common';
|
||||
|
||||
import Login from './Login';
|
||||
import Chooseaccount from './Chooseaccount';
|
||||
import Consent from './Consent';
|
||||
|
||||
const styles = () => ({
|
||||
});
|
||||
|
||||
class Loginscreen extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
this.props.dispatch(executeHello());
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hello } = this.props;
|
||||
|
||||
const loading = hello === null;
|
||||
return (
|
||||
<ResponsiveScreen loading={loading} withoutPadding={true} >
|
||||
<Switch>
|
||||
<Route path="/identifier" exact component={Login}></Route>
|
||||
<Route path="/chooseaccount" exact component={Chooseaccount}></Route>
|
||||
<Route path="/consent" exact component={Consent}></Route>
|
||||
<RedirectWithQuery target="/identifier"/>
|
||||
</Switch>
|
||||
</ResponsiveScreen>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loginscreen.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
hello: PropTypes.object,
|
||||
|
||||
dispatch: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hello } = state.common;
|
||||
|
||||
return {
|
||||
hello
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Loginscreen));
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './Loginscreen';
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
|
||||
import ResponsiveScreen from '../../components/ResponsiveScreen';
|
||||
import { executeLogoff } from '../../actions/common';
|
||||
|
||||
const styles = theme => ({
|
||||
button: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 100
|
||||
},
|
||||
subHeader: {
|
||||
marginBottom: theme.spacing(5)
|
||||
}
|
||||
});
|
||||
|
||||
class Welcomescreen extends React.PureComponent {
|
||||
render() {
|
||||
const { classes, hello } = this.props;
|
||||
|
||||
const loading = hello === null;
|
||||
return (
|
||||
<ResponsiveScreen loading={loading}>
|
||||
<Typography variant="h5" component="h3" className="oc-light">
|
||||
<FormattedMessage
|
||||
id="konnect.welcome.headline"
|
||||
defaultMessage="Welcome {displayName}"
|
||||
values={{displayName: hello.displayName}}>
|
||||
</FormattedMessage>
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader + " oc-light"}>
|
||||
{hello.username}
|
||||
</Typography>
|
||||
|
||||
<Typography gutterBottom className="oc-light">
|
||||
<FormattedMessage id="konnect.welcome.message"
|
||||
defaultMessage="You are signed in - awesome!"></FormattedMessage>
|
||||
</Typography>
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
variant="contained"
|
||||
onClick={(event) => this.logoff(event)}
|
||||
>
|
||||
<FormattedMessage id="konnect.welcome.signoutButton.label" defaultMessage="Sign out"></FormattedMessage>
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveScreen>
|
||||
);
|
||||
}
|
||||
|
||||
logoff(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatch(executeLogoff()).then((response) => {
|
||||
const { history } = this.props;
|
||||
|
||||
if (response.success) {
|
||||
history.push('/identifier');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Welcomescreen.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
hello: PropTypes.object,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hello } = state.common;
|
||||
|
||||
return {
|
||||
hello
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Welcomescreen));
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './Welcomescreen';
|
||||
@@ -0,0 +1,68 @@
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
export const ERROR_LOGIN_VALIDATE_MISSINGUSERNAME = 'konnect.error.login.validate.missingUsername';
|
||||
export const ERROR_LOGIN_VALIDATE_MISSINGPASSWORD = 'konnect.error.login.validate.missingPassword';
|
||||
export const ERROR_LOGIN_FAILED = 'konnect.error.login.failed';
|
||||
export const ERROR_HTTP_NETWORK_ERROR = 'konnet.error.http.networkError';
|
||||
export const ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS = 'konnect.error.http.unexpectedResponseStatus';
|
||||
export const ERROR_HTTP_UNEXPECTED_RESPONSE_STATE = 'konnect.error.http.unexpectedResponseState';
|
||||
|
||||
// Translatable error messages.
|
||||
const translations = defineMessages({
|
||||
[ERROR_LOGIN_VALIDATE_MISSINGUSERNAME]: {
|
||||
id: ERROR_LOGIN_VALIDATE_MISSINGUSERNAME,
|
||||
defaultMessage: 'Enter an username'
|
||||
},
|
||||
[ERROR_LOGIN_VALIDATE_MISSINGPASSWORD]: {
|
||||
id: ERROR_LOGIN_VALIDATE_MISSINGPASSWORD,
|
||||
defaultMessage: 'Enter a password'
|
||||
},
|
||||
[ERROR_LOGIN_FAILED]: {
|
||||
id: ERROR_LOGIN_FAILED,
|
||||
defaultMessage: 'Logon failed. Please verify your credentials and try again.'
|
||||
},
|
||||
[ERROR_HTTP_NETWORK_ERROR]: {
|
||||
id: ERROR_HTTP_NETWORK_ERROR,
|
||||
defaultMessage: 'Network error. Please check your connection and try again.'
|
||||
},
|
||||
[ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS]: {
|
||||
id: ERROR_HTTP_UNEXPECTED_RESPONSE_STATUS,
|
||||
defaultMessage: 'Unexpected HTTP response: {status}. Please check your connection and try again.'
|
||||
},
|
||||
[ERROR_HTTP_UNEXPECTED_RESPONSE_STATE]: {
|
||||
id: ERROR_HTTP_UNEXPECTED_RESPONSE_STATE,
|
||||
defaultMessage: 'Unexpected response state: {state}'
|
||||
}
|
||||
});
|
||||
|
||||
// Error with values.
|
||||
export class ExtendedError extends Error {
|
||||
values = undefined;
|
||||
|
||||
constructor(message, values) {
|
||||
super(message);
|
||||
if (Error.captureStackTrace !== undefined) {
|
||||
Error.captureStackTrace(this, ExtendedError);
|
||||
}
|
||||
this.values = values;
|
||||
}
|
||||
}
|
||||
|
||||
// Component to translate error text with values.
|
||||
function ErrorMessageComponent(props) {
|
||||
const { error, intl } = props;
|
||||
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const id = error.id ? error.id : error.message;
|
||||
const messageDescriptor = Object.assign({}, {
|
||||
id,
|
||||
defaultMessage: error.id ? error.message : undefined
|
||||
}, translations[id]);
|
||||
|
||||
return intl.formatMessage(messageDescriptor, error.values);
|
||||
}
|
||||
|
||||
export const ErrorMessage = injectIntl(ErrorMessageComponent);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,13 @@
|
||||
import 'kpop/static/css/base.css';
|
||||
import 'kpop/static/css/scrollbar.css';
|
||||
import './app.css';
|
||||
|
||||
import * as kpop from 'kpop/es/version';
|
||||
|
||||
import * as version from './version';
|
||||
|
||||
console.info(`Kopano Identifier build version: ${version.build}`); // eslint-disable-line no-console
|
||||
console.info(`Kopano Kpop build version: ${kpop.build}`); // eslint-disable-line no-console
|
||||
|
||||
// NOTE(longsleep): Load async, this enables code splitting via Webpack.
|
||||
import(/* webpackChunkName: "identifier-app" */ './app');
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "Verbindung zum Server fehlgeschlagen",
|
||||
"konnect.login.retryButton.label": "Wiederholen",
|
||||
"konnect.scopeDescription.aliasBasic": "Zugriff auf Ihre grundlegenden Benutzerinformationen",
|
||||
"konnect.scopeDescription.offlineAccess": "Dauerhaften Zugriff (läuft nicht ab)",
|
||||
"konnect.scopeDescription.scope": "Geltungsbereich: {scope}",
|
||||
"konnect.goodbye.headline": "Bis bald",
|
||||
"konnect.goodbye.subHeader": "sie sind jetzt von Ihrem Kopano Konto abgemeldet",
|
||||
"konnect.goodbye.message.close": "Sie können dieses Fenster jetzt schließen.",
|
||||
"konnect.goodbye.confirm.headline": "Hallo {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "Bitte bestätigen Sie, dass Sie sich abmelden möchten",
|
||||
"konnect.goodbye.message.confirm": "Klicken Sie auf die Schaltfläche unten um sich aus Ihrem Kopano Konto abzumelden.",
|
||||
"konnect.goodbye.signoutButton.label": "Abmelden",
|
||||
"konnect.welcome.signoutButton.label": "Abmelden",
|
||||
"konnect.chooseaccount.headline": "Konto auswählen",
|
||||
"konnect.chooseaccount.subHeader": "um sich bei Kopano anzumelden",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Anderes Konto",
|
||||
"konnect.consent.headline": "Hi {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} möchte",
|
||||
"konnect.consent.tooltip.client": "Wenn Sie \"Einverstanden\" klicken werden Sie zu {redirectURI} weitergeleitet",
|
||||
"konnect.consent.question": "{clientDisplayName} den Zugriff gestatten?",
|
||||
"konnect.consent.consequence": "Wenn Sie Einverstanden klicken, erhält die App Zugriff auf Ihre Informationen.",
|
||||
"konnect.consent.cancelButton.label": "Abbrechen",
|
||||
"konnect.consent.allowButton.label": "Einverstanden",
|
||||
"konnect.login.headline": "Anmelden",
|
||||
"konnect.login.subHeader": "mit Ihrem Kopano Konto",
|
||||
"konnect.login.usernameField.label": "Benutzername",
|
||||
"konnect.login.passwordField.label": "Passwort",
|
||||
"konnect.login.nextButton.label": "Weiter",
|
||||
"konnect.welcome.headline": "Willkommen {displayName}",
|
||||
"konnect.welcome.message": "Sie sind angemeldet - super!",
|
||||
"konnect.error.login.validate.missingUsername": "Geben Sie einen Benutzername ein",
|
||||
"konnect.error.login.validate.missingPassword": "Geben Sie ein Passwort ein",
|
||||
"konnect.error.login.failed": "Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Eingabe und versuchen Sie es noch einmal.",
|
||||
"konnet.error.http.networkError": "Netzwerkfehler. Bitte prüfen Sie Ihre Verbindung und versuchen Sie es noch einmal.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Unerwartete HTTP-Antwort: {status}. Bitte prüfen Sie Ihre Verbindung und versuchen Sie es noch einmal.",
|
||||
"konnect.error.http.unexpectedResponseState": "Unerwarteter Antwort-Status: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "La connexion au serveur a échoué",
|
||||
"konnect.login.retryButton.label": "Réessayer",
|
||||
"konnect.scopeDescription.aliasBasic": "Consulter les informations de base de votre compte",
|
||||
"konnect.scopeDescription.offlineAccess": "Conserver les autorisations d'accès à l'avenir",
|
||||
"konnect.scopeDescription.scope": "Portée : {scope}",
|
||||
"konnect.goodbye.headline": "Au revoir",
|
||||
"konnect.goodbye.subHeader": "vous avez été déconnecté de Kopano",
|
||||
"konnect.goodbye.message.close": "Vous pouvez fermer cette fenêtre à présent.",
|
||||
"konnect.goodbye.confirm.headline": "Bonjour {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "Confirmer votre déconnexion",
|
||||
"konnect.goodbye.message.confirm": "Cliquer le bouton ci-dessous, pour quitter Kopano.",
|
||||
"konnect.goodbye.signoutButton.label": "Quitter",
|
||||
"konnect.welcome.signoutButton.label": "Quitter",
|
||||
"konnect.chooseaccount.headline": "Choisir un compte",
|
||||
"konnect.chooseaccount.subHeader": "pour vous authentifier dans Kopano",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Utiliser un autre compte",
|
||||
"konnect.consent.headline": "Bonjour {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} souhaite",
|
||||
"konnect.consent.tooltip.client": "En cliquant \"Autoriser\" vous serez redirigé vers : {redirectURI}",
|
||||
"konnect.consent.question": "Autoriser {clientDisplayName} à faire cela ?",
|
||||
"konnect.consent.consequence": "En cliquant, vous autoriser l'app à accéder à vos informations.",
|
||||
"konnect.consent.cancelButton.label": "Annuler",
|
||||
"konnect.consent.allowButton.label": "Autoriser",
|
||||
"konnect.login.headline": "Identification",
|
||||
"konnect.login.subHeader": "avec vos identifiants Kopano",
|
||||
"konnect.login.usernameField.label": "Utilisateur",
|
||||
"konnect.login.passwordField.label": "Mot de passe",
|
||||
"konnect.login.nextButton.label": "Suivant",
|
||||
"konnect.welcome.headline": "Bienvenue {displayName}",
|
||||
"konnect.welcome.message": "Magnifique - Vous êtes connecté !",
|
||||
"konnect.error.login.validate.missingUsername": "Saisir un identifiant",
|
||||
"konnect.error.login.validate.missingPassword": "Saisir un mot de passe",
|
||||
"konnect.error.login.failed": "Echec de connexion. Vérifier vos identifiants et essayer à nouveau.",
|
||||
"konnet.error.http.networkError": "Erreur réseau. Vérifier votre connexion, et réessayer.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Erreur HTTP inattendue : {status}. Vérifier votre connexion et réessayer.",
|
||||
"konnect.error.http.unexpectedResponseState": "Erreur d'état inattendue : {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "सर्वर से कनेक्ट करने में विफल",
|
||||
"konnect.login.retryButton.label": "पुन: प्रयास करें",
|
||||
"konnect.scopeDescription.aliasBasic": "अपनी मूल खाता जानकारी देखे",
|
||||
"konnect.scopeDescription.offlineAccess": "अनुमत पहुंच को लगातार और हमेशा बनाए रखें",
|
||||
"konnect.scopeDescription.scope": "क्षेत्र: {scope}",
|
||||
"konnect.goodbye.headline": "अलविदा",
|
||||
"konnect.goodbye.subHeader": "आपको अपने Kopano खाते से साइन आउट कर दिया गया है",
|
||||
"konnect.goodbye.message.close": "अब आप इस विंडो को बंद कर सकते हैं.",
|
||||
"konnect.goodbye.confirm.headline": "नमस्ते {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "कृपया साइन आउट की पुष्टि करें",
|
||||
"konnect.goodbye.message.confirm": "अपने Kopano खाते से साइन आउट करने के लिए नीचे दिए गए बटन को दबाएं.",
|
||||
"konnect.goodbye.signoutButton.label": "साइन आउट",
|
||||
"konnect.welcome.signoutButton.label": "साइन आउट",
|
||||
"konnect.chooseaccount.headline": "खाता चुनें",
|
||||
"konnect.chooseaccount.subHeader": "Kopano में साइन इन करने के लिए",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "दूसरे खाते का उपयोग करें",
|
||||
"konnect.consent.headline": "नमस्ते {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} चाहते है की",
|
||||
"konnect.consent.tooltip.client": "\"अनुमति\" पर क्लिक करने से आपको {redirecturI} पे पुनर्निर्देशित किया जायेगा",
|
||||
"konnect.consent.question": "क्या {clientDisplayName} को ये करने की अनुमति देना चाहते है?",
|
||||
"konnect.consent.consequence": "अनुमति पर क्लिक करके, आप इस एप्लिकेशन को आपकी जानकारी का उपयोग करने की अनुमति देते हैं.",
|
||||
"konnect.consent.cancelButton.label": "रद्द करें",
|
||||
"konnect.consent.allowButton.label": "अनुमति दीजिये",
|
||||
"konnect.login.headline": "साइन इन",
|
||||
"konnect.login.subHeader": "अपने Kopano खाते के साथ",
|
||||
"konnect.login.usernameField.label": "उपयोगकर्ता नाम",
|
||||
"konnect.login.passwordField.label": "पासवर्ड",
|
||||
"konnect.login.nextButton.label": "अगला",
|
||||
"konnect.welcome.headline": "स्वागत हे {displayName}",
|
||||
"konnect.welcome.message": "आप साइंड इन हैं - अद्भुत!",
|
||||
"konnect.error.login.validate.missingUsername": "उपयोगकर्ता नाम डालिये",
|
||||
"konnect.error.login.validate.missingPassword": "पासवर्ड डालिए",
|
||||
"konnect.error.login.failed": "लोगऑन नाकाम रहा. कृपया अपने क्रेडेंशियल्स जांचे और पुनः प्रयास करें.",
|
||||
"konnet.error.http.networkError": "नेटवर्क त्रुटि। कृपया अपने संपर्क की जांच करे और फिर से प्रयास करें.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "अनपेक्षित HTTP प्रतिक्रिया: {status}. कृपया अपने संपर्क की जांच करे और फिर से प्रयास करें.",
|
||||
"konnect.error.http.unexpectedResponseState": "अनपेक्षित प्रतिक्रिया अवस्था: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// NOTE(longsleep): This loads all translation files to be included in the
|
||||
// app bundle. They are not that large.
|
||||
|
||||
// Please keep imports and exports alphabetically sorted.
|
||||
import de from './de.json';
|
||||
import fr from './fr.json';
|
||||
import hi from './hi.json';
|
||||
import is from './is.json';
|
||||
import nb from './nb.json';
|
||||
import nl from './nl.json';
|
||||
import ptPT from './pt_PT.json';
|
||||
import ru from './ru.json';
|
||||
|
||||
function enableLocales(locales, enabled) {
|
||||
if (process.env.NODE_ENV !== 'production') { // eslint-disable-line no-undef
|
||||
return locales;
|
||||
}
|
||||
return enabled.reduce(function(value, locale) {
|
||||
value[locale] = locales[locale];
|
||||
return value;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// Locales must follow BCP 47 format (https://tools.ietf.org/html/rfc5646).
|
||||
const locales = enableLocales({
|
||||
de,
|
||||
'en-GB': {},
|
||||
'en-US': {},
|
||||
fr,
|
||||
hi,
|
||||
is,
|
||||
nb,
|
||||
nl,
|
||||
'pt-PT': ptPT,
|
||||
ru
|
||||
}, [
|
||||
// List of enabled languages in production builds.
|
||||
'de',
|
||||
'en-GB',
|
||||
'en-US',
|
||||
'fr',
|
||||
'hi',
|
||||
'is',
|
||||
'nb',
|
||||
'nl',
|
||||
'pt-PT',
|
||||
'ru'
|
||||
]);
|
||||
|
||||
export default locales;
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "Mistókst að tengjast netþjóni",
|
||||
"konnect.login.retryButton.label": "Reyna aftur",
|
||||
"konnect.scopeDescription.aliasBasic": "Komast í grunnupplýsingar um þig",
|
||||
"konnect.scopeDescription.offlineAccess": "Viðhalda heimildunum alltaf og að eilífu (mundu mig)",
|
||||
"konnect.scopeDescription.scope": "Gildissvið: {scope}",
|
||||
"konnect.goodbye.headline": "Bless",
|
||||
"konnect.goodbye.subHeader": "útskráning Kopano-aðgangsins þíns tókst",
|
||||
"konnect.goodbye.message.close": "Þú getur lokað þessum glugga núna.",
|
||||
"konnect.goodbye.confirm.headline": "Halló {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "vinsamlegast staðfestu útskráningu",
|
||||
"konnect.goodbye.message.confirm": "Smelltu á takkann fyrir neðan til að skrá þig út af Kopano aðganginum núna.",
|
||||
"konnect.goodbye.signoutButton.label": "Útskrá",
|
||||
"konnect.welcome.signoutButton.label": "Útskrá",
|
||||
"konnect.chooseaccount.headline": "Veldu aðgang",
|
||||
"konnect.chooseaccount.subHeader": "til að skrá þig inn í Kopano",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Nota annan aðgang",
|
||||
"konnect.consent.headline": "Hæ {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} vill",
|
||||
"konnect.consent.tooltip.client": "Þegar þú smellir á \"Leyfa\" áframsendist þú á: {redirectURI}",
|
||||
"konnect.consent.question": "Leyfa {clientDisplayName} að gera þetta?",
|
||||
"konnect.consent.consequence": "Með því að smella á \"Leyfa\", leyfir þú þessu forriti að nota upplýsingarnar um þig.",
|
||||
"konnect.consent.cancelButton.label": "Hætta við",
|
||||
"konnect.consent.allowButton.label": "Leyfa",
|
||||
"konnect.login.headline": "Innskrá",
|
||||
"konnect.login.subHeader": "með Kopano-aðganginum þínum",
|
||||
"konnect.login.usernameField.label": "Notandanafn",
|
||||
"konnect.login.passwordField.label": "Lykilorð",
|
||||
"konnect.login.nextButton.label": "Næsta",
|
||||
"konnect.welcome.headline": "Halló {displayName}",
|
||||
"konnect.welcome.message": "Innskráningin tókst - frábært!",
|
||||
"konnect.error.login.validate.missingUsername": "Sláðu inn notandanafn",
|
||||
"konnect.error.login.validate.missingPassword": "Sláðu inn lykilorð",
|
||||
"konnect.error.login.failed": "Innskráning mistókst. Vinsamlegast staðfestu notandaupplýsingarnar og reyndu aftur.",
|
||||
"konnet.error.http.networkError": "Netvilla. Vinsamlegast athugaðu tenginguna þína og reyndu aftur.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Ófyrirsjáanlegt HTTP svar: {status}. Vinsamlegast athugaðu tenginguna þína og reyndu aftur.",
|
||||
"konnect.error.http.unexpectedResponseState": "Ófyrirsjáanleg svarstaða: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "Klarte ikke å forbinde med server",
|
||||
"konnect.login.retryButton.label": "Prøv igjen",
|
||||
"konnect.scopeDescription.aliasBasic": "Tilgang til informasjon om basiskonto",
|
||||
"konnect.scopeDescription.offlineAccess": "Behold den tillate tilgangen for alltid",
|
||||
"konnect.scopeDescription.scope": "Omfang: {scope}",
|
||||
"konnect.goodbye.headline": "Farvel",
|
||||
"konnect.goodbye.subHeader": "du har blitt logget ut fra din Kopano konto",
|
||||
"konnect.goodbye.message.close": "Du kan lukke dette vinduet nå.",
|
||||
"konnect.goodbye.confirm.headline": "Hallo {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "vennligst bekreft utlogging",
|
||||
"konnect.goodbye.message.confirm": "Trykk på knappen under, for å logge ut av din Kopano konto.",
|
||||
"konnect.goodbye.signoutButton.label": "Logg ut",
|
||||
"konnect.welcome.signoutButton.label": "Logg ut",
|
||||
"konnect.chooseaccount.headline": "Velg en konto",
|
||||
"konnect.chooseaccount.subHeader": "for å logge inn i Kopano",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Bruk en annen konto",
|
||||
"konnect.consent.headline": "Hei {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} ønsker",
|
||||
"konnect.consent.tooltip.client": "Ved å klikke på \"Tillatt\" så vil du bli ledet til: {redirectURI}",
|
||||
"konnect.consent.question": "Tillatt {clientDisplayName} å gjøre dette?",
|
||||
"konnect.consent.consequence": "Ved å klikke på Aksepter, så tillater du at appen bruker din informasjon.",
|
||||
"konnect.consent.cancelButton.label": "Avbryt",
|
||||
"konnect.consent.allowButton.label": "Tillat",
|
||||
"konnect.login.headline": "Logg inn",
|
||||
"konnect.login.subHeader": "med din Kopano konto",
|
||||
"konnect.login.usernameField.label": "Brukernavn",
|
||||
"konnect.login.passwordField.label": "Passord",
|
||||
"konnect.login.nextButton.label": "Neste",
|
||||
"konnect.welcome.headline": "Velkommen {displayName}",
|
||||
"konnect.welcome.message": "Du er logget på!",
|
||||
"konnect.error.login.validate.missingUsername": "Skriv inn et brukernavn",
|
||||
"konnect.error.login.validate.missingPassword": "Skriv inn et passord",
|
||||
"konnect.error.login.failed": "Logg inn feilet. Vennligst sjekk brukernavn/passord, og forsøk igjen.",
|
||||
"konnet.error.http.networkError": "Nettverksfeil. Sjekk din forbindelse, og forsøk igjen.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Uventet HTTP respons: {status}. Sjekk tilkoblingen din og prøv igjen.",
|
||||
"konnect.error.http.unexpectedResponseState": "Uventet svar-status: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "Kon niet met server verbinden",
|
||||
"konnect.login.retryButton.label": "Opnieuw",
|
||||
"konnect.scopeDescription.aliasBasic": "Basis accountgegevens weergeven",
|
||||
"konnect.scopeDescription.offlineAccess": "De toestemming voor altijd onthouden",
|
||||
"konnect.scopeDescription.scope": "Scope: {scope}",
|
||||
"konnect.goodbye.headline": "Tot ziens",
|
||||
"konnect.goodbye.subHeader": "je bent afgemeld van je Kopano account",
|
||||
"konnect.goodbye.message.close": "Dit venster kan nu worden gesloten.",
|
||||
"konnect.goodbye.confirm.headline": "Hallo {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "bevestig afmelden",
|
||||
"konnect.goodbye.message.confirm": "Klik op onderstaande knop om af te melden van je Kopano account.",
|
||||
"konnect.goodbye.signoutButton.label": "Afmelden",
|
||||
"konnect.welcome.signoutButton.label": "Afmelden",
|
||||
"konnect.chooseaccount.headline": "Account kiezen",
|
||||
"konnect.chooseaccount.subHeader": "om aan te melden bij Kopano",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Gebruik een ander account",
|
||||
"konnect.consent.headline": "Hoi {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} wil",
|
||||
"konnect.consent.tooltip.client": "Door op \"Toestaan\" te klikken word je doorverwezen naar: {redirectURI}",
|
||||
"konnect.consent.question": "{clientDisplayName} toestaan dit te doen?",
|
||||
"konnect.consent.consequence": "Door op Toestaan te klikken, krijgt deze app toestemming je informatie te gebruiken.",
|
||||
"konnect.consent.cancelButton.label": "Annuleren",
|
||||
"konnect.consent.allowButton.label": "Toestaan",
|
||||
"konnect.login.headline": "Aanmelden",
|
||||
"konnect.login.subHeader": "met je Kopano account",
|
||||
"konnect.login.usernameField.label": "Gebruikersnaam",
|
||||
"konnect.login.passwordField.label": "Wachtwoord",
|
||||
"konnect.login.nextButton.label": "Volgende",
|
||||
"konnect.welcome.headline": "Welkom {displayName}",
|
||||
"konnect.welcome.message": "Je bent aangemeld - fantastisch!",
|
||||
"konnect.error.login.validate.missingUsername": "Voer een gebruikersnaam in",
|
||||
"konnect.error.login.validate.missingPassword": "Voer een wachtwoord in",
|
||||
"konnect.error.login.failed": "Inloggen mislukt. Controleer logingegevens en probeer opnieuw.",
|
||||
"konnet.error.http.networkError": "Netwerk probleem. Controleer je verbinding en probeer opnieuw.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Onverwachte HTTP respons: {status}. Controleer je verbinding en probeer opnieuw.",
|
||||
"konnect.error.http.unexpectedResponseState": "Onverwachte respons status: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "Falhou a ligar ao servidor",
|
||||
"konnect.login.retryButton.label": "Tentar novamente",
|
||||
"konnect.scopeDescription.aliasBasic": "Aceder à sua informação básica da conta",
|
||||
"konnect.scopeDescription.offlineAccess": "Manter o acesso permitido de forma persistente e para sempre",
|
||||
"konnect.scopeDescription.scope": "Contexto: {scope}",
|
||||
"konnect.goodbye.headline": "Adeus",
|
||||
"konnect.goodbye.subHeader": "a sessão da sua conta Kopano foi terminada",
|
||||
"konnect.goodbye.message.close": "Pode fechar esta janela agora.",
|
||||
"konnect.goodbye.confirm.headline": "Olá {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "por favor confirme o fim de sessão",
|
||||
"konnect.goodbye.message.confirm": "Carregue no botão abaixo para terminar sessão na sua conta Kopano agora.",
|
||||
"konnect.goodbye.signoutButton.label": "Terminar sessão",
|
||||
"konnect.welcome.signoutButton.label": "Terminar sessão",
|
||||
"konnect.chooseaccount.headline": "Escolher uma conta",
|
||||
"konnect.chooseaccount.subHeader": "para iniciar sessão no Kopano",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Use outra conta",
|
||||
"konnect.consent.headline": "Olá {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} quer",
|
||||
"konnect.consent.tooltip.client": "Ao carregar em \"Permitir\" será redirecionado para: {redirectURI}",
|
||||
"konnect.consent.question": "Permitir que {clientDisplayName} faça isto?",
|
||||
"konnect.consent.consequence": "Ao carregar em Permitir, está a permitir que esta app use os seus dados.",
|
||||
"konnect.consent.cancelButton.label": "Cancelar",
|
||||
"konnect.consent.allowButton.label": "Permitir",
|
||||
"konnect.login.headline": "Iniciar sessão",
|
||||
"konnect.login.subHeader": "com a sua conta Kopano",
|
||||
"konnect.login.usernameField.label": "Utilizador",
|
||||
"konnect.login.passwordField.label": "Palavra-passe",
|
||||
"konnect.login.nextButton.label": "Seguinte",
|
||||
"konnect.welcome.headline": "Bem vindo {displayName}",
|
||||
"konnect.welcome.message": "Iniciou sessão - fantástico!",
|
||||
"konnect.error.login.validate.missingUsername": "Insira um utilizador",
|
||||
"konnect.error.login.validate.missingPassword": "Insira uma palavra-passe",
|
||||
"konnect.error.login.failed": "Falhou início de sessão. Por favor verifique as suas credenciais e tente novamente.",
|
||||
"konnet.error.http.networkError": "Erro de rede. Por favor verifique as ligações e tente novamente.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Resposta HTTP inesperada: {status}. Por favor verifique a sua ligação e tente novamente.",
|
||||
"konnect.error.http.unexpectedResponseState": "Resposta de estado inesperada: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "Не удалось подключиться к серверу",
|
||||
"konnect.login.retryButton.label": "Повторить",
|
||||
"konnect.scopeDescription.aliasBasic": "Доступ к вашей основной учетной записи",
|
||||
"konnect.scopeDescription.offlineAccess": "Сохраните разрешение на доступ как постоянное",
|
||||
"konnect.scopeDescription.scope": "Охват: {scope}",
|
||||
"konnect.goodbye.headline": "До свидания",
|
||||
"konnect.goodbye.subHeader": "вы вышли из вашей учетной записи Kopano",
|
||||
"konnect.goodbye.message.close": "Теперь вы можете закрыть окно.",
|
||||
"konnect.goodbye.confirm.headline": "Здравствуйте, {displayName}",
|
||||
"konnect.goodbye.confirm.subHeader": "пожалуйста, подтвердите выход",
|
||||
"konnect.goodbye.message.confirm": "Нажмите кнопку ниже, чтобы выйти из вашей учетной записи Kopano.",
|
||||
"konnect.goodbye.signoutButton.label": "Выход",
|
||||
"konnect.welcome.signoutButton.label": "Выход",
|
||||
"konnect.chooseaccount.headline": "Выберите учётную запись",
|
||||
"konnect.chooseaccount.subHeader": "для входа в Kopano",
|
||||
"konnect.chooseaccount.useOther.persona.label": "?",
|
||||
"konnect.chooseaccount.useOther.label": "Другой пользователь",
|
||||
"konnect.consent.headline": "Привет, {displayName}",
|
||||
"konnect.consent.message": "{clientDisplayName} хочет",
|
||||
"konnect.consent.tooltip.client": "После нажатия \"Разрешить\" вы будете перенаправлена на: {redirectURI}",
|
||||
"konnect.consent.question": "Разрешить {clientDisplayName} сделать это?",
|
||||
"konnect.consent.consequence": "Нажимая Разрешить, ты даёте разрешение этому приложению использовать вашу информацию.",
|
||||
"konnect.consent.cancelButton.label": "Отмена",
|
||||
"konnect.consent.allowButton.label": "Разрешить",
|
||||
"konnect.login.headline": "Вход",
|
||||
"konnect.login.subHeader": "с вашей учётной записью Kopano",
|
||||
"konnect.login.usernameField.label": "Имя пользователя",
|
||||
"konnect.login.passwordField.label": "Пароль",
|
||||
"konnect.login.nextButton.label": "Далее",
|
||||
"konnect.welcome.headline": "Добро пожаловать, {displayName}",
|
||||
"konnect.welcome.message": "Вы вошли - круто!",
|
||||
"konnect.error.login.validate.missingUsername": "Введите имя пользователя",
|
||||
"konnect.error.login.validate.missingPassword": "Введите пароль",
|
||||
"konnect.error.login.failed": "Не удалось войти. Пожалуйста, проверьте ваши учетные данные и попробуйте снова.",
|
||||
"konnet.error.http.networkError": "Сетевая ошибка. Пожалуйста, проверьте ваше соединение и попробуйте снова.",
|
||||
"konnect.error.http.unexpectedResponseStatus": "Неожиданный ответ HTTP: {status}. Проверьте подключение и повторите попытку.",
|
||||
"konnect.error.http.unexpectedResponseState": "Неожиданный ответ: {state}"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"konnect.loading.error.headline": "",
|
||||
"konnect.login.retryButton.label": "",
|
||||
"konnect.scopeDescription.aliasBasic": "",
|
||||
"konnect.scopeDescription.offlineAccess": "",
|
||||
"konnect.scopeDescription.scope": "",
|
||||
"konnect.goodbye.headline": "",
|
||||
"konnect.goodbye.subHeader": "",
|
||||
"konnect.goodbye.message.close": "",
|
||||
"konnect.goodbye.confirm.headline": "",
|
||||
"konnect.goodbye.confirm.subHeader": "",
|
||||
"konnect.goodbye.message.confirm": "",
|
||||
"konnect.goodbye.signoutButton.label": "",
|
||||
"konnect.welcome.signoutButton.label": "",
|
||||
"konnect.chooseaccount.headline": "",
|
||||
"konnect.chooseaccount.subHeader": "",
|
||||
"konnect.chooseaccount.useOther.persona.label": "",
|
||||
"konnect.chooseaccount.useOther.label": "",
|
||||
"konnect.consent.headline": "",
|
||||
"konnect.consent.message": "",
|
||||
"konnect.consent.tooltip.client": "",
|
||||
"konnect.consent.question": "",
|
||||
"konnect.consent.consequence": "",
|
||||
"konnect.consent.cancelButton.label": "",
|
||||
"konnect.consent.allowButton.label": "",
|
||||
"konnect.login.headline": "",
|
||||
"konnect.login.subHeader": "",
|
||||
"konnect.login.usernameField.label": "",
|
||||
"konnect.login.passwordField.label": "",
|
||||
"konnect.login.nextButton.label": "",
|
||||
"konnect.welcome.headline": "",
|
||||
"konnect.welcome.message": "",
|
||||
"konnect.error.login.validate.missingUsername": "",
|
||||
"konnect.error.login.validate.missingPassword": "",
|
||||
"konnect.error.login.failed": "",
|
||||
"konnet.error.http.networkError": "",
|
||||
"konnect.error.http.unexpectedResponseStatus": "",
|
||||
"konnect.error.http.unexpectedResponseState": ""
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
export function newHelloRequest(flow, query) {
|
||||
const r = {};
|
||||
|
||||
if (query.prompt) {
|
||||
// TODO(longsleep): Validate prompt values?
|
||||
r.prompt = query.prompt;
|
||||
}
|
||||
|
||||
let selectedFlow = flow;
|
||||
switch (flow) {
|
||||
case 'oauth':
|
||||
case 'consent':
|
||||
case 'oidc':
|
||||
r.scope = query.scope || '';
|
||||
r.client_id = query.client_id || ''; // eslint-disable-line camelcase
|
||||
r.redirect_uri = query.redirect_uri || ''; // eslint-disable-line camelcase
|
||||
if (query.id_token_hint) {
|
||||
r.id_token_hint = query.id_token_hint; // eslint-disable-line camelcase
|
||||
}
|
||||
if (query.max_age) {
|
||||
r.max_age = query.max_age; // eslint-disable-line camelcase
|
||||
}
|
||||
if (query.claims_scope) {
|
||||
// Add additional scopes from claims request if given.
|
||||
r.scope += ' ' + query.claims_scope;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
selectedFlow = null;
|
||||
}
|
||||
|
||||
if (selectedFlow) {
|
||||
r.flow = selectedFlow;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
RECEIVE_ERROR,
|
||||
RESET_HELLO,
|
||||
RECEIVE_HELLO,
|
||||
SERVICE_WORKER_NEW_CONTENT
|
||||
} from '../actions/types';
|
||||
import queryString from 'query-string';
|
||||
|
||||
const query = queryString.parse(document.location.search);
|
||||
const flow = query.flow || '';
|
||||
delete query.flow;
|
||||
|
||||
const defaultPathPrefix = (() => {
|
||||
const root = document.getElementById('root');
|
||||
let pathPrefix = root ? root.getAttribute('data-path-prefix') : null;
|
||||
if (!pathPrefix || pathPrefix === '__PATH_PREFIX__') {
|
||||
// Not replaced, probably we are running in debug mode or whatever. Use sane default.
|
||||
pathPrefix = '/signin/v1';
|
||||
}
|
||||
return pathPrefix;
|
||||
})();
|
||||
|
||||
const defaultState = {
|
||||
hello: null,
|
||||
error: null,
|
||||
flow: flow,
|
||||
query: query,
|
||||
updateAvailable: false,
|
||||
pathPrefix: defaultPathPrefix
|
||||
};
|
||||
|
||||
function commonReducer(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case RECEIVE_ERROR:
|
||||
return Object.assign({}, state, {
|
||||
error: action.error
|
||||
});
|
||||
|
||||
case RESET_HELLO:
|
||||
return Object.assign({}, state, {
|
||||
hello: null
|
||||
});
|
||||
|
||||
case RECEIVE_HELLO:
|
||||
return Object.assign({}, state, {
|
||||
hello: {
|
||||
state: action.state,
|
||||
username: action.username,
|
||||
displayName: action.displayName,
|
||||
details: action.hello
|
||||
}
|
||||
});
|
||||
|
||||
case SERVICE_WORKER_NEW_CONTENT:
|
||||
return Object.assign({}, state, {
|
||||
updateAvailable: true
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default commonReducer;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { combineReducers } from 'redux';
|
||||
|
||||
import commonReducer from './common';
|
||||
import loginReducer from './login';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
common: commonReducer,
|
||||
login: loginReducer
|
||||
});
|
||||
|
||||
export default rootReducer;
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
RECEIVE_VALIDATE_LOGON,
|
||||
REQUEST_LOGON,
|
||||
RECEIVE_LOGON,
|
||||
RECEIVE_LOGOFF,
|
||||
REQUEST_CONSENT_ALLOW,
|
||||
REQUEST_CONSENT_CANCEL,
|
||||
RECEIVE_CONSENT,
|
||||
UPDATE_INPUT
|
||||
} from '../actions/types';
|
||||
|
||||
function loginReducer(state = {
|
||||
loading: '',
|
||||
username: '',
|
||||
password: '',
|
||||
errors: {}
|
||||
}, action) {
|
||||
switch (action.type) {
|
||||
case RECEIVE_VALIDATE_LOGON:
|
||||
return Object.assign({}, state, {
|
||||
errors: action.errors,
|
||||
loading: ''
|
||||
});
|
||||
|
||||
case REQUEST_CONSENT_ALLOW:
|
||||
case REQUEST_CONSENT_CANCEL:
|
||||
case REQUEST_LOGON:
|
||||
return Object.assign({}, state, {
|
||||
loading: action.type,
|
||||
errors: {}
|
||||
});
|
||||
|
||||
case RECEIVE_CONSENT:
|
||||
case RECEIVE_LOGON:
|
||||
if (!action.success) {
|
||||
return Object.assign({}, state, {
|
||||
errors: action.errors ? action.errors : {},
|
||||
loading: ''
|
||||
});
|
||||
}
|
||||
return state;
|
||||
|
||||
case RECEIVE_LOGOFF:
|
||||
return Object.assign({}, state, {
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
|
||||
case UPDATE_INPUT:
|
||||
delete state.errors[action.name];
|
||||
return Object.assign({}, state, {
|
||||
[action.name]: action.value
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default loginReducer;
|
||||
@@ -0,0 +1,24 @@
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { createLogger } from 'redux-logger';
|
||||
|
||||
import rootReducer from './reducers';
|
||||
|
||||
const middlewares = [
|
||||
thunkMiddleware
|
||||
];
|
||||
|
||||
if (process.env.NODE_ENV === 'development') { // eslint-disable-line no-undef
|
||||
middlewares.push(createLogger()); // must be last middleware in the chain.
|
||||
}
|
||||
|
||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
composeEnhancers(applyMiddleware(
|
||||
...middlewares,
|
||||
))
|
||||
);
|
||||
|
||||
export default store;
|
||||
@@ -0,0 +1,87 @@
|
||||
export function withClientRequestState(obj) {
|
||||
// Generate a 16 byte random token
|
||||
const values = new Uint8Array(16);
|
||||
crypto.getRandomValues(values);
|
||||
// Convert the 16 byte to a hex string and assign to the state attribute
|
||||
obj.state = Array.prototype.map.call(values, x => x.toString(16)).join('');
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function dirname(s) {
|
||||
return s.replace(/\\/g,'/').replace(/\/[^/]*$/, '');
|
||||
}
|
||||
|
||||
export function propertyFromStylesheet(selector, attribute, asURL=false) {
|
||||
let value;
|
||||
let sheetHref;
|
||||
|
||||
Array.prototype.some.call(document.styleSheets, function(sheet) {
|
||||
try {
|
||||
return Array.prototype.some.call(sheet.cssRules, function(rule) {
|
||||
sheetHref = sheet.href;
|
||||
if (selector === rule.selectorText) {
|
||||
return Array.prototype.some.call(rule.style, function(style) {
|
||||
if (attribute === style) {
|
||||
value = rule.style.getPropertyValue(attribute);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
} catch(e) {
|
||||
// Ignore sheels which caused errors. This for example can happen if an
|
||||
// extension injected styles from an other origin.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (value && asURL) {
|
||||
// This removes url() shit if there.
|
||||
value = value.match(/(?:\(['|"]?)(.*?)(?:['|"]?\))/)[1];
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
if (sheetHref) {
|
||||
// URLs in CSS are relative to the CSS - so lets add stuff.
|
||||
const baseHref = dirname(sheetHref);
|
||||
value = baseHref + '/' + value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function enhanceBodyBackground() {
|
||||
const bg = propertyFromStylesheet('#bg-enhanced.enhanced', 'background-image', true);
|
||||
const overlay = propertyFromStylesheet('#bg-enhanced.enhanced::after', 'background-image', true);
|
||||
|
||||
const promises = [];
|
||||
if (bg) {
|
||||
promises.push(new Promise(resolve => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
resolve();
|
||||
};
|
||||
// Set image source to whatever the url from css holds.
|
||||
img.src = bg;
|
||||
}));
|
||||
}
|
||||
if (overlay) {
|
||||
promises.push(new Promise(resolve => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
resolve();
|
||||
};
|
||||
// Set image source to whatever the url from css holds.
|
||||
img.src = overlay;
|
||||
}));
|
||||
}
|
||||
Promise.all(promises).then(() => {
|
||||
window.document.getElementById('bg-enhanced').className += ' enhanced';
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*global process: true*/
|
||||
|
||||
const build = process.env.REACT_APP_KOPANO_BUILD || '0.0.0-no-proper-build';
|
||||
|
||||
export {
|
||||
build
|
||||
};
|
||||
Reference in New Issue
Block a user