This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,4 +49,4 @@ RUN yarn run heroku-postbuild

ENV PLOTLY_CONNECTOR_PORT 9494
EXPOSE 9494
ENTRYPOINT yarn run build-web && yarn run start-headless
ENTRYPOINT yarn run start-headless
54 changes: 25 additions & 29 deletions app/components/Configuration.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,21 @@ import cookie from 'react-cookies';
import React, { Component, PropTypes } from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Settings from './Settings/Settings.react';
import {isElectron} from '../utils/utils';
import * as SessionsActions from '../actions/sessions';
import Login from './Login.react';

import Login from './Login.react.js';
import * as SessionsActions from '../actions/sessions.js';
import Settings from './Settings/Settings.react.js';
import {isElectron, setUsernameListener} from '../utils/utils.js';


class Configuration extends Component {
constructor(props) {
super(props);
this.state = {
authEnabled: (cookie.load('db-connector-auth-enabled') === 'true'),
clientId: cookie.load('db-connector-oauth2-client-id'),
isMenuOpen: false,
username: cookie.load('db-connector-user'),
/* global PLOTLY_ENV */
authDisabled: !PLOTLY_ENV.AUTH_ENABLED
username: cookie.load('db-connector-user')
};
this.toggle = this.toggle.bind(this);
this.close = this.close.bind(this);
Expand All@@ -27,12 +28,9 @@ class Configuration extends Component {
* In the browser, the username is set with a cookie but in electron
* this is set using electron's ipcRenderer.
*/
if (isElectron()) {
window.require('electron').ipcRenderer.once('username',
(event, message) => {
this.setState({username: message});
});
}
setUsernameListener((event, message) => {
this.setState({username: message});}
);
}

toggle() {
Expand All@@ -44,26 +42,24 @@ class Configuration extends Component {
}

logOut() {
/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
this.setState({ username: ''});

/*
* Delete all the cookies and reset user state. This does not kill
* any running connections, but user will not be able to access them
* without logging in again.
*/
cookie.remove('db-connector-user');
cookie.remove('plotly-auth-token');
cookie.remove('db-connector-auth-token');
cookie.remove('db-connector-auth-disabled');
this.setState({ username: ''});

// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
// reload page when running in browser:
if (!isElectron()) {
window.location.reload();
}
}

render() {
return (isElectron() || this.state.authDisabled || this.state.username) ? (
return (isElectron() || !this.state.authEnabled || this.state.username) ? (
<div className="fullApp">
<Settings username={this.state.username} logout={this.logOut}/>
</div>
Expand Down
28 changes: 1 addition & 27 deletions app/components/Link.react.js
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
import R from 'ramda';
import React from 'react';
import PropTypes from 'prop-types';
import {dynamicRequireElectron} from '../utils/utils';


let shell;
try {
shell = dynamicRequireElectron().shell;
} catch (e) {
shell = null;
}

export function Link(props) {
const {href} = props;
if (shell) {
return (
<span
className={`${props.className}`}
onClick={() => {shell.openExternal(href);}}
{...R.omit(['className'], props)}
/>
);
}
return <a href={href} target="_blank" {...props}/>;
return <a target="_blank" rel="noopener" {...props}/>;
}

Link.propTypes = {
href: PropTypes.string,
className: PropTypes.string
};
83 changes: 34 additions & 49 deletions app/components/Login.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
baseUrl,
dynamicRequireElectron,
homeUrl,
isOnPrem,
plotlyUrl
Expand All@@ -21,35 +20,12 @@ const CLOUD = 'cloud';
const ONPREM = 'onprem';

window.document.title = `${build.productName} v${version}`;
let usernameLogged = '';

// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const PopupCenter = (url, title, w, h) => {
// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const popupWindow = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
return popupWindow;
};

class Login extends Component {
constructor(props) {
super(props);
this.state = {
clientId: cookie.load('db-connector-oauth2-client-id'),
domain: (isOnPrem() ? plotlyUrl() : 'https://plot.ly'),
statusMessage: '',
serverType: CLOUD,
Expand All@@ -58,6 +34,11 @@ class Login extends Component {
this.buildOauthUrl = this.buildOauthUrl.bind(this);
this.oauthPopUp = this.oauthPopUp.bind(this);
this.logIn = this.logIn.bind(this);

// the web app:
// - sets this property to the popup window opened for authorization
// - and triggers a reload when `this.popup.closed` becomes true
this.popup = null;
}

componentDidMount() {
Expand All@@ -69,8 +50,7 @@ class Login extends Component {
* to check for authentication.
*/
setInterval(() => {
usernameLogged = cookie.load('db-connector-user');
if (usernameLogged) {
if (this.popup && this.popup.closed) {
if (serverType === ONPREM) {
this.setState({
status: 'authorized',
Expand DownExpand Up@@ -101,7 +81,6 @@ class Login extends Component {
}

saveDomainToSettings() {

const {domain} = this.state;
let PLOTLY_API_SSL_ENABLED = true;
let PLOTLY_API_DOMAIN = '';
Expand All@@ -125,35 +104,41 @@ class Login extends Component {

}
buildOauthUrl() {
const {domain} = this.state;
/* global PLOTLY_ENV */
const oauthClientId = PLOTLY_ENV.OAUTH2_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to get rid of this.. #Cleanup.. :)


const {clientId, domain} = this.state;
const redirect_uri = baseUrlWrapped;
return (
`${domain}/o/authorize/?response_type=token&` +
`client_id=${oauthClientId}&` +
`client_id=${clientId}&` +
`redirect_uri=${redirect_uri}/oauth2/callback`
);
}

oauthPopUp() {
try {
const electron = dynamicRequireElectron();
const oauthUrl = this.buildOauthUrl();
electron.shell.openExternal(oauthUrl);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Unable to openExternal, opening a popupWindow instead:');
// eslint-disable-next-line no-console
console.log(e);
const popupWindow = PopupCenter(
this.buildOauthUrl(), 'Authorization', '500', '500'
);
if (window.focus) {
popupWindow.focus();
}
}
// http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
const url = this.buildOauthUrl();
const title = 'Authorization';
const w = '600';
const h = '600';

// Fixes dual-screen position
const dualScreenLeft = 'screenLeft' in window ? window.screenLeft : screen.left;
const dualScreenTop = 'screenTop' in window ? window.screenTop : screen.top;

const width = window.innerWidth
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;

const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;

this.popup = window.open(url, title, `scrollbars=yes, width=${w}, height=${h}, top=${top}, left=${left}`);
}

logIn () {
Expand Down
32 changes: 14 additions & 18 deletions app/components/Settings/Preview/Preview.react.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,7 +171,6 @@ class Preview extends Component {
}

fetchDatacache(payload, type) {

const {username} = this.props;
const payloadJSON = JSON.stringify({
payload: payload, type: type, requestor: username});
Expand All@@ -187,7 +186,8 @@ class Preview extends Component {
}).then(resp => {
return resp.json();
}).then(data => {
const plotlyLinks = this.state.plotlyLinks;
const {plotlyLinks} = this.state;

let link;

if (!('error' in data)) {
Expand DownExpand Up@@ -456,6 +456,15 @@ class Preview extends Component {
<TabPanel>
<div className="export-options-container">
<div style={{margin: '20px 0'}}>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'grid')}
Expand All@@ -465,19 +474,12 @@ class Preview extends Component {
{!isOnPrem() &&
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(this.getCSVString(), 'csv')}
onClick={() => window.open(
`data:text/csv;base64,${Buffer.from(this.getCSVString()).toString('base64')}`
)}
>
Download CSV
</button>}
<button
className="btn btn-outline"
onClick={() => this.fetchDatacache(
JSON.stringify(this.state.plotlyJSON),
'plot'
)}
>
Send chart to Chart Studio
</button>
</div>
<div style={{width: 650, height: 200, border: '1px solid #dfe8f3',
fontFamily: '\'Ubuntu Mono\', courier, monospace', paddingTop: 10,
Expand All@@ -491,12 +493,6 @@ class Preview extends Component {
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'csv' &&
<div>
<div style={{color: '#00cc96'}}>💾 Your CSV has been saved ⬇️</div>
<Link href={link.url} target="_blank" className="externalLink">{link.url}</Link>
</div>
}
{link.type === 'plot' &&
<div>
<div style={{color: '#00cc96'}}>📈 Link to your chart on Chart Studio ⬇️</div>
Expand Down
Loading