Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MBS-11599: Allow adding URL relationships to genre pages #2497

Merged
merged 9 commits into from Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion entities.json
Expand Up @@ -239,7 +239,8 @@
"edit_table": true,
"mbid": {
"indexable": true,
"multiple": false
"multiple": false,
"relatable": "overview"
},
"merging": false,
"model": "Genre",
Expand Down
2 changes: 2 additions & 0 deletions lib/MusicBrainz/Server/Controller/Genre.pm
Expand Up @@ -9,11 +9,13 @@ use MusicBrainz::Server::Entity::Util::JSON qw( to_json_array );
with 'MusicBrainz::Server::Controller::Role::Load' => {
model => 'Genre',
entity_name => 'genre',
relationships => { cardinal => ['show', 'edit'], default => ['url'] },
};
with 'MusicBrainz::Server::Controller::Role::LoadWithRowID';
with 'MusicBrainz::Server::Controller::Role::Annotation';
with 'MusicBrainz::Server::Controller::Role::Details';
with 'MusicBrainz::Server::Controller::Role::EditListing';
with 'MusicBrainz::Server::Controller::Role::EditRelationships';

use MusicBrainz::Server::Constants qw(
$EDIT_GENRE_CREATE
Expand Down
11 changes: 10 additions & 1 deletion lib/MusicBrainz/Server/Controller/Role/EditRelationships.pm
Expand Up @@ -298,14 +298,23 @@ role {
my $seeded_relationships = get_seeded_relationships($c, $source_type, $source);
my @link_type_tree = $c->model('LinkType')->get_full_tree;
my @link_attribute_types = $c->model('LinkAttributeType')->get_all;
my $type_info = build_type_info(
$c,
qr/(^$source_type-|-$source_type$)/,
@link_type_tree,
);

$c->stash(
seeded_relationships => $c->json->encode(to_json_array($seeded_relationships)),
source_entity => $c->json->encode($source_entity),
attr_info => $c->json->encode(\@link_attribute_types),
type_info => $c->json->encode(build_type_info($c, qr/(^$source_type-|-$source_type$)/, @link_type_tree)),
type_info => $c->json->encode($type_info),
);

$c->stash->{component_props}{sourceEntity} = $source_entity;
$c->stash->{component_props}{attrInfo} = to_json_array(\@link_attribute_types);
$c->stash->{component_props}{typeInfo} = $type_info;

my $post_creation = delete $opts{post_creation};

$opts{post_creation} = sub {
Expand Down
1 change: 1 addition & 0 deletions lib/MusicBrainz/Server/Data/Genre.pm
Expand Up @@ -55,6 +55,7 @@ sub can_delete { 1 }
sub delete {
my ($self, $genre_id) = @_;

$self->c->model('Relationship')->delete_entities('genre', $genre_id);
$self->annotation->delete($genre_id);
$self->delete_returning_gids($genre_id);
return;
Expand Down
1 change: 1 addition & 0 deletions lib/MusicBrainz/Server/Entity/Genre.pm
Expand Up @@ -7,6 +7,7 @@ extends 'MusicBrainz::Server::Entity::CoreEntity';
with 'MusicBrainz::Server::Entity::Role::Annotation';
with 'MusicBrainz::Server::Entity::Role::Comment';
with 'MusicBrainz::Server::Entity::Role::LastUpdate';
with 'MusicBrainz::Server::Entity::Role::Linkable';

sub entity_type { 'genre' }

Expand Down
1 change: 1 addition & 0 deletions lib/MusicBrainz/Server/Form/Genre.pm
Expand Up @@ -3,6 +3,7 @@ use HTML::FormHandler::Moose;

extends 'MusicBrainz::Server::Form';
with 'MusicBrainz::Server::Form::Role::Edit';
with 'MusicBrainz::Server::Form::Role::Relationships';

has '+name' => ( default => 'edit-genre' );

Expand Down
23 changes: 20 additions & 3 deletions root/genre/CreateGenre.js
Expand Up @@ -10,21 +10,38 @@
import * as React from 'react';

import Layout from '../layout';
import * as manifest from '../static/manifest';
import GenreEditForm from '../static/scripts/genre/components/GenreEditForm';

import GenreEditForm from './GenreEditForm';
import type {GenreFormT} from './types';

type Props = {
+$c: CatalystContextT,
+attrInfo: LinkAttrTypeOptionsT,
+form: GenreFormT,
+sourceEntity: {entityType: 'genre'},
+typeInfo: LinkTypeOptionsT,
};

const CreateGenre = ({$c, form}: Props): React.Element<typeof Layout> => (
const CreateGenre = ({
$c,
attrInfo,
form,
sourceEntity,
typeInfo,
}: Props): React.Element<typeof Layout> => (
<Layout fullWidth title={l('Add a new genre')}>
<div id="content">
<h1>{l('Add a new genre')}</h1>
<GenreEditForm $c={$c} form={form} />
<GenreEditForm
$c={$c}
attrInfo={attrInfo}
form={form}
sourceEntity={sourceEntity}
typeInfo={typeInfo}
/>
</div>
{manifest.js('genre/components/GenreEditForm', {async: 'async'})}
</Layout>
);

Expand Down
19 changes: 17 additions & 2 deletions root/genre/EditGenre.js
Expand Up @@ -9,28 +9,43 @@

import * as React from 'react';

import GenreEditForm from './GenreEditForm';
import * as manifest from '../static/manifest';
import GenreEditForm from '../static/scripts/genre/components/GenreEditForm';

import GenreLayout from './GenreLayout';
import type {GenreFormT} from './types';

type Props = {
+$c: CatalystContextT,
+attrInfo: LinkAttrTypeOptionsT,
+entity: GenreT,
+form: GenreFormT,
+sourceEntity: GenreT,
+typeInfo: LinkTypeOptionsT,
};

const EditGenre = ({
$c,
attrInfo,
entity,
form,
sourceEntity,
typeInfo,
}: Props): React.Element<typeof GenreLayout> => (
<GenreLayout
entity={entity}
fullWidth
page="edit"
title={l('Edit genre')}
>
<GenreEditForm $c={$c} form={form} />
<GenreEditForm
$c={$c}
attrInfo={attrInfo}
form={form}
sourceEntity={sourceEntity}
typeInfo={typeInfo}
/>
{manifest.js('genre/components/GenreEditForm', {async: 'async'})}
</GenreLayout>
);

Expand Down
49 changes: 0 additions & 49 deletions root/genre/GenreEditForm.js

This file was deleted.

2 changes: 2 additions & 0 deletions root/genre/GenreIndex.js
Expand Up @@ -9,6 +9,7 @@

import * as React from 'react';

import Relationships from '../components/Relationships';
import Annotation from '../static/scripts/common/components/Annotation';
import TagLink from '../static/scripts/common/components/TagLink';
import * as manifest from '../static/manifest';
Expand Down Expand Up @@ -42,6 +43,7 @@ const GenreIndex = ({
entity={genre}
numberOfRevisions={numberOfRevisions}
/>
<Relationships source={genre} />
{manifest.js('genre/index', {async: 'async'})}
</GenreLayout>
);
Expand Down
3 changes: 3 additions & 0 deletions root/layout/components/sidebar/GenreSidebar.js
Expand Up @@ -12,6 +12,7 @@ import * as React from 'react';
import {CatalystContext} from '../../../context';
import {isRelationshipEditor}
from '../../../static/scripts/common/utility/privileges';
import ExternalLinks from '../ExternalLinks';

import AnnotationLinks from './AnnotationLinks';
import EditLinks from './EditLinks';
Expand All @@ -27,6 +28,8 @@ const GenreSidebar = ({genre}: Props): React.Element<'div'> => {

return (
<div id="sidebar">
<ExternalLinks empty entity={genre} />

{isRelationshipEditor($c.user) ? (
<EditLinks entity={genre}>
<AnnotationLinks entity={genre} />
Expand Down
51 changes: 31 additions & 20 deletions root/static/scripts/edit/externalLinks.js
Expand Up @@ -12,8 +12,6 @@ import punycode from 'punycode';
import $ from 'jquery';
import ko from 'knockout';
import * as React from 'react';
// $FlowIgnore[missing-export]
import {flushSync} from 'react-dom';
import * as ReactDOMClient from 'react-dom/client';

import {
Expand Down Expand Up @@ -1710,15 +1708,24 @@ function isMusicBrainz(url) {
type InitialOptionsT = {
errorObservable?: (boolean) => void,
mountPoint: Element,
sourceData: CoreEntityT,
sourceData: CoreEntityT | {
+entityType: CoreEntityTypeT,
+id?: void,
+relationships?: void,
},
};

type SeededUrlShape = {
+link_type_id?: string,
+text?: string,
};

MB.createExternalLinksEditor = function (options: InitialOptionsT) {
export function createExternalLinksEditor(
options: InitialOptionsT,
): {
+externalLinksEditorRef: React$Ref<typeof ExternalLinksEditor>,
+root: {+unmount: () => void, ...},
} {
const sourceData = options.sourceData;
const sourceType = sourceData.entityType;
const entityTypes = [sourceType, 'url'].sort().join('-');
Expand Down Expand Up @@ -1794,21 +1801,25 @@ MB.createExternalLinksEditor = function (options: InitialOptionsT) {
const errorObservable = options.errorObservable ||
validation.errorField(ko.observable(false));

const root = ReactDOMClient.createRoot(options.mountPoint);
const mountPoint = options.mountPoint;
let root = $(mountPoint).data('react-root');
if (!root) {
root = ReactDOMClient.createRoot(mountPoint);
$(mountPoint).data('react-root', root);
}

const externalLinksEditorRef = React.createRef();
flushSync(() => {
root.render(
<ExternalLinksEditor
errorObservable={errorObservable}
initialLinks={initialLinks}
isNewEntity={!sourceData.id}
ref={externalLinksEditorRef}
sourceType={sourceData.entityType}
typeOptions={typeOptions}
/>,
);
});
return externalLinksEditorRef;
};
root.render(
<ExternalLinksEditor
errorObservable={errorObservable}
initialLinks={initialLinks}
isNewEntity={!sourceData.id}
ref={externalLinksEditorRef}
sourceType={sourceData.entityType}
typeOptions={typeOptions}
/>,
);
return {externalLinksEditorRef, root};
}

export const createExternalLinksEditor = MB.createExternalLinksEditor;
MB.createExternalLinksEditor = createExternalLinksEditor;