mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-08-02 09:01:48 +00:00
Refactored AppComponent from class-based to functional
This commit is contained in:
462
src/app.tsx
462
src/app.tsx
@@ -1,6 +1,5 @@
|
|||||||
import * as H from 'history';
|
import * as H from 'history';
|
||||||
import * as queryString from 'query-string';
|
import * as queryString from 'query-string';
|
||||||
import React from 'react';
|
|
||||||
import {analyticsEvent} from './util/analytics';
|
import {analyticsEvent} from './util/analytics';
|
||||||
import {Changelog} from './changelog';
|
import {Changelog} from './changelog';
|
||||||
import {DataSourceEnum, SourceSelection} from './datasource/data_source';
|
import {DataSourceEnum, SourceSelection} from './datasource/data_source';
|
||||||
@@ -15,6 +14,7 @@ import {Media} from './util/media';
|
|||||||
import {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';
|
import {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';
|
||||||
import {TopBar} from './menu/top_bar';
|
import {TopBar} from './menu/top_bar';
|
||||||
import {TopolaData} from './util/gedcom_util';
|
import {TopolaData} from './util/gedcom_util';
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
import {
|
import {
|
||||||
Chart,
|
Chart,
|
||||||
ChartType,
|
ChartType,
|
||||||
@@ -184,270 +184,233 @@ function hasUpdatedValues<T>(state: T, changes: Partial<T> | undefined) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
function AppComponent(props: RouteComponentProps & WrappedComponentProps) {
|
||||||
/** State of the application. */
|
/** State of the application. */
|
||||||
state: AppState;
|
const [state, setState] = useState<AppState>(AppState.INITIAL);
|
||||||
/** Loaded data. */
|
/** Loaded data. */
|
||||||
data?: TopolaData;
|
const [data, setData] = useState<TopolaData>();
|
||||||
/** Selected individual. */
|
/** Selected individual. */
|
||||||
selection?: IndiInfo;
|
const [selection, setSelection] = useState<IndiInfo>();
|
||||||
/** Error to display. */
|
/** Error to display. */
|
||||||
error?: string;
|
const [error, setError] = useState<string>();
|
||||||
/** Whether the side panel is shown. */
|
/** Whether the side panel is shown. */
|
||||||
showSidePanel?: boolean;
|
const [showSidePanel, setShowSidePanel] = useState(false);
|
||||||
/** Whether the app is in standalone mode, i.e. showing 'open file' menus. */
|
/** Whether the app is in standalone mode, i.e. showing 'open file' menus. */
|
||||||
standalone: boolean;
|
const [standalone, setStandalone] = useState(true);
|
||||||
/** Type of displayed chart. */
|
/** Type of displayed chart. */
|
||||||
chartType: ChartType;
|
const [chartType, setChartType] = useState<ChartType>(ChartType.Hourglass);
|
||||||
/** Whether to show the error popup. */
|
/** Whether to show the error popup. */
|
||||||
showErrorPopup: boolean;
|
const [showErrorPopup, setShowErrorPopup] = useState(false);
|
||||||
/** Specification of the source of the data. */
|
/** Specification of the source of the data. */
|
||||||
sourceSpec?: DataSourceSpec;
|
const [sourceSpec, setSourceSpec] = useState<DataSourceSpec>();
|
||||||
/** Freeze animations after initial chart render. */
|
/** Freeze animations after initial chart render. */
|
||||||
freezeAnimation?: boolean;
|
const [freezeAnimation, setFreezeAnimation] = useState(false);
|
||||||
config: Config;
|
const [config, setConfig] = useState(DEFALUT_CONFIG);
|
||||||
}
|
|
||||||
|
|
||||||
class AppComponent extends React.Component<
|
|
||||||
RouteComponentProps & WrappedComponentProps,
|
|
||||||
{}
|
|
||||||
> {
|
|
||||||
state: State = {
|
|
||||||
state: AppState.INITIAL,
|
|
||||||
standalone: true,
|
|
||||||
chartType: ChartType.Hourglass,
|
|
||||||
showErrorPopup: false,
|
|
||||||
config: DEFALUT_CONFIG,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Sets the state with a new individual selection and chart type. */
|
/** Sets the state with a new individual selection and chart type. */
|
||||||
private updateDisplay(
|
function updateDisplay(newSelection: IndiInfo) {
|
||||||
selection: IndiInfo,
|
|
||||||
otherStateChanges?: Partial<State>,
|
|
||||||
) {
|
|
||||||
if (
|
if (
|
||||||
!this.state.selection ||
|
!selection ||
|
||||||
this.state.selection.id !== selection.id ||
|
selection.id !== newSelection.id ||
|
||||||
this.state.selection!.generation !== selection.generation ||
|
selection!.generation !== newSelection.generation
|
||||||
hasUpdatedValues(this.state, otherStateChanges)
|
|
||||||
) {
|
) {
|
||||||
this.setState(
|
setSelection(newSelection);
|
||||||
Object.assign({}, this.state, {selection}, otherStateChanges),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets error message after data load failure. */
|
/** Sets error message after data load failure. */
|
||||||
private setError(error: string) {
|
function setErrorMessage(message: string) {
|
||||||
this.setState(
|
setError(message);
|
||||||
Object.assign({}, this.state, {
|
setState(AppState.ERROR);
|
||||||
state: AppState.ERROR,
|
|
||||||
error,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
const uploadedDataSource = new UploadedDataSource();
|
||||||
this.componentDidUpdate();
|
const gedcomUrlDataSource = new GedcomUrlDataSource();
|
||||||
}
|
const wikiTreeDataSource = new WikiTreeDataSource(props.intl);
|
||||||
|
const embeddedDataSource = new EmbeddedDataSource();
|
||||||
|
|
||||||
private readonly uploadedDataSource = new UploadedDataSource();
|
function isNewData(newSourceSpec: DataSourceSpec, newSelection?: IndiInfo) {
|
||||||
private readonly gedcomUrlDataSource = new GedcomUrlDataSource();
|
if (!sourceSpec || sourceSpec.source !== newSourceSpec.source) {
|
||||||
private readonly wikiTreeDataSource = new WikiTreeDataSource(this.props.intl);
|
|
||||||
private readonly embeddedDataSource = new EmbeddedDataSource();
|
|
||||||
|
|
||||||
private isNewData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {
|
|
||||||
if (
|
|
||||||
!this.state.sourceSpec ||
|
|
||||||
this.state.sourceSpec.source !== sourceSpec.source
|
|
||||||
) {
|
|
||||||
// New data source means new data.
|
// New data source means new data.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const newSource = {spec: sourceSpec, selection};
|
const newSource = {spec: newSourceSpec, selection: newSelection};
|
||||||
const oldSouce = {
|
const oldSouce = {
|
||||||
spec: this.state.sourceSpec,
|
spec: sourceSpec,
|
||||||
selection: this.state.selection,
|
selection: selection,
|
||||||
};
|
};
|
||||||
switch (newSource.spec.source) {
|
switch (newSource.spec.source) {
|
||||||
case DataSourceEnum.UPLOADED:
|
case DataSourceEnum.UPLOADED:
|
||||||
return this.uploadedDataSource.isNewData(
|
return uploadedDataSource.isNewData(
|
||||||
newSource as SourceSelection<UploadSourceSpec>,
|
newSource as SourceSelection<UploadSourceSpec>,
|
||||||
oldSouce as SourceSelection<UploadSourceSpec>,
|
oldSouce as SourceSelection<UploadSourceSpec>,
|
||||||
this.state.data,
|
data,
|
||||||
);
|
);
|
||||||
case DataSourceEnum.GEDCOM_URL:
|
case DataSourceEnum.GEDCOM_URL:
|
||||||
return this.gedcomUrlDataSource.isNewData(
|
return gedcomUrlDataSource.isNewData(
|
||||||
newSource as SourceSelection<UrlSourceSpec>,
|
newSource as SourceSelection<UrlSourceSpec>,
|
||||||
oldSouce as SourceSelection<UrlSourceSpec>,
|
oldSouce as SourceSelection<UrlSourceSpec>,
|
||||||
this.state.data,
|
data,
|
||||||
);
|
);
|
||||||
case DataSourceEnum.WIKITREE:
|
case DataSourceEnum.WIKITREE:
|
||||||
return this.wikiTreeDataSource.isNewData(
|
return wikiTreeDataSource.isNewData(
|
||||||
newSource as SourceSelection<WikiTreeSourceSpec>,
|
newSource as SourceSelection<WikiTreeSourceSpec>,
|
||||||
oldSouce as SourceSelection<WikiTreeSourceSpec>,
|
oldSouce as SourceSelection<WikiTreeSourceSpec>,
|
||||||
this.state.data,
|
data,
|
||||||
);
|
);
|
||||||
case DataSourceEnum.EMBEDDED:
|
case DataSourceEnum.EMBEDDED:
|
||||||
return this.embeddedDataSource.isNewData(
|
return embeddedDataSource.isNewData(
|
||||||
newSource as SourceSelection<EmbeddedSourceSpec>,
|
newSource as SourceSelection<EmbeddedSourceSpec>,
|
||||||
oldSouce as SourceSelection<EmbeddedSourceSpec>,
|
oldSouce as SourceSelection<EmbeddedSourceSpec>,
|
||||||
this.state.data,
|
data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {
|
function loadData(newSourceSpec: DataSourceSpec, newSelection?: IndiInfo) {
|
||||||
switch (sourceSpec.source) {
|
switch (newSourceSpec.source) {
|
||||||
case DataSourceEnum.UPLOADED:
|
case DataSourceEnum.UPLOADED:
|
||||||
return this.uploadedDataSource.loadData({spec: sourceSpec, selection});
|
return uploadedDataSource.loadData({
|
||||||
|
spec: newSourceSpec,
|
||||||
|
selection: newSelection,
|
||||||
|
});
|
||||||
case DataSourceEnum.GEDCOM_URL:
|
case DataSourceEnum.GEDCOM_URL:
|
||||||
return this.gedcomUrlDataSource.loadData({spec: sourceSpec, selection});
|
return gedcomUrlDataSource.loadData({
|
||||||
|
spec: newSourceSpec,
|
||||||
|
selection: newSelection,
|
||||||
|
});
|
||||||
case DataSourceEnum.WIKITREE:
|
case DataSourceEnum.WIKITREE:
|
||||||
return this.wikiTreeDataSource.loadData({spec: sourceSpec, selection});
|
return wikiTreeDataSource.loadData({
|
||||||
|
spec: newSourceSpec,
|
||||||
|
selection: newSelection,
|
||||||
|
});
|
||||||
case DataSourceEnum.EMBEDDED:
|
case DataSourceEnum.EMBEDDED:
|
||||||
return this.embeddedDataSource.loadData({spec: sourceSpec, selection});
|
return embeddedDataSource.loadData({
|
||||||
|
spec: newSourceSpec,
|
||||||
|
selection: newSelection,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidUpdate() {
|
useEffect(() => {
|
||||||
if (this.props.location.pathname !== '/view') {
|
(async () => {
|
||||||
if (this.state.state !== AppState.INITIAL) {
|
if (props.location.pathname !== '/view') {
|
||||||
this.setState(Object.assign({}, this.state, {state: AppState.INITIAL}));
|
if (state !== AppState.INITIAL) {
|
||||||
|
setState(AppState.INITIAL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = getArguments(this.props.location);
|
const args = getArguments(props.location);
|
||||||
|
|
||||||
if (!args.sourceSpec) {
|
if (!args.sourceSpec) {
|
||||||
this.props.history.replace({pathname: '/'});
|
props.history.replace({pathname: '/'});
|
||||||
} else if (
|
return;
|
||||||
this.state.state === AppState.INITIAL ||
|
|
||||||
this.isNewData(args.sourceSpec, args.selection)
|
|
||||||
) {
|
|
||||||
// Set loading state.
|
|
||||||
this.setState(
|
|
||||||
Object.assign({}, this.state, {
|
|
||||||
state: AppState.LOADING,
|
|
||||||
sourceSpec: args.sourceSpec,
|
|
||||||
selection: args.selection,
|
|
||||||
standalone: args.standalone,
|
|
||||||
chartType: args.chartType,
|
|
||||||
config: args.config,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const data = await this.loadData(args.sourceSpec, args.selection);
|
|
||||||
// Set state with data.
|
|
||||||
this.setState(
|
|
||||||
Object.assign({}, this.state, {
|
|
||||||
state: AppState.SHOWING_CHART,
|
|
||||||
data,
|
|
||||||
selection: getSelection(data.chartData, args.selection),
|
|
||||||
showSidePanel: args.showSidePanel,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
this.setError(getI18nMessage(error, this.props.intl));
|
|
||||||
}
|
}
|
||||||
} else if (
|
|
||||||
this.state.state === AppState.SHOWING_CHART ||
|
if (
|
||||||
this.state.state === AppState.LOADING_MORE
|
state === AppState.INITIAL ||
|
||||||
) {
|
isNewData(args.sourceSpec, args.selection)
|
||||||
// Update selection if it has changed in the URL.
|
) {
|
||||||
const selection = getSelection(
|
// Set loading state.
|
||||||
this.state.data!.chartData,
|
setState(AppState.LOADING);
|
||||||
args.selection,
|
// Set state from URL parameters.
|
||||||
);
|
setSourceSpec(args.sourceSpec);
|
||||||
const loadMoreFromWikitree =
|
setSelection(args.selection);
|
||||||
args.sourceSpec.source === DataSourceEnum.WIKITREE &&
|
setStandalone(args.standalone);
|
||||||
(!this.state.selection || this.state.selection.id !== selection.id);
|
setChartType(args.chartType);
|
||||||
this.updateDisplay(selection, {
|
setConfig(args.config);
|
||||||
chartType: args.chartType,
|
|
||||||
state: loadMoreFromWikitree
|
|
||||||
? AppState.LOADING_MORE
|
|
||||||
: AppState.SHOWING_CHART,
|
|
||||||
});
|
|
||||||
if (loadMoreFromWikitree) {
|
|
||||||
try {
|
try {
|
||||||
const data = await loadWikiTree(args.selection!.id, this.props.intl);
|
const data = await loadData(args.sourceSpec, args.selection);
|
||||||
const selection = getSelection(data.chartData, args.selection);
|
// Set state with data.
|
||||||
this.setState(
|
setData(data);
|
||||||
Object.assign({}, this.state, {
|
setSelection(getSelection(data.chartData, args.selection));
|
||||||
state: AppState.SHOWING_CHART,
|
setShowSidePanel(args.showSidePanel);
|
||||||
data,
|
setState(AppState.SHOWING_CHART);
|
||||||
selection,
|
} catch (error: any) {
|
||||||
}),
|
setErrorMessage(getI18nMessage(error, props.intl));
|
||||||
);
|
}
|
||||||
} catch (error) {
|
} else if (
|
||||||
this.showErrorPopup(
|
state === AppState.SHOWING_CHART ||
|
||||||
this.props.intl.formatMessage(
|
state === AppState.LOADING_MORE
|
||||||
{
|
) {
|
||||||
id: 'error.failed_wikitree_load_more',
|
// Update selection if it has changed in the URL.
|
||||||
defaultMessage: 'Failed to load data from WikiTree. {error}',
|
const newSelection = getSelection(data!.chartData, args.selection);
|
||||||
},
|
const loadMoreFromWikitree =
|
||||||
{error},
|
args.sourceSpec.source === DataSourceEnum.WIKITREE &&
|
||||||
),
|
(!selection || selection.id !== newSelection.id);
|
||||||
{state: AppState.SHOWING_CHART},
|
setChartType(args.chartType);
|
||||||
);
|
setState(
|
||||||
|
loadMoreFromWikitree ? AppState.LOADING_MORE : AppState.SHOWING_CHART,
|
||||||
|
);
|
||||||
|
updateDisplay(newSelection);
|
||||||
|
if (loadMoreFromWikitree) {
|
||||||
|
try {
|
||||||
|
const data = await loadWikiTree(args.selection!.id, props.intl);
|
||||||
|
const newSelection = getSelection(data.chartData, args.selection);
|
||||||
|
setData(data);
|
||||||
|
setSelection(newSelection);
|
||||||
|
setState(AppState.SHOWING_CHART);
|
||||||
|
} catch (error: any) {
|
||||||
|
setState(AppState.SHOWING_CHART);
|
||||||
|
displayErrorPopup(
|
||||||
|
props.intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'error.failed_wikitree_load_more',
|
||||||
|
defaultMessage: 'Failed to load data from WikiTree. {error}',
|
||||||
|
},
|
||||||
|
{error},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})();
|
||||||
}
|
});
|
||||||
|
|
||||||
private updateUrl(args: queryString.ParsedQuery<any>) {
|
function updateUrl(args: queryString.ParsedQuery<any>) {
|
||||||
const location = this.props.location;
|
const location = props.location;
|
||||||
const search = queryString.parse(location.search);
|
const search = queryString.parse(location.search);
|
||||||
for (const key in args) {
|
for (const key in args) {
|
||||||
search[key] = args[key];
|
search[key] = args[key];
|
||||||
}
|
}
|
||||||
location.search = queryString.stringify(search);
|
location.search = queryString.stringify(search);
|
||||||
this.props.history.push(location);
|
props.history.push(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user clicks an individual box in the chart.
|
* Called when the user clicks an individual box in the chart.
|
||||||
* Updates the browser URL.
|
* Updates the browser URL.
|
||||||
*/
|
*/
|
||||||
private onSelection = (selection: IndiInfo) => {
|
function onSelection(selection: IndiInfo) {
|
||||||
// Don't allow selecting WikiTree private profiles.
|
// Don't allow selecting WikiTree private profiles.
|
||||||
if (selection.id.startsWith(PRIVATE_ID_PREFIX)) {
|
if (selection.id.startsWith(PRIVATE_ID_PREFIX)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
analyticsEvent('selection_changed');
|
analyticsEvent('selection_changed');
|
||||||
this.updateUrl({
|
updateUrl({
|
||||||
indi: selection.id,
|
indi: selection.id,
|
||||||
gen: selection.generation,
|
gen: selection.generation,
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
private onPrint = () => {
|
|
||||||
analyticsEvent('print');
|
|
||||||
printChart();
|
|
||||||
};
|
|
||||||
|
|
||||||
private showErrorPopup(message: string, otherStateChanges?: Partial<State>) {
|
|
||||||
this.setState(
|
|
||||||
Object.assign(
|
|
||||||
{},
|
|
||||||
this.state,
|
|
||||||
{
|
|
||||||
showErrorPopup: true,
|
|
||||||
error: message,
|
|
||||||
},
|
|
||||||
otherStateChanges,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDownloadPdf = async () => {
|
function onPrint() {
|
||||||
|
analyticsEvent('print');
|
||||||
|
printChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayErrorPopup(message: string) {
|
||||||
|
setShowErrorPopup(true);
|
||||||
|
setError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDownloadPdf() {
|
||||||
analyticsEvent('download_pdf');
|
analyticsEvent('download_pdf');
|
||||||
try {
|
try {
|
||||||
await downloadPdf();
|
await downloadPdf();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showErrorPopup(
|
displayErrorPopup(
|
||||||
this.props.intl.formatMessage({
|
props.intl.formatMessage({
|
||||||
id: 'error.failed_pdf',
|
id: 'error.failed_pdf',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Failed to generate PDF file.' +
|
'Failed to generate PDF file.' +
|
||||||
@@ -455,15 +418,15 @@ class AppComponent extends React.Component<
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
private onDownloadPng = async () => {
|
async function onDownloadPng() {
|
||||||
analyticsEvent('download_png');
|
analyticsEvent('download_png');
|
||||||
try {
|
try {
|
||||||
await downloadPng();
|
await downloadPng();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showErrorPopup(
|
displayErrorPopup(
|
||||||
this.props.intl.formatMessage({
|
props.intl.formatMessage({
|
||||||
id: 'error.failed_png',
|
id: 'error.failed_png',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Failed to generate PNG file.' +
|
'Failed to generate PNG file.' +
|
||||||
@@ -471,49 +434,42 @@ class AppComponent extends React.Component<
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
private onDownloadSvg = () => {
|
function onDownloadSvg() {
|
||||||
analyticsEvent('download_svg');
|
analyticsEvent('download_svg');
|
||||||
downloadSvg();
|
downloadSvg();
|
||||||
};
|
}
|
||||||
|
|
||||||
private onDismissErrorPopup = () => {
|
function onDismissErrorPopup() {
|
||||||
this.setState(
|
setShowErrorPopup(false);
|
||||||
Object.assign({}, this.state, {
|
}
|
||||||
showErrorPopup: false,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderMainArea = () => {
|
function renderMainArea() {
|
||||||
switch (this.state.state) {
|
switch (state) {
|
||||||
case AppState.SHOWING_CHART:
|
case AppState.SHOWING_CHART:
|
||||||
case AppState.LOADING_MORE:
|
case AppState.LOADING_MORE:
|
||||||
const sidePanelTabs = [
|
const sidePanelTabs = [
|
||||||
{
|
{
|
||||||
menuItem: this.props.intl.formatMessage({
|
menuItem: props.intl.formatMessage({
|
||||||
id: 'tab.info',
|
id: 'tab.info',
|
||||||
defaultMessage: 'Info',
|
defaultMessage: 'Info',
|
||||||
}),
|
}),
|
||||||
render: () => (
|
render: () => (
|
||||||
<Details
|
<Details gedcom={data!.gedcom} indi={selection!.id} />
|
||||||
gedcom={this.state.data!.gedcom}
|
|
||||||
indi={this.state.selection!.id}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
menuItem: this.props.intl.formatMessage({
|
menuItem: props.intl.formatMessage({
|
||||||
id: 'tab.settings',
|
id: 'tab.settings',
|
||||||
defaultMessage: 'Settings',
|
defaultMessage: 'Settings',
|
||||||
}),
|
}),
|
||||||
render: () => (
|
render: () => (
|
||||||
<ConfigPanel
|
<ConfigPanel
|
||||||
config={this.state.config}
|
config={config}
|
||||||
onChange={(config) => {
|
onChange={(config) => {
|
||||||
this.setState(Object.assign({}, this.state, {config}));
|
setConfig(config);
|
||||||
this.updateUrl(configToArgs(config));
|
updateUrl(configToArgs(config));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -522,22 +478,22 @@ class AppComponent extends React.Component<
|
|||||||
return (
|
return (
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ErrorPopup
|
<ErrorPopup
|
||||||
open={this.state.showErrorPopup}
|
open={showErrorPopup}
|
||||||
message={this.state.error}
|
message={error}
|
||||||
onDismiss={this.onDismissErrorPopup}
|
onDismiss={onDismissErrorPopup}
|
||||||
/>
|
/>
|
||||||
{this.state.state === AppState.LOADING_MORE ? (
|
{state === AppState.LOADING_MORE ? (
|
||||||
<Loader active size="small" className="loading-more" />
|
<Loader active size="small" className="loading-more" />
|
||||||
) : null}
|
) : null}
|
||||||
<Chart
|
<Chart
|
||||||
data={this.state.data!.chartData}
|
data={data!.chartData}
|
||||||
selection={this.state.selection!}
|
selection={selection!}
|
||||||
chartType={this.state.chartType}
|
chartType={chartType}
|
||||||
onSelection={this.onSelection}
|
onSelection={onSelection}
|
||||||
freezeAnimation={this.state.freezeAnimation}
|
freezeAnimation={freezeAnimation}
|
||||||
colors={this.state.config.color}
|
colors={config.color}
|
||||||
/>
|
/>
|
||||||
{this.state.showSidePanel ? (
|
{showSidePanel ? (
|
||||||
<Media greaterThanOrEqual="large" className="sidePanel">
|
<Media greaterThanOrEqual="large" className="sidePanel">
|
||||||
<Tab panes={sidePanelTabs} />
|
<Tab panes={sidePanelTabs} />
|
||||||
</Media>
|
</Media>
|
||||||
@@ -547,52 +503,48 @@ class AppComponent extends React.Component<
|
|||||||
);
|
);
|
||||||
|
|
||||||
case AppState.ERROR:
|
case AppState.ERROR:
|
||||||
return <ErrorMessage message={this.state.error!} />;
|
return <ErrorMessage message={error!} />;
|
||||||
|
|
||||||
case AppState.INITIAL:
|
case AppState.INITIAL:
|
||||||
case AppState.LOADING:
|
case AppState.LOADING:
|
||||||
return <Loader active size="large" />;
|
return <Loader active size="large" />;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Route
|
|
||||||
render={(props: RouteComponentProps) => (
|
|
||||||
<TopBar
|
|
||||||
{...props}
|
|
||||||
data={this.state.data && this.state.data.chartData}
|
|
||||||
allowAllRelativesChart={
|
|
||||||
this.state.sourceSpec?.source !== DataSourceEnum.WIKITREE
|
|
||||||
}
|
|
||||||
showingChart={
|
|
||||||
this.props.history.location.pathname === '/view' &&
|
|
||||||
(this.state.state === AppState.SHOWING_CHART ||
|
|
||||||
this.state.state === AppState.LOADING_MORE)
|
|
||||||
}
|
|
||||||
standalone={this.state.standalone}
|
|
||||||
eventHandlers={{
|
|
||||||
onSelection: this.onSelection,
|
|
||||||
onPrint: this.onPrint,
|
|
||||||
onDownloadPdf: this.onDownloadPdf,
|
|
||||||
onDownloadPng: this.onDownloadPng,
|
|
||||||
onDownloadSvg: this.onDownloadSvg,
|
|
||||||
}}
|
|
||||||
showWikiTreeMenus={
|
|
||||||
this.state.sourceSpec?.source === DataSourceEnum.WIKITREE
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Switch>
|
|
||||||
<Route exact path="/" component={Intro} />
|
|
||||||
<Route exact path="/view" render={this.renderMainArea} />
|
|
||||||
<Redirect to={'/'} />
|
|
||||||
</Switch>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Route
|
||||||
|
render={(props: RouteComponentProps) => (
|
||||||
|
<TopBar
|
||||||
|
{...props}
|
||||||
|
data={data?.chartData}
|
||||||
|
allowAllRelativesChart={
|
||||||
|
sourceSpec?.source !== DataSourceEnum.WIKITREE
|
||||||
|
}
|
||||||
|
showingChart={
|
||||||
|
props.history.location.pathname === '/view' &&
|
||||||
|
(state === AppState.SHOWING_CHART ||
|
||||||
|
state === AppState.LOADING_MORE)
|
||||||
|
}
|
||||||
|
standalone={standalone}
|
||||||
|
eventHandlers={{
|
||||||
|
onSelection,
|
||||||
|
onPrint,
|
||||||
|
onDownloadPdf,
|
||||||
|
onDownloadPng,
|
||||||
|
onDownloadSvg,
|
||||||
|
}}
|
||||||
|
showWikiTreeMenus={sourceSpec?.source === DataSourceEnum.WIKITREE}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/" component={Intro} />
|
||||||
|
<Route exact path="/view" render={renderMainArea} />
|
||||||
|
<Redirect to={'/'} />
|
||||||
|
</Switch>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const App = injectIntl(AppComponent);
|
export const App = injectIntl(AppComponent);
|
||||||
|
|||||||
Reference in New Issue
Block a user