Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/js/__tests__/components/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ describe('Test for Notification Component', function () {
notification={notification}
key={notification.id} />);

expect(instance.state.readClass).toBe('row notification');
expect(instance.state.read).toBeFalsy();
expect(instance.state.isRead).toBeFalsy();
expect(instance.openBrowser).toBeDefined();
expect(instance.markAsRead).toBeDefined();

Expand Down Expand Up @@ -99,8 +98,7 @@ describe('Test for Notification Component', function () {
notification={notification}
key={notification.id} />);

expect(instance.state.readClass).toBe('row notification');
expect(instance.state.read).toBeFalsy();
expect(instance.state.isRead).toBeFalsy();
expect(instance.openBrowser).toBeDefined();
expect(instance.markAsRead).toBeDefined();

Expand All @@ -127,8 +125,7 @@ describe('Test for Notification Component', function () {
notification={notification}
key={notification.id} />);

expect(instance.state.readClass).toBe('row notification');
expect(instance.state.read).toBeFalsy();
expect(instance.state.isRead).toBeFalsy();
expect(instance.openBrowser).toBeDefined();
expect(instance.markAsRead).toBeDefined();

Expand Down
81 changes: 80 additions & 1 deletion src/js/__tests__/components/repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var TestUtils = React.addons.TestUtils;

describe('Test for Repository Component', function () {

var Actions, Repository;
var apiRequests, Actions, Repository;

beforeEach(function () {
// Mock Electron's window.require
Expand All @@ -37,6 +37,7 @@ describe('Test for Repository Component', function () {
}
};

apiRequests = require('../../utils/api-requests.js');
Actions = require('../../actions/actions.js');
Repository = require('../../components/repository.js');
});
Expand All @@ -63,8 +64,10 @@ describe('Test for Repository Component', function () {
);

expect(instance.props.repo[0].repository.full_name).toBe('ekonstantinidis/gitify');
expect(instance.isRead).toBeFalsy();
expect(instance.getAvatar).toBeDefined();
expect(instance.openBrowser).toBeDefined();
expect(instance.markRepoAsRead).toBeDefined();

// Get Avatar
var avatar = instance.getAvatar();
Expand All @@ -75,4 +78,80 @@ describe('Test for Repository Component', function () {

});

it('Should mark a repo as read - successfully', function () {

var repoDetails = [{
'repository': {
'name': 'gitify',
'full_name': 'ekonstantinidis/gitify',
'owner': {
'login': 'ekonstantinidis',
'avatar_url': 'http://avatar.url'
}
},
'subject': {
'type': 'Issue'
}
}];

var instance = TestUtils.renderIntoDocument(
<Repository
repo={repoDetails}
repoName='ekonstantinidis/gitify'
key='ekonstantinidis/gitify' />
);

expect(instance.state.isRead).toBeFalsy();
expect(instance.markRepoAsRead).toBeDefined();

var superagent = require('superagent');
superagent.__setResponse(200, 'ok', {}, false);

// Mark Repo as Read
instance.markRepoAsRead();
jest.runAllTimers();

expect(instance.state.isRead).toBeTruthy();

});

it('Should mark a repo as read - fail', function () {

var repoDetails = [{
'repository': {
'name': 'gitify',
'full_name': 'ekonstantinidis/gitify',
'owner': {
'login': 'ekonstantinidis',
'avatar_url': 'http://avatar.url'
}
},
'subject': {
'type': 'Issue'
}
}];

var instance = TestUtils.renderIntoDocument(
<Repository
repo={repoDetails}
repoName='ekonstantinidis/gitify'
key='ekonstantinidis/gitify' />
);

expect(instance.state.isRead).toBeFalsy();
expect(instance.state.errors).toBeFalsy();
expect(instance.markRepoAsRead).toBeDefined();

var superagent = require('superagent');
superagent.__setResponse(400, false);

// Mark Repo as Read
instance.markRepoAsRead();
jest.runAllTimers();

expect(instance.state.isRead).toBeFalsy();
expect(instance.state.errors).toBeTruthy();

});

});
14 changes: 9 additions & 5 deletions src/js/components/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ var NotificationItem = React.createClass({

getInitialState: function () {
return {
readClass: 'row notification',
read: false
isRead: this.props.isRead
};
},

componentWillReceiveProps: function (nextProps) {
this.setState({
isRead: nextProps.isRead
});
},

openBrowser: function () {
var url = this.props.notification.subject.url.replace('api.github.com/repos', 'www.github.com');
if (url.indexOf('/pulls/') != -1) {
Expand All @@ -30,8 +35,7 @@ var NotificationItem = React.createClass({
if (response && response.ok) {
// Notification Read
self.setState({
readClass: self.state.readClass + ' read',
read: true
isRead: true
});
} else {
// Error - Show messages.
Expand All @@ -54,7 +58,7 @@ var NotificationItem = React.createClass({
}

return (
<div className={this.state.readClass}>
<div className={this.state.isRead ? 'row notification read' : 'row notification'}>
<div className='col-xs-1'><span className={typeIconClass} /></div>
<div className='col-xs-10 subject' onClick={this.openBrowser}>
{this.props.notification.subject.title}
Expand Down
48 changes: 45 additions & 3 deletions src/js/components/repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ var remote = window.require('remote');
var shell = remote.require('shell');

var SingleNotification = require('../components/notification');
var apiRequests = require('../utils/api-requests');

var Repository = React.createClass({

getInitialState: function () {
return {
isRead: false,
errors: false
};
},

getAvatar: function () {
return this.props.repo[0].repository.owner.avatar_url;
},
Expand All @@ -15,7 +23,32 @@ var Repository = React.createClass({
shell.openExternal(url);
},

markRepoAsRead: function () {
var self = this;
var loginId = this.props.repo[0].repository.owner.login;
var repoId = this.props.repo[0].repository.name;

apiRequests
.putAuth('https://api.github.com/repos/' + loginId + '/' + repoId + '/notifications', {})
.end(function (err, response) {
if (response && response.ok) {
// Notification Read
self.setState({
isRead: true,
errors: false
});
} else {
self.setState({
isRead: false,
errors: true
});
}
});

},

render: function () {
var self = this;
var organisationName, repositoryName;

if (typeof this.props.repoName === 'string') {
Expand All @@ -26,16 +59,25 @@ var Repository = React.createClass({

return (
<div>
<div className='row repository'>
<div className={this.state.isRead ? 'row repository read' : 'row repository'}>
<div className='col-xs-2'><img className='avatar' src={this.getAvatar()} /></div>
<div className='col-xs-10 name' onClick={this.openBrowser}>
<div className='col-xs-9 name' onClick={this.openBrowser}>
<span>{'/' + repositoryName}</span>
<span>{organisationName}</span>
</div>
<div className='col-xs-1 check-wrapper'>
<span className='octicon octicon-check' onClick={this.markRepoAsRead} />
</div>
</div>

{this.state.errors ?
<div className="alert alert-danger">
<strong>Oops!</strong> We couldn't mark this repo as read.
</div> : null
}

{this.props.repo.map(function (obj) {
return <SingleNotification notification={obj} key={obj.id} />;
return <SingleNotification isRead={self.state.isRead} notification={obj} key={obj.id} />;
})}

</div>
Expand Down
10 changes: 10 additions & 0 deletions src/js/utils/api-requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ var apiRequests = {
.set('User-Agent', 'Gitify');
},

putAuth: function (url, params) {
return request
.put(url)
.send(params)
.set('Accept', 'application/vnd.github.v3+json')
.set('Authorization', 'token ' + AuthStore.authStatus())
.set('Cache-Control', 'no-cache')
.set('User-Agent', 'Gitify');
},

patchAuth: function (url, params) {
return request
.patch(url)
Expand Down
56 changes: 44 additions & 12 deletions src/less/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
@White: #ffffff;
@LightGray: #f5f5f5;

@ErrorColor: #DB423C;

/* @end Colors */

/* @group Typography */
Expand Down Expand Up @@ -98,12 +100,32 @@
opacity: @opacity;
}

.CheckOcticon() {
&.octicon-check {
-webkit-transition: all .4s;
-moz-transition: all .4s;
transition: all .4s;
color: darken(@LightGray, 20%);

&:hover {
color: @ThemeGreen;
cursor: pointer;
}
}
}

/* @end Mixins */


/* @group Bootstrap Overrides */

@navbar-height: 35px;

@alert-padding: 8px;
@alert-border-radius: 0;
@alert-danger-bg: @ErrorColor;
@alert-danger-text: @White;

@octicons-font-path: "../../build/fonts";

/* @end Bootstrap Overrides */
Expand Down Expand Up @@ -155,6 +177,12 @@ input {
outline: none;
}

.alert {
border: 0;
text-align: center;
margin-bottom: 0;
}

/* @end Misc */


Expand Down Expand Up @@ -287,6 +315,10 @@ input {
margin: 0;
background-color: @LightGray;

&.read {
.Opacity(0.4);
}

.col-xs-2,
.col-xs-10 {
padding: 0;
Expand All @@ -299,9 +331,10 @@ input {
}

.name {
font-size: 18px;
.FontOpenSansSemibold();
font-size: 18px;
text-align: right;
padding: 0 5px;

span {
display: inline-block;
Expand All @@ -312,6 +345,15 @@ input {
overflow: hidden;
}
}

.check-wrapper {
padding: 0 5px;

.octicon {
font-size: 22px;
.CheckOcticon();
}
}
}

/* @end Component / Repository */
Expand Down Expand Up @@ -376,17 +418,7 @@ input {
.octicon {
margin-right: 10px;
font-size: 20px;

&.octicon-check {
-webkit-transition: all .4s;
-moz-transition: all .4s;
transition: all .4s;

&:hover {
color: @ThemeGreen;
cursor: pointer;
}
}
.CheckOcticon();
}

&.read {
Expand Down