1
1
import { compose } from 'recompose' ;
2
2
import { connect } from 'react-redux' ;
3
3
import { 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' ;
4
8
import log from 'electron-log' ;
5
9
import got from 'got' ;
6
10
import FormData from 'form-data' ;
@@ -60,7 +64,11 @@ const config = {
60
64
} ;
61
65
const youtrack = new Youtrack ( config ) ;
62
66
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
+ }
64
72
65
73
/* eslint-disable-next-line no-useless-escape */
66
74
const linkPattern = / ! * \[ (?< alttext > [ ^ \] ] * ?) \] \( (?< filename > .* ?) * (? = \" | \) ) (?< optionalpart > \" .* \" ) ? \) / gm;
@@ -76,7 +84,13 @@ function getMarkDownLocalFileLinkMatches(markdown) {
76
84
// capturing group n: match[n]
77
85
const { filename } = match . groups ;
78
86
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
+ ) {
80
94
// file exists
81
95
linkMatches . push ( { ...match } ) ;
82
96
links . push ( decodedFilename ) ;
@@ -199,6 +213,32 @@ async function postAttachmentsToIssue(
199
213
. on ( 'error' , handleError ) ;
200
214
}
201
215
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
+
202
242
async function getConfigXmlSettings ( appState ) {
203
243
const { workspace } =
204
244
workspaceHelpers . getCurrentWorkspaceFullPath ( appState ) || { } ;
@@ -254,6 +294,7 @@ class SubmitHelpTicket extends React.Component<Props> {
254
294
async ( event , appState ) => {
255
295
try {
256
296
await this . refreshMarkdownDescription ( appState ) ;
297
+ this . setState ( { appState } ) ;
257
298
} catch ( error ) {
258
299
log . error ( error ) ;
259
300
}
@@ -329,7 +370,7 @@ class SubmitHelpTicket extends React.Component<Props> {
329
370
330
371
async removeTempFiles ( ) {
331
372
const { appStateFilePath , activeBundleFilePath } = this . state ;
332
- if ( await fs . exists ( appStateFilePath ) ) {
373
+ if ( appStateFilePath && ( await fs . exists ( appStateFilePath ) ) ) {
333
374
await fs . remove ( appStateFilePath ) ;
334
375
}
335
376
if ( activeBundleFilePath && ( await fs . exists ( activeBundleFilePath ) ) ) {
@@ -341,6 +382,10 @@ class SubmitHelpTicket extends React.Component<Props> {
341
382
342
383
mdParser = null ;
343
384
385
+ handleFeedbackTypeChange = event => {
386
+ this . setState ( { feedbackType : event . target . value } ) ;
387
+ } ;
388
+
344
389
handleEditorChange = ( { text } ) = > {
345
390
this . setState ( {
346
391
description : text . replace ( linkPattern , replaceEmptyAltTextWithFileName )
@@ -406,32 +451,59 @@ class SubmitHelpTicket extends React.Component<Props> {
406
451
this . setState ( { isUploading } ) ;
407
452
} ;
408
453
409
- handleError = ( /* error, body, response */ ) => {
454
+ handleError = ( error /* , body, response */ ) => {
455
+ log . error ( error ) ;
410
456
this . setState ( { isUploading : false } ) ;
411
457
} ;
412
458
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
+
413
486
handleClickSendFeedback = async ( ) = > {
414
- const { title, description } = this . state ;
487
+ const { title, description : sourceDescription } = this . state ;
415
488
try {
489
+ const finalDescription = this . getFinalDescription ( ) ;
416
490
// create a new issue
417
491
const issue = await youtrack . issues . create ( {
418
492
summary : title ,
419
- description : description . replace (
420
- linkPattern ,
421
- replaceFilePathsWithFileNames
422
- ) ,
493
+ description : finalDescription ,
423
494
project : {
424
- id : DBL_PROJECT_ID
495
+ id : DBL_SUPPORT_PROJECT_ID
425
496
} ,
426
497
usesMarkdown : true
427
498
} ) ;
428
499
// try to upload attachment
429
500
await postAttachmentsToIssue (
430
501
issue ,
431
- description ,
502
+ sourceDescription ,
432
503
this . handleProgress ,
433
504
this . handleError
434
505
) ;
506
+ await postTagsToIssue ( issue ) ;
435
507
const currentWindow = servicesHelpers . getCurrentWindow ( ) ;
436
508
currentWindow . close ( ) ;
437
509
} catch ( error ) {
@@ -440,7 +512,12 @@ class SubmitHelpTicket extends React.Component<Props> {
440
512
} ;
441
513
442
514
render ( ) {
443
- const { title , description , isUploading } = this . state ;
515
+ const {
516
+ title,
517
+ description,
518
+ isUploading,
519
+ feedbackType = 'BugReport'
520
+ } = this . state ;
444
521
const { classes } = this . props ;
445
522
return (
446
523
< React . Fragment >
@@ -454,7 +531,7 @@ class SubmitHelpTicket extends React.Component<Props> {
454
531
disabled = { title . trim ( ) . length < 3 || isUploading }
455
532
>
456
533
< CloudUpload style = { { marginRight : '10px' } } />
457
- Send
534
+ Send { splitCamelCaseToSpaced ( feedbackType ) }
458
535
{ isUploading && (
459
536
< CircularProgress
460
537
className = { classes . buttonProgress }
@@ -475,6 +552,16 @@ class SubmitHelpTicket extends React.Component<Props> {
475
552
} }
476
553
>
477
554
< 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 >
478
565
< TextField
479
566
required
480
567
id = "title"
0 commit comments