Skip to content

Commit 8941404

Browse files
authored
Merge pull request metabrainz#1860 from reosarevok/MBS-11326
MBS-11326: Convert edit/notes.tt to React
2 parents 48cab4e + 7e239ce commit 8941404

File tree

20 files changed

+307
-80
lines changed

20 files changed

+307
-80
lines changed

lib/MusicBrainz/Server/Controller/User.pm

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ sub serialize_user {
111111
entityType => 'editor',
112112
gravatar => $user->gravatar,
113113
id => 0 + $user->id,
114+
is_limited => boolean_to_json($user->is_limited),
114115
name => $user->name,
115116
preferences => {
116117
public_ratings => boolean_to_json($preferences->public_ratings),

lib/MusicBrainz/Server/Edit.pm

+1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ sub TO_JSON {
333333
display_data => $self->display_data,
334334
data => $self->data,
335335
edit_kind => $self->edit_kind,
336+
edit_notes => to_json_array($self->edit_notes),
336337
edit_type => $self->edit_type + 0,
337338
editor_id => $self->editor_id + 0,
338339
expires_time => datetime_to_iso8601($self->expires_time),

lib/MusicBrainz/Server/Entity/EditNote.pm

+15
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ use Moose;
77
use namespace::autoclean;
88

99
use MusicBrainz::Server::Constants qw( $EDITOR_MODBOT );
10+
use MusicBrainz::Server::Data::Utils qw( datetime_to_iso8601 );
1011
use MusicBrainz::Server::Entity::Types;
1112
use MusicBrainz::Server::Filters qw( format_editnote );
1213
use MusicBrainz::Server::Types qw( DateTime );
1314

15+
extends 'MusicBrainz::Server::Entity';
16+
1417
has 'editor_id' => (
1518
isa => 'Int',
1619
is => 'rw',
@@ -117,6 +120,18 @@ sub localize {
117120
return $text;
118121
}
119122

123+
around TO_JSON => sub {
124+
my ($orig, $self) = @_;
125+
126+
my $json = $self->$orig;
127+
$json->{editor_id} = $self->editor_id + 0;
128+
$json->{editor} = $self->editor->TO_JSON;
129+
$json->{post_time} = datetime_to_iso8601($self->post_time);
130+
$json->{formatted_text} = $self->editor_id == 4 ? $self->localize : format_editnote($self->text);
131+
132+
return $json;
133+
};
134+
120135
no Moose;
121136
__PACKAGE__->meta->make_immutable;
122137
1;

lib/MusicBrainz/Server/Entity/Editor.pm

+1
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ sub TO_JSON {
327327
entityType => 'editor',
328328
gravatar => $self->gravatar,
329329
id => $self->id,
330+
is_limited => boolean_to_json($self->is_limited),
330331
name => $self->name,
331332
privileges => 0 + $self->public_privileges,
332333
};

lib/MusicBrainz/Server/Entity/Vote.pm

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ has 'vote' => (
4141
is => 'rw',
4242
);
4343

44+
# Converted to JavaScript at root/static/scripts/edit/utility/getVoteName.js
4445
sub vote_name
4546
{
4647
my $self = shift;

root/components/UserAccountLayout.js

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type AccountLayoutUserT = {
2020
+entityType: 'editor',
2121
+gravatar: string,
2222
+id: number,
23+
+is_limited: boolean,
2324
+name: string,
2425
+preferences: {
2526
+public_ratings: boolean,

root/components/common-macros.tt

-38
Original file line numberDiff line numberDiff line change
@@ -876,44 +876,6 @@ END -%]
876876
[%- END -%]
877877
[%~ END ~%]
878878

879-
[%- MACRO note_anchor(edit, loop) BLOCK -%]
880-
note-[%- edit.id -%]-[%- loop.count() -%]
881-
[%- END -%]
882-
883-
[% MACRO votename(note) BLOCK %]
884-
[%- edit = note.edit -%]
885-
[%- is_owner = edit.editor_id == note.editor_id -%]
886-
[%- FOR vote=edit.votes -%]
887-
[%- show_class = (vote.editor_id == note.editor_id) && !vote.superseded -%]
888-
[%- vote.vote_name IF show_class -%]
889-
[%- ' ' IF show_class && is_owner -%]
890-
[%- END -%]
891-
[%- 'owner' IF is_owner -%]
892-
[% END %]
893-
894-
[%~ MACRO edit_note_display(edit_note) BLOCK ~%]
895-
<div class="edit-note" id="[% note_anchor(note.edit, loop) %]">
896-
<h3 class="[%- votename(note) -%]">
897-
[%- link_entity(note.editor) -%]
898-
[%- FOR vote=note.edit.votes;
899-
IF (vote.editor_id == note.editor_id) &&
900-
!vote.superseded &&
901-
(vote.vote_name == 'Approve' || vote.vote_name == 'No' || vote.vote_name == 'Yes');
902-
'<div class="voting-icon"></div>'; LAST;
903-
END; END; -%]
904-
<a href="#[% note_anchor(note.edit, loop) %]" class="date">[%- UserDate.format(note.post_time) -%]</a>
905-
[%- editor_type_info(note.editor) -%]
906-
</h3>
907-
<div class="edit-note-text [% 'modbot' IF note.editor_id == 4 %]">
908-
[%- IF note.editor_id == 4;
909-
note.localize;
910-
ELSE;
911-
note.text | format_editnote;
912-
END; -%]
913-
</div>
914-
</div>
915-
[%~ END ~%]
916-
917879
[%~ MACRO bugtracker_url(description) BLOCK; # Converted to React at root/static/scripts/common/utility/bugTrackerURL.js
918880
'http://tickets.metabrainz.org/secure/CreateIssueDetails!init.jspa?' _
919881
'pid=10000&issuetype=1' _

root/edit/components/EditNote.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2020 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 {
13+
EDIT_VOTE_APPROVE,
14+
EDIT_VOTE_NO,
15+
EDIT_VOTE_YES,
16+
} from '../../constants';
17+
import {CatalystContext} from '../../context';
18+
import EditorLink from '../../static/scripts/common/components/EditorLink';
19+
import expand2react from '../../static/scripts/common/i18n/expand2react';
20+
import getVoteName from '../../static/scripts/edit/utility/getVoteName';
21+
import formatUserDate from '../../utility/formatUserDate';
22+
23+
import EditorTypeInfo from './EditorTypeInfo';
24+
25+
type PropsT = {
26+
+edit: $ReadOnly<{...EditT, +id: number}>,
27+
+editNote: EditNoteT,
28+
+index: number,
29+
};
30+
31+
function returnNoteAnchor(edit, index) {
32+
return `note-${edit.id}-${index + 1}`;
33+
}
34+
35+
function returnVoteClass(vote, isOwner) {
36+
let className = '';
37+
38+
if (vote) {
39+
className = getVoteName(vote.vote);
40+
}
41+
42+
if (isOwner) {
43+
if (vote) {
44+
className += ' ';
45+
}
46+
className += 'owner';
47+
}
48+
49+
return className;
50+
}
51+
52+
const EditNote = ({
53+
edit,
54+
editNote,
55+
index,
56+
}: PropsT): React.Element<'div'> => {
57+
const $c = React.useContext(CatalystContext);
58+
const isModBot = editNote.editor_id === 4;
59+
const anchor = returnNoteAnchor(edit, index);
60+
const isOwner = edit.editor_id === editNote.editor_id;
61+
const lastRelevantVote = edit.votes.find(vote => (
62+
vote.editor_id === editNote.editor_id &&
63+
!vote.superseded
64+
));
65+
const showVotingIcon = lastRelevantVote && (
66+
lastRelevantVote.vote === EDIT_VOTE_APPROVE ||
67+
lastRelevantVote.vote === EDIT_VOTE_NO ||
68+
lastRelevantVote.vote === EDIT_VOTE_YES
69+
);
70+
71+
return (
72+
<div className="edit-note" id={anchor}>
73+
<h3 className={returnVoteClass(lastRelevantVote, isOwner)}>
74+
<EditorLink editor={editNote.editor} />
75+
{showVotingIcon /*:: === true */
76+
? <div className="voting-icon" />
77+
: null}
78+
{' '}
79+
<EditorTypeInfo editor={editNote.editor} />
80+
<a className="date" href={`#${anchor}`}>
81+
{formatUserDate($c, editNote.post_time)}
82+
</a>
83+
</h3>
84+
<div className={'edit-note-text' + (isModBot ? ' modbot' : '')}>
85+
{expand2react(editNote.formatted_text)}
86+
</div>
87+
</div>
88+
);
89+
};
90+
91+
export default EditNote;

root/edit/components/EditNotes.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2020 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 {CatalystContext} from '../../context';
13+
import DBDefs from '../../static/scripts/common/DBDefs';
14+
import {
15+
editorMayAddNote,
16+
} from '../../utility/edit';
17+
18+
import EditNote from './EditNote';
19+
20+
type Props = {
21+
+edit: {...EditT, +id: number},
22+
+hide?: boolean,
23+
+index?: number,
24+
+verbose?: boolean,
25+
};
26+
27+
const EditNotes = ({
28+
edit,
29+
hide = false,
30+
index = 0,
31+
verbose = true,
32+
}: Props): React.Element<'div'> => {
33+
const $c = React.useContext(CatalystContext);
34+
const user = $c.user;
35+
const mayAddNote = editorMayAddNote(edit, user);
36+
const editNotes = edit.edit_notes;
37+
38+
return (
39+
<div className="edit-notes">
40+
{editNotes?.length ? (
41+
editNotes.map((note, index) => (
42+
<EditNote
43+
edit={edit}
44+
editNote={note}
45+
index={index}
46+
key={index}
47+
/>
48+
))
49+
) : verbose ? (
50+
<div className="edit-note">
51+
<em>{l('No edit notes have been added.')}</em>
52+
</div>
53+
) : null}
54+
55+
{DBDefs.DB_READ_ONLY ? null : (
56+
mayAddNote ? (
57+
<div
58+
className="add-edit-note edit-note"
59+
style={hide ? {display: 'none'} : {}}
60+
>
61+
<p className="edit-note-help">
62+
{exp.l(
63+
`Edit notes support
64+
{doc_formatting|a limited set of wiki formatting options}.
65+
Please do always keep the {doc_coc|Code of Conduct} in mind
66+
when writing edit notes!`,
67+
{
68+
doc_coc: {href: '/doc/Code_of_Conduct', target: '_blank'},
69+
doc_formatting: {href: '/doc/Edit_Note', target: '_blank'},
70+
},
71+
)}
72+
</p>
73+
<div className="edit-note-text">
74+
<textarea
75+
className="edit-note"
76+
name={`enter-vote.vote.${index}.edit_note`}
77+
placeholder={l('Add an edit note')}
78+
rows="5"
79+
/>
80+
</div>
81+
</div>
82+
) : (
83+
<p>
84+
{exp.l(
85+
`You are not currently able to add notes to this edit.
86+
({url|Details})`,
87+
{url: '/doc/Editing_FAQ'},
88+
)}
89+
</p>
90+
)
91+
)}
92+
</div>
93+
);
94+
};
95+
96+
export default EditNotes;
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2020 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 bracketed from '../../static/scripts/common/utility/bracketed';
13+
import {isBot} from '../../static/scripts/common/utility/privileges';
14+
15+
type Props = {
16+
+editor: EditorT,
17+
};
18+
19+
const EditorTypeInfo = ({
20+
editor,
21+
}: Props): React.Element<typeof React.Fragment> => (
22+
<>
23+
{editor.is_limited ? (
24+
<span className="editor-class">
25+
{bracketed(
26+
<span
27+
className="tooltip"
28+
title={l('This user is new to MusicBrainz.')}
29+
>
30+
{l('beginner')}
31+
</span>,
32+
)}
33+
</span>
34+
) : null}
35+
{isBot(editor) ? (
36+
<span className="editor-class">
37+
{bracketed(
38+
<span className="tooltip" title={l('This user is automated.')}>
39+
{l('bot')}
40+
</span>,
41+
)}
42+
</span>
43+
) : null}
44+
</>
45+
);
46+
47+
export default EditorTypeInfo;

root/edit/index.tt

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575

7676
<h2>[% l('Edit notes') %]</h2>
7777
[%~ IF c.user_exists ~%]
78-
[% INCLUDE 'edit/notes.tt' %]
78+
[%- React.embed(c, 'edit/components/EditNotes', {edit => edit_json_obj, index => 0}) -%]
7979
[%- IF edit.editor_may_vote(c.user) -%]
8080
[%- form_submit(l('Submit vote and note')) -%]
8181
[%- ELSIF edit.editor_may_add_note(c.user) -%]

root/edit/list.tt

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
</div>
7474

7575
[%~ IF c.user_exists ~%]
76-
[% INCLUDE 'edit/notes.tt' verbose='not-verbose' hide=1 rows=1 index=loop.index %]
76+
[%- React.embed(c, 'edit/components/EditNotes', {edit => edit_json_obj, hide => 1, index => loop.index, verbose => 0}) -%]
7777
[%~ END ~%]
7878
<div class="seperator">
7979
</div>

root/edit/notes-received.tt

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
[%~ link_edit(edit, show, l('Edit #{id} - {name}', { id => edit.id, name => edit.l_edit_name})) ~%]
2525
</h2>
2626
</div>
27-
[% edit_note_display(note) %]
27+
[%- edit_json_obj = React.to_json_object(edit); edit_json_obj.delete('display_data') -%]
28+
[%- React.embed(c, 'edit/components/EditNote', {edit => edit_json_obj, editNote => React.to_json_object(note), index => 0}) -%]
2829
</div>
2930
[% END %]
3031
[% END %]

0 commit comments

Comments
 (0)