11import { compose } from 'recompose' ;
22import { connect } from 'react-redux' ;
33import { withStyles } from '@material-ui/core/styles' ;
4+ import FormControl from '@material-ui/core/FormControl' ;
5+ import InputLabel from '@material-ui/core/InputLabel' ;
6+ import Select from '@material-ui/core/Select' ;
7+ import MenuItem from '@material-ui/core/MenuItem' ;
48import log from 'electron-log' ;
59import got from 'got' ;
610import FormData from 'form-data' ;
@@ -60,7 +64,11 @@ const config = {
6064} ;
6165const youtrack = new Youtrack ( config ) ;
6266
63- const DBL_PROJECT_ID = '0-30' ;
67+ const DBL_SUPPORT_PROJECT_ID = '0-31' ; // Digital Bible Library Support (DBLS)
68+
69+ function splitCamelCaseToSpaced ( str ) {
70+ return str . split ( / (? = [ A - Z ] ) / ) . join ( ' ' ) ;
71+ }
6472
6573/* eslint-disable-next-line no-useless-escape */
6674const linkPattern = / ! * \[ (?< alttext > [ ^ \] ] * ?) \] \( (?< filename > .* ?) * (? = \" | \) ) (?< optionalpart > \" .* \" ) ? \) / gm;
@@ -76,7 +84,13 @@ function getMarkDownLocalFileLinkMatches(markdown) {
7684 // capturing group n: match[n]
7785 const { filename } = match . groups ;
7886 const decodedFilename = decodeURIComponent ( filename ) ;
79- if ( fs . existsSync ( decodedFilename ) ) {
87+ if (
88+ decodedFilename &&
89+ ! decodedFilename . startsWith ( 'http:' ) &&
90+ ! decodedFilename . startsWith ( 'https:' ) &&
91+ ! decodedFilename . startsWith ( 'mailto:' ) &&
92+ fs . existsSync ( decodedFilename )
93+ ) {
8094 // file exists
8195 linkMatches . push ( { ...match } ) ;
8296 links . push ( decodedFilename ) ;
@@ -199,6 +213,32 @@ async function postAttachmentsToIssue(
199213 . on ( 'error' , handleError ) ;
200214}
201215
216+ async function postTagsToIssue ( issue ) {
217+ const NATHANAEL_TAG_ID = '5-170' ;
218+ try {
219+ const json = { issues : [ { id : issue . id } ] } ;
220+ const requestOptions = {
221+ method : 'POST' ,
222+ headers : {
223+ Authorization : `Bearer ${ config . token } ` ,
224+ Accept : 'application/json' ,
225+ 'Content-Type' : 'application/json'
226+ } ,
227+ body : JSON . stringify ( json )
228+ } ;
229+ const fields =
230+ 'name,issues,color,untagOnResolve,owner,visibleFor,updateableBy' ;
231+ const query = `fields=${ fields } ` ;
232+ const response = await fetch (
233+ `${ config . baseUrl } /api/issueTags/${ NATHANAEL_TAG_ID } ?${ query } ` ,
234+ requestOptions
235+ ) ;
236+ return response ;
237+ } catch ( error ) {
238+ log . error ( error ) ;
239+ }
240+ }
241+
202242async function getConfigXmlSettings ( appState ) {
203243 const { workspace } =
204244 workspaceHelpers . getCurrentWorkspaceFullPath ( appState ) || { } ;
@@ -254,6 +294,7 @@ class SubmitHelpTicket extends React.Component<Props> {
254294 async ( event , appState ) => {
255295 try {
256296 await this . refreshMarkdownDescription ( appState ) ;
297+ this . setState ( { appState } ) ;
257298 } catch ( error ) {
258299 log . error ( error ) ;
259300 }
@@ -329,7 +370,7 @@ class SubmitHelpTicket extends React.Component<Props> {
329370
330371 async removeTempFiles ( ) {
331372 const { appStateFilePath , activeBundleFilePath } = this . state ;
332- if ( await fs . exists ( appStateFilePath ) ) {
373+ if ( appStateFilePath && ( await fs . exists ( appStateFilePath ) ) ) {
333374 await fs . remove ( appStateFilePath ) ;
334375 }
335376 if ( activeBundleFilePath && ( await fs . exists ( activeBundleFilePath ) ) ) {
@@ -341,6 +382,10 @@ class SubmitHelpTicket extends React.Component<Props> {
341382
342383 mdParser = null ;
343384
385+ handleFeedbackTypeChange = event => {
386+ this . setState ( { feedbackType : event . target . value } ) ;
387+ } ;
388+
344389 handleEditorChange = ( { text } ) = > {
345390 this . setState ( {
346391 description : text . replace ( linkPattern , replaceEmptyAltTextWithFileName )
@@ -406,32 +451,59 @@ class SubmitHelpTicket extends React.Component<Props> {
406451 this . setState ( { isUploading } ) ;
407452 } ;
408453
409- handleError = ( /* error, body, response */ ) => {
454+ handleError = ( error /* , body, response */ ) => {
455+ log . error ( error ) ;
410456 this . setState ( { isUploading : false } ) ;
411457 } ;
412458
459+ getFinalDescription = ( ) => {
460+ const { description, appState, feedbackType = 'BugReport' } = this . state ;
461+ const descriptionWithServerLinks = description . replace (
462+ linkPattern ,
463+ replaceFilePathsWithFileNames
464+ ) ;
465+ const app = servicesHelpers . getApp ( ) ;
466+ const appName = app . getName ( ) ;
467+ const appVersion = app . getVersion ( ) ;
468+ const { authentication } = appState ;
469+ const { whoami } = authentication ;
470+ const { display_name : userName , email : userEmail } = whoami || { } ;
471+ const finalDescription = `${ descriptionWithServerLinks }
472+ -----------
473+ \`\`\`
474+ Product Name: ${ appName }
475+ Product Version: ${ appVersion }
476+ Feedback Type: ${ feedbackType }
477+ Email: ${ userEmail || '' }
478+ Keep Informed: ${ userEmail ? 'True' : '' }
479+ User: ${ userName || '' }
480+ \`\`\`
481+ ------------
482+ ` ;
483+ return finalDescription ;
484+ } ;
485+
413486 handleClickSendFeedback = async ( ) = > {
414- const { title, description } = this . state ;
487+ const { title, description : sourceDescription } = this . state ;
415488 try {
489+ const finalDescription = this . getFinalDescription ( ) ;
416490 // create a new issue
417491 const issue = await youtrack . issues . create ( {
418492 summary : title ,
419- description : description . replace (
420- linkPattern ,
421- replaceFilePathsWithFileNames
422- ) ,
493+ description : finalDescription ,
423494 project : {
424- id : DBL_PROJECT_ID
495+ id : DBL_SUPPORT_PROJECT_ID
425496 } ,
426497 usesMarkdown : true
427498 } ) ;
428499 // try to upload attachment
429500 await postAttachmentsToIssue (
430501 issue ,
431- description ,
502+ sourceDescription ,
432503 this . handleProgress ,
433504 this . handleError
434505 ) ;
506+ await postTagsToIssue ( issue ) ;
435507 const currentWindow = servicesHelpers . getCurrentWindow ( ) ;
436508 currentWindow . close ( ) ;
437509 } catch ( error ) {
@@ -440,7 +512,12 @@ class SubmitHelpTicket extends React.Component<Props> {
440512 } ;
441513
442514 render ( ) {
443- const { title , description , isUploading } = this . state ;
515+ const {
516+ title,
517+ description,
518+ isUploading,
519+ feedbackType = 'BugReport'
520+ } = this . state ;
444521 const { classes } = this . props ;
445522 return (
446523 < React . Fragment >
@@ -454,7 +531,7 @@ class SubmitHelpTicket extends React.Component<Props> {
454531 disabled = { title . trim ( ) . length < 3 || isUploading }
455532 >
456533 < CloudUpload style = { { marginRight : '10px' } } />
457- Send
534+ Send { splitCamelCaseToSpaced ( feedbackType ) }
458535 { isUploading && (
459536 < CircularProgress
460537 className = { classes . buttonProgress }
@@ -475,6 +552,16 @@ class SubmitHelpTicket extends React.Component<Props> {
475552 } }
476553 >
477554 < nav className = "nav" >
555+ < FormControl fullWidth >
556+ < InputLabel color = "secondary" > Type of Feedback</ InputLabel >
557+ < Select
558+ value = { feedbackType }
559+ onChange = { this . handleFeedbackTypeChange }
560+ >
561+ < MenuItem value = "BugReport" > Bug Report</ MenuItem >
562+ < MenuItem value = "FeatureRequest" > Feature Request</ MenuItem >
563+ </ Select >
564+ </ FormControl >
478565 < TextField
479566 required
480567 id = "title"
0 commit comments