mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-08-03 09:31:52 +00:00
Added "Select WikiTree ID" to the menu
This commit is contained in:
16
src/app.tsx
16
src/app.tsx
@@ -133,8 +133,18 @@ class GedcomUrlDataSource implements DataSource {
|
|||||||
/** Loading data from the WikiTree API. */
|
/** Loading data from the WikiTree API. */
|
||||||
class WikiTreeDataSource implements DataSource {
|
class WikiTreeDataSource implements DataSource {
|
||||||
isNewData(args: Arguments, state: State): boolean {
|
isNewData(args: Arguments, state: State): boolean {
|
||||||
// WikiTree is always a single data source.
|
if (state.selection && state.selection.id === args.indi) {
|
||||||
return false;
|
// Selection unchanged -> don't reload.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
state.data &&
|
||||||
|
state.data.chartData.indis.some((indi) => indi.id === args.indi)
|
||||||
|
) {
|
||||||
|
// New selection exists in current view -> animate instead of reloading.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadData(args: Arguments): Promise<TopolaData> {
|
async loadData(args: Arguments): Promise<TopolaData> {
|
||||||
@@ -379,7 +389,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
|||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {
|
||||||
data: undefined,
|
data: undefined,
|
||||||
selection: undefined,
|
selection: {id: args.indi},
|
||||||
hash: args.hash,
|
hash: args.hash,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
|||||||
@@ -126,3 +126,8 @@ div.ui.card.intro {
|
|||||||
.content .ui.image.blockImage {
|
.content .ui.image.blockImage {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link-span {
|
||||||
|
color: #4183c4;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|||||||
192
src/top_bar.tsx
192
src/top_bar.tsx
@@ -25,6 +25,9 @@ import {
|
|||||||
Responsive,
|
Responsive,
|
||||||
} from 'semantic-ui-react';
|
} from 'semantic-ui-react';
|
||||||
|
|
||||||
|
const WIKITREE_LOGO_URL =
|
||||||
|
'https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png';
|
||||||
|
|
||||||
enum WikiTreeLoginState {
|
enum WikiTreeLoginState {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
NOT_LOGGED_IN,
|
NOT_LOGGED_IN,
|
||||||
@@ -39,7 +42,9 @@ enum ScreenSize {
|
|||||||
/** Menus and dialogs state. */
|
/** Menus and dialogs state. */
|
||||||
interface State {
|
interface State {
|
||||||
loadUrlDialogOpen: boolean;
|
loadUrlDialogOpen: boolean;
|
||||||
|
wikiTreeIdDialogOpen: boolean;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
wikiTreeId?: string;
|
||||||
wikiTreeLoginState: WikiTreeLoginState;
|
wikiTreeLoginState: WikiTreeLoginState;
|
||||||
wikiTreeLoginUsername?: string;
|
wikiTreeLoginUsername?: string;
|
||||||
searchResults: SearchResultProps[];
|
searchResults: SearchResultProps[];
|
||||||
@@ -87,6 +92,7 @@ export class TopBar extends React.Component<
|
|||||||
> {
|
> {
|
||||||
state: State = {
|
state: State = {
|
||||||
loadUrlDialogOpen: false,
|
loadUrlDialogOpen: false,
|
||||||
|
wikiTreeIdDialogOpen: false,
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,
|
wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,
|
||||||
};
|
};
|
||||||
@@ -96,6 +102,7 @@ export class TopBar extends React.Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
urlInputRef: React.RefObject<Input> = React.createRef();
|
urlInputRef: React.RefObject<Input> = React.createRef();
|
||||||
|
wikiTreeIdInputRef: React.RefObject<Input> = React.createRef();
|
||||||
wikiTreeLoginFormRef: React.RefObject<HTMLFormElement> = React.createRef();
|
wikiTreeLoginFormRef: React.RefObject<HTMLFormElement> = React.createRef();
|
||||||
wikiTreeReturnUrlRef: React.RefObject<HTMLInputElement> = React.createRef();
|
wikiTreeReturnUrlRef: React.RefObject<HTMLInputElement> = React.createRef();
|
||||||
searchRef?: {setValue(value: string): void};
|
searchRef?: {setValue(value: string): void};
|
||||||
@@ -163,12 +170,24 @@ export class TopBar extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancels the "Load from URL" dialog. */
|
private openWikiTreeIdDialog() {
|
||||||
private handleClose() {
|
this.setState(
|
||||||
this.setState(Object.assign({}, this.state, {loadUrlDialogOpen: false}));
|
Object.assign({}, this.state, {wikiTreeIdDialogOpen: true}),
|
||||||
|
() => this.wikiTreeIdInputRef.current!.focus(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Upload button clicked in the "Load from URL" dialog. */
|
/** Cancels any of the open dialogs. */
|
||||||
|
private handleClose() {
|
||||||
|
this.setState(
|
||||||
|
Object.assign({}, this.state, {
|
||||||
|
loadUrlDialogOpen: false,
|
||||||
|
wikiTreeIdDialogOpen: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load button clicked in the "Load from URL" dialog. */
|
||||||
private handleLoad() {
|
private handleLoad() {
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {
|
||||||
@@ -184,11 +203,43 @@ export class TopBar extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when the URL input is typed into. */
|
/** Select button clicked in the "Select WikiTree ID" dialog. */
|
||||||
private handleUrlChange(event: React.SyntheticEvent) {
|
private handleSelectWikiTreeId() {
|
||||||
this.setState(
|
this.setState(
|
||||||
Object.assign({}, this.state, {
|
Object.assign({}, this.state, {
|
||||||
url: (event.target as HTMLInputElement).value,
|
wikiTreeIdDialogOpen: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (this.state.wikiTreeId) {
|
||||||
|
analyticsEvent('wikitree_id_selected');
|
||||||
|
const search = queryString.parse(this.props.location.search);
|
||||||
|
const standalone =
|
||||||
|
search.standalone !== undefined ? search.standalone : true;
|
||||||
|
this.props.history.push({
|
||||||
|
pathname: '/view',
|
||||||
|
search: queryString.stringify({
|
||||||
|
indi: this.state.wikiTreeId,
|
||||||
|
source: 'wikitree',
|
||||||
|
standalone,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when the URL input is typed into. */
|
||||||
|
private handleUrlChange(value: string) {
|
||||||
|
this.setState(
|
||||||
|
Object.assign({}, this.state, {
|
||||||
|
url: value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when the URL input is typed into. */
|
||||||
|
private handleWikiTreeIdChange(value: string) {
|
||||||
|
this.setState(
|
||||||
|
Object.assign({}, this.state, {
|
||||||
|
wikiTreeId: value,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -289,7 +340,7 @@ export class TopBar extends React.Component<
|
|||||||
<Input
|
<Input
|
||||||
placeholder="https://"
|
placeholder="https://"
|
||||||
fluid
|
fluid
|
||||||
onChange={(e) => this.handleUrlChange(e)}
|
onChange={(e, data) => this.handleUrlChange(data.value)}
|
||||||
ref={this.urlInputRef}
|
ref={this.urlInputRef}
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
@@ -324,6 +375,95 @@ export class TopBar extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enterWikiTreeId(event: React.MouseEvent, id: string) {
|
||||||
|
event.preventDefault(); // Do not follow link in href.
|
||||||
|
((this.wikiTreeIdInputRef.current as unknown) as {
|
||||||
|
inputRef: HTMLInputElement;
|
||||||
|
}).inputRef.value = id;
|
||||||
|
this.handleWikiTreeIdChange(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private wikiTreeIdModal() {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={this.state.wikiTreeIdDialogOpen}
|
||||||
|
onClose={() => this.handleClose()}
|
||||||
|
centered={false}
|
||||||
|
>
|
||||||
|
<Header>
|
||||||
|
<img
|
||||||
|
src={WIKITREE_LOGO_URL}
|
||||||
|
alt="WikiTree logo"
|
||||||
|
style={{width: '32px', height: '32px'}}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id="select_wikitree_id.title"
|
||||||
|
defaultMessage="Select WikiTree ID"
|
||||||
|
children={(txt) => txt}
|
||||||
|
/>
|
||||||
|
</Header>
|
||||||
|
<Modal.Content>
|
||||||
|
<Form onSubmit={() => this.handleLoad()}>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id="select_wikitree_id.comment"
|
||||||
|
defaultMessage={
|
||||||
|
'Enter a {wikiTreeLink} profile ID. Examples: {example1}, {example2}.'
|
||||||
|
}
|
||||||
|
values={{
|
||||||
|
wikiTreeLink: (
|
||||||
|
<a
|
||||||
|
href="https://wikitree.com/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
WikiTree
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
example1: (
|
||||||
|
<span
|
||||||
|
onClick={(e) => this.enterWikiTreeId(e, 'Wojtyla-13')}
|
||||||
|
className="link-span"
|
||||||
|
>
|
||||||
|
Wojtyla-13
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
example2: (
|
||||||
|
<span
|
||||||
|
onClick={(e) => this.enterWikiTreeId(e, 'Skłodowska-2')}
|
||||||
|
className="link-span"
|
||||||
|
>
|
||||||
|
Skłodowska-2
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<Input
|
||||||
|
fluid
|
||||||
|
onChange={(e, data) => this.handleWikiTreeIdChange(data.value)}
|
||||||
|
ref={this.wikiTreeIdInputRef}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Modal.Content>
|
||||||
|
<Modal.Actions>
|
||||||
|
<Button secondary onClick={() => this.handleClose()}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="select_wikitree_id.cancel"
|
||||||
|
defaultMessage="Cancel"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<Button primary onClick={() => this.handleSelectWikiTreeId()}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="select_wikitree_id.load"
|
||||||
|
defaultMessage="Load"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Modal.Actions>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private search() {
|
private search() {
|
||||||
return (
|
return (
|
||||||
<Search
|
<Search
|
||||||
@@ -521,9 +661,23 @@ export class TopBar extends React.Component<
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
const loadWikiTreeItem = (
|
||||||
|
<>
|
||||||
|
<img
|
||||||
|
src={WIKITREE_LOGO_URL}
|
||||||
|
alt="WikiTree logo"
|
||||||
|
style={{width: '24px', height: '24px'}}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id="menu.select_wikitree_id"
|
||||||
|
defaultMessage="Select WikiTree ID"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
const commonElements = (
|
const commonElements = (
|
||||||
<>
|
<>
|
||||||
{this.loadFromUrlModal()}
|
{this.loadFromUrlModal()}
|
||||||
|
{this.wikiTreeIdModal()}
|
||||||
<input
|
<input
|
||||||
className="hidden"
|
className="hidden"
|
||||||
type="file"
|
type="file"
|
||||||
@@ -555,6 +709,9 @@ export class TopBar extends React.Component<
|
|||||||
<Dropdown.Item onClick={() => this.openLoadUrlDialog()}>
|
<Dropdown.Item onClick={() => this.openLoadUrlDialog()}>
|
||||||
{loadUrlItem}
|
{loadUrlItem}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
|
<Dropdown.Item onClick={() => this.openWikiTreeIdDialog()}>
|
||||||
|
{loadWikiTreeItem}
|
||||||
|
</Dropdown.Item>
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
) : (
|
) : (
|
||||||
@@ -565,6 +722,9 @@ export class TopBar extends React.Component<
|
|||||||
<Menu.Item onClick={() => this.openLoadUrlDialog()}>
|
<Menu.Item onClick={() => this.openLoadUrlDialog()}>
|
||||||
{loadUrlItem}
|
{loadUrlItem}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item onClick={() => this.openWikiTreeIdDialog()}>
|
||||||
|
{loadWikiTreeItem}
|
||||||
|
</Menu.Item>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@@ -583,6 +743,9 @@ export class TopBar extends React.Component<
|
|||||||
<Dropdown.Item onClick={() => this.openLoadUrlDialog()}>
|
<Dropdown.Item onClick={() => this.openLoadUrlDialog()}>
|
||||||
{loadUrlItem}
|
{loadUrlItem}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
|
<Dropdown.Item onClick={() => this.openWikiTreeIdDialog()}>
|
||||||
|
{loadWikiTreeItem}
|
||||||
|
</Dropdown.Item>
|
||||||
<Dropdown.Divider />
|
<Dropdown.Divider />
|
||||||
{commonElements}
|
{commonElements}
|
||||||
</>
|
</>
|
||||||
@@ -594,9 +757,6 @@ export class TopBar extends React.Component<
|
|||||||
if (!this.props.showWikiTreeLogin) {
|
if (!this.props.showWikiTreeLogin) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const wikiTreeLogoUrl =
|
|
||||||
'https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png';
|
|
||||||
|
|
||||||
switch (this.state.wikiTreeLoginState) {
|
switch (this.state.wikiTreeLoginState) {
|
||||||
case WikiTreeLoginState.NOT_LOGGED_IN:
|
case WikiTreeLoginState.NOT_LOGGED_IN:
|
||||||
const loginForm = (
|
const loginForm = (
|
||||||
@@ -619,7 +779,7 @@ export class TopBar extends React.Component<
|
|||||||
return (
|
return (
|
||||||
<Menu.Item onClick={() => this.wikiTreeLogin()}>
|
<Menu.Item onClick={() => this.wikiTreeLogin()}>
|
||||||
<img
|
<img
|
||||||
src={wikiTreeLogoUrl}
|
src={WIKITREE_LOGO_URL}
|
||||||
alt="WikiTree logo"
|
alt="WikiTree logo"
|
||||||
style={{width: '24px', height: '24px'}}
|
style={{width: '24px', height: '24px'}}
|
||||||
/>
|
/>
|
||||||
@@ -636,7 +796,7 @@ export class TopBar extends React.Component<
|
|||||||
<>
|
<>
|
||||||
<Dropdown.Item onClick={() => this.wikiTreeLogin()}>
|
<Dropdown.Item onClick={() => this.wikiTreeLogin()}>
|
||||||
<img
|
<img
|
||||||
src={wikiTreeLogoUrl}
|
src={WIKITREE_LOGO_URL}
|
||||||
alt="WikiTree logo"
|
alt="WikiTree logo"
|
||||||
style={{width: '24px', height: '24px'}}
|
style={{width: '24px', height: '24px'}}
|
||||||
/>
|
/>
|
||||||
@@ -670,7 +830,7 @@ export class TopBar extends React.Component<
|
|||||||
return (
|
return (
|
||||||
<Menu.Item title={tooltip}>
|
<Menu.Item title={tooltip}>
|
||||||
<img
|
<img
|
||||||
src={wikiTreeLogoUrl}
|
src={WIKITREE_LOGO_URL}
|
||||||
alt="WikiTree logo"
|
alt="WikiTree logo"
|
||||||
style={{width: '24px', height: '24px'}}
|
style={{width: '24px', height: '24px'}}
|
||||||
/>
|
/>
|
||||||
@@ -686,7 +846,7 @@ export class TopBar extends React.Component<
|
|||||||
<>
|
<>
|
||||||
<Menu.Item title={tooltip}>
|
<Menu.Item title={tooltip}>
|
||||||
<img
|
<img
|
||||||
src={wikiTreeLogoUrl}
|
src={WIKITREE_LOGO_URL}
|
||||||
alt="WikiTree logo"
|
alt="WikiTree logo"
|
||||||
style={{width: '24px', height: '24px'}}
|
style={{width: '24px', height: '24px'}}
|
||||||
/>
|
/>
|
||||||
@@ -725,6 +885,7 @@ export class TopBar extends React.Component<
|
|||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
href="https://github.com/PeWu/topola-viewer"
|
href="https://github.com/PeWu/topola-viewer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="menu.github"
|
id="menu.github"
|
||||||
@@ -753,6 +914,7 @@ export class TopBar extends React.Component<
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
href="https://github.com/PeWu/topola-viewer"
|
href="https://github.com/PeWu/topola-viewer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="menu.github"
|
id="menu.github"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"menu.open": "Otwórz",
|
"menu.open": "Otwórz",
|
||||||
"menu.open_file": "Otwórz plik",
|
"menu.open_file": "Otwórz plik",
|
||||||
"menu.load_from_url": "Otwórz URL",
|
"menu.load_from_url": "Otwórz URL",
|
||||||
|
"menu.select_wikitree_id": "WikiTree ID",
|
||||||
"menu.print": "Drukuj",
|
"menu.print": "Drukuj",
|
||||||
"menu.download": "Pobierz",
|
"menu.download": "Pobierz",
|
||||||
"menu.pdf_file": "Plik PDF",
|
"menu.pdf_file": "Plik PDF",
|
||||||
@@ -32,6 +33,10 @@
|
|||||||
"load_from_url.comment": "Dane z podanego adresu URL zostaną załadowane poprzez usługę {link} w celu uniknięcia problemów z CORS.",
|
"load_from_url.comment": "Dane z podanego adresu URL zostaną załadowane poprzez usługę {link} w celu uniknięcia problemów z CORS.",
|
||||||
"load_from_url.cancel": "Anuluj",
|
"load_from_url.cancel": "Anuluj",
|
||||||
"load_from_url.load": "Otwórz",
|
"load_from_url.load": "Otwórz",
|
||||||
|
"select_wikitree_id.title": "Podaj WikiTree ID",
|
||||||
|
"select_wikitree_id.comment": "Wpisz identyfikator profilu {wikiTreeLink}. Przykłady: {example1}, {example2}",
|
||||||
|
"select_wikitree_id.cancel": "Anuluj",
|
||||||
|
"select_wikitree_id.load": "Otwórz",
|
||||||
"gedcom.BAPM": "Chrzest",
|
"gedcom.BAPM": "Chrzest",
|
||||||
"gedcom.BIRT": "Narodziny",
|
"gedcom.BIRT": "Narodziny",
|
||||||
"gedcom.BURI": "Pogrzeb",
|
"gedcom.BURI": "Pogrzeb",
|
||||||
|
|||||||
Reference in New Issue
Block a user