Skip to content

Commit 6b32ad4

Browse files
authored
Merge pull request #3656 from reosarevok/MBS-12744
MBS-12744: Convert Edit artist credit page to React
2 parents 768ccfe + f5174bc commit 6b32ad4

File tree

14 files changed

+305
-79
lines changed

14 files changed

+305
-79
lines changed

lib/MusicBrainz/Server/Controller/Artist.pm

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -821,11 +821,6 @@ sub split : Chained('load') Edit {
821821
$_->link->type->gid eq $ARTIST_ARTIST_COLLABORATION
822822
} $artist->all_relationships;
823823

824-
$c->stash(
825-
in_use => $c->model('ArtistCredit')->in_use($ac),
826-
collaborators => \@collaborators,
827-
);
828-
829824
my $edit = $self->edit_action(
830825
$c,
831826
form => 'EditArtistCredit',
@@ -870,6 +865,20 @@ sub split : Chained('load') Edit {
870865
);
871866

872867
$c->stash->{form}->field('artist_credit')->stash_field;
868+
869+
my %props = (
870+
artist => $artist->TO_JSON,
871+
artistCredit => $ac->TO_JSON,
872+
collaborators => to_json_array(\@collaborators),
873+
form => $c->stash->{form}->TO_JSON,
874+
inUse => boolean_to_json($c->model('ArtistCredit')->in_use($ac)),
875+
);
876+
$c->stash(
877+
component_path => 'artist/SplitArtist',
878+
component_props => \%props,
879+
current_view => 'Node',
880+
);
881+
873882
}
874883

875884
sub credit : Chained('load') PathPart('credit') CaptureArgs(1) {

root/artist/EditArtistCredit.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2025 MetaBrainz Foundation
4+
*
5+
* This file is part of MusicBrainz, the open internet music database,
6+
* and is licensed under the GPL version 2, or (at your option) any
7+
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
8+
*/
9+
10+
import * as React from 'react';
11+
12+
import manifest from '../static/manifest.mjs';
13+
import EditArtistCreditForm
14+
from '../static/scripts/artist/components/EditArtistCreditForm.js';
15+
import ArtistCreditLink
16+
from '../static/scripts/common/components/ArtistCreditLink.js';
17+
18+
import ArtistLayout from './ArtistLayout.js';
19+
import type {EditArtistCreditFormT} from './types.js';
20+
21+
component EditArtistCredit(
22+
artist: ArtistT,
23+
artistCredit: ArtistCreditT,
24+
form: EditArtistCreditFormT,
25+
) {
26+
const title = lp('Edit artist credit', 'header');
27+
28+
return (
29+
<ArtistLayout
30+
entity={artist}
31+
fullWidth
32+
page="split"
33+
title={title}
34+
>
35+
<h2>{title}</h2>
36+
37+
<div className="half-width">
38+
<p>
39+
{exp.l(
40+
`This form allows you to edit the artist credit “{ac}”.
41+
When the edit is accepted, all tracks, recordings, releases
42+
and release groups using this artist credit will be
43+
updated to use the new one.`,
44+
{ac: <ArtistCreditLink artistCredit={artistCredit} />},
45+
)}
46+
</p>
47+
</div>
48+
49+
<EditArtistCreditForm
50+
artistCredit={artistCredit}
51+
form={form}
52+
/>
53+
{manifest('artist/components/EditArtistCreditForm', {async: true})}
54+
</ArtistLayout>
55+
);
56+
}
57+
58+
export default EditArtistCredit;

root/artist/SplitArtist.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2022 MetaBrainz Foundation
4+
*
5+
* This file is part of MusicBrainz, the open internet music database,
6+
* and is licensed under the GPL version 2, or (at your option) any
7+
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
8+
*/
9+
10+
import * as React from 'react';
11+
12+
import manifest from '../static/manifest.mjs';
13+
import EditArtistCreditForm
14+
from '../static/scripts/artist/components/EditArtistCreditForm.js';
15+
import EntityLink
16+
from '../static/scripts/common/components/EntityLink.js';
17+
import entityHref from '../static/scripts/common/utility/entityHref.js';
18+
19+
import ArtistLayout from './ArtistLayout.js';
20+
import type {EditArtistCreditFormT} from './types.js';
21+
22+
component SplitArtist(
23+
artist: ArtistT,
24+
artistCredit: ArtistCreditT,
25+
collaborators: $ReadOnlyArray<ArtistT>,
26+
form: EditArtistCreditFormT,
27+
inUse: boolean,
28+
) {
29+
return (
30+
<ArtistLayout
31+
entity={artist}
32+
fullWidth
33+
page="split"
34+
title={l('Split artist')}
35+
>
36+
<h2>{l('Split into separate artists')}</h2>
37+
38+
{inUse ? (
39+
<>
40+
<div className="half-width">
41+
<p>
42+
{exp.l(
43+
`This form allows you to split {artist} into separate artists.
44+
When the edit is accepted, existing artist credits will be
45+
updated, and collaboration relationships will be removed.`,
46+
{artist: <EntityLink entity={artist} />},
47+
)}
48+
</p>
49+
50+
{collaborators.length ? (
51+
<>
52+
<h3>{addColonText(l('Collaborators on this artist'))}</h3>
53+
<ul>
54+
{collaborators.map((collaborator, index) => (
55+
<li key={index}>
56+
<EntityLink entity={collaborator} />
57+
</li>
58+
))}
59+
</ul>
60+
</>
61+
) : null}
62+
</div>
63+
<EditArtistCreditForm
64+
artistCredit={artistCredit}
65+
form={form}
66+
/>
67+
</>
68+
) : (
69+
<p>
70+
{exp.l(
71+
`There are no recordings, release groups, releases or tracks
72+
credited to only {name}. If you are trying to remove {name},
73+
please edit all artist credits at the bottom of the
74+
{alias_uri|aliases} tab and remove all existing
75+
{rel_uri|relationships} instead, which will allow ModBot
76+
to automatically remove this artist in the upcoming days.`,
77+
{
78+
alias_uri: entityHref(artist, 'aliases'),
79+
name: <EntityLink entity={artist} />,
80+
rel_uri: entityHref(artist, 'relationships'),
81+
},
82+
)}
83+
</p>
84+
)}
85+
{manifest('artist/components/EditArtistCreditForm', {async: true})}
86+
</ArtistLayout>
87+
);
88+
}
89+
90+
export default SplitArtist;

root/artist/split.tt

Lines changed: 0 additions & 54 deletions
This file was deleted.

root/artist/types.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* @flow strict
3+
* Copyright (C) 2025 MetaBrainz Foundation
4+
*
5+
* This file is part of MusicBrainz, the open internet music database,
6+
* and is licensed under the GPL version 2, or (at your option) any
7+
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
8+
*/
9+
10+
export type EditArtistCreditFormT = FormT<{
11+
+artist_credit: FieldT<ArtistCreditT>,
12+
+edit_note: FieldT<string>,
13+
+make_votable: FieldT<boolean>,
14+
+preview: FieldT<string>,
15+
}>;

root/server/components.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export default {
9090
'artist/ArtistWorks': (): Promise<mixed> => import('../artist/ArtistWorks.js'),
9191
'artist/CannotSplit': (): Promise<mixed> => import('../artist/CannotSplit.js'),
9292
'artist/SpecialPurpose': (): Promise<mixed> => import('../artist/SpecialPurpose.js'),
93+
'artist/SplitArtist': (): Promise<mixed> => import('../artist/SplitArtist.js'),
9394
'artist_credit/ArtistCreditIndex': (): Promise<mixed> => import('../artist_credit/ArtistCreditIndex.js'),
9495
'artist_credit/EntityList': (): Promise<mixed> => import('../artist_credit/EntityList.js'),
9596
'cdstub/BrowseCDStubs': (): Promise<mixed> => import('../cdstub/BrowseCDStubs.js'),
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2022 MetaBrainz Foundation
4+
*
5+
* This file is part of MusicBrainz, the open internet music database,
6+
* and is licensed under the GPL version 2, or (at your option) any
7+
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
8+
*/
9+
10+
import mutate from 'mutate-cow';
11+
import * as React from 'react';
12+
13+
import type {EditArtistCreditFormT} from '../../../../artist/types.js';
14+
import ArtistCreditEditor, {
15+
createInitialState as createArtistCreditState,
16+
reducer as runArtistCreditReducer,
17+
} from '../../edit/components/ArtistCreditEditor.js';
18+
import {
19+
type ActionT as ArtistCreditActionT,
20+
type StateT as ArtistCreditStateT,
21+
} from '../../edit/components/ArtistCreditEditor/types.js';
22+
import EnterEdit from '../../edit/components/EnterEdit.js';
23+
import EnterEditNote from '../../edit/components/EnterEditNote.js';
24+
import FormRow from '../../edit/components/FormRow.js';
25+
26+
/* eslint-disable ft-flow/sort-keys */
27+
type ActionT =
28+
| {+type: 'update-artist-credit', +action: ArtistCreditActionT};
29+
/* eslint-enable ft-flow/sort-keys */
30+
31+
type CreateInitialStatePropsT = {
32+
+artistCredit: ArtistCreditT,
33+
+form: EditArtistCreditFormT,
34+
};
35+
36+
type StateT = {
37+
+artistCredit: ArtistCreditStateT,
38+
+form: EditArtistCreditFormT,
39+
};
40+
41+
function createInitialState({
42+
artistCredit,
43+
form,
44+
}: CreateInitialStatePropsT): StateT {
45+
return {
46+
artistCredit: createArtistCreditState({
47+
artistCredit,
48+
formName: form.name,
49+
id: 'source',
50+
}),
51+
form,
52+
};
53+
}
54+
55+
function reducer(state: StateT, action: ActionT): StateT {
56+
const newStateCtx = mutate(state);
57+
58+
match (action) {
59+
{type: 'update-artist-credit', const action} => {
60+
newStateCtx.set(
61+
'artistCredit',
62+
runArtistCreditReducer(state.artistCredit, action),
63+
);
64+
}
65+
}
66+
return newStateCtx.final();
67+
}
68+
69+
component EditArtistCreditForm(
70+
artistCredit: ArtistCreditT,
71+
form as initialForm: EditArtistCreditFormT,
72+
) {
73+
const [state, dispatch] = React.useReducer(
74+
reducer,
75+
{artistCredit, form: initialForm},
76+
createInitialState,
77+
);
78+
79+
const artistCreditEditorDispatch = React.useCallback((
80+
action: ArtistCreditActionT,
81+
) => {
82+
dispatch({action, type: 'update-artist-credit'});
83+
}, [dispatch]);
84+
85+
return (
86+
<form method="post">
87+
<div className="half-width">
88+
<fieldset>
89+
<legend>{l('New artist credit')}</legend>
90+
<FormRow>
91+
<label className="required" htmlFor="ac-source-single-artist">
92+
{addColonText(l('Artist'))}
93+
</label>
94+
<ArtistCreditEditor
95+
dispatch={artistCreditEditorDispatch}
96+
state={state.artistCredit}
97+
/>
98+
</FormRow>
99+
</fieldset>
100+
101+
<EnterEditNote field={state.form.field.edit_note} />
102+
<EnterEdit form={state.form} />
103+
</div>
104+
</form>
105+
);
106+
}
107+
108+
export default (hydrate<React.PropsOf<EditArtistCreditForm>>(
109+
'div.split-artist-form',
110+
EditArtistCreditForm,
111+
): component(...React.PropsOf<EditArtistCreditForm>));

0 commit comments

Comments
 (0)