,
) {
if (
!this.state.selection ||
this.state.selection.id !== selection.id ||
this.state.selection!.generation !== selection.generation ||
hasUpdatedValues(this.state, otherStateChanges)
) {
this.setState(
Object.assign({}, this.state, {selection}, otherStateChanges),
);
}
}
/** Sets error message after data load failure. */
private setError(error: string) {
this.setState(
Object.assign({}, this.state, {
error: error,
loading: false,
}),
);
}
private async onMessage(message: EmbeddedMessage) {
if (message.message === EmbeddedMessageType.PARENT_READY) {
// Parent didn't receive the first 'ready' message, so we need to send it again.
window.parent.postMessage({message: EmbeddedMessageType.READY}, '*');
} else if (message.message === EmbeddedMessageType.GEDCOM) {
const gedcom = (message as GedcomMessage).gedcom;
if (!gedcom) {
return;
}
try {
const data = await loadGedcom('', gedcom);
const software = getSoftware(data.gedcom.head);
analyticsEvent('embedded_file_loaded', {
event_label: software,
});
// Set state with data.
this.setState(
Object.assign({}, this.state, {
data,
selection: getSelection(data.chartData),
error: undefined,
loading: false,
}),
);
} catch (error) {
analyticsEvent('embedded_file_error');
this.setError(error.message);
}
}
}
componentDidMount() {
this.componentDidUpdate();
}
async componentDidUpdate() {
if (this.props.location.pathname !== '/view') {
return;
}
const args = getArguments(this.props.location);
if (args.embedded && !this.state.embedded) {
this.setState(
Object.assign({}, this.state, {
embedded: true,
standalone: false,
showSidePanel: args.showSidePanel,
}),
);
// Notify the parent window that we are ready.
window.parent.postMessage('ready', '*');
window.addEventListener('message', (data) => this.onMessage(data.data));
}
if (args.embedded) {
// If the app is embedded, do not run the normal loading code.
return;
}
const dataSource = DATA_SOURCES.get(args.source!);
if (!dataSource) {
this.props.history.replace({pathname: '/'});
} else if (
(!this.state.loading && !this.state.data && !this.state.error) ||
args.source !== this.state.source ||
dataSource.isNewData(args, this.state)
) {
// Set loading state.
this.setState(
Object.assign({}, this.state, {
data: undefined,
selection: undefined,
hash: args.hash,
error: undefined,
loading: true,
url: args.url,
standalone: args.standalone,
chartType: args.chartType,
source: args.source,
}),
);
try {
const data = await dataSource.loadData(args);
// Set state with data.
this.setState(
Object.assign({}, this.state, {
data,
hash: args.hash,
selection: getSelection(data.chartData, args.indi, args.generation),
error: undefined,
loading: false,
url: args.url,
showSidePanel: args.showSidePanel,
standalone: args.standalone,
chartType: args.chartType,
source: args.source,
}),
);
} catch (error) {
this.setError(error.message);
}
} else if (this.state.data && this.state.selection) {
// Update selection if it has changed in the URL.
const selection = getSelection(
this.state.data.chartData,
args.indi,
args.generation,
);
const loadMoreFromWikitree =
args.source === DataSourceEnum.WIKITREE &&
(!this.state.selection || this.state.selection.id !== selection.id);
this.updateDisplay(selection, {
chartType: args.chartType,
loadingMore: loadMoreFromWikitree || undefined,
});
if (loadMoreFromWikitree) {
const data = await loadWikiTree(args.indi!);
this.setState(
Object.assign({}, this.state, {
data,
hash: args.hash,
selection: getSelection(data.chartData, args.indi, args.generation),
error: undefined,
loading: false,
url: args.url,
showSidePanel: args.showSidePanel,
standalone: args.standalone,
chartType: args.chartType,
source: args.source,
loadingMore: false,
}),
);
}
}
}
/**
* Called when the user clicks an individual box in the chart.
* Updates the browser URL.
*/
private onSelection = (selection: IndiInfo) => {
analyticsEvent('selection_changed');
if (this.state.embedded) {
// In embedded mode the URL doesn't change.
this.updateDisplay(selection);
return;
}
const location = this.props.location;
const search = queryString.parse(location.search);
search.indi = selection.id;
search.gen = String(selection.generation);
location.search = queryString.stringify(search);
this.props.history.push(location);
};
private onPrint = () => {
analyticsEvent('print');
this.chartRef && this.chartRef.print();
};
private showErrorPopup(message: string) {
this.setState(
Object.assign({}, this.state, {
showErrorPopup: true,
error: message,
}),
);
}
private onDownloadPdf = async () => {
analyticsEvent('download_pdf');
try {
this.chartRef && (await this.chartRef.downloadPdf());
} catch (e) {
this.showErrorPopup(
this.context.intl.formatMessage({
id: 'error.failed_pdf',
defaultMessage:
'Failed to generate PDF file.' +
' Please try with a smaller diagram or download an SVG file.',
}),
);
}
};
private onDownloadPng = async () => {
analyticsEvent('download_png');
try {
this.chartRef && (await this.chartRef.downloadPng());
} catch (e) {
this.showErrorPopup(
this.context.intl.formatMessage({
id: 'error.failed_png',
defaultMessage:
'Failed to generate PNG file.' +
' Please try with a smaller diagram or download an SVG file.',
}),
);
}
};
private onDownloadSvg = () => {
analyticsEvent('download_svg');
this.chartRef && this.chartRef.downloadSvg();
};
onDismissErrorPopup = () => {
this.setState(
Object.assign({}, this.state, {
showErrorPopup: false,
}),
);
};
private renderMainArea = () => {
if (this.state.data && this.state.selection) {
return (
{this.state.loadingMore ? (
) : null}
(this.chartRef = ref)}
/>
{this.state.showSidePanel ? (
) : null}
);
}
if (this.state.error) {
return ;
}
return ;
};
render() {
return (
<>
(
)}
/>
>
);
}
}