Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
13861f60
Commit
13861f60
authored
Jun 14, 2019
by
Sarah Groff Hennigh-Palermo
Committed by
Mike Greiling
Jun 14, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Breakup first pass
It has a lot of files because that's the point
parent
09238aba
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
571 additions
and
378 deletions
+571
-378
app/assets/javascripts/visual_review_toolbar/components/comment.js
...s/javascripts/visual_review_toolbar/components/comment.js
+127
-0
app/assets/javascripts/visual_review_toolbar/components/constants.js
...javascripts/visual_review_toolbar/components/constants.js
+37
-0
app/assets/javascripts/visual_review_toolbar/components/index.js
...ets/javascripts/visual_review_toolbar/components/index.js
+23
-0
app/assets/javascripts/visual_review_toolbar/components/login.js
...ets/javascripts/visual_review_toolbar/components/login.js
+51
-0
app/assets/javascripts/visual_review_toolbar/components/note.js
...sets/javascripts/visual_review_toolbar/components/note.js
+27
-0
app/assets/javascripts/visual_review_toolbar/components/utils.js
...ets/javascripts/visual_review_toolbar/components/utils.js
+41
-0
app/assets/javascripts/visual_review_toolbar/components/wrapper.js
...s/javascripts/visual_review_toolbar/components/wrapper.js
+82
-0
app/assets/javascripts/visual_review_toolbar/components/wrapper_icons.js
...scripts/visual_review_toolbar/components/wrapper_icons.js
+15
-0
app/assets/javascripts/visual_review_toolbar/index.js
app/assets/javascripts/visual_review_toolbar/index.js
+36
-1
app/assets/javascripts/visual_review_toolbar/store/events.js
app/assets/javascripts/visual_review_toolbar/store/events.js
+36
-0
app/assets/javascripts/visual_review_toolbar/store/index.js
app/assets/javascripts/visual_review_toolbar/store/index.js
+5
-0
app/assets/javascripts/visual_review_toolbar/store/state.js
app/assets/javascripts/visual_review_toolbar/store/state.js
+76
-0
app/assets/javascripts/visual_review_toolbar/store/utils.js
app/assets/javascripts/visual_review_toolbar/store/utils.js
+15
-0
vendor/assets/javascripts/visual_review_toolbar.js
vendor/assets/javascripts/visual_review_toolbar.js
+0
-377
No files found.
app/assets/javascripts/visual_review_toolbar/components/comment.js
0 → 100644
View file @
13861f60
import
{
BLACK
,
COMMENT_BOX
,
MUTED
,
LOGOUT
}
from
'
./constants
'
;
import
{
clearNote
,
note
,
postError
}
from
'
./note
'
;
import
{
buttonClearStyles
,
selectCommentBox
,
selectCommentButton
,
selectNote
}
from
'
./utils
'
;
const
comment
=
`
<div>
<textarea id="
${
COMMENT_BOX
}
" name="
${
COMMENT_BOX
}
" rows="3" placeholder="Enter your feedback or idea" class="gitlab-input" aria-required="true"></textarea>
${
note
}
<p class="gitlab-metadata-note">Additional metadata will be included: browser, OS, current page, user agent, and viewport dimensions.</p>
</div>
<div class="gitlab-button-wrapper">
<button class="gitlab-button gitlab-button-secondary" style="
${
buttonClearStyles
}
" type="button" id="
${
LOGOUT
}
"> Logout </button>
<button class="gitlab-button gitlab-button-success" style="
${
buttonClearStyles
}
" type="button" id="gitlab-comment-button"> Send feedback </button>
</div>
`
;
const
resetCommentBox
=
()
=>
{
const
commentBox
=
selectCommentBox
();
const
commentButton
=
selectCommentButton
();
commentButton
.
innerText
=
'
Send feedback
'
;
commentButton
.
classList
.
replace
(
'
gitlab-button-secondary
'
,
'
gitlab-button-success
'
);
commentButton
.
style
.
opacity
=
1
;
commentBox
.
style
.
pointerEvents
=
'
auto
'
;
commentBox
.
style
.
color
=
BLACK
;
};
const
resetCommentButton
=
()
=>
{
const
commentBox
=
selectCommentBox
();
const
currentNote
=
selectNote
();
commentBox
.
value
=
''
;
currentNote
.
innerText
=
''
;
};
const
resetComment
=
()
=>
{
resetCommentBox
();
resetCommentButton
();
};
const
confirmAndClear
=
mergeRequestId
=>
{
const
commentButton
=
selectCommentButton
();
const
currentNote
=
selectNote
();
commentButton
.
innerText
=
'
Feedback sent
'
;
currentNote
.
innerText
=
`Your comment was successfully posted to merge request #
${
mergeRequestId
}
`
;
setTimeout
(
resetComment
,
2000
);
};
const
setInProgressState
=
()
=>
{
const
commentButton
=
selectCommentButton
();
const
commentBox
=
selectCommentBox
();
commentButton
.
innerText
=
'
Sending feedback
'
;
commentButton
.
classList
.
replace
(
'
gitlab-button-success
'
,
'
gitlab-button-secondary
'
);
commentButton
.
style
.
opacity
=
0.5
;
commentBox
.
style
.
color
=
MUTED
;
commentBox
.
style
.
pointerEvents
=
'
none
'
;
};
const
postComment
=
({
href
,
platform
,
browser
,
userAgent
,
innerWidth
,
innerHeight
,
projectId
,
mergeRequestId
,
mrUrl
,
token
,
})
=>
{
// Clear any old errors
clearNote
(
COMMENT_BOX
);
setInProgressState
();
const
commentText
=
selectCommentBox
().
value
.
trim
();
if
(
!
commentText
)
{
postError
(
'
Your comment appears to be empty.
'
,
COMMENT_BOX
);
resetCommentBox
();
return
;
}
const
detailText
=
`
\n
<details>
<summary>Metadata</summary>
Posted from
${
href
}
|
${
platform
}
|
${
browser
}
|
${
innerWidth
}
x
${
innerHeight
}
.
<br /><br />
<em>User agent:
${
userAgent
}
</em>
</details>
`
;
const
url
=
`
${
mrUrl
}
/api/v4/projects/
${
projectId
}
/merge_requests/
${
mergeRequestId
}
/discussions`
;
const
body
=
`
${
commentText
}
${
detailText
}
`
;
fetch
(
url
,
{
method
:
'
POST
'
,
headers
:
{
'
PRIVATE-TOKEN
'
:
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
(
`Your comment could not be sent. Please try again. Error:
${
err
.
message
}
`
,
COMMENT_BOX
,
);
resetCommentBox
();
});
};
export
{
comment
,
postComment
};
app/assets/javascripts/visual_review_toolbar/components/constants.js
0 → 100644
View file @
13861f60
// component selectors
const
COLLAPSE_BUTTON
=
'
gitlab-collapse
'
;
const
COMMENT_BOX
=
'
gitlab-comment
'
;
const
COMMENT_BUTTON
=
'
gitlab-comment-button
'
;
const
FORM
=
'
gitlab-form-wrapper
'
;
const
LOGIN
=
'
gitlab-login
'
;
const
LOGOUT
=
'
gitlab-logout-button
'
;
const
NOTE
=
'
gitlab-validation-note
'
;
const
REMEMBER_TOKEN
=
'
gitlab-remember_token
'
;
const
REVIEW_CONTAINER
=
'
gitlab-review-container
'
;
const
TOKEN_BOX
=
'
gitlab-token
'
;
// colors — these are applied programmatically
// rest of styles belong in ./styles
const
BLACK
=
'
rgba(46, 46, 46, 1)
'
;
const
CLEAR
=
'
rgba(255, 255, 255, 0)
'
;
const
MUTED
=
'
rgba(223, 223, 223, 0.5)
'
;
const
RED
=
'
rgba(219, 59, 33, 1)
'
;
const
WHITE
=
'
rgba(255, 255, 255, 1)
'
;
export
{
COLLAPSE_BUTTON
,
COMMENT_BOX
,
COMMENT_BUTTON
,
FORM
,
LOGIN
,
LOGOUT
,
NOTE
,
REMEMBER_TOKEN
,
REVIEW_CONTAINER
,
TOKEN_BOX
,
BLACK
,
CLEAR
,
MUTED
,
RED
,
WHITE
,
};
app/assets/javascripts/visual_review_toolbar/components/index.js
0 → 100644
View file @
13861f60
import
{
comment
,
postComment
}
from
'
./comment
'
;
import
{
COLLAPSE_BUTTON
,
COMMENT_BUTTON
,
LOGIN
,
LOGOUT
,
REVIEW_CONTAINER
}
from
'
./constants
'
;
import
{
authorizeUser
,
login
}
from
'
./login
'
;
import
{
selectContainer
}
from
'
./utils
'
;
import
{
form
,
logoutUser
,
toggleForm
}
from
'
./wrapper
'
;
import
{
collapseButton
}
from
'
./wrapper_icons
'
;
export
{
authorizeUser
,
collapseButton
,
comment
,
form
,
login
,
logoutUser
,
postComment
,
selectContainer
,
toggleForm
,
COLLAPSE_BUTTON
,
COMMENT_BUTTON
,
LOGIN
,
LOGOUT
,
REVIEW_CONTAINER
,
};
app/assets/javascripts/visual_review_toolbar/components/login.js
0 → 100644
View file @
13861f60
import
{
LOGIN
,
REMEMBER_TOKEN
,
TOKEN_BOX
}
from
'
./constants
'
;
import
{
clearNote
,
note
,
postError
}
from
'
./note
'
;
import
{
buttonClearStyles
,
selectRemember
,
selectToken
}
from
'
./utils
'
;
import
{
addCommentForm
}
from
'
./wrapper
'
;
const
login
=
`
<div>
<label for="
${
TOKEN_BOX
}
" class="gitlab-label">Enter your <a class="gitlab-link" href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">personal access token</a></label>
<input class="gitlab-input" type="password" id="
${
TOKEN_BOX
}
" name="
${
TOKEN_BOX
}
" aria-required="true" autocomplete="current-password">
${
note
}
</div>
<div class="gitlab-checkbox-wrapper">
<input type="checkbox" id="
${
REMEMBER_TOKEN
}
" name="
${
REMEMBER_TOKEN
}
" value="remember">
<label for="
${
REMEMBER_TOKEN
}
" class="gitlab-checkbox-label">Remember me</label>
</div>
<div class="gitlab-button-wrapper">
<button class="gitlab-button-wide gitlab-button gitlab-button-success" style="
${
buttonClearStyles
}
" type="button" id="
${
LOGIN
}
"> Submit </button>
</div>
`
;
const
storeToken
=
(
token
,
state
)
=>
{
const
{
localStorage
}
=
window
;
const
rememberMe
=
selectRemember
().
checked
;
// All the browsers we support have localStorage, so let's silently fail
// and go on with the rest of the functionality.
try
{
if
(
rememberMe
)
{
localStorage
.
setItem
(
'
token
'
,
token
);
}
}
finally
{
state
.
token
=
token
;
}
};
const
authorizeUser
=
state
=>
{
// Clear any old errors
clearNote
(
TOKEN_BOX
);
const
token
=
selectToken
().
value
;
if
(
!
token
)
{
postError
(
'
Please enter your token.
'
,
TOKEN_BOX
);
return
;
}
storeToken
(
token
,
state
);
addCommentForm
();
};
export
{
authorizeUser
,
login
};
app/assets/javascripts/visual_review_toolbar/components/note.js
0 → 100644
View file @
13861f60
import
{
NOTE
,
RED
}
from
'
./constants
'
;
import
{
selectById
,
selectNote
}
from
'
./utils
'
;
const
note
=
`
<p id=
${
NOTE
}
class='gitlab-message'></p>
`
;
const
clearNote
=
inputId
=>
{
const
currentNote
=
selectNote
();
currentNote
.
innerText
=
''
;
currentNote
.
style
.
color
=
''
;
if
(
inputId
)
{
const
field
=
document
.
getElementById
(
inputId
);
field
.
style
.
borderColor
=
''
;
}
};
const
postError
=
(
message
,
inputId
)
=>
{
const
currentNote
=
selectNote
();
const
field
=
selectById
(
inputId
);
field
.
style
.
borderColor
=
RED
;
currentNote
.
style
.
color
=
RED
;
currentNote
.
innerText
=
message
;
};
export
{
clearNote
,
note
,
postError
};
app/assets/javascripts/visual_review_toolbar/components/utils.js
0 → 100644
View file @
13861f60
/* global document */
import
{
COLLAPSE_BUTTON
,
COMMENT_BOX
,
COMMENT_BUTTON
,
FORM
,
NOTE
,
REMEMBER_TOKEN
,
REVIEW_CONTAINER
,
TOKEN_BOX
,
}
from
'
./constants
'
;
// this style must be applied inline in a handful of components
const
buttonClearStyles
=
`
-webkit-appearance: none;
`
;
// selector functions to abstract out a little
const
selectById
=
id
=>
document
.
getElementById
(
id
);
const
selectCollapseButton
=
()
=>
document
.
getElementById
(
COLLAPSE_BUTTON
);
const
selectCommentBox
=
()
=>
document
.
getElementById
(
COMMENT_BOX
);
const
selectCommentButton
=
()
=>
document
.
getElementById
(
COMMENT_BUTTON
);
const
selectContainer
=
()
=>
document
.
getElementById
(
REVIEW_CONTAINER
);
const
selectForm
=
()
=>
document
.
getElementById
(
FORM
);
const
selectNote
=
()
=>
document
.
getElementById
(
NOTE
);
const
selectRemember
=
()
=>
document
.
getElementById
(
REMEMBER_TOKEN
);
const
selectToken
=
()
=>
document
.
getElementById
(
TOKEN_BOX
);
export
{
buttonClearStyles
,
selectById
,
selectCollapseButton
,
selectContainer
,
selectCommentBox
,
selectCommentButton
,
selectForm
,
selectNote
,
selectRemember
,
selectToken
,
};
app/assets/javascripts/visual_review_toolbar/components/wrapper.js
0 → 100644
View file @
13861f60
import
{
comment
}
from
'
./comment
'
;
import
{
CLEAR
,
FORM
,
WHITE
}
from
'
./constants
'
;
import
{
login
}
from
'
./login
'
;
import
{
selectCollapseButton
,
selectContainer
,
selectForm
}
from
'
./utils
'
;
import
{
commentIcon
,
compressIcon
}
from
'
./wrapper_icons
'
;
const
form
=
content
=>
`
<form id=
${
FORM
}
>
${
content
}
</form>
`
;
const
addCommentForm
=
()
=>
{
const
formWrapper
=
selectForm
();
formWrapper
.
innerHTML
=
comment
;
};
const
addLoginForm
=
()
=>
{
const
formWrapper
=
selectForm
();
formWrapper
.
innerHTML
=
login
;
};
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
toggleForm
()
{
const
container
=
selectContainer
();
const
collapseButton
=
selectCollapseButton
();
const
currentForm
=
selectForm
();
const
OPEN
=
'
open
'
;
const
CLOSED
=
'
closed
'
;
/*
You may wonder why we spread the arrays before we reverse them.
In the immortal words of MDN,
Careful: reverse is destructive. It also changes the original array
*/
const
openButtonClasses
=
[
'
gitlab-collapse-closed
'
,
'
gitlab-collapse-open
'
];
const
closedButtonClasses
=
[...
openButtonClasses
].
reverse
();
const
openContainerClasses
=
[
'
gitlab-closed-wrapper
'
,
'
gitlab-open-wrapper
'
];
const
closedContainerClasses
=
[...
openContainerClasses
].
reverse
();
const
stateVals
=
{
[
OPEN
]:
{
buttonClasses
:
openButtonClasses
,
containerClasses
:
openContainerClasses
,
icon
:
compressIcon
,
display
:
'
flex
'
,
backgroundColor
:
WHITE
,
},
[
CLOSED
]:
{
buttonClasses
:
closedButtonClasses
,
containerClasses
:
closedContainerClasses
,
icon
:
commentIcon
,
display
:
'
none
'
,
backgroundColor
:
CLEAR
,
},
};
const
nextState
=
collapseButton
.
classList
.
contains
(
'
gitlab-collapse-open
'
)
?
CLOSED
:
OPEN
;
const
currentVals
=
stateVals
[
nextState
];
container
.
classList
.
replace
(...
currentVals
.
containerClasses
);
container
.
style
.
backgroundColor
=
currentVals
.
backgroundColor
;
currentForm
.
style
.
display
=
currentVals
.
display
;
collapseButton
.
classList
.
replace
(...
currentVals
.
buttonClasses
);
collapseButton
.
innerHTML
=
currentVals
.
icon
;
}
export
{
addCommentForm
,
addLoginForm
,
form
,
logoutUser
,
toggleForm
};
app/assets/javascripts/visual_review_toolbar/components/wrapper_icons.js
0 → 100644
View file @
13861f60
import
{
buttonClearStyles
}
from
'
./utils
'
;
const
commentIcon
=
`
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="gitlab-comment-icon"/></svg>
`
;
const
compressIcon
=
`
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/compress</title><path d="M5.27 12.182l-1.562 1.561a1 1 0 0 1-1.414 0h-.001a1 1 0 0 1 0-1.415l1.56-1.56L2.44 9.353a.5.5 0 0 1 .353-.854H7.09a.5.5 0 0 1 .5.5v4.294a.5.5 0 0 1-.853.353l-1.467-1.465zm6.911-6.914l1.464 1.464a.5.5 0 0 1-.353.854H8.999a.5.5 0 0 1-.5-.5V2.793a.5.5 0 0 1 .854-.354l1.414 1.415 1.56-1.561a1 1 0 1 1 1.415 1.414l-1.561 1.56z" id="gitlab-compress-icon"/></svg>
`
;
const
collapseButton
=
`
<button id='gitlab-collapse' style='
${
buttonClearStyles
}
' class='gitlab-button gitlab-button-secondary gitlab-collapse gitlab-collapse-open'>
${
compressIcon
}
</button>
`
;
export
{
commentIcon
,
compressIcon
,
collapseButton
};
app/assets/javascripts/visual_review_toolbar/index.js
View file @
13861f60
import
'
./styles/toolbar.css
'
;
import
'
vendor/visual_review_toolbar
'
;
import
{
form
,
selectContainer
,
REVIEW_CONTAINER
}
from
'
./components
'
;
import
{
debounce
,
eventLookup
,
getInitialView
,
initializeState
,
updateWindowSize
}
from
'
./store
'
;
/*
Welcome to the visual review toolbar files. A few useful notes:
- These files build a static script that is served from our webpack
assets folder. (https://gitlab.com/assets/webpack/visual_review_toolbar.js)
- To compile this file, run `yarn webpack-vrt`.
- Vue is not used in these files because we do not want to ask users to
install another library at this time. It's all pure vanilla javascript.
*/
window
.
addEventListener
(
'
load
'
,
()
=>
{
initializeState
(
window
,
document
);
const
{
content
,
toggleButton
}
=
getInitialView
(
window
);
const
container
=
document
.
createElement
(
'
div
'
);
container
.
setAttribute
(
'
id
'
,
REVIEW_CONTAINER
);
container
.
insertAdjacentHTML
(
'
beforeend
'
,
toggleButton
);
container
.
insertAdjacentHTML
(
'
beforeend
'
,
form
(
content
));
document
.
body
.
insertBefore
(
container
,
document
.
body
.
firstChild
);
selectContainer
().
addEventListener
(
'
click
'
,
event
=>
{
eventLookup
(
event
)();
});
window
.
addEventListener
(
'
resize
'
,
debounce
(
updateWindowSize
.
bind
(
null
,
window
),
200
));
});
app/assets/javascripts/visual_review_toolbar/store/events.js
0 → 100644
View file @
13861f60
import
{
authorizeUser
,
logoutUser
,
postComment
,
toggleForm
,
COLLAPSE_BUTTON
,
COMMENT_BUTTON
,
LOGIN
,
LOGOUT
,
}
from
'
../components
'
;
import
{
state
}
from
'
./state
'
;
const
noop
=
()
=>
{};
const
eventLookup
=
({
target
:
{
id
}
})
=>
{
switch
(
id
)
{
case
COLLAPSE_BUTTON
:
return
toggleForm
;
case
COMMENT_BUTTON
:
return
postComment
.
bind
(
null
,
state
);
case
LOGIN
:
return
authorizeUser
.
bind
(
null
,
state
);
case
LOGOUT
:
return
logoutUser
;
default
:
return
noop
;
}
};
const
updateWindowSize
=
wind
=>
{
state
.
innerWidth
=
wind
.
innerWidth
;
state
.
innerHeight
=
wind
.
innerHeight
;
};
export
{
eventLookup
,
updateWindowSize
};
app/assets/javascripts/visual_review_toolbar/store/index.js
0 → 100644
View file @
13861f60
import
{
eventLookup
,
updateWindowSize
}
from
'
./events
'
;
import
{
getInitialView
,
initializeState
}
from
'
./state
'
;
import
debounce
from
'
./utils
'
;
export
{
debounce
,
eventLookup
,
getInitialView
,
initializeState
,
updateWindowSize
};
app/assets/javascripts/visual_review_toolbar/store/state.js
0 → 100644
View file @
13861f60
import
{
comment
,
login
,
collapseButton
}
from
'
../components
'
;
const
state
=
{
browser
:
''
,
href
:
''
,
innerWidth
:
''
,
innerHeight
:
''
,
mergeRequestId
:
''
,
mrUrl
:
''
,
platform
:
''
,
projectId
:
''
,
userAgent
:
''
,
token
:
''
,
};
// adapted from https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator#Example_2_Browser_detect_and_return_an_index
const
getBrowserId
=
sUsrAg
=>
{
const
aKeys
=
[
'
MSIE
'
,
'
Edge
'
,
'
Firefox
'
,
'
Safari
'
,
'
Chrome
'
,
'
Opera
'
];
let
nIdx
=
aKeys
.
length
-
1
;
for
(
nIdx
;
nIdx
>
-
1
&&
sUsrAg
.
indexOf
(
aKeys
[
nIdx
])
===
-
1
;
nIdx
-=
1
);
return
aKeys
[
nIdx
];
};
const
initializeState
=
(
wind
,
doc
)
=>
{
const
{
innerWidth
,
innerHeight
,
location
:
{
href
},
navigator
:
{
platform
,
userAgent
},
}
=
wind
;
const
browser
=
getBrowserId
(
userAgent
);
const
scriptEl
=
doc
.
getElementById
(
'
review-app-toolbar-script
'
);
const
{
projectId
,
mergeRequestId
,
mrUrl
}
=
scriptEl
.
dataset
;
// This mutates our default state object above. It's weird but it makes the linter happy.
Object
.
assign
(
state
,
{
browser
,
href
,
innerWidth
,
innerHeight
,
mergeRequestId
,
mrUrl
,
platform
,
projectId
,
userAgent
,
});
};
function
getInitialView
({
localStorage
})
{
const
loginView
=
{
content
:
login
,
toggleButton
:
collapseButton
,
};
const
commentView
=
{
content
:
comment
,
toggleButton
:
collapseButton
,
};
try
{
const
token
=
localStorage
.
getItem
(
'
token
'
);
if
(
token
)
{
state
.
token
=
token
;
return
commentView
;
}
return
loginView
;
}
catch
(
err
)
{
return
loginView
;
}
}
export
{
initializeState
,
getInitialView
,
state
};
app/assets/javascripts/visual_review_toolbar/store/utils.js
0 → 100644
View file @
13861f60
const
debounce
=
(
fn
,
time
)
=>
{
let
current
;
const
debounced
=
()
=>
{
if
(
current
)
{
clearTimeout
(
current
);
}
current
=
setTimeout
(
fn
,
time
);
};
return
debounced
;
};
export
default
debounce
;
vendor/assets/javascripts/visual_review_toolbar.js
deleted
100644 → 0
View file @
09238aba
///////////////////////////////////////////////
/////////////////// STYLES ////////////////////
///////////////////////////////////////////////
// this style must be applied inline
const
buttonClearStyles
=
`
-webkit-appearance: none;
`
;
///////////////////////////////////////////////
/////////////////// STATE ////////////////////
///////////////////////////////////////////////
const
data
=
{};
///////////////////////////////////////////////
///////////////// COMPONENTS //////////////////
///////////////////////////////////////////////
const
note
=
`
<p id='gitlab-validation-note' class='gitlab-message'></p>
`
;
const
comment
=
`
<div>
<textarea id='gitlab-comment' name='gitlab-comment' rows='3' placeholder='Enter your feedback or idea' class='gitlab-input'></textarea>
${
note
}
<p class='gitlab-metadata-note'>Additional metadata will be included: browser, OS, current page, user agent, and viewport dimensions.</p>
</div>
<div class='gitlab-button-wrapper''>
<button class='gitlab-button gitlab-button-secondary' style='
${
buttonClearStyles
}
' type='button' id='gitlab-logout-button'> Logout </button>
<button class='gitlab-button gitlab-button-success' style='
${
buttonClearStyles
}
' type='button' id='gitlab-comment-button'> Send feedback </button>
</div>
`
;
const
commentIcon
=
`
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="gitlab-comment-icon"/></svg>
`
;
const
compressIcon
=
`
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/compress</title><path d="M5.27 12.182l-1.562 1.561a1 1 0 0 1-1.414 0h-.001a1 1 0 0 1 0-1.415l1.56-1.56L2.44 9.353a.5.5 0 0 1 .353-.854H7.09a.5.5 0 0 1 .5.5v4.294a.5.5 0 0 1-.853.353l-1.467-1.465zm6.911-6.914l1.464 1.464a.5.5 0 0 1-.353.854H8.999a.5.5 0 0 1-.5-.5V2.793a.5.5 0 0 1 .854-.354l1.414 1.415 1.56-1.561a1 1 0 1 1 1.415 1.414l-1.561 1.56z" id="gitlab-compress-icon"/></svg>
`
;
const
collapseButton
=
`
<button id='gitlab-collapse' style='
${
buttonClearStyles
}
' class='gitlab-button gitlab-button-secondary gitlab-collapse gitlab-collapse-open'>
${
compressIcon
}
</button>
`
;
const
form
=
content
=>
`
<div id='gitlab-form-wrapper'>
${
content
}
</div>
`
;
const
login
=
`
<div>
<label for='gitlab-token' class='gitlab-label'>Enter your <a class='gitlab-link' href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">personal access token</a></label>
<input class='gitlab-input' type='password' id='gitlab-token' name='gitlab-token'>
${
note
}
</div>
<div class='gitlab-checkbox-wrapper'>
<input type="checkbox" id="remember_token" name="remember_token" value="remember">
<label for="remember_token" class='gitlab-checkbox-label'>Remember me</label>
</div>
<div class='gitlab-button-wrapper'>
<button class='gitlab-button-wide gitlab-button gitlab-button-success' style='
${
buttonClearStyles
}
' type='button' id='gitlab-login'> Submit </button>
</div>
`
;
///////////////////////////////////////////////
//////////////// 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
<details>
<summary>Metadata</summary>
Posted from
${
href
}
|
${
platform
}
|
${
browser
}
|
${
innerWidth
}
x
${
innerHeight
}
.
<br /><br />
<em>User agent:
${
userAgent
}
</em>
</details>
`
;
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
)();
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment