Skip to content

Commit

Permalink
Merge pull request brave#8065 from cezaraugusto/extensions/6530
Browse files Browse the repository at this point in the history
Add extensionsTab to preferences page
  • Loading branch information
bsclifton authored Apr 10, 2017
2 parents 3815b29 + caa2c39 commit 16ac2ab
Show file tree
Hide file tree
Showing 27 changed files with 954 additions and 13 deletions.
37 changes: 37 additions & 0 deletions app/browser/reducers/extensionsReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

const electron = require('electron')
const app = electron.app

const path = require('path')
const rimraf = require('../../../tools/lib/rimraf')

const extensionState = require('../../common/state/extensionState')
const ExtensionConstants = require('../../common/constants/extensionConstants')
const {makeImmutable} = require('../../common/state/immutableUtil')

const extensionsReducer = (state, action) => {
action = makeImmutable(action)
switch (action.get('actionType')) {
case ExtensionConstants.EXTENSION_UNINSTALLED:
let extensionId = action.get('extensionId').toString()
let extensionPath = path.join(app.getPath('userData'), 'Extensions', extensionId)

state = extensionState.extensionUninstalled(state, action)
// Remove extension folder
rimraf(extensionPath, err => {
if (err) {
console.log('unable to remove extension', err)
}
console.log(`extension id ${extensionId} removed at: \n ${extensionPath}`)
})
break
}
return state
}

module.exports = extensionsReducer
14 changes: 14 additions & 0 deletions app/common/state/extensionState.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ const extensionState = {
return state.setIn(['extensions', extensionId], action.get('installInfo'))
},

extensionUninstalled: (state, action) => {
action = makeImmutable(action)
state = makeImmutable(state)
let extensionId = action.get('extensionId').toString()
let extension = extensionState.getExtensionById(state, extensionId)
// Since we populate uninstalled extensions with dummyContent,
// removing installInfo would just add dummy data instead of removing it
// so we add a prop called 'excluded' and use it to hide extension on UI
if (extension) {
return state.setIn(['extensions', extensionId], extension.set('excluded', true))
}
return state
},

extensionEnabled: (state, action) => {
action = makeImmutable(action)
state = makeImmutable(state)
Expand Down
1 change: 1 addition & 0 deletions app/extensions/brave/about-preferences.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<link rel="localization" href="locales/{locale}/bravery.properties">
<link rel="localization" href="locales/{locale}/error.properties">
<link rel="localization" href="locales/{locale}/errorMessages.properties">
<link rel="localization" href="locales/{locale}/extensions.properties">
</head>
<body>
<div id="appContainer"/>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions app/extensions/brave/locales/en-US/extensions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,29 @@ extensions=Extensions
extensionIdLabel=ID:
extensionPathLabel=Path:
extensionPermissionsLabel=Permissions:
notifyNewExtensionsAdded=Notify me when new extensions are added
extensionsTabFooterInfo=Looking for a favorite extension? Let us know through our
latest=Latest
notInstalled=Not Installed
integrated=Integrated

l10nBitwarden=bitwarden - Free Password Manager
l10nBitwardenDesc=bitwarden is a secure and free password manager for all of your devices.
l10nDashlane=Dashlane Secure Password Manager
l10nDashlaneDesc=Never forget another password. Dashlane password manager - the most secure password storage and fast login on all of your devices
l10nEnpass=Enpass Password Manager
l10nEnpassDesc=Enpass Password Manager extension for Brave
l10nLastpass= LastPass: Free Password Manager
l10nLastpassDesc=LastPass, an award-winning password manager, saves your passwords and gives you secure access from every computer and mobile device.
l10nOnepassword= 1Password: Password Manager and Secure Wallet
l10nOnepasswordDesc=1Password extension for Brave.
l10nPdfjs= PDF Viewer
l10nPdfjsDesc=Uses HTML5 to display PDF files directly in the browser.
l10nPocket= Save to Pocket
l10nPocketDesc=Pocket Extension for Brave - The best way to save articles, videos and more.
l10nSync= Brave Sync
l10nSyncDesc=
l10nWebtorrent= Torrent Viewer
l10nWebtorrentDesc=
l10nVimium= Vimium
l10nVimiumDesc=
23 changes: 23 additions & 0 deletions app/renderer/components/helpfulText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const ImmutableComponent = require('../../../js/components/immutableComponent')
const globalStyles = require('./styles/global')

class HelpfulText extends ImmutableComponent {
render () {
return <div className={this.props.wrapperClassName}>
<span className={globalStyles.appIcons.moreInfo}
style={{
color: globalStyles.color.mediumGray,
fontSize: '20px',
marginRight: '5px'
}} />
<span className={this.props.textClassName} data-l10n-id={this.props.l10nId} />
{this.props.children}
</div>
}
}

module.exports = HelpfulText
205 changes: 205 additions & 0 deletions app/renderer/components/preferences/extensionsTab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const React = require('react')
const ImmutableComponent = require('../../../../js/components/immutableComponent')

const {StyleSheet, css} = require('aphrodite')
const globalStyles = require('../styles/global')
const {thirdPartyPasswordManagers} = require('../../../../js/constants/passwordManagers')
const aboutActions = require('../../../../js/about/aboutActions')
const settings = require('../../../../js/constants/settings')
const config = require('../../../../js/constants/config')
const {getSetting} = require('../../../../js/settings')
const {SettingCheckbox} = require('../settings')
const {isPasswordManager, getExtensionKey, isBuiltInExtension, bravifyText} = require('../../lib/extensionsUtil')

const HelpfulText = require('../helpfulText')
const SortableTable = require('../../../../js/components/sortableTable')

class ExtensionsTab extends ImmutableComponent {
getIcon (extension) {
const icon = extension.getIn(['manifest', 'icons', '128'])
return `${extension.get('base_path')}/${icon}`
}

onRemoveExtension (extensionId) {
// Disable extension before uninstalling
// otherwise user will not be able to install it again
isPasswordManager(extensionId)
? aboutActions.changeSetting(settings.ACTIVE_PASSWORD_MANAGER, extensionId)
: aboutActions.changeSetting(getExtensionKey(extensionId), false)

aboutActions.extensionUninstalled(extensionId)
}

getCheckedExtension (extensionId) {
const activePwManager = getSetting(settings.ACTIVE_PASSWORD_MANAGER, this.props.settings)
return isPasswordManager(extensionId)
? activePwManager === getExtensionKey(extensionId)
: void (0)
}

isRemovableExtension (extension) {
// do not allow built-in extensions from being uninstalled
return extension.get('excluded') && !isBuiltInExtension(extension.get('id'))
}

getRow (extension) {
if ([config.braveExtensionId, config.syncExtensionId].includes(extension.get('id')) ||
(!extension.get('dummy') && this.isRemovableExtension(extension))) {
return []
}

return [
{ // Icon
html: <img className={css(styles.icon)} src={this.getIcon(extension)} />
},
{ // Name
html: <span data-extension-id={extension.get('id')}
data-extension-enabled={extension.get('enabled')}
data-l10n-id={bravifyText(extension.get('name'))} />
},
{ // Description
html: <span data-l10n-id={bravifyText(extension.get('description'))} />
},
{ // Version
html: <span data-l10n-id={extension.get('version') || 'latest'} />
},
{ // Enable/Disable toggle
html: <SettingCheckbox
forPassword={thirdPartyPasswordManagers.includes(extension.get('id'))}
prefKey={getExtensionKey(extension.get('id'))}
settings={this.props.settings}
checked={this.getCheckedExtension(extension.get('id'))}
onChangeSetting={this.props.onChangeSetting} />
},
{ // Exclude option
html: !extension.get('isDummy') && !isBuiltInExtension(extension.get('id'))
? <div className={globalStyles.appIcons.trash}
onClick={this.onRemoveExtension.bind(this, extension.get('id'))} />
: <span data-l10n-id={isBuiltInExtension(extension.get('id')) ? 'integrated' : 'notInstalled'} />
}
]
}

get columnClassNames () {
return [
css(styles.extensionsColumn, styles.center),
css(styles.extensionsColumn),
css(styles.extensionsColumn),
css(styles.extensionsColumn),
css(styles.extensionsColumn, styles.center),
css(styles.extensionsColumn, styles.center)
]
}

render () {
if (!this.props.extensions) {
return null
}
return <div className={css(styles.extensionsContainer)}>
<main className={css(styles.extensionsMain)}>
<header className={css(styles.extensionsOption)}>
<h1 className={css(styles.extensionsHeading)} data-l10n-id='extensions' />
</header>
<SortableTable
tableClassNames={css(styles.extensionsTable)}
headings={['icon', 'name', 'description', 'version', 'enabled', 'exclude']}
columnClassNames={this.columnClassNames}
rowClassNames={
this.props.extensions.map(entry => (entry = css(styles.extensionsRow))).toJS()
}
rows={this.props.extensions.map(entry => this.getRow(entry))} />
</main>
<footer className={css(styles.moreInfo)}>
<HelpfulText l10nId='extensionsTabFooterInfo'>&nbsp;
<span data-l10n-id='community'
className={css(styles.moreInfoLink)}
onClick={aboutActions.createTabRequested.bind(null, {
url: 'https://community.brave.com'
}, true)} />.
</HelpfulText>
</footer>
</div>
}
}

const styles = StyleSheet.create({
extensionsContainer: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%'
},

extensionsMain: {
paddingBottom: '40px'
},

icon: {
display: 'flex',
margin: 'auto',
width: '32px',
height: '32px'
},

extensionsOption: {
display: 'flex',
flex: '1',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: '15px'
},

extensionsHeading: {
color: '#444444',
cursor: 'default',
fontSize: '1.2em',
marginBottom: '0.7em',
userSelect: 'none'
},

extensionsTable: {
width: '100%'
},

extensionsRow: {
background: '#fff',
height: '56px',

':nth-child(even)': {
background: globalStyles.color.veryLightGray
},
':hover': {
background: globalStyles.color.lightGray
}
},

extensionsColumn: {
padding: '0 8px'
},

center: {
textAlign: 'center'
},

moreInfo: {
display: 'flex',
flex: '1',
alignItems: 'flex-end'
},

moreInfoLink: {
cursor: 'pointer',
color: globalStyles.color.braveOrange,
textDecoration: 'none',

':hover': {
textDecoration: 'underline'
}
}
})

module.exports = ExtensionsTab
2 changes: 1 addition & 1 deletion app/renderer/components/preferences/helpfulHints.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const styles = StyleSheet.create({
cursor: 'default',
visibility: 'hidden',

'@media (min-height: 700px)': {
'@media (min-height: 750px)': {
position: 'absolute',
bottom: '0',
visibility: 'visible'
Expand Down
11 changes: 7 additions & 4 deletions app/renderer/components/preferences/preferenceNavigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const globalStyles = require('../styles/global')
const iconGeneral = require('../../../extensions/brave/img/preferences/browser_prefs_general.svg')
const iconSearch = require('../../../extensions/brave/img/preferences/browser_prefs_search.svg')
const iconTabs = require('../../../extensions/brave/img/preferences/browser_prefs_tabs.svg')
// const iconExtensions = require('../../../extensions/brave/img/preferences/browser_prefs_extensions.svg')
const iconExtensions = require('../../../extensions/brave/img/preferences/browser_prefs_extensions.svg')
const iconPlugins = require('../../../extensions/brave/img/preferences/browser_prefs_plugins.svg')
const iconSecurity = require('../../../extensions/brave/img/preferences/browser_prefs_security.svg')
const iconShields = require('../../../extensions/brave/img/preferences/browser_prefs_shields.svg')
Expand Down Expand Up @@ -53,9 +53,11 @@ class PreferenceNavigation extends ImmutableComponent {
onClick={this.props.changeTab.bind(null, preferenceTabs.PAYMENTS)}
selected={this.props.preferenceTab === preferenceTabs.PAYMENTS}
/>
{
/* TODO @cezaraugusto add extensions panel */
}
<PreferenceNavigationButton icon={styles.extensions}
dataL10nId='extensions'
onClick={this.props.changeTab.bind(null, preferenceTabs.EXTENSIONS)}
selected={this.props.preferenceTab === preferenceTabs.EXTENSIONS}
/>
<PreferenceNavigationButton icon={styles.plugins}
dataL10nId='plugins'
onClick={this.props.changeTab.bind(null, preferenceTabs.PLUGINS)}
Expand Down Expand Up @@ -106,6 +108,7 @@ const styles = StyleSheet.create({
shields: navIcon(iconShields),
payments: navIcon(iconPayments),
sync: navIcon(iconSync),
extensions: navIcon(iconExtensions),
advanced: navIcon(iconAdvanced)
})

Expand Down
13 changes: 12 additions & 1 deletion app/renderer/components/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const getSetting = require('../../../js/settings').getSetting
const {changeSetting} = require('../lib/settingsUtil')
const SwitchControl = require('../../../js/components/switchControl')
const cx = require('../../../js/lib/classSet')
const settings = require('../../../js/constants/settings')

class SettingsList extends ImmutableComponent {
render () {
Expand Down Expand Up @@ -58,10 +59,20 @@ class SettingCheckbox extends ImmutableComponent {
}

onClick (e) {
if (this.props.forPassword) {
// You can only have one active password manager
// if user decide to disable all,
// switch back password to unmanaged (null)
e.target.value
? aboutActions.changeSetting(settings.ACTIVE_PASSWORD_MANAGER, this.props.prefKey)
: aboutActions.changeSetting(settings.ACTIVE_PASSWORD_MANAGER, void (0))
}
if (this.props.disabled) {
return
}
return this.props.onChange ? this.props.onChange(e) : changeSetting(this.props.onChangeSetting, this.props.prefKey, e)
return this.props.onChange
? this.props.onChange(e)
: changeSetting(this.props.onChangeSetting, this.props.prefKey, e)
}

render () {
Expand Down
Loading

0 comments on commit 16ac2ab

Please sign in to comment.