diff --git a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.js b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.js index d17f6699..7d69f275 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.js +++ b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.js @@ -13,12 +13,14 @@ export default class ConflictFile extends React.Component { const fileIcon = filePath.endsWith('.js') ? TEXT_DOCUMENT_ICON : EMPTY_DOCUMENT_ICON; const showInfoIcon = filePath.endsWith('.js'); + console.log(filePath, conflict); + const showFileDetails = branch => { - startFileConflictDetails(Object.assign(conflict, { selectedBranch: branch })); - displayConflictDetails(Object.assign(conflict, { overviewType: 'fileDetails', selectedBranch: branch })); + startFileConflictDetails(Object.assign(conflict, { selectedBranch: branch, selectedFile: filePath })); + displayConflictDetails(Object.assign(conflict, { overviewType: 'fileDetails', selectedBranch: branch, selectedFile: filePath })); }; - const { branches } = conflict.files.get(conflict.selectedFile); + const { branches } = conflict.files.get(filePath); return (
diff --git a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.scss b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.scss index d68d8f2b..856c3a76 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.scss +++ b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/ConflictFile/ConflictFile.scss @@ -3,7 +3,7 @@ } .separator { height: 1px; - background-color: #343232; + background-color: lightgray; margin-left: 15px; margin-right: 15px; } diff --git a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.js b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.js index 86182c6e..aa739f8c 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.js +++ b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.js @@ -1,6 +1,6 @@ import React from 'react'; import * as styles from './FileDetails.scss'; -import { LOADING_ICON } from '../Icons'; +import { GIT_BRANCH_ICON } from '../Icons'; export default class FileDetails extends React.Component { constructor(props) { @@ -9,50 +9,112 @@ export default class FileDetails extends React.Component { } renderError(error) { + console.log(error); return ( -
- Could not load conflict details. Reason: - - {error} - +
+
+ +
Could not fetch details
+
); } renderLoadingIndicator() { return ( -
-
- {LOADING_ICON} +
+
+ +
Loading file conflict details...
+
+
+ ); + } + + renderBranchListItem(repositoryUrl, selectedFile, branch) { + return ( +
+
+ {GIT_BRANCH_ICON} +
+ +
+ ); + } + + renderInvolvedUser(user) { + console.log(user); + + return ( +
+ +
+ {user.gitSignature}
-
Loading file conflict details
); } render() { - const { isFileDetailsProcessing, fileDetails: { selectedConflict, repositoryUrl, fetchError } } = this.props; - const { conflictBranch, selectedBranch, selectedFile } = selectedConflict; - console.log(selectedConflict, this.props.fileDetails); + const { isFileDetailsProcessing, fileDetails: { selectedConflict, repositoryUrl, error, changes } } = this.props; + const { conflictBranch, selectedBranch, selectedFile, conflictStakeholder, otherStakeholder } = selectedConflict; if (isFileDetailsProcessing) { return this.renderLoadingIndicator(); } - if (fetchError) { - return this.renderError(fetchError); + if (error) { + return this.renderError(error); } + console.log(changes); return ( -
+
- {'Conflict between branches '} - - {conflictBranch} - - {'and '} - - {selectedBranch} - + Conflicts in file + + {selectedFile} + +
+
+ {this.renderBranchListItem(repositoryUrl, selectedFile, conflictBranch)} + {this.renderBranchListItem(repositoryUrl, selectedFile, selectedBranch)} +
+
+
+
Involved Users
+
+ {this.renderInvolvedUser(conflictStakeholder)} + {this.renderInvolvedUser(otherStakeholder)} +
+
+
+
+
Detailed changes that cause issues:
+
+ {changes.map(c => +
+ + {c.type} + + on + + {c.end - c.start <= 1 ? 'line ' : 'lines '} + + + {c.start} + + {c.end - c.start > 1 && to } + {c.end - c.start > 1 && + + {c.end} + } +
+ )} +
); diff --git a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.scss b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.scss index 343870ca..caadcda6 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.scss +++ b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileDetails/FileDetails.scss @@ -1,8 +1,57 @@ -.loadingIcon { - height: 15px; - width: 15px; +.centerContent { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100%; +} +.content { + overflow: scroll; + height: 100%; + padding: 0 17px; +} + +.changeItem { + margin-left: 10px; +} +.textConsolas { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; +} + +.separator { + height: 1px; + background-color: lightgray; +} + +.userIcon { + align-self: center; + margin-right: 5px; + padding-left: 3px; } -.loadingWrapper { +.paddingLeft { + padding-left: 10px; +} +.flexRow { display: flex; flex-direction: row; } + +.branchIcon { + width: 22px; +} +.loadingIndicator { + font-size: 2rem; + animation: rotation 2s infinite linear; +} +.errorIndicator { + font-size: 2rem; + color: indianred; +} +@keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} diff --git a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileOverview/FileOverview.scss b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileOverview/FileOverview.scss index 1164578e..1ab2fed1 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileOverview/FileOverview.scss +++ b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/FileOverview/FileOverview.scss @@ -1,3 +1,4 @@ .content { + height: 100%; overflow: scroll; } diff --git a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/Icons.js b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/Icons.js index 5344c736..52077499 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/Icons.js +++ b/ui/src/visualizations/team-awareness/components/ConflictDetailsModal/Icons.js @@ -1,5 +1,3 @@ -import React from 'react'; - const GIT_BRANCH_ICON = ( ); -const LOADING_ICON = ( - - - -); - -export { TEXT_DOCUMENT_ICON, EMPTY_DOCUMENT_ICON, GIT_BRANCH_ICON, MAGNIFY_DOCUMENT_ICON, LOADING_ICON }; +export { TEXT_DOCUMENT_ICON, EMPTY_DOCUMENT_ICON, GIT_BRANCH_ICON, MAGNIFY_DOCUMENT_ICON }; diff --git a/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.js b/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.js index 1fcc86e3..5d40f0ad 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.js +++ b/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.js @@ -15,75 +15,87 @@ export default class ConflictOverview extends React.Component { }; } - render() { + renderLoadingIndicator() { + return ( +
+ + Processing conflicts... +
+ ); + } + + renderNoConflictIndicator() { + return ( +
+ + + + Could not detect any conflicts +
+ ); + } + + renderConflictIndicator() { const { conflicts } = this.props; const participantsTag = conflicts.length > 1 ? 'participants' : 'participant'; - const handleContentMouseOver = () => { - clearTimeout(this.state.contentCloseTimeout); - this.setState({ showContent: true }); + const handleContentClose = () => { + this.setState({ contentCloseTimeout: setTimeout(() => this.setState({ showContent: false }), 250) }); }; + return ( +
this.setState({ showContent: true })} + onMouseOut={handleContentClose}> + + + + + Detected conflicts with {conflicts.length} {participantsTag} + +
+ ); + } + + render() { + const { conflicts } = this.props; + const handleContentClose = () => { this.setState({ contentCloseTimeout: setTimeout(() => this.setState({ showContent: false }), 250) }); }; + const handleContentMouseOver = () => { + clearTimeout(this.state.contentCloseTimeout); + this.setState({ showContent: true }); + }; if (!this.props.branchSelected) { return
No conflict branch selected
; } if (this.props.loading) { - return ( -
- - - - Processing conflicts... -
- ); + return this.renderLoadingIndicator(); } if (conflicts.length === 0) { - return ( -
- - - - Could not detect any possible conflicts with selected branches -
- ); + return this.renderNoConflictIndicator(); } return (
-
this.setState({ showContent: true })} - onMouseOut={handleContentClose}> - - - - - Detected conflicts with {conflicts.length} {participantsTag} - -
+ {this.renderConflictIndicator()} {this.state.showContent &&
{conflicts.map((conflict, i) => diff --git a/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.scss b/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.scss index 5763ac14..6849c124 100644 --- a/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.scss +++ b/ui/src/visualizations/team-awareness/components/ConflictOverview/ConflictOverview.scss @@ -4,6 +4,22 @@ position: absolute; } +.indicator { + margin: 0 4px; + font-size: 1.1rem; + align-self: center; +} +.loadingIndicator { + animation: rotation 2s infinite linear; +} +@keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} .conflictOverviewIcon { height: 25px; width: 25px; diff --git a/ui/src/visualizations/team-awareness/sagas/conflictCalculations.js b/ui/src/visualizations/team-awareness/sagas/conflictCalculations.js index 6fdb5bc4..c13ca1b3 100644 --- a/ui/src/visualizations/team-awareness/sagas/conflictCalculations.js +++ b/ui/src/visualizations/team-awareness/sagas/conflictCalculations.js @@ -35,7 +35,7 @@ const analyseConflictsFromAppState = async appState => { if (conflictingStakeholders.length === 0) continue; for (const s of conflictingStakeholders) { - const { conflictStakeholder, otherStakeholder, hunks, changes } = s; + const { conflictStakeholder, otherStakeholder, changes } = s; const combined = `${conflictStakeholder.stakeholder.id}${otherStakeholder.stakeholder.id}`; if (!stakeholders.has(combined)) { diff --git a/ui/src/visualizations/team-awareness/sagas/conflictDetailsOperation.js b/ui/src/visualizations/team-awareness/sagas/conflictDetailsOperation.js index 5d320539..ea83f1e3 100644 --- a/ui/src/visualizations/team-awareness/sagas/conflictDetailsOperation.js +++ b/ui/src/visualizations/team-awareness/sagas/conflictDetailsOperation.js @@ -17,14 +17,14 @@ const analyseFileConflictDetails = async appState => { const { selectedConflict: { files, selectedBranch, conflictBranch, selectedFile } } = fileDetails; const { file, changes } = files.get(selectedFile); - + console.log(fileDetails.selectedConflict.selectedFile, selectedFile, files.get(selectedFile)); const blobIndex = file.url.indexOf('blob/'); if (!githubExpression.test(file.url) || blobIndex === -1) { - console.log('No tu wien binocular url'); + console.log('Only GitHub content supported'); return { fileDetails: { selectedConflict: fileDetails.selectedConflict, - fetchError: 'No Github file detected' + error: 'No Github file detected' } }; } @@ -32,28 +32,31 @@ const analyseFileConflictDetails = async appState => { const repositoryUrl = file.url.substring(0, blobIndex); const githubContentUrl = repositoryUrl.replace('github.com', 'raw.githubusercontent.com'); - const results = await Promise.all([ - fetch(`${githubContentUrl}${conflictBranch}/${file.path}`, { mode: 'cors' }) - ]); + const results = await Promise.all([fetch(`${githubContentUrl}${conflictBranch}/${file.path}`, { mode: 'cors' })]); if (results.filter(response => response.status >= 400).length >= 1) { return { fileDetails: { selectedConflict: fileDetails.selectedConflict, - fetchError: 'Could not fetch files from Github' + error: 'Could not fetch files from Github' } }; } const resultData = await Promise.all(results.map(r => r.text())); try { const trees = resultData.map(d => MyParser.parse(d, { ecmaVersion: 2020, sourceType: 'module', locations: true })); - const changedNodes = getChangedAstNodes(trees[0], changes, conflictBranch); - + const changeDescriptions = []; + for (const changedNode of getChangedAstNodes(trees[0], changes, selectedBranch)) { + changeDescriptions.push({ + type: splitPascalCase(changedNode.type).reduce((a, b) => `${a} ${b}`), + start: changedNode.loc.start.line, + end: changedNode.loc.end.line + }); + } - fileDetails.changes = changedNodes; + fileDetails.changes = changeDescriptions; } catch (e) { - fileDetails.parsingError = e; - console.error(e); + fileDetails.error = e; } fileDetails.repositoryUrl = repositoryUrl + 'blob/'; return { fileDetails };