зеркало из https://github.com/mozilla/email-tabs.git
Start #41, put in an interstitial template selector
This puts the selector into the gmail compose window itself as an overlay. For now rendering of the templates stays in the background process, but arguably it should be moved into the compose window.
This commit is contained in:
Родитель
5817aec421
Коммит
e03638d329
|
@ -1,6 +1,4 @@
|
|||
/* globals TestPilotGA, emailTemplates, templateMetadata */
|
||||
let selectedTemplate = templateMetadata.defaultTemplateName;
|
||||
|
||||
browser.runtime.onMessage.addListener((message, source) => {
|
||||
if (message.type === "sendEmail") {
|
||||
sendEmail(message.tabIds).catch((e) => {
|
||||
|
@ -25,10 +23,8 @@ browser.runtime.onMessage.addListener((message, source) => {
|
|||
delete message.type;
|
||||
sendEvent(message);
|
||||
return Promise.resolve(null);
|
||||
} else if (message.type === "setSelectedTemplate") {
|
||||
return setSelectedTemplate(message.name);
|
||||
} else if (message.type === "getSelectedTemplate") {
|
||||
return Promise.resolve(selectedTemplate);
|
||||
} else if (message.type === "renderTemplate") {
|
||||
return renderTabs(message.tabInfo, message.selectedTemplate);
|
||||
}
|
||||
console.error("Unexpected message type:", message.type);
|
||||
return null;
|
||||
|
@ -94,16 +90,14 @@ async function getTabInfo(tabIds, {wantsScreenshots, wantsReadability}) {
|
|||
return tabIds.map(id => tabInfo[id]);
|
||||
}
|
||||
|
||||
async function renderTabs(tabIds, templateName) {
|
||||
let { wantsScreenshots, wantsReadability } = templateMetadata.getTemplate(templateName);
|
||||
let tabInfo = await getTabInfo(tabIds, {wantsScreenshots, wantsReadability});
|
||||
async function renderTabs(tabInfo, templateName) {
|
||||
let TemplateComponent = emailTemplates[templateMetadata.getTemplate(templateName).componentName];
|
||||
if (!TemplateComponent) {
|
||||
throw new Error(`No component found for template: ${templateName}`);
|
||||
}
|
||||
let html = emailTemplates.renderEmail(tabInfo, TemplateComponent);
|
||||
let subject = emailTemplates.renderSubject(tabInfo);
|
||||
return { html, tabInfo, subject };
|
||||
return { html, subject };
|
||||
}
|
||||
|
||||
async function sendEmail(tabIds) {
|
||||
|
@ -126,22 +120,25 @@ async function sendEmail(tabIds) {
|
|||
loginInterrupt();
|
||||
}
|
||||
}, 1000);
|
||||
let { html, tabInfo, subject } = await renderTabs(tabIds, selectedTemplate);
|
||||
let tabInfo = await getTabInfo(tabIds, {wantsScreenshots: true, wantsReadability: true});
|
||||
await browser.tabs.executeScript(newTab.id, {
|
||||
file: "templateMetadata.js",
|
||||
runAt: "document_start",
|
||||
});
|
||||
await browser.tabs.executeScript(newTab.id, {
|
||||
file: "set-html-email.js",
|
||||
runAt: "document_start",
|
||||
});
|
||||
await browser.tabs.sendMessage(newTab.id, {
|
||||
type: "setHtml",
|
||||
html,
|
||||
subject,
|
||||
type: "sendTabInfo",
|
||||
thisTabId: newTab.id,
|
||||
tabInfo
|
||||
});
|
||||
}
|
||||
|
||||
async function copyTabHtml(tabIds) {
|
||||
let { html } = await renderTabs(tabIds, selectedTemplate);
|
||||
let tabInfo = await getTabInfo(tabIds, {wantsScreenshots: false, wantsReadability: false});
|
||||
let { html } = await renderTabs(tabInfo, "just_links");
|
||||
copyHtmlToClipboard(html);
|
||||
}
|
||||
|
||||
|
@ -195,23 +192,3 @@ async function closeManyTabs(composeTabId, otherTabInfo) {
|
|||
}
|
||||
await browser.tabs.remove(toClose);
|
||||
}
|
||||
|
||||
async function setSelectedTemplate(newName) {
|
||||
selectedTemplate = newName;
|
||||
await browser.storage.local.set({selectedTemplate: newName});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
let result = await browser.storage.local.get(["selectedTemplate"]);
|
||||
if (result && result.selectedTemplate) {
|
||||
try {
|
||||
// Checks that the template really exists:
|
||||
templateMetadata.getTemplate(result.selectedTemplate);
|
||||
selectedTemplate = result.selectedTemplate;
|
||||
} catch (e) {
|
||||
console.error("Could not set template", result.selectedTemplate, "to:", String(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
|
|
@ -73,6 +73,15 @@
|
|||
box-shadow: 0 2px 8px rgba(12, 12, 13, 0.1);
|
||||
}
|
||||
|
||||
.big-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
#loading-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -213,5 +222,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="choose-template" class="container big-container" style="display: none">
|
||||
<div class="template-template" style="display: none">
|
||||
<button>
|
||||
Use <span data-substitute="title"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="choose-template-container" class="card-container">
|
||||
<button id="choose-template-cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
let activeTabLi;
|
||||
let selected = new Map();
|
||||
let isChoosingTemplate = false;
|
||||
let selectedTemplate = templateMetadata.defaultTemplateName;
|
||||
const LOGIN_ERROR_TIME = 90 * 1000; // 90 seconds
|
||||
|
||||
class Tab extends React.Component {
|
||||
|
@ -78,7 +76,6 @@ class Popup extends React.Component {
|
|||
<input checked={allChecked} ref={allCheckbox => this.allCheckbox = allCheckbox} type="checkbox" id="allCheckbox" onChange={this.onClickCheckAll.bind(this)} />
|
||||
Select All
|
||||
</label>
|
||||
<button onClick={this.chooseTemplate.bind(this)}>Change template ({selectedTemplate})</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="separator"></div>
|
||||
|
@ -143,10 +140,6 @@ class Popup extends React.Component {
|
|||
}, 300);
|
||||
}
|
||||
|
||||
chooseTemplate() {
|
||||
isChoosingTemplate = true;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
class LoginError extends React.Component {
|
||||
|
@ -158,47 +151,6 @@ class LoginError extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
class TemplateChooser extends React.Component {
|
||||
render() {
|
||||
let templates = templateMetadata.metadata.map(template => {
|
||||
return <TemplateItem key={template.name} selected={template.name === selectedTemplate} {...template} />;
|
||||
});
|
||||
return <div>
|
||||
<ul>
|
||||
{ templates }
|
||||
</ul>
|
||||
<footer className="panel-footer toggle-enabled">
|
||||
<button onClick={this.onCancel.bind(this)}>
|
||||
Cancel
|
||||
</button>
|
||||
</footer>
|
||||
</div>;
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
isChoosingTemplate = false;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateItem extends React.Component {
|
||||
render() {
|
||||
let className = "";
|
||||
if (this.props.selected) {
|
||||
className += " selected";
|
||||
}
|
||||
return <li className={className}>
|
||||
<button onClick={this.selectTemplate.bind(this)}>{this.props.title}</button>
|
||||
</li>;
|
||||
}
|
||||
|
||||
selectTemplate() {
|
||||
setSelectedTemplate(this.props.name);
|
||||
isChoosingTemplate = false;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
async function render(firstRun) {
|
||||
let tabs = await browser.tabs.query({currentWindow: true});
|
||||
if (firstRun) {
|
||||
|
@ -214,12 +166,7 @@ async function render(firstRun) {
|
|||
if (Date.now() - showLoginError > LOGIN_ERROR_TIME) {
|
||||
showLoginError = 0;
|
||||
}
|
||||
let page;
|
||||
if (isChoosingTemplate) {
|
||||
page = <TemplateChooser />;
|
||||
} else {
|
||||
page = <Popup selected={selected} tabs={tabs} showLoginError={showLoginError} />;
|
||||
}
|
||||
let page = <Popup selected={selected} tabs={tabs} showLoginError={showLoginError} />;
|
||||
ReactDOM.render(page, document.getElementById("panel"));
|
||||
if (firstRun) {
|
||||
activeTabLi.scrollIntoView({
|
||||
|
@ -296,13 +243,7 @@ for (let eventName of ["onAttached", "onCreated", "onDetached", "onMoved", "onUp
|
|||
|
||||
browser.tabs.onRemoved.addListener(renderWithDelay);
|
||||
|
||||
async function setSelectedTemplate(name) {
|
||||
selectedTemplate = name;
|
||||
await browser.runtime.sendMessage({type: "setSelectedTemplate", name});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
selectedTemplate = await browser.runtime.sendMessage({type: "getSelectedTemplate"});
|
||||
render(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
/* globals cloneInto */
|
||||
/* globals cloneInto, templateMetadata */
|
||||
|
||||
browser.runtime.onMessage.addListener((message) => {
|
||||
try {
|
||||
thisTabId = message.thisTabId;
|
||||
closeTabInfo = message.tabInfo;
|
||||
setSubject(message.subject);
|
||||
setHtml(message.html);
|
||||
tabInfo = message.tabInfo;
|
||||
} catch (e) {
|
||||
console.error("Unable to setHtml:", String(e), e.stack);
|
||||
console.error("Error getting tabInfo:", String(e), e.stack);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
let completed = false;
|
||||
let thisTabId;
|
||||
let closeTabInfo;
|
||||
let tabInfo;
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
if (completed) {
|
||||
|
@ -28,7 +26,6 @@ window.addEventListener("beforeunload", () => {
|
|||
browser.runtime.sendMessage({
|
||||
type: "sendFailed"
|
||||
});
|
||||
console.error("beforeunload");
|
||||
});
|
||||
|
||||
function setSubject(subject) {
|
||||
|
@ -116,7 +113,7 @@ function showCloseButtons() {
|
|||
let done = iframeDocument.querySelector("#done");
|
||||
let doneMsg = iframeDocument.querySelector("#done-message");
|
||||
let closeAllTabs = iframeDocument.querySelector("#close-all-tabs");
|
||||
let numTabs = closeTabInfo.length;
|
||||
let numTabs = tabInfo.length;
|
||||
if (numTabs === 1) {
|
||||
closeAllTabs.textContent = closeAllTabs.getAttribute("data-one-tab");
|
||||
doneMsg.textContent = doneMsg.getAttribute("data-one-tab");
|
||||
|
@ -133,7 +130,7 @@ function showCloseButtons() {
|
|||
closeAllTabs.addEventListener("click", async () => {
|
||||
await browser.runtime.sendMessage({
|
||||
type: "closeTabs",
|
||||
closeTabInfo,
|
||||
closeTabInfo: tabInfo,
|
||||
composeTabId: thisTabId
|
||||
});
|
||||
});
|
||||
|
@ -143,6 +140,39 @@ function showLoading() {
|
|||
showIframe("#loading-container");
|
||||
}
|
||||
|
||||
function showTemplateSelector() {
|
||||
showIframe("#choose-template");
|
||||
let cancel = iframeDocument.querySelector("#choose-template-cancel");
|
||||
cancel.addEventListener("click", async () => {
|
||||
completed = true;
|
||||
await browser.runtime.sendMessage({
|
||||
type: "closeComposeTab",
|
||||
tabId: thisTabId,
|
||||
});
|
||||
});
|
||||
let elTemplate = iframeDocument.querySelector(".template-template");
|
||||
for (let template of templateMetadata.metadata) {
|
||||
let instance = elTemplate.cloneNode(true);
|
||||
instance.style.display = "";
|
||||
instance.classList.remove("template-template");
|
||||
for (let el of instance.querySelectorAll("*[data-substitute]")) {
|
||||
el.textContent = template[el.getAttribute("data-substitute")];
|
||||
el.removeAttribute("data-substitute");
|
||||
}
|
||||
instance.addEventListener("click", async () => {
|
||||
showLoading();
|
||||
let { html, subject } = await browser.runtime.sendMessage({
|
||||
type: "renderTemplate",
|
||||
selectedTemplate: template.name,
|
||||
tabInfo,
|
||||
});
|
||||
setSubject(subject);
|
||||
setHtml(html);
|
||||
});
|
||||
cancel.parentNode.insertBefore(instance, cancel);
|
||||
}
|
||||
}
|
||||
|
||||
let iframe = null;
|
||||
let initPromise;
|
||||
let iframeDocument = null;
|
||||
|
@ -182,7 +212,7 @@ function createIframe() {
|
|||
}
|
||||
|
||||
function showIframe(container) {
|
||||
let containers = ["#loading-container", "#done-container"];
|
||||
let containers = ["#loading-container", "#done-container", "#choose-template"];
|
||||
if (!containers.includes(container)) {
|
||||
throw new Error(`Unexpected container: ${container}`);
|
||||
}
|
||||
|
@ -203,5 +233,5 @@ function hideIframe() {
|
|||
createIframe();
|
||||
|
||||
initPromise.then(() => {
|
||||
showLoading();
|
||||
showTemplateSelector();
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче