/* globals React, ReactDOM, ReactDOMServer, templateMetadata */
/* eslint jsx-a11y/click-events-have-key-events: 0 */
/* eslint jsx-a11y/no-noninteractive-element-interactions: 0 */
let activeTabLi;
let activeTabSelected = false;
let selected = new Map();
let mailProvider;
let showAdditionalProviders;
let isSelectingMailProvider = false;
const LOGIN_ERROR_TIME = 90 * 1000; // 90 seconds
/* True if this is a tab we can "send". Doesn't include about:preferences, etc. */
function isSelectableTabUrl(url) {
return url.startsWith("http");
}
class Tab extends React.Component {
render() {
let tab = this.props.tab;
let checkId = `checkbox-${this.props.tab.id}`;
let checked = this.props.selected.get(tab.id);
let liClass;
if (tab.active) {
liClass = "active";
}
let image = ;
return
{
if (this.props.tab.active) {
activeTabLi = li;
}
}}>
{ isSelectableTabUrl(tab.url) ? this.checkbox = checkbox} /> : }
{ image }
{tab.title}
;
}
async onFeedback() {
browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "button-click",
el: "feedback",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd2: getSelectedCount(),
cd3: activeTabSelected,
cd7: mailProvider,
});
}
onChange() {
selected.set(this.props.tab.id, this.checkbox.checked);
if (this.props.onChange) {
this.props.onChange();
}
render();
}
}
function getSelectedCount() {
return Array.from(selected.values()).filter(x => x).length;
}
class TabList extends React.Component {
render() {
let tabElements = this.props.tabs.map(
tab =>
);
return ;
}
onChangeSelection() {
selectionCache.saveSelectedTabs(this.props.tabs);
}
}
class Popup extends React.Component {
constructor(props) {
super(props);
this.state = {
showLoginError: this.props.showLoginError,
};
}
dismissError() {
this.setState({showLoginError: false});
}
render() {
let anyChecked = false;
let allChecked = true;
this.indeterminate = false;
const linkableTabs = this.props.tabs.filter((t) => isSelectableTabUrl(t.url));
for (let tab of linkableTabs) {
if (!this.props.selected.get(tab.id)) {
allChecked = false;
} else {
anyChecked = true;
this.indeterminate = true;
}
}
if (allChecked) {
this.indeterminate = false;
}
let emailTabsTitle = null;
if (this.props.incognito) {
emailTabsTitle = "Emailing tabs from a Private Browsing window is not supported";
}
return
{ this.state.showLoginError ?
: null }
What do you think of Email Tabs? Let us know.
Copy Tabs to Clipboard
Email Tabs
;
}
componentDidMount() {
this.allCheckbox.indeterminate = this.indeterminate;
}
componentDidUpdate() {
this.componentDidMount();
}
async onClickCheckAll() {
let allChecked = true;
let selectableTabs = this.props.tabs.filter(tab => isSelectableTabUrl(tab.url));
for (let tab of selectableTabs) {
allChecked = allChecked && this.props.selected.get(tab.id);
}
for (let tab of selectableTabs) {
selected.set(tab.id, !allChecked);
}
selectionCache.clear();
render();
await browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "select-all",
el: "browser-action",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd7: mailProvider,
});
}
async sendEmail() {
let sendTabs = this.props.tabs.filter(tab => this.props.selected.get(tab.id));
if (!sendTabs.length) {
console.info("Tried to send tabs with nothing selected");
return;
}
localStorage.removeItem("loginInterrupt");
sendTabs = sendTabs.map(tab => tab.id);
await browser.runtime.sendMessage({
type: "sendEmail",
mailProvider,
tabIds: sendTabs,
customDimensions: {
cd1: await browser.tabs.query({currentWindow: true}).length,
cd2: getSelectedCount(),
cd3: activeTabSelected,
cd6: this.allCheckbox.checked,
cd7: mailProvider,
},
});
browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "button-click",
el: "email-tabs",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd2: getSelectedCount(),
cd3: activeTabSelected,
cd6: this.allCheckbox.checked,
cd7: mailProvider,
});
window.close();
}
async copyTabs() {
let sendTabs = this.props.tabs.filter(tab => this.props.selected.get(tab.id));
if (!sendTabs.length) {
console.info("Tried to copy tabs with nothing selected");
return;
}
sendTabs = sendTabs.map(tab => tab.id);
await browser.runtime.sendMessage({
type: "copyTabHtml",
tabIds: sendTabs,
});
setTimeout(() => {
window.close();
}, 300);
browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "button-click",
el: "copy-tabs-to-clipboard",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd2: getSelectedCount(),
cd3: activeTabSelected,
cd7: mailProvider,
});
}
async onSelectProvider() {
isSelectingMailProvider = true;
render();
}
}
class LoginError extends React.Component {
render() {
return
Don't forget to sign in to your email account to use Email Tabs.
;
}
}
class MailPreference extends React.Component {
render() {
let footer = (
);
const providerCb = showAdditionalProviders ? this.onSelect : () => {};
return
Select Default Mail Provider
Sorry we don't support any other mail providers. Learn more
{this.props.mailProvider ? footer : null}
;
}
async onSelect(provider) {
let previousMailProvider = mailProvider;
mailProvider = provider;
browser.storage.local.set({mailProvider});
isSelectingMailProvider = false;
await browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "provider-select",
el: provider,
cd1: await browser.tabs.query({currentWindow: true}).length,
cd7: previousMailProvider,
});
render();
}
async onLearnMore() {
await browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "provider-learn-more",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd7: mailProvider,
});
}
async onCancel() {
isSelectingMailProvider = false;
await browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "provider-cancel",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd7: mailProvider,
});
render();
}
}
async function render(firstRun) {
let tabs = await browser.tabs.query({currentWindow: true});
let incognito = tabs.some(tab => tab.incognito);
if (firstRun) {
if (!selectionCache.loadSelectedTabs(tabs)) {
for (let tab of tabs) {
if (tab.active && isSelectableTabUrl(tab.url)) {
activeTabSelected = true;
selected.set(tab.id, true);
}
}
}
}
let showLoginError = parseInt(localStorage.getItem("loginInterrupt") || "0", 10);
if (Date.now() - showLoginError > LOGIN_ERROR_TIME) {
showLoginError = false;
}
let page;
if (mailProvider && !isSelectingMailProvider) {
page = ;
} else {
await browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "provider-preference",
el: isSelectingMailProvider ? "settings" : "first-time",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd7: mailProvider,
});
page = ;
}
ReactDOM.render(page, document.getElementById("panel"));
if (firstRun) {
activeTabLi.scrollIntoView({
behavior: "instant",
block: "center",
});
}
}
const selectionCache = {
timeout: 30 * 60 * 1000, // 30 minutes
key: "selectionCache",
load() {
let value = localStorage.getItem(this.key);
if (!value) {
return null;
}
value = JSON.parse(value);
if (Date.now() - value.time > this.timeout) {
localStorage.removeItem(this.key);
return null;
}
return value.cache;
},
loadSelectedTabs(tabs) {
let value = this.load();
if (!value) {
return false;
}
let anyFound = false;
for (let tab of tabs) {
if (value[tab.id] && value[tab.id].url === tab.url) {
anyFound = true;
selected.set(tab.id, true);
}
}
return anyFound;
},
save(value) {
localStorage.setItem(this.key, JSON.stringify({
cache: value,
time: Date.now(),
}));
},
saveSelectedTabs(tabs) {
let newValue = {};
for (let tab of tabs) {
if (selected.get(tab.id)) {
newValue[tab.id] = {url: tab.url};
}
}
this.save(newValue);
},
clear() {
localStorage.removeItem(this.key);
},
};
/** Calls render(), then calls it again soon */
function renderWithDelay() {
render();
setTimeout(render, 300);
}
for (let eventName of ["onAttached", "onCreated", "onDetached", "onMoved", "onUpdated"]) {
browser.tabs[eventName].addListener(render);
}
browser.tabs.onRemoved.addListener(renderWithDelay);
async function init() {
let result = await browser.storage.local.get("mailProvider");
mailProvider = result.mailProvider;
let providerResult = await browser.storage.local.get("showAdditionalProviders");
showAdditionalProviders = providerResult.showAdditionalProviders;
render(true);
browser.runtime.sendMessage({
type: "sendEvent",
ec: "interface",
ea: "expand-panel",
el: "browser-action",
cd1: await browser.tabs.query({currentWindow: true}).length,
cd7: mailProvider,
});
}
init();