first implementation
This commit is contained in:
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"
|
||||
}, function () {
|
||||
chrome.tabs.executeScript(tabId, {
|
||||
code: "const div = document.createElement('div'); div.setAttribute('id', 'moco'); document.body.appendChild(div)"
|
||||
file: "/bubble.js"
|
||||
}, function () {
|
||||
chrome.tabs.executeScript(tabId, {
|
||||
file: "/popup.js"
|
||||
}, function () {
|
||||
console.log("inejected /popup.js");
|
||||
});
|
||||
// chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
|
||||
console.log("injected bubble.js"); // })
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -148,7 +145,7 @@ function () {
|
||||
_createClass(DomainCheck, [{
|
||||
key: "hasMatch",
|
||||
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>
|
||||
<meta charset='utf-8'>
|
||||
</head>
|
||||
<body class='moco-options'>
|
||||
<div id="moco"></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.
|
||||
<body>
|
||||
<div id="moco-bx-container"></div>
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
</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": {
|
||||
"@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-react": "^7.0.0",
|
||||
"axios": "^0.18.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^8.0.4",
|
||||
"babel-plugin-module-resolver": "^3.1.1",
|
||||
"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-dom": "^16.5.2",
|
||||
"webpack": "^4.15.0",
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
</head>
|
||||
<body class='moco-options'>
|
||||
<div id="moco"></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.
|
||||
<body>
|
||||
<div id="moco-bx-container"></div>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</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
|
||||
chrome.tabs.insertCSS(tabId, {file: "/styles.css"}, () => {
|
||||
chrome.tabs.executeScript(tabId, {
|
||||
code: "const div = document.createElement('div'); div.setAttribute('id', 'moco'); document.body.appendChild(div)"
|
||||
}, () => {
|
||||
chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
|
||||
console.log("inejected /popup.js")
|
||||
})
|
||||
chrome.tabs.executeScript(tabId, {file: "/bubble.js"}, () => {
|
||||
// chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
|
||||
console.log("injected bubble.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() {
|
||||
const onClick = (e) => {
|
||||
alert("clicked")
|
||||
class Modal extends Component {
|
||||
static propTypes = {
|
||||
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
|
||||
|
||||
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 React, { createElement } from 'react'
|
||||
import { createElement } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Modal from './components/Modal'
|
||||
import Form from './components/Form'
|
||||
|
||||
const domContainer = document.querySelector('#moco')
|
||||
ReactDOM.render(createElement(Modal), domContainer)
|
||||
const domContainer = document.querySelector('#moco-bx-container')
|
||||
ReactDOM.render(createElement(Form, {inline: false}), domContainer)
|
||||
|
||||
Reference in New Issue
Block a user