Merge branch 'master' into italian-translation

This commit is contained in:
Kim Gardner
2018-10-30 15:35:53 +00:00
committed by GitHub
35 changed files with 306 additions and 211 deletions
@@ -123,7 +123,7 @@ class UserDetailComment extends React.Component {
<IfHasLink text={comment.body}>
<span className={styles.hasLinks}>
{/* TODO: translate string */}
<Icon name="error_outline" /> Contains Link
<Icon name="error_outline" /> {t('common.contains_link')}
</span>
</IfHasLink>
<div className={styles.actions}>
@@ -155,9 +155,19 @@ class StreamSettings extends React.Component {
<ConfigureCard
checked={settings.infoBoxEnable}
onCheckbox={this.updateInfoBoxEnable}
title={t('configure.include_comment_stream')}
title={t('configure.code_of_conduct_summary')}
>
<p>{t('configure.include_comment_stream_desc')}</p>
<p>
{t('configure.code_of_conduct_summary_desc')}
&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="https://guides.coralproject.net/create-a-code-of-conduct/"
>
Code of Conduct Guide.
</a>
</p>
<div
className={cn(
styles.configSettingInfoBox,
@@ -238,12 +248,14 @@ class StreamSettings extends React.Component {
onChange={this.updateClosedTimeout}
value={getTimeoutAmount(settings.closedTimeout)}
label={t('configure.closed_comments_label')}
disabled={!settings.autoCloseStream}
/>
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={this.updateClosedTimeoutMeasure}
readOnly={!settings.autoCloseStream}
>
<Option value={'hours'}>{t('configure.hours')}</Option>
<Option value={'days'}>{t('configure.days')}</Option>
@@ -6,7 +6,7 @@ import ConfigureCard from 'coral-framework/components/ConfigureCard';
const Wordlist = ({ suspectWords, bannedWords, onChangeWordlist }) => (
<div>
<ConfigureCard title={t('configure.banned_words_title')}>
<ConfigureCard collapsible title={t('configure.banned_words_title')}>
<p>{t('configure.banned_word_text')}</p>
<TagsInput
value={bannedWords}
@@ -16,7 +16,7 @@ const Wordlist = ({ suspectWords, bannedWords, onChangeWordlist }) => (
onChange={tags => onChangeWordlist('banned', tags)}
/>
</ConfigureCard>
<ConfigureCard title={t('configure.suspect_word_title')}>
<ConfigureCard collapsible title={t('configure.suspect_word_title')}>
<p>{t('configure.suspect_word_text')}</p>
<TagsInput
value={suspectWords}
@@ -183,8 +183,7 @@ class Comment extends React.Component {
<div className={styles.sideActions}>
<IfHasLink text={comment.body}>
<span className={styles.hasLinks}>
{/* TODO: translate string */}
<Icon name="error_outline" /> Contains Link
<Icon name="error_outline" /> {t('common.contains_link')}
</span>
</IfHasLink>
<div className={`actions ${styles.actions}`}>
@@ -50,7 +50,7 @@ class StoriesContainer extends Component {
const { updateAssetState } = this.props;
try {
updateAssetState(id, closeStream ? Date.now() : null);
await updateAssetState(id, closeStream ? Date.now() : null);
this.fetchAssets();
} catch (err) {
console.error(err);
@@ -138,6 +138,7 @@
}
.commentAvatar {
margin-top: 10px;
max-width: 60px;
}
@@ -206,15 +206,30 @@ class Stream extends React.Component {
);
}
renderQuestionBox() {
const {
root,
asset,
asset: {
settings: { questionBoxEnable, questionBoxContent, questionBoxIcon },
},
} = this.props;
const slotPassthrough = { root, asset };
if (questionBoxEnable) {
return (
<QuestionBox content={questionBoxContent} icon={questionBoxIcon}>
<Slot fill="streamQuestionArea" passthrough={slotPassthrough} />
</QuestionBox>
);
}
}
render() {
const {
root,
appendItemArray,
asset,
asset: {
comment: highlightedComment,
settings: { questionBoxEnable },
},
asset: { comment: highlightedComment },
postComment,
notify,
updateItem,
@@ -260,14 +275,7 @@ class Stream extends React.Component {
content={asset.settings.infoBoxContent}
enable={asset.settings.infoBoxEnable}
/>
{questionBoxEnable && (
<QuestionBox
content={asset.settings.questionBoxContent}
icon={asset.settings.questionBoxIcon}
>
<Slot fill="streamQuestionArea" passthrough={slotPassthrough} />
</QuestionBox>
)}
{this.renderQuestionBox()}
{!banned &&
temporarilySuspended && (
<RestrictedMessageBox>
@@ -305,6 +313,7 @@ class Stream extends React.Component {
) : (
<Markdown content={asset.settings.disableCommentingMessage} />
)}
{this.renderQuestionBox()}
</div>
)}
@@ -112,6 +112,11 @@ body {
position: relative;
}
.talk-stream-comment {
display: flex;
flex-direction: row;
}
/* Comment styles */
.comment {
margin-bottom: 10px;
+6 -14
View File
@@ -1,10 +1,5 @@
import * as actions from '../constants/auth';
import jwtDecode from 'jwt-decode';
function cleanAuthData(localStorage) {
localStorage.removeItem('token');
localStorage.removeItem('exp');
}
import { setStorageAuthToken, clearStorageAuthToken } from '../services/auth';
export const checkLogin = () => (
dispatch,
@@ -15,7 +10,7 @@ export const checkLogin = () => (
rest('/auth')
.then(result => {
if (!result.user) {
cleanAuthData(localStorage);
clearStorageAuthToken(localStorage);
dispatch(checkLoginSuccess(null));
client.resetWebsocket();
return;
@@ -32,7 +27,7 @@ export const checkLogin = () => (
.catch(error => {
if (error.status && error.status === 401 && localStorage) {
// Unauthorized.
cleanAuthData(localStorage);
clearStorageAuthToken(localStorage);
client.resetWebsocket();
} else {
console.error(error);
@@ -58,8 +53,7 @@ export const setAuthToken = token => (
_,
{ localStorage, client }
) => {
localStorage.setItem('exp', jwtDecode(token).exp);
localStorage.setItem('token', token);
setStorageAuthToken(localStorage, token);
// Dispatch the set auth token action. For some browsers and situations, we
// may not be able to persist the auth token any other way. Keep it in redux!
@@ -76,9 +70,7 @@ export const handleSuccessfulLogin = (user, token) => (
_,
{ client, localStorage, postMessage }
) => {
const { exp } = jwtDecode(token);
localStorage.setItem('exp', exp);
localStorage.setItem('token', token);
setStorageAuthToken(localStorage, token);
// Send the message via the messages service to the window.opener if it
// exists.
@@ -117,7 +109,7 @@ export const logout = () => async (
}
// Clear the auth data persisted to localStorage.
cleanAuthData(localStorage);
clearStorageAuthToken(localStorage);
// Reset the websocket.
client.resetWebsocket();
@@ -1,50 +1,48 @@
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
min-height: 100px;
flex-direction: row;
align-items: flex-start;
margin-bottom: 20px;
overflow: visible;
}
.collapsibleCard {
min-height: auto;
}
.enabledCard {
border-left: 7px solid #00796b;
}
.action {
flex-shrink: 0;
margin-right: 12px;
}
.wrapper {
flex-grow: 1;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 2px;
}
.title {
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
.body {
margin-top: 7px;
font-size: 14px;
letter-spacing: 0;
}
.action {
display: inline-block;
position: absolute;
top: 0;
left: 0;
padding: 20px;
}
.content {
display: inline-block;
padding: 0px 30px;
box-sizing: border-box;
}
.enabledSetting {
border-left-color: #00796b;
border-left-style: solid;
border-left-width: 7px;
}
.disabledSetting {
padding-left: 22px;
}
.disabledSettingText {
.disabledBody {
color: #ccc;
pointer-events: none;
}
@@ -1,46 +1,69 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import styles from './ConfigureCard.css';
import classnames from 'classnames/bind';
import { Card } from 'coral-ui';
import { Checkbox } from 'react-mdl';
import cn from 'classnames';
import { Checkbox, IconButton } from 'react-mdl';
const ConfigureCard = ({
title,
children,
className,
onCheckbox,
checked,
...rest
}) => (
<Card
{...rest}
className={cn(styles.card, className, {
[styles.enabledSetting]: checked === true,
[styles.disabledSetting]: checked === false,
})}
>
{checked !== undefined && (
<div className={styles.action}>
<Checkbox onChange={onCheckbox} checked={checked} />
</div>
)}
<div
className={cn(styles.wrapper, {
[styles.content]: checked !== undefined,
})}
>
<div className={styles.header}>{title}</div>
<div
className={cn({
[styles.disabledSettingText]: checked === false,
import styles from './ConfigureCard.css';
const cn = classnames.bind(styles);
class ConfigureCard extends PureComponent {
state = {
isOpen: !this.props.collapsible,
};
toggle = () => this.setState({ isOpen: !this.state.isOpen });
render() {
const {
title,
children,
className,
onCheckbox,
checked,
collapsible,
...rest
} = this.props;
const { isOpen } = this.state;
const iconName = isOpen ? 'keyboard_arrow_up' : 'keyboard_arrow_down';
return (
<Card
{...rest}
className={cn(styles.card, className, {
enabledCard: checked === true,
collapsibleCard: collapsible,
})}
>
{children}
</div>
</div>
</Card>
);
{checked !== undefined && (
<div className={styles.action}>
<Checkbox onChange={onCheckbox} checked={checked} />
</div>
)}
<div className={styles.wrapper}>
<div className={styles.header}>
<div className={styles.title}>{title}</div>
{collapsible && (
<IconButton onClick={this.toggle} name={iconName} ripple />
)}
</div>
{isOpen && (
<div
className={cn(styles.body, {
disabledBody: checked === false,
})}
>
{children}
</div>
)}
</div>
</Card>
);
}
}
ConfigureCard.propTypes = {
title: PropTypes.string,
@@ -48,6 +71,7 @@ ConfigureCard.propTypes = {
onCheckbox: PropTypes.func,
checked: PropTypes.bool,
children: PropTypes.node,
collapsible: PropTypes.bool,
};
export default ConfigureCard;
+11
View File
@@ -0,0 +1,11 @@
import jwtDecode from 'jwt-decode';
export const setStorageAuthToken = (storage, token) => {
storage.setItem('exp', jwtDecode(token).exp);
storage.setItem('token', token);
};
export const clearStorageAuthToken = storage => {
storage.removeItem('token');
storage.removeItem('exp');
};
+25 -1
View File
@@ -22,6 +22,7 @@ import {
} from 'coral-framework/services/storage';
import { createHistory } from 'coral-framework/services/history';
import { createIntrospection } from 'coral-framework/services/introspection';
import { setStorageAuthToken } from 'coral-framework/services/auth';
import introspectionData from 'coral-framework/graphql/introspection.json';
import coreReducers from '../reducers';
import { checkLogin as checkLoginAction } from '../actions/auth';
@@ -46,7 +47,30 @@ const getAuthToken = (store, storage) => {
// capable of storing the token in localStorage, then we would have
// persisted it to the redux state.
return state.config.auth_token || state.auth.token;
} else if (!bowser.safari && !bowser.ios && storage) {
} else if (location.hash && location.hash.startsWith('#access_token=')) {
// Check to see if the access token is living in the URL as a hash.
const token = location.hash.substring(14);
history.replaceState(
{},
document.title,
window.location.pathname + window.location.search
);
// Once we clear the hash above, this login method will not persist across
// refreshes. We will need to persist the token to storage if it's
// available.
if (storage) {
setStorageAuthToken(storage, token);
}
return token;
} else if (
!bowser.safari &&
!bowser.ios &&
storage &&
storage.getItem('token')
) {
// Use local storage auth tokens where there's a stable api.
return storage.getItem('token');
}