mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-08-01 08:31:47 +00:00
Load more data when navigating in WikiTree
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -14558,9 +14558,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"topola": {
|
"topola": {
|
||||||
"version": "2.5.1",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/topola/-/topola-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/topola/-/topola-3.0.0.tgz",
|
||||||
"integrity": "sha512-g1hDeT0X4HHg/p1/Wr/fgUceN9JlP6LC1rIJkfP7lPwWVZcplzfdCDzuyIKPfkrUsE5OZAhw3LoPu6xKv1u/dg==",
|
"integrity": "sha512-taln1nxS819MpxtIwqCjW47VljOT9HBhpaxtjSqVEKPBzXV8/zLIemF2NkIMSuH+7jnKJllpXAc1FAseBC0cfA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"array-flat-polyfill": "^1.0.1",
|
"array-flat-polyfill": "^1.0.1",
|
||||||
"d3": "^5.4.0",
|
"d3": "^5.4.0",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^4.3.1",
|
||||||
"semantic-ui-css": "^2.4.1",
|
"semantic-ui-css": "^2.4.1",
|
||||||
"semantic-ui-react": "^0.84.0",
|
"semantic-ui-react": "^0.84.0",
|
||||||
"topola": "^2.5.1"
|
"topola": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/array.prototype.flatmap": "^1.2.0",
|
"@types/array.prototype.flatmap": "^1.2.0",
|
||||||
|
|||||||
52
src/app.tsx
52
src/app.tsx
@@ -74,6 +74,16 @@ interface GedcomMessage extends EmbeddedMessage {
|
|||||||
gedcom?: string;
|
gedcom?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returs true if the changes object has values that are different than those in state. */
|
||||||
|
function hasUpdatedValues<T>(state: T, changes: Partial<T> | undefined) {
|
||||||
|
if (!changes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Object.entries(changes).some(
|
||||||
|
([key, value]) => value !== undefined && state[key] !== value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
/** Loaded data. */
|
/** Loaded data. */
|
||||||
data?: TopolaData;
|
data?: TopolaData;
|
||||||
@@ -99,6 +109,7 @@ interface State {
|
|||||||
showErrorPopup: boolean;
|
showErrorPopup: boolean;
|
||||||
/** True if data is loaded from WikiTree. */
|
/** True if data is loaded from WikiTree. */
|
||||||
wikiTreeSource: boolean;
|
wikiTreeSource: boolean;
|
||||||
|
loadingMore?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class App extends React.Component<RouteComponentProps, {}> {
|
export class App extends React.Component<RouteComponentProps, {}> {
|
||||||
@@ -135,18 +146,18 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the state with a new individual selection and chart type. */
|
/** Sets the state with a new individual selection and chart type. */
|
||||||
private updateDisplay(selection: IndiInfo, chartType?: ChartType) {
|
private updateDisplay(
|
||||||
|
selection: IndiInfo,
|
||||||
|
otherStateChanges?: Partial<State>,
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
!this.state.selection ||
|
!this.state.selection ||
|
||||||
this.state.selection.id !== selection.id ||
|
this.state.selection.id !== selection.id ||
|
||||||
this.state.selection!.generation !== selection.generation ||
|
this.state.selection!.generation !== selection.generation ||
|
||||||
(chartType !== undefined && chartType !== this.state.chartType)
|
hasUpdatedValues(this.state, otherStateChanges)
|
||||||
) {
|
) {
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {selection}, otherStateChanges),
|
||||||
selection,
|
|
||||||
chartType: chartType !== undefined ? chartType : this.state.chartType,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +319,31 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
indi,
|
indi,
|
||||||
generation,
|
generation,
|
||||||
);
|
);
|
||||||
this.updateDisplay(selection, chartType);
|
const loadMoreFromWikitree =
|
||||||
|
source === 'wikitree' &&
|
||||||
|
(!this.state.selection || this.state.selection.id !== selection.id);
|
||||||
|
this.updateDisplay(selection, {
|
||||||
|
chartType,
|
||||||
|
loadingMore: loadMoreFromWikitree || undefined,
|
||||||
|
});
|
||||||
|
if (loadMoreFromWikitree) {
|
||||||
|
const data = await loadWikiTree(indi!);
|
||||||
|
this.setState(
|
||||||
|
Object.assign({}, this.state, {
|
||||||
|
data,
|
||||||
|
hash,
|
||||||
|
selection: getSelection(data.chartData, indi, generation),
|
||||||
|
error: undefined,
|
||||||
|
loading: false,
|
||||||
|
url,
|
||||||
|
showSidePanel,
|
||||||
|
standalone,
|
||||||
|
chartType,
|
||||||
|
wikiTreeSource: source === 'wikitree',
|
||||||
|
loadingMore: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,6 +434,9 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
message={this.state.error}
|
message={this.state.error}
|
||||||
onDismiss={this.onDismissErrorPopup}
|
onDismiss={this.onDismissErrorPopup}
|
||||||
/>
|
/>
|
||||||
|
{this.state.loadingMore ? (
|
||||||
|
<Loader active inline size="small" />
|
||||||
|
) : null}
|
||||||
<Chart
|
<Chart
|
||||||
data={this.state.data.chartData}
|
data={this.state.data.chartData}
|
||||||
selection={this.state.selection}
|
selection={this.state.selection}
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
updateSvgSize: false,
|
updateSvgSize: false,
|
||||||
locale: this.context.intl.locale,
|
locale: this.context.intl.locale,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.chart!.setData(this.props.data);
|
||||||
}
|
}
|
||||||
const chartInfo = this.chart!.render({
|
const chartInfo = this.chart!.render({
|
||||||
startIndi: this.props.selection.id,
|
startIndi: this.props.selection.id,
|
||||||
@@ -227,9 +229,7 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: ChartProps) {
|
componentDidUpdate(prevProps: ChartProps) {
|
||||||
const initialRender =
|
const initialRender = this.props.chartType !== prevProps.chartType;
|
||||||
this.props.data !== prevProps.data ||
|
|
||||||
this.props.chartType !== prevProps.chartType;
|
|
||||||
this.renderChart({initialRender});
|
this.renderChart({initialRender});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,16 @@ async function wikiTreeGet(request: WikiTreeRequest, handleCors: boolean) {
|
|||||||
return JSON.parse(responseBody);
|
return JSON.parse(responseBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieves ancestors from WikiTree for the given person ID. */
|
/**
|
||||||
|
* Retrieves ancestors from WikiTree for the given person ID.
|
||||||
|
* Uses sessionStorage for caching responses.
|
||||||
|
*/
|
||||||
async function getAncestors(key: string, handleCors: boolean) {
|
async function getAncestors(key: string, handleCors: boolean) {
|
||||||
|
const cacheKey = `wikitree:ancestors:${key}`;
|
||||||
|
const cachedData = sessionStorage.getItem(cacheKey);
|
||||||
|
if (cachedData) {
|
||||||
|
return JSON.parse(cachedData);
|
||||||
|
}
|
||||||
const response = await wikiTreeGet(
|
const response = await wikiTreeGet(
|
||||||
{
|
{
|
||||||
action: 'getAncestors',
|
action: 'getAncestors',
|
||||||
@@ -73,21 +81,48 @@ async function getAncestors(key: string, handleCors: boolean) {
|
|||||||
},
|
},
|
||||||
handleCors,
|
handleCors,
|
||||||
);
|
);
|
||||||
return response[0].ancestors as Person[];
|
const result = response[0].ancestors as Person[];
|
||||||
|
sessionStorage.setItem(cacheKey, JSON.stringify(result));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieves relatives from WikiTree for the given array of person IDs. */
|
/**
|
||||||
|
* Retrieves relatives from WikiTree for the given array of person IDs.
|
||||||
|
* Uses sessionStorage for caching responses.
|
||||||
|
*/
|
||||||
async function getRelatives(keys: string[], handleCors: boolean) {
|
async function getRelatives(keys: string[], handleCors: boolean) {
|
||||||
|
const result: Person[] = [];
|
||||||
|
const keysToFetch: string[] = [];
|
||||||
|
keys.forEach((key) => {
|
||||||
|
const cachedData = sessionStorage.getItem(`wikitree:relatives:${key}`);
|
||||||
|
if (cachedData) {
|
||||||
|
result.push(JSON.parse(cachedData));
|
||||||
|
} else {
|
||||||
|
keysToFetch.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (keysToFetch.length === 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
const response = await wikiTreeGet(
|
const response = await wikiTreeGet(
|
||||||
{
|
{
|
||||||
action: 'getRelatives',
|
action: 'getRelatives',
|
||||||
keys: keys.join(','),
|
keys: keysToFetch.join(','),
|
||||||
getChildren: true,
|
getChildren: true,
|
||||||
getSpouses: true,
|
getSpouses: true,
|
||||||
},
|
},
|
||||||
handleCors,
|
handleCors,
|
||||||
);
|
);
|
||||||
return response[0].items.map((x: {person: Person}) => x.person) as Person[];
|
const fetchedResults = response[0].items.map(
|
||||||
|
(x: {person: Person}) => x.person,
|
||||||
|
) as Person[];
|
||||||
|
fetchedResults.forEach((person) => {
|
||||||
|
sessionStorage.setItem(
|
||||||
|
`wikitree:relatives:${person.Name}`,
|
||||||
|
JSON.stringify(person),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return result.concat(fetchedResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user