Skip to content

Commit

Permalink
Merge pull request #294 from bookbrainz/bc-dates
Browse files Browse the repository at this point in the history
Allow BCE dates
  • Loading branch information
MonkeyDo committed Jul 30, 2019
2 parents 55b5d66 + 14c0925 commit 98f6484
Show file tree
Hide file tree
Showing 16 changed files with 539 additions and 870 deletions.
1,123 changes: 334 additions & 789 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -7,7 +7,7 @@
"mkdirs": "mkdir -p config static/js/editor static/js/entity static/stylesheets",
"clean": "./scripts/clean.sh",
"prepublishOnly": "npm run clean && npm run mkdirs && npm run copy-client-scripts",
"postinstall": "npm run prepublishOnly",
"postinstall": "npm dedup && npm run prepublishOnly",
"build-server-js": "babel src --out-dir lib",
"build-client-js": "./scripts/build-client-js.sh",
"build-less": "./scripts/build-less.sh",
Expand Down Expand Up @@ -41,11 +41,12 @@
"babel-runtime": "^6.23.0",
"bluebird": "^3.5.1",
"body-parser": "^1.14.1",
"bookbrainz-data": "^2.2.1",
"bookbrainz-data": "^2.3.0",
"browserify": "^16.2.3",
"classnames": "^2.2.5",
"compression": "^1.7.1",
"connect-redis": "^3.0.1",
"date-fns": "^2.0.0-alpha.34",
"debug": "^3.1.0",
"elasticsearch": "^15.2.0",
"express": "^4.16.2",
Expand All @@ -57,15 +58,14 @@
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"log": "^1.4.0",
"moment": "^2.19.3",
"morgan": "^1.9.1",
"passport": "^0.4.0",
"passport-musicbrainz-oauth2": "github:LordSputnik/passport-musicbrainz-oauth2",
"prop-types": "^15.6.0",
"react": "^16.2.0",
"react-addons-shallow-compare": "^15.6.0",
"react-bootstrap": "^0.31.5",
"react-datepicker": "^1.6.0",
"react-datepicker": "^2.7.0",
"react-dom": "^16.2.0",
"react-fontawesome": "^1.5.0",
"react-redux": "^5.0.2",
Expand Down
7 changes: 4 additions & 3 deletions src/client/components/pages/entities/author.js
Expand Up @@ -29,8 +29,9 @@ import React from 'react';
import {kebabCase as _kebabCase} from 'lodash';
import {labelsForAuthor} from '../../../helpers/utils';


const {deletedEntityMessage, extractAttribute, getTypeAttribute, getEntityUrl,
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias} = entityHelper;
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias, transformISODateForDisplay} = entityHelper;
const {Button, Col, Row} = bootstrap;

function AuthorAttributes({author}) {
Expand All @@ -41,8 +42,8 @@ function AuthorAttributes({author}) {
const gender = extractAttribute(author.gender, 'name');
const beginArea = extractAttribute(author.beginArea, 'name');
const endArea = extractAttribute(author.endArea, 'name');
const beginDate = extractAttribute(author.beginDate);
const endDate = extractAttribute(author.endDate);
const beginDate = transformISODateForDisplay(extractAttribute(author.beginDate));
const endDate = transformISODateForDisplay(extractAttribute(author.endDate));
const sortNameOfDefaultAlias = getSortNameOfDefaultAlias(author);

const isGroup = type === 'Group';
Expand Down
6 changes: 3 additions & 3 deletions src/client/components/pages/entities/publisher.js
Expand Up @@ -30,7 +30,7 @@ import PropTypes from 'prop-types';
import React from 'react';

const {deletedEntityMessage, extractAttribute, getTypeAttribute, getEntityUrl,
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias} = entityHelper;
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias, transformISODateForDisplay} = entityHelper;
const {Col, Row} = bootstrap;

function PublisherAttributes({publisher}) {
Expand All @@ -39,8 +39,8 @@ function PublisherAttributes({publisher}) {
}
const type = getTypeAttribute(publisher.publisherType).data;
const area = extractAttribute(publisher.area, 'name');
const beginDate = extractAttribute(publisher.beginDate);
const endDate = extractAttribute(publisher.endDate);
const beginDate = transformISODateForDisplay(extractAttribute(publisher.beginDate));
const endDate = transformISODateForDisplay(extractAttribute(publisher.endDate));
const sortNameOfDefaultAlias = getSortNameOfDefaultAlias(publisher);
return (
<div>
Expand Down
4 changes: 2 additions & 2 deletions src/client/components/pages/parts/editor-profile.js
Expand Up @@ -45,8 +45,8 @@ class EditorProfileTab extends React.Component {
name,
gender
} = editor;
const createdAtDate = formatDate(editor.createdAt, true);
const lastActiveDate = formatDate(editor.activeAt, true);
const createdAtDate = formatDate(new Date(editor.createdAt), true);
const lastActiveDate = formatDate(new Date(editor.activeAt), true);

let musicbrainzAccount = 'No Linked MusicBrainz Account';
if (cachedMetabrainzName) {
Expand Down
19 changes: 12 additions & 7 deletions src/client/components/pages/revision.js
Expand Up @@ -26,29 +26,34 @@ import PropTypes from 'prop-types';
import React from 'react';
import _ from 'lodash';
import request from 'superagent-bluebird-promise';
import {transformISODateForDisplay} from '../../helpers/entity';


const {Button, Col, ListGroup, ListGroupItem, Row} = bootstrap;
const {formatDate} = utilsHelper;

class RevisionPage extends React.Component {
static formatValueList(list) {
static formatValueList(list, isChangeADate) {
if (!list) {
return null;
}
return list.map(
(val, idx) => <div key={`${idx}${val}`}>{val.toString()}</div>
(val, idx) => {
const formattedValue = isChangeADate ? transformISODateForDisplay(val) : val.toString();
return <div key={`${idx}${val}`}>{formattedValue}</div>;
}
);
}

static formatChange(change) {
const isChangeADate = change.key.toLowerCase().match(/\bdate\b/);
if (change.kind === 'N') {
return (
<tr className="success" key={change.key}>
<th scope="row">{change.key}</th>
<td></td>
<td>
{RevisionPage.formatValueList(change.rhs)}
{RevisionPage.formatValueList(change.rhs, isChangeADate)}
</td>
</tr>
);
Expand All @@ -59,10 +64,10 @@ class RevisionPage extends React.Component {
<tr className="warning" key={change.key}>
<th scope="row">{change.key}</th>
<td>
{RevisionPage.formatValueList(change.lhs)}
{RevisionPage.formatValueList(change.lhs, isChangeADate)}
</td>
<td>
{RevisionPage.formatValueList(change.rhs)}
{RevisionPage.formatValueList(change.rhs, isChangeADate)}
</td>
</tr>
);
Expand All @@ -73,7 +78,7 @@ class RevisionPage extends React.Component {
<tr className="danger" key={change.key}>
<th scope="row">{change.key}</th>
<td>
{RevisionPage.formatValueList(change.lhs)}
{RevisionPage.formatValueList(change.lhs, isChangeADate)}
</td>
<td></td>
</tr>
Expand Down Expand Up @@ -178,7 +183,7 @@ class RevisionPage extends React.Component {
revisionNotes = <p> No revision notes present </p>;
}

const dateRevisionCreated = formatDate(new Date(revision.createdAt));
const dateRevisionCreated = formatDate(new Date(revision.createdAt), true);

return (
<Row>
Expand Down
101 changes: 77 additions & 24 deletions src/client/entity-editor/common/new-date-field.js
@@ -1,6 +1,7 @@
import * as _ from 'lodash';
import {Button, FormControl, InputGroup} from 'react-bootstrap';
import {dateObjectToString, getTodayDate} from '../../helpers/utils';

import {dateObjectToISOString, getTodayDate} from '../../helpers/utils';
import {isValid, parseISO} from 'date-fns';
import CustomInput from '../../input';
import DatePicker from 'react-datepicker';
import FontAwesome from 'react-fontawesome';
Expand All @@ -9,7 +10,7 @@ import React from 'react';
import ValidationLabel from './validation-label';
import classNames from 'classnames';
import {dateIsBefore} from '../validators/base';
import moment from 'moment';
import {transformISODateForDisplay} from '../../helpers/entity';


class DateField extends React.Component {
Expand All @@ -18,10 +19,10 @@ class DateField extends React.Component {

const {day, month, year} = this.props.defaultValue;
this.state = {
day: !day ? '' : day,
month: !month ? '' : month,
day: !day ? '' : this.padMonthOrDay(day),
month: !month ? '' : this.padMonthOrDay(month),
warn: dateIsBefore(getTodayDate(), {day, month, year}),
year: !year ? '' : year
year: !year ? '' : this.formatYearForDisplay(year)
};
}

Expand All @@ -35,33 +36,82 @@ class DateField extends React.Component {
this.setState({warn: dateIsBefore(getTodayDate(), {day, month, year})});
};

setStateCallback = () => {
this.updateDate(this.state.day, this.state.month, this.state.year);
};

handleYearChange = (event) => {
const year = event.target.value;
this.setState({year});
this.updateDate(this.state.day, this.state.month, year);
this.setState(
{year},
this.setStateCallback
);
};

handleMonthChange = (event) => {
const month = event.target.value;
this.setState({month});
this.updateDate(this.state.day, month, this.state.year);
this.setState(
{month},
this.setStateCallback
);
};

handleDayChange = (event) => {
const day = event.target.value;
this.setState({day});
this.updateDate(day, this.state.month, this.state.year);
this.setState(
{day},
this.setStateCallback
);
};

/**
* If year is a number, pad it for clarity ('84' -> '0084' to clarify it isn't '1984')
* If it is too long (eg. extended ISO format ±YYYYYY), trim it
* @function formatYearForDisplay
* @param {string|number} year - The year string or number to format
* @returns {string} a short ISO date string (YYYY-MM-DD)
*/
formatYearForDisplay = (year) => {
if (isNaN(Number(year)) || year === '') {
return year;
}
const isCommonEraDate = Math.sign(year) === 1;
const ISOyear = `${isCommonEraDate ? '+' : '-'}${_.padStart(Math.abs(year), 6, 0)}`;
return transformISODateForDisplay(ISOyear);
};

padMonthOrDay = (num) => {
// If month/day is a number, pad it, mostly to match the year padding mechanism
if (isNaN(Number(num)) || num === '') {
return num;
}
return _.padStart(num, 2, 0);
};

handleYearInputBlur = (event) => {
const year = event.target.value;
this.setState({year: this.formatYearForDisplay(year)});
};

handleMonthInputBlur = (event) => {
const month = event.target.value;
this.setState({month: this.padMonthOrDay(month)});
};

handleDayInputBlur = (event) => {
const day = event.target.value;
this.setState({day: this.padMonthOrDay(day)});
};

handleChangeOfDatePicker = (value) => {
const date = new Date(value);
const year = date.getFullYear().toString();
const month = (date.getMonth() + 1).toString();
const day = date.getDate().toString();
this.setState({year});
this.setState({month});
this.setState({day});
this.updateDate(day, month, year);
this.setState(
{day: this.padMonthOrDay(day), month: this.padMonthOrDay(month), year: this.formatYearForDisplay(year)},
this.setStateCallback
);
};

render() {
Expand All @@ -75,26 +125,27 @@ class DateField extends React.Component {
{this.props.label}
</ValidationLabel>
);

const selectedDate = dateObjectToString({
const dateString = dateObjectToISOString({
day: this.state.day,
month: this.state.month,
year: this.state.year
});
const momentDate = moment(selectedDate);
const selectedDate = parseISO(dateString);
const groupClassName = classNames({hidden: !this.props.show});
const isCommonEraDate = Math.sign(this.state.year) === 1;
return (
<div>
<CustomInput
groupClassName={groupClassName}
label={labelElement}
>
<InputGroup style={{width: '17em'}}>
<InputGroup style={{width: '18em'}}>
<FormControl
maxLength="4"
maxLength={isCommonEraDate ? 4 : 5}
placeholder="YYYY"
type="text"
value={this.state.year}
onBlur={this.handleYearInputBlur}
onChange={this.handleYearChange}
/>
<InputGroup.Addon style={{padding: '0 0.5em'}}>-</InputGroup.Addon>
Expand All @@ -104,6 +155,7 @@ class DateField extends React.Component {
style={{width: '3.5em'}}
type="text"
value={this.state.month}
onBlur={this.handleMonthInputBlur}
onChange={this.handleMonthChange}
/>
<InputGroup.Addon style={{padding: '0 0.5em'}}>-</InputGroup.Addon>
Expand All @@ -113,6 +165,7 @@ class DateField extends React.Component {
style={{width: '3.5em'}}
type="text"
value={this.state.day}
onBlur={this.handleDayInputBlur}
onChange={this.handleDayChange}
/>
<InputGroup.Button style={{fontSize: 'inherit'}}>
Expand All @@ -125,11 +178,11 @@ class DateField extends React.Component {
<FontAwesome name="calendar-alt"/>
</Button>
}
dateFormat="YYYY-MM-DD"
dateFormat="uuuuuu-MM-dd"
disabled={!isCommonEraDate}
dropdownMode="select"
selected={momentDate.isValid() ? momentDate : null}
selected={isValid(selectedDate) ? selectedDate : null}
timeFormat="false"
viewMode="years"
onChange={this.handleChangeOfDatePicker}
/>
</InputGroup.Button>
Expand Down
Expand Up @@ -18,8 +18,6 @@

// @flow

import * as moment from 'moment';

import {
type Action,
debouncedUpdateBeginDate,
Expand Down
4 changes: 2 additions & 2 deletions src/client/entity-editor/validators/date.js
@@ -1,7 +1,7 @@
/* eslint-disable no-extra-parens,eqeqeq,sort-keys */
export function dateValidator(day, month, year) {
const isPosIntRegx = /^\+?([0-9]\d*)$/;
const isYearPosInt = isPosIntRegx.test(year) && year > 0;
const isYearInt = Number.isInteger(Number(year));
const isMonthPosInt = isPosIntRegx.test(month) && month > 0;
const isDayPosInt = isPosIntRegx.test(day) && day > 0;

Expand All @@ -12,7 +12,7 @@ export function dateValidator(day, month, year) {
return {isValid: false, errorMessage: 'Year must be entered'};
}

if (!isYearPosInt) {
if (!isYearInt) {
return {isValid: false, errorMessage: 'Year is not valid'};
}
if (!month) {
Expand Down

0 comments on commit 98f6484

Please sign in to comment.