Skip to content

Implement for App Alert #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 11, 2017
Merged
Show file tree
Hide file tree
Changes from 12 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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"bootstrap-sass": "^3.3.7",
"color": "~1.0.3",
"cookie-parser": "~1.4.3",
"cors": "^2.8.3",
"crossroads": "~0.12.2",
"diff2html": "^2.3.0",
"express": "~4.15.2",
Expand Down Expand Up @@ -60,6 +61,7 @@
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"redux": "^3.6.0",
"redux-thunk": "^2.2.0",
"rimraf": "~2.6.1",
"semver": "~5.3.0",
"serve-static": "~1.12.2",
Expand Down
16 changes: 16 additions & 0 deletions source/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const BugTracker = require('./bugtracker');
const bugtracker = new BugTracker('server');
const usageStatistics = require('./usage-statistics');
const express = require('express');
const cors = require('cors')
const gitApi = require('./git-api');
const winston = require('winston');
const sysinfo = require('./sysinfo');
Expand Down Expand Up @@ -107,6 +108,7 @@ const noCache = (req, res, next) => {
app.use(noCache);

app.use(require('body-parser').json());
app.use(cors()); // we should consider to remove it when we complete this project

if (config.autoShutdownTimeout) {
let autoShutdownTimeout;
Expand Down Expand Up @@ -270,6 +272,20 @@ app.get('/serverdata.js', (req, res) => {
});
});

app.get('/ungit/config', (req, res) => {
sysinfo.getUserHash()
.then((hash) => {
const ungitConfig = {
config,
userHash: hash,
version: config.ungitDevVersion,
platform: os.platform(),
pluginApiVersion: require('../package.json').ungitPluginApiVersion
};
res.send(JSON.stringify(ungitConfig));
});
});

app.get('/api/latestversion', (req, res) => {
sysinfo.getUngitLatestVersion()
.then((latestVersion) => {
Expand Down
12 changes: 12 additions & 0 deletions src-react/actions/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { fetchUngitConfig } from './ungit-config';
import { fetchLatestVersion, fetchGitVersion } from './version';
import { pending } from './common';

export function bootstrap() {
return dispatch => {
dispatch(pending(4));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to make my understanding much clearer.
I think the usage of pending is to know how many APIs don't finish yet.

If my understanding is correct, according to this bootstrap action, there're three fetch action, why do you use 4 here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to ungitconfig action will also fetch userconfig, but after I check the code, there's a condition to decide if fetching userconfig or not, so I think I need to move a pending call into ungitconfig

It's fixed

dispatch(fetchUngitConfig());
dispatch(fetchLatestVersion());
dispatch(fetchGitVersion());
};
}
16 changes: 16 additions & 0 deletions src-react/actions/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* This export common using actionCreator */
import * as types from 'constants/action-types';

export function pending(count) {
return {
type: types.PATH_PAGE_PENDING,
payload: count || 1
};
};

export function apiError(message) {
return {
type: types.PATH_PAGE_API_ERR,
payload: message
};
}
28 changes: 28 additions & 0 deletions src-react/actions/ungit-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as types from 'constants/action-types';
import { fetchUserConfig } from './user-config';
import { apiError } from './common';

export function fetchUngitConfig() {
return dispatch => {
// consider wrap API call in separate modules
// it will be easy to stub module's function when testing
fetch('http://localhost:8448/ungit/config')
.then(response => response.json())
.then(json => {
if (!json.config.bugtracking) {
dispatch(fetchUserConfig());
}
dispatch(receiveUgitConfig(json));
})
.catch(e => {
dispatch(apiError(e.message));
});
};
};

function receiveUgitConfig(ungitConfig) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's typo in the function name?

receiveUgitConfig => receiveUngitConfig

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return {
type: types.RECEIVE_UNGIT_CONFIG,
payload: ungitConfig
};
};
25 changes: 25 additions & 0 deletions src-react/actions/user-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as types from 'constants/action-types';
import { apiError } from './common';

export function fetchUserConfig() {
return dispatch => {
// consider wrap API call in separate modules
// it will be easy to stub module's function when testing
fetch('http://localhost:8448/api/userconfig')
.then(response => response.json())
.then(json => {
dispatch(receiveUserConfig(json));
})
.catch(e => {
dispatch(apiError(e.message));
});
};
};


function receiveUserConfig(userConfig) {
return {
type: types.RECEIVE_USER_CONFIG,
payload: userConfig
};
};
46 changes: 46 additions & 0 deletions src-react/actions/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as types from 'constants/action-types';
import { apiError } from './common';

export function fetchLatestVersion() {
return dispatch => {
// consider wrap API call in separate modules
// it will be easy to stub module's function when testing
fetch('http://localhost:8448/api/latestversion')
.then(response => response.json())
.then(json => {
dispatch(receiveLatestVersion(json));
})
.catch(e => {
dispatch(apiError(e.message));
});
};
}

export function fetchGitVersion() {
return dispatch => {
// consider wrap API call in separate modules
// it will be easy to stub module's function when testing
fetch('http://localhost:8448/api/gitversion')
.then(response => response.json())
.then(json => {
dispatch(receiveGitVersion(json));
})
.catch(e => {
dispatch(apiError(e.message));
});
};
}

function receiveGitVersion(gitVersion) {
return {
type: types.RECEIVE_GIT_VERSION,
payload: gitVersion
};
}

function receiveLatestVersion(latestVersion) {
return {
type: types.RECEIVE_LATEST_VERSION,
payload: latestVersion
};
};
17 changes: 17 additions & 0 deletions src-react/components/alert-area/bug-tracking-nag-screen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { Component } from 'react';

class BugTrackingNagScreen extends Component {
render() {
return (
<div className="alert alert-info clearfix">
<button type="button" className="close" data-bind="click: dismissBugtrackingNagscreen">&times;</button>
<p><strong>Help make ungit better with the press of a button!</strong></p>
<button className="btn btn-primary" data-bind="click: enableBugtrackingAndStatistics">Enable automatic bug reports + anonymous usage statistics</button>
<button className="btn btn-primary" data-bind="click: enableBugtracking">Enable automatic bug reports</button>
<button className="btn btn-default" data-bind="click: dismissBugtrackingNagscreen">Naah, I&#39;ll skip that</button>
</div>
);
}
}

export default BugTrackingNagScreen;
18 changes: 18 additions & 0 deletions src-react/components/alert-area/git-version-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { Component, PropTypes } from 'react';

class GitVersionError extends Component {
static propTypes = {
gitVersionError: PropTypes.string
}

render() {
return (
<div class="alert alert-danger">
<span>{ this.props.gitVersionError }</span>
<button type="button" className="close" data-bind="click: dismissGitVersionError">&times;</button>
</div>
);
}
}

export default GitVersionError;
62 changes: 62 additions & 0 deletions src-react/components/alert-area/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { Component, PropTypes } from 'react';

import GitVersionError from './git-version-error';
import NewVersionAvailable from './new-version-available';
import BugTrackingNagScreen from './bug-tracking-nag-screen';
import NPSSurvey from './nps-survey';

class AlertArea extends Component {
static propTypes = {
config: PropTypes.object.isRequired,
gitVersionErrorVisible: PropTypes.bool.isRequired,
showNewVersionAvailable: PropTypes.bool.isRequired,
showBugtrackingNagscreen: PropTypes.bool.isRequired,
showNPSSurvey: PropTypes.bool.isRequired
}

render() {
const {
config: {
ungitConfig: { platform },
versions: {
gitVersion: { error },
latestVersion: {
latestVersion
}
}
},
gitVersionErrorVisible,
showNewVersionAvailable,
showBugtrackingNagscreen,
showNPSSurvey
} = this.props;
return (
<div className="container" data-ta-container="app">
{
gitVersionErrorVisible ? (
<GitVersionError gitVersionError={ error }/>
) : null
}
{
showNewVersionAvailable ? (
<NewVersionAvailable
latestVersion={ latestVersion }
platform={ platform }/>
) : null
}
{
showBugtrackingNagscreen ? (
<BugTrackingNagScreen />
) : null
}
{
showNPSSurvey ? (
<NPSSurvey />
) : null
}
</div>
);
}
}

export default AlertArea;
24 changes: 24 additions & 0 deletions src-react/components/alert-area/new-version-available.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { Component, PropTypes } from 'react';

class NewVersionAvailable extends Component {
static propTypes = {
latestVersion: PropTypes.string,
platform: PropTypes.string
}

render() {
const newVersionInstallCommand = `#{this.props.platform ? '' : 'sudo -H'}npm update -g ungit`;
return (
<div className="alert alert-info">
A new version of ungit (<span>{ this.props.latestVersion }</span>) is
<a href="https://github.com/FredrikNoren/ungit">available</a>! Run
<code>{ newVersionInstallCommand }</code> to install.
See what's new in the
<a href="https://github.com/FredrikNoren/ungit/blob/master/CHANGELOG.md">changelog</a>.
<button type="button" className="close" data-bind="click: dismissNewVersion">&times;</button>
</div>
);
}
}

export default NewVersionAvailable;
37 changes: 37 additions & 0 deletions src-react/components/alert-area/nps-survey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { Component } from 'react';

class NPSSurvey extends Component {
static propTypes = {
}

render() {
return (
<div className="alert alert-info clearfix" data-bind="visible: showNPSSurvey">
<button type="button" className="close" data-bind="click: dismissNPSSurvey">&times;</button>
<span className="text-dimmed">Hi! This is a one-question survey to learn more about how people use Ungit. You can dismiss it by clicking the x in the upper right corner.</span>
<p><h4>Question: How likely are you to recommend Ungit to your friends and colleagues?</h4></p>
<p>
<div className="btn-group btn-group-justified">
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 0)">0</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 1)">1</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 2)">2</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 3)">3</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 4)">4</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 5)">5</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 6)">6</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 7)">7</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 8)">8</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 9)">9</a>
<a className="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 10)">10</a>
</div>
<div className="clearfix">
Not at all likely
<div className="pull-right">Extremely likely</div>
</div>
</p>
</div>
);
}
}

export default NPSSurvey;
6 changes: 6 additions & 0 deletions src-react/constants/action-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const RECEIVE_GIT_VERSION = 'RECEIVE_GIT_VERSION';
export const RECEIVE_LATEST_VERSION = 'RECEIVE_LATEST_VERSION';
export const RECEIVE_USER_CONFIG = 'RECEIVE_USER_CONFIG';
export const RECEIVE_UNGIT_CONFIG = 'RECEIVE_UNGIT_CONFIG';
export const PATH_PAGE_PENDING = 'PATH_PAGE_PENDING';
export const PATH_PAGE_API_ERR = 'PATH_PAGE_API_ERR';
Loading