Add option to set custom user agent (#98)

This commit is contained in:
Quang Lam 2020-01-03 22:58:33 -06:00 committed by GitHub
parent 748d072ed1
commit 24db550232
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 271 additions and 28 deletions

View file

@ -9,6 +9,8 @@ const { autoUpdater } = require('electron-updater');
const aboutWindow = require('../windows/about');
const addWorkspaceWindow = require('../windows/add-workspace');
const codeInjectionWindow = require('../windows/code-injection');
const customUserAgentWindow = require('../windows/custom-user-agent');
const displayMediaWindow = require('../windows/display-media');
const editWorkspaceWindow = require('../windows/edit-workspace');
const licenseRegistrationWindow = require('../windows/license-registration');
@ -196,6 +198,32 @@ function createMenu() {
}
},
},
{
label: 'Code Injection Window',
click: () => {
const win = codeInjectionWindow.get();
if (win != null) {
if (win.webContents.isDevToolsOpened()) {
win.webContents.closeDevTools();
} else {
win.webContents.openDevTools({ mode: 'detach' });
}
}
},
},
{
label: 'Custom User Agent Window',
click: () => {
const win = customUserAgentWindow.get();
if (win != null) {
if (win.webContents.isDevToolsOpened()) {
win.webContents.closeDevTools();
} else {
win.webContents.openDevTools({ mode: 'detach' });
}
}
},
},
{
label: 'Add Workspace Window',
click: () => {

View file

@ -88,6 +88,7 @@ const defaultPreferences = {
askForDownloadPath: true,
attachToMenubar: false,
cssCodeInjection: null,
customUserAgent: null,
downloadPath: getDefaultDownloadsPath(),
hibernateUnusedWorkspacesAtLaunch: false,
jsCodeInjection: null,

View file

@ -32,6 +32,7 @@ const addView = (browserWindow, workspace) => {
if (views[workspace.id] != null) return;
const {
customUserAgent,
rememberLastPageVisited,
shareWorkspaceBrowsingData,
unreadCountBadge,
@ -47,35 +48,43 @@ const addView = (browserWindow, workspace) => {
},
});
// Hide Electron from UA to improve compatibility
// https://github.com/quanglam2807/webcatalog/issues/182
const uaStr = view.webContents.getUserAgent();
const commonUaStr = uaStr
// Fix WhatsApp requires Google Chrome 49+ bug
.replace(` ${app.getName()}/${app.getVersion()}`, '')
let adjustUserAgentByUrl = () => false;
if (customUserAgent) {
view.webContents.setUserAgent(customUserAgent);
} else {
// Hide Electron from UA to improve compatibility
// https://github.com/quanglam2807/webcatalog/issues/182
.replace(` Electron/${process.versions.electron}`, '');
view.webContents.setUserAgent(commonUaStr);
const uaStr = view.webContents.getUserAgent();
const commonUaStr = uaStr
// Fix WhatsApp requires Google Chrome 49+ bug
.replace(` ${app.getName()}/${app.getVersion()}`, '')
// Hide Electron from UA to improve compatibility
// https://github.com/quanglam2807/webcatalog/issues/182
.replace(` Electron/${process.versions.electron}`, '');
view.webContents.setUserAgent(customUserAgent || commonUaStr);
// fix Google prevents signing in because of security concerns
// https://github.com/quanglam2807/webcatalog/issues/455
// https://github.com/meetfranz/franz/issues/1720#issuecomment-566460763
const fakedEdgeUaStr = `${commonUaStr} Edge/18.18875`;
const adjustUserAgentByUrl = (url) => {
const navigatedDomain = extractDomain(url);
const currentUaStr = view.webContents.getUserAgent();
if (navigatedDomain === 'accounts.google.com') {
if (currentUaStr !== fakedEdgeUaStr) {
view.webContents.setUserAgent(fakedEdgeUaStr);
// fix Google prevents signing in because of security concerns
// https://github.com/quanglam2807/webcatalog/issues/455
// https://github.com/meetfranz/franz/issues/1720#issuecomment-566460763
const fakedEdgeUaStr = `${commonUaStr} Edge/18.18875`;
adjustUserAgentByUrl = (url) => {
if (customUserAgent) return false;
const navigatedDomain = extractDomain(url);
const currentUaStr = view.webContents.getUserAgent();
if (navigatedDomain === 'accounts.google.com') {
if (currentUaStr !== fakedEdgeUaStr) {
view.webContents.setUserAgent(fakedEdgeUaStr);
return true;
}
} else if (currentUaStr !== commonUaStr) {
view.webContents.setUserAgent(commonUaStr);
return true;
}
} else if (currentUaStr !== commonUaStr) {
view.webContents.setUserAgent(commonUaStr);
return true;
}
return false;
};
return false;
};
}
view.webContents.on('will-navigate', (e, url) => {
adjustUserAgentByUrl(url);
});

View file

@ -49,6 +49,7 @@ const createMenu = require('../libs/create-menu');
const aboutWindow = require('../windows/about');
const addWorkspaceWindow = require('../windows/add-workspace');
const codeInjectionWindow = require('../windows/code-injection');
const customUserAgentWindow = require('../windows/custom-user-agent');
const displayMediaWindow = require('../windows/display-media');
const editWorkspaceWindow = require('../windows/edit-workspace');
const licenseRegistrationWindow = require('../windows/license-registration');
@ -128,6 +129,9 @@ const loadListeners = () => {
codeInjectionWindow.show(type);
});
ipcMain.on('request-show-custom-user-agent-window', () => {
customUserAgentWindow.show();
});
ipcMain.on('request-reset-preferences', () => {
dialog.showMessageBox(preferencesWindow.get(), {

View file

@ -0,0 +1 @@
window.mode = 'custom-user-agent';

View file

@ -0,0 +1,50 @@
const { BrowserWindow } = require('electron');
const path = require('path');
const { REACT_PATH } = require('../constants/paths');
const { getPreference } = require('../libs/preferences');
const mainWindow = require('./main');
let win;
const get = () => win;
const create = () => {
const attachToMenubar = getPreference('attachToMenubar');
win = new BrowserWindow({
width: 400,
height: 200,
resizable: false,
maximizable: false,
minimizable: false,
fullscreenable: false,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, '..', 'preload', 'custom-user-agent.js'),
},
parent: attachToMenubar ? null : mainWindow.get(),
});
win.loadURL(REACT_PATH);
win.on('closed', () => {
win = null;
});
};
const show = () => {
if (win == null) {
create();
} else {
win.show();
}
};
module.exports = {
get,
create,
show,
};

View file

@ -0,0 +1,78 @@
import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import connectComponent from '../../helpers/connect-component';
import { updateForm, save } from '../../state/custom-user-agent/actions';
const styles = (theme) => ({
root: {
background: theme.palette.background.paper,
height: '100vh',
width: '100vw',
padding: theme.spacing.unit * 3,
display: 'flex',
flexDirection: 'column',
},
flexGrow: {
flex: 1,
},
button: {
float: 'right',
},
});
const CustomUserAgent = ({
classes, code, onUpdateForm, onSave,
}) => (
<div className={classes.root}>
<div className={classes.flexGrow}>
<TextField
id="outlined-full-width"
label="User-Agent"
placeholder=""
helperText="Leave it blank to use default User-Agent string."
fullWidth
margin="dense"
variant="outlined"
multiline={false}
InputLabelProps={{
shrink: true,
}}
value={code}
onChange={(e) => onUpdateForm({ code: e.target.value })}
/>
</div>
<div>
<Button color="primary" variant="contained" className={classes.button} onClick={onSave}>
Save
</Button>
</div>
</div>
);
CustomUserAgent.propTypes = {
classes: PropTypes.object.isRequired,
code: PropTypes.string.isRequired,
onUpdateForm: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
code: state.customUserAgent.form.code,
});
const actionCreators = {
updateForm,
save,
};
export default connectComponent(
CustomUserAgent,
mapStateToProps,
actionCreators,
styles,
);

View file

@ -30,6 +30,7 @@ import {
requestSetPreference,
requestSetSystemPreference,
requestShowCodeInjectionWindow,
requestShowCustomUserAgentWindow,
requestShowRequireRestartDialog,
} from '../../senders';
@ -53,6 +54,11 @@ const styles = (theme) => ({
display: 'flex',
justifyContent: 'space-between',
},
secondaryEllipsis: {
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
},
});
const getThemeString = (theme) => {
@ -124,6 +130,7 @@ const Preferences = ({
attachToMenubar,
classes,
cssCodeInjection,
customUserAgent,
downloadPath,
hibernateUnusedWorkspacesAtLaunch,
isDefaultMailClient,
@ -531,6 +538,15 @@ const Preferences = ({
</ListItemSecondaryAction>
</ListItem>
<Divider />
<ListItem button onClick={requestShowCustomUserAgentWindow}>
<ListItemText
primary="Custom User Agent"
secondary={customUserAgent || 'Not set'}
classes={{ secondary: classes.secondaryEllipsis }}
/>
<ChevronRightIcon color="action" />
</ListItem>
<Divider />
<ListItem button onClick={() => requestShowCodeInjectionWindow('js')}>
<ListItemText primary="JS Code Injection" secondary={jsCodeInjection ? 'Set' : 'Not set'} />
<ChevronRightIcon color="action" />
@ -576,6 +592,7 @@ const Preferences = ({
Preferences.defaultProps = {
cssCodeInjection: null,
jsCodeInjection: null,
customUserAgent: null,
};
Preferences.propTypes = {
@ -584,6 +601,7 @@ Preferences.propTypes = {
attachToMenubar: PropTypes.bool.isRequired,
classes: PropTypes.object.isRequired,
cssCodeInjection: PropTypes.string,
customUserAgent: PropTypes.string,
downloadPath: PropTypes.string.isRequired,
hibernateUnusedWorkspacesAtLaunch: PropTypes.bool.isRequired,
isDefaultMailClient: PropTypes.bool.isRequired,
@ -611,6 +629,7 @@ const mapStateToProps = (state) => ({
askForDownloadPath: state.preferences.askForDownloadPath,
attachToMenubar: state.preferences.attachToMenubar,
cssCodeInjection: state.preferences.cssCodeInjection,
customUserAgent: state.preferences.customUserAgent,
downloadPath: state.preferences.downloadPath,
hibernateUnusedWorkspacesAtLaunch: state.preferences.hibernateUnusedWorkspacesAtLaunch,
isDefaultMailClient: state.general.isDefaultMailClient,

View file

@ -32,6 +32,9 @@ export const UPDATE_FIND_IN_PAGE_MATCHES = 'UPDATE_FIND_IN_PAGE_MATCHES';
// Code Injection
export const UPDATE_CODE_INJECTION_FORM = 'UPDATE_CODE_INJECTION_FORM';
// Custom User Agent
export const UPDATE_CUSTOM_USER_AGENT_FORM = 'UPDATE_CUSTOM_USER_AGENT_FORM';
// Auth
export const UPDATE_AUTH_FORM = 'UPDATE_AUTH_FORM';

View file

@ -16,6 +16,7 @@ const About = React.lazy(() => import('./components/about'));
const AddWorkspace = React.lazy(() => import('./components/add-workspace'));
const Auth = React.lazy(() => import('./components/auth'));
const CodeInjection = React.lazy(() => import('./components/code-injection'));
const CustomUserAgent = React.lazy(() => import('./components/custom-user-agent'));
const DisplayMedia = React.lazy(() => import('./components/display-media'));
const EditWorkspace = React.lazy(() => import('./components/edit-workspace'));
const LicenseRegistration = React.lazy(() => import('./components/license-registration'));
@ -30,6 +31,7 @@ const App = () => {
case 'add-workspace': return <AddWorkspace />;
case 'auth': return <Auth />;
case 'code-injection': return <CodeInjection />;
case 'custom-user-agent': return <CustomUserAgent />;
case 'display-media': return <DisplayMedia />;
case 'edit-workspace': return <EditWorkspace />;
case 'license-registration': return <LicenseRegistration />;
@ -78,6 +80,8 @@ const runApp = () => {
document.title = 'Notifications';
} else if (window.mode === 'display-media') {
document.title = 'Share your Screen';
} else if (window.mode === 'custom-user-agent') {
document.title = 'Edit Custom User Agent';
} else {
document.title = 'Singlebox';
}

View file

@ -10,12 +10,13 @@ export const requestGoBack = () => ipcRenderer.send('request-go-back');
export const requestGoForward = () => ipcRenderer.send('request-go-forward');
export const requestReload = () => ipcRenderer.send('request-reload');
export const requestShowPreferencesWindow = () => ipcRenderer.send('request-show-preferences-window');
export const requestShowEditWorkspaceWindow = (id) => ipcRenderer.send('request-show-edit-workspace-window', id);
export const requestShowAddWorkspaceWindow = () => ipcRenderer.send('request-show-add-workspace-window');
export const requestShowCodeInjectionWindow = (type) => ipcRenderer.send('request-show-code-injection-window', type);
export const requestShowCustomUserAgentWindow = () => ipcRenderer.send('request-show-custom-user-agent-window');
export const requestShowEditWorkspaceWindow = (id) => ipcRenderer.send('request-show-edit-workspace-window', id);
export const requestShowLicenseRegistrationWindow = () => ipcRenderer.send('request-show-license-registration-window');
export const requestShowNotificationsWindow = () => ipcRenderer.send('request-show-notifications-window');
export const requestShowPreferencesWindow = () => ipcRenderer.send('request-show-preferences-window');
// Notifications
export const requestShowNotification = (opts) => ipcRenderer.send('request-show-notification', opts);

View file

@ -15,7 +15,7 @@ export const save = () => (dispatch, getState) => {
const codeInjectionType = window.require('electron').remote.getGlobal('codeInjectionType');
requestSetPreference(`${codeInjectionType}CodeInjection`, form.code);
remote.getCurrentWindow().close();
requestShowRequireRestartDialog();
remote.getCurrentWindow().close();
};

View file

@ -0,0 +1,25 @@
import { UPDATE_CUSTOM_USER_AGENT_FORM } from '../../constants/actions';
import {
getPreference,
requestSetPreference,
requestShowRequireRestartDialog,
} from '../../senders';
const { remote } = window.require('electron');
export const updateForm = (changes) => (dispatch) => dispatch({
type: UPDATE_CUSTOM_USER_AGENT_FORM,
changes,
});
export const save = () => (dispatch, getState) => {
const { form } = getState().customUserAgent;
if (getPreference('customUserAgent') !== form.code) {
requestSetPreference('customUserAgent', form.code);
requestShowRequireRestartDialog();
}
remote.getCurrentWindow().close();
};

View file

@ -0,0 +1,18 @@
import { combineReducers } from 'redux';
import { UPDATE_CUSTOM_USER_AGENT_FORM } from '../../constants/actions';
import { getPreference } from '../../senders';
const defaultForm = {
code: getPreference('customUserAgent'),
};
const form = (state = defaultForm, action) => {
switch (action.type) {
case UPDATE_CUSTOM_USER_AGENT_FORM: return { ...state, ...action.changes };
default: return state;
}
};
export default combineReducers({ form });

View file

@ -8,6 +8,7 @@ import thunkMiddleware from 'redux-thunk';
import addWorkspace from './add-workspace/reducers';
import auth from './auth/reducers';
import codeInjection from './code-injection/reducers';
import customUserAgent from './custom-user-agent/reducers';
import editWorkspace from './edit-workspace/reducers';
import findInPage from './find-in-page/reducers';
import general from './general/reducers';
@ -23,6 +24,7 @@ const rootReducer = combineReducers({
addWorkspace,
auth,
codeInjection,
customUserAgent,
editWorkspace,
findInPage,
general,