mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-08-02 17:11:48 +00:00
Added WikiTree login button
This commit is contained in:
10
package-lock.json
generated
10
package-lock.json
generated
@@ -1784,6 +1784,11 @@
|
|||||||
"integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==",
|
"integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/js-cookie": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA=="
|
||||||
|
},
|
||||||
"@types/jspdf": {
|
"@types/jspdf": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jspdf/-/jspdf-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jspdf/-/jspdf-1.2.2.tgz",
|
||||||
@@ -9354,6 +9359,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
|
||||||
"integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
|
"integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
|
||||||
},
|
},
|
||||||
|
"js-cookie": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
|
||||||
|
},
|
||||||
"js-levenshtein": {
|
"js-levenshtein": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "src/index.tsx",
|
"main": "src/index.tsx",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/js-cookie": "^2.2.4",
|
||||||
"array.prototype.flatmap": "^1.2.1",
|
"array.prototype.flatmap": "^1.2.1",
|
||||||
"canvas-toBlob": "^1.0.0",
|
"canvas-toBlob": "^1.0.0",
|
||||||
"d3": "^5.7.0",
|
"d3": "^5.7.0",
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
"file-saver": "^2.0.1",
|
"file-saver": "^2.0.1",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"javascript-natural-sort": "^0.7.1",
|
"javascript-natural-sort": "^0.7.1",
|
||||||
|
"js-cookie": "^2.2.1",
|
||||||
"jspdf": "^1.5.3",
|
"jspdf": "^1.5.3",
|
||||||
"lunr": "^2.3.6",
|
"lunr": "^2.3.6",
|
||||||
"md5": "^2.2.1",
|
"md5": "^2.2.1",
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
const standalone = getParam('standalone') !== 'false'; // True by default.
|
const standalone = getParam('standalone') !== 'false'; // True by default.
|
||||||
const view = getParam('view');
|
const view = getParam('view');
|
||||||
const source = getParam('source');
|
const source = getParam('source');
|
||||||
|
const authcode = getParam('?authcode');
|
||||||
|
|
||||||
const chartTypes = new Map<string | undefined, ChartType>([
|
const chartTypes = new Map<string | undefined, ChartType>([
|
||||||
['relatives', ChartType.Relatives],
|
['relatives', ChartType.Relatives],
|
||||||
@@ -278,7 +279,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
);
|
);
|
||||||
const data =
|
const data =
|
||||||
source === 'wikitree'
|
source === 'wikitree'
|
||||||
? await loadWikiTree(indi!)
|
? await loadWikiTree(indi!, authcode)
|
||||||
: hash
|
: hash
|
||||||
? await loadGedcom(hash, gedcom, images)
|
? await loadGedcom(hash, gedcom, images)
|
||||||
: await loadFromUrl(url!, handleCors);
|
: await loadFromUrl(url!, handleCors);
|
||||||
@@ -485,6 +486,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
onDownloadPng: this.onDownloadPng,
|
onDownloadPng: this.onDownloadPng,
|
||||||
onDownloadSvg: this.onDownloadSvg,
|
onDownloadSvg: this.onDownloadSvg,
|
||||||
}}
|
}}
|
||||||
|
showWikiTreeLogin={this.state.wikiTreeSource}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
237
src/top_bar.tsx
237
src/top_bar.tsx
@@ -1,5 +1,6 @@
|
|||||||
import * as queryString from 'query-string';
|
import * as queryString from 'query-string';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
import debounce from 'debounce';
|
import debounce from 'debounce';
|
||||||
import md5 from 'md5';
|
import md5 from 'md5';
|
||||||
import {analyticsEvent} from './analytics';
|
import {analyticsEvent} from './analytics';
|
||||||
@@ -24,10 +25,18 @@ import {
|
|||||||
SearchResultProps,
|
SearchResultProps,
|
||||||
} from 'semantic-ui-react';
|
} from 'semantic-ui-react';
|
||||||
|
|
||||||
|
enum WikiTreeLoginState {
|
||||||
|
UNKNOWN,
|
||||||
|
NOT_LOGGED_IN,
|
||||||
|
LOGGED_IN,
|
||||||
|
}
|
||||||
|
|
||||||
/** Menus and dialogs state. */
|
/** Menus and dialogs state. */
|
||||||
interface State {
|
interface State {
|
||||||
loadUrlDialogOpen: boolean;
|
loadUrlDialogOpen: boolean;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
wikiTreeLoginState: WikiTreeLoginState;
|
||||||
|
wikiTreeLoginUsername?: string;
|
||||||
searchResults: SearchResultProps[];
|
searchResults: SearchResultProps[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +57,8 @@ interface Props {
|
|||||||
/** Whether to show the "All relatives" chart type in the menu. */
|
/** Whether to show the "All relatives" chart type in the menu. */
|
||||||
allowAllRelativesChart: boolean;
|
allowAllRelativesChart: boolean;
|
||||||
eventHandlers: EventHandlers;
|
eventHandlers: EventHandlers;
|
||||||
|
/** Whether to show the 'Log in to WikiTree' button. */
|
||||||
|
showWikiTreeLogin: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFileAsText(file: File): Promise<string> {
|
function loadFileAsText(file: File): Promise<string> {
|
||||||
@@ -72,13 +83,21 @@ export class TopBar extends React.Component<
|
|||||||
state: State = {
|
state: State = {
|
||||||
loadUrlDialogOpen: false,
|
loadUrlDialogOpen: false,
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
|
wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,
|
||||||
};
|
};
|
||||||
inputRef?: Input;
|
/** Make intl appear in this.context. */
|
||||||
|
static contextTypes = {
|
||||||
|
intl: intlShape,
|
||||||
|
};
|
||||||
|
|
||||||
|
urlInputRef: React.RefObject<Input> = React.createRef();
|
||||||
|
wikiTreeLoginFormRef: React.RefObject<HTMLFormElement> = React.createRef();
|
||||||
|
wikiTreeReturnUrlRef: React.RefObject<HTMLInputElement> = React.createRef();
|
||||||
searchRef?: {setValue(value: string): void};
|
searchRef?: {setValue(value: string): void};
|
||||||
searchIndex?: SearchIndex;
|
searchIndex?: SearchIndex;
|
||||||
|
|
||||||
/** Handles the "Upload file" button. */
|
/** Handles the "Upload file" button. */
|
||||||
async handleUpload(event: React.SyntheticEvent<HTMLInputElement>) {
|
private async handleUpload(event: React.SyntheticEvent<HTMLInputElement>) {
|
||||||
const files = (event.target as HTMLInputElement).files;
|
const files = (event.target as HTMLInputElement).files;
|
||||||
if (!files || !files.length) {
|
if (!files || !files.length) {
|
||||||
return;
|
return;
|
||||||
@@ -132,20 +151,20 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Opens the "Load from URL" dialog. */
|
/** Opens the "Load from URL" dialog. */
|
||||||
handleLoadFromUrl() {
|
private openLoadUrlDialog() {
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {loadUrlDialogOpen: true}),
|
Object.assign({}, this.state, {loadUrlDialogOpen: true}),
|
||||||
() => this.inputRef!.focus(),
|
() => this.urlInputRef.current!.focus(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancels the "Load from URL" dialog. */
|
/** Cancels the "Load from URL" dialog. */
|
||||||
handleClose() {
|
private handleClose() {
|
||||||
this.setState(Object.assign({}, this.state, {loadUrlDialogOpen: false}));
|
this.setState(Object.assign({}, this.state, {loadUrlDialogOpen: false}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Upload button clicked in the "Load from URL" dialog. */
|
/** Upload button clicked in the "Load from URL" dialog. */
|
||||||
handleLoad() {
|
private handleLoad() {
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {
|
||||||
loadUrlDialogOpen: false,
|
loadUrlDialogOpen: false,
|
||||||
@@ -161,7 +180,7 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Called when the URL input is typed into. */
|
/** Called when the URL input is typed into. */
|
||||||
handleUrlChange(event: React.SyntheticEvent) {
|
private handleUrlChange(event: React.SyntheticEvent) {
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {
|
||||||
url: (event.target as HTMLInputElement).value,
|
url: (event.target as HTMLInputElement).value,
|
||||||
@@ -170,7 +189,7 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** On search input change. */
|
/** On search input change. */
|
||||||
handleSearch(input: string | undefined) {
|
private handleSearch(input: string | undefined) {
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -181,13 +200,13 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** On search result selected. */
|
/** On search result selected. */
|
||||||
handleResultSelect(id: string) {
|
private handleResultSelect(id: string) {
|
||||||
analyticsEvent('search_result_selected');
|
analyticsEvent('search_result_selected');
|
||||||
this.props.eventHandlers.onSelection({id, generation: 0});
|
this.props.eventHandlers.onSelection({id, generation: 0});
|
||||||
this.searchRef!.setValue('');
|
this.searchRef!.setValue('');
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeSearchIndex() {
|
private initializeSearchIndex() {
|
||||||
if (this.props.gedcom) {
|
if (this.props.gedcom) {
|
||||||
this.searchIndex = buildSearchIndex(this.props.gedcom);
|
this.searchIndex = buildSearchIndex(this.props.gedcom);
|
||||||
}
|
}
|
||||||
@@ -203,23 +222,50 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
/**
|
||||||
|
* Redirect to the WikiTree Apps login page with a return URL pointing to
|
||||||
|
* Topola Viewer hosted on apps.wikitree.com.
|
||||||
|
*/
|
||||||
|
private wikiTreeLogin() {
|
||||||
|
const wikiTreeTopolaUrl =
|
||||||
|
'https://apps.wikitree.com/apps/wiech13/topola-viewer';
|
||||||
|
// Append '&' because the login page appends '?authcode=...' to this URL.
|
||||||
|
// TODO: remove ?authcode if it is in the current URL.
|
||||||
|
const returnUrl = `${wikiTreeTopolaUrl}${window.location.hash}&`;
|
||||||
|
this.wikiTreeReturnUrlRef.current!.value = returnUrl;
|
||||||
|
this.wikiTreeLoginFormRef.current!.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkWikiTreeLoginState() {
|
||||||
|
const wikiTreeLoginState =
|
||||||
|
Cookies.get('wikidb_wtb_UserID') !== undefined
|
||||||
|
? WikiTreeLoginState.LOGGED_IN
|
||||||
|
: WikiTreeLoginState.NOT_LOGGED_IN;
|
||||||
|
if (this.state.wikiTreeLoginState !== wikiTreeLoginState) {
|
||||||
|
const wikiTreeLoginUsername = Cookies.get('wikidb_wtb_UserName');
|
||||||
|
this.setState(
|
||||||
|
Object.assign({}, this.state, {
|
||||||
|
wikiTreeLoginState,
|
||||||
|
wikiTreeLoginUsername,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
this.checkWikiTreeLoginState();
|
||||||
this.initializeSearchIndex();
|
this.initializeSearchIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
|
this.checkWikiTreeLoginState();
|
||||||
if (prevProps.gedcom !== this.props.gedcom) {
|
if (prevProps.gedcom !== this.props.gedcom) {
|
||||||
this.initializeSearchIndex();
|
this.initializeSearchIndex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Make intl appear in this.context. */
|
private loadFromUrlModal() {
|
||||||
static contextTypes = {
|
return (
|
||||||
intl: intlShape,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const loadFromUrlModal = (
|
|
||||||
<Modal
|
<Modal
|
||||||
open={this.state.loadUrlDialogOpen}
|
open={this.state.loadUrlDialogOpen}
|
||||||
onClose={() => this.handleClose()}
|
onClose={() => this.handleClose()}
|
||||||
@@ -239,7 +285,7 @@ export class TopBar extends React.Component<
|
|||||||
placeholder="https://"
|
placeholder="https://"
|
||||||
fluid
|
fluid
|
||||||
onChange={(e) => this.handleUrlChange(e)}
|
onChange={(e) => this.handleUrlChange(e)}
|
||||||
ref={(ref) => (this.inputRef = ref!)}
|
ref={this.urlInputRef}
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -271,8 +317,13 @@ export class TopBar extends React.Component<
|
|||||||
</Modal.Actions>
|
</Modal.Actions>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const chartMenus = this.props.showingChart ? (
|
private chartMenus() {
|
||||||
|
if (!this.props.showingChart) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<Menu.Item as="a" onClick={() => this.props.eventHandlers.onPrint()}>
|
<Menu.Item as="a" onClick={() => this.props.eventHandlers.onPrint()}>
|
||||||
<Icon name="print" />
|
<Icon name="print" />
|
||||||
@@ -367,16 +418,21 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null;
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const fileMenus = this.props.standalone ? (
|
private fileMenus() {
|
||||||
|
if (!this.props.standalone) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<b>Topola Genealogy</b>
|
<b>Topola Genealogy</b>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Link>
|
</Link>
|
||||||
<Menu.Item as="a" onClick={() => this.handleLoadFromUrl()}>
|
<Menu.Item as="a" onClick={() => this.openLoadUrlDialog()}>
|
||||||
<Icon name="cloud download" />
|
<Icon name="cloud download" />
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="menu.load_from_url"
|
id="menu.load_from_url"
|
||||||
@@ -401,44 +457,111 @@ export class TopBar extends React.Component<
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</label>
|
</label>
|
||||||
</>
|
</>
|
||||||
) : null;
|
|
||||||
|
|
||||||
const sourceLink = this.props.standalone ? (
|
|
||||||
<>
|
|
||||||
<Menu.Item
|
|
||||||
as="a"
|
|
||||||
href="https://github.com/PeWu/topola-viewer"
|
|
||||||
position="right"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id="menu.github"
|
|
||||||
defaultMessage="Source on GitHub"
|
|
||||||
/>
|
|
||||||
</Menu.Item>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Menu.Item
|
|
||||||
as="a"
|
|
||||||
href="https://pewu.github.com/topola-viewer"
|
|
||||||
position="right"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id="menu.powered_by"
|
|
||||||
defaultMessage="Powered by Topola"
|
|
||||||
/>
|
|
||||||
</Menu.Item>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private wikiTreeLoginMenu() {
|
||||||
|
if (!this.props.showWikiTreeLogin) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const wikiTreeLogoUrl =
|
||||||
|
'https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png';
|
||||||
|
switch (this.state.wikiTreeLoginState) {
|
||||||
|
case WikiTreeLoginState.NOT_LOGGED_IN:
|
||||||
|
return (
|
||||||
|
<Menu.Item as="a" onClick={() => this.wikiTreeLogin()}>
|
||||||
|
<img
|
||||||
|
src={wikiTreeLogoUrl}
|
||||||
|
alt="WikiTree logo"
|
||||||
|
style={{width: '24px', height: '24px'}}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id="menu.wikitree_login"
|
||||||
|
defaultMessage="Log in to WikiTree"
|
||||||
|
/>
|
||||||
|
<form
|
||||||
|
action="https://apps.wikitree.com/api.php"
|
||||||
|
method="POST"
|
||||||
|
style={{display: 'hidden'}}
|
||||||
|
ref={this.wikiTreeLoginFormRef}
|
||||||
|
>
|
||||||
|
<input type="hidden" name="action" value="clientLogin" />
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="returnURL"
|
||||||
|
ref={this.wikiTreeReturnUrlRef}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
case WikiTreeLoginState.LOGGED_IN:
|
||||||
|
const tooltip = this.state.wikiTreeLoginUsername
|
||||||
|
? this.context.intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'menu.wikitree_popup_username',
|
||||||
|
defaultMessage: 'Logged in to WikiTree as {username}',
|
||||||
|
},
|
||||||
|
{username: this.state.wikiTreeLoginUsername},
|
||||||
|
)
|
||||||
|
: this.context.intl.formatMessage({
|
||||||
|
id: 'menu.wikitree_popup',
|
||||||
|
defaultMessage: 'Logged in to WikiTree',
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Menu.Item title={tooltip}>
|
||||||
|
<img
|
||||||
|
src={wikiTreeLogoUrl}
|
||||||
|
alt="WikiTree logo"
|
||||||
|
style={{width: '24px', height: '24px'}}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id="menu.wikitree_logged_in"
|
||||||
|
defaultMessage="Logged in"
|
||||||
|
/>
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sourceLink() {
|
||||||
|
return (
|
||||||
|
<Menu.Item
|
||||||
|
as="a"
|
||||||
|
href="https://github.com/PeWu/topola-viewer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<FormattedMessage id="menu.github" defaultMessage="Source on GitHub" />
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private poweredByLink() {
|
||||||
|
return (
|
||||||
|
<Menu.Item
|
||||||
|
as="a"
|
||||||
|
href="https://pewu.github.com/topola-viewer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="menu.powered_by"
|
||||||
|
defaultMessage="Powered by Topola"
|
||||||
|
/>
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<Menu attached="top" inverted color="blue" size="large">
|
<Menu attached="top" inverted color="blue" size="large">
|
||||||
{fileMenus}
|
{this.fileMenus()}
|
||||||
{chartMenus}
|
{this.chartMenus()}
|
||||||
{sourceLink}
|
<Menu.Menu position="right">
|
||||||
{loadFromUrlModal}
|
{this.wikiTreeLoginMenu()}
|
||||||
|
{this.props.standalone ? this.sourceLink() : this.poweredByLink()}
|
||||||
|
</Menu.Menu>
|
||||||
|
{this.loadFromUrlModal()}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
"menu.hourglass": "Wykres klepsydrowy",
|
"menu.hourglass": "Wykres klepsydrowy",
|
||||||
"menu.relatives": "Wszyscy krewni",
|
"menu.relatives": "Wszyscy krewni",
|
||||||
"menu.fancy": "Ozdobne drzewo (eksperymentalne)",
|
"menu.fancy": "Ozdobne drzewo (eksperymentalne)",
|
||||||
|
"menu.wikitree_login": "Zaloguj do WikiTree",
|
||||||
|
"menu.wikitree_logged_in": "Zalogowano",
|
||||||
|
"menu.wikitree_popup_username": "Zalogowano do WikiTree jako {username}",
|
||||||
|
"menu.wikitree_popup": "Zalogowano do WikiTree",
|
||||||
"menu.github": "Źródła na GitHub",
|
"menu.github": "Źródła na GitHub",
|
||||||
"menu.powered_by": "Topola Genealogy",
|
"menu.powered_by": "Topola Genealogy",
|
||||||
"menu.search.placeholder": "Szukaj osoby",
|
"menu.search.placeholder": "Szukaj osoby",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {GedcomData, TopolaData} from './gedcom_util';
|
import Cookies from 'js-cookie';
|
||||||
import {Date, JsonFam, JsonIndi} from 'topola';
|
import {Date, JsonFam, JsonIndi} from 'topola';
|
||||||
|
import {GedcomData, TopolaData} from './gedcom_util';
|
||||||
import {GedcomEntry} from 'parse-gedcom';
|
import {GedcomEntry} from 'parse-gedcom';
|
||||||
|
|
||||||
/** WikiTree API getAncestors request. */
|
/** WikiTree API getAncestors request. */
|
||||||
@@ -17,7 +18,12 @@ interface GetRelatives {
|
|||||||
getSpouses?: true;
|
getSpouses?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
type WikiTreeRequest = GetAncestorsRequest | GetRelatives;
|
interface ClientLogin {
|
||||||
|
action: 'clientLogin';
|
||||||
|
authcode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type WikiTreeRequest = GetAncestorsRequest | GetRelatives | ClientLogin;
|
||||||
|
|
||||||
/** Person structure returned from WikiTree API. */
|
/** Person structure returned from WikiTree API. */
|
||||||
interface Person {
|
interface Person {
|
||||||
@@ -128,14 +134,35 @@ async function getRelatives(keys: string[], handleCors: boolean) {
|
|||||||
return result.concat(fetchedResults);
|
return result.concat(fetchedResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function clientLogin(authcode: string) {
|
||||||
|
const response = await wikiTreeGet(
|
||||||
|
{
|
||||||
|
action: 'clientLogin',
|
||||||
|
authcode,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
return response.clientLogin;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads data from WikiTree to populate an hourglass chart starting from the
|
* Loads data from WikiTree to populate an hourglass chart starting from the
|
||||||
* given person ID.
|
* given person ID.
|
||||||
*/
|
*/
|
||||||
export async function loadWikiTree(key: string): Promise<TopolaData> {
|
export async function loadWikiTree(
|
||||||
|
key: string,
|
||||||
|
authcode?: string,
|
||||||
|
): Promise<TopolaData> {
|
||||||
// Work around CORS if not in apps.wikitree.com domain.
|
// Work around CORS if not in apps.wikitree.com domain.
|
||||||
const handleCors = window.location.hostname !== 'apps.wikitree.com';
|
const handleCors = window.location.hostname !== 'apps.wikitree.com';
|
||||||
|
|
||||||
|
if (!handleCors && !Cookies.get('wikidb_wtb_UserID') && authcode) {
|
||||||
|
const loginResult = await clientLogin(authcode);
|
||||||
|
if (loginResult.result === 'Success') {
|
||||||
|
sessionStorage.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const everyone: Person[] = [];
|
const everyone: Person[] = [];
|
||||||
|
|
||||||
// Fetch the ancestors of the input person and ancestors of his/her spouses.
|
// Fetch the ancestors of the input person and ancestors of his/her spouses.
|
||||||
|
|||||||
Reference in New Issue
Block a user