mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-02-19 08:16:34 +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
|
||||
},
|
||||
"topola": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/topola/-/topola-2.5.1.tgz",
|
||||
"integrity": "sha512-g1hDeT0X4HHg/p1/Wr/fgUceN9JlP6LC1rIJkfP7lPwWVZcplzfdCDzuyIKPfkrUsE5OZAhw3LoPu6xKv1u/dg==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/topola/-/topola-3.0.0.tgz",
|
||||
"integrity": "sha512-taln1nxS819MpxtIwqCjW47VljOT9HBhpaxtjSqVEKPBzXV8/zLIemF2NkIMSuH+7jnKJllpXAc1FAseBC0cfA==",
|
||||
"requires": {
|
||||
"array-flat-polyfill": "^1.0.1",
|
||||
"d3": "^5.4.0",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"react-router-dom": "^4.3.1",
|
||||
"semantic-ui-css": "^2.4.1",
|
||||
"semantic-ui-react": "^0.84.0",
|
||||
"topola": "^2.5.1"
|
||||
"topola": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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;
|
||||
}
|
||||
|
||||
/** 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 {
|
||||
/** Loaded data. */
|
||||
data?: TopolaData;
|
||||
@@ -99,6 +109,7 @@ interface State {
|
||||
showErrorPopup: boolean;
|
||||
/** True if data is loaded from WikiTree. */
|
||||
wikiTreeSource: boolean;
|
||||
loadingMore?: boolean;
|
||||
}
|
||||
|
||||
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. */
|
||||
private updateDisplay(selection: IndiInfo, chartType?: ChartType) {
|
||||
private updateDisplay(
|
||||
selection: IndiInfo,
|
||||
otherStateChanges?: Partial<State>,
|
||||
) {
|
||||
if (
|
||||
!this.state.selection ||
|
||||
this.state.selection.id !== selection.id ||
|
||||
this.state.selection!.generation !== selection.generation ||
|
||||
(chartType !== undefined && chartType !== this.state.chartType)
|
||||
hasUpdatedValues(this.state, otherStateChanges)
|
||||
) {
|
||||
this.setState(
|
||||
Object.assign({}, this.state, {
|
||||
selection,
|
||||
chartType: chartType !== undefined ? chartType : this.state.chartType,
|
||||
}),
|
||||
Object.assign({}, this.state, {selection}, otherStateChanges),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -308,7 +319,31 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
indi,
|
||||
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}
|
||||
onDismiss={this.onDismissErrorPopup}
|
||||
/>
|
||||
{this.state.loadingMore ? (
|
||||
<Loader active inline size="small" />
|
||||
) : null}
|
||||
<Chart
|
||||
data={this.state.data.chartData}
|
||||
selection={this.state.selection}
|
||||
|
||||
@@ -164,6 +164,8 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
||||
updateSvgSize: false,
|
||||
locale: this.context.intl.locale,
|
||||
});
|
||||
} else {
|
||||
this.chart!.setData(this.props.data);
|
||||
}
|
||||
const chartInfo = this.chart!.render({
|
||||
startIndi: this.props.selection.id,
|
||||
@@ -227,9 +229,7 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ChartProps) {
|
||||
const initialRender =
|
||||
this.props.data !== prevProps.data ||
|
||||
this.props.chartType !== prevProps.chartType;
|
||||
const initialRender = this.props.chartType !== prevProps.chartType;
|
||||
this.renderChart({initialRender});
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,16 @@ async function wikiTreeGet(request: WikiTreeRequest, handleCors: boolean) {
|
||||
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) {
|
||||
const cacheKey = `wikitree:ancestors:${key}`;
|
||||
const cachedData = sessionStorage.getItem(cacheKey);
|
||||
if (cachedData) {
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
const response = await wikiTreeGet(
|
||||
{
|
||||
action: 'getAncestors',
|
||||
@@ -73,21 +81,48 @@ async function getAncestors(key: string, handleCors: boolean) {
|
||||
},
|
||||
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) {
|
||||
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(
|
||||
{
|
||||
action: 'getRelatives',
|
||||
keys: keys.join(','),
|
||||
keys: keysToFetch.join(','),
|
||||
getChildren: true,
|
||||
getSpouses: true,
|
||||
},
|
||||
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