Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Commit

Permalink
Merge 867397f into 9fe9fcb
Browse files Browse the repository at this point in the history
  • Loading branch information
kuychaco committed Nov 28, 2018
2 parents 9fe9fcb + 867397f commit 06c111f
Show file tree
Hide file tree
Showing 30 changed files with 1,190 additions and 84 deletions.
41 changes: 41 additions & 0 deletions lib/containers/commit-detail-container.js
@@ -0,0 +1,41 @@
import React from 'react';
import PropTypes from 'prop-types';
import yubikiri from 'yubikiri';

import ObserveModel from '../views/observe-model';
import LoadingView from '../views/loading-view';
import CommitDetailController from '../controllers/commit-detail-controller';

export default class CommitDetailContainer extends React.Component {
static propTypes = {
repository: PropTypes.object.isRequired,
sha: PropTypes.string.isRequired,
}

fetchData = repository => {
return yubikiri({
commit: repository.getCommit(this.props.sha),
});
}

render() {
return (
<ObserveModel model={this.props.repository} fetchData={this.fetchData}>
{this.renderResult}
</ObserveModel>
);
}

renderResult = data => {
if (this.props.repository.isLoading() || data === null || !data.commit.isPresent()) {
return <LoadingView />;
}

return (
<CommitDetailController
{...data}
{...this.props}
/>
);
}
}
105 changes: 105 additions & 0 deletions lib/controllers/commit-detail-controller.js
@@ -0,0 +1,105 @@
import React from 'react';
import PropTypes from 'prop-types';
import {emojify} from 'node-emoji';
import moment from 'moment';

import MultiFilePatchController from './multi-file-patch-controller';

const avatarAltText = 'committer avatar';

export default class CommitDetailController extends React.Component {
static propTypes = {
repository: PropTypes.object.isRequired,

workspace: PropTypes.object.isRequired,
commands: PropTypes.object.isRequired,
keymaps: PropTypes.object.isRequired,
tooltips: PropTypes.object.isRequired,
config: PropTypes.object.isRequired,

destroy: PropTypes.func.isRequired,
commit: PropTypes.object.isRequired,
}

render() {
const commit = this.props.commit;
// const {messageHeadline, messageBody, abbreviatedOid, url} = this.props.item;
// const {avatarUrl, name, date} = this.props.item.committer;

return (
<div className="github-CommitDetailView">
<div className="github-CommitDetailView-header">
<div className="github-CommitDetailView-commitContainer">
<div className="github-CommitDetailView-commit">
<h3 className="github-CommitDetailView-title">
{emojify(commit.getMessageSubject())}
</h3>
<pre className="github-CommitDetailView-moreText">
{emojify(commit.getMessageBody())}</pre>
<div className="github-CommitDetailView-meta">
{/* TODO fix image src */}
{this.renderAuthors()}
<span className="github-CommitDetailView-metaText">
{commit.getAuthorEmail()} committed {this.humanizeTimeSince(commit.getAuthorDate())}
</span>
</div>
</div>
<div className="github-CommitDetailView-sha">
{/* TODO fix href */}
<a href="https://github.com/atom/github/commit/6e0781600cccc3de2cc981f0d43209bf31cf86c8"
title={`open commit ${commit.getSha()} on GitHub.com`}>
{commit.getSha()}
</a>
</div>
</div>
</div>
<MultiFilePatchController
multiFilePatch={commit.getMultiFileDiff()}
autoHeight={false}
{...this.props}
disableStageUnstage={true}
/>
</div>
);
}

humanizeTimeSince(date) {
return moment(date * 1000).fromNow();
}

getAuthorInfo() {
const coAuthorCount = this.props.commit.getCoAuthors().length;
return coAuthorCount ? this.props.commit.getAuthorEmail() : `${coAuthorCount + 1} people`;
}

renderAuthor(email) {
const match = email.match(/^(\d+)\+[^@]+@users.noreply.github.com$/);

let avatarUrl;
if (match) {
avatarUrl = 'https://avatars.githubusercontent.com/u/' + match[1] + '?s=32';
} else {
avatarUrl = 'https://avatars.githubusercontent.com/u/e?email=' + encodeURIComponent(email) + '&s=32';
}

return (
<img className="github-RecentCommit-avatar"
key={email}
src={avatarUrl}
title={email}
alt={`${email}'s avatar'`}
/>
);
}

renderAuthors() {
const coAuthorEmails = this.props.commit.getCoAuthors().map(author => author.email);
const authorEmails = [this.props.commit.getAuthorEmail(), ...coAuthorEmails];

return (
<span className="github-RecentCommit-authors">
{authorEmails.map(this.renderAuthor)}
</span>
);
}
}
1 change: 1 addition & 0 deletions lib/controllers/commit-preview-controller.js
Expand Up @@ -23,6 +23,7 @@ export default class CommitPreviewController extends React.Component {
return (
<MultiFilePatchController
surface={this.props.surfaceToCommitPreviewButton}
autoHeight={false}
{...this.props}
/>
);
Expand Down
8 changes: 5 additions & 3 deletions lib/controllers/multi-file-patch-controller.js
Expand Up @@ -22,9 +22,11 @@ export default class MultiFilePatchController extends React.Component {
config: PropTypes.object.isRequired,

destroy: PropTypes.func.isRequired,
discardLines: PropTypes.func.isRequired,
undoLastDiscard: PropTypes.func.isRequired,
surface: PropTypes.func.isRequired,
discardLines: PropTypes.func,
undoLastDiscard: PropTypes.func,
surface: PropTypes.func,
autoHeight: PropTypes.bool,
disableStageUnstage: PropTypes.bool,
}

constructor(props) {
Expand Down
46 changes: 46 additions & 0 deletions lib/controllers/recent-commits-controller.js
@@ -1,13 +1,49 @@
import React from 'react';
import PropTypes from 'prop-types';
import {autobind} from '../helpers';
import {addEvent} from '../reporter-proxy';

import CommitDetailItem from '../items/commit-detail-item';
import URIPattern from '../atom/uri-pattern';
import RecentCommitsView from '../views/recent-commits-view';
import {CompositeDisposable} from 'event-kit';

export default class RecentCommitsController extends React.Component {
static propTypes = {
commits: PropTypes.arrayOf(PropTypes.object).isRequired,
isLoading: PropTypes.bool.isRequired,
undoLastCommit: PropTypes.func.isRequired,
workspace: PropTypes.object.isRequired,
repository: PropTypes.object.isRequired,
}

constructor(props, context) {
super(props, context);
autobind(this, 'openCommit', 'updateSelectedCommit');

this.subscriptions = new CompositeDisposable(
this.props.workspace.onDidChangeActivePaneItem(this.updateSelectedCommit),
);
this.state = {selectedCommitSha: ''};
}

updateSelectedCommit() {
const activeItem = this.props.workspace.getActivePaneItem();

const pattern = new URIPattern(decodeURIComponent(
CommitDetailItem.buildURI(
this.props.repository.getWorkingDirectoryPath(),
'{sha}'),
));

if (activeItem && activeItem.getURI) {
const match = pattern.matches(activeItem.getURI());
const {sha} = match.getParams();
if (match.ok() && sha && sha !== this.state.selectedCommitSha) {
return new Promise(resolve => this.setState({selectedCommitSha: sha}, resolve));
}
}
return Promise.resolve();
}

render() {
Expand All @@ -16,7 +52,17 @@ export default class RecentCommitsController extends React.Component {
commits={this.props.commits}
isLoading={this.props.isLoading}
undoLastCommit={this.props.undoLastCommit}
openCommit={this.openCommit}
selectedCommitSha={this.state.selectedCommitSha}
/>
);
}

openCommit({sha}) {
const workdir = this.props.repository.getWorkingDirectoryPath();
const uri = CommitDetailItem.buildURI(workdir, sha);
this.props.workspace.open(uri).then(() => {
addEvent('open-commit-in-pane', {package: 'github', from: 'recent commit'});
});
}
}
57 changes: 57 additions & 0 deletions lib/controllers/root-controller.js
Expand Up @@ -11,12 +11,14 @@ import Panel from '../atom/panel';
import PaneItem from '../atom/pane-item';
import CloneDialog from '../views/clone-dialog';
import OpenIssueishDialog from '../views/open-issueish-dialog';
import OpenCommitDialog from '../views/open-commit-dialog';
import InitDialog from '../views/init-dialog';
import CredentialDialog from '../views/credential-dialog';
import Commands, {Command} from '../atom/commands';
import GitTimingsView from '../views/git-timings-view';
import ChangedFileItem from '../items/changed-file-item';
import IssueishDetailItem from '../items/issueish-detail-item';
import CommitDetailItem from '../items/commit-detail-item';
import CommitPreviewItem from '../items/commit-preview-item';
import GitTabItem from '../items/git-tab-item';
import GitHubTabItem from '../items/github-tab-item';
Expand Down Expand Up @@ -139,6 +141,7 @@ export default class RootController extends React.Component {
<Command command="github:toggle-github-tab" callback={this.githubTabTracker.toggle} />
<Command command="github:toggle-github-tab-focus" callback={this.githubTabTracker.toggleFocus} />
<Command command="github:clone" callback={this.openCloneDialog} />
<Command command="github:open-commit" callback={this.showOpenCommitDialog} />
<Command
command="github:view-unstaged-changes-for-current-file"
callback={this.viewUnstagedChangesForCurrentFile}
Expand Down Expand Up @@ -188,6 +191,7 @@ export default class RootController extends React.Component {
{this.renderCloneDialog()}
{this.renderCredentialDialog()}
{this.renderOpenIssueishDialog()}
{this.renderOpenCommitDialog()}
</Fragment>
);
}
Expand Down Expand Up @@ -244,6 +248,22 @@ export default class RootController extends React.Component {
);
}

renderOpenCommitDialog() {
if (!this.state.openCommitDialogActive) {
return null;
}

return (
<Panel workspace={this.props.workspace} location="modal">
<OpenCommitDialog
commandRegistry={this.props.commandRegistry}
didAccept={this.acceptOpenCommit}
didCancel={this.cancelOpenCommit}
/>
</Panel>
);
}

renderCredentialDialog() {
if (this.state.credentialDialogQuery === null) {
return null;
Expand Down Expand Up @@ -362,6 +382,26 @@ export default class RootController extends React.Component {
/>
)}
</PaneItem>
<PaneItem
workspace={this.props.workspace}
uriPattern={CommitDetailItem.uriPattern}
className="github-CommitDetail-root">
{({itemHolder, params}) => (
<CommitDetailItem
ref={itemHolder.setter}

workdirContextPool={this.props.workdirContextPool}
workingDirectory={params.workingDirectory}
workspace={this.props.workspace}
commands={this.props.commandRegistry}
keymaps={this.props.keymaps}
tooltips={this.props.tooltips}
config={this.props.config}

sha={params.sha}
/>
)}
</PaneItem>
<PaneItem workspace={this.props.workspace} uriPattern={IssueishDetailItem.uriPattern}>
{({itemHolder, params}) => (
<IssueishDetailItem
Expand Down Expand Up @@ -490,6 +530,10 @@ export default class RootController extends React.Component {
this.setState({openIssueishDialogActive: true});
}

showOpenCommitDialog = () => {
this.setState({openCommitDialogActive: true});
}

showWaterfallDiagnostics() {
this.props.workspace.open(GitTimingsView.buildURI());
}
Expand Down Expand Up @@ -548,6 +592,19 @@ export default class RootController extends React.Component {
this.setState({openIssueishDialogActive: false});
}

acceptOpenCommit = ({sha}) => {
const workdir = this.props.repository.getWorkingDirectoryPath();
const uri = CommitDetailItem.buildURI(workdir, sha);
this.setState({openCommitDialogActive: false});
this.props.workspace.open(uri).then(() => {
addEvent('open-commit-in-pane', {package: 'github', from: 'dialog'});
});
}

cancelOpenCommit = () => {
this.setState({openCommitDialogActive: false});
}

surfaceFromFileAtPath = (filePath, stagingStatus) => {
const gitTab = this.gitTabTracker.getComponent();
return gitTab && gitTab.focusAndSelectStagingItem(filePath, stagingStatus);
Expand Down
8 changes: 8 additions & 0 deletions lib/git-shell-out-strategy.js
Expand Up @@ -687,6 +687,14 @@ export default class GitShellOutStrategy {
return headCommit;
}

async getDiffsForCommit(sha) {
const output = await this.exec([
'diff', '--no-prefix', '--no-ext-diff', '--no-renames', `${sha}~`, sha,
]);

return parseDiff(output);
}

async getCommits(options = {}) {
const {max, ref, includeUnborn} = {max: 1, ref: 'HEAD', includeUnborn: false, ...options};

Expand Down
10 changes: 10 additions & 0 deletions lib/github-package.js
Expand Up @@ -389,6 +389,16 @@ export default class GithubPackage {
return item;
}

createCommitDetailStub({uri}) {
const item = StubItem.create('git-commit-detail', {
title: 'Commit',
}, uri);
if (this.controller) {
this.rerender();
}
return item;
}

destroyGitTabItem() {
if (this.gitTabStubItem) {
this.gitTabStubItem.destroy();
Expand Down

0 comments on commit 06c111f

Please sign in to comment.