Refactored the Events component

Split collecting data from rendering HTML
This commit is contained in:
Przemek Wiech 2022-02-28 22:59:58 +01:00
parent 1a8e9e7edf
commit 6107aef874

View File

@ -1,7 +1,7 @@
import * as queryString from 'query-string'; import * as queryString from 'query-string';
import flatMap from 'array.prototype.flatmap'; import flatMap from 'array.prototype.flatmap';
import {calcAge} from '../util/age_util'; import {calcAge} from '../util/age_util';
import {compareDates, translateDate} from '../util/date_util'; import {compareDates, formatDateOrRange} from '../util/date_util';
import {DateOrRange, getDate} from 'topola'; import {DateOrRange, getDate} from 'topola';
import {dereference, GedcomData, getData, getName} from '../util/gedcom_util'; import {dereference, GedcomData, getData, getName} from '../util/gedcom_util';
import {GedcomEntry} from 'parse-gedcom'; import {GedcomEntry} from 'parse-gedcom';
@ -37,13 +37,13 @@ interface Props {
entries: GedcomEntry[]; entries: GedcomEntry[];
} }
interface Event { interface EventData {
type: string; type: string;
date: DateOrRange | undefined; date?: DateOrRange;
header: JSX.Element; age?: string;
subHeader: JSX.Element | null; personLink?: GedcomEntry;
place: JSX.Element | null; place?: string[];
notes: JSX.Element | null; notes: string[][];
} }
const EVENT_TAGS = [ const EVENT_TAGS = [
@ -59,111 +59,65 @@ const EVENT_TAGS = [
const FAMILY_EVENT_TAGS = ['MARR', 'DIV']; const FAMILY_EVENT_TAGS = ['MARR', 'DIV'];
function eventHeader(tag: string, date: GedcomEntry | null, intl: IntlShape) { function EventHeader(props: {event: EventData}) {
const intl = useIntl();
return ( return (
<div> <div>
<span style={{textTransform: 'uppercase'}} className="ui small header"> <span style={{textTransform: 'uppercase'}} className="ui small header">
<TranslatedTag tag={tag} /> <TranslatedTag tag={props.event.type} />
</span> </span>
{date && date.data ? ( {props.event.date ? (
<span className="ui sub header right floated"> <span className="ui sub header right floated">
{translateDate(date.data, intl)} {formatDateOrRange(props.event.date, intl)}
</span> </span>
) : null} ) : null}
</div> </div>
); );
} }
function eventFamilyDetails( function getSpouse(indi: string, familyEntry: GedcomEntry, gedcom: GedcomData) {
indi: string,
familyEntry: GedcomEntry,
gedcom: GedcomData,
) {
const spouseReference = familyEntry.tree const spouseReference = familyEntry.tree
.filter((familySubEntry) => ['WIFE', 'HUSB'].includes(familySubEntry.tag)) .filter((familySubEntry) => ['WIFE', 'HUSB'].includes(familySubEntry.tag))
.find((familySubEntry) => !familySubEntry.data.includes(indi)); .find((familySubEntry) => !familySubEntry.data.includes(indi));
if (spouseReference) { if (!spouseReference) {
const spouse = dereference( return undefined;
spouseReference,
gedcom,
(gedcom) => gedcom.indis,
);
return <PersonLink person={spouse} />;
} }
return null; return dereference(spouseReference, gedcom, (gedcom) => gedcom.indis);
} }
function eventAdditionalDetails( function getAge(
eventEntry: GedcomEntry, eventEntry: GedcomEntry,
indi: string, indi: string,
gedcom: GedcomData, gedcom: GedcomData,
intl: IntlShape, intl: IntlShape,
) { ): string | undefined {
if (eventEntry.tag === 'DEAT') { if (eventEntry.tag !== 'DEAT') {
const deathDate = resolveDate(eventEntry); return undefined;
const birthDate = gedcom.indis[indi].tree
.filter((indiSubEntry) => indiSubEntry.tag === 'BIRT')
.map((birthEvent) => resolveDate(birthEvent))
.find((topolaDate) => topolaDate);
if (birthDate && deathDate) {
return (
<div className="meta">
{calcAge(birthDate?.data, deathDate?.data, intl)}
</div>
);
}
} }
return null; const deathDate = resolveDate(eventEntry);
const birthDate = gedcom.indis[indi].tree
.filter((indiSubEntry) => indiSubEntry.tag === 'BIRT')
.map((birthEvent) => resolveDate(birthEvent))
.find((topolaDate) => topolaDate);
if (!birthDate || !deathDate) {
return undefined;
}
return calcAge(birthDate?.data, deathDate?.data, intl);
} }
function eventPlace(entry: GedcomEntry) { function eventPlace(entry: GedcomEntry) {
const place = entry.tree.find((subEntry) => subEntry.tag === 'PLAC'); const place = entry.tree.find((subEntry) => subEntry.tag === 'PLAC');
if (place && place.data) { return place?.data ? getData(place) : undefined;
return <div className="description">{getData(place)}</div>;
}
return null;
} }
function eventNotes(entry: GedcomEntry, gedcom: GedcomData) { function eventNotes(entry: GedcomEntry, gedcom: GedcomData): string[][] {
const notes = entry.tree return entry.tree
.filter((subentry) => ['NOTE', 'TYPE'].includes(subentry.tag)) .filter((subentry) => ['NOTE', 'TYPE'].includes(subentry.tag))
.map((note) => dereference(note, gedcom, (gedcom) => gedcom.other)) .map((note) => dereference(note, gedcom, (gedcom) => gedcom.other))
.map((note) => noteDetails(note)); .map((note) => getData(note));
if (notes && notes.length) {
return (
<div className="description">
{notes.map((note, index) => (
<div key={index}>{note}</div>
))}
</div>
);
}
return null;
}
function noteDetails(entry: GedcomEntry) {
return (
<MultilineText
lines={getData(entry).map((line, index) => (
<i key={index}>{line}</i>
))}
/>
);
}
function eventDetails(event: Event) {
return (
<div className="content">
{event.header}
{event.subHeader}
{event.place}
{event.notes}
</div>
);
} }
function toEvent( function toEvent(
@ -171,9 +125,9 @@ function toEvent(
gedcom: GedcomData, gedcom: GedcomData,
indi: string, indi: string,
intl: IntlShape, intl: IntlShape,
): Event[] { ): EventData[] {
if (entry.tag === 'FAMS') { if (entry.tag === 'FAMS') {
return toFamilyEvents(entry, gedcom, indi, intl); return toFamilyEvents(entry, gedcom, indi);
} }
return toIndiEvent(entry, gedcom, indi, intl); return toIndiEvent(entry, gedcom, indi, intl);
} }
@ -183,14 +137,13 @@ function toIndiEvent(
gedcom: GedcomData, gedcom: GedcomData,
indi: string, indi: string,
intl: IntlShape, intl: IntlShape,
): Event[] { ): EventData[] {
const date = resolveDate(entry) || null; const date = resolveDate(entry) || null;
return [ return [
{ {
date: date ? getDate(date.data) : undefined, date: date ? getDate(date.data) : undefined,
type: entry.tag, type: entry.tag,
header: eventHeader(entry.tag, date, intl), age: getAge(entry, indi, gedcom, intl),
subHeader: eventAdditionalDetails(entry, indi, gedcom, intl),
place: eventPlace(entry), place: eventPlace(entry),
notes: eventNotes(entry, gedcom), notes: eventNotes(entry, gedcom),
}, },
@ -205,8 +158,7 @@ function toFamilyEvents(
entry: GedcomEntry, entry: GedcomEntry,
gedcom: GedcomData, gedcom: GedcomData,
indi: string, indi: string,
intl: IntlShape, ): EventData[] {
): Event[] {
const family = dereference(entry, gedcom, (gedcom) => gedcom.fams); const family = dereference(entry, gedcom, (gedcom) => gedcom.fams);
return flatMap(FAMILY_EVENT_TAGS, (tag) => return flatMap(FAMILY_EVENT_TAGS, (tag) =>
family.tree.filter((entry) => entry.tag === tag), family.tree.filter((entry) => entry.tag === tag),
@ -215,14 +167,43 @@ function toFamilyEvents(
return { return {
date: date ? getDate(date.data) : undefined, date: date ? getDate(date.data) : undefined,
type: familyMarriageEvent.tag, type: familyMarriageEvent.tag,
header: eventHeader(familyMarriageEvent.tag, date, intl), personLink: getSpouse(indi, family, gedcom),
subHeader: eventFamilyDetails(indi, family, gedcom),
place: eventPlace(familyMarriageEvent), place: eventPlace(familyMarriageEvent),
notes: eventNotes(familyMarriageEvent, gedcom), notes: eventNotes(familyMarriageEvent, gedcom),
}; };
}); });
} }
function Event(props: {event: EventData}) {
return (
<div className="ui attached item">
<div className="content">
<EventHeader event={props.event} />
{!!props.event.age && <div className="meta">{props.event.age}</div>}
{!!props.event.personLink && (
<PersonLink person={props.event.personLink} />
)}
{!!props.event.place && (
<div className="description">{props.event.place}</div>
)}
{!!props.event.notes.length && (
<div className="description">
{props.event.notes.map((note, index) => (
<div key={index}>
<MultilineText
lines={note.map((line, index) => (
<i key={index}>{line}</i>
))}
/>
</div>
))}
</div>
)}
</div>
</div>
);
}
export function Events(props: Props) { export function Events(props: Props) {
const intl = useIntl(); const intl = useIntl();
@ -231,16 +212,13 @@ export function Events(props: Props) {
.filter((entry) => entry.tag === tag) .filter((entry) => entry.tag === tag)
.map((eventEntry) => toEvent(eventEntry, props.gedcom, props.indi, intl)) .map((eventEntry) => toEvent(eventEntry, props.gedcom, props.indi, intl))
.flatMap((events) => events) .flatMap((events) => events)
.sort((event1, event2) => compareDates(event1.date, event2.date)) .sort((event1, event2) => compareDates(event1.date, event2.date)),
.map((event) => eventDetails(event)),
); );
if (events && events.length) { if (events.length) {
return ( return (
<div className="ui segment divided items"> <div className="ui segment divided items">
{events.map((eventElement, index) => ( {events.map((event, index) => (
<div className="ui attached item" key={index}> <Event event={event} key={index} />
{eventElement}
</div>
))} ))}
</div> </div>
); );