From 746df9277cab74f809d0e30cdff77b88ff8d6d02 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Mon, 9 Nov 2015 15:07:17 +0900 Subject: [PATCH] =?UTF-8?q?=E8=A1=8C=E5=8B=95=E3=83=87=E3=83=BC=E3=82=BF,?= =?UTF-8?q?=20contact=20form,=20default=20article=E3=81=AB=E8=8B=B1?= =?UTF-8?q?=E8=AA=9E=E6=96=87=E8=BF=BD=E5=8A=A0,=20Intro=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser/finder/index.js | 3 + browser/main/HomePage/ArticleDetail.js | 9 +- browser/main/index.js | 3 + .../main/HomeContainer/lib/Preferences.styl | 64 +++++++++ .../main/HomeContainer/lib/Tutorial.styl | 3 +- lib/activityRecord.js | 122 +++++++++++++++++ lib/api.js | 10 +- lib/components/modal/Preference/ContactTab.js | 123 ++++++++++++++++++ lib/components/modal/Preferences.js | 10 +- lib/components/modal/Tutorial.js | 13 +- lib/dataStore.js | 2 +- lib/reducer.js | 5 +- 12 files changed, 356 insertions(+), 11 deletions(-) create mode 100644 lib/activityRecord.js create mode 100644 lib/components/modal/Preference/ContactTab.js diff --git a/browser/finder/index.js b/browser/finder/index.js index fb2a40a7..7fef8c7d 100644 --- a/browser/finder/index.js +++ b/browser/finder/index.js @@ -8,6 +8,7 @@ import FinderList from './FinderList' import FinderDetail from './FinderDetail' import { selectArticle, searchArticle, refreshData } from './actions' import _ from 'lodash' +import activityRecord from 'boost/activityRecord' import remote from 'remote' var hideFinder = remote.getGlobal('hideFinder') @@ -46,6 +47,7 @@ class FinderMain extends React.Component { if (e.keyCode === 13) { let { activeArticle } = this.props clipboard.writeText(activeArticle.content) + activityRecord.emit('FINDER_COPY') hideFinder() e.preventDefault() } @@ -174,6 +176,7 @@ var store = createStore(reducer) window.onfocus = e => { store.dispatch(refreshData()) + activityRecord.emit('FINDER_OPEN') } ReactDOM.render(( diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index fcecf5de..75d1f628 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -12,6 +12,7 @@ import linkState from 'boost/linkState' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' import TagSelect from 'boost/components/TagSelect' +import activityRecord from 'boost/activityRecord' var modeOptions = aceModes.map(function (mode) { return { @@ -93,6 +94,7 @@ export default class ArticleDetail extends React.Component { let { dispatch, activeArticle } = this.props dispatch(destroyArticle(activeArticle.key)) + activityRecord.emit('ARTICLE_DESTROY') this.setState({openDeleteConfirmMenu: false}) } @@ -182,7 +184,12 @@ export default class ArticleDetail extends React.Component { delete newArticle.status newArticle.updatedAt = new Date() - if (newArticle.createdAt == null) newArticle.createdAt = new Date() + if (newArticle.createdAt == null) { + newArticle.createdAt = new Date() + activityRecord.emit('ARTICLE_CREATE') + } else { + activityRecord.emit('ARTICLE_UPDATE') + } dispatch(updateArticle(newArticle)) dispatch(switchMode(IDLE_MODE)) diff --git a/browser/main/index.js b/browser/main/index.js index 29e05a9c..a749bdbe 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -10,6 +10,9 @@ import ReactDOM from 'react-dom' require('../styles/main/index.styl') import { openModal } from 'boost/modal' import Tutorial from 'boost/components/modal/Tutorial' +import activityRecord from 'boost/activityRecord' + +activityRecord.init() let routes = ( diff --git a/browser/styles/main/HomeContainer/lib/Preferences.styl b/browser/styles/main/HomeContainer/lib/Preferences.styl index 004c3eaa..79fe0e56 100644 --- a/browser/styles/main/HomeContainer/lib/Preferences.styl +++ b/browser/styles/main/HomeContainer/lib/Preferences.styl @@ -119,6 +119,70 @@ iptFocusBorderColor = #369DCD &.error color errorTextColor background-color errorBackgroundColor + &.ContactTab + &.done + .message + margin-top 75px + margin-bottom 15px + text-align center + font-size 22px + .checkIcon + margin-bottom 15px + font-size 144px + color brandColor + text-align center + .control + text-align center + button + border solid 1px borderColor + border-radius 5px + background-color white + padding 15px 15px + font-size 14px + &:hover + background-color darken(white, 10%) + &.form + padding 10px + .title + font-size 18px + color brandColor + margin-top 10px + margin-bottom 10px + .description + margin-bottom 15px + .iptGroup + margin-bottom 10px + input, textarea + border-radius 5px + border 1px solid borderColor + font-size 14px + outline none + padding 10px 15px + width 100% + &:focus + border-color iptFocusBorderColor + textarea + resize vertical + min-height 150px + .formControl + clearfix() + .alert + float right + padding 10px 15px + margin 0 5px 0 + font-size 14px + line-height normal + button + padding 10px 15px + background-color brandColor + color white + font-size 14px + border-radius 5px + border none + float right + &:hover + background-color lighten(brandColor, 10%) + &.AppSettingTab .description marked() diff --git a/browser/styles/main/HomeContainer/lib/Tutorial.styl b/browser/styles/main/HomeContainer/lib/Tutorial.styl index ced4547c..913bfbe5 100644 --- a/browser/styles/main/HomeContainer/lib/Tutorial.styl +++ b/browser/styles/main/HomeContainer/lib/Tutorial.styl @@ -109,8 +109,9 @@ slideBgColor4 = #00B493 .slide3 background-color slideBgColor3 .content + font-size 18px &>img - margin-top 45px + margin-top 25px .slide4 background-color slideBgColor4 .content diff --git a/lib/activityRecord.js b/lib/activityRecord.js new file mode 100644 index 00000000..71e61a82 --- /dev/null +++ b/lib/activityRecord.js @@ -0,0 +1,122 @@ +import _ from 'lodash' +import moment from 'moment' +import keygen from 'boost/keygen' +import dataStore from 'boost/dataStore' +import { request, WEB_URL } from 'boost/api' + +function isSameDate (a, b) { + a = moment(a).utcOffset(+540).format('YYYYMMDD') + b = moment(b).utcOffset(+540).format('YYYYMMDD') + + return a === b +} + +export function init () { + let records = getAllRecords() + if (records == null) { + saveAllRecords([]) + } + + postRecords() + if (window != null) { + window.addEventListener('online', postRecords) + window.setInterval(postRecords, 1000 * 60 * 60 * 24) + } +} + +export function getClientKey () { + let clientKey = localStorage.getItem('clientKey') + if (!_.isString(clientKey) || clientKey.length !== 40) { + clientKey = keygen() + localStorage.setItem('clientKey', clientKey) + } + + return clientKey +} + +export function getAllRecords () { + return JSON.parse(localStorage.getItem('activityRecords')) +} + +export function saveAllRecords (records) { + localStorage.setItem('activityRecords', JSON.stringify(records)) +} + +/* +Post all records(except today) +and remove all posted records +*/ +export function postRecords (data) { + let records = getAllRecords() + records = records.filter(record => { + return !isSameDate(new Date(), record.date) + }) + + if (records.length === 0) { + console.log('No records to post') + return + } + + console.log('posting...', records) + let input = { + clientKey: getClientKey(), + records + } + return request.post(WEB_URL + 'apis/activity') + .send(input) + .then(res => { + let records = getAllRecords() + let todayRecord = _.find(records, record => { + return isSameDate(new Date(), record.date) + }) + if (todayRecord != null) saveAllRecords([todayRecord]) + else saveAllRecords([]) + }) + .catch(err => { + console.error(err) + }) +} + +export function emit (type, data) { + let records = getAllRecords() + + let index = _.findIndex(records, record => { + return isSameDate(new Date(), record.date) + }) + + let todayRecord + if (index < 0) { + todayRecord = {date: new Date()} + records.push(todayRecord) + } + else todayRecord = records[index] + console.log(type) + switch (type) { + case 'ARTICLE_CREATE': + case 'ARTICLE_UPDATE': + case 'ARTICLE_DESTROY': + case 'FOLDER_CREATE': + case 'FOLDER_UPDATE': + case 'FOLDER_DESTROY': + case 'FINDER_OPEN': + case 'FINDER_COPY': + todayRecord[type] = todayRecord[type] == null + ? 1 + : todayRecord[type] + 1 + + break + } + + let storeData = dataStore.getData() + todayRecord.FOLDER_COUNT = _.isArray(storeData.folders) ? storeData.folders.length : 0 + todayRecord.ARTICLE_COUNT = _.isArray(storeData.articles) ? storeData.articles.length : 0 + + saveAllRecords(records) +} + +export default { + init, + emit, + getClientKey, + postRecords +} diff --git a/lib/api.js b/lib/api.js index 5a3ffa8f..e7156ce5 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,9 +1,12 @@ import superagent from 'superagent' import superagentPromise from 'superagent-promise' -import { API_URL } from '../config' import auth from 'boost/auth' -const request = superagentPromise(superagent, Promise) +export const API_URL = 'http://boost-api4.elasticbeanstalk.com/' +// export const WEB_URL = 'http://b00st.io/' +export const WEB_URL = 'http://localhost:3333/' + +export const request = superagentPromise(superagent, Promise) export function login (input) { return request @@ -163,6 +166,9 @@ export function sendEmail (input) { } export default { + API_URL, + WEB_URL, + request, login, signup, updateUserInfo, diff --git a/lib/components/modal/Preference/ContactTab.js b/lib/components/modal/Preference/ContactTab.js new file mode 100644 index 00000000..0de972a3 --- /dev/null +++ b/lib/components/modal/Preference/ContactTab.js @@ -0,0 +1,123 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import { getClientKey } from 'boost/activityRecord' +import linkState from 'boost/linkState' +import _ from 'lodash' +import { request, WEB_URL } from 'boost/api' + +const FORM_MODE = 'FORM_MODE' +const DONE_MODE = 'DONE_MODE' + +export default class ContactTab extends React.Component { + constructor (props) { + super(props) + + this.state = { + title: '', + content: '', + email: '', + mode: FORM_MODE, + alert: null + } + } + + componentDidMount () { + let titleInput = ReactDOM.findDOMNode(this.refs.title) + if (titleInput != null) titleInput.focus() + } + + handleBackButtonClick (e) { + this.setState({ + mode: FORM_MODE + }) + } + + handleSendButtonClick (e) { + let input = _.pick(this.state, ['title', 'content', 'email']) + input.clientKey = getClientKey() + + this.setState({ + alert: { + type: 'info', + message: 'Sending...' + } + }, () => { + request.post(WEB_URL + 'apis/inquiry') + .send(input) + .then(res => { + console.log('sent') + this.setState({ + title: '', + content: '', + mode: DONE_MODE, + alert: null + }) + }) + .catch(err => { + if (err.code === 'ECONNREFUSED') { + this.setState({ + alert: { + type: 'error', + message: 'Can\'t connect to API server.' + } + }) + } else { + console.error(err) + this.setState({ + alert: { + type: 'error', + message: err.message + } + }) + } + }) + }) + } + + render () { + switch (this.state.mode) { + case DONE_MODE: + return ( +
+
+
+ Your message has been sent successfully!! +
+
+ +
+
+ ) + case FORM_MODE: + default: + let alertElement = this.state.alert != null + ? ( +
{this.state.alert.message}
+ ) + : null + return ( +
+
Contact form
+
+ Your feedback is highly appreciated and will help us to improve our app. :D +
+
+ +
+
+