first implementation

This commit is contained in:
Tobias Miesel
2018-10-17 15:21:32 +02:00
parent 0f8209ca22
commit 7c2b4fae5b
20 changed files with 65105 additions and 52 deletions

View File

@@ -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"
}
}
]
]
}

View File

@@ -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/);
}
}]);

29670
bubble.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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>

8154
popup.js

File diff suppressed because it is too large Load Diff

66
src/js/api/Client.js Normal file
View 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
View 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
View 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 })
}

View File

@@ -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
View 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)
}

View 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
View 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

View File

@@ -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

View 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
View File

View 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)

View File

@@ -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)