From 9377eb18224aaee188ff1f497c043c9fc3af3059 Mon Sep 17 00:00:00 2001 From: Sarah Groff Hennigh-Palermo Date: Wed, 29 May 2019 20:24:15 +0000 Subject: Build visual_review_toolbar with webpack It takes a lot of lines to move a script --- vendor/assets/javascripts/visual_review_toolbar.js | 377 +++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 vendor/assets/javascripts/visual_review_toolbar.js (limited to 'vendor/assets') diff --git a/vendor/assets/javascripts/visual_review_toolbar.js b/vendor/assets/javascripts/visual_review_toolbar.js new file mode 100644 index 00000000000..12a3a4c9672 --- /dev/null +++ b/vendor/assets/javascripts/visual_review_toolbar.js @@ -0,0 +1,377 @@ +/////////////////////////////////////////////// +/////////////////// STYLES //////////////////// +/////////////////////////////////////////////// + +// this style must be applied inline +const buttonClearStyles = ` + -webkit-appearance: none; +`; + +/////////////////////////////////////////////// +/////////////////// STATE //////////////////// +/////////////////////////////////////////////// +const data = {}; + +/////////////////////////////////////////////// +///////////////// COMPONENTS ////////////////// +/////////////////////////////////////////////// +const note = ` +

+`; + +const comment = ` +
+ + ${note} + +
+
+ + +
+`; + +const commentIcon = ` + icn/comment +`; + +const compressIcon = ` + icn/compress +`; + +const collapseButton = ` + +`; + +const form = content => ` +
+ ${content} +
+`; + +const login = ` +
+ + + ${note} +
+
+ + +
+
+ +
+`; + +/////////////////////////////////////////////// +//////////////// INTERACTIONS ///////////////// +/////////////////////////////////////////////// + +// from https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator +function getBrowserId(sUsrAg) { + var aKeys = ['MSIE', 'Edge', 'Firefox', 'Safari', 'Chrome', 'Opera'], + nIdx = aKeys.length - 1; + + for (nIdx; nIdx > -1 && sUsrAg.indexOf(aKeys[nIdx]) === -1; nIdx--); + return aKeys[nIdx]; +} + +function addCommentForm() { + const formWrapper = document.getElementById('gitlab-form-wrapper'); + formWrapper.innerHTML = comment; +} + +function addLoginForm() { + const formWrapper = document.getElementById('gitlab-form-wrapper'); + formWrapper.innerHTML = login; +} + +function authorizeUser() { + // Clear any old errors + clearNote('gitlab-token'); + + const token = document.getElementById('gitlab-token').value; + const rememberMe = document.getElementById('remember_token').checked; + + if (!token) { + postError('Please enter your token.', 'gitlab-token'); + return; + } + + if (rememberMe) { + storeToken(token); + } + + authSuccess(token); + return; +} + +function authSuccess(token) { + data.token = token; + addCommentForm(); +} + +function clearNote(inputId) { + const note = document.getElementById('gitlab-validation-note'); + note.innerText = ''; + note.style.color = ''; + + if (inputId) { + const field = document.getElementById(inputId); + field.style.borderColor = ''; + } +} + +function confirmAndClear(mergeRequestId) { + const commentButton = document.getElementById('gitlab-comment-button'); + const note = document.getElementById('gitlab-validation-note'); + + commentButton.innerText = 'Feedback sent'; + note.innerText = `Your comment was successfully posted to merge request #${mergeRequestId}`; + + setTimeout(resetCommentButton, 1000); +} + +function getInitialState() { + const { localStorage } = window; + + try { + let token = localStorage.getItem('token'); + + if (token) { + data.token = token; + return comment; + } + + return login; + } catch (err) { + return login; + } +} + +function getProjectDetails() { + const { + innerWidth, + innerHeight, + location: { href }, + navigator: { platform, userAgent }, + } = window; + const browser = getBrowserId(userAgent); + + const scriptEl = document.getElementById('review-app-toolbar-script'); + const { projectId, mergeRequestId, mrUrl } = scriptEl.dataset; + + return { + href, + platform, + browser, + userAgent, + innerWidth, + innerHeight, + projectId, + mergeRequestId, + mrUrl, + }; +} + +function logoutUser() { + const { localStorage } = window; + + // All the browsers we support have localStorage, so let's silently fail + // and go on with the rest of the functionality. + try { + localStorage.removeItem('token'); + } catch (err) { + return; + } + + addLoginForm(); +} + +function postComment({ + href, + platform, + browser, + userAgent, + innerWidth, + innerHeight, + projectId, + mergeRequestId, + mrUrl, +}) { + // Clear any old errors + clearNote('gitlab-comment'); + + setInProgressState(); + + const commentText = document.getElementById('gitlab-comment').value.trim(); + + if (!commentText) { + postError('Your comment appears to be empty.', 'gitlab-comment'); + resetCommentBox(); + return; + } + + const detailText = ` + \n +
+ Metadata + Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}. +

+ User agent: ${userAgent} +
+ `; + + const url = ` + ${mrUrl}/api/v4/projects/${projectId}/merge_requests/${mergeRequestId}/discussions`; + + const body = `${commentText} ${detailText}`; + + fetch(url, { + method: 'POST', + headers: { + 'PRIVATE-TOKEN': data.token, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ body }), + }) + .then(response => { + if (response.ok) { + confirmAndClear(mergeRequestId); + return; + } + + throw new Error(`${response.status}: ${response.statusText}`); + }) + .catch(err => { + postError( + `The feedback was not sent successfully. Please try again. Error: ${err.message}`, + 'gitlab-comment', + ); + resetCommentBox(); + }); +} + +function postError(message, inputId) { + const note = document.getElementById('gitlab-validation-note'); + const field = document.getElementById(inputId); + field.style.borderColor = '#db3b21'; + note.style.color = '#db3b21'; + note.innerText = message; +} + +function resetCommentBox() { + const commentBox = document.getElementById('gitlab-comment'); + const commentButton = document.getElementById('gitlab-comment-button'); + + commentButton.innerText = 'Send feedback'; + commentButton.classList.replace('gitlab-button-secondary', 'gitlab-button-success'); + commentButton.style.opacity = 1; + + commentBox.style.pointerEvents = 'auto'; + commentBox.style.color = 'rgba(0, 0, 0, 1)'; +} + +function resetCommentButton() { + const commentBox = document.getElementById('gitlab-comment'); + const note = document.getElementById('gitlab-validation-note'); + + commentBox.value = ''; + note.innerText = ''; + resetCommentBox(); +} + +function setInProgressState() { + const commentButton = document.getElementById('gitlab-comment-button'); + const commentBox = document.getElementById('gitlab-comment'); + + commentButton.innerText = 'Sending feedback'; + commentButton.classList.replace('gitlab-button-success', 'gitlab-button-secondary'); + commentButton.style.opacity = 0.5; + commentBox.style.color = 'rgba(223, 223, 223, 0.5)'; + commentBox.style.pointerEvents = 'none'; +} + +function storeToken(token) { + const { localStorage } = window; + + // All the browsers we support have localStorage, so let's silently fail + // and go on with the rest of the functionality. + try { + localStorage.setItem('token', token); + } catch (err) { + return; + } +} + +function toggleForm() { + const container = document.getElementById('gitlab-review-container'); + const collapseButton = document.getElementById('gitlab-collapse'); + const form = document.getElementById('gitlab-form-wrapper'); + const OPEN = 'open'; + const CLOSED = 'closed'; + + const stateVals = { + [OPEN]: { + buttonClasses: ['gitlab-collapse-closed', 'gitlab-collapse-open'], + containerClasses: ['gitlab-closed-wrapper', 'gitlab-open-wrapper'], + icon: compressIcon, + display: 'flex', + backgroundColor: 'rgba(255, 255, 255, 1)', + }, + [CLOSED]: { + buttonClasses: ['gitlab-collapse-open', 'gitlab-collapse-closed'], + containerClasses: ['gitlab-open-wrapper', 'gitlab-closed-wrapper'], + icon: commentIcon, + display: 'none', + backgroundColor: 'rgba(255, 255, 255, 0)', + }, + }; + + const nextState = collapseButton.classList.contains('gitlab-collapse-open') ? CLOSED : OPEN; + + container.classList.replace(...stateVals[nextState].containerClasses); + container.style.backgroundColor = stateVals[nextState].backgroundColor; + form.style.display = stateVals[nextState].display; + collapseButton.classList.replace(...stateVals[nextState].buttonClasses); + collapseButton.innerHTML = stateVals[nextState].icon; +} + +/////////////////////////////////////////////// +///////////////// INJECTION ////////////////// +/////////////////////////////////////////////// + +function noop() {} + +const eventLookup = ({ target: { id } }) => { + switch (id) { + case 'gitlab-collapse': + return toggleForm; + case 'gitlab-comment-button': + const projectDetails = getProjectDetails(); + return postComment.bind(null, projectDetails); + case 'gitlab-login': + return authorizeUser; + case 'gitlab-logout-button': + return logoutUser; + default: + return noop; + } +}; + +window.addEventListener('load', () => { + const content = getInitialState(); + const container = document.createElement('div'); + + container.setAttribute('id', 'gitlab-review-container'); + container.insertAdjacentHTML('beforeend', collapseButton); + container.insertAdjacentHTML('beforeend', form(content)); + + document.body.insertBefore(container, document.body.firstChild); + + document.getElementById('gitlab-review-container').addEventListener('click', event => { + eventLookup(event)(); + }); + +}); -- cgit v1.2.3