Merge branch 'master' into feat/api/add_statement
This commit is contained in:
commit
65ecb15706
65 changed files with 916 additions and 906 deletions
45
.eslintrc
45
.eslintrc
|
|
@ -31,38 +31,53 @@
|
|||
"jsx": true
|
||||
},
|
||||
"globals": {
|
||||
"document": false,
|
||||
"window": false,
|
||||
"global": false,
|
||||
"require": false,
|
||||
"expect": false,
|
||||
"should": false,
|
||||
"chai": false,
|
||||
"sinon": false,
|
||||
"Power2": false,
|
||||
"Sine": false,
|
||||
"__DEVELOPMENT__": false
|
||||
},
|
||||
"rules": {
|
||||
"max-len": [0],
|
||||
"new-cap": ["warn", {"capIsNewExceptions": [
|
||||
"Immutable",
|
||||
"Map",
|
||||
"List",
|
||||
"Set",
|
||||
"OrderedSet",
|
||||
"Range",
|
||||
"Moment",
|
||||
"Linker",
|
||||
"Moment"
|
||||
]}],
|
||||
"comma-dangle": ["warn", "always-multiline"], // disallow or enforce trailing commas
|
||||
"curly": ["warn", "multi-or-nest"], // specify curly brace conventions for all control statements
|
||||
"indent": ["warn", 2, {"SwitchCase": 1}], // this option sets a specific tab width for your code (off by default)
|
||||
"indent": ["warn", 2, {
|
||||
"SwitchCase": 1,
|
||||
"MemberExpression": 1,
|
||||
"FunctionDeclaration": {"parameters": "first"},
|
||||
"FunctionExpression": {"parameters": "first"},
|
||||
"CallExpression": {"arguments": 1}
|
||||
}], // this option sets a specific tab width for your code (off by default)
|
||||
"brace-style": ["warn", "1tbs", { "allowSingleLine": true }], // enforce one true brace style (off by default)
|
||||
"key-spacing": ["warn", {"beforeColon": false, "afterColon": true}], // enforces spacing between keys and values in object literal properties
|
||||
"quotes": ["warn", "single", "avoid-escape"], // specify whether double or single quotes should be used
|
||||
"keyword-spacing": ["warn", {"before": true, "after": true}], // require a space after certain keywords (off by default)
|
||||
"no-underscore-dangle": [0],
|
||||
"import/no-extraneous-dependencies": [0], // because broken
|
||||
"import/no-unresolved": [0], // because broken
|
||||
"import/extensions": [0],
|
||||
"import/prefer-default-export": [0],
|
||||
"global-require": [0],
|
||||
"linebreak-style": "off",
|
||||
// React
|
||||
//"computed-property-spacing": ["warn", "never"], // disallow spaces inside computed properties
|
||||
"react/jsx-indent-props": ["warn", 2]
|
||||
"react/jsx-indent-props": ["warn", 2],
|
||||
"react/forbid-prop-types": [0],
|
||||
"react/no-unescaped-entities": [0],
|
||||
"react/no-unused-prop-types": [0], // because broken
|
||||
"react/sort-comp": [1, {
|
||||
"order": [
|
||||
"static-methods",
|
||||
"lifecycle",
|
||||
"/^on.+$/",
|
||||
"everything-else",
|
||||
"/^render.+$/",
|
||||
"render"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable import/imports-first */
|
||||
import jquery from 'jquery';
|
||||
global.$ = jquery;
|
||||
global.jQuery = jquery;
|
||||
|
|
|
|||
16
package.json
16
package.json
|
|
@ -12,8 +12,8 @@
|
|||
"copy-mocks": "node scripts/copy-mocks.js",
|
||||
"renderer": "node ./renderer/renderer.js",
|
||||
"clean": "./node_modules/.bin/rimraf ./build integration/main.css integration/static && echo --- Cleaned ---",
|
||||
"lint": "eslint src",
|
||||
"lint-fix": "eslint src --fix",
|
||||
"lint": "eslint --ext .js --ext .jsx src webpack stories",
|
||||
"lint-fix": "npm run lint -- --fix",
|
||||
"test": "jest --coverage",
|
||||
"test-watch": "jest --watch",
|
||||
"e2e-test": "./node_modules/.bin/nightwatch",
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
"@kadira/storybook-addon-knobs": "^1.4.1",
|
||||
"babel-cli": "<6.3.0",
|
||||
"babel-core": "<6.3.0",
|
||||
"babel-eslint": "^6.0.0",
|
||||
"babel-eslint": "^7.1.0",
|
||||
"babel-loader": "~6.2.1",
|
||||
"babel-plugin-ramda": "^1.1.6",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
|
|
@ -89,11 +89,11 @@
|
|||
"copy-webpack-plugin": "^2.1.3",
|
||||
"css-loader": "^0.23.0",
|
||||
"enzyme": "^2.3.0",
|
||||
"eslint": "^2.5.3",
|
||||
"eslint-config-airbnb": "^6.2.0",
|
||||
"eslint-plugin-import": "^1.10.2",
|
||||
"eslint-plugin-jsx-a11y": "^1.5.5",
|
||||
"eslint-plugin-react": "^4.2.3",
|
||||
"eslint": "^3.9.1",
|
||||
"eslint-config-airbnb": "^12.0.0",
|
||||
"eslint-plugin-import": "^1.16.0",
|
||||
"eslint-plugin-jsx-a11y": "^2.2.3",
|
||||
"eslint-plugin-react": "^6.5.0",
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"file-loader": "^0.8.4",
|
||||
"html-webpack-plugin": "^2.15.0",
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
/* eslint-disable jsx-a11y/href-no-hash */
|
||||
import React from 'react';
|
||||
import paths from 'constants/paths';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
const Footer = () => (
|
||||
<footer className="footer">
|
||||
<div className="col-md-1"></div>
|
||||
<div className="col-md-3">
|
||||
<small>
|
||||
<a href="#">Crédits</a>
|
||||
<footer className="footer">
|
||||
<div className="col-md-1" />
|
||||
<div className="col-md-3">
|
||||
<small>
|
||||
<a href="#">Crédits</a>
|
||||
|
|
||||
<a href="#">Mentions Légales</a>
|
||||
</small></div>
|
||||
<div className="col-md-7">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><Link to={paths.about}>À propos</Link></li>
|
||||
<li><Link to={paths.contact}>Contact</Link></li>
|
||||
<li><Link to={paths.external.twitter} target="_blank">Twitter</Link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="col-md-1"></div>
|
||||
</footer>
|
||||
<a href="#">Mentions Légales</a>
|
||||
</small></div>
|
||||
<div className="col-md-7">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><Link to={paths.about}>À propos</Link></li>
|
||||
<li><Link to={paths.contact}>Contact</Link></li>
|
||||
<li><Link to={paths.external.twitter} target="_blank">Twitter</Link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="col-md-1" />
|
||||
</footer>
|
||||
);
|
||||
|
||||
export default Footer;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
/* eslint-disable jsx-a11y/href-no-hash */
|
||||
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import paths from 'constants/paths';
|
||||
|
||||
const ConnectedUserMenu = ({ user, onLogOut }) => (
|
||||
<li className="dropdown">
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown">
|
||||
{user.name}
|
||||
<b className="caret"></b>
|
||||
</a>
|
||||
<ul className="dropdown-menu">
|
||||
<li><Link to={paths.getFor.user(user)}>Profil</Link></li>
|
||||
<li className="divider"></li>
|
||||
<li><a onClick={onLogOut}>Déconnexion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="dropdown">
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown">
|
||||
{user.name}
|
||||
<b className="caret" />
|
||||
</a>
|
||||
<ul className="dropdown-menu">
|
||||
<li><Link to={paths.getFor.user(user)}>Profil</Link></li>
|
||||
<li className="divider" />
|
||||
<li><a href="#" onClick={onLogOut}>Déconnexion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
ConnectedUserMenu.propTypes = {
|
||||
user: PropTypes.object.isRequired,
|
||||
onLogOut: PropTypes.func.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
onLogOut: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ConnectedUserMenu;
|
||||
|
|
|
|||
|
|
@ -4,18 +4,19 @@ import cssModules from 'react-css-modules';
|
|||
import { Navbar, Nav, NavItem } from 'react-bootstrap';
|
||||
import { LinkContainer, IndexLinkContainer } from 'react-router-bootstrap';
|
||||
|
||||
import paths from 'constants/paths';
|
||||
|
||||
import ConnectedUserMenu from './';
|
||||
|
||||
import paths from 'constants/paths';
|
||||
import logoImg from './images/logo_header.png';
|
||||
import styles from './Header.css';
|
||||
|
||||
const renderUserMenu = (isConnected) => (
|
||||
const renderUserMenu = isConnected => (
|
||||
isConnected
|
||||
? <ConnectedUserMenu />
|
||||
: [
|
||||
<li><a>Connexion</a></li>, /* modal ! */
|
||||
<li><a>Inscription</a></li>, /* modal ! */
|
||||
<li><a>Connexion</a></li>, /* modal ! */
|
||||
<li><a>Inscription</a></li>, /* modal ! */
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ const Header = () => (
|
|||
<Navbar.Brand>
|
||||
<IndexLinkContainer to="/" styleName="logo">
|
||||
<NavItem>
|
||||
<img src={logoImg} />
|
||||
<img alt="Brand Logo" src={logoImg} />
|
||||
</NavItem>
|
||||
</IndexLinkContainer>
|
||||
</Navbar.Brand>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import React from 'react';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { storiesOf, action, linkTo } from '@kadira/storybook';
|
||||
import { withKnobs, text, boolean, number } from '@kadira/storybook-addon-knobs';
|
||||
|
||||
|
|
@ -10,9 +11,9 @@ const stories = storiesOf('Header', module);
|
|||
stories.addDecorator(withKnobs);
|
||||
|
||||
stories.addWithInfo(
|
||||
'default header',
|
||||
'Description of the story',
|
||||
() => (
|
||||
<Header />
|
||||
'default header',
|
||||
'Description of the story',
|
||||
() => (
|
||||
<Header />
|
||||
)
|
||||
);
|
||||
|
|
@ -5,18 +5,18 @@ import Footer from './Footer';
|
|||
import GoogleAnalytics from './GoogleAnalytics';
|
||||
|
||||
const Main = ({ children }) => (
|
||||
<div className="container-fluid">
|
||||
<Header />
|
||||
<FlashMessages />
|
||||
<div className="main-content">
|
||||
{children}
|
||||
</div>
|
||||
<Footer />
|
||||
<GoogleAnalytics />
|
||||
<div className="container-fluid">
|
||||
<Header />
|
||||
<FlashMessages />
|
||||
<div className="main-content">
|
||||
{children}
|
||||
</div>
|
||||
<Footer />
|
||||
<GoogleAnalytics />
|
||||
</div>
|
||||
);
|
||||
Main.propTypes = {
|
||||
children: PropTypes.node,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default Main;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import { curry } from 'ramda';
|
||||
import axios from 'axios';
|
||||
import Config from 'Config';
|
||||
|
|
@ -13,5 +15,4 @@ export const getStatements = () => get('statements');
|
|||
export const getPublicFiguresAutocomplete = typed => get(`autocomplete/public_figure/${typed}`);
|
||||
export const getSubjectsAutocomplete = typed => get(`autocomplete/subject/${typed}`);
|
||||
export const getPositions = subjectId => get(`subjects/${subjectId}/positions`);
|
||||
|
||||
export const postStatement = statement => post('statements', statement);
|
||||
|
|
|
|||
|
|
@ -14,45 +14,45 @@ const getId = prop('id');
|
|||
const getAttributesPair = objectWithAttributes => toPairs(objectWithAttributes.attributes);
|
||||
|
||||
const mergeAttributes = entityWithAttributes => compose(
|
||||
reduce(
|
||||
(entity, attributePair) => (assoc(attributePair[0], attributePair[1], entity)),
|
||||
entityWithAttributes
|
||||
reduce(
|
||||
(entity, attributePair) => (assoc(attributePair[0], attributePair[1], entity)),
|
||||
entityWithAttributes
|
||||
),
|
||||
getAttributesPair,
|
||||
getAttributesPair,
|
||||
)(entityWithAttributes);
|
||||
|
||||
const overAttributes = over(lensProp('attributes'));
|
||||
|
||||
const toCamelCase = replace(
|
||||
/-[a-z]/g,
|
||||
compose(replace('-', ''), toUpper)
|
||||
/-[a-z]/g,
|
||||
compose(replace('-', ''), toUpper)
|
||||
);
|
||||
|
||||
const toCamelCaseAttributes = overAttributes(pipe(
|
||||
toPairs,
|
||||
map(
|
||||
([k, v]) => ([toCamelCase(k), v])
|
||||
toPairs,
|
||||
map(
|
||||
([k, v]) => ([toCamelCase(k), v])
|
||||
),
|
||||
fromPairs,
|
||||
fromPairs,
|
||||
));
|
||||
|
||||
export const flattenAttributes = pipe(
|
||||
map(pipe(
|
||||
toCamelCaseAttributes,
|
||||
mergeAttributes,
|
||||
dissoc('attributes'),
|
||||
map(pipe(
|
||||
toCamelCaseAttributes,
|
||||
mergeAttributes,
|
||||
dissoc('attributes'),
|
||||
)),
|
||||
);
|
||||
|
||||
export const index = pipe(
|
||||
when(isNotArray, of),
|
||||
flattenAttributes,
|
||||
map(indexBy(getId))
|
||||
when(isNotArray, of),
|
||||
flattenAttributes,
|
||||
map(indexBy(getId))
|
||||
);
|
||||
|
||||
export const indexAndGroup = pipe(
|
||||
when(isNotArray, of),
|
||||
flattenAttributes,
|
||||
groupBy(getType),
|
||||
map(indexBy(getId))
|
||||
when(isNotArray, of),
|
||||
flattenAttributes,
|
||||
groupBy(getType),
|
||||
map(indexBy(getId))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
require('bootstrap-loader');
|
||||
import moment from 'moment';
|
||||
import 'bootstrap-loader';
|
||||
|
||||
const locale = 'fr';
|
||||
moment.locale(locale);
|
||||
|
|
@ -16,7 +16,8 @@ moment.locale(locale, {
|
|||
|
||||
// Install ImmutableDevTools
|
||||
if (process.env.NODE_ENV !== 'production') { // DEBUG/DEV MODE
|
||||
const Immutable = require('immutable');
|
||||
const installDevTools = require('immutable-devtools').default;
|
||||
installDevTools(Immutable);
|
||||
const Immutable = require('immutable');
|
||||
const installDevTools = require('immutable-devtools').default;
|
||||
|
||||
installDevTools(Immutable);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import AddStatementModal from 'components/AddStatementModal';
|
||||
import connect from './connector';
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import AddSubjectFrom from './AddSubjectForm';
|
|||
|
||||
const SubjectStep = ({ selected, onSelection }) => (
|
||||
<Well>
|
||||
<<<<<<< HEAD
|
||||
<FormGroup controlId="subjectSelect" validationState={selected && !selected.customOption ? 'success' : undefined}>
|
||||
<ControlLabel>Quel est le sujet qui fait débat ?</ControlLabel>
|
||||
{(!selected || !selected.customOption) &&
|
||||
|
|
@ -16,6 +17,11 @@ const SubjectStep = ({ selected, onSelection }) => (
|
|||
onChange={onSelection}
|
||||
onCancel={() => onSelection(null)}
|
||||
/>}
|
||||
=======
|
||||
<FormGroup controlId="subjectSelect" validationState={!!selected ? 'success' : undefined}>
|
||||
<ControlLabel>Quel est le sujet qui fait débat ?</ControlLabel>
|
||||
<SubjectAutocompleteInput selected={selected} onSelection={onSelection} />
|
||||
>>>>>>> master
|
||||
</FormGroup>
|
||||
</Well>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@ import React, { PropTypes } from 'react';
|
|||
import { propEq, prop, compose, find } from 'ramda';
|
||||
import { Well } from 'react-bootstrap';
|
||||
|
||||
const getPositionInSubject = (position, subject) => compose(
|
||||
find(po => propEq('id', position, po)),
|
||||
prop('positions'),
|
||||
)(subject);
|
||||
|
||||
|
||||
const SummaryStep = ({
|
||||
publicFigure, subject, position, date, evidenceUrl, evidenceFile, evidenceSource, quote, note, tags,
|
||||
}) => (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getAddStatementPublicFigure, getAddStatementSubject, getAddStatementPosition,
|
||||
getAddStatementEvidenceUrl, getAddStatementEvidenceFile
|
||||
getAddStatementEvidenceUrl, getAddStatementEvidenceFile,
|
||||
} from 'store/selectors';
|
||||
import {
|
||||
onAddStatementPublicFigureSelection, onAddStatementSubjectSelection, onAddStatementPositionSelection,
|
||||
|
|
@ -10,20 +10,20 @@ import {
|
|||
} from 'store/actions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
selectedPublicFigure: getAddStatementPublicFigure(state),
|
||||
selectedSubject: getAddStatementSubject(state),
|
||||
selectedPosition: getAddStatementPosition(state),
|
||||
evidenceUrl: getAddStatementEvidenceUrl(state),
|
||||
evidenceFile: getAddStatementEvidenceFile(state),
|
||||
selectedPublicFigure: getAddStatementPublicFigure(state),
|
||||
selectedSubject: getAddStatementSubject(state),
|
||||
selectedPosition: getAddStatementPosition(state),
|
||||
evidenceUrl: getAddStatementEvidenceUrl(state),
|
||||
evidenceFile: getAddStatementEvidenceFile(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onPublicFigureSelection: publicFigureId => dispatch(onAddStatementPublicFigureSelection(publicFigureId)),
|
||||
onSubjectSelection: publicFigureId => dispatch(onAddStatementSubjectSelection(publicFigureId)),
|
||||
onPositionSelection: publicFigureId => dispatch(onAddStatementPositionSelection(publicFigureId)),
|
||||
onUpdateEvidenceUrl: url => dispatch(onAddStatementUpdateEvidenceUrl(url)),
|
||||
onUpdateEvidenceFile: file => dispatch(onAddStatementUpdateEvidenceFile(file)),
|
||||
onValidate: () => dispatch(onAddStatementValidate()),
|
||||
onPublicFigureSelection: publicFigureId => dispatch(onAddStatementPublicFigureSelection(publicFigureId)),
|
||||
onSubjectSelection: publicFigureId => dispatch(onAddStatementSubjectSelection(publicFigureId)),
|
||||
onPositionSelection: publicFigureId => dispatch(onAddStatementPositionSelection(publicFigureId)),
|
||||
onUpdateEvidenceUrl: url => dispatch(onAddStatementUpdateEvidenceUrl(url)),
|
||||
onUpdateEvidenceFile: file => dispatch(onAddStatementUpdateEvidenceFile(file)),
|
||||
onValidate: () => dispatch(onAddStatementValidate()),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
|
||||
const getText = nb => {
|
||||
if (nb === 0)
|
||||
return "Aucun sujet actif";
|
||||
else if (nb > 1)
|
||||
return nb + "sujets actifs";
|
||||
const getText = (nb) => {
|
||||
if (nb === 0)
|
||||
return 'Aucun sujet actif';
|
||||
else if (nb > 1)
|
||||
return `${nb}sujets actifs`;
|
||||
|
||||
return "1 sujet actif";
|
||||
return '1 sujet actif';
|
||||
};
|
||||
|
||||
const AssociatedSubjects = ({ publicFigure }) => (
|
||||
<h6 style={{ color: "#f21e40 !important" }}>{getText(publicFigure.nbActiveSubjects)}</h6>
|
||||
<h6 style={{ color: '#f21e40 !important' }}>{getText(publicFigure.nbActiveSubjects)}</h6>
|
||||
);
|
||||
|
||||
AssociatedSubjects.propTypes = {
|
||||
publicFigure: PropTypes.object.isRequired
|
||||
publicFigure: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default AssociatedSubjects;
|
||||
|
|
@ -5,7 +5,7 @@ import React, { PropTypes } from 'react';
|
|||
|
||||
const Identity = ({ children }) => <div>{children}</div>;
|
||||
Identity.propTypes = {
|
||||
children: PropTypes.node,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default Identity;
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
import React from 'react';
|
||||
import { storiesOf, action, linkTo } from '@kadira/storybook';
|
||||
import Identity from './';
|
||||
|
|
@ -6,28 +6,28 @@ import cssModules from 'react-css-modules';
|
|||
import style from './Statement.css';
|
||||
|
||||
const Statement = ({ statement }) => (
|
||||
<li id={`statement-${statement.id}`} styleName="wrapper" >
|
||||
<PublicFigureAvatar publicFigure={statement.publicFigure} />
|
||||
<div styleName="public-figure-text">
|
||||
<strong>
|
||||
<Link to={paths.getFor.publicFigure(statement.publicFigure)}>
|
||||
{statement.publicFigure.name}
|
||||
</Link>
|
||||
</strong>
|
||||
<li id={`statement-${statement.id}`} styleName="wrapper" >
|
||||
<PublicFigureAvatar publicFigure={statement.publicFigure} />
|
||||
<div styleName="public-figure-text">
|
||||
<strong>
|
||||
<Link to={paths.getFor.publicFigure(statement.publicFigure)}>
|
||||
{statement.publicFigure.name}
|
||||
</Link>
|
||||
</strong>
|
||||
s'est déclaré pour
|
||||
"<strong>{statement.position.title}</strong>"
|
||||
dans le débat sur
|
||||
<strong><Link to={paths.getFor.subject(statement.subject)}>
|
||||
{statement.subject.title}
|
||||
{statement.subject.title}
|
||||
</Link></strong>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
Statement.propTypes = {
|
||||
statement: PropTypes.shape({
|
||||
publicFigure: PropTypes.object,
|
||||
position: PropTypes.object,
|
||||
}).isRequired,
|
||||
statement: PropTypes.shape({
|
||||
publicFigure: PropTypes.object,
|
||||
position: PropTypes.object,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default cssModules(Statement, style);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { getLatestStatements } from 'store/selectors';
|
|||
import { onLastStatementsAccess } from 'store/actions/entities';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
statements: getLatestStatements(state),
|
||||
statements: getLatestStatements(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onAccess: () => dispatch(onLastStatementsAccess()),
|
||||
onAccess: () => dispatch(onLastStatementsAccess()),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps);
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import cssModules from 'react-css-modules';
|
||||
import Statement from './Statement';
|
||||
import LastStatementsStyle from './LastStatements.css';
|
||||
import connect from './connector';
|
||||
|
||||
class LastStatements extends Component {
|
||||
static propTypes = {
|
||||
statements: PropTypes.arrayOf(PropTypes.object),
|
||||
onAccess: PropTypes.func.isRequired,
|
||||
};
|
||||
static propTypes = {
|
||||
statements: PropTypes.arrayOf(PropTypes.object),
|
||||
onAccess: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.props.onAccess();
|
||||
}
|
||||
componentWillMount() {
|
||||
this.props.onAccess();
|
||||
}
|
||||
|
||||
renderStatements = () => this.props.statements.map(
|
||||
(s, i) => <Statement key={i} statement={s} />
|
||||
renderStatements = () => this.props.statements.map(
|
||||
(s, i) => <Statement key={i} statement={s} />
|
||||
);
|
||||
|
||||
render() {
|
||||
if (!this.props.statements) return <span>loading last statements ...</span>;
|
||||
render() {
|
||||
if (!this.props.statements) return <span>loading last statements ...</span>;
|
||||
|
||||
return (
|
||||
<div styleName="wrapper">
|
||||
<h2 styleName="title">Les dernières prises de positions</h2>
|
||||
<ul styleName="wrapper">
|
||||
{ this.renderStatements() };
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div styleName="wrapper">
|
||||
<h2 styleName="title">Les dernières prises de positions</h2>
|
||||
<ul styleName="wrapper">
|
||||
{ this.renderStatements() };
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(CSSModules(LastStatements, LastStatementsStyle));
|
||||
export default connect(cssModules(LastStatements, LastStatementsStyle));
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { head, of, compose, when, prop, not, isNil } from 'ramda';
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
import {head, of, compose, when, prop, not, isNil} from 'ramda';
|
||||
import Typeahead from 'react-bootstrap-typeahead';
|
||||
import { getPublicFiguresAutocomplete } from 'api/debats';
|
||||
import { flattenAttributes } from 'api/jsonApiParser';
|
||||
import {getPublicFiguresAutocomplete} from 'api/debats';
|
||||
import {flattenAttributes} from 'api/jsonApiParser';
|
||||
import PublicFigureAvatar from 'components/PublicFigureAvatar';
|
||||
import { makeCancelable } from 'helpers/promises';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@ import cssModules from 'react-css-modules';
|
|||
import styles from './PublicFigureAvatar.css';
|
||||
|
||||
const PublicFigureAvatar = ({ publicFigure }) => (
|
||||
<div
|
||||
styleName="wrapper"
|
||||
style={{
|
||||
backgroundImage: !!publicFigure.picture
|
||||
<div
|
||||
styleName="wrapper"
|
||||
style={{
|
||||
backgroundImage: publicFigure.picture
|
||||
? `url(${publicFigure.picture.url})`
|
||||
: undefined,
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
);
|
||||
PublicFigureAvatar.propTypes = {
|
||||
publicFigure: PropTypes.object.isRequired,
|
||||
publicFigure: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default cssModules(PublicFigureAvatar, styles);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
import React from 'react';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
import { storiesOf, action, linkTo } from '@kadira/storybook';
|
||||
import { withKnobs, text, boolean, number } from '@kadira/storybook-addon-knobs';
|
||||
import withReadme from 'storybook-readme/with-readme';
|
||||
|
|
@ -12,15 +13,15 @@ const stories = storiesOf('PublicFigureAvatar', module);
|
|||
stories.addDecorator(withKnobs);
|
||||
|
||||
stories.addWithInfo(
|
||||
'pony avatar',
|
||||
'Description of the story',
|
||||
withReadme(README,
|
||||
() => (
|
||||
<PublicFigureAvatar publicFigure={{ picture: { url: text('image url', 'http://tinyurl.com/jucz8b9') } }} />
|
||||
'pony avatar',
|
||||
'Description of the story',
|
||||
withReadme(README,
|
||||
() => (
|
||||
<PublicFigureAvatar publicFigure={{ picture: { url: text('image url', 'http://tinyurl.com/jucz8b9') } }} />
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
stories.add('jake avatar', () => (
|
||||
<PublicFigureAvatar publicFigure={{ picture: { url: 'http://tinyurl.com/jb286jq' } }} />
|
||||
<PublicFigureAvatar publicFigure={{ picture: { url: 'http://tinyurl.com/jb286jq' } }} />
|
||||
));
|
||||
|
|
@ -13,7 +13,6 @@ const parseSubjects = raw => pipe(
|
|||
)
|
||||
)(raw.data);
|
||||
|
||||
|
||||
class SubjectAutocompleteInput extends Component {
|
||||
|
||||
static propTypes = {
|
||||
|
|
@ -57,23 +56,41 @@ class SubjectAutocompleteInput extends Component {
|
|||
|
||||
onSelection = compose(this.props.onSelection, head);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Typeahead
|
||||
name="subject"
|
||||
options={this.state.suggestions}
|
||||
selected={of(this.props.selected)}
|
||||
emptyLabel="Aucun sujet correspondante"
|
||||
labelKey="title"
|
||||
minLength={3}
|
||||
allowNew
|
||||
newSelectionPrefix="Ajouter "
|
||||
onChange={this.onSelection}
|
||||
onInputChange={this.loadSuggestions}
|
||||
renderMenuItemChildren={this.renderMenuItemChildren}
|
||||
/>
|
||||
);
|
||||
}
|
||||
loadSuggestions = (typed) => {
|
||||
if (this.props.selected) this.props.onSelection(null);
|
||||
if (typed.length) {
|
||||
getSubjectsAutocomplete(typed)
|
||||
.then((response) => {
|
||||
this.setState({
|
||||
suggestions: flattenAttributes(response.data.data),
|
||||
});
|
||||
}); }
|
||||
};
|
||||
|
||||
renderMenuItemChildren = (typeaheadProps, subject) => (
|
||||
<div>
|
||||
<p>{subject.title} </p>
|
||||
<small>{take(100)(subject.presentation)}</small>
|
||||
</div>
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Typeahead
|
||||
name="subject"
|
||||
options={this.state.suggestions}
|
||||
selected={of(this.props.selected)}
|
||||
emptyLabel="Aucun sujet correspondante"
|
||||
labelKey="title"
|
||||
minLength={3}
|
||||
allowNew
|
||||
newSelectionPrefix="Ajouter "
|
||||
onChange={this.onSelection}
|
||||
onInputChange={this.loadSuggestions}
|
||||
renderMenuItemChildren={this.renderMenuItemChildren}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
const Empty = () => <span />;
|
||||
export default Empty;
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
export default {
|
||||
root: '/',
|
||||
subjects: '/s',
|
||||
publicFigures: '/p',
|
||||
getFor: {
|
||||
subject: (s) => `/s/${s.slug}`,
|
||||
publicFigure: (pf) => `/p/${pf.slug}`,
|
||||
},
|
||||
manual: '/guide',
|
||||
about: '/about',
|
||||
contact: '/contact',
|
||||
external: {
|
||||
twitter: 'https://twitter.com/debatsco',
|
||||
},
|
||||
root: '/',
|
||||
subjects: '/s',
|
||||
publicFigures: '/p',
|
||||
getFor: {
|
||||
subject: s => `/s/${s.slug}`,
|
||||
publicFigure: pf => `/p/${pf.slug}`,
|
||||
},
|
||||
manual: '/guide',
|
||||
about: '/about',
|
||||
contact: '/contact',
|
||||
external: {
|
||||
twitter: 'https://twitter.com/debatsco',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { pipe, always } from 'ramda';
|
||||
import { pipe } from 'ramda';
|
||||
|
||||
const print = message => x => {
|
||||
if (!!message) console.warn(message);
|
||||
return x;
|
||||
const print = message => (x) => {
|
||||
if (message) console.warn(message);
|
||||
return x;
|
||||
};
|
||||
|
||||
const printWithPrefix = (prefix) => (x) => {
|
||||
console.warn('-------------------------------');
|
||||
console.warn(`---- ${prefix}: `, x);
|
||||
return x;
|
||||
const printWithPrefix = prefix => (x) => {
|
||||
console.warn('-------------------------------');
|
||||
console.warn(`---- ${prefix}: `, x);
|
||||
return x;
|
||||
};
|
||||
|
||||
const printBefore = printWithPrefix('BEFORE');
|
||||
|
|
@ -16,5 +16,5 @@ const printAfter = printWithPrefix('AFTER');
|
|||
|
||||
export const withConsole = (f, message) => pipe(printBefore, print(message), f, printAfter);
|
||||
|
||||
export const log = x => { console.log(x); return x; }; /* eslint no-console: 0 */
|
||||
export const warn = x => { console.warn(x); return x; }; /* eslint no-console: 0 */
|
||||
export const log = (x) => { console.log(x); return x; }; /* eslint no-console: 0 */
|
||||
export const warn = (x) => { console.warn(x); return x; }; /* eslint no-console: 0 */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { complement } from 'ramda';
|
||||
|
||||
export const hasWindow = () => (typeof window !== 'undefined');
|
||||
export const isClientSide = hasWindow;
|
||||
export const isServerSide = complement(hasWindow);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import 'react-hot-loader/patch'; // https://github.com/gaearon/redux-devtools/commit/64f58b7010a1b2a71ad16716eb37ac1031f93915
|
||||
import 'babel-polyfill';
|
||||
|
||||
// First boot side effects
|
||||
import './boot.js';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
|
@ -11,6 +8,10 @@ import { match } from 'react-router';
|
|||
|
||||
// Styles
|
||||
import 'styles/_main.css';
|
||||
|
||||
// First boot side effects
|
||||
import './boot.js';
|
||||
|
||||
// Redux
|
||||
import { store, history } from './store'; // Redux store
|
||||
import Root from './root'; // App root (Router, Provider, Hot reload ...
|
||||
|
|
@ -18,31 +19,31 @@ import routes from './routes'; // React-router routes
|
|||
|
||||
// Init app function
|
||||
const loadApplication = (DOMElementId) => {
|
||||
const DOMElement = document.getElementById(DOMElementId);
|
||||
const DOMElement = document.getElementById(DOMElementId);
|
||||
|
||||
match({ history, routes, location }, () => {
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Root store={store} history={history} />
|
||||
</AppContainer>,
|
||||
DOMElement
|
||||
match({ history, routes }, () => {
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Root store={store} history={history} />
|
||||
</AppContainer>,
|
||||
DOMElement
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./root/index.jsx', () => {
|
||||
if (module.hot) {
|
||||
module.hot.accept('./root/index.jsx', () => {
|
||||
// If you use Webpack 2 in ES modules mode, you can
|
||||
// use <App /> here rather than require() a <NextApp />.
|
||||
const NextApp = require('./root/index').default;
|
||||
const NextApp = require('./root/index').default;
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<NextApp store={store} history={history} />
|
||||
</AppContainer>,
|
||||
DOMElement
|
||||
);
|
||||
});
|
||||
}
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<NextApp store={store} history={history} />
|
||||
</AppContainer>,
|
||||
DOMElement
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { store, history };
|
||||
|
|
@ -3,31 +3,31 @@ import cssModules from 'react-css-modules';
|
|||
import styles from './About.css';
|
||||
|
||||
const About = () => (
|
||||
<div>
|
||||
<h1>Pourquoi Débats.co ?</h1>
|
||||
<p>
|
||||
<i>Débats</i> est un projet francophone et participatif, ayant pour objectif d’offrir une synthèse ouverte, impartiale et vérifiable, des sujets clivants de notre société.
|
||||
</p>
|
||||
<p>
|
||||
Dans un monde de plus en plus complexe, les discours simplistes sont légion. Régulièrement, des controverses se construisent sur la base d'arguments à peine vérifiés, ou simplement invérifiables.
|
||||
C’est ainsi que semblent se développer des idées confuses et des discussions stériles.
|
||||
</p>
|
||||
<p>
|
||||
L'intelligibilité du débat public et son accessibilité par tous ceux qui aspirent à y participer déterminent la qualité de notre système démocratique. Or, aujourd’hui, aucun outil, simple d’utilisation, ne permet d’accéder à la pluralité des idées de celles et ceux qui font le choix de s’exprimer publiquement. Prendre connaissance de leurs arguments, comprendre ce qu’ils prétendent ou ce qu’ils laissent entendre est un véritable parcours du combattant. Nous cédons ainsi régulièrement à la facilité, en accordant une valeur démesurée à des postures démagogiques ou en laissant à d’autres le soin de les produire.
|
||||
</p>
|
||||
<p>
|
||||
Pourtant, les ressources sont là, sous nos yeux, elles existent ! Les blogs, les tweets, les journaux télévisés, les discours ou les livres sont un trésor démocratique laissé en jachère, une réserve politique fertile jusqu’ici largement sous-exploitée. Ces traces essaimées tout autour de nous constituent une des clés pour y voir plus clair.
|
||||
</p>
|
||||
<p>
|
||||
L’ambition de <i>Débats</i> est simple : recenser, sur chaque grand thème qui fait débat, les prises de position de celles et ceux qui décident de s’engager. Cartographier l’évolution de leur pensée, de leurs opinions, des combats qu’ils ont menés. Donner à voir la constance, la progression ou l’incohérence d’un engagement dans le temps.
|
||||
</p>
|
||||
<p>
|
||||
Rédigé par des volontaires sur une plateforme en ligne, et fonctionnant sur le principe du wiki, <i>Débats</i> a pour objectif d'offrir un contenu libre, impartial et vérifiable des prises de position de celles et ceux qui participent aux débats publics.
|
||||
</p>
|
||||
<p>
|
||||
Face à ce que nous considérons comme un dévoiement démocratique, nous partageons une conviction : l’information est notre première arme. Redonner à l’engagement la crédibilité et la valeur qui doivent être les siennes : voilà la raison d’être de <i>Débats</i>.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Pourquoi Débats.co ?</h1>
|
||||
<p>
|
||||
<i>Débats</i> est un projet francophone et participatif, ayant pour objectif d’offrir une synthèse ouverte, impartiale et vérifiable, des sujets clivants de notre société.
|
||||
</p>
|
||||
<p>
|
||||
Dans un monde de plus en plus complexe, les discours simplistes sont légion. Régulièrement, des controverses se construisent sur la base d'arguments à peine vérifiés, ou simplement invérifiables.
|
||||
C’est ainsi que semblent se développer des idées confuses et des discussions stériles.
|
||||
</p>
|
||||
<p>
|
||||
L'intelligibilité du débat public et son accessibilité par tous ceux qui aspirent à y participer déterminent la qualité de notre système démocratique. Or, aujourd’hui, aucun outil, simple d’utilisation, ne permet d’accéder à la pluralité des idées de celles et ceux qui font le choix de s’exprimer publiquement. Prendre connaissance de leurs arguments, comprendre ce qu’ils prétendent ou ce qu’ils laissent entendre est un véritable parcours du combattant. Nous cédons ainsi régulièrement à la facilité, en accordant une valeur démesurée à des postures démagogiques ou en laissant à d’autres le soin de les produire.
|
||||
</p>
|
||||
<p>
|
||||
Pourtant, les ressources sont là, sous nos yeux, elles existent ! Les blogs, les tweets, les journaux télévisés, les discours ou les livres sont un trésor démocratique laissé en jachère, une réserve politique fertile jusqu’ici largement sous-exploitée. Ces traces essaimées tout autour de nous constituent une des clés pour y voir plus clair.
|
||||
</p>
|
||||
<p>
|
||||
L’ambition de <i>Débats</i> est simple : recenser, sur chaque grand thème qui fait débat, les prises de position de celles et ceux qui décident de s’engager. Cartographier l’évolution de leur pensée, de leurs opinions, des combats qu’ils ont menés. Donner à voir la constance, la progression ou l’incohérence d’un engagement dans le temps.
|
||||
</p>
|
||||
<p>
|
||||
Rédigé par des volontaires sur une plateforme en ligne, et fonctionnant sur le principe du wiki, <i>Débats</i> a pour objectif d'offrir un contenu libre, impartial et vérifiable des prises de position de celles et ceux qui participent aux débats publics.
|
||||
</p>
|
||||
<p>
|
||||
Face à ce que nous considérons comme un dévoiement démocratique, nous partageons une conviction : l’information est notre première arme. Redonner à l’engagement la crédibilité et la valeur qui doivent être les siennes : voilà la raison d’être de <i>Débats</i>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default cssModules(About, styles);
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ const Contact = () => (
|
|||
<ul className="list-inline banner-social-buttons">
|
||||
<li>
|
||||
<a href="mailto:contact@debats.co" className="btn btn-default btn-lg">
|
||||
<i className="fa fa-envelope-o fa-fw"></i><span className="network-name"> Courriel</span>
|
||||
<i className="fa fa-envelope-o fa-fw" /><span className="network-name"> Courriel</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/debatsco" className="btn btn-default btn-lg">
|
||||
<i className="fa fa-twitter fa-fw"></i> <span className="network-name">Twitter</span>
|
||||
<i className="fa fa-twitter fa-fw" /> <span className="network-name">Twitter</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import cssModules from 'react-css-modules';
|
|||
import styles from './Guide.css';
|
||||
|
||||
const Guide = () => (
|
||||
<div>
|
||||
<div className="col-md-1"></div>
|
||||
<div className="col-md-10">
|
||||
<h1>Mode d'emploi</h1>
|
||||
<div>
|
||||
<div className="col-md-1" />
|
||||
<div className="col-md-10">
|
||||
<h1>Mode d'emploi</h1>
|
||||
|
||||
<h2>Panel de discussion</h2>
|
||||
<h2>Panel de discussion</h2>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
La confiance
|
||||
La plateforme Débats.co est basée sur la confiance. C’est-à-dire que le contenu est systématiquement considéré comme a priori valide, mais peut être retiré en cas de signalement. Plusieurs motifs peuvent pousser les autres membres à signaler votre contenu :
|
||||
Le contenu ne respecte pas les règles de rédaction
|
||||
|
|
@ -18,142 +18,142 @@ const Guide = () => (
|
|||
Le contenu pré-existe autrepart sur la plateforme
|
||||
</p>
|
||||
|
||||
<h2>Comment éviter d’être signalé ?</h2>
|
||||
<h2>Comment éviter d’être signalé ?</h2>
|
||||
|
||||
<div className="panel-group" id="accordion">
|
||||
<div className="panel-group" id="accordion">
|
||||
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse1">
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse1">
|
||||
Débats.co, qu’est-ce que c’est ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse1" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse1" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
Débats.co n’est pas un site de débats.
|
||||
<br />
|
||||
Débats.co a pour objectif de référencer et cartographier les positions prises par des personnalités publiques au cours des débats qui font controverse en France. Ainsi, les contributeurs ne sont pas appelés à donner leur avis mais plutôt à rechercher, collecter et recenser les positions exprimées sur un sujet particulier.
|
||||
Nous invitons les contributeurs de Débats.co à lire ce mode d’emploi et à s’y référer en cas de doute lors de l’utilisation du site.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse2">Comment fonctionne Débats.co ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse2" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse2">Comment fonctionne Débats.co ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse2" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
Le premier usage de Débats.co consiste à consulter la plateforme, en explorant les différents sujets enregistrés et le positionnement des personnalités publiques au sein de ces sujets.
|
||||
Le deuxième usage consiste à contribuer à la plateforme en y apportant le contenu nécessaire pour une synthèse des différents sujets et prises de positions.
|
||||
<br />
|
||||
Dans les deux cas, il est important de bien comprendre la structure du site. Sur Débats.co, l’information est articulée autour de plusieurs éléments :
|
||||
</p>
|
||||
<ul id="reputation">
|
||||
<li>Le sujet</li>
|
||||
<li>La position</li>
|
||||
<li>La personnalité</li>
|
||||
<li>La prise de position</li>
|
||||
<li>L’argument</li>
|
||||
<li>La source</li>
|
||||
</ul>
|
||||
<p>
|
||||
<ul id="reputation">
|
||||
<li>Le sujet</li>
|
||||
<li>La position</li>
|
||||
<li>La personnalité</li>
|
||||
<li>La prise de position</li>
|
||||
<li>L’argument</li>
|
||||
<li>La source</li>
|
||||
</ul>
|
||||
<p>
|
||||
Chaque <b>sujet</b> comporte au moins deux <b>positions</b>. Une <b>position</b> peut être "prise" par plusieurs <b>personnalités</b>. A chaque <b>prise de position</b> sont attachés une <b>personnalité</b> et un ensemble d’<b>arguments</b>. Pour que la <b>prise de position</b> puisse être recensée, elle doit être justifiée par au moins une <b>source</b>.
|
||||
<br />
|
||||
En somme, pour qu’un <b>sujet</b> puisse être référencé, il est nécessaire d'y recenser au moins une <b>prise de position</b> par au moins une <b>personnalité</b> dans chacune des <b>positions</b>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse5">
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse5">
|
||||
Qu'est-ce qu'une source ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse5" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse5" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
Une <b>source</b> est un élément permettant d’appuyer une prise de position. La source peut être constituée à partir de toute référence audio, vidéo, article, ou livre dont la diffusion est, ou a été, publique.
|
||||
Il sera demandé au contributeur de retranscrire la citation qui soutient le propos en question, la date à laquelle ce propos a été tenu, et, si possible, de fournir un lien qui permet d'y accéder directement.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse3">Comment référencer et recenser un contenu de qualité ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse3" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
<b>
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse3">Comment référencer et recenser un contenu de qualité ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse3" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
<b>
|
||||
Préexistence d'un contenu :
|
||||
</b>
|
||||
<br />
|
||||
<br />
|
||||
Tout d'abord, le contributeur peut se demander si le contenu qu'il s'apprête à référencer existe déjà.
|
||||
Lorsqu'il référence un nouveau sujet, il peut vérifier si celui-ci est déjà traité, ou partiellement traité, dans un autre sujet : auquel cas, il pourra enrichir ou modifier le débat déjà existant.
|
||||
<br />
|
||||
Lors du recensement d'une prise de position, l'exercice est similaire : il pourra vérifier la pré-existence d'une prise de position pour la personnalité en question, et l'enrichir avec de nouvelles sources le cas échant. Les arguments, matérialisés sur Débats.co par des tags, peuvent être infinis. Le formulaire vous suggérera néanmoins les tags proposés par d'autres utilisateurs.
|
||||
<br />
|
||||
<br />
|
||||
<b>
|
||||
<br />
|
||||
<b>
|
||||
Notoriété et véracité :
|
||||
</b>
|
||||
<br />
|
||||
<br />
|
||||
Il est nécessaire pour le contributeur de se demander si le contenu est suffisamment important, fiable et pertinent pour être référencé ou recensé sur Débats.co. Basé sur une logique collaborative, le site comporte plusieurs mécanismes d'autorégulation, permettant d’éviter la publication de contenu considérés comme fallacieux ou hors-sujet. Chaque entrée, notamment lors du référencement d’une nouvelle personnalité ou d’un nouveau sujet, sont soumis à l'approbation des utilisateurs expérimentés de la plateforme.
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<b>Neutralité et cohérence du point de vue</b>
|
||||
<br />
|
||||
<b>Neutralité et cohérence du point de vue</b>
|
||||
<br />
|
||||
Parler de "cohérence" lorsque l'on tente de référencer des sujets controversées est presque en soi contradictoire. Débats.co a néanmoins comme volonté de tendre un maximum vers l'objectivité.
|
||||
<br />
|
||||
Afin de s'en rapprocher, il est nécessaire pour chaque utilisateur de se demander comment exprimer de façon objective l’intitulé des sujets ou les positions des personnalités. Dans un premier temps, et pour simplifier cette démarche "objective", les sujets ainsi que les pages de personnalités peuvent être référencées directement depuis la base de données de Wikipedia. Il suffira donc, après la composition des premières lettres de l'intitulé de la personnalité ou du sujet, de sélectionner l'élément correspondant.
|
||||
<br />
|
||||
Lors de la recension d'une prise d'une position ensuite, il est important d'attacher à la source une citation qui justifie la prise de position. La citation doit être isolée, entre guillemets, ne pas être modifiée, et se suffir à elle-même pour être comprise (ne pas être sortie d'un contexte qui peut altérer sa compréhensioin). Pour être valide dans le cadre d’un sujet, un argument doit être explicitement mobilisé par la personnalité publique et répondre à la problématique initiale du débat.
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse4">Comment avoir accès à toutes les fonctions de Débats.co ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse4" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapse4">Comment avoir accès à toutes les fonctions de Débats.co ?</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse4" className="panel-collapse collapse">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
La contribution au site est simple. Toutefois, avant de pouvoir accéder à toutes les fonctions de référencement, de recension et de modification, une phase d’apprentissage est nécessaire. Pour apprendre, il faut s’exercer. Ainsi, pour chaque contribution au site, un nombre de points est attribué à l’utilisateur. L’accumulation de ces points lui donne progressivement accès aux fonctionnalités les plus puissantes de Débats.co. Le signalement par d'autres utilisateurs de la violation des principes inscrits dans ce mode d'emploi peut entraîner la perte de points. Ainsi 5 status existent sur Débats.co :
|
||||
</p>
|
||||
<ul id="reputation">
|
||||
<li><b>Le Métèque</b> : Son score est neutre. Il vient d'arriver sur la plateforme, et doit encore faire ses preuves. Il peut soumettre des arguments et des sources. </li>
|
||||
<li><b>L'Eloquent</b> : Son score est positif, il peut ajouter de nouvelles personnalités, de nouveau sujets, mais également suggérer l'approbation ou le rejet de modifications proposées par d'autres utilisateurs. Par défaut, tous les membres fondateurs inscrits lors de la bêta de Débats.co sont considérés comme Eloquents.</li>
|
||||
<li><b>L'Idéaliste</b> : Son score est élevé, il peut instantanément supprimer la plupart des modifications faites par d'autres utilisateurs.</li>
|
||||
<li><b>Le Sophiste</b> : son score est négatif, il a tout autant de droits que le nouvel arrivant, mais part avec autant de points de retard pour redevenir Métèque. </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="reputation">
|
||||
<li><b>Le Métèque</b> : Son score est neutre. Il vient d'arriver sur la plateforme, et doit encore faire ses preuves. Il peut soumettre des arguments et des sources. </li>
|
||||
<li><b>L'Eloquent</b> : Son score est positif, il peut ajouter de nouvelles personnalités, de nouveau sujets, mais également suggérer l'approbation ou le rejet de modifications proposées par d'autres utilisateurs. Par défaut, tous les membres fondateurs inscrits lors de la bêta de Débats.co sont considérés comme Eloquents.</li>
|
||||
<li><b>L'Idéaliste</b> : Son score est élevé, il peut instantanément supprimer la plupart des modifications faites par d'autres utilisateurs.</li>
|
||||
<li><b>Le Sophiste</b> : son score est négatif, il a tout autant de droits que le nouvel arrivant, mais part avec autant de points de retard pour redevenir Métèque. </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-1" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default cssModules(Guide, styles);
|
||||
|
|
|
|||
|
|
@ -6,40 +6,40 @@ import PublicFigureAvatar from 'components/PublicFigureAvatar';
|
|||
|
||||
const PublicFigureAvatarMapper = pf => <PublicFigureAvatar key={pf.id} publicFigure={pf} />;
|
||||
const renderAssociatedPublicFigures = compose(
|
||||
map(PublicFigureAvatarMapper),
|
||||
take(5),
|
||||
prop('remarquablePublicFigures'),
|
||||
map(PublicFigureAvatarMapper),
|
||||
take(5),
|
||||
prop('remarquablePublicFigures'),
|
||||
);
|
||||
|
||||
const HomeSubject = ({ subject }) => (
|
||||
<li>
|
||||
<tr>
|
||||
<td style={{ width: '50%', border: 'none', textTransform: 'uppercase' }}>
|
||||
<h2 className="subjects-title">
|
||||
<Link to={paths.getFor.subject(subject)}>
|
||||
{subject.title}
|
||||
</Link>
|
||||
</h2>
|
||||
<h6 className="count">
|
||||
{`${subject.remarquablePublicFigures.length} personnalité(s)`}
|
||||
</h6>
|
||||
</td>
|
||||
<td style={{ width: '50%', textAlign: 'center', verticalAlign: 'middle' }}>
|
||||
{renderAssociatedPublicFigures(subject)}
|
||||
</td>
|
||||
<td className="seemore">
|
||||
<div>
|
||||
<Link to={paths.getFor.subject(subject)}>Voir plus de personnalités</Link>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</li>
|
||||
<li>
|
||||
<tr>
|
||||
<td style={{ width: '50%', border: 'none', textTransform: 'uppercase' }}>
|
||||
<h2 className="subjects-title">
|
||||
<Link to={paths.getFor.subject(subject)}>
|
||||
{subject.title}
|
||||
</Link>
|
||||
</h2>
|
||||
<h6 className="count">
|
||||
{`${subject.remarquablePublicFigures.length} personnalité(s)`}
|
||||
</h6>
|
||||
</td>
|
||||
<td style={{ width: '50%', textAlign: 'center', verticalAlign: 'middle' }}>
|
||||
{renderAssociatedPublicFigures(subject)}
|
||||
</td>
|
||||
<td className="seemore">
|
||||
<div>
|
||||
<Link to={paths.getFor.subject(subject)}>Voir plus de personnalités</Link>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</li>
|
||||
);
|
||||
HomeSubject.propTypes = {
|
||||
subject: PropTypes.shape({
|
||||
title: PropTypes.string.isRequired,
|
||||
remarquablePublicFigures: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
}).isRequired,
|
||||
subject: PropTypes.shape({
|
||||
title: PropTypes.string.isRequired,
|
||||
remarquablePublicFigures: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default (HomeSubject);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { getHomeSubjectsWithRelations } from 'store/selectors';
|
|||
import { onHottestSubjectsAccess } from 'store/actions/entities';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
subjects: getHomeSubjectsWithRelations(state),
|
||||
subjects: getHomeSubjectsWithRelations(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onAccess: () => dispatch(onHottestSubjectsAccess()),
|
||||
onAccess: () => dispatch(onHottestSubjectsAccess()),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps);
|
||||
|
|
|
|||
|
|
@ -4,26 +4,26 @@ import connector from './connectors';
|
|||
|
||||
class HomeSubjects extends Component {
|
||||
|
||||
static propTypes = {
|
||||
subjects: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onAccess: PropTypes.func.isRequired,
|
||||
}
|
||||
static propTypes = {
|
||||
subjects: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onAccess: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.onAccess();
|
||||
}
|
||||
componentWillMount() {
|
||||
this.props.onAccess();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.subjects) return <span>loading subjects ...</span>;
|
||||
render() {
|
||||
if (!this.props.subjects) return <span>loading subjects ...</span>;
|
||||
|
||||
return (
|
||||
<div> {/* TODO Bootstrap */}
|
||||
{this.props.subjects.map(
|
||||
s => <HomeSubject key={s.id} subject={s} />
|
||||
return (
|
||||
<div> {/* TODO Bootstrap */}
|
||||
{this.props.subjects.map(
|
||||
s => <HomeSubject key={s.id} subject={s} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,40 @@
|
|||
import React from 'react';
|
||||
import cssModules from 'react-css-modules';
|
||||
import HomeSubjects from './HomeSubjects';
|
||||
import LastStatements from 'components/LastStatements';
|
||||
import HomeSubjects from './HomeSubjects';
|
||||
import bgSrc from './images/intro-bg.jpg';
|
||||
import styles from './Home.css';
|
||||
import AddStatementButton from 'components/AddStatementButton';
|
||||
|
||||
const Home = () => (
|
||||
<div className="container-fluid" styleName="container">
|
||||
<AddStatementButton />
|
||||
<div className="row" styleName="background-title" style={{ backgroundImage: `url(${bgSrc})` }}>
|
||||
<div styleName="title-mask">
|
||||
<h5 >Bienvenue sur Débats.co</h5>
|
||||
<div styleName="introduction">
|
||||
<span>Débats est un projet francophone et participatif, ayant pour objectif </span>
|
||||
<span>d’offrir une synthèse ouverte, impartiale et vérifiable, des sujets clivants de notre société.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container-fluid" styleName="container">
|
||||
<AddStatementButton />
|
||||
<div className="row" styleName="background-title" style={{ backgroundImage: `url(${bgSrc})` }}>
|
||||
<div styleName="title-mask">
|
||||
<h5 >Bienvenue sur Débats.co</h5>
|
||||
<div styleName="introduction">
|
||||
<span>Débats est un projet francophone et participatif, ayant pour objectif </span>
|
||||
<span>d’offrir une synthèse ouverte, impartiale et vérifiable, des sujets clivants de notre société.</span>
|
||||
</div>
|
||||
<div className="col-md-1"></div>
|
||||
<div className="subjects-index col-md-7 subjects-home">
|
||||
<h1>Sujets d'actualité</h1>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<ul id="subject">
|
||||
<HomeSubjects />
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col-md-3 col-centered" style={{ textAlign: 'right' }} >
|
||||
<LastStatements />
|
||||
</div>
|
||||
<div className="col-md-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-1"></div>
|
||||
<div className="subjects-index col-md-7 subjects-home">
|
||||
<h1>Sujets d'actualité</h1>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<ul id="subject">
|
||||
<HomeSubjects />
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col-md-1" />
|
||||
<div className="col-md-3 col-centered" style={{ textAlign: 'right' }}>
|
||||
<LastStatements />
|
||||
</div>
|
||||
<div className="col-md-1" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default cssModules(Home, styles);
|
||||
|
|
|
|||
|
|
@ -1,55 +1,47 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import paths from '../../../constants/paths';
|
||||
import cssModules from 'react-css-modules';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import paths from 'constants/paths';
|
||||
import PublicFigureAvatar from 'components/PublicFigureAvatar';
|
||||
import AssociatedSubjects from 'components/AssociatedSubjects';
|
||||
|
||||
import PublicFigureStyle from './PublicFigure.css';
|
||||
import cssModules from 'react-css-modules';
|
||||
|
||||
const PublicFigureInList = ({ publicFigure }) => (
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<ul id="subject">
|
||||
<li>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<ul id="subject">
|
||||
<li>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<PublicFigureAvatar publicFigure={publicFigure} />
|
||||
</td>
|
||||
<td style= {{ width: '33%' }}>
|
||||
<tr>
|
||||
<td>
|
||||
<PublicFigureAvatar publicFigure={publicFigure} />
|
||||
</td>
|
||||
<td style={{ width: '33%' }}>
|
||||
|
||||
<h2 className="figure-title" style={{ color: '#333333 !important' }}>
|
||||
<Link to={paths.getFor.subject(publicFigure)}>
|
||||
{publicFigure.name}
|
||||
</Link>
|
||||
</h2>
|
||||
<AssociatedSubjects publicFigure={publicFigure} />
|
||||
<h2 className="figure-title" style={{ color: '#333333 !important' }}>
|
||||
<Link to={paths.getFor.subject(publicFigure)}>
|
||||
{publicFigure.name}
|
||||
</Link>
|
||||
</h2>
|
||||
<AssociatedSubjects publicFigure={publicFigure} />
|
||||
</td>
|
||||
|
||||
<td styleName="presentationWrapper">
|
||||
<p className="figure-presentation-text" styleName="presentation">
|
||||
{publicFigure.presentation}
|
||||
</p>
|
||||
<p className="figure-presentation-text" styleName="presentation">
|
||||
{publicFigure.presentation}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
</li>
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
PublicFigureInList.propTypes = {
|
||||
publicFigure: PropTypes.object.isRequired,
|
||||
publicFigure: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default cssModules(PublicFigureInList, PublicFigureStyle);
|
||||
|
||||
/*
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
|
||||
$(".figure-presentation-text").shorten();
|
||||
|
||||
});
|
||||
</script>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { getPublicFiguresWithRelations } from 'store/selectors';
|
|||
import { onPublicFiguresListAccess } from 'store/actions/entities';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
publicFigures: getPublicFiguresWithRelations(state),
|
||||
publicFigures: getPublicFiguresWithRelations(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onAccess: () => dispatch(onPublicFiguresListAccess()),
|
||||
onAccess: () => dispatch(onPublicFiguresListAccess()),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps);
|
||||
|
|
|
|||
|
|
@ -1,37 +1,38 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
|
||||
import LastStatements from 'components/LastStatements';
|
||||
import PublicFigureInList from './PublicFigureInList';
|
||||
import connect from './connector';
|
||||
import LastStatements from 'components/LastStatements';
|
||||
|
||||
class PublicFigures extends Component {
|
||||
|
||||
static propTypes = {
|
||||
publicFigures: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onAccess: PropTypes.func.isRequired,
|
||||
}
|
||||
static propTypes = {
|
||||
publicFigures: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onAccess: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.onAccess();
|
||||
}
|
||||
componentWillMount() {
|
||||
this.props.onAccess();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.publicFigures) return <span>loading public figures ...</span>;
|
||||
render() {
|
||||
if (!this.props.publicFigures) return <span>loading public figures ...</span>;
|
||||
|
||||
const renderChilds = () => this.props.publicFigures.map(
|
||||
pf => <PublicFigureInList key={pf.id} publicFigure={pf} />
|
||||
const renderChilds = () => this.props.publicFigures.map(
|
||||
pf => <PublicFigureInList key={pf.id} publicFigure={pf} />
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="col-md-9">
|
||||
{renderChilds()}
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<LastStatements />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="col-md-9">
|
||||
{renderChilds()}
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<LastStatements />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(PublicFigures);
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ import { Router } from 'react-router';
|
|||
import routes from '../routes';
|
||||
|
||||
const Root = ({ store, history }) => (
|
||||
<Provider store={store}>
|
||||
<Router history={history} routes={routes} />
|
||||
</Provider>
|
||||
<Provider store={store}>
|
||||
<Router history={history} routes={routes} />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
Root.propTypes = {
|
||||
store: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
store: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Root;
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Route, IndexRoute } from 'react-router';
|
||||
|
||||
// Main Page
|
||||
import Main from 'Main/index';
|
||||
export default (
|
||||
<Route path="/" component={Main}>
|
||||
<IndexRoute
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/Home').default);
|
||||
}, 'home');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path="contact"
|
||||
name="contact"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/Contact').default);
|
||||
}, 'contact');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path="about"
|
||||
name="about"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/About').default);
|
||||
}, 'about');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path="guide"
|
||||
name="guide"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/Guide').default);
|
||||
}, 'guide');
|
||||
}}
|
||||
/>
|
||||
<Route path="s" name="subjects">
|
||||
<IndexRoute
|
||||
name="subjects.index"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/Subjects').default);
|
||||
}, 'subjects.index');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
name="subjects.page"
|
||||
path=":subjectSlug"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/Subject').default);
|
||||
}, 'subjects.page');
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="p" name="publicFigures">
|
||||
<IndexRoute
|
||||
name="publicFigures.index"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/PublicFigures').default);
|
||||
}, 'publicFigures.index');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
name="publicFigures.page"
|
||||
path=":publicFigureSlug"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], require => {
|
||||
done(null, require('pages/publicFigure').default);
|
||||
}, 'publicFigures.page');
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
);
|
||||
82
src/routes/index.jsx
Normal file
82
src/routes/index.jsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import React from 'react';
|
||||
import { Route, IndexRoute } from 'react-router';
|
||||
|
||||
// Main Page
|
||||
import Main from 'Main/index';
|
||||
|
||||
export default (
|
||||
<Route path="/" component={Main}>
|
||||
<IndexRoute
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/Home').default);
|
||||
}, 'home');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path="contact"
|
||||
name="contact"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/Contact').default);
|
||||
}, 'contact');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path="about"
|
||||
name="about"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/About').default);
|
||||
}, 'about');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path="guide"
|
||||
name="guide"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/Guide').default);
|
||||
}, 'guide');
|
||||
}}
|
||||
/>
|
||||
<Route path="s" name="subjects">
|
||||
<IndexRoute
|
||||
name="subjects.index"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/Subjects').default);
|
||||
}, 'subjects.index');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
name="subjects.page"
|
||||
path=":subjectSlug"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/Subject').default);
|
||||
}, 'subjects.page');
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="p" name="publicFigures">
|
||||
<IndexRoute
|
||||
name="publicFigures.index"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/PublicFigures').default);
|
||||
}, 'publicFigures.index');
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
name="publicFigures.page"
|
||||
path=":publicFigureSlug"
|
||||
getComponent={(nextState, done) => {
|
||||
require.ensure([], (require) => {
|
||||
done(null, require('pages/publicFigure').default);
|
||||
}, 'publicFigures.page');
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import types from '../actions_types';
|
||||
|
||||
export const onAddStatementValidate = (newStatement) => ({
|
||||
export const onAddStatementValidate = newStatement => ({
|
||||
type: types.ADD_STATEMENT_VALIDATE,
|
||||
payload: { ...newStatement },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import actionsTypes from '../actions_types';
|
||||
|
||||
export const onLastStatementsAccess = () => ({
|
||||
type: actionsTypes.ENTITY_ACCESS,
|
||||
accessType: 'list',
|
||||
entityType: 'statements',
|
||||
listType: 'latest',
|
||||
type: actionsTypes.ENTITY_ACCESS,
|
||||
accessType: 'list',
|
||||
entityType: 'statements',
|
||||
listType: 'latest',
|
||||
});
|
||||
|
||||
export const onHottestSubjectsAccess = () => ({
|
||||
type: actionsTypes.ENTITY_ACCESS,
|
||||
accessType: 'list',
|
||||
entityType: 'subjects',
|
||||
listType: 'hottest',
|
||||
type: actionsTypes.ENTITY_ACCESS,
|
||||
accessType: 'list',
|
||||
entityType: 'subjects',
|
||||
listType: 'hottest',
|
||||
});
|
||||
|
||||
export const onPublicFiguresListAccess = () => ({
|
||||
type: actionsTypes.ENTITY_ACCESS,
|
||||
accessType: 'list',
|
||||
entityType: 'publicFigures',
|
||||
listType: 'all',
|
||||
type: actionsTypes.ENTITY_ACCESS,
|
||||
accessType: 'list',
|
||||
entityType: 'publicFigures',
|
||||
listType: 'all',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import { routerReducer, routerMiddleware } from 'react-router-redux';
|
||||
import { routerReducer, routerMiddleware, syncHistoryWithStore } from 'react-router-redux';
|
||||
import { map } from 'ramda';
|
||||
import { isClientSide } from 'helpers/env';
|
||||
import rootSaga from './sagas';
|
||||
import { entitiesReducer, addStatementReducer } from './reducers';
|
||||
|
||||
import { useRouterHistory } from 'react-router';
|
||||
import { syncHistoryWithStore } from 'react-router-redux';
|
||||
import createHashHistory from 'history/lib/createHashHistory';
|
||||
import createMemoryHistory from 'history/lib/createMemoryHistory';
|
||||
|
||||
import rootSaga from './sagas';
|
||||
import { entitiesReducer } from './reducers';
|
||||
|
||||
// Build history
|
||||
const createHistory = isClientSide() ? createHashHistory : createMemoryHistory;
|
||||
// const browserHistory = useScroll(useRouterHistory(createHistory))();
|
||||
|
|
@ -21,31 +20,30 @@ const initialState = isClientSide() ? window.__INITIAL_STATE__ : {};
|
|||
|
||||
// REDUCERS
|
||||
const reducer = combineReducers({
|
||||
routing: routerReducer,
|
||||
entities: entitiesReducer,
|
||||
addStatement: addStatementReducer,
|
||||
routing: routerReducer,
|
||||
entities: entitiesReducer,
|
||||
});
|
||||
|
||||
// MIDDLEWARE
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
const middlewares = [routerMiddleware(browserHistory), sagaMiddleware];
|
||||
if (isClientSide() && process.env.NODE_ENV !== 'production') {
|
||||
middlewares.push(require('redux-logger')({
|
||||
stateTransformer: map(state => ((state && state.toJS) ? state.toJS() : state)),
|
||||
timestamp: true,
|
||||
duration: true,
|
||||
collapsed: true,
|
||||
predicate: (getState, action) => action.type !== 'APP_LOG',
|
||||
}));
|
||||
middlewares.push(require('redux-logger')({
|
||||
stateTransformer: map(state => ((state && state.toJS) ? state.toJS() : state)),
|
||||
timestamp: true,
|
||||
duration: true,
|
||||
collapsed: true,
|
||||
predicate: (getState, action) => action.type !== 'APP_LOG',
|
||||
}));
|
||||
}
|
||||
|
||||
// STORE
|
||||
const store = createStore(
|
||||
reducer,
|
||||
initialState,
|
||||
compose(
|
||||
applyMiddleware(...middlewares),
|
||||
window.devToolsExtension ? window.devToolsExtension() : f => f,
|
||||
reducer,
|
||||
initialState,
|
||||
compose(
|
||||
applyMiddleware(...middlewares),
|
||||
window.devToolsExtension ? window.devToolsExtension() : f => f,
|
||||
)
|
||||
);
|
||||
const history = syncHistoryWithStore(browserHistory, store);
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import {
|
||||
assoc, compose, not, allPass, prop, isNil, either, complement,
|
||||
} from 'ramda';
|
||||
import actionsTypes from '../actions_types';
|
||||
|
||||
const initialState = {
|
||||
publicFigureId: null,
|
||||
subjectId: null,
|
||||
positionId: null,
|
||||
statementDate: null,
|
||||
evidenceUrl: null,
|
||||
evidenceFile: null,
|
||||
quote: null,
|
||||
note: null,
|
||||
tags: [],
|
||||
};
|
||||
|
||||
const isPublicFigureChosen = compose(not, isNil, prop('publicFigureId'));
|
||||
const isSubjectChosen = compose(not, isNil, prop('publicFigureId'));
|
||||
const isPositionChosen = compose(not, isNil, prop('publicFigureId'));
|
||||
const isStatementComplete = allPass([
|
||||
compose(not, isNil, prop('statementDate')),
|
||||
compose(not, isNil, prop('quote')),
|
||||
compose(not, isNil, prop('statementDate')),
|
||||
either(
|
||||
compose(not, isNil, prop('evidenceUrl')),
|
||||
compose(not, isNil, prop('evidenceFile')),
|
||||
)
|
||||
]);
|
||||
|
||||
const isPublicFigureMissing = complement(isPublicFigureChosen);
|
||||
const isSubjectMissing = complement(isSubjectChosen);
|
||||
const isPositionMissing = complement(isPositionChosen);
|
||||
const isStatementIncomplete = complement(isStatementComplete);
|
||||
|
||||
export const addStatementReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case actionsTypes.ADD_STATEMENT_PUBLIC_FIGURE_SELECTION: return assoc('publicFigureId', action.id, state);
|
||||
case actionsTypes.ADD_STATEMENT_SUBJECT_SELECTION: return assoc('subjectId', action.id, state);
|
||||
case actionsTypes.ADD_STATEMENT_POSITION_SELECTION: return assoc('positionId', action.id, state);
|
||||
case actionsTypes.ADD_STATEMENT_UPDATE_EVIDENCE_URL: return assoc('evidenceUrl', action.url, state);
|
||||
case actionsTypes.ADD_STATEMENT_UPDATE_EVIDENCE_FILE: return assoc('evidenceFile', action.file, state);
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,35 +1,36 @@
|
|||
import actionsType from '../actions_types';
|
||||
import { map, compose, prop, merge, assoc, not, isNil, pipe, when } from 'ramda';
|
||||
const initialState = {};
|
||||
import { indexAndGroup } from 'api/jsonApiParser';
|
||||
import actionsType from '../actions_types';
|
||||
|
||||
const initialState = {};
|
||||
|
||||
const isNotNil = compose(not, isNil);
|
||||
|
||||
export const indexAndGroupMainData = compose(
|
||||
indexAndGroup,
|
||||
map(assoc('fetched', true)),
|
||||
prop('data')
|
||||
indexAndGroup,
|
||||
map(assoc('fetched', true)),
|
||||
prop('data')
|
||||
);
|
||||
|
||||
const indexAndGroupIncludedData = pipe(
|
||||
prop('included'),
|
||||
when(
|
||||
isNotNil,
|
||||
compose(
|
||||
indexAndGroup,
|
||||
map(assoc('fetched', true)),
|
||||
prop('included'),
|
||||
when(
|
||||
isNotNil,
|
||||
compose(
|
||||
indexAndGroup,
|
||||
map(assoc('fetched', true)),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
const saveData = data => compose(
|
||||
merge(indexAndGroupIncludedData(data)),
|
||||
indexAndGroupMainData,
|
||||
merge(indexAndGroupIncludedData(data)),
|
||||
indexAndGroupMainData,
|
||||
)(data);
|
||||
|
||||
export const entitiesReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case actionsType.ENTITY_READ: return merge(state, saveData(action.data));
|
||||
default: return state;
|
||||
}
|
||||
switch (action.type) {
|
||||
case actionsType.ENTITY_READ: return merge(state, saveData(action.data));
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
export * from './entities';
|
||||
export * from './addStatement';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,43 +1,45 @@
|
|||
import { identity } from 'ramda';
|
||||
import actionsTypes from '../actions_types';
|
||||
import { takeEvery } from 'redux-saga';
|
||||
import { call, put } from 'redux-saga/effects';
|
||||
import { getSubjects, getStatements, getPositions } from 'api/debats';
|
||||
|
||||
import actionsTypes from '../actions_types';
|
||||
|
||||
const getApiCallFor = (entityType, entityRequest) => {
|
||||
switch (entityType) {
|
||||
case 'statements': return getStatements;
|
||||
case 'subjects':
|
||||
switch (entityRequest) {
|
||||
case 'hottest': return getSubjects;
|
||||
default: return identity;
|
||||
}
|
||||
case 'publicFigures':
|
||||
switch (entityRequest) {
|
||||
case 'hottest': return getSubjects;
|
||||
case 'all': return getSubjects;
|
||||
default: return identity;
|
||||
}
|
||||
switch (entityType) {
|
||||
case 'statements': return getStatements;
|
||||
case 'subjects':
|
||||
switch (entityRequest) {
|
||||
case 'hottest': return getSubjects;
|
||||
default: return identity;
|
||||
}
|
||||
}
|
||||
case 'publicFigures':
|
||||
switch (entityRequest) {
|
||||
case 'hottest': return getSubjects;
|
||||
case 'all': return getSubjects;
|
||||
default: return identity;
|
||||
}
|
||||
default: return identity;
|
||||
}
|
||||
};
|
||||
|
||||
function* fetchEntityIfNeeded(action) {
|
||||
// Test Do we have to call API
|
||||
|
||||
const apiCall = getApiCallFor(action.entityType, (
|
||||
action.accessType === 'list' ? action.listType : action.accessedId
|
||||
const apiCall = getApiCallFor(action.entityType, (
|
||||
action.accessType === 'list' ? action.listType : action.accessedId
|
||||
));
|
||||
|
||||
// Call API
|
||||
const response = yield call(apiCall);
|
||||
const response = yield call(apiCall);
|
||||
|
||||
// Error actions
|
||||
|
||||
// Success actions
|
||||
yield put({ type: actionsTypes.ENTITY_READ, data: response.data });
|
||||
yield put({ type: actionsTypes.ENTITY_READ, data: response.data });
|
||||
}
|
||||
|
||||
export function* watchEntityAccess() {
|
||||
yield* takeEvery(actionsTypes.ENTITY_ACCESS, fetchEntityIfNeeded);
|
||||
yield* takeEvery(actionsTypes.ENTITY_ACCESS, fetchEntityIfNeeded);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import { createSelector } from 'reselect';
|
|||
import { prop, propEq, find, pipe, dissoc, when, not, isNil, compose } from 'ramda';
|
||||
import { getPublicFigures, getSubjects, getPositions, enrichWithRelationships } from './entities';
|
||||
|
||||
import { withConsole } from 'helpers/debug';
|
||||
|
||||
const isNotNil = compose(not, isNil);
|
||||
|
||||
const getAddState = state => state.addStatement;
|
||||
|
|
@ -11,43 +9,43 @@ const getAddState = state => state.addStatement;
|
|||
const injectPositions = enrichWithRelationships('positions', 'positions');
|
||||
|
||||
export const getAddStatementPublicFigure = createSelector(
|
||||
getAddState,
|
||||
getPublicFigures,
|
||||
(addState, publicFigures) => find(
|
||||
propEq('id', prop('publicFigureId')(addState))
|
||||
)(publicFigures)
|
||||
getAddState,
|
||||
getPublicFigures,
|
||||
(addState, publicFigures) => find(
|
||||
propEq('id', prop('publicFigureId')(addState))
|
||||
)(publicFigures)
|
||||
);
|
||||
|
||||
export const getAddStatementSubject = createSelector(
|
||||
getAddState,
|
||||
getSubjects,
|
||||
getPositions,
|
||||
(addState, allSubjects, allPositions) => pipe(
|
||||
find(propEq('id', prop('subjectId')(addState))),
|
||||
when(
|
||||
isNotNil,
|
||||
pipe(
|
||||
injectPositions(allPositions),
|
||||
dissoc('relationthips')
|
||||
)
|
||||
),
|
||||
)(allSubjects)
|
||||
getAddState,
|
||||
getSubjects,
|
||||
getPositions,
|
||||
(addState, allSubjects, allPositions) => pipe(
|
||||
find(propEq('id', prop('subjectId')(addState))),
|
||||
when(
|
||||
isNotNil,
|
||||
pipe(
|
||||
injectPositions(allPositions),
|
||||
dissoc('relationthips')
|
||||
)
|
||||
),
|
||||
)(allSubjects)
|
||||
);
|
||||
|
||||
export const getAddStatementPosition = createSelector(
|
||||
getAddState,
|
||||
getPositions,
|
||||
(addState, publicFigures) => find(
|
||||
propEq('id', prop('positionId')(addState))
|
||||
)(publicFigures)
|
||||
getAddState,
|
||||
getPositions,
|
||||
(addState, publicFigures) => find(
|
||||
propEq('id', prop('positionId')(addState))
|
||||
)(publicFigures)
|
||||
);
|
||||
|
||||
export const getAddStatementEvidenceUrl = createSelector(
|
||||
getAddState,
|
||||
prop('evidenceUrl')
|
||||
getAddState,
|
||||
prop('evidenceUrl')
|
||||
);
|
||||
|
||||
export const getAddStatementEvidenceFile = createSelector(
|
||||
getAddState,
|
||||
prop('evidenceFile')
|
||||
getAddState,
|
||||
prop('evidenceFile')
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { curry, assoc, compose, when, take, path, is, map, isNil, always, values, propEq, ifElse, pipe, find } from 'ramda';
|
||||
|
||||
export const getSubjects = state => values(state.entities.subjects);
|
||||
export const getPositions = state => values(state.entities.positions);
|
||||
export const getStatements = state => values(state.entities.statements);
|
||||
export const getPublicFigures = state => values(state.entities['public-figures']);
|
||||
|
||||
import { withConsole, warn } from 'helpers/debug';
|
||||
|
||||
const getEntityByRef = curry(
|
||||
(sourceEntities, entityReference) => ifElse(
|
||||
isNil,
|
||||
always(entityReference), // No source entities yet, return reference object
|
||||
pipe(
|
||||
find(propEq('id', entityReference.id)),
|
||||
when(isNil, always(entityReference)), // Entity not fetched yet, return reference object
|
||||
(sourceEntities, entityReference) => ifElse(
|
||||
isNil,
|
||||
always(entityReference), // No source entities yet, return reference object
|
||||
pipe(
|
||||
find(propEq('id', entityReference.id)),
|
||||
when(isNil, always(entityReference)), // Entity not fetched yet, return reference object
|
||||
),
|
||||
)(sourceEntities)
|
||||
);
|
||||
|
|
@ -23,14 +23,14 @@ const getEntityByRef = curry(
|
|||
* const injectPublicFigure = enrichWithRelationship('publicFigure', 'public-figure');
|
||||
*/
|
||||
export const enrichWithRelationship = curry(
|
||||
(propertyName, relationshipName, fromCollection, entity) => assoc(
|
||||
propertyName,
|
||||
compose(
|
||||
getEntityByRef(fromCollection),
|
||||
when(is(Array), take(1)),
|
||||
path(['relationships', relationshipName, 'data']),
|
||||
(propertyName, relationshipName, fromCollection, entity) => assoc(
|
||||
propertyName,
|
||||
compose(
|
||||
getEntityByRef(fromCollection),
|
||||
when(is(Array), take(1)),
|
||||
path(['relationships', relationshipName, 'data']),
|
||||
)(entity),
|
||||
entity
|
||||
entity
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -41,13 +41,13 @@ export const enrichWithRelationship = curry(
|
|||
* = enrichWithRelationships('remarquablePublicFigures', 'remarquable-public-figures');
|
||||
*/
|
||||
export const enrichWithRelationships = curry(
|
||||
(propertyName, relationshipName, fromCollection, entity) => assoc(
|
||||
propertyName,
|
||||
compose(
|
||||
map(getEntityByRef(fromCollection)),
|
||||
path(['relationships', relationshipName, 'data'])
|
||||
(propertyName, relationshipName, fromCollection, entity) => assoc(
|
||||
propertyName,
|
||||
compose(
|
||||
map(getEntityByRef(fromCollection)),
|
||||
path(['relationships', relationshipName, 'data'])
|
||||
)(entity),
|
||||
entity
|
||||
entity
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
/* eslint-disable import/export */ // since positions.js is an empty file
|
||||
export * from './positions';
|
||||
|
||||
export * from './publicFigures';
|
||||
export * from './statements';
|
||||
export * from './subjects';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { values, pipe, compose, map, dissoc, prop } from 'ramda';
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import { values, pipe, compose, map, dissoc } from 'ramda';
|
||||
import { whenNotNil } from 'helpers/ramda-ext';
|
||||
import { createSelector } from 'reselect';
|
||||
import { enrichWithRelationships, getSubjects, getPublicFigures } from './entities';
|
||||
|
|
@ -6,14 +8,14 @@ import { enrichWithRelationships, getSubjects, getPublicFigures } from './entiti
|
|||
const injectSubjects = enrichWithRelationships('subjects', 'subjects');
|
||||
|
||||
export const getPublicFiguresWithRelations = createSelector(
|
||||
getPublicFigures,
|
||||
getSubjects,
|
||||
(publicFigures, allSubjects) => whenNotNil(
|
||||
compose(
|
||||
values,
|
||||
map(pipe(
|
||||
getPublicFigures,
|
||||
getSubjects,
|
||||
(publicFigures, allSubjects) => whenNotNil(
|
||||
compose(
|
||||
values,
|
||||
map(pipe(
|
||||
// injectSubjects(allSubjects),
|
||||
dissoc('relationthips'),
|
||||
dissoc('relationthips'),
|
||||
))
|
||||
)
|
||||
)(publicFigures)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { values, pipe, map, compose, dissoc } from 'ramda';
|
|||
import { createSelector } from 'reselect';
|
||||
import { whenNotNil } from 'helpers/ramda-ext';
|
||||
import {
|
||||
enrichWithRelationship, getPublicFigures, getPositions, getSubjects, getStatements
|
||||
enrichWithRelationship, getPublicFigures, getPositions, getSubjects, getStatements,
|
||||
} from './entities';
|
||||
|
||||
const injectPublicFigure = enrichWithRelationship('publicFigure', 'public-figure');
|
||||
|
|
@ -10,18 +10,18 @@ const injectPosition = enrichWithRelationship('position', 'position');
|
|||
const injectSubject = enrichWithRelationship('subject', 'subject');
|
||||
|
||||
export const getLatestStatements = createSelector(
|
||||
getStatements,
|
||||
getPublicFigures,
|
||||
getPositions,
|
||||
getSubjects,
|
||||
(allStatements, allPublicFigures, allPositions, allSubjects) => whenNotNil(
|
||||
compose(
|
||||
values,
|
||||
map(pipe(
|
||||
injectPublicFigure(allPublicFigures),
|
||||
injectSubject(allSubjects),
|
||||
injectPosition(allPositions),
|
||||
dissoc('relationthips'),
|
||||
getStatements,
|
||||
getPublicFigures,
|
||||
getPositions,
|
||||
getSubjects,
|
||||
(allStatements, allPublicFigures, allPositions, allSubjects) => whenNotNil(
|
||||
compose(
|
||||
values,
|
||||
map(pipe(
|
||||
injectPublicFigure(allPublicFigures),
|
||||
injectSubject(allSubjects),
|
||||
injectPosition(allPositions),
|
||||
dissoc('relationthips'),
|
||||
))
|
||||
)
|
||||
)(allStatements)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { values, pipe, compose, map, dissoc, propEq } from 'ramda';
|
||||
import { find, values, pipe, compose, map, dissoc, propEq } from 'ramda';
|
||||
import { createSelector } from 'reselect';
|
||||
import { whenNotNil } from 'helpers/ramda-ext';
|
||||
import { enrichWithRelationships, getPublicFigures, getPositions, getSubjects } from './entities';
|
||||
|
||||
import { withConsole } from 'helpers/debug';
|
||||
import { enrichWithRelationships, getPublicFigures, getPositions, getSubjects } from './entities';
|
||||
|
||||
export const getHomeSubjects = state => values(getSubjects(state));
|
||||
|
||||
|
|
@ -13,29 +12,29 @@ const injectRemarquablePublicFigures
|
|||
const injectPositions = enrichWithRelationships('positions', 'positions');
|
||||
|
||||
export const getHomeSubjectsWithRelations = createSelector(
|
||||
getHomeSubjects,
|
||||
getPublicFigures,
|
||||
getPositions,
|
||||
(homeSubjects, allPublicFigures, allPositions) => whenNotNil(
|
||||
compose(
|
||||
values,
|
||||
map(pipe(
|
||||
injectRemarquablePublicFigures(allPublicFigures),
|
||||
injectPositions(allPositions),
|
||||
dissoc('relationships'),
|
||||
getHomeSubjects,
|
||||
getPublicFigures,
|
||||
getPositions,
|
||||
(homeSubjects, allPublicFigures, allPositions) => whenNotNil(
|
||||
compose(
|
||||
values,
|
||||
map(pipe(
|
||||
injectRemarquablePublicFigures(allPublicFigures),
|
||||
injectPositions(allPositions),
|
||||
dissoc('relationships'),
|
||||
))
|
||||
)
|
||||
)(homeSubjects)
|
||||
);
|
||||
|
||||
export const getSubject = (state, props) => createSelector(
|
||||
getSubjects,
|
||||
getPositions,
|
||||
getPublicFigures,
|
||||
(allSubjects, allPositions, allPublicFigures) => pipe(
|
||||
find(propEq('id', props.subjectId)),
|
||||
injectRemarquablePublicFigures(allPublicFigures),
|
||||
injectPositions(allPositions),
|
||||
dissoc('relationthips'),
|
||||
getSubjects,
|
||||
getPositions,
|
||||
getPublicFigures,
|
||||
(allSubjects, allPositions, allPublicFigures) => pipe(
|
||||
find(propEq('id', props.subjectId)),
|
||||
injectRemarquablePublicFigures(allPublicFigures),
|
||||
injectPositions(allPositions),
|
||||
dissoc('relationthips'),
|
||||
)(allSubjects)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import { urlRegex } from './generic';
|
||||
|
||||
export const isValidEvidenceUrl = (tested) => urlRegex.test(tested);
|
||||
export const isValidEvidenceUrl = tested => urlRegex.test(tested);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ const SRC_FOLDER = 'src';
|
|||
const APP_ROOT = path.normalize(path.join(__dirname, '..'));
|
||||
|
||||
const constants = Object.freeze({
|
||||
APP_NAME: 'debats',
|
||||
APP_ROOT,
|
||||
APP_PATH: path.join(APP_ROOT, SRC_FOLDER),
|
||||
STATIC_PATH: path.join(APP_ROOT, SRC_FOLDER, 'static'),
|
||||
SRC_FOLDER,
|
||||
APP_NAME: 'debats',
|
||||
APP_ROOT,
|
||||
APP_PATH: path.join(APP_ROOT, SRC_FOLDER),
|
||||
STATIC_PATH: path.join(APP_ROOT, SRC_FOLDER, 'static'),
|
||||
SRC_FOLDER,
|
||||
});
|
||||
|
||||
module.exports = constants;
|
||||
|
|
|
|||
|
|
@ -1,54 +1,55 @@
|
|||
const CONSTANTS = require('../webpack/constants');
|
||||
|
||||
const APP_PATH = CONSTANTS.APP_PATH;
|
||||
|
||||
const loaders = [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /src.+\.css$/,
|
||||
loader: 'style-loader'
|
||||
+ '!css-loader'
|
||||
+ '?modules&localIdentName=[name]__[local]___[hash:base64:5]'
|
||||
+ '&importLoaders=1'
|
||||
+ '!postcss-loader',
|
||||
},
|
||||
{
|
||||
test: /node_modules.*\.css$/,
|
||||
loader: 'style-loader'
|
||||
+ '!css-loader'
|
||||
+ '!postcss-loader',
|
||||
},
|
||||
{
|
||||
test: /\.(png|svg|gif|jpeg|jpg)$/,
|
||||
include: APP_PATH,
|
||||
loader: 'file?name=images/[name].[ext]',
|
||||
},
|
||||
{
|
||||
test: /\.(eot|woff|ttf)$/,
|
||||
include: APP_PATH,
|
||||
loader: 'file-loader?name=fonts/[name].[ext]',
|
||||
},
|
||||
{
|
||||
test: /\.(svg|woff2?)$/,
|
||||
exclude: APP_PATH,
|
||||
loader: 'url?limit=10000',
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot)$/,
|
||||
exclude: APP_PATH,
|
||||
loader: 'file',
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json',
|
||||
},
|
||||
{
|
||||
test: /\.md$/,
|
||||
loader: 'raw',
|
||||
},
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /src.+\.css$/,
|
||||
loader: 'style-loader'
|
||||
+ '!css-loader'
|
||||
+ '?modules&localIdentName=[name]__[local]___[hash:base64:5]'
|
||||
+ '&importLoaders=1'
|
||||
+ '!postcss-loader',
|
||||
},
|
||||
{
|
||||
test: /node_modules.*\.css$/,
|
||||
loader: 'style-loader'
|
||||
+ '!css-loader'
|
||||
+ '!postcss-loader',
|
||||
},
|
||||
{
|
||||
test: /\.(png|svg|gif|jpeg|jpg)$/,
|
||||
include: APP_PATH,
|
||||
loader: 'file?name=images/[name].[ext]',
|
||||
},
|
||||
{
|
||||
test: /\.(eot|woff|ttf)$/,
|
||||
include: APP_PATH,
|
||||
loader: 'file-loader?name=fonts/[name].[ext]',
|
||||
},
|
||||
{
|
||||
test: /\.(svg|woff2?)$/,
|
||||
exclude: APP_PATH,
|
||||
loader: 'url?limit=10000',
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot)$/,
|
||||
exclude: APP_PATH,
|
||||
loader: 'file',
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json',
|
||||
},
|
||||
{
|
||||
test: /\.md$/,
|
||||
loader: 'raw',
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = loaders;
|
||||
|
|
|
|||
|
|
@ -12,35 +12,36 @@ const postcssHide = require('postcss-hide');
|
|||
|
||||
const CONSTANTS = require('./constants');
|
||||
|
||||
const makeMap = aPath => {
|
||||
try {
|
||||
return webpackPostcssTools.makeVarMap(aPath);
|
||||
} catch (e) {
|
||||
const makeMap = (aPath) => {
|
||||
try {
|
||||
return webpackPostcssTools.makeVarMap(aPath);
|
||||
} catch (e) {
|
||||
// console.log(`${aPath} not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
return null;
|
||||
process.exit(1);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const map = makeMap('./src/styles/_constants.css');
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = webpack => [
|
||||
postcssMixins({
|
||||
mixinsDir: path.join(CONSTANTS.APP_PATH, 'style', 'mixins'),
|
||||
}),
|
||||
atImport(),
|
||||
postcssShortPosition(),
|
||||
postcssAssets(),
|
||||
postcssColorFunction(),
|
||||
postcssNext({
|
||||
features: {
|
||||
customProperties: {
|
||||
variables: map.vars,
|
||||
warnings: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
lost(),
|
||||
postcssInlineSVG(),
|
||||
postcssHide(),
|
||||
postcssMixins({
|
||||
mixinsDir: path.join(CONSTANTS.APP_PATH, 'style', 'mixins'),
|
||||
}),
|
||||
atImport(),
|
||||
postcssShortPosition(),
|
||||
postcssAssets(),
|
||||
postcssColorFunction(),
|
||||
postcssNext({
|
||||
features: {
|
||||
customProperties: {
|
||||
variables: map.vars,
|
||||
warnings: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
lost(),
|
||||
postcssInlineSVG(),
|
||||
postcssHide(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
const path = require('path');
|
||||
|
||||
const CONSTANTS = require('./constants');
|
||||
|
||||
const APP_ROOT = CONSTANTS.APP_ROOT;
|
||||
|
||||
module.exports = {
|
||||
alias: {
|
||||
react: path.resolve(APP_ROOT, 'node_modules/react'),
|
||||
},
|
||||
modulesDirectories: ['src', 'web_modules', 'node_modules'],
|
||||
extensions: ['', '.js', '.jsx'],
|
||||
alias: {
|
||||
react: path.resolve(APP_ROOT, 'node_modules/react'),
|
||||
},
|
||||
modulesDirectories: ['src', 'web_modules', 'node_modules'],
|
||||
extensions: ['', '.js', '.jsx'],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const path = require('path');
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const CONSTANTS = require('./constants');
|
||||
|
||||
const APP_ROOT = CONSTANTS.APP_ROOT;
|
||||
const SRC_FOLDER = CONSTANTS.SRC_FOLDER;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const path = require('path');
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const CONSTANTS = require('./constants');
|
||||
|
||||
const APP_ROOT = CONSTANTS.APP_ROOT;
|
||||
const APP_PATH = CONSTANTS.APP_PATH;
|
||||
const SRC_FOLDER = CONSTANTS.SRC_FOLDER;
|
||||
|
|
@ -21,12 +22,12 @@ config.plugins.push(new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"product
|
|||
config.plugins.push(new webpack.optimize.DedupePlugin());
|
||||
config.plugins.push(new webpack.optimize.OccurenceOrderPlugin(true)); // Assign the module and chunk ids by occurrence count
|
||||
config.plugins.push(new webpack.optimize.UglifyJsPlugin({
|
||||
compressor: { screw_ie8: false, keep_fnames: true, warnings: true },
|
||||
mangle: {
|
||||
except: ['$super', '$', 'exports', 'require'],
|
||||
screw_ie8: false,
|
||||
keep_fnames: true,
|
||||
},
|
||||
compressor: { screw_ie8: false, keep_fnames: true, warnings: true },
|
||||
mangle: {
|
||||
except: ['$super', '$', 'exports', 'require'],
|
||||
screw_ie8: false,
|
||||
keep_fnames: true,
|
||||
},
|
||||
}));
|
||||
// config.plugins.push(new CompressionPlugin({threshold: 10240}));
|
||||
config.plugins.push(new CopyWebpackPlugin(copyConfig));
|
||||
|
|
@ -34,7 +35,7 @@ config.devtool = 'cheap-module-source-map';
|
|||
config.cache = false;
|
||||
config.debug = false;
|
||||
config.entry = `${APP_PATH}/index.js`;
|
||||
config.output.path = outputPath;;
|
||||
config.output.path = outputPath;
|
||||
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
|||
|
|
@ -2,20 +2,21 @@ const config = require('./webpack.base.config');
|
|||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const CONSTANTS = require('./constants');
|
||||
|
||||
const APP_PATH = CONSTANTS.APP_PATH;
|
||||
const APP_NAME = CONSTANTS.APP_NAME;
|
||||
const SRC_FOLDER = CONSTANTS.SRC_FOLDER;
|
||||
|
||||
|
||||
config.plugins.push(new HtmlWebpackPlugin({
|
||||
template: `${SRC_FOLDER}/index.html`,
|
||||
inject: true,
|
||||
filename: 'index.html',
|
||||
template: `${SRC_FOLDER}/index.html`,
|
||||
inject: true,
|
||||
filename: 'index.html',
|
||||
}));
|
||||
|
||||
config.entry = `${APP_PATH}/index.js`;
|
||||
config.entry = `${APP_PATH}/index.jsx`;
|
||||
config.output = {
|
||||
filename: `${APP_NAME}.[name].[hash].js`,
|
||||
filename: `${APP_NAME}.[name].[hash].js`,
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const path = require('path');
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const CONSTANTS = require('./constants');
|
||||
|
||||
const APP_ROOT = CONSTANTS.APP_ROOT;
|
||||
const SRC_FOLDER = CONSTANTS.SRC_FOLDER;
|
||||
|
||||
|
|
@ -33,30 +34,30 @@ var devServerAPIUrl;
|
|||
var devServerRewrite;
|
||||
|
||||
if (args.proxy) {
|
||||
if (args.mockAPI) {
|
||||
devServerAPIUrl = 'http://localhost:3030';
|
||||
devServerRewrite = function rewrite(req) {
|
||||
req.url = req.url // eslint-disable-line no-param-reassign
|
||||
if (args.mockAPI) {
|
||||
devServerAPIUrl = 'http://localhost:3030';
|
||||
devServerRewrite = function rewrite(req) {
|
||||
req.url = req.url // eslint-disable-line no-param-reassign
|
||||
.replace(/\/api\//, '/fake-api/') // Local mocks as API
|
||||
.split('?')[0]; // strip query string if exists
|
||||
if (req.method === 'POST') req.method = 'GET'; // eslint-disable-line no-param-reassign
|
||||
};
|
||||
}
|
||||
if (args.prodAPI) devServerAPIUrl = 'http://api.débats.co';
|
||||
if (req.method === 'POST') req.method = 'GET'; // eslint-disable-line no-param-reassign
|
||||
};
|
||||
}
|
||||
if (args.prodAPI) devServerAPIUrl = 'http://api.débats.co';
|
||||
}
|
||||
|
||||
config.devServer = {
|
||||
quiet: false,
|
||||
stats: { colors: true },
|
||||
outputPath,
|
||||
proxy: {
|
||||
'/api/*': {
|
||||
target: devServerAPIUrl,
|
||||
rewrite: devServerRewrite,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
quiet: false,
|
||||
stats: { colors: true },
|
||||
outputPath,
|
||||
proxy: {
|
||||
'/api/*': {
|
||||
target: devServerAPIUrl,
|
||||
rewrite: devServerRewrite,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue