Mount Bubble only for configured services

This commit is contained in:
Manuel Bouza
2019-02-05 18:19:11 +01:00
parent ed711c954e
commit 73e0c47833
16 changed files with 1331 additions and 116 deletions

View File

@@ -1,16 +1,24 @@
import DomainCheck from "./services/DomainCheck";
import DomainCheck from "./services/DomainCheck"
import config from "./config"
const domainCheck = new DomainCheck(config)
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// inject files only after the page is fully loaded
if (changeInfo.status != "complete") return;
if (changeInfo.status != "complete") {
return
}
// inject files only for supported websites
const domainCheck = new DomainCheck(tab.url);
if (!domainCheck.hasMatch) return;
// inject files only for supported services
const service = domainCheck.match(tab.url)
chrome.tabs.executeScript(tabId, { file: "/bubble.js" }, () => {
// chrome.tabs.executeScript(tabId, {file: "/popup.js"}, () => {
console.log("injected bubble.js");
// })
});
});
if (service) {
chrome.tabs.sendMessage(tabId, { type: "mountBubble", service }, () => {
console.log("bubble mounted")
})
} else {
chrome.tabs.sendMessage(tabId, { type: "unmountBubble" }, () => {
console.log("bubble unmounted")
})
}
})

View File

@@ -1,17 +0,0 @@
import { createElement } from "react";
import ReactDOM from "react-dom";
import Bubble from "./components/Bubble";
import "../css/main.scss";
// 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

@@ -1,21 +1,25 @@
import React, { Component } from "react";
import Modal, { Content } from "components/Modal";
import Form from "components/Form";
import { observable } from "mobx";
import { observer } from "mobx-react";
import logoUrl from "../../images/logo.png";
import React, { Component } from "react"
import Modal, { Content } from "components/Modal"
import Form from "components/Form"
import { observable } from "mobx"
import { observer } from "mobx-react"
import logoUrl from "../../images/logo.png"
@observer
class Bubble extends Component {
@observable open = false;
@observable open = false
onOpen = _e => {
this.open = true;
};
onOpen = _event => {
this.open = true
}
onClose = _e => {
this.open = false;
};
onClose = _event => {
this.open = false
}
componentDidMount() {
console.log(this.props.service)
}
// RENDER -------------------------------------------------------------------
@@ -37,8 +41,8 @@ class Bubble extends Component {
</Modal>
)}
</>
);
)
}
}
export default Bubble;
export default Bubble

View File

@@ -1,25 +1,25 @@
import React, { Component } from 'react'
import { createPortal } from 'react-dom'
import PropTypes from 'prop-types'
import React, { Component } from "react"
import { createPortal } from "react-dom"
import PropTypes from "prop-types"
class Modal extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
children: PropTypes.node.isRequired
}
constructor(props) {
super(props)
this.el = document.createElement('div')
this.el.setAttribute('class', 'moco-bx-modal')
this.el = document.createElement("div")
this.el.setAttribute("class", "moco-bx-modal")
}
componentDidMount() {
const modalRoot = document.getElementById('moco-bx-container')
const modalRoot = document.getElementById("moco-bx-container")
modalRoot.appendChild(this.el)
}
componentWillUnmount() {
const modalRoot = document.getElementById('moco-bx-container')
const modalRoot = document.getElementById("moco-bx-container")
modalRoot.removeChild(this.el)
}
@@ -30,10 +30,12 @@ class Modal extends Component {
}
}
export function Content({children}) {
return (
<div className="moco-bx-modal-content">{children}</div>
)
export function Content({ children }) {
return <div className="moco-bx-modal-content">{children}</div>
}
Content.propTypes = {
children: PropTypes.node
}
export default Modal

View File

@@ -1,26 +1,26 @@
import React, { Component } from "react";
import { observable } from "mobx";
import { observer } from "mobx-react";
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 = "";
@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 || "";
});
this.loading = false
this.subdomain = store.subdomain || ""
this.apiKey = store.api_key || ""
})
}
// EVENTS
onChange = event => {
this[event.target.name] = event.target.value;
};
this[event.target.name] = event.target.value
}
onSubmit = _event => {
chrome.storage.sync.set(
@@ -29,13 +29,13 @@ class Setup extends Component {
api_key: this.apiKey.trim()
},
() => window.close()
);
};
)
}
// RENDER -------------------------------------------------------------------
render() {
if (this.loading) return null;
if (this.loading) return null
return (
<form onSubmit={this.onSubmit}>
@@ -67,8 +67,8 @@ class Setup extends Component {
</div>
<button>Save</button>
</form>
);
)
}
}
export default Setup;
export default Setup

12
src/js/config.js Normal file
View File

@@ -0,0 +1,12 @@
export default {
services: [
{
name: "github",
urlPattern: "https://github.com/:org/:repo/pull/:id(/:tab)",
description: (document, { org, repo, id }) =>
`${org}/${repo}/${id} - ${document
.querySelector(".gh-header-title")
.textContent.trim()}`
}
]
}

47
src/js/content.js Normal file
View File

@@ -0,0 +1,47 @@
import { createElement } from "react"
import ReactDOM from "react-dom"
import Bubble from "./components/Bubble"
import "../css/main.scss"
chrome.runtime.onMessage.addListener(({ type, service }) => {
switch (type) {
case "mountBubble": {
return mountBubble(service)
}
case "unmountBubble": {
return unmountBubble()
}
}
})
const mountBubble = service => {
if (document.getElementById("moco-bx-bubble")) {
return
}
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, { service }), domBubble)
}
const unmountBubble = () => {
const domBubble = document.getElementById("moco-bx-bubble")
const domContainer = document.getElementById("moco-bx-container")
if (domBubble) {
ReactDOM.unmountComponentAtNode(domBubble)
domBubble.remove()
}
if (domContainer) {
ReactDOM.unmountComponentAtNode(domContainer)
domContainer.remove()
}
}

View File

@@ -1,7 +1,7 @@
import { createElement } from "react";
import ReactDOM from "react-dom";
import Setup from "./components/Setup";
import "../css/main.scss";
import { createElement } from "react"
import ReactDOM from "react-dom"
import Setup from "./components/Setup"
import "../css/main.scss"
const domContainer = document.querySelector("#moco-bx-container");
ReactDOM.render(createElement(Setup), domContainer);
const domContainer = document.querySelector("#moco-bx-container")
ReactDOM.render(createElement(Setup), domContainer)

View File

@@ -1,11 +1,31 @@
import Route from "route-parser"
class DomainCheck {
constructor(url) {
this.url = url
#services;
constructor(config) {
this.#services = config.services.map(service => ({
...service,
route: new Route(service.urlPattern),
}))
}
get hasMatch() {
return this.url.match(/github/) || this.url.match(/trello/) || this.url.match(/mocoapp/)
#findService = url =>
this.#services.find(service => service.route.match(url));
match(url) {
const service = this.#findService(url)
if (!service) {
return false
}
return {
...service,
match: service.route.match(url),
}
}
}
export default DomainCheck