mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-05-26 23:26:15 +00:00
Upgraded to the newest version of react-intl
This commit is contained in:
1280
package-lock.json
generated
1280
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
|||||||
"query-string": "^7.0.0",
|
"query-string": "^7.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-intl": "^2.8.0",
|
"react-intl": "^5.15.5",
|
||||||
"react-linkify": "^0.2.2",
|
"react-linkify": "^0.2.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"semantic-ui-css": "^2.4.1",
|
"semantic-ui-css": "^2.4.1",
|
||||||
@@ -45,7 +45,6 @@
|
|||||||
"@types/md5": "^2.3.0",
|
"@types/md5": "^2.3.0",
|
||||||
"@types/react": "^17.0.3",
|
"@types/react": "^17.0.3",
|
||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@types/react-intl": "^2.3.15",
|
|
||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
||||||
"@typescript-eslint/parser": "^4.19.0",
|
"@typescript-eslint/parser": "^4.19.0",
|
||||||
|
|||||||
34
src/app.tsx
34
src/app.tsx
@@ -2,13 +2,12 @@ import * as H from 'history';
|
|||||||
import * as queryString from 'query-string';
|
import * as queryString from 'query-string';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {analyticsEvent} from './util/analytics';
|
import {analyticsEvent} from './util/analytics';
|
||||||
import {Chart, ChartType} from './chart';
|
import {Chart, ChartComponent, ChartType} from './chart';
|
||||||
import {Details} from './details';
|
import {Details} from './details';
|
||||||
import {EmbeddedDataSource, EmbeddedSourceSpec} from './datasource/embedded';
|
import {EmbeddedDataSource, EmbeddedSourceSpec} from './datasource/embedded';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage, WrappedComponentProps} from 'react-intl';
|
||||||
import {TopolaData} from './util/gedcom_util';
|
import {TopolaData} from './util/gedcom_util';
|
||||||
import {IndiInfo} from 'topola';
|
import {IndiInfo} from 'topola';
|
||||||
import {intlShape} from 'react-intl';
|
|
||||||
import {Intro} from './intro';
|
import {Intro} from './intro';
|
||||||
import {Loader, Message, Portal, Responsive} from 'semantic-ui-react';
|
import {Loader, Message, Portal, Responsive} from 'semantic-ui-react';
|
||||||
import {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';
|
import {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';
|
||||||
@@ -187,19 +186,17 @@ interface State {
|
|||||||
freezeAnimation?: boolean;
|
freezeAnimation?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class App extends React.Component<RouteComponentProps, {}> {
|
export class App extends React.Component<
|
||||||
|
RouteComponentProps & WrappedComponentProps,
|
||||||
|
{}
|
||||||
|
> {
|
||||||
state: State = {
|
state: State = {
|
||||||
state: AppState.INITIAL,
|
state: AppState.INITIAL,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
chartType: ChartType.Hourglass,
|
chartType: ChartType.Hourglass,
|
||||||
showErrorPopup: false,
|
showErrorPopup: false,
|
||||||
};
|
};
|
||||||
chartRef: Chart | null = null;
|
chartRef: ChartComponent | null = null;
|
||||||
|
|
||||||
/** Make intl appear in this.context. */
|
|
||||||
static contextTypes = {
|
|
||||||
intl: intlShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Sets the state with a new individual selection and chart type. */
|
/** Sets the state with a new individual selection and chart type. */
|
||||||
private updateDisplay(
|
private updateDisplay(
|
||||||
@@ -234,9 +231,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
|
|
||||||
private readonly uploadedDataSource = new UploadedDataSource();
|
private readonly uploadedDataSource = new UploadedDataSource();
|
||||||
private readonly gedcomUrlDataSource = new GedcomUrlDataSource();
|
private readonly gedcomUrlDataSource = new GedcomUrlDataSource();
|
||||||
private readonly wikiTreeDataSource = new WikiTreeDataSource(
|
private readonly wikiTreeDataSource = new WikiTreeDataSource(this.props.intl);
|
||||||
this.context.intl,
|
|
||||||
);
|
|
||||||
private readonly embeddedDataSource = new EmbeddedDataSource();
|
private readonly embeddedDataSource = new EmbeddedDataSource();
|
||||||
|
|
||||||
private isNewData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {
|
private isNewData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {
|
||||||
@@ -331,7 +326,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setError(getI18nMessage(error, this.context.intl));
|
this.setError(getI18nMessage(error, this.props.intl));
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
this.state.state === AppState.SHOWING_CHART ||
|
this.state.state === AppState.SHOWING_CHART ||
|
||||||
@@ -353,10 +348,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
});
|
});
|
||||||
if (loadMoreFromWikitree) {
|
if (loadMoreFromWikitree) {
|
||||||
try {
|
try {
|
||||||
const data = await loadWikiTree(
|
const data = await loadWikiTree(args.selection!.id, this.props.intl);
|
||||||
args.selection!.id,
|
|
||||||
this.context.intl,
|
|
||||||
);
|
|
||||||
const selection = getSelection(data.chartData, args.selection);
|
const selection = getSelection(data.chartData, args.selection);
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {
|
||||||
@@ -367,7 +359,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showErrorPopup(
|
this.showErrorPopup(
|
||||||
this.context.intl.formatMessage(
|
this.props.intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: 'error.failed_wikitree_load_more',
|
id: 'error.failed_wikitree_load_more',
|
||||||
defaultMessage: 'Failed to load data from WikiTree. {error}',
|
defaultMessage: 'Failed to load data from WikiTree. {error}',
|
||||||
@@ -424,7 +416,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
this.chartRef && (await this.chartRef.downloadPdf());
|
this.chartRef && (await this.chartRef.downloadPdf());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showErrorPopup(
|
this.showErrorPopup(
|
||||||
this.context.intl.formatMessage({
|
this.props.intl.formatMessage({
|
||||||
id: 'error.failed_pdf',
|
id: 'error.failed_pdf',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Failed to generate PDF file.' +
|
'Failed to generate PDF file.' +
|
||||||
@@ -440,7 +432,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
this.chartRef && (await this.chartRef.downloadPng());
|
this.chartRef && (await this.chartRef.downloadPng());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showErrorPopup(
|
this.showErrorPopup(
|
||||||
this.context.intl.formatMessage({
|
this.props.intl.formatMessage({
|
||||||
id: 'error.failed_png',
|
id: 'error.failed_png',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Failed to generate PNG file.' +
|
'Failed to generate PNG file.' +
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {select, Selection} from 'd3-selection';
|
import {select, Selection} from 'd3-selection';
|
||||||
import {interpolateNumber} from 'd3-interpolate';
|
import {interpolateNumber} from 'd3-interpolate';
|
||||||
import {intlShape} from 'react-intl';
|
import {injectIntl, WrappedComponentProps} from 'react-intl';
|
||||||
import {max, min} from 'd3-array';
|
import {max, min} from 'd3-array';
|
||||||
import {Responsive} from 'semantic-ui-react';
|
import {Responsive} from 'semantic-ui-react';
|
||||||
import {saveAs} from 'file-saver';
|
import {saveAs} from 'file-saver';
|
||||||
@@ -149,7 +149,10 @@ export interface ChartProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Component showing the genealogy chart and handling transition animations. */
|
/** Component showing the genealogy chart and handling transition animations. */
|
||||||
export class Chart extends React.PureComponent<ChartProps, {}> {
|
export class ChartComponent extends React.PureComponent<
|
||||||
|
ChartProps & WrappedComponentProps,
|
||||||
|
{}
|
||||||
|
> {
|
||||||
private chart?: ChartHandle;
|
private chart?: ChartHandle;
|
||||||
/** Animation is in progress. */
|
/** Animation is in progress. */
|
||||||
private animating = false;
|
private animating = false;
|
||||||
@@ -214,7 +217,7 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
indiCallback: (info) => this.props.onSelection(info),
|
indiCallback: (info) => this.props.onSelection(info),
|
||||||
animate: true,
|
animate: true,
|
||||||
updateSvgSize: false,
|
updateSvgSize: false,
|
||||||
locale: this.context.intl.locale,
|
locale: this.props.intl.locale,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.chart!.setData(this.props.data);
|
this.chart!.setData(this.props.data);
|
||||||
@@ -303,11 +306,6 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
this.renderChart({initialRender});
|
this.renderChart({initialRender});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Make intl appear in this.context. */
|
|
||||||
static contextTypes = {
|
|
||||||
intl: intlShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="svgContainer">
|
<div id="svgContainer">
|
||||||
@@ -410,3 +408,4 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
doc.save('topola.pdf');
|
doc.save('topola.pdf');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const Chart = injectIntl(ChartComponent, {forwardRef: true});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {DataSource, DataSourceEnum, SourceSelection} from './data_source';
|
|||||||
import {Date, DateOrRange, JsonFam, JsonIndi} from 'topola';
|
import {Date, DateOrRange, JsonFam, JsonIndi} from 'topola';
|
||||||
import {GedcomData, normalizeGedcom, TopolaData} from '../util/gedcom_util';
|
import {GedcomData, normalizeGedcom, TopolaData} from '../util/gedcom_util';
|
||||||
import {GedcomEntry} from 'parse-gedcom';
|
import {GedcomEntry} from 'parse-gedcom';
|
||||||
import {InjectedIntl} from 'react-intl';
|
import {IntlShape} from 'react-intl';
|
||||||
import {TopolaError} from '../util/error';
|
import {TopolaError} from '../util/error';
|
||||||
|
|
||||||
/** Prefix for IDs of private individuals. */
|
/** Prefix for IDs of private individuals. */
|
||||||
@@ -223,7 +223,7 @@ export function getLoggedInUserName(): string | undefined {
|
|||||||
*/
|
*/
|
||||||
export async function loadWikiTree(
|
export async function loadWikiTree(
|
||||||
key: string,
|
key: string,
|
||||||
intl: InjectedIntl,
|
intl: IntlShape,
|
||||||
authcode?: string,
|
authcode?: string,
|
||||||
): Promise<TopolaData> {
|
): Promise<TopolaData> {
|
||||||
// Work around CORS if not in apps.wikitree.com domain.
|
// Work around CORS if not in apps.wikitree.com domain.
|
||||||
@@ -419,7 +419,7 @@ function getFamilyId(spouse1: number, spouse2: number) {
|
|||||||
return `${spouse2}_${spouse1}`;
|
return `${spouse2}_${spouse1}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertPerson(person: Person, intl: InjectedIntl): JsonIndi {
|
function convertPerson(person: Person, intl: IntlShape): JsonIndi {
|
||||||
const indi: JsonIndi = {
|
const indi: JsonIndi = {
|
||||||
id: person.Name,
|
id: person.Name,
|
||||||
};
|
};
|
||||||
@@ -578,7 +578,7 @@ export interface WikiTreeSourceSpec {
|
|||||||
|
|
||||||
/** Loading data from the WikiTree API. */
|
/** Loading data from the WikiTree API. */
|
||||||
export class WikiTreeDataSource implements DataSource<WikiTreeSourceSpec> {
|
export class WikiTreeDataSource implements DataSource<WikiTreeSourceSpec> {
|
||||||
constructor(private intl: InjectedIntl) {}
|
constructor(private intl: IntlShape) {}
|
||||||
|
|
||||||
isNewData(
|
isNewData(
|
||||||
newSource: SourceSelection<WikiTreeSourceSpec>,
|
newSource: SourceSelection<WikiTreeSourceSpec>,
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import flatMap from 'array.prototype.flatmap';
|
import flatMap from 'array.prototype.flatmap';
|
||||||
import Linkify from 'react-linkify';
|
import Linkify from 'react-linkify';
|
||||||
import {FormattedMessage, InjectedIntl} from 'react-intl';
|
import {
|
||||||
|
FormattedMessage,
|
||||||
|
injectIntl,
|
||||||
|
IntlShape,
|
||||||
|
WrappedComponentProps,
|
||||||
|
} from 'react-intl';
|
||||||
import {GedcomData, pointerToId} from './util/gedcom_util';
|
import {GedcomData, pointerToId} from './util/gedcom_util';
|
||||||
import {GedcomEntry} from 'parse-gedcom';
|
import {GedcomEntry} from 'parse-gedcom';
|
||||||
import {intlShape} from 'react-intl';
|
|
||||||
import {translateDate} from './util/date_util';
|
import {translateDate} from './util/date_util';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -76,7 +80,7 @@ function getData(entry: GedcomEntry) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventDetails(entry: GedcomEntry, intl: InjectedIntl) {
|
function eventDetails(entry: GedcomEntry, intl: IntlShape) {
|
||||||
const lines = [];
|
const lines = [];
|
||||||
if (entry.data && entry.data.length > 1) {
|
if (entry.data && entry.data.length > 1) {
|
||||||
lines.push(<i>{entry.data}</i>);
|
lines.push(<i>{entry.data}</i>);
|
||||||
@@ -205,12 +209,10 @@ function dereference(entry: GedcomEntry, gedcom: GedcomData) {
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Details extends React.Component<Props, {}> {
|
class DetailsComponent extends React.Component<
|
||||||
/** Make intl appear in this.context. */
|
Props & WrappedComponentProps,
|
||||||
static contextTypes = {
|
{}
|
||||||
intl: intlShape,
|
> {
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const entries = this.props.gedcom.indis[this.props.indi].tree;
|
const entries = this.props.gedcom.indis[this.props.indi].tree;
|
||||||
const entriesWithData = entries
|
const entriesWithData = entries
|
||||||
@@ -221,7 +223,7 @@ export class Details extends React.Component<Props, {}> {
|
|||||||
<div className="ui segments" id="details">
|
<div className="ui segments" id="details">
|
||||||
{getDetails(entries, ['NAME'], nameDetails)}
|
{getDetails(entries, ['NAME'], nameDetails)}
|
||||||
{getDetails(entries, EVENT_TAGS, (entry) =>
|
{getDetails(entries, EVENT_TAGS, (entry) =>
|
||||||
eventDetails(entry, this.context.intl as InjectedIntl),
|
eventDetails(entry, this.props.intl),
|
||||||
)}
|
)}
|
||||||
{getOtherDetails(entriesWithData)}
|
{getOtherDetails(entriesWithData)}
|
||||||
{getDetails(entriesWithData, ['NOTE'], noteDetails)}
|
{getDetails(entriesWithData, ['NOTE'], noteDetails)}
|
||||||
@@ -229,3 +231,4 @@ export class Details extends React.Component<Props, {}> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const Details = injectIntl(DetailsComponent);
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
import * as locale_de from 'react-intl/locale-data/de';
|
|
||||||
import * as locale_en from 'react-intl/locale-data/en';
|
|
||||||
import * as locale_fr from 'react-intl/locale-data/fr';
|
|
||||||
import * as locale_it from 'react-intl/locale-data/it';
|
|
||||||
import * as locale_pl from 'react-intl/locale-data/pl';
|
|
||||||
import * as locale_ru from 'react-intl/locale-data/ru';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
import messages_de from './translations/de.json';
|
import messages_de from './translations/de.json';
|
||||||
@@ -11,7 +5,6 @@ import messages_fr from './translations/fr.json';
|
|||||||
import messages_it from './translations/it.json';
|
import messages_it from './translations/it.json';
|
||||||
import messages_pl from './translations/pl.json';
|
import messages_pl from './translations/pl.json';
|
||||||
import messages_ru from './translations/ru.json';
|
import messages_ru from './translations/ru.json';
|
||||||
import {addLocaleData} from 'react-intl';
|
|
||||||
import {App} from './app';
|
import {App} from './app';
|
||||||
import {detect} from 'detect-browser';
|
import {detect} from 'detect-browser';
|
||||||
import {HashRouter as Router, Route} from 'react-router-dom';
|
import {HashRouter as Router, Route} from 'react-router-dom';
|
||||||
@@ -20,15 +13,6 @@ import './index.css';
|
|||||||
import 'semantic-ui-css/semantic.min.css';
|
import 'semantic-ui-css/semantic.min.css';
|
||||||
import 'canvas-toBlob';
|
import 'canvas-toBlob';
|
||||||
|
|
||||||
addLocaleData([
|
|
||||||
...locale_de,
|
|
||||||
...locale_en,
|
|
||||||
...locale_fr,
|
|
||||||
...locale_it,
|
|
||||||
...locale_pl,
|
|
||||||
...locale_ru,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
de: messages_de,
|
de: messages_de,
|
||||||
fr: messages_fr,
|
fr: messages_fr,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {analyticsEvent} from '../util/analytics';
|
|||||||
import {buildSearchIndex, SearchIndex, SearchResult} from './search_index';
|
import {buildSearchIndex, SearchIndex, SearchResult} from './search_index';
|
||||||
import {formatDateOrRange} from '../util/date_util';
|
import {formatDateOrRange} from '../util/date_util';
|
||||||
import {IndiInfo, JsonGedcomData} from 'topola';
|
import {IndiInfo, JsonGedcomData} from 'topola';
|
||||||
import {intlShape} from 'react-intl';
|
import {injectIntl, WrappedComponentProps} from 'react-intl';
|
||||||
import {JsonIndi} from 'topola';
|
import {JsonIndi} from 'topola';
|
||||||
import {RouteComponentProps} from 'react-router-dom';
|
import {RouteComponentProps} from 'react-router-dom';
|
||||||
import {Search, SearchProps, SearchResultProps} from 'semantic-ui-react';
|
import {Search, SearchProps, SearchResultProps} from 'semantic-ui-react';
|
||||||
@@ -32,24 +32,20 @@ interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Displays and handles the search box in the top bar. */
|
/** Displays and handles the search box in the top bar. */
|
||||||
export class SearchBar extends React.Component<
|
class SearchBarComponent extends React.Component<
|
||||||
RouteComponentProps & Props,
|
RouteComponentProps & WrappedComponentProps & Props,
|
||||||
State
|
State
|
||||||
> {
|
> {
|
||||||
state: State = {
|
state: State = {
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
};
|
};
|
||||||
/** Make intl appear in this.context. */
|
|
||||||
static contextTypes = {
|
|
||||||
intl: intlShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
searchRef?: {setValue(value: string): void};
|
searchRef?: {setValue(value: string): void};
|
||||||
searchIndex?: SearchIndex;
|
searchIndex?: SearchIndex;
|
||||||
|
|
||||||
private getDescriptionLine(indi: JsonIndi) {
|
private getDescriptionLine(indi: JsonIndi) {
|
||||||
const birthDate = formatDateOrRange(indi.birth, this.context.intl);
|
const birthDate = formatDateOrRange(indi.birth, this.props.intl);
|
||||||
const deathDate = formatDateOrRange(indi.death, this.context.intl);
|
const deathDate = formatDateOrRange(indi.death, this.props.intl);
|
||||||
if (!deathDate) {
|
if (!deathDate) {
|
||||||
return birthDate;
|
return birthDate;
|
||||||
}
|
}
|
||||||
@@ -108,11 +104,11 @@ export class SearchBar extends React.Component<
|
|||||||
)}
|
)}
|
||||||
onResultSelect={(_, data) => this.handleResultSelect(data.result.id)}
|
onResultSelect={(_, data) => this.handleResultSelect(data.result.id)}
|
||||||
results={this.state.searchResults}
|
results={this.state.searchResults}
|
||||||
noResultsMessage={this.context.intl.formatMessage({
|
noResultsMessage={this.props.intl.formatMessage({
|
||||||
id: 'menu.search.no_results',
|
id: 'menu.search.no_results',
|
||||||
defaultMessage: 'No results found',
|
defaultMessage: 'No results found',
|
||||||
})}
|
})}
|
||||||
placeholder={this.context.intl.formatMessage({
|
placeholder={this.props.intl.formatMessage({
|
||||||
id: 'menu.search.placeholder',
|
id: 'menu.search.placeholder',
|
||||||
defaultMessage: 'Search for people',
|
defaultMessage: 'Search for people',
|
||||||
})}
|
})}
|
||||||
@@ -127,3 +123,4 @@ export class SearchBar extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const SearchBar = injectIntl(SearchBarComponent);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class LunrSearchIndex implements SearchIndex {
|
|||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
const self = this;
|
const self = this;
|
||||||
this.index = lunr(function() {
|
this.index = lunr(function () {
|
||||||
this.use((lunr as any).multiLanguage('de', 'en', 'fr', 'it', 'ru'));
|
this.use((lunr as any).multiLanguage('de', 'en', 'fr', 'it', 'ru'));
|
||||||
this.ref('id');
|
this.ref('id');
|
||||||
this.field('id');
|
this.field('id');
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ export class UrlMenu extends React.Component<
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="load_from_url.title"
|
id="load_from_url.title"
|
||||||
defaultMessage="Load from URL"
|
defaultMessage="Load from URL"
|
||||||
children={(txt) => txt}
|
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as queryString from 'query-string';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import wikitreeLogo from './wikitree.png';
|
import wikitreeLogo from './wikitree.png';
|
||||||
import {analyticsEvent} from '../util/analytics';
|
import {analyticsEvent} from '../util/analytics';
|
||||||
import {FormattedMessage, intlShape} from 'react-intl';
|
import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';
|
||||||
import {getLoggedInUserName} from '../datasource/wikitree';
|
import {getLoggedInUserName} from '../datasource/wikitree';
|
||||||
import {MenuItem, MenuType} from './menu_item';
|
import {MenuItem, MenuType} from './menu_item';
|
||||||
import {RouteComponentProps} from 'react-router-dom';
|
import {RouteComponentProps} from 'react-router-dom';
|
||||||
@@ -106,7 +106,6 @@ export class WikiTreeMenu extends React.Component<
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="select_wikitree_id.title"
|
id="select_wikitree_id.title"
|
||||||
defaultMessage="Select WikiTree ID"
|
defaultMessage="Select WikiTree ID"
|
||||||
children={(txt) => txt}
|
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
@@ -196,17 +195,13 @@ interface LoginState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Displays and handles the "Log in to WikiTree" menu. */
|
/** Displays and handles the "Log in to WikiTree" menu. */
|
||||||
export class WikiTreeLoginMenu extends React.Component<
|
class WikiTreeLoginMenuComponent extends React.Component<
|
||||||
RouteComponentProps & Props,
|
RouteComponentProps & WrappedComponentProps & Props,
|
||||||
LoginState
|
LoginState
|
||||||
> {
|
> {
|
||||||
state: LoginState = {
|
state: LoginState = {
|
||||||
wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,
|
wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,
|
||||||
};
|
};
|
||||||
/** Make intl appear in this.context. */
|
|
||||||
static contextTypes = {
|
|
||||||
intl: intlShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
wikiTreeLoginFormRef: React.RefObject<HTMLFormElement> = React.createRef();
|
wikiTreeLoginFormRef: React.RefObject<HTMLFormElement> = React.createRef();
|
||||||
wikiTreeReturnUrlRef: React.RefObject<HTMLInputElement> = React.createRef();
|
wikiTreeReturnUrlRef: React.RefObject<HTMLInputElement> = React.createRef();
|
||||||
@@ -285,14 +280,14 @@ export class WikiTreeLoginMenu extends React.Component<
|
|||||||
|
|
||||||
case WikiTreeLoginState.LOGGED_IN:
|
case WikiTreeLoginState.LOGGED_IN:
|
||||||
const tooltip = this.state.wikiTreeLoginUsername
|
const tooltip = this.state.wikiTreeLoginUsername
|
||||||
? this.context.intl.formatMessage(
|
? this.props.intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: 'menu.wikitree_popup_username',
|
id: 'menu.wikitree_popup_username',
|
||||||
defaultMessage: 'Logged in to WikiTree as {username}',
|
defaultMessage: 'Logged in to WikiTree as {username}',
|
||||||
},
|
},
|
||||||
{username: this.state.wikiTreeLoginUsername},
|
{username: this.state.wikiTreeLoginUsername},
|
||||||
)
|
)
|
||||||
: this.context.intl.formatMessage({
|
: this.props.intl.formatMessage({
|
||||||
id: 'menu.wikitree_popup',
|
id: 'menu.wikitree_popup',
|
||||||
defaultMessage: 'Logged in to WikiTree',
|
defaultMessage: 'Logged in to WikiTree',
|
||||||
});
|
});
|
||||||
@@ -309,3 +304,4 @@ export class WikiTreeLoginMenu extends React.Component<
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const WikiTreeLoginMenu = injectIntl(WikiTreeLoginMenuComponent);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Date as TopolaDate, DateOrRange, DateRange, getDate} from 'topola';
|
import {Date as TopolaDate, DateOrRange, DateRange, getDate} from 'topola';
|
||||||
import {InjectedIntl} from 'react-intl';
|
import {IntlShape} from 'react-intl';
|
||||||
|
|
||||||
const DATE_QUALIFIERS = new Map([
|
const DATE_QUALIFIERS = new Map([
|
||||||
['abt', 'about'],
|
['abt', 'about'],
|
||||||
@@ -7,7 +7,7 @@ const DATE_QUALIFIERS = new Map([
|
|||||||
['est', 'estimated'],
|
['est', 'estimated'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function formatDate(date: TopolaDate, intl: InjectedIntl) {
|
function formatDate(date: TopolaDate, intl: IntlShape) {
|
||||||
const hasDay = date.day !== undefined;
|
const hasDay = date.day !== undefined;
|
||||||
const hasMonth = date.month !== undefined;
|
const hasMonth = date.month !== undefined;
|
||||||
const hasYear = date.year !== undefined;
|
const hasYear = date.year !== undefined;
|
||||||
@@ -41,7 +41,7 @@ function formatDate(date: TopolaDate, intl: InjectedIntl) {
|
|||||||
return [translatedQualifier, translatedDate].join(' ');
|
return [translatedQualifier, translatedDate].join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDateRage(dateRange: DateRange, intl: InjectedIntl) {
|
function formatDateRage(dateRange: DateRange, intl: IntlShape) {
|
||||||
const fromDate = dateRange.from;
|
const fromDate = dateRange.from;
|
||||||
const toDate = dateRange.to;
|
const toDate = dateRange.to;
|
||||||
const translatedFromDate = fromDate && formatDate(fromDate, intl);
|
const translatedFromDate = fromDate && formatDate(fromDate, intl);
|
||||||
@@ -79,7 +79,7 @@ function formatDateRage(dateRange: DateRange, intl: InjectedIntl) {
|
|||||||
/** Formats a DateOrRange object. */
|
/** Formats a DateOrRange object. */
|
||||||
export function formatDateOrRange(
|
export function formatDateOrRange(
|
||||||
dateOrRange: DateOrRange | undefined,
|
dateOrRange: DateOrRange | undefined,
|
||||||
intl: InjectedIntl,
|
intl: IntlShape,
|
||||||
): string {
|
): string {
|
||||||
if (!dateOrRange) {
|
if (!dateOrRange) {
|
||||||
return '';
|
return '';
|
||||||
@@ -94,6 +94,6 @@ export function formatDateOrRange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a date given in GEDCOM format. */
|
/** Formats a date given in GEDCOM format. */
|
||||||
export function translateDate(gedcomDate: string, intl: InjectedIntl): string {
|
export function translateDate(gedcomDate: string, intl: IntlShape): string {
|
||||||
return formatDateOrRange(getDate(gedcomDate), intl);
|
return formatDateOrRange(getDate(gedcomDate), intl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {InjectedIntl} from 'react-intl';
|
import {IntlShape} from 'react-intl';
|
||||||
import {TopolaError} from './error';
|
import {TopolaError} from './error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a translated message for the given error. If the message can't be
|
* Returns a translated message for the given error. If the message can't be
|
||||||
* translated, the original error.message is returned.
|
* translated, the original error.message is returned.
|
||||||
*/
|
*/
|
||||||
export function getI18nMessage(error: Error, intl: InjectedIntl): string {
|
export function getI18nMessage(error: Error, intl: IntlShape): string {
|
||||||
if (!(error instanceof TopolaError)) {
|
if (!(error instanceof TopolaError)) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user