Load more data when navigating in WikiTree

This commit is contained in:
Przemek Wiech
2020-01-25 21:42:50 +01:00
parent d4bd801980
commit 48cfb221f3
5 changed files with 92 additions and 19 deletions

6
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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}

View File

@@ -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});
}

View File

@@ -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);
}
/**