first implementation
This commit is contained in:
committed by
Manuel Bouza
parent
aff72595c7
commit
ade0055441
17
.babelrc
17
.babelrc
@@ -1,3 +1,18 @@
|
|||||||
{
|
{
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||||
|
"plugins": [
|
||||||
|
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||||
|
["@babel/plugin-proposal-class-properties", { "loose": true }],
|
||||||
|
[
|
||||||
|
"module-resolver",
|
||||||
|
{
|
||||||
|
"root": [
|
||||||
|
"./src/js"
|
||||||
|
],
|
||||||
|
"alias": {
|
||||||
|
"images": "./src/images"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,13 +108,10 @@ chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
|
|||||||
file: "/styles.css"
|
file: "/styles.css"
|
||||||
}, function () {
|
}, function () {
|
||||||
chrome.tabs.executeScript(tabId, {
|
chrome.tabs.executeScript(tabId, {
|
||||||
code: "const div = document.createElement('div'); div.setAttribute('id', 'moco'); document.body.appendChild(div)"
|
file: "/bubble.js"
|
||||||
}, function () {
|
}, function () {
|
||||||
chrome.tabs.executeScript(tabId, {
|
// chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
|
||||||
file: "/popup.js"
|
console.log("injected bubble.js"); // })
|
||||||
}, function () {
|
|
||||||
console.log("inejected /popup.js");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -148,7 +145,7 @@ function () {
|
|||||||
_createClass(DomainCheck, [{
|
_createClass(DomainCheck, [{
|
||||||
key: "hasMatch",
|
key: "hasMatch",
|
||||||
get: function get() {
|
get: function get() {
|
||||||
return this.url.match(/github/);
|
return this.url.match(/github/) || this.url.match(/trello/) || this.url.match(/mocoapp/);
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
</head>
|
</head>
|
||||||
<body class='moco-options'>
|
<body>
|
||||||
<div id="moco"></div>
|
<div id="moco-bx-container"></div>
|
||||||
A dedicated page for customising settings of your extension. This page should persist any settings to the store, to be fetched again by other parts of your plugin.
|
|
||||||
<script src="options.js"></script>
|
<script src="options.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
26987
options.js
26987
options.js
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -9,10 +9,21 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.1.2",
|
"@babel/core": "^7.1.2",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.1.0",
|
||||||
|
"@babel/plugin-proposal-decorators": "^7.1.2",
|
||||||
"@babel/preset-env": "^7.1.0",
|
"@babel/preset-env": "^7.1.0",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"axios": "^0.18.0",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-loader": "^8.0.4",
|
"babel-loader": "^8.0.4",
|
||||||
|
"babel-plugin-module-resolver": "^3.1.1",
|
||||||
"copyfiles": "^2.1.0",
|
"copyfiles": "^2.1.0",
|
||||||
|
"eslint": "^5.7.0",
|
||||||
|
"eslint-plugin-jest": "^21.25.0",
|
||||||
|
"eslint-plugin-react": "^7.11.1",
|
||||||
|
"mobx": "^5.5.0",
|
||||||
|
"mobx-react": "^5.2.8",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.5.2",
|
"react": "^16.5.2",
|
||||||
"react-dom": "^16.5.2",
|
"react-dom": "^16.5.2",
|
||||||
"webpack": "^4.15.0",
|
"webpack": "^4.15.0",
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
</head>
|
</head>
|
||||||
<body class='moco-options'>
|
<body>
|
||||||
<div id="moco"></div>
|
<div id="moco-bx-container"></div>
|
||||||
The little app you see when clicking/activating an extension. Can be build with any framework like React or Vue or just vanilla JS. We used Vue.
|
|
||||||
<script src="popup.js"></script>
|
<script src="popup.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
66
src/js/api/Client.js
Normal file
66
src/js/api/Client.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
constructor() {
|
||||||
|
// this.warningPresent = false
|
||||||
|
this.client = axios.create({responseType: 'json', headers: {'x-client-version': this.clientVersion()}})
|
||||||
|
// this.client.interceptors.response.use(this.forceUpdateInterceptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// csrfToken() {
|
||||||
|
// return document.getElementsByName('csrf-token')[0].getAttribute('content')
|
||||||
|
// }
|
||||||
|
|
||||||
|
clientVersion() {
|
||||||
|
return "1.0.0"
|
||||||
|
// FIXME: on server sync with real version numbers return chrome.runtime.getManifest().version
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientVersionMajor() {
|
||||||
|
// const [major] = this.clientVersion().split('.')
|
||||||
|
// return parseInt(major)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// forceUpdateInterceptor = (response) => {
|
||||||
|
// const [serverMajor] = response.headers['x-moco-version'].split('.')
|
||||||
|
|
||||||
|
// // if (parseInt(serverMajor) > this.clientVersionMajor() && !this.warningPresent) {
|
||||||
|
// // this.warningPresent = true
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// if (response.headers['x-moco-version'] != this.clientVersion())
|
||||||
|
// window.updateRequired = true
|
||||||
|
|
||||||
|
// return response
|
||||||
|
// }
|
||||||
|
|
||||||
|
get(url, config = {}) {
|
||||||
|
return this.client.get(url, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
post(url, data) {
|
||||||
|
return this.client.post(url, data, {
|
||||||
|
// headers: {'x-csrf-token': this.csrfToken()}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
put(url, data) {
|
||||||
|
return this.client.put(url, data, {
|
||||||
|
// headers: { 'x-csrf-token': this.csrfToken() }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(url, data) {
|
||||||
|
return this.client.patch(url, data, {
|
||||||
|
// headers: { 'x-csrf-token': this.csrfToken() }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(url) {
|
||||||
|
return this.client.delete(url, {
|
||||||
|
// headers: { 'x-csrf-token': this.csrfToken() }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Client()
|
||||||
5
src/js/api/projects.js
Normal file
5
src/js/api/projects.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import client from './Client'
|
||||||
|
|
||||||
|
export function getProjects(subdomain, apiKey) {
|
||||||
|
return client.post(`https://${subdomain}.mocoapp.com/api/browser_extensions/session`, { api_key: apiKey })
|
||||||
|
}
|
||||||
5
src/js/api/session.js
Normal file
5
src/js/api/session.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import client from './Client'
|
||||||
|
|
||||||
|
export function login(subdomain, apiKey) {
|
||||||
|
return client.post(`https://${subdomain}.mocoapp.com/api/browser_extensions/session`, { api_key: apiKey })
|
||||||
|
}
|
||||||
@@ -10,12 +10,10 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|||||||
|
|
||||||
// inject css + js
|
// inject css + js
|
||||||
chrome.tabs.insertCSS(tabId, {file: "/styles.css"}, () => {
|
chrome.tabs.insertCSS(tabId, {file: "/styles.css"}, () => {
|
||||||
chrome.tabs.executeScript(tabId, {
|
chrome.tabs.executeScript(tabId, {file: "/bubble.js"}, () => {
|
||||||
code: "const div = document.createElement('div'); div.setAttribute('id', 'moco'); document.body.appendChild(div)"
|
// chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
|
||||||
}, () => {
|
console.log("injected bubble.js")
|
||||||
chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
|
// })
|
||||||
console.log("inejected /popup.js")
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
16
src/js/bubble.js
Normal file
16
src/js/bubble.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { createElement } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import Bubble from './components/Bubble'
|
||||||
|
|
||||||
|
// don't initiate bubble twice
|
||||||
|
if (!document.querySelector('#moco-bx-container')) {
|
||||||
|
const domContainer = document.createElement('div')
|
||||||
|
domContainer.setAttribute('id', 'moco-bx-container')
|
||||||
|
document.body.appendChild(domContainer)
|
||||||
|
|
||||||
|
const domBubble = document.createElement('div')
|
||||||
|
domBubble.setAttribute('id', 'moco-bx-bubble')
|
||||||
|
document.body.appendChild(domBubble)
|
||||||
|
|
||||||
|
ReactDOM.render(createElement(Bubble), domBubble)
|
||||||
|
}
|
||||||
43
src/js/components/Bubble.js
Normal file
43
src/js/components/Bubble.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import React, { Component, Fragment } from 'react'
|
||||||
|
import Modal, { Content } from 'components/Modal'
|
||||||
|
import Form from 'components/Form'
|
||||||
|
import { observable } from 'mobx'
|
||||||
|
import { observer } from 'mobx-react'
|
||||||
|
|
||||||
|
@observer
|
||||||
|
class Bubble extends Component {
|
||||||
|
@observable open = false
|
||||||
|
|
||||||
|
onOpen = (_e) => {
|
||||||
|
this.open = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose = (_e) => {
|
||||||
|
this.open = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RENDER -------------------------------------------------------------------
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<img
|
||||||
|
onClick={this.onOpen}
|
||||||
|
src={chrome.extension.getURL('src/images/logo.png')}
|
||||||
|
width="50%"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{this.open &&
|
||||||
|
<Modal>
|
||||||
|
<Content>
|
||||||
|
<Form />
|
||||||
|
<button onClick={this.onClose}>Close</button>
|
||||||
|
</Content>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Bubble
|
||||||
40
src/js/components/Form.js
Normal file
40
src/js/components/Form.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { login } from 'api/session'
|
||||||
|
import { observable } from 'mobx'
|
||||||
|
import { observer } from 'mobx-react'
|
||||||
|
|
||||||
|
@observer
|
||||||
|
class Form extends Component {
|
||||||
|
@observable loading = true
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
inline: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
chrome.storage.sync.get(null, (store) => {
|
||||||
|
login(store.subdomain, store.api_key)
|
||||||
|
.then((response) => this.loading = false)
|
||||||
|
.catch((error) => console.log(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RENDER -------------------------------------------------------------------
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.loading) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is the Form {this.props.inline ? <span>INLINE</span> : <span>DEDICATED</span>}.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form
|
||||||
@@ -1,11 +1,39 @@
|
|||||||
import React from 'react'
|
import React, { Component } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
function Modal() {
|
class Modal extends Component {
|
||||||
const onClick = (e) => {
|
static propTypes = {
|
||||||
alert("clicked")
|
children: PropTypes.node.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div onClick={onClick}>REACT</div>
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.el = document.createElement('div')
|
||||||
|
this.el.setAttribute('class', 'moco-bx-modal')
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const modalRoot = document.getElementById('moco-bx-container')
|
||||||
|
modalRoot.appendChild(this.el)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const modalRoot = document.getElementById('moco-bx-container')
|
||||||
|
modalRoot.removeChild(this.el)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RENDER -------------------------------------------------------------------
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return createPortal(this.props.children, this.el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Content({children}) {
|
||||||
|
return (
|
||||||
|
<div className="moco-bx-modal-content">{children}</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Modal
|
export default Modal
|
||||||
|
|||||||
57
src/js/components/Setup.js
Normal file
57
src/js/components/Setup.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { observable } from 'mobx'
|
||||||
|
import { observer } from 'mobx-react'
|
||||||
|
|
||||||
|
@observer
|
||||||
|
class Setup extends Component {
|
||||||
|
@observable loading = true
|
||||||
|
@observable subdomain = ""
|
||||||
|
@observable apiKey = ""
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
chrome.storage.sync.get(null, (store) => {
|
||||||
|
this.loading = false
|
||||||
|
this.subdomain = store.subdomain || ""
|
||||||
|
this.apiKey = store.api_key || ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVENTS
|
||||||
|
|
||||||
|
onChange = (e) => {
|
||||||
|
this[e.target.name] = e.target.value
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit = (_e) => {
|
||||||
|
chrome.storage.sync.set({
|
||||||
|
subdomain: this.subdomain.trim(),
|
||||||
|
api_key: this.apiKey.trim(),
|
||||||
|
}, () => window.close())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RENDER -------------------------------------------------------------------
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.loading) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={this.onSubmit}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="subdomain"
|
||||||
|
value={this.subdomain}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="apiKey"
|
||||||
|
value={this.apiKey}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
<button>Save</button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Setup
|
||||||
0
src/js/containers/App.js
Normal file
0
src/js/containers/App.js
Normal file
@@ -1 +1,6 @@
|
|||||||
console.log("okish from options.js")
|
import { createElement } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import Setup from './components/Setup'
|
||||||
|
|
||||||
|
const domContainer = document.querySelector('#moco-bx-container')
|
||||||
|
ReactDOM.render(createElement(Setup), domContainer)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
console.log("okish from popup.js")
|
import { createElement } from 'react'
|
||||||
|
|
||||||
import React, { createElement } from 'react'
|
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import Modal from './components/Modal'
|
import Form from './components/Form'
|
||||||
|
|
||||||
const domContainer = document.querySelector('#moco')
|
const domContainer = document.querySelector('#moco-bx-container')
|
||||||
ReactDOM.render(createElement(Modal), domContainer)
|
ReactDOM.render(createElement(Form, {inline: false}), domContainer)
|
||||||
|
|||||||
Reference in New Issue
Block a user