import flatMap from 'array.prototype.flatmap'; import {GedcomEntry} from 'parse-gedcom'; import {FormattedMessage} from 'react-intl'; import {Header, Item} from 'semantic-ui-react'; import { dereference, GedcomData, getData, getFileName, getImageFileEntry, getNonImageFileEntry, mapToSource, resolveFileUrl, } from '../../util/gedcom_util'; import {Config, Ids} from '../config/config'; import {AdditionalFiles, FileEntry} from './additional-files'; import {ALL_SUPPORTED_EVENT_TYPES, Events} from './events'; import {ImmediateFamily} from './immediate-family'; import {MultilineText} from './multiline-text'; import {Sources} from './sources'; import {TranslatedTag} from './translated-tag'; import {WrappedImage} from './wrapped-image'; const EXCLUDED_TAGS = [ ...ALL_SUPPORTED_EVENT_TYPES, 'NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR', 'FACT', ]; function dataDetails(entry: GedcomEntry) { const lines = []; if (entry.data) { lines.push(...getData(entry)); } entry.tree .filter((subentry) => subentry.tag === 'NOTE') .forEach((note) => getData(note).forEach((line) => lines.push({line})), ); if (!lines.length) { return null; } return ( <>
); } function attributeDetails(entry: GedcomEntry) { if (!entry.data) { return null; } const attributeName = entry.tree .filter((subentry) => subentry.tag === 'TYPE') .flatMap((type) => getData(type)) .join() .trim(); const attributeValue = getData(entry).join(' ').trim(); if (attributeName) { return ( <>
{attributeName}: {attributeValue}
); } else { return ( <>
{attributeValue}
); } } function imageDetails( objectEntryReference: GedcomEntry, gedcom: GedcomData, images?: Map, ) { const imageEntry = dereference( objectEntryReference, gedcom, (gedcom) => gedcom.other, ); const imageFileEntry = getImageFileEntry(imageEntry); if (!imageFileEntry || !hasData(imageEntry)) { return null; } return (
); } function sourceDetails( sourceReferenceEntries: GedcomEntry[], gedcom: GedcomData, ) { const sources = sourceReferenceEntries.map((sourceEntryReference) => mapToSource(sourceEntryReference, gedcom), ); if (!sources.length) { return null; } return ( <>
); } function fileDetails( objectEntries: GedcomEntry[], gedcom: GedcomData, images?: Map, ) { const files: FileEntry[] = []; objectEntries .map((objectEntry) => dereference(objectEntry, gedcom, (gedcom) => gedcom.other), ) .forEach((objectEntry) => { const fileEntry = getNonImageFileEntry(objectEntry); if (fileEntry) { files.push({ url: resolveFileUrl(fileEntry.data, images), filename: getFileName(fileEntry), titl: objectEntry.tree.find((entry) => entry.tag === 'TITL')?.data, }); } }); if (!files.length) { return null; } return ( <>
); } function noteDetails(noteEntryReference: GedcomEntry, gedcom: GedcomData) { const noteEntry = dereference( noteEntryReference, gedcom, (gedcom) => gedcom.other, ); if (!noteEntry || !hasData(noteEntry)) { return null; } return ( ( {line} ))} /> ); } function nameDetails(entry: GedcomEntry) { const prefix = entry.tree.find((entry) => entry.tag === 'NPFX')?.data; let given = entry.tree.find((entry) => entry.tag === 'GIVN')?.data; let rufname = entry.tree.find((entry) => entry.tag === '_RUFNAME')?.data; const nickname = entry.tree.find((entry) => entry.tag === 'NICK')?.data; const surnamePrefix = entry.tree.find((entry) => entry.tag === 'SPFX')?.data; const surname = entry.tree.find((entry) => entry.tag === 'SURN')?.data; const suffix = entry.tree.find((entry) => entry.tag === 'NSFX')?.data; // If _RUFNAME is included in GIVN, then replace this part in GIVN with this part in quotation marks, // so that this name is not shown twice. if (given && rufname && given.includes(rufname)) { given = given.replace(rufname, `"${rufname}"`); rufname = undefined; } const fullNameParts = [ prefix, given, rufname && `"${rufname}"`, nickname && `(${nickname})`, surnamePrefix, surname, suffix, ].filter(Boolean); const fullName = fullNameParts.join(' ').trim() || entry.data.replaceAll('/', '') || ''; const nameType = entry.tree.find( (entry) => entry.tag === 'TYPE' && entry.data !== 'Unknown', )?.data; return ( <>
{fullName ? ( fullName ) : ( )}
{fullName && nameType && ( )} ); } function getSectionForEachMatchingEntry( entries: GedcomEntry[], gedcom: GedcomData, tags: string[], detailsFunction: ( entry: GedcomEntry, gedcom: GedcomData, images?: Map, ) => React.ReactNode | null, images?: Map, ): React.ReactNode[] { return flatMap(tags, (tag) => entries .filter((entry) => entry.tag === tag) .map((entry) => detailsFunction(entry, gedcom, images)), ) .filter((element) => element !== null) .map((element, index) => ( {element} )); } function combineAllMatchingEntriesIntoSingleSection( entries: GedcomEntry[], gedcom: GedcomData, tags: string[], detailsFunction: ( entries: GedcomEntry[], gedcom: GedcomData, images?: Map, ) => React.ReactNode | null, images?: Map, ): React.ReactNode { const entriesWithMatchingTag = flatMap(tags, (tag) => entries.filter((entry) => entry.tag === tag), ).filter((element) => element !== null); const sectionWithDetails = entriesWithMatchingTag.length ? detailsFunction(entriesWithMatchingTag, gedcom, images) : null; if (!sectionWithDetails) { return null; } return ( {sectionWithDetails} ); } /** * Returns true if there is displayable information in this entry. * Returns false if there is no data in this entry or this is only a reference * to another entry. */ function hasData(entry: GedcomEntry) { return entry.tree.length > 0 || (entry.data && !entry.data.startsWith('@')); } function getOtherSections(entries: GedcomEntry[], gedcom: GedcomData) { return entries .filter((entry) => !EXCLUDED_TAGS.includes(entry.tag)) .map((entry) => dereference(entry, gedcom, (gedcom) => gedcom.other)) .filter(hasData) .map((entry) => dataDetails(entry)) .filter((element) => element !== null) .map((element, index) => ( {element} )); } function getSectionForId(indi: string): React.ReactNode { return (
{indi}
); } interface Props { gedcom: GedcomData; indi: string; config: Config; images?: Map; } export function Details(props: Props) { const entries = props.gedcom.indis[props.indi].tree; return (
{getSectionForEachMatchingEntry( entries, props.gedcom, ['NAME'], nameDetails, )} {getSectionForEachMatchingEntry( entries, props.gedcom, ['OBJE'], imageDetails, props.images, )} {props.config.id === Ids.SHOW ? getSectionForId(props.indi) : null} {getSectionForEachMatchingEntry( entries, props.gedcom, ['FACT'], attributeDetails, )} {getOtherSections(entries, props.gedcom)} {getSectionForEachMatchingEntry( entries, props.gedcom, ['NOTE'], noteDetails, )} {combineAllMatchingEntriesIntoSingleSection( entries, props.gedcom, ['OBJE'], fileDetails, props.images, )} {combineAllMatchingEntriesIntoSingleSection( entries, props.gedcom, ['SOUR'], sourceDetails, )}
); }