From 72e4a4a2cac77750693a7e35fe8cdc864e858319 Mon Sep 17 00:00:00 2001 From: Ed Summers Date: Thu, 21 May 2020 09:23:41 -0400 Subject: [PATCH] Display Authorization URL This commit makes the URL for authorizing the Hydator at Twitter visible for users where the Hydrator was prevented from opening the window directly. See #38 and #27 for context. In addition the User's screen name is collected after linking so that can be displayed along with the keys. --- app/main/index.js | 37 ++++++++++++----- app/renderer/actions/settings.js | 8 ++-- app/renderer/components/Settings.js | 62 ++++++++++++++++++++++++----- app/renderer/reducers/settings.js | 4 +- app/utils/twitter.js | 16 ++++++++ 5 files changed, 102 insertions(+), 25 deletions(-) diff --git a/app/main/index.js b/app/main/index.js index e1026f1..2fc3b94 100644 --- a/app/main/index.js +++ b/app/main/index.js @@ -5,7 +5,7 @@ import storage from 'electron-json-storage' import TwitterPinAuth from 'twitter-pin-auth' import { app, shell, crashReporter, BrowserWindow, Menu, ipcMain } from 'electron' -import { hydrateToStream, toCsv } from '../utils/twitter' +import { hydrateToStream, toCsv, getUserSettings } from '../utils/twitter' import { GET_SAVED_STORE, SAVE, AUTOSAVE, AUTHORIZE, SEND_PIN } from '../renderer/actions/settings' import { START_CSV_EXPORT, STOP_CSV_EXPORT, START_HYDRATION, STOP_HYDRATION, STOPPED_HYDRATION, FINISH_HYDRATION, DELETE_DATASET } from '../renderer/actions/dataset' @@ -126,23 +126,40 @@ app.on('ready', async () => { }) }) - ipcMain.on(AUTHORIZE, () => { + ipcMain.on(AUTHORIZE, event => { twitterPinAuth.requestAuthUrl(). then(url => { + event.returnValue = url shell.openExternal(url) }).catch(err => { console.error(err) }) }) - ipcMain.on(SEND_PIN, (event, arg) => { - twitterPinAuth.authorize(arg.pin) - .then(credentials => { - event.returnValue = credentials - }).catch(err => { - console.error(err) - event.returnValue = null - }) + ipcMain.on(SEND_PIN, async (event, arg) => { + let result = null + try { + const credentials = await twitterPinAuth.authorize(arg.pin) + if (credentials) { + // use keys to get the screenName of the authenticating user + const auth = { + consumer_key: "TWITTER_CONSUMER_KEY", + consumer_secret: "TWITTER_CONSUMER_SECRET", + access_token: credentials.accessTokenKey, + access_token_secret: credentials.accessTokenSecret + } + const settings = await getUserSettings(auth) + if (settings) { + result = { + ...credentials, + screenName: settings.screen_name + } + } + } + } catch(e) { + console.error(e) + } + event.returnValue = result }) ipcMain.on(START_CSV_EXPORT, (event, arg) => { diff --git a/app/renderer/actions/settings.js b/app/renderer/actions/settings.js index 03c7913..3855cec 100644 --- a/app/renderer/actions/settings.js +++ b/app/renderer/actions/settings.js @@ -13,9 +13,10 @@ export const GET_SAVED_STORE = 'GET_SAVED_STORE' export const AUTOSAVE = 'AUTOSAVE' export function authorize() { - ipcRenderer.send(AUTHORIZE) + const url = ipcRenderer.sendSync(AUTHORIZE) return { - type: AUTHORIZE + type: AUTHORIZE, + url: url } } @@ -29,7 +30,8 @@ export function getTwitterCredentials(pin) { return { type: SET_TWITTER_CREDENTIALS, twitterAccessKey: credentials.accessTokenKey, - twitterAccessSecret: credentials.accessTokenSecret + twitterAccessSecret: credentials.accessTokenSecret, + twitterScreenName: credentials.screenName } } } diff --git a/app/renderer/components/Settings.js b/app/renderer/components/Settings.js index d9391f8..42937b2 100644 --- a/app/renderer/components/Settings.js +++ b/app/renderer/components/Settings.js @@ -5,6 +5,9 @@ const Container = styled.div` input { width: auto; } + form { + padding: 0px; + } ` const Key = styled.div` @@ -12,6 +15,11 @@ const Key = styled.div` font-size: smaller; ` +const ScreenName = styled.div` + font-size: smaller; + color: blue; +` + const Reset = styled.button` color: red; ` @@ -20,6 +28,11 @@ const Error = styled.div` color: red; ` +const TwitterAuthUrl = styled.div` + font-style: italic; + font-size: smaller; +` + export default class Settings extends Component { reset() { @@ -38,6 +51,13 @@ export default class Settings extends Component { e.preventDefault() this.props.getTwitterCredentials(pin.value) }}> +

+ Note: If a browser window did not open for you please open this URL in your browser + and follow the instructions to obtain your PIN: +
+
+ {this.props.settings.authorize} +

pin = node} name="twitterPin" @@ -48,6 +68,7 @@ export default class Settings extends Component { type="text" />       +
} else { form = @@ -59,28 +80,47 @@ export default class Settings extends Component { } + let screenName = "" + if (this.props.settings.twitterScreenName) { + screenName = <> + + @{this.props.settings.twitterScreenName} +
+ + } + + let settings = "" + if (this.props.settings.twitterAccessKey) { + settings = <> + {screenName} + + { this.props.settings.twitterAccessKey } +
+ + { this.props.settings.twitterAccessSecret } +
+
+ this.reset()}>Reset + + } + return (
Settings

- These are your settings that control who you connect - to the Twitter API as. You will be directed over to - Twitter to grant your Hydrator application permission to - use your account to retrieve Twitter data. + These settings that control who you connect + to the Twitter API as. When you click Link Your Twitter Account + you will be directed to Twitter to grant your Hydrator + application permission to use your account to retrieve Twitter + data.

{ form }
- - { this.props.settings.twitterAccessKey } -
- - { this.props.settings.twitterAccessSecret } + { settings }
{ error } -
- this.reset()}>Reset Settings
) } diff --git a/app/renderer/reducers/settings.js b/app/renderer/reducers/settings.js index afa70aa..965e2a8 100644 --- a/app/renderer/reducers/settings.js +++ b/app/renderer/reducers/settings.js @@ -12,6 +12,7 @@ export default function settings(state = {}, action) { ...state, twitterAccessKey: state.twitterAccessKey, twitterAccessSecret: state.twitterAccessSecret, + twitterScreenName: state.twitterScreenName, authorize: false, invalidPin: false } @@ -20,7 +21,7 @@ export default function settings(state = {}, action) { case AUTHORIZE: { return { ...state, - authorize: true, + authorize: action.url, invalidPin: false } } @@ -40,6 +41,7 @@ export default function settings(state = {}, action) { invalidPin: false, twitterAccessKey: action.twitterAccessKey, twitterAccessSecret: action.twitterAccessSecret, + twitterScreenName: action.twitterScreenName, } } diff --git a/app/utils/twitter.js b/app/utils/twitter.js index 80e44a0..6d62212 100644 --- a/app/utils/twitter.js +++ b/app/utils/twitter.js @@ -25,6 +25,22 @@ export function checkTweetIdFile(path) { }) } +/** + * Returns the Twitter account settings for the supplied keys. + * @param {*} auth an object containing Twitter API authentication keys + */ + +export async function getUserSettings(auth) { + var twitter = Twit({ + consumer_key: auth.consumer_key, + consumer_secret: auth.consumer_secret, + access_token: auth.access_token, + access_token_secret: auth.access_token_secret + }) + const resp = await twitter.get('account/settings') + return resp.data +} + /** * This is kind of an awful function with tons of side effects which * will write hdyrated tweets for a set of tweet ids to an output stream