Personal information
Nickname: yyoung
IRC nick: yyoung
GitHub: y-young
MusicBrainz: yyoung
Timezone: UTC+08:00
E-mail: yyoung2001 AT gmail DOT com
Proposal
Project Overview
On MusicBrainz, URLs are external links attached to entities; relationships define their role and include metadata like date period, they provide extra information about the entity and inspire users to explore further.
Currently, the URL relationship editor on MusicBrainz has many shortcomes. Though it is a relationship, the URL editor has a different UI from other editors. Automatic rules were built to ease the effort, but outdated and buggy rules make them an obstacle in some situations. For better user experience, we would like to enhance the URL relationship editor, to support multiple relationship types for a single link, to provide a unified appearance, better user experience and more flexibility.
Features
-
Allow selecting multiple relationship types for an entered URL, the editor will validate the selections
-
Support auto-selection of multiple relationship types
-
Enable date period and ended flag editing from editing pages of other entities, such as releases and artists
-
A separate field for clean URL to avoid confusion
UI Prototype
On editing pages of entities other than URL, such as ‘Add a release’ page, ‘External Links’ field will be changed to a relationship list similar to that on URL editing page, as follows:
Each link is shown as an ordered list item, with relationships displayed below. Ended relationships are shown in gray color.
With such a list we’re able to add multiple relationships to a link. (MBS-9902)
Users may input a new URL in the empty box below to add a new link, after adding a new link, an empty box is automatically appended.
When the input box is focused, a bubble is displayed on the right:
This bubble will notify the user of the clean URL produced by URLCleanup.js, original input won’t be overwritten; auto-selected relationship type will be automatically added to the relationship list. (MBS-11391)
When the URL is changed, clean URL will be updated accordingly. To avoid conflicts, if this link already had relationships, auto-selected relationship type won’t be appended.
By clicking the ‘x’ icon next to the input box, this link is removed along with its relationships.
When the user click ‘Add another relationship’ button or the edit icon besides each relationship item, the following dialog will pop up:
The help text indicated by ‘?’ icon is identical to current one.
The ‘Type’ select is the same as current relationship type selector: (This prototype is for mock-up purposes so only a subset of items are shown)
As for some relationship types with extra attributes, for example, ‘stream for free’, ‘video’ checkbox will be shown below the select.
If selected relationship type is invalid for this link, an error message will be shown:
Unlike current version, in which link type will be displayed as text when automatically determined, the new ‘Type’ select and the checkbox under the error message provides an approach to bypass outdated and bugged rules. More details will be discussed later since it’s moved to stretch goals. (MBS-4684, MBS-9040)
Also, conflicts between relationships are indicated in the list:
Date period and ended flag are associated with each relationship type, they work just the same as other relationships, and since they are shown in the dialog, users will be able to set these attributes from editing page of linked entity, and they can preview the result on the list, just like the URL editing page. (MBS-3774)
Implementations
I will implement a new ExternalLinksEditor
in root/static/scripts/edit/externalLinks.js
, resembling that on URL editing page in appearance, for displaying links.
Here’s the code for UI prototype:
import * as React from "react";
const ItunesIcon = require("../../images/external-favicons/itunes-16.png");
class RelationshipItem extends React.Component {
render() {
return (
<tr>
<td />
<td style={{padding: "0 30px"}}>
<div
className={this.props.ended ? "deleted" : undefined}
style={{ display: "flex" }}
>
<span
className="favicon itunes-favicon"
style={{
backgroundImage: ItunesIcon,
backgroundSize: "16px",
display: "inline-block",
width: "16px",
height: "16px",
verticalAlign: "middle",
marginRight: "4px",
}}
/>
<div style={{ flex: "3 1" }}>{this.props.name}</div>
<div style={{ flex: "1 1", textAlign: "right" }}>
{`(${this.props.beginYear}-${this.props.endYear})`}
</div>
</div>
{this.props.error && (
<div className="error field-error">
{"This relationship has conflicts."}
</div>
)}
</td>
<td>
<div
style={{
position: "relative",
display: "inline-block",
minWidth: "30px",
}}
>
<div className="img icon help" />
<button
className="icon edit-item"
title="Edit Relationship"
type="button"
/>
<button
className="nobutton icon remove-item"
title="Remove Relationship"
type="button"
/>
</div>
</td>
</tr>
);
}
}
RelationshipItem.defaultProps = {
endYear: "",
ended: false,
};
class RelationshipList extends React.Component {
render() {
return (
<>
<RelationshipItem
beginYear="2016"
endYear="2020"
ended
error="This relationship has conflicts."
name="purchase for download"
/>
<RelationshipItem beginYear="2016" endYear="" name="show notes" />
<tr>
<td />
<td>
<button
className="add-item with-label"
style={{ margin: "5px 0", paddingLeft: "30px" }}
type="button"
>
{l("Add another relationship")}
</button>
</td>
</tr>
</>
);
}
}
class ExternalLinkItem extends React.Component {
render() {
return (
<>
<tr>
<td>{1}</td>
<td>
<div style={{ marginBottm: "10px" }}>
<input
style={{width: "100%"}}
value="http://itunes.apple.com/au/preorder/the-last-of-the-tourists/id499465357"
/>
</div>
</td>
<td>
<div
style={{
position: "relative",
display: "inline-block",
minWidth: "50px",
verticalAlign: "top",
}}
>
<button
className="nobutton icon remove-item"
title="Remove Link"
type="button"
/>
</div>
</td>
<div
className="bubble left-tail"
style={{
display: "block",
width: "651px",
top: "820.938px",
left: "560px",
}}
>
<p>
{"This URL will be cleaned up to “"}
<a>{"https://itunes.apple.com/au/preorder/id499465357"}</a>
{"”."}
</p>
</div>
</tr>
<RelationshipList />
</>
);
}
}
class ExternalLinksEditor extends React.Component {
render() {
return (
<>
<table className="row-form">
<tbody>
<ExternalLinkItem />
<tr>
<td>{2}</td>
<td>
<input style={{width: "100%"}} />
</td>
</tr>
</tbody>
</table>
</>
);
}
}
export default ExternalLinksEditor;
I’ll also use React to build a ExternalLinkEditDialog
for adding and editing external links, this dialog will be based on root/components/relationship-editor.tt
.
Here’s the code:
/* eslint-disable react/jsx-no-literals */
/* eslint-disable react/jsx-sort-props */
import * as React from "react";
class DateInput extends React.Component {
render() {
return (
<>
<input type="text" maxLength="4" placeholder="YYYY" size="4" />
-
<input type="text" maxLength="2" placeholder="MM" size="2" />
-
<input type="text" maxLength="2" placeholder="DD" size="2" />
</>
);
}
}
class ExternalLinkEditDialog extends React.Component {
render() {
return (
<div
className="ui-dialog ui-widget ui-widget-content
ui-corner-all ui-front rel-editor-dialog"
tabIndex="-1"
role="dialog"
aria-describedby="dialog"
style={{
display: "block",
top: "113.739px",
left: "0px",
height: "auto",
width: "auto",
}}
aria-labelledby="ui-id-1"
>
<div
id="dialog"
className="rel-editor-dialog ui-dialog-content ui-widget-content"
style={{
display: "block",
width: "auto",
minHeight: "0px",
maxHeight: "none",
height: "auto",
}}
>
<div>
<table>
<tbody>
<tr>
<td className="section">URL:</td>
<td>
<a>
{
"http://itunes.apple.com/au/preorder/the-last-of-the-tourists/id499465357"
}
</a>
</td>
</tr>
<tr>
<td className="section">Type:</td>
<td>
<div>
<select className="link-type">
<option value="171">discography entry</option>
<option disabled>get the music</option>
<option value="1">
purchase for mail order
</option>
<option value="2">
purchase for download
</option>
<option value="3">show notes</option>
<option value="4">cover art</option>
</select>
{" ("}
<a href="#">help</a>
{")"}
</div>
<div className="error field-error">
{'Enter a valid url e.g. "http://google.com/"'}
</div>
</td>
</tr>
<tr>
<td />
<td>
<input type="checkbox" />
<label>video</label>
</td>
</tr>
<tr>
<td className="section">Begin date:</td>
<td className="partial-date">
<DateInput />
{" "}
<button
className="icon copy-date"
type="button"
title="Copy to end date"
/>
</td>
</tr>
<tr>
<td className="section">End date:</td>
<td className="partial-date">
<DateInput />
</td>
</tr>
<tr>
<td />
<td>
<label>
<input type="checkbox" />
This relationship has ended.
</label>
</td>
</tr>
</tbody>
</table>
</div>
<div
className="buttons ui-helper-clearfix"
style={{ marginTop: "1em" }}
>
<button type="button" className="negative">
Cancel
</button>
<div
className="buttons-right"
style={{ float: "right", textAalign: "right" }}
>
<button type="button" className="positive">
Done
</button>
</div>
</div>
</div>
</div>
);
}
}
export default ExternalLinkEditDialog;
Since the whole form is moved into the dialog, we’ll have to modify root/static/scripts/edit/URLCleanup.js
to make it compatible with the new UI.
Then URLCleanup.js will be refactored to support auto-selection and validation of multiple relationship types.
Finally we’ll make sure the new interface can interact with backend server correctly, basically the backend will receive multiple relationships and add each of them respectively.
Pre-Community Bonding
My pull requests: https://github.com/metabrainz/musicbrainz-server/pulls?q=is%3Apr+author%3Ay-young
Schedule
Every coding task in the schedule will include writing documentation and tests.
Before & During Community Bonding Period
I plan to continue exploring the codebase for a better view of the whole structure and architecture, and learn the related skills.
In the meantime, I’ll try to submit some bug fixes and features to get myself familiar with the code, workflow and coding styles.
Also we’ll review, discuss and finalize the UI prototype.
Then I’ll complete prerequisite tasks such as refactoring related components, if there’s any.
Week 1
Review and finalize design of the table for displaying external links and their relationship types, implement UI based on current ExternalLinksEditor
, ensure correct display of existing data
Week 2
Review, finalize and implement ExternalLinkBubble
UI, add support for independent display of clean URL
Milestone: A separate field for clean URL to avoid confusion
Week 3
Review, finalize and implement ExternalLinkEditDialog
UI based on current relationship editor dialog
Week 4
Support date period and ended flag editing from other pages in the dialog, modify backend code for compatibility
Milestone: Enable date period and ended flag editing from editing pages of other entities, such as releases and artists
Week 5
Support adding multiple relationship types for a single link in the backend
Week 6
Refactor URLCleanup to support validation of multiple relationship types
Week 7
Enable the new ExternalLinksEditor
to validate multiple relationship types
Milestone: Allow selecting multiple relationship types for an entered URL, the editor will validate the selections
Week 8
Refactor URLCleanup to support auto-selection of multiple relationship types
Week 9
Enable the new ExternalLinksEditor
to automatically select multiple relationship types
Milestone: Support auto-selection of multiple relationship types
Week 10
Review and optimize the code, fix possible bugs and complete this project
Stretch Goal
-
Support bypassing validation rules in some cases
-
Support getting WikiData item from Wikipedia link
Detailed information about yourself
-
Tell us about the computer(s) you have available for working on your SoC project!
I have a ThinkPad L480 with Intel i7 8th gen CPU and 16GB memory.
-
When did you first start programming?
In 7th grade, with C++.
-
What type of music do you listen to? (Please list a series of MBIDs as examples.)
American/European Pop, Anime/Game soundtracks, JPop and Classical
-
What aspects of the project you’re applying for (e.g., MusicBrainz, AcousticBrainz, etc.) interest you the most?
MusicBrainz provides an open and free database for music lovers, researchers and many others, it enables music lovers like me to organize and make full use of our collections, and dive into the long history of music. -
Have you ever used MusicBrainz to tag your files?
Yes, with Mp3Tag.
-
Have you contributed to other Open Source projects? If so, which projects and can we see some of your code?
react-component/collapse, hexo-theme-icarus, hexo-component-inferno
The above commits are small bug fixes, I also build my own project with React, see this music voting system. -
What sorts of programming projects have you done on your own time?
Information systems, along with tools that will help me collect and organize information. In general, projects that will benefit others and myself. -
How much time do you have available, and how would you plan to use it?
I’ll have at least 5-hour free time on weekdays and 10-hour on weekends. I plan to invest about 30 hours a week in this project. -
Do you plan to have a job or study during the summer in conjunction with Summer of Code?
Yes, in July I will have to take a course in the university.