diff --git a/src/options/components/TogglSection.js b/src/options/components/TogglSection.js
new file mode 100644
index 0000000..92cfb21
--- /dev/null
+++ b/src/options/components/TogglSection.js
@@ -0,0 +1,105 @@
+import React, { useEffect, useState } from 'react';
+import StorageHandler from '../../storage/StorageHandler';
+
+/**
+ * @typedef {Object} TogglConfig
+ * @property {boolean} enabled
+ * @property {string} token
+ * @property {string[]} breakTags
+ */
+
+/**
+ * @param {TogglConfig} config
+ */
+async function updateConfig(config) {
+ await StorageHandler.setTogglConfig(config);
+ setConfig(config);
+}
+
+/**
+ * @param {string[]} breakTags
+ * @returns {string}
+ */
+const breakTagsToText = (breakTags) => breakTags.join(', ');
+
+/**
+ * @param {string} text
+ * @returns {string[]}
+ */
+const textToBreakTags = text => text.split(',')
+ .map(t => t.trim())
+ .filter(t => t);
+
+export default function TogglSection() {
+ const [enabled, setEnabled] = useState();
+ const [token, setToken] = useState();
+ const [breakTagsText, setBreakTagsText] = useState();
+ const [loading, setLoading] = useState(true);
+
+ const loadConfig = () => {
+ StorageHandler.getTogglConfig().then(config => {
+ setEnabled(config.enabled);
+ setToken(config.token);
+ setBreakTagsText(breakTagsToText(config.breakTags));
+ setLoading(false);
+ });
+ };
+
+ const save = async () => {
+ setLoading(true);
+ await StorageHandler.setTogglConfig({
+ enabled,
+ token,
+ breakTags: textToBreakTags(breakTagsText),
+ });
+ loadConfig();
+ };
+
+ useEffect(() => {
+ loadConfig();
+ }, []);
+
+ return (
+
+
Toggl Integration Settings
+
+ Allow access to blocked websites when you're tracking a break in
+ Toggl
+
+
+ {!loading &&
+
+ }
+
+ );
+}
\ No newline at end of file
diff --git a/src/options/index.js b/src/options/index.js
index 1b177d3..8bef6c2 100644
--- a/src/options/index.js
+++ b/src/options/index.js
@@ -6,6 +6,7 @@ import MessageTypes from '../enums/messages';
import DomainListItem from './components/DomainListItem';
import SettingsSection from './components/SettingsSection';
import ExtensionStatus from './components/ExtensionStatus';
+import TogglSection from './components/TogglSection';
class Options extends React.Component {
constructor(props) {
@@ -153,6 +154,7 @@ class Options extends React.Component {
+
diff --git a/src/options/options.css b/src/options/options.css
index 2d35990..e936026 100644
--- a/src/options/options.css
+++ b/src/options/options.css
@@ -178,3 +178,26 @@ hr {
font-size: 20px;
}
}
+
+.toggl__header {
+ font-size: 1.8rem;
+ color: #1a1a1a;
+}
+
+.toggl__subheader {
+ margin: 0;
+}
+
+.toggl__link {
+ margin-left: 0.2em;
+}
+
+.toggl__text-input {
+ margin-left: 0.4em;
+ width: 20em;
+}
+
+.toggl__save {
+ margin-left: 0;
+ margin-top: 0.2em;
+}
\ No newline at end of file
diff --git a/src/storage/StorageHandler.js b/src/storage/StorageHandler.js
index 8d3506d..5e26f8c 100644
--- a/src/storage/StorageHandler.js
+++ b/src/storage/StorageHandler.js
@@ -20,6 +20,37 @@ export default class StorageHandler {
return browser.storage.local.get('sites');
}
+
+ /**
+ * @typedef {Object} TogglConfig
+ * @property {boolean} enabled
+ * @property {string} token
+ * @property {string[]} breakTags
+ */
+
+ /**
+ * @returns {Promise}
+ */
+ static async getTogglConfig() {
+ const storage = await browser.storage.local.get('togglConfig');
+ return storage.togglConfig || {
+ enabled: false,
+ token: '',
+ breakTags: [
+ 'pomodoro-break',
+ 'break',
+ ],
+ };
+ }
+
+ /**
+ *
+ * @param {TogglConfig} togglConfig
+ */
+ static setTogglConfig(togglConfig) {
+ return browser.storage.local.set({ togglConfig });
+ }
+
static async isDomainBlocked(urlToMatch) {
const websites = await StorageHandler.getWebsiteDomains();
return websites.includes(urlToMatch);
diff --git a/src/utils/functions.js b/src/utils/functions.js
index bbdab6e..62c2350 100644
--- a/src/utils/functions.js
+++ b/src/utils/functions.js
@@ -1,14 +1,51 @@
+/**
+ * @typedef {Object} TogglConfig
+ * @property {boolean} enabled
+ * @property {string} token
+ * @property {string[]} breakTags
+ */
+import StorageHandler from "../storage/StorageHandler";
+
export function openOptionsPage() {
browser.runtime.openOptionsPage();
window.close();
}
-export function redirectToBlockedPage(requestDetails) {
+export async function redirectToBlockedPage(requestDetails) {
const original = encodeURIComponent(requestDetails.url);
const interceptPage = `/resources/redirect.html?target=${original}`;
+
+ const togglConfig = await StorageHandler.getTogglConfig();
+ if (togglConfig.enabled && await isOnBreak(togglConfig)) {
+ return;
+ }
+
browser.tabs.update(requestDetails.tabId, { url: interceptPage });
}
export function backgroundResponse(value) {
return new Promise(res => res(value));
}
+
+/**
+ * @param togglConfig {TogglConfig}
+ * @returns {Promise}
+ */
+async function isOnBreak(togglConfig) {
+ const endpoint = 'https://www.toggl.com/api/v8/time_entries/current';
+ const auth = `Basic ${btoa(`${togglConfig.token}:api_token`)}`;
+
+ const response = await fetch(endpoint, {
+ headers: new Headers({
+ Authorization: auth
+ })
+ });
+ if (!response.ok) return false;
+
+ const body = await response.json();
+ if (!body.data) return false;
+ if (!(body.data.tags instanceof Array)) return false;
+
+ const breakTags = new Set(togglConfig.breakTags);
+ return body.data.tags.some(tag => breakTags.has(tag));
+}