From 24e2544544dd6d4b234ff05b18e7eade7ebbe97e Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Sun, 13 Dec 2015 14:22:45 +0900 Subject: [PATCH] Url share done --- browser/main/HomePage.js | 9 +- .../HomePage/ArticleDetail/ShareButton.js | 135 +++++++++++++ .../index.js} | 14 +- browser/main/MainPage.js | 10 - .../components/ArticleDetail.styl | 82 +++++++- lib/actions.js | 20 ++ lib/activityRecord.js | 15 +- lib/api.js | 189 +----------------- lib/clientKey.js | 23 +++ lib/components/modal/ContactModal.js | 85 -------- lib/reducer.js | 4 +- 11 files changed, 291 insertions(+), 295 deletions(-) create mode 100644 browser/main/HomePage/ArticleDetail/ShareButton.js rename browser/main/HomePage/{ArticleDetail.js => ArticleDetail/index.js} (98%) create mode 100644 lib/clientKey.js delete mode 100644 lib/components/modal/ContactModal.js diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js index cc0c127a..9b44174b 100644 --- a/browser/main/HomePage.js +++ b/browser/main/HomePage.js @@ -9,7 +9,7 @@ import _ from 'lodash' import { isModalOpen, closeModal } from 'boost/modal' const electron = require('electron') -const BrowserWindow = electron.remote.BrowserWindow +const remote = electron.remote const TEXT_FILTER = 'TEXT_FILTER' const FOLDER_FILTER = 'FOLDER_FILTER' @@ -29,10 +29,10 @@ class HomePage extends React.Component { } handleKeyDown (e) { - if (process.env.BOOST_ENV === 'development' && e.keyCode === 73 && e.metaKey && e.altKey) { + if (e.keyCode === 73 && e.metaKey && e.altKey) { e.preventDefault() e.stopPropagation() - BrowserWindow.getFocusedWindow().toggleDevTools() + remote.getCurrentWebContents().openDevTools() return } @@ -106,7 +106,7 @@ class HomePage extends React.Component { list.selectNextArticle() } - if (e.keyCode === 65 || (e.keyCode === 13 && e.metaKey) || (e.keyCode === 78 && e.metaKey)) { + if ((e.keyCode === 65 && !e.metaKey && !e.ctrlKey) || (e.keyCode === 13 && e.metaKey) || (e.keyCode === 78 && e.metaKey)) { nav.handleNewPostButtonClick() e.preventDefault() } @@ -142,6 +142,7 @@ class HomePage extends React.Component { Copied! + copied: false + } +} + +export default class ShareButton extends React.Component { + constructor (props) { + super(props) + this.state = getDefault() + } + + componentWillReceiveProps (nextProps) { + this.setState(getDefault()) + } + + componentDidMount () { + this.dropdownInterceptor = e => { + this.dropdownClicked = true + } + ReactDOM.findDOMNode(this.refs.dropdown).addEventListener('click', this.dropdownInterceptor) + this.shareWithPublicURLHandler = e => { + this.handleShareWithPublicURLClick(e) + } + } + + componentWillUnmount () { + document.removeEventListener('click', this.dropdownHandler) + ReactDOM.findDOMNode(this.refs.dropdown).removeEventListener('click', this.dropdownInterceptor) + } + + handleOpenButtonClick (e) { + this.openDropdown() + if (this.dropdownHandler == null) { + this.dropdownHandler = e => { + if (!this.dropdownClicked) { + this.closeDropdown() + } else { + this.dropdownClicked = false + } + } + } + document.removeEventListener('click', this.dropdownHandler) + document.addEventListener('click', this.dropdownHandler) + } + + openDropdown () { + this.setState({openDropdown: true}) + } + + closeDropdown () { + document.removeEventListener('click', this.dropdownHandler) + this.setState({openDropdown: false}) + } + + handleShareWithPublicURLClick (e) { + let input = Object.assign({}, this.props.article, {clientKey: clientKey.get()}) + api.shareWithPublicURL(input) + .then(res => { + let url = res.body.url + this.setState({url: url}) + }) + .catch(err => { + console.log(err) + }) + } + + handleCopyURLClick () { + clipboard.writeText(this.state.url) + this.setState({copied: true}) + } + + // Restore copy url tooltip + handleCopyURLMouseLeave () { + this.setState({copied: false}) + } + + render () { + let hasPublicURL = this.state.url != null + return ( +
+ +
+ { + !hasPublicURL ? ( + + ) : ( +
+ + +
This url is valid for 7 days.
+
+ ) + } +
+
+ ) + } +} + +ShareButton.propTypes = { + article: PropTypes.shape({ + publicURL: PropTypes.string + }) +} diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail/index.js similarity index 98% rename from browser/main/HomePage/ArticleDetail.js rename to browser/main/HomePage/ArticleDetail/index.js index e11c140b..bbdf1ba9 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail/index.js @@ -24,6 +24,8 @@ import TagLink from 'boost/components/TagLink' import TagSelect from 'boost/components/TagSelect' import ModeSelect from 'boost/components/ModeSelect' import activityRecord from 'boost/activityRecord' +import api from 'boost/api' +import ShareButton from './ShareButton' const electron = require('electron') const clipboard = electron.clipboard @@ -106,12 +108,16 @@ export default class ArticleDetail extends React.Component { isTagChanged: false, isTitleChanged: false, isContentChanged: false, - isModeChanged: false + isModeChanged: false, + openShareDropdown: false } } componentDidMount () { this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) + this.shareDropdownInterceptor = e => { + e.stopPropagation() + } } componentWillUnmount () { @@ -234,9 +240,12 @@ export default class ArticleDetail extends React.Component {
{tags}
+ + + @@ -586,7 +595,8 @@ export default class ArticleDetail extends React.Component { ArticleDetail.propTypes = { status: PropTypes.shape(), activeArticle: PropTypes.shape(), - activeUser: PropTypes.shape(), + user: PropTypes.shape(), + folders: PropTypes.array, dispatch: PropTypes.func } ArticleDetail.prototype.linkState = linkState diff --git a/browser/main/MainPage.js b/browser/main/MainPage.js index e1490e96..a5db1fa9 100644 --- a/browser/main/MainPage.js +++ b/browser/main/MainPage.js @@ -2,8 +2,6 @@ const electron = require('electron') const ipc = electron.ipcRenderer import React, { PropTypes } from 'react' -var ContactModal = require('boost/components/modal/ContactModal') - export default class MainContainer extends React.Component { constructor (props) { super(props) @@ -20,20 +18,12 @@ export default class MainContainer extends React.Component { ipc.send('update-app', 'Deal with it.') } - openContactModal () { - this.openModal(ContactModal) - } - render () { return (
{this.state.updateAvailable ? ( ) : null} - {/* */} {this.props.children}
) diff --git a/browser/styles/main/HomeContainer/components/ArticleDetail.styl b/browser/styles/main/HomeContainer/components/ArticleDetail.styl index e64e5da6..61e4d8d9 100644 --- a/browser/styles/main/HomeContainer/components/ArticleDetail.styl +++ b/browser/styles/main/HomeContainer/components/ArticleDetail.styl @@ -284,7 +284,87 @@ iptFocusBorderColor = #369DCD color noTagsColor .right z-index 30 - button + div.share-dropdown + position absolute + right 5px + top 30px + background-color transparentify(invBackgroundColor, 80%) + padding 5px 0 + width 200px + &.hide + display none + &>button + width 200px + text-align left + display block + height 33px + background-color transparent + color white + font-size 14px + padding 0 10px + border none + &:hover + background-color transparentify(lighten(invBackgroundColor, 30%), 80%) + &>.ShareButton-url + clearfix() + input.ShareButton-url-input + width 155px + margin 0 0 0 5px + height 25px + outline none + border none + border-top-left-radius 5px + border-bottom-left-radius 5px + float left + padding 5px + button.ShareButton-url-button + width 35px + height 25px + border none + margin 0 5px 0 0 + outline none + border-top-right-radius 5px + border-bottom-right-radius 5px + background-color darken(white, 5%) + color inactiveTextColor + float right + div.ShareButton-url-button-tooltip + tooltip() + right 10px + &:hover + color textColor + div.ShareButton-url-button-tooltip + opacity 1 + div.ShareButton-url-alert + float left + height 25px + line-height 25px + padding 0 15px + color white + + .ShareButton + display inline-block + button.ShareButton-open-button + border-radius 16.5px + cursor pointer + height 33px + width 33px + border none + margin-right 5px + font-size 18px + color inactiveTextColor + background-color darken(white, 5%) + padding 0 + .tooltip + tooltip() + margin-top 25px + margin-left -40px + &:hover + color textColor + .tooltip + opacity 1 + + &>button border-radius 16.5px cursor pointer height 33px diff --git a/lib/actions.js b/lib/actions.js index 39c7a4f4..b33b0945 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -146,3 +146,23 @@ export function toggleTutorial () { type: TOGGLE_TUTORIAL } } + +export default { + updateUser, + clearNewArticle, + updateArticle, + destroyArticle, + createFolder, + updateFolder, + destroyFolder, + replaceFolder, + switchFolder, + switchMode, + switchArticle, + setSearchFilter, + setTagFilter, + clearSearch, + lockStatus, + unlockStatus, + toggleTutorial +} diff --git a/lib/activityRecord.js b/lib/activityRecord.js index 79036010..821ab7e6 100644 --- a/lib/activityRecord.js +++ b/lib/activityRecord.js @@ -1,8 +1,8 @@ import _ from 'lodash' import moment from 'moment' -import keygen from 'boost/keygen' import dataStore from 'boost/dataStore' import { request, WEB_URL } from 'boost/api' +import clientKey from 'boost/clientKey' const electron = require('electron') const version = electron.remote.app.getVersion() @@ -28,16 +28,6 @@ export function init () { } } -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')) } @@ -67,7 +57,7 @@ export function postRecords (data) { console.log('posting...', records) let input = { - clientKey: getClientKey(), + clientKey: clientKey.get(), records } return request.post(WEB_URL + 'apis/activity') @@ -142,6 +132,5 @@ export function emit (type, data = {}) { export default { init, emit, - getClientKey, postRecords } diff --git a/lib/api.js b/lib/api.js index 6a5f9f91..63b307cf 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,191 +1,22 @@ import superagent from 'superagent' import superagentPromise from 'superagent-promise' -import auth from 'boost/auth' +// import auth from 'boost/auth' -export const API_URL = 'http://boost-api4.elasticbeanstalk.com/' -export const WEB_URL = 'https://b00st.io/' -// export const WEB_URL = 'http://localhost:3333/' +// export const SERVER_URL = 'https://b00st.io/' +export const SERVER_URL = 'http://localhost:3333/' export const request = superagentPromise(superagent, Promise) -export function login (input) { +export function shareWithPublicURL (input) { return request - .post(API_URL + 'auth/login') - .send(input) -} - -export function signup (input) { - return request - .post(API_URL + 'auth/register') - .send(input) -} - -export function updateUserInfo (input) { - return request - .put(API_URL + 'auth/user') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function updatePassword (input) { - return request - .post(API_URL + 'auth/password') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function fetchCurrentUser () { - return request - .get(API_URL + 'auth/user') - .set({ - Authorization: 'Bearer ' + auth.token() - }) -} - -export function fetchArticles (userId) { - return request - .get(API_URL + 'teams/' + userId + '/articles') - .set({ - Authorization: 'Bearer ' + auth.token() - }) -} - -export function createArticle (input) { - return request - .post(API_URL + 'articles/') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function saveArticle (input) { - return request - .put(API_URL + 'articles/' + input.id) - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function destroyArticle (articleId) { - return request - .del(API_URL + 'articles/' + articleId) - .set({ - Authorization: 'Bearer ' + auth.token() - }) -} - -export function createTeam (input) { - return request - .post(API_URL + 'teams') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function updateTeamInfo (teamId, input) { - return request - .put(API_URL + 'teams/' + teamId) - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function destroyTeam (teamId) { - return request - .del(API_URL + 'teams/' + teamId) - .set({ - Authorization: 'Bearer ' + auth.token() - }) -} - -export function searchUser (key) { - return request - .get(API_URL + 'search/users') - .query({key: key}) -} - -export function setMember (teamId, input) { - return request - .post(API_URL + 'teams/' + teamId + '/members') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function deleteMember (teamId, input) { - return request - .del(API_URL + 'teams/' + teamId + '/members') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function createFolder (input) { - return request - .post(API_URL + 'folders/') - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function updateFolder (id, input) { - return request - .put(API_URL + 'folders/' + id) - .set({ - Authorization: 'Bearer ' + auth.token() - }) - .send(input) -} - -export function destroyFolder (id) { - return request - .del(API_URL + 'folders/' + id) - .set({ - Authorization: 'Bearer ' + auth.token() - }) -} - -export function sendEmail (input) { - return request - .post(API_URL + 'mail') - .set({ - Authorization: 'Bearer ' + auth.token() - }) + .post(SERVER_URL + 'apis/share') + // .set({ + // Authorization: 'Bearer ' + auth.token() + // }) .send(input) } export default { - API_URL, - WEB_URL, - request, - login, - signup, - updateUserInfo, - updatePassword, - fetchCurrentUser, - fetchArticles, - createArticle, - saveArticle, - destroyArticle, - createTeam, - updateTeamInfo, - destroyTeam, - searchUser, - setMember, - deleteMember, - createFolder, - updateFolder, - destroyFolder, - sendEmail + SERVER_URL, + shareWithPublicURL } diff --git a/lib/clientKey.js b/lib/clientKey.js new file mode 100644 index 00000000..b76d6bb8 --- /dev/null +++ b/lib/clientKey.js @@ -0,0 +1,23 @@ +import _ from 'lodash' +import keygen from 'boost/keygen' + +function getClientKey () { + let clientKey = localStorage.getItem('clientKey') + if (!_.isString(clientKey) || clientKey.length !== 40) { + clientKey = keygen() + setClientKey(clientKey) + } + + return clientKey +} + +function setClientKey (newKey) { + localStorage.setItem('clientKey', newKey) +} + +getClientKey() + +export default { + get: getClientKey, + set: setClientKey +} diff --git a/lib/components/modal/ContactModal.js b/lib/components/modal/ContactModal.js deleted file mode 100644 index f3106ea6..00000000 --- a/lib/components/modal/ContactModal.js +++ /dev/null @@ -1,85 +0,0 @@ -import React, { PropTypes, findDOMNode } from 'react' -import linkState from 'boost/linkState' -import { sendEmail } from 'boost/api' - -export default class ContactModal extends React.Component { - constructor (props) { - super(props) - - this.linkState = linkState - - this.state = { - isSent: false, - mail: { - title: '', - content: '' - } - } - } - - onKeyCast (e) { - switch (e.status) { - case 'closeModal': - this.props.close() - break - case 'submitContactModal': - if (this.state.isSent) { - this.props.close() - return - } - this.sendEmail() - break - } - } - - componentDidMount () { - findDOMNode(this.refs.title).focus() - } - - sendEmail () { - sendEmail(this.state.mail) - .then(function (res) { - this.setState({isSent: !this.state.isSent}) - }.bind(this)) - .catch(function (err) { - console.error(err) - }) - } - - render () { - return ( -
-

Contact form

- - {!this.state.isSent ? ( -
-
-
- -
-
-