Skip to content

Commit

Permalink
Merge pull request #2863 from reosarevok/MBS-12622
Browse files Browse the repository at this point in the history
MBS-12622: Show if URL / URL relationship has pending edits in links editor
  • Loading branch information
reosarevok authored Sep 18, 2023
2 parents 5f316eb + 796a3d2 commit 93d8363
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 63 deletions.
50 changes: 50 additions & 0 deletions root/static/scripts/edit/components/EntityPendingEditsWarning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* @flow strict-local
* Copyright (C) 2023 MetaBrainz Foundation
*
* This file is part of MusicBrainz, the open internet music database,
* and is licensed under the GPL version 2, or (at your option) any
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
*/

import * as React from 'react';

import openEditsForEntityIconUrl
from '../../../images/icons/open_edits_for_entity.svg';
import entityHref from '../../common/utility/entityHref.js';

import Tooltip from './Tooltip.js';

type PropsT = {
+entity: RelatableEntityT,
};

const EntityPendingEditsWarning = ({
entity,
}: PropsT): React$Element<typeof React.Fragment> | null => {
const hasPendingEdits = Boolean(entity.editsPending);
const openEditsLink = entityHref(entity, '/open_edits');

return hasPendingEdits && nonEmpty(openEditsLink) ? (
<>
{' '}
<Tooltip
content={exp.l(
'This entity has {edits_link|pending edits}.',
{edits_link: openEditsLink},
)}
target={
<img
alt={l('This entity has pending edits.')}
className="info"
height={16}
src={openEditsForEntityIconUrl}
style={{verticalAlign: 'middle'}}
/>
}
/>
</>
) : null;
};

export default EntityPendingEditsWarning;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* @flow strict-local
* Copyright (C) 2023 MetaBrainz Foundation
*
* This file is part of MusicBrainz, the open internet music database,
* and is licensed under the GPL version 2, or (at your option) any
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
*/

import * as React from 'react';

import openEditsForRelIconUrl
from '../../../images/icons/open_edits_for_rel.svg';
import type {
RelationshipStateT,
} from '../../relationship-editor/types.js';
import getOpenEditsLink
from '../../relationship-editor/utility/getOpenEditsLink.js';
import type {
LinkRelationshipT,
} from '../externalLinks.js';

import Tooltip from './Tooltip.js';

type PropsT = {
+relationship: LinkRelationshipT | RelationshipStateT,
};

const RelationshipPendingEditsWarning = ({
relationship,
}: PropsT): React$Element<typeof React.Fragment> | null => {
const hasPendingEdits = relationship.editsPending;
const openEditsLink = getOpenEditsLink(relationship);

return hasPendingEdits && nonEmpty(openEditsLink) ? (
<>
{' '}
<Tooltip
content={exp.l(
'This relationship has {edit_search|pending edits}.',
{edit_search: openEditsLink},
)}
target={
<img
alt={l('This relationship has pending edits.')}
className="info"
height={16}
src={openEditsForRelIconUrl}
style={{verticalAlign: 'middle'}}
/>
}
/>
</>
) : null;
};

export default RelationshipPendingEditsWarning;
51 changes: 42 additions & 9 deletions root/static/scripts/edit/externalLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ import {
} from '../relationship-editor/utility/prepareHtmlFormSubmission.js';
import {isMalware} from '../url/utility/isGreyedOut.js';

import EntityPendingEditsWarning
from './components/EntityPendingEditsWarning.js';
import ExternalLinkAttributeDialog
from './components/ExternalLinkAttributeDialog.js';
import HelpIcon from './components/HelpIcon.js';
import RelationshipPendingEditsWarning
from './components/RelationshipPendingEditsWarning.js';
import RemoveButton from './components/RemoveButton.js';
import URLInputPopover from './components/URLInputPopover.js';
import withLoadedTypeInfo from './components/withLoadedTypeInfo.js';
Expand Down Expand Up @@ -79,6 +83,18 @@ type LinkTypeOptionT = {
export type LinkStateT = $ReadOnly<{
...DatePeriodRoleT,
+deleted: boolean,
+editsPending: boolean,
+entity0?:
| RelatableEntityT
| {
+entityType: RelatableEntityTypeT,
+id?: void,
+isNewEntity?: true,
+name?: string,
+orderingTypeID?: number,
+relationships?: void,
},
+entity1?: RelatableEntityT,
+pendingTypes?: $ReadOnlyArray<number>,
+rawUrl: string,
// New relationships will use a unique string ID like "new-1".
Expand Down Expand Up @@ -139,7 +155,7 @@ export class _ExternalLinksEditor
const sourceData = props.sourceData;
const sourceType = sourceData.entityType;
const entityTypes = [sourceType, 'url'].sort().join('-');
let initialLinks = parseRelationships(sourceData.relationships);
let initialLinks = parseRelationships(sourceData);

initialLinks.sort(function (a, b) {
const typeA = a.type && linkedEntities.link_type[a.type];
Expand Down Expand Up @@ -923,7 +939,7 @@ export class _ExternalLinksEditor
* The first element of tuple `item` is not the URL
* when the URL is not submitted therefore isn't grouped.
*/
const {url, rawUrl} = relationships[0];
const {url, rawUrl, entity1} = relationships[0];
const isLastLink = index === linksByUrl.length - 1;
const links = [...relationships];
const linkIndexes = [];
Expand Down Expand Up @@ -1058,6 +1074,7 @@ export class _ExternalLinksEditor
relationships={links}
typeOptions={typeOptions}
url={url}
urlEntity={entity1}
urlMatchesType={urlMatchesType}
validateLink={(link) => this.validateLink(link)}
/>
Expand Down Expand Up @@ -1237,6 +1254,7 @@ const ExternalLinkRelationship =
{link.url && !link.error && !hasUrlError
? <TypeDescription type={link.type} url={link.url} />
: null}
<RelationshipPendingEditsWarning relationship={link} />
{hasDate ? (
<span className="date-period">
{' '}
Expand Down Expand Up @@ -1300,6 +1318,7 @@ type LinkProps = {
+relationships: $ReadOnlyArray<LinkRelationshipT>,
+typeOptions: $ReadOnlyArray<LinkTypeOptionT>,
+url: string,
+urlEntity?: RelatableEntityT,
+urlMatchesType: boolean,
+validateLink: (LinkRelationshipT | LinkStateT) => ErrorT | null,
};
Expand Down Expand Up @@ -1442,6 +1461,9 @@ export class ExternalLink extends React.Component<LinkProps> {
{props.url}
</a>
)}
{props.urlEntity ? (
<EntityPendingEditsWarning entity={props.urlEntity} />
) : null}
{props.url && props.duplicate !== null ? (
<div
className="error field-error"
Expand Down Expand Up @@ -1550,8 +1572,11 @@ export const ExternalLinksEditor:
const defaultLinkState: LinkStateT = {
begin_date: EMPTY_PARTIAL_DATE,
deleted: false,
editsPending: false,
end_date: EMPTY_PARTIAL_DATE,
ended: false,
entity0: undefined,
entity1: undefined,
rawUrl: '',
relationship: null,
submitted: false,
Expand Down Expand Up @@ -1608,15 +1633,18 @@ const isVideoAttribute =
(attr: LinkAttrT) => attr.type.gid === VIDEO_ATTRIBUTE_GID;

export function parseRelationships(
relationships?: $ReadOnlyArray<RelationshipT | {
+id: null,
+linkTypeID?: number,
+target: {
+entityType: 'url',
+name: string,
sourceData?:
| RelatableEntityT
| {
+entityType: RelatableEntityTypeT,
+id?: void,
+isNewEntity?: true,
+name?: string,
+orderingTypeID?: number,
+relationships?: void,
},
}>,
): Array<LinkStateT> {
const relationships = sourceData?.relationships;
if (!relationships) {
return [];
}
Expand All @@ -1626,8 +1654,11 @@ export function parseRelationships(
accum.push({
begin_date: data.begin_date || EMPTY_PARTIAL_DATE,
deleted: false,
editsPending: data.editsPending,
end_date: data.end_date || EMPTY_PARTIAL_DATE,
ended: data.ended || false,
entity0: sourceData || undefined,
entity1: target,
rawUrl: target.name,
relationship: data.id,
submitted: true,
Expand Down Expand Up @@ -1749,6 +1780,8 @@ type InitialOptionsT = {
+entityType: RelatableEntityTypeT,
+id?: void,
+isNewEntity?: true,
+name?: string,
+orderingTypeID?: number,
+relationships?: void,
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@
import * as React from 'react';
import * as tree from 'weight-balanced-tree';

import openEditsForEntityIconUrl
from '../../../images/icons/open_edits_for_entity.svg';
import openEditsForRelIconUrl
from '../../../images/icons/open_edits_for_rel.svg';
import ButtonPopover from '../../common/components/ButtonPopover.js';
import DescriptiveLink from '../../common/components/DescriptiveLink.js';
import {bracketedText} from '../../common/utility/bracketed.js';
import {displayLinkAttributesText}
from '../../common/utility/displayLinkAttribute.js';
import entityHref from '../../common/utility/entityHref.js';
import {
performReactUpdateAndMaintainFocus,
} from '../../common/utility/focusManagement.js';
Expand All @@ -29,7 +24,10 @@ import {
} from '../../common/utility/isLinkTypeDirectionOrderable.js';
import relationshipDateText
from '../../common/utility/relationshipDateText.js';
import Tooltip from '../../edit/components/Tooltip.js';
import EntityPendingEditsWarning
from '../../edit/components/EntityPendingEditsWarning.js';
import RelationshipPendingEditsWarning
from '../../edit/components/RelationshipPendingEditsWarning.js';
import {
getPhraseAndExtraAttributesText,
} from '../../edit/utility/linkPhrase.js';
Expand All @@ -46,7 +44,6 @@ import type {
RelationshipEditorActionT,
} from '../types/actions.js';
import getLinkPhrase from '../utility/getLinkPhrase.js';
import getOpenEditsLink from '../utility/getOpenEditsLink.js';
import getRelationshipKey from '../utility/getRelationshipKey.js';
import getRelationshipLinkType from '../utility/getRelationshipLinkType.js';
import getRelationshipStatusName
Expand Down Expand Up @@ -83,9 +80,6 @@ const RelationshipItem = (React.memo<PropsT>(({
const [sourceCredit, targetCredit] = backward
? [relationship.entity1_credit, relationship.entity0_credit]
: [relationship.entity0_credit, relationship.entity1_credit];
const relHasPendingEdits = relationship.editsPending;
const targetHasPendingEdits = Boolean(target.editsPending);
const openEditsLink = getOpenEditsLink(relationship);
const isRemoved = relationship._status === REL_STATUS_REMOVE;
const removeButtonId =
'remove-relationship-' + getRelationshipKey(relationship);
Expand Down Expand Up @@ -285,46 +279,8 @@ const RelationshipItem = (React.memo<PropsT>(({
})
)
: targetDisplay}
{targetHasPendingEdits ? (
<>
{' '}
<Tooltip
content={exp.l(
'This entity has {edits_link|pending edits}.',
{edits_link: entityHref(target, '/open_edits')},
)}
target={
<img
alt={l('This entity has pending edits.')}
className="info"
height={16}
src={openEditsForEntityIconUrl}
style={{verticalAlign: 'middle'}}
/>
}
/>
</>
) : null}
{relHasPendingEdits && nonEmpty(openEditsLink) ? (
<>
{' '}
<Tooltip
content={exp.l(
'This relationship has {edit_search|pending edits}.',
{edit_search: openEditsLink},
)}
target={
<img
alt={l('This relationship has pending edits.')}
className="info"
height={16}
src={openEditsForRelIconUrl}
style={{verticalAlign: 'middle'}}
/>
}
/>
</>
) : null}
<EntityPendingEditsWarning entity={target} />
<RelationshipPendingEditsWarning relationship={relationship} />
{datesAndAttributes}
</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* @flow strict
* @flow strict-local
* Copyright (C) 2022 MetaBrainz Foundation
*
* This file is part of MusicBrainz, the open internet music database,
Expand All @@ -8,15 +8,27 @@
*/

import isDatabaseRowId from '../../common/utility/isDatabaseRowId.js';
import type {
LinkRelationshipT,
} from '../../edit/externalLinks.js';
import type {
RelationshipStateT,
} from '../types.js';

export default function getOpenEditsLink(
relationship: RelationshipStateT,
relationship: LinkRelationshipT | RelationshipStateT,
): string | null {
const entity0 = relationship.entity0;
const entity1 = relationship.entity1;
if (!entity0 || !entity1) {
return null;
}

const entity0Name = entity0.name;
const entity1Name = entity1.name;
if (empty(entity0Name) || empty(entity1Name)) {
return null;
}

if (!isDatabaseRowId(entity0.id) || !isDatabaseRowId(entity1.id)) {
return null;
Expand All @@ -26,11 +38,11 @@ export default function getOpenEditsLink(
'/search/edits?auto_edit_filter=&order=desc&negation=0&combinator=and' +
`&conditions.0.field=${encodeURIComponent(entity0.entityType)}` +
'&conditions.0.operator=%3D' +
`&conditions.0.name=${encodeURIComponent(entity0.name)}` +
`&conditions.0.name=${encodeURIComponent(entity0Name)}` +
`&conditions.0.args.0=${encodeURIComponent(String(entity0.id))}` +
`&conditions.1.field=${encodeURIComponent(entity1.entityType)}` +
'&conditions.1.operator=%3D' +
`&conditions.1.name=${encodeURIComponent(entity1.name)}` +
`&conditions.1.name=${encodeURIComponent(entity1Name)}` +
`&conditions.1.args.0=${encodeURIComponent(String(entity1.id))}` +
'&conditions.2.field=type' +
'&conditions.2.operator=%3D&conditions.2.args=90%2C233' +
Expand Down

0 comments on commit 93d8363

Please sign in to comment.