Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1 from EpocDotFr/copy-mr-info
Browse files Browse the repository at this point in the history
Copy basic Merge Request information
  • Loading branch information
EpocDotFr authored Apr 15, 2020
2 parents e52ed23 + a3e6cd3 commit 391e6ed
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 54 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ A browser extension that enhance all Merge Requests lists on any instance of Git

- Display source and target branches
- Buttons allowing to easily copy these branches name (can be disabled in the extension preferences)
- Button allowing to copy Merge Request information (useful when sharing the Merge Request on e.g instant messaging softwares)
- Can be disabled in the extension preferences
- Text format is customizable (with support of placeholders)
- Compatible with all GitLab editions (GitLab CE, GitLab EE, GitLab.com) (look at the prerequisites, though)
- No configuration needed

Expand All @@ -28,15 +31,15 @@ You can also install this add-on manually by using one of the ZIP files on the [

## Credits

- Logo by [Thanga Vignesh P](https://www.iconfinder.com/icons/5402348/add_list_playlist_icon) (CC BY-NC 3.0)
- Logo: [GitLab](https://about.gitlab.com/press/press-kit/#logos) and [Octicons](https://primer.style/octicons/git-pull-request) (MIT License)

## Roadmap

👉 = current version

- **1.0** - Initial release (display Merge Request source and target branches)
- 👉 **1.1** - Copy source and target branches name
- **1.2** - Copy basic Merge Request information (intended for sharing on e.g instant messaging softwares)
- **1.1** - Copy source and target branches name
- 👉 **1.2** - Copy Merge Request information (intended for sharing on e.g instant messaging softwares)
- **1.3** - Direct Jira ticket link (automatic detection of ticket ID in branch name or Merge Request title)
- **1.4** - WIP / unWIP toggle button

Expand Down
70 changes: 66 additions & 4 deletions css/options.css
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
/************************************************************************
* Global styles */

/*
* Firefox for Mac: use a dark theme if the OS/browser also uses one.
* This doesn't affects Firefox for Windows (as for now), as dark/light theme detection isn't implemented for this OS.
*/
@supports (-moz-appearance:none) {
@media (prefers-color-scheme: dark) {
body {
background-color: #202023;
color: rgb(249, 249, 250);
}
}
}

.is-hidden {
display: none;
}

/************************************************************************
* Text styles */

.txt-center {
text-align: center;
}

.monospaced {
font-family: monospace;
}

/************************************************************************
* Sizing styles */

.w100 {
width: 100%;
}

.w40p {
width: 40px;
}

/************************************************************************
* Preferences form */
* Layout styles */

.row {
display: table;
width: 100%;
}

.row > * {
display: table-cell;
}

/************************************************************************
* Margin and padding styles */

.pas {
padding: 5px;
}

.pts {
padding-top: 5px;
}

.pbs {
padding-bottom: 5px;
}

.pll {
padding-left: 40px;
}

form > * {
padding-top: 10px;
padding-bottom: 10px;
.man {
margin: 0;
}
27 changes: 24 additions & 3 deletions html/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,31 @@
</head>
<body>
<form>
<div class="browser-style">
<input type="checkbox" id="enable_buttons_to_copy_source_and_target_branches_name" class="browser-style"> <label for="enable_buttons_to_copy_source_and_target_branches_name">Enable buttons allowing to copy source and target branches name</label>
<div class="pts pbs man row">
<div class="w40p txt-center browser-style">
<input type="checkbox" id="enable_buttons_to_copy_source_and_target_branches_name">
</div>
<div>
<label for="enable_buttons_to_copy_source_and_target_branches_name">Enable buttons allowing to copy source and target branches name</label>
</div>
</div>
<div class="txt-center"><button type="submit" class="browser-style">Save preferences</button></div>
<div class="pts man row">
<div class="w40p txt-center browser-style">
<input type="checkbox" id="enable_button_to_copy_mr_info">
</div>
<div>
<label for="enable_button_to_copy_mr_info">Enable button allowing to copy Merge Request information</label>
</div>
</div>
<div class="pbs pll">
<small>Useful when sharing the Merge Request on e.g instant messaging softwares</small>
</div>
<div class="pll pts pbs man" id="copy-mr-info-options">
<div class="pbs"><label for="copy_mr_info_format">Text format:</label></div>
<div><textarea class="browser-style w100 monospaced man pas" id="copy_mr_info_format" rows="6" required></textarea></div>
<div class="pts"><small>Available placeholders: <code>{MR_TITLE}</code>, <code>{MR_ID}</code>, <code>{MR_URL}</code>, <code>{MR_DIFFS_URL}</code>, <code>{MR_AUTHOR_NAME}</code>, <code>{MR_STATUS}</code>, <code>{MR_SOURCE_BRANCH_NAME}</code>, <code>{MR_TARGET_BRANCH_NAME}</code></small></div>
</div>
<div class="txt-center pts pbs man"><button type="submit" class="browser-style">Save preferences</button></div>
</form>

<script src="../js/preferences.js"></script>
Expand Down
Binary file modified images/logo_128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/logo_16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/logo_48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/logo_96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
173 changes: 134 additions & 39 deletions js/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
this.baseApiUrl = this.baseUrl + '/api/v4/';
this.apiClient = new GitLabApiClient(this.baseApiUrl);

let currentMergeRequestIds = this.getCurrentMergeRequestIdsAndSetUuidDataAttributes();
let currentMergeRequestIds = this.getCurrentMergeRequestIds();
let preferencesManager = new globals.Gmrle.PreferencesManager();

let self = this;
Expand Down Expand Up @@ -121,18 +121,13 @@
}

/**
* Gets all Merge Requests IDs that are currently displayed AND sets the `iid` data attribute (public Merge
* Request identifier) on all DOM nodes representing a Merge Requests (it's used later in the process).
* Gets all Merge Requests IDs that are currently displayed.
*/
getCurrentMergeRequestIdsAndSetUuidDataAttributes() {
getCurrentMergeRequestIds() {
return Array.from(
document.querySelectorAll('.mr-list .merge-request')
document.querySelectorAll('.mr-list .merge-request .issuable-reference')
).map(function(el) {
let iid = el.querySelector('.issuable-reference').textContent.trim().replace('!', '');

el.dataset.iid = iid;

return iid;
return el.textContent.trim().replace('!', '');
});
}

Expand All @@ -148,7 +143,14 @@
if (this.status == 200) {
self.removeExistingTargetBranchNodes();
self.updateMergeRequestsNodes(this.response);
self.attachClickEventToCopyBranchNameButtons();

if (self.preferences.enable_buttons_to_copy_source_and_target_branches_name) {
self.attachClickEventToCopyBranchNameButtons();
}

if (self.preferences.enable_button_to_copy_mr_info) {
self.attachClickEventToCopyMergeRequestInfoButtons();
}
} else {
alert('Got error from GitLab, check console for more information.');

Expand All @@ -170,81 +172,174 @@
}

/**
* Append the given HTML string at the end of the given child target node.
* Parses HTML code and applies a callback on all of the parsed root DOM nodes.
*/
parseHtmlAndAppendChild(targetNode, html) {
parseHtml(html, callback) {
new DOMParser()
.parseFromString(html, 'text/html')
.querySelector('body')
.childNodes
.forEach(function(node) {
targetNode.appendChild(node);
callback(node);
}
)
);
}

/**
* Prepends the given HTML string at the beginning of the given child target node.
*/
parseHtmlAndPrepend(targetNode, html) {
this.parseHtml(html, function(node) {
targetNode.prepend(node);
});
}

/**
* Appends the given HTML string at the end of the given child target node.
*/
parseHtmlAndAppend(targetNode, html) {
this.parseHtml(html, function(node) {
targetNode.append(node);
});
}

/**
* Actually updates the UI by altering the DOM by adding our stuff.
*/
updateMergeRequestsNodes(mergeRequestsDetails) {
let self = this;

mergeRequestsDetails.forEach(function(mergeRequest) {
let infoDiv = document
.querySelector('.mr-list .merge-request[data-iid="' + mergeRequest.iid + '"] .issuable-main-info');
let mergeRequestContainer = document.querySelector('.mr-list .merge-request[data-id="' + mergeRequest.id + '"]');

this.setDataAttributesToMergeRequestContainer(mergeRequestContainer, mergeRequest);

// -----------------------------------------------
// Copy MR info button

if (this.preferences.enable_button_to_copy_mr_info) {
let copyMrInfoButton = '<button class="btn btn-secondary btn-md btn-default btn-transparent btn-clipboard has-tooltip gmrle-copy-mr-info" title="Copy Merge Request info">' +
'<i class="fa fa-share-square-o" aria-hidden="true"></i>' +
'</button> ';

this.parseHtmlAndPrepend(
mergeRequestContainer.querySelector('.issuable-info'),
copyMrInfoButton
);
}

// -----------------------------------------------
// Source and target branches info

let html = '<div class="issuable-info">' +
// Source branch name
let newInfoLineToInject = '<div class="issuable-info">' +
'<span class="project-ref-path has-tooltip" title="Source branch">' +
'<a class="ref-name" href="' + self.baseProjectUrl + '/-/commits/' + mergeRequest.source_branch + '">' + mergeRequest.source_branch + '</a>' +
'<a class="ref-name" href="' + this.baseProjectUrl + '/-/commits/' + mergeRequest.source_branch + '">' + mergeRequest.source_branch + '</a>' +
'</span>';

if (self.preferences.enable_buttons_to_copy_source_and_target_branches_name) {
html += ' <button class="btn btn-secondary btn-md btn-default btn-transparent btn-clipboard has-tooltip gmrle-copy-branch-name" title="Copy branch name" data-branch-name="' + mergeRequest.source_branch + '">' +
// Copy source branch name button
if (this.preferences.enable_buttons_to_copy_source_and_target_branches_name) {
newInfoLineToInject += ' <button class="btn btn-secondary btn-md btn-default btn-transparent btn-clipboard has-tooltip gmrle-copy-branch-name" title="Copy branch name" data-branch-name-to-copy="source">' +
'<i class="fa fa-clipboard" aria-hidden="true"></i>' +
'</button>'
'</button>';
}

html += ' <i class="fa fa-long-arrow-right" aria-hidden="true"></i> ' +
// Target branch name
newInfoLineToInject += ' <i class="fa fa-long-arrow-right" aria-hidden="true"></i> ' +
'<span class="project-ref-path has-tooltip" title="Target branch">' +
'<a class="ref-name" href="' + self.baseProjectUrl + '/-/commits/' + mergeRequest.target_branch + '">' + mergeRequest.target_branch + '</a>' +
'<a class="ref-name" href="' + this.baseProjectUrl + '/-/commits/' + mergeRequest.target_branch + '">' + mergeRequest.target_branch + '</a>' +
'</span>';

if (self.preferences.enable_buttons_to_copy_source_and_target_branches_name) {
html += ' <button class="btn btn-secondary btn-md btn-default btn-transparent btn-clipboard has-tooltip gmrle-copy-branch-name" title="Copy branch name" data-branch-name="' + mergeRequest.target_branch + '">' +
// Copy target branch name button
if (this.preferences.enable_buttons_to_copy_source_and_target_branches_name) {
newInfoLineToInject += ' <button class="btn btn-secondary btn-md btn-default btn-transparent btn-clipboard has-tooltip gmrle-copy-branch-name" title="Copy branch name" data-branch-name-to-copy="target">' +
'<i class="fa fa-clipboard" aria-hidden="true"></i>' +
'</button>';
}

html += '</div>';
newInfoLineToInject += '</div>';

self.parseHtmlAndAppendChild(
infoDiv,
html
this.parseHtmlAndAppend(
mergeRequestContainer.querySelector('.issuable-main-info'),
newInfoLineToInject
);
});
}, this);
}

/**
* Sets several data-* attributes on a DOM node representing a Merge Request so these values may be used later.
*/
setDataAttributesToMergeRequestContainer(mergeRequestContainer, mergeRequest) {
mergeRequestContainer.dataset.title = mergeRequest.title;
mergeRequestContainer.dataset.iid = mergeRequest.iid;
mergeRequestContainer.dataset.url = mergeRequest.web_url;
mergeRequestContainer.dataset.diffsUrl = mergeRequest.web_url + '/diffs';
mergeRequestContainer.dataset.authorName = mergeRequest.author.name;
mergeRequestContainer.dataset.status = mergeRequest.state;
mergeRequestContainer.dataset.sourceBranchName = mergeRequest.source_branch;
mergeRequestContainer.dataset.targetBranchName = mergeRequest.target_branch;
}

/**
* Attach a click event to all buttons inserted by the extension allowing to copy the source and target
* branches name (if feature is enabled by the user).
* branches name.
*/
attachClickEventToCopyBranchNameButtons() {
if (!this.preferences.enable_buttons_to_copy_source_and_target_branches_name) {
return
}

document.querySelectorAll('button.gmrle-copy-branch-name').forEach(function(el) {
el.addEventListener('click', function(e) {
e.preventDefault();

navigator.clipboard.writeText(el.dataset.branchName).then(function() {
let branchName = this.closest('.merge-request').dataset[this.dataset.branchNameToCopy + 'BranchName'];

navigator.clipboard.writeText(branchName).then(function() {
// Do nothing if copy was successful.
}, function() {
alert('Unable to copy branch name.');
});
});
});
}

/**
* Attach a click event to all buttons inserted by the extension allowing to copy Merge Request info.
*/
attachClickEventToCopyMergeRequestInfoButtons() {
let self = this;

document.querySelectorAll('button.gmrle-copy-mr-info').forEach(function(el) {
el.addEventListener('click', function(e) {
e.preventDefault();

let text = self.buildMergeRequestInfoText(this.closest('.merge-request'));

navigator.clipboard.writeText(text).then(function() {
// Do nothing if copy was successful.
}, function() {
alert('Unable to copy Merge Request info.');
});
});
});
}

/**
* Creates the Merge Request info text from a Merge Request container DOM node.
*/
buildMergeRequestInfoText(mergeRequestContainer) {
let placeholders = {
'MR_TITLE': mergeRequestContainer.dataset.title,
'MR_ID': mergeRequestContainer.dataset.iid,
'MR_URL': mergeRequestContainer.dataset.url,
'MR_DIFFS_URL': mergeRequestContainer.dataset.diffsUrl,
'MR_AUTHOR_NAME': mergeRequestContainer.dataset.authorName,
'MR_STATUS': mergeRequestContainer.dataset.status,
'MR_SOURCE_BRANCH_NAME': mergeRequestContainer.dataset.sourceBranchName,
'MR_TARGET_BRANCH_NAME': mergeRequestContainer.dataset.targetBranchName
};

let placeholdersReplaceRegex = new RegExp('{(' + Object.keys(placeholders).join('|') + ')}', 'g');

return this.preferences.copy_mr_info_format.replace(placeholdersReplaceRegex, function(_, placeholder) {
return placeholders[placeholder];
});
}
}

let cs = new ContentScript();
Expand Down
Loading

0 comments on commit 391e6ed

Please sign in to comment.