Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {components} from 'react-select';
import {connect} from 'react-redux';
import {convertMapToObject} from '../../helpers/utils';
import {validateAuthorCreditSection} from '../validators/common';
import {RecentlyUsed} from '../../unified-form/common/recently-used';


type OwnProps = {
Expand Down Expand Up @@ -181,6 +182,7 @@ function AuthorCreditSection({
onChange={onChangeHandler}
{...rest}
type="author"
recentlyUsedEntityType="authors"
/>
</div>
<InputGroup.Append>{editButton}</InputGroup.Append>
Expand Down Expand Up @@ -233,6 +235,9 @@ function mapStateToProps(rootState, {type}): StateProps {
function mapDispatchToProps(dispatch: Dispatch<Action>): DispatchProps {
return {
onAuthorChange: (value) => {
if(value && value.id && value.text){
RecentlyUsed.addItem('authors', {id: value.id, name: value.text});
}
dispatch(updateCreditAuthorValue(-1, value));
},
onClearHandler: (aid) => dispatch(clearAuthor(aid)),
Expand Down
17 changes: 15 additions & 2 deletions src/client/entity-editor/author-section/author-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import EntitySearchFieldOption from '../common/entity-search-field-option';
import type {Map} from 'immutable';
import Select from 'react-select';
import {connect} from 'react-redux';
import {RecentlyUsed} from '../../unified-form/common/recently-used';


type AuthorType = {
Expand Down Expand Up @@ -235,6 +236,7 @@ function AuthorSection({
label={beginAreaLabel}
type="area"
value={beginAreaValue}
recentlyUsedEntityType="areas"
onChange={onBeginAreaChange}
/>
</Col>
Expand Down Expand Up @@ -269,6 +271,7 @@ function AuthorSection({
label={endAreaLabel}
type="area"
value={endAreaValue}
recentlyUsedEntityType="areas"
onChange={onEndAreaChange}
/>
</Col>
Expand Down Expand Up @@ -321,10 +324,20 @@ function mapStateToProps(rootState, {authorTypes}: OwnProps): StateProps {

function mapDispatchToProps(dispatch: Dispatch<Action>): DispatchProps {
return {
onBeginAreaChange: (value) => dispatch(updateBeginArea(value)),
onBeginAreaChange: (value) => {
if(value && value.id && value.text){
RecentlyUsed.addItem('areas', {id: value.id, name: value.text});
}
return dispatch(updateBeginArea(value));
},
onBeginDateChange: (beginDate) =>
dispatch(debouncedUpdateBeginDate(beginDate)),
onEndAreaChange: (value) => dispatch(updateEndArea(value)),
onEndAreaChange: (value) => {
if(value && value.id && value.text){
RecentlyUsed.addItem('areas', {id: value.id, name: value.text});
}
return dispatch(updateEndArea(value));
},
onEndDateChange: (endDate) =>
dispatch(debouncedUpdateEndDate(endDate)),
onEndedChange: (event) =>
Expand Down
111 changes: 106 additions & 5 deletions src/client/entity-editor/common/entity-search-field-option.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
import LinkedEntitySelect from './linked-entity-select';
import PropTypes from 'prop-types';
import React from 'react';
import {RecentlyUsed} from '../../unified-form/common/recently-used';
import SelectAsync from 'react-select/async';
import ValidationLabel from '../common/validation-label';
import _ from 'lodash';
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons';
import {isValidBBID} from '../../../common/helpers/utils';
import makeImmutable from './make-immutable';
import request from 'superagent';

Check warning on line 33 in src/client/entity-editor/common/entity-search-field-option.js

View workflow job for this annotation

GitHub Actions / ESLint

src/client/entity-editor/common/entity-search-field-option.js#L33

Expected 2 empty lines after import statement not followed by another import (import/newline-after-import)


const ImmutableAsyncSelect = makeImmutable(SelectAsync);

class EntitySearchFieldOption extends React.Component {
Expand All @@ -44,6 +44,25 @@
this.entityToOption = this.entityToOption.bind(this);
}

static entityTypeMappings = {
// Saving
Area: 'areas',
Author: 'authors',
Edition: 'editions',
EditionGroup: 'editiongroups',
Publisher: 'publishers',
Series: 'series',
Work: 'works',
// Loading
areas: 'Area',
authors: 'Author',
editiongroups: 'EditionGroup',
editions: 'Edition',
publishers: 'Publisher',
series: 'Series',
works: 'Work'
};

/**
* Determines whether an entity provided to the EntitySearch component is an
* Area, using the present attributes.
Expand Down Expand Up @@ -86,11 +105,66 @@
return entityOption;
}

async fetchOptions(query) {
if (!query) {
getRecentlyUsedOptions() {
if (!this.props.recentlyUsedEntityType) {
return [];
}
const entityTypes = Array.isArray(this.props.recentlyUsedEntityType) ?
this.props.recentlyUsedEntityType : [this.props.recentlyUsedEntityType];
const validEntityTypes = entityTypes.filter(type =>
type && typeof type === 'string' && type.trim().length > 0);
if (validEntityTypes.length === 0) {
return [];
}
const allRecentlyUsedItems = [];
validEntityTypes.forEach(entityType => {
const items = RecentlyUsed.getItems(entityType);
if (Array.isArray(items)) {
items.forEach(item => {
allRecentlyUsedItems.push({
...item,
_sourceType: entityType
});
});
}
});
if (allRecentlyUsedItems.length === 0) {
return [];
}
const uniqueItems = _.uniqBy(allRecentlyUsedItems, 'id').slice(0, 10);
return uniqueItems.map(item => {
const singularType = EntitySearchFieldOption.entityTypeMappings[item._sourceType] || item._sourceType.replace(/s$/, '');
return {
options: []
id: item.id,
isRecentlyUsed: true,
text: item.name,
type: singularType
};
});
}

getDefaultOptions() {
const recentOptions = this.getRecentlyUsedOptions();
if (recentOptions.length === 0) {
return true;
}
return [{
label: 'Recently Used',
options: recentOptions
}];
}

async fetchOptions(query) {
const recentOptions = this.getRecentlyUsedOptions();
const uniqueRecentOptions = _.uniqBy(recentOptions, 'id').slice(0, 10);
if (!query) {
if (uniqueRecentOptions.length === 0) {
return [];
}
return [{
label: 'Recently Used',
options: uniqueRecentOptions
}];
}
let manipulatedQuery = query;
const bookbrainzURLRegex =
Expand All @@ -104,6 +178,13 @@
if (entity && typeof this.props.onChange === 'function' && (_.snakeCase(entity.type) === this.props.type ||
(_.isArray(this.props.type) && this.props.type.includes(entity.type)))) {
const entityOption = this.entityToOption(entity);
if (entityOption.id && entityOption.text && entityOption.type) {
const storageKey = EntitySearchFieldOption.entityTypeMappings[entityOption.type] || `${entityOption.type.toLowerCase()}s`;
RecentlyUsed.addItem(storageKey, {
id: entityOption.id,
name: entityOption.text
});
}
const newValue = this.props.isMulti ? [...this.props.value, entityOption] : entityOption;
this.props.onChange(newValue);
this.selectRef.current.blur();
Expand All @@ -123,10 +204,23 @@
...this.props.filters
);
const filteredOptions = response.body.filter(combinedFilters);
return filteredOptions.map(this.entityToOption);
const searchResults = filteredOptions.map(this.entityToOption);
if (uniqueRecentOptions.length > 0) {
return [
{
label: 'Recently Used',
options: uniqueRecentOptions
},
{
label: 'Search Results',
options: searchResults
}
];
}
return searchResults;
}

renderInputGroup({buttonAfter, help, wrappedSelect, ...props}) {

Check warning on line 223 in src/client/entity-editor/common/entity-search-field-option.js

View workflow job for this annotation

GitHub Actions / ESLint

src/client/entity-editor/common/entity-search-field-option.js#L223

'props' is defined but never used (@typescript-eslint/no-unused-vars)
if (!buttonAfter) {
return (
<>
Expand Down Expand Up @@ -175,6 +269,7 @@
<SelectWrapper
{...this.props}
blurInputOnSelect
cacheOptions
isClearable
className={`Select${this.props.className ? ` ${this.props.className}` : ''}`}
classNamePrefix="react-select"
Expand All @@ -184,6 +279,7 @@
...this.props.customComponents &&
this.props.customComponents
}}
defaultOptions={this.props.recentlyUsedEntityType ? this.getDefaultOptions() : true}
filterOptions={false}
getOptionLabel={this.getOptionLabel}
getOptionValue={this.getOptionValue}
Expand Down Expand Up @@ -220,6 +316,10 @@
label: PropTypes.string,
languageOptions: PropTypes.array,
onChange: PropTypes.func.isRequired,
recentlyUsedEntityType: PropTypes.oneOfType([
PropTypes.string,
PropTypes.arrayOf(PropTypes.string)
]),
tooltipText: PropTypes.string,
type: PropTypes.oneOfType([
PropTypes.string,
Expand All @@ -241,6 +341,7 @@
isMulti: false,
label: '',
languageOptions: [],
recentlyUsedEntityType: null,
tooltipText: null,
value: null
};
Expand Down
23 changes: 22 additions & 1 deletion src/client/entity-editor/edition-section/edition-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {clearEditionGroups} from '../../unified-form/detail-tab/action';
import {connect} from 'react-redux';
import {entityToOption} from '../../helpers/entity';
import makeImmutable from '../common/make-immutable';
import {RecentlyUsed} from '../../unified-form/common/recently-used';


const ImmutableLanguageField = makeImmutable(LanguageField);
Expand Down Expand Up @@ -240,6 +241,7 @@ function EditionSection({
}
type="editionGroup"
value={editionGroupValue}
recentlyUsedEntityType="editiongroups"
onChange={onEditionGroupChange}
{...rest}
/>
Expand Down Expand Up @@ -350,6 +352,7 @@ function EditionSection({
label="Publisher"
type="publisher"
value={publisherValue}
recentlyUsedEntityType="publishers"
onChange={onPublisherChange}
/>
</Col>}
Expand Down Expand Up @@ -516,6 +519,12 @@ function mapDispatchToProps(dispatch: Dispatch<Action>): DispatchProps {
event.target.value ? parseInt(event.target.value, 10) : null
)),
onEditionGroupChange: (value, action) => {
if(value && value.id && value.text){
RecentlyUsed.addItem('editiongroups', {
id: value.id,
name: value.text
});
}
// If the user selected a new edition group, we need to clear the old one
if (['clear', 'pop-value', 'select-option'].includes(action.action)) {
dispatch(clearEditionGroups());
Expand All @@ -539,7 +548,19 @@ function mapDispatchToProps(dispatch: Dispatch<Action>): DispatchProps {
onPagesChange: (event) => dispatch(debouncedUpdatePages(
event.target.value ? parseInt(event.target.value, 10) : null
)),
onPublisherChange: (value) => dispatch(updatePublisher(Object.fromEntries(value.map((pub, index) => [index, pub])))),
onPublisherChange: (value) => {
if(value && Array.isArray(value)){
value.forEach(publisher => {
if(publisher && publisher.id && publisher.text){
RecentlyUsed.addItem('publishers', {
id: publisher.id,
name: publisher.text
});
}
});
}
dispatch(updatePublisher(Object.fromEntries(value.map((pub, index) => [index, pub]))));
},
onReleaseDateChange: (releaseDate) =>
dispatch(debouncedUpdateReleaseDate(releaseDate)),
onStatusChange: (value: {value: number} | null) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {Map} from 'immutable';
import Select from 'react-select';
import {connect} from 'react-redux';
import {isNullDate} from '../../helpers/utils';
import { RecentlyUsed } from '../../unified-form/common/recently-used';


type PublisherType = {
Expand Down Expand Up @@ -164,6 +165,7 @@ function PublisherSection({
tooltipText="Country or place the publisher is registered in"
type="area"
value={areaValue}
recentlyUsedEntityType="areas"
onChange={onAreaChange}
/>
</Col>
Expand Down Expand Up @@ -230,7 +232,12 @@ function mapStateToProps(rootState): StateProps {

function mapDispatchToProps(dispatch: Dispatch<Action>): DispatchProps {
return {
onAreaChange: (value) => dispatch(updateArea(value)),
onAreaChange: (value) => {
if(value && value.id && value.text){
RecentlyUsed.addItem('areas', {id: value.id, name: value.text});
}
return dispatch(updateArea(value));
},
onBeginDateChange: (beginDate) => {
dispatch(debouncedUpdateBeginDate(beginDate));
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import ReactSelect from 'react-select';
import RelationshipSelect from './relationship-select';
import _ from 'lodash';
import {getEntityLink} from '../../../common/helpers/utils';
import {RecentlyUsed} from '../../unified-form/common/recently-used';


function isValidRelationship(relationship: _Relationship) {
Expand Down Expand Up @@ -274,6 +275,13 @@ class RelationshipModal
}

handleEntityChange = (value: EntitySearchResult) => {
if(value && value.id && value.text && value.type){
const storageKey = EntitySearchFieldOption.entityTypeMappings[value.type] || `${value.type.toLowerCase()}s`;
RecentlyUsed.addItem(storageKey, {
id: value.id,
name: value.text
});
}
this.setState({
relationship: null,
relationshipType: null,
Expand All @@ -282,6 +290,7 @@ class RelationshipModal
};

handleRelationshipTypeChange = (value: _Relationship) => {

this.setState({
relationship: value,
relationshipType: value.relationshipType
Expand Down Expand Up @@ -378,6 +387,7 @@ class RelationshipModal
name="entity"
type={types}
value={this.state.targetEntity}
recentlyUsedEntityType={['works', 'authors', 'editions', 'publishers', 'series']}
onChange={this.handleEntityChange}
/>
);
Expand Down
Loading
Loading