diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..be8857d
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "editor.codeActionsOnSave": ["source.organizeImports", "source.fixAll"]
+}
diff --git a/assets/js/app.jsx b/assets/js/app.jsx
deleted file mode 100644
index 6da1c43..0000000
--- a/assets/js/app.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-/* This is a fix for Bootstrap requiring jQuery */
-global.jQuery = require('jquery');
-require('bootstrap');
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-// React components
-var WakaTime = require('./components/WakaTime.jsx');
-
-ReactDOM.render(, document.getElementById('wakatime'));
diff --git a/assets/js/components/Alert.jsx b/assets/js/components/Alert.jsx
deleted file mode 100644
index 04e121f..0000000
--- a/assets/js/components/Alert.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-var React = require('react');
-var reactCreateClass = require('create-react-class');
-var classNames = require('classnames');
-
-var Alert = reactCreateClass({
- render: function () {
- return
{this.props.text}
;
- },
-});
-
-module.exports = Alert;
diff --git a/assets/js/components/MainList.jsx b/assets/js/components/MainList.jsx
deleted file mode 100644
index 7098203..0000000
--- a/assets/js/components/MainList.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-/* global browser */
-
-var React = require('react');
-var reactCreateClass = require('create-react-class');
-var MainList = reactCreateClass({
- _openOptionsPage: function () {
- if (browser.runtime.openOptionsPage) {
- // New way to open options pages, if supported (Chrome 42+).
- browser.runtime.openOptionsPage();
- } else {
- // Reasonable fallback.
- window.open(browser.runtime.getURL('options.html'));
- }
- },
-
- render: function () {
- var that = this;
-
- var loginLogoutButton = function () {
- if (that.props.loggedIn === true) {
- return (
-
- );
- }
-
- return (
-
-
- Login
-
- );
- };
-
- // If logging is enabled, display that info to user
- var loggingStatus = function () {
- if (that.props.loggingEnabled === true && that.props.loggedIn === true) {
- return (
-
- );
- } else if (that.props.loggingEnabled === false && that.props.loggedIn === true) {
- return (
-
- );
- }
- };
-
- var totalTimeLoggedToday = function () {
- if (that.props.loggedIn === true) {
- return (
-
-
-
- {that.props.totalTimeLoggedToday}
-
- TOTAL TIME LOGGED TODAY
-
-
-
-
- );
- }
- };
-
- return (
-
- {totalTimeLoggedToday()}
-
- {loggingStatus()}
-
-
-
- );
- },
-});
-
-module.exports = MainList;
diff --git a/assets/js/components/NavBar.jsx b/assets/js/components/NavBar.jsx
deleted file mode 100644
index 790a173..0000000
--- a/assets/js/components/NavBar.jsx
+++ /dev/null
@@ -1,102 +0,0 @@
-var React = require('react');
-var reactCreateClass = require('create-react-class');
-
-var NavBar = reactCreateClass({
- render: function () {
- var that = this;
-
- var signedInAs = function () {
- if (that.props.loggedIn === true) {
- return (
-
- Signed in as {that.props.user.full_name}
-
- );
- }
- };
-
- var dashboard = function () {
- if (that.props.loggedIn === true) {
- return (
-
-
-
- Dashboard
-
-
- );
- }
- };
-
- var customRules = function () {
- if (that.props.loggedIn === true) {
- return (
-
-
-
- Custom Rules
-
-
- );
- }
- };
-
- return (
-
- );
- },
-});
-
-module.exports = NavBar;
diff --git a/assets/js/components/Options.jsx b/assets/js/components/Options.jsx
deleted file mode 100644
index 419675a..0000000
--- a/assets/js/components/Options.jsx
+++ /dev/null
@@ -1,231 +0,0 @@
-/* global browser */
-
-var React = require('react');
-var reactCreateClass = require('create-react-class');
-var ReactCSSTransitionGroup = require('react-transition-group/CSSTransitionGroup');
-
-var config = require('../config');
-
-// React components
-var Alert = require('./Alert.jsx');
-var SitesList = require('./SitesList.jsx');
-
-/**
- * One thing to keep in mind is that you cannot use this.refs.blacklist if
- * the blacklist select box is not being rendered on the form.
- *
- * @type {*|Function}
- */
-var Options = reactCreateClass({
- getInitialState: function () {
- return {
- theme: config.theme,
- blacklist: '',
- whitelist: '',
- loggingType: config.loggingType,
- loggingStyle: config.loggingStyle,
- displayAlert: false,
- alertType: config.alert.success.type,
- alertText: config.alert.success.text,
- };
- },
-
- componentDidMount: function () {
- this.restoreSettings();
- },
-
- restoreSettings: function () {
- var that = this;
-
- browser.storage.sync
- .get({
- theme: config.theme,
- blacklist: '',
- whitelist: '',
- loggingType: config.loggingType,
- loggingStyle: config.loggingStyle,
- })
- .then(function (items) {
- that.setState({
- theme: items.theme,
- blacklist: items.blacklist,
- whitelist: items.whitelist,
- loggingType: items.loggingType,
- loggingStyle: items.loggingStyle,
- });
-
- that.refs.theme.value = items.theme;
- that.refs.loggingType.value = items.loggingType;
- that.refs.loggingStyle.value = items.loggingStyle;
- });
- },
-
- _handleSubmit: function (e) {
- e.preventDefault();
-
- this.saveSettings();
- },
-
- saveSettings: function () {
- var that = this;
-
- var theme = this.refs.theme.value.trim();
- var loggingType = this.refs.loggingType.value.trim();
- var loggingStyle = this.refs.loggingStyle.value.trim();
- // Trimming blacklist and whitelist removes blank lines and spaces.
- var blacklist = that.state.blacklist.trim();
- var whitelist = that.state.whitelist.trim();
-
- // Sync options with google storage.
- browser.storage.sync
- .set({
- theme: theme,
- blacklist: blacklist,
- whitelist: whitelist,
- loggingType: loggingType,
- loggingStyle: loggingStyle,
- })
- .then(function () {
- // Set state to be newly entered values.
- that.setState({
- theme: theme,
- blacklist: blacklist,
- whitelist: whitelist,
- loggingType: loggingType,
- loggingStyle: loggingStyle,
- displayAlert: true,
- });
- });
- },
-
- _displayBlackOrWhiteList: function () {
- var loggingStyle = this.refs.loggingStyle.value.trim();
-
- this.setState({ loggingStyle: loggingStyle });
- },
-
- _updateBlacklistState: function (sites) {
- this.setState({
- blacklist: sites,
- });
- },
-
- _updateWhitelistState: function (sites) {
- this.setState({
- whitelist: sites,
- });
- },
-
- render: function () {
- var that = this;
-
- var alert = function () {
- if (that.state.displayAlert === true) {
- setTimeout(function () {
- that.setState({ displayAlert: false });
- }, 2000);
-
- return (
-
- );
- }
- };
-
- var loggingStyle = function () {
- if (that.state.loggingStyle == 'blacklist') {
- return (
-
- );
- }
-
- return (
-
- );
- };
-
- return (
-
- );
- },
-});
-
-module.exports = Options;
diff --git a/assets/js/components/SitesList.jsx b/assets/js/components/SitesList.jsx
deleted file mode 100644
index 8aeaf60..0000000
--- a/assets/js/components/SitesList.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-var React = require('react');
-var reactCreateClass = require('create-react-class');
-
-var SitesList = reactCreateClass({
- getDefaultProps: function () {
- return {
- placeholder: 'http://google.com',
- };
- },
-
- _handleChange: function (event) {
- var sites = event.target.value;
-
- this.props.handleChange(sites);
- },
-
- render: function () {
- return (
-
-
-
-
-
-
- {this.props.helpText}
-
- One line per site.
-
-
-
- );
- },
-});
-
-module.exports = SitesList;
diff --git a/assets/js/components/WakaTime.jsx b/assets/js/components/WakaTime.jsx
deleted file mode 100644
index d953c0b..0000000
--- a/assets/js/components/WakaTime.jsx
+++ /dev/null
@@ -1,163 +0,0 @@
-/* global browser */
-
-var React = require('react');
-var reactCreateClass = require('create-react-class');
-var $ = require('jquery');
-
-var config = require('../config');
-
-// React components
-var NavBar = require('./NavBar.jsx');
-var MainList = require('./MainList.jsx');
-
-// Core
-var WakaTimeCore = require('../core/WakaTimeCore').default;
-
-// Helpers
-var changeExtensionState = require('../helpers/changeExtensionState');
-
-var Wakatime = reactCreateClass({
- getInitialState: function () {
- return {
- user: {
- full_name: null,
- email: null,
- photo: null,
- },
- loggedIn: false,
- loggingEnabled: config.loggingEnabled,
- totalTimeLoggedToday: '0 minutes',
- };
- },
-
- componentDidMount: function () {
- var wakatime = new WakaTimeCore();
-
- var that = this;
-
- wakatime.checkAuth().done(function (data) {
- if (data !== false) {
- browser.storage.sync
- .get({
- loggingEnabled: config.loggingEnabled,
- })
- .then(function (items) {
- that.setState({ loggingEnabled: items.loggingEnabled });
-
- if (items.loggingEnabled === true) {
- changeExtensionState('allGood');
- } else {
- changeExtensionState('notLogging');
- }
- });
-
- that.setState({
- user: {
- full_name: data.full_name,
- email: data.email,
- photo: data.photo,
- },
- loggedIn: true,
- });
-
- wakatime.getTotalTimeLoggedToday().done(function (grand_total) {
- that.setState({
- totalTimeLoggedToday: grand_total.text,
- });
- });
-
- wakatime.recordHeartbeat();
- } else {
- changeExtensionState('notSignedIn');
- }
- });
- },
-
- logoutUser: function () {
- var deferredObject = $.Deferred();
-
- var that = this;
-
- $.ajax({
- url: config.logoutUserUrl,
- method: 'GET',
- success: function () {
- deferredObject.resolve(that);
- },
- error: function (xhr, status, err) {
- console.error(config.logoutUserUrl, status, err.toString());
-
- deferredObject.resolve(that);
- },
- });
-
- return deferredObject.promise();
- },
-
- _logoutUser: function () {
- var that = this;
-
- this.logoutUser().done(function () {
- that.setState({
- user: {
- full_name: null,
- email: null,
- photo: null,
- },
- loggedIn: false,
- loggingEnabled: false,
- });
-
- changeExtensionState('notSignedIn');
- });
- },
-
- _disableLogging: function () {
- this.setState({
- loggingEnabled: false,
- });
-
- changeExtensionState('notLogging');
-
- browser.storage.sync.set({
- loggingEnabled: false,
- });
- },
-
- _enableLogging: function () {
- this.setState({
- loggingEnabled: true,
- });
-
- changeExtensionState('allGood');
-
- browser.storage.sync.set({
- loggingEnabled: true,
- });
- },
-
- render: function () {
- return (
-
- );
- },
-});
-
-module.exports = Wakatime;
diff --git a/assets/js/config.js b/assets/js/config.js
deleted file mode 100644
index 01d9d06..0000000
--- a/assets/js/config.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* global browser */
-//jshint esnext:true
-var config = {
- // Extension name
- name: 'WakaTime',
- // Extension version
- version: process.env.NODE_ENV === 'test' ? 'test' : browser.runtime.getManifest().version,
- // Time for idle state of the browser
- // The user is considered idle if there was
- // no activity in the browser for x seconds
- detectionIntervalInSeconds: 60,
- // Default logging style
- // Log all except blacklisted sites
- // or log only the white listed sites.
- loggingStyle: 'blacklist',
- // Default logging type
- loggingType: 'domain',
- // By default logging is enabled
- loggingEnabled: true,
- // Url to which to send the heartbeat
- heartbeatApiUrl: 'https://wakatime.com/api/v1/users/current/heartbeats',
- // Url from which to detect if the user is logged in
- currentUserApiUrl: 'https://wakatime.com/api/v1/users/current',
- // The url to logout the user from wakatime
- logoutUserUrl: 'https://wakatime.com/logout',
- // Gets stats from the WakaTime API
- summariesApiUrl: 'https://wakatime.com/api/v1/users/current/summaries',
- // Different colors for different states of the extension
- colors: {
- allGood: '',
- notLogging: 'gray',
- notSignedIn: 'red',
- lightTheme: 'white',
- },
- // Tooltips for each of the extension states
- tooltips: {
- allGood: '',
- notLogging: 'Not logging',
- notSignedIn: 'Not signed In',
- blacklisted: 'This URL is blacklisted',
- whitelisted: 'This URL is not on your whitelist',
- },
- // Default theme
- theme: 'light',
- // Valid extension states
- states: ['allGood', 'notLogging', 'notSignedIn', 'blacklisted', 'whitelisted'],
- // Predefined alert type and text for success and failure.
- alert: {
- success: {
- type: 'success',
- text: 'Options have been saved!',
- },
- failure: {
- type: 'danger',
- text: 'There was an error while saving the options!',
- },
- },
-};
-
-module.exports = config;
diff --git a/assets/js/core/WakaTimeCore.js b/assets/js/core/WakaTimeCore.js
deleted file mode 100644
index 7dd93e2..0000000
--- a/assets/js/core/WakaTimeCore.js
+++ /dev/null
@@ -1,300 +0,0 @@
-/* global browser */
-//jshint esnext:true
-
-var $ = require('jquery');
-var moment = require('moment');
-var config = require('./../config');
-
-// Helpers
-var getDomainFromUrl = require('./../helpers/getDomainFromUrl');
-var changeExtensionState = require('../helpers/changeExtensionState');
-var in_array = require('./../helpers/in_array');
-var contains = require('./../helpers/contains');
-
-class WakaTimeCore {
- constructor() {
- this.tabsWithDevtoolsOpen = [];
- }
-
- /**
- * Settter for tabsWithDevtoolsOpen
- *
- * @param tabs
- */
- setTabsWithDevtoolsOpen(tabs) {
- this.tabsWithDevtoolsOpen = tabs;
- }
-
- getTotalTimeLoggedToday() {
- var deferredObject = $.Deferred();
- var today = moment().format('YYYY-MM-DD');
-
- $.ajax({
- url: config.summariesApiUrl + '?start=' + today + '&end=' + today,
- dataType: 'json',
- success: (data) => {
- deferredObject.resolve(data.data[0].grand_total);
- },
- error: (xhr, status, err) => {
- console.error(config.summariesApiUrl, status, err.toString());
-
- deferredObject.resolve(false);
- },
- });
-
- return deferredObject.promise();
- }
-
- /**
- * Checks if the user is logged in.
- *
- * @returns {*}
- */
- checkAuth() {
- var deferredObject = $.Deferred();
-
- $.ajax({
- url: config.currentUserApiUrl,
- dataType: 'json',
- success: (data) => {
- deferredObject.resolve(data.data);
- },
- error: (xhr, status, err) => {
- console.error(config.currentUserApiUrl, status, err.toString());
- deferredObject.resolve(false);
- },
- });
-
- return deferredObject.promise();
- }
-
- /**
- * Depending on various factors detects the current active tab URL or domain,
- * and sends it to WakaTime for logging.
- */
- recordHeartbeat() {
- browser.storage.sync
- .get({
- loggingEnabled: config.loggingEnabled,
- loggingStyle: config.loggingStyle,
- blacklist: '',
- whitelist: '',
- })
- .then((items) => {
- if (items.loggingEnabled === true) {
- changeExtensionState('allGood');
-
- browser.idle.queryState(config.detectionIntervalInSeconds).then((newState) => {
- if (newState === 'active') {
- // Get current tab URL.
- browser.tabs.query({ currentWindow: true, active: true }).then((tabs) => {
- if (tabs.length == 0) return;
-
- var currentActiveTab = tabs[0];
- var debug = false;
-
- // If the current active tab has devtools open
- if (in_array(currentActiveTab.id, this.tabsWithDevtoolsOpen)) {
- debug = true;
- }
-
- if (items.loggingStyle == 'blacklist') {
- if (!contains(currentActiveTab.url, items.blacklist)) {
- this.sendHeartbeat(
- {
- url: currentActiveTab.url,
- project: null,
- },
- debug,
- );
- } else {
- changeExtensionState('blacklisted');
- console.log(currentActiveTab.url + ' is on a blacklist.');
- }
- }
-
- if (items.loggingStyle == 'whitelist') {
- var heartbeat = this.getHeartbeat(currentActiveTab.url, items.whitelist);
- if (heartbeat.url) {
- this.sendHeartbeat(heartbeat, debug);
- } else {
- changeExtensionState('whitelisted');
- console.log(currentActiveTab.url + ' is not on a whitelist.');
- }
- }
- });
- }
- });
- } else {
- changeExtensionState('notLogging');
- }
- });
- }
-
- /**
- * Creates an array from list using \n as delimiter
- * and checks if any element in list is contained in the url.
- * Also checks if element is assigned to a project using @@ as delimiter
- *
- * @param url
- * @param list
- * @returns {object}
- */
- getHeartbeat(url, list) {
- var projectIndicatorCharacters = '@@';
-
- var lines = list.split('\n');
- for (var i = 0; i < lines.length; i++) {
- // strip (http:// or https://) and trailing (`/` or `@@`)
- var cleanLine = lines[i]
- .trim()
- .replace(/(\/|@@)$/, '')
- .replace(/^(?:https?:\/\/)?/i, '');
- if (cleanLine === '') continue;
-
- var projectIndicatorIndex = cleanLine.lastIndexOf(projectIndicatorCharacters);
- var projectIndicatorExists = projectIndicatorIndex > -1;
- var projectName = null;
- var urlFromLine = cleanLine;
- if (projectIndicatorExists) {
- var start = projectIndicatorIndex + projectIndicatorCharacters.length;
- projectName = cleanLine.substring(start);
- urlFromLine = cleanLine
- .replace(cleanLine.substring(projectIndicatorIndex), '')
- .replace(/\/$/, '');
- }
- var schemaHttpExists = url.match(/^http:\/\//i);
- var schemaHttpsExists = url.match(/^https:\/\//i);
- var schema = '';
- if (schemaHttpExists) {
- schema = 'http://';
- }
- if (schemaHttpsExists) {
- schema = 'https://';
- }
- var cleanUrl = url
- .trim()
- .replace(/(\/|@@)$/, '')
- .replace(/^(?:https?:\/\/)?/i, '');
- var startsWithUrl = cleanUrl.toLowerCase().indexOf(urlFromLine.toLowerCase()) > -1;
- if (startsWithUrl) {
- return {
- url: schema + urlFromLine,
- project: projectName,
- };
- }
- }
-
- return {
- url: null,
- project: null,
- };
- }
-
- /**
- * Creates payload for the heartbeat and returns it as JSON.
- *
- * @param heartbeat
- * @param type
- * @param debug
- * @returns {*}
- * @private
- */
- _preparePayload(heartbeat, type, debug = false) {
- return JSON.stringify({
- entity: heartbeat.url,
- type: type,
- time: moment().format('X'),
- project: heartbeat.project || '<>',
- is_debugging: debug,
- plugin: 'browser-wakatime/' + config.version,
- });
- }
-
- /**
- * Returns a promise with logging type variable.
- *
- * @returns {*}
- * @private
- */
- _getLoggingType() {
- var deferredObject = $.Deferred();
-
- browser.storage.sync
- .get({
- loggingType: config.loggingType,
- })
- .then(function (items) {
- deferredObject.resolve(items.loggingType);
- });
-
- return deferredObject.promise();
- }
-
- /**
- * Given the heartbeat and logging type it creates a payload and
- * sends an ajax post request to the API.
- *
- * @param heartbeat
- * @param debug
- */
- sendHeartbeat(heartbeat, debug) {
- var payload = null;
-
- this._getLoggingType().done((loggingType) => {
- // Get only the domain from the entity.
- // And send that in heartbeat
- if (loggingType == 'domain') {
- heartbeat.url = getDomainFromUrl(heartbeat.url);
- payload = this._preparePayload(heartbeat, 'domain', debug);
- console.log(payload);
- this.sendAjaxRequestToApi(payload);
- }
- // Send entity in heartbeat
- else if (loggingType == 'url') {
- payload = this._preparePayload(heartbeat, 'url', debug);
- console.log(payload);
- this.sendAjaxRequestToApi(payload);
- }
- });
- }
-
- /**
- * Sends AJAX request with payload to the heartbeat API as JSON.
- *
- * @param payload
- * @param method
- * @returns {*}
- */
- sendAjaxRequestToApi(payload, method = 'POST') {
- var deferredObject = $.Deferred();
-
- $.ajax({
- url: config.heartbeatApiUrl,
- dataType: 'json',
- contentType: 'application/json',
- method: method,
- data: payload,
- statusCode: {
- 401: function () {
- changeExtensionState('notSignedIn');
- },
- 201: function () {
- // nothing to do here
- },
- },
- success: (response) => {
- deferredObject.resolve(this);
- },
- error: (xhr, status, err) => {
- console.error(config.heartbeatApiUrl, status, err.toString());
-
- deferredObject.resolve(this);
- },
- });
-
- return deferredObject.promise();
- }
-}
-
-export default WakaTimeCore;
diff --git a/assets/js/devtools.js b/assets/js/devtools.js
deleted file mode 100644
index b54892d..0000000
--- a/assets/js/devtools.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* global browser */
-
-// Create a connection to the background page
-var backgroundPageConnection = browser.runtime.connect({
- name: 'devtools-page',
-});
-
-// Send a message to background page with the current active tabId
-backgroundPageConnection.postMessage({
- name: 'init',
- tabId: browser.devtools.inspectedWindow.tabId,
-});
diff --git a/assets/js/events.js b/assets/js/events.js
deleted file mode 100644
index f7daea0..0000000
--- a/assets/js/events.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/* global browser */
-
-// Core
-var WakaTimeCore = require('./core/WakaTimeCore').default;
-
-// initialize class
-var wakatime = new WakaTimeCore();
-
-// Holds currently open connections (ports) with devtools
-// Uses tabId as index key.
-var connections = {};
-
-// Add a listener to resolve alarms
-browser.alarms.onAlarm.addListener(function (alarm) {
- // |alarm| can be undefined because onAlarm also gets called from
- // window.setTimeout on old chrome versions.
- if (alarm && alarm.name == 'heartbeatAlarm') {
- console.log('recording a heartbeat - alarm triggered');
-
- wakatime.recordHeartbeat();
- }
-});
-
-// Create a new alarm for heartbeats.
-browser.alarms.create('heartbeatAlarm', { periodInMinutes: 2 });
-
-/**
- * Whenever a active tab is changed it records a heartbeat with that tab url.
- */
-browser.tabs.onActivated.addListener(function (activeInfo) {
- browser.tabs.get(activeInfo.tabId).then(function (tab) {
- console.log('recording a heartbeat - active tab changed');
-
- wakatime.recordHeartbeat();
- });
-});
-
-/**
- * Whenever a active window is changed it records a heartbeat with the active tab url.
- */
-browser.windows.onFocusChanged.addListener(function (windowId) {
- if (windowId != browser.windows.WINDOW_ID_NONE) {
- console.log('recording a heartbeat - active window changed');
-
- wakatime.recordHeartbeat();
- } else {
- console.log('lost focus');
- }
-});
-
-/**
- * Whenever any tab is updated it checks if the updated tab is the tab that is
- * currently active and if it is, then it records a heartbeat.
- */
-browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
- if (changeInfo.status === 'complete') {
- // Get current tab URL.
- browser.tabs.query({ currentWindow: true, active: true }).then(function (tabs) {
- // If tab updated is the same as active tab
- if (tabId == tabs[0].id) {
- console.log('recording a heartbeat - tab updated');
-
- wakatime.recordHeartbeat();
- }
- });
- }
-});
-
-/**
- * This is in charge of detecting if devtools are opened or closed
- * and sending a heartbeat depending on that.
- */
-browser.runtime.onConnect.addListener(function (port) {
- if (port.name == 'devtools-page') {
- // Listen to messages sent from the DevTools page
- port.onMessage.addListener(function (message, sender, sendResponse) {
- if (message.name == 'init') {
- connections[message.tabId] = port;
-
- wakatime.setTabsWithDevtoolsOpen(Object.keys(connections));
-
- wakatime.recordHeartbeat();
- }
- });
-
- port.onDisconnect.addListener(function (port) {
- var tabs = Object.keys(connections);
-
- for (var i = 0, len = tabs.length; i < len; i++) {
- if (connections[tabs[i]] == port) {
- delete connections[tabs[i]];
- break;
- }
- }
-
- wakatime.setTabsWithDevtoolsOpen(Object.keys(connections));
-
- wakatime.recordHeartbeat();
- });
- }
-});
diff --git a/assets/js/helpers/changeExtensionIcon.js b/assets/js/helpers/changeExtensionIcon.js
deleted file mode 100644
index 7905eb0..0000000
--- a/assets/js/helpers/changeExtensionIcon.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* global browser */
-
-var config = require('../config');
-
-/**
- * It changes the extension icon color.
- * Supported values are: 'red', 'white', 'gray' and ''.
- *
- * @param color
- */
-function changeExtensionIcon(color) {
- color = color ? color : '';
-
- var path = null;
-
- if (color !== '') {
- color = '-' + color;
-
- path = './graphics/wakatime-logo-38' + color + '.png';
-
- browser.browserAction.setIcon({
- path: path,
- });
- }
-
- if (color === '') {
- browser.storage.sync
- .get({
- theme: config.theme,
- })
- .then(function (items) {
- if (items.theme == config.theme) {
- path = './graphics/wakatime-logo-38.png';
-
- browser.browserAction.setIcon({
- path: path,
- });
- } else {
- path = './graphics/wakatime-logo-38-white.png';
-
- browser.browserAction.setIcon({
- path: path,
- });
- }
- });
- }
-}
-
-module.exports = changeExtensionIcon;
diff --git a/assets/js/helpers/changeExtensionState.js b/assets/js/helpers/changeExtensionState.js
deleted file mode 100644
index f881bc9..0000000
--- a/assets/js/helpers/changeExtensionState.js
+++ /dev/null
@@ -1,42 +0,0 @@
-var config = require('../config');
-
-// Helpers
-var changeExtensionIcon = require('./changeExtensionIcon');
-var changeExtensionTooltip = require('./changeExtensionTooltip');
-var in_array = require('./in_array');
-
-/**
- * Sets the current state of the extension.
- *
- * @param state
- */
-function changeExtensionState(state) {
- if (!in_array(state, config.states)) {
- throw new Error('Not a valid state!');
- }
-
- switch (state) {
- case 'allGood':
- changeExtensionIcon(config.colors.allGood);
- changeExtensionTooltip(config.tooltips.allGood);
- break;
- case 'notLogging':
- changeExtensionIcon(config.colors.notLogging);
- changeExtensionTooltip(config.tooltips.notLogging);
- break;
- case 'notSignedIn':
- changeExtensionIcon(config.colors.notSignedIn);
- changeExtensionTooltip(config.tooltips.notSignedIn);
- break;
- case 'blacklisted':
- changeExtensionIcon(config.colors.notLogging);
- changeExtensionTooltip(config.tooltips.blacklisted);
- break;
- case 'whitelisted':
- changeExtensionIcon(config.colors.notLogging);
- changeExtensionTooltip(config.tooltips.whitelisted);
- break;
- }
-}
-
-module.exports = changeExtensionState;
diff --git a/assets/js/helpers/changeExtensionTooltip.js b/assets/js/helpers/changeExtensionTooltip.js
deleted file mode 100644
index b65caf0..0000000
--- a/assets/js/helpers/changeExtensionTooltip.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* global browser */
-
-var config = require('../config');
-
-/**
- * It changes the extension title
- *
- * @param text
- */
-function changeExtensionTooltip(text) {
- if (text === '') {
- text = config.name;
- } else {
- text = config.name + ' - ' + text;
- }
-
- browser.browserAction.setTitle({ title: text });
-}
-
-module.exports = changeExtensionTooltip;
diff --git a/assets/js/helpers/contains.js b/assets/js/helpers/contains.js
deleted file mode 100644
index 62751ad..0000000
--- a/assets/js/helpers/contains.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Creates an array from list using \n as delimiter
- * and checks if any element in list is contained in the url.
- *
- * @param url
- * @param list
- * @returns {boolean}
- */
-function contains(url, list) {
- var lines = list.split('\n');
-
- for (var i = 0; i < lines.length; i++) {
- // Trim all lines from the list one by one
- var cleanLine = lines[i].trim();
-
- // If by any chance one line in the list is empty, ignore it
- if (cleanLine === '') continue;
-
- var lineRe = new RegExp(cleanLine.replace('.', '.').replace('*', '.*'));
-
- // If url matches the current line return true
- if (lineRe.test(url)) {
- return true;
- }
- }
-
- return false;
-}
-
-module.exports = contains;
diff --git a/assets/js/helpers/getDomainFromUrl.js b/assets/js/helpers/getDomainFromUrl.js
deleted file mode 100644
index fd45232..0000000
--- a/assets/js/helpers/getDomainFromUrl.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * Returns domain from given URL.
- *
- * @param url
- * @returns {string}
- */
-function getDomainFromUrl(url) {
- var parts = url.split('/');
-
- return parts[0] + '//' + parts[2];
-}
-
-module.exports = getDomainFromUrl;
diff --git a/assets/js/helpers/in_array.js b/assets/js/helpers/in_array.js
deleted file mode 100644
index 2a6774b..0000000
--- a/assets/js/helpers/in_array.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Returns boolean if needle is found in haystack or not.
- *
- * @param needle
- * @param haystack
- * @returns {boolean}
- */
-function in_array(needle, haystack) {
- for (var i = 0; i < haystack.length; i++) {
- if (needle == haystack[i]) {
- return true;
- }
- }
-
- return false;
-}
-
-module.exports = in_array;
diff --git a/assets/js/options.jsx b/assets/js/options.jsx
deleted file mode 100644
index de9c79f..0000000
--- a/assets/js/options.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* global browser */
-
-/* This is a fix for Bootstrap requiring jQuery */
-global.jQuery = require('jquery');
-require('bootstrap');
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-// React components
-var Options = require('./components/Options.jsx');
-
-ReactDOM.render(, document.getElementById('wakatime-options'));
diff --git a/devtools.html b/devtools.html
deleted file mode 100644
index 20696ee..0000000
--- a/devtools.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/manifest.json b/manifest.json
deleted file mode 100644
index bdbb377..0000000
--- a/manifest.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "background": {
- "scripts": ["public/js/browser-polyfill.min.js", "public/js/events.js"],
- "persistent": false
- },
- "browser_action": {
- "default_icon": {
- "19": "graphics/wakatime-logo-19.png",
- "38": "graphics/wakatime-logo-38.png"
- },
- "default_title": "WakaTime",
- "default_popup": "popup.html"
- },
- "browser_specific_settings": {
- "gecko": {
- "id": "addon@wakatime.com",
- "strict_min_version": "48.0"
- }
- },
- "description": "Automatic time tracking for Chrome.",
- "devtools_page": "devtools.html",
- "homepage_url": "https://wakatime.com",
- "icons": {
- "16": "graphics/wakatime-logo-16.png",
- "48": "graphics/wakatime-logo-48.png",
- "128": "graphics/wakatime-logo-128.png"
- },
- "manifest_version": 2,
- "name": "WakaTime",
- "options_ui": {
- "page": "options.html",
- "chrome_style": false
- },
- "permissions": [
- "https://api.wakatime.com/*",
- "https://wakatime.com/*",
- "alarms",
- "tabs",
- "storage",
- "idle"
- ],
- "version": "3.0.0"
-}
diff --git a/options.html b/options.html
deleted file mode 100644
index ab85418..0000000
--- a/options.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
- WakaTime options
-
-
-
-
-
-
-
-
-
-
diff --git a/package-lock.json b/package-lock.json
index 9a37ac3..3515542 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^12.6.0",
+ "@types/chai": "^4.3.4",
"@types/chrome": "0.0.128",
"@types/classnames": "^2.2.11",
"@types/copy-webpack-plugin": "^10.1.0",
@@ -3631,6 +3632,12 @@
"@babel/types": "^7.3.0"
}
},
+ "node_modules/@types/chai": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
+ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==",
+ "dev": true
+ },
"node_modules/@types/chrome": {
"version": "0.0.128",
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.128.tgz",
@@ -26005,6 +26012,12 @@
"@babel/types": "^7.3.0"
}
},
+ "@types/chai": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
+ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==",
+ "dev": true
+ },
"@types/chrome": {
"version": "0.0.128",
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.128.tgz",
diff --git a/package.json b/package.json
index bb155be..2a3bdf3 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^12.6.0",
+ "@types/chai": "^4.3.4",
"@types/chrome": "0.0.128",
"@types/classnames": "^2.2.11",
"@types/copy-webpack-plugin": "^10.1.0",
diff --git a/popup.html b/popup.html
deleted file mode 100644
index e1c8f3c..0000000
--- a/popup.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
- WakaTime
-
-
-
-
-
-
-
-
-
-
diff --git a/src/components/WakaTime.tsx b/src/components/WakaTime.tsx
index 6b29727..faf0641 100644
--- a/src/components/WakaTime.tsx
+++ b/src/components/WakaTime.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { ApiKeyReducer, ReduxSelector } from '../types/store';
import { fetchUserData } from '../utils/user';
@@ -10,6 +10,7 @@ import MainList from './MainList';
export default function WakaTime(): JSX.Element {
const dispatch = useDispatch();
+ const [extensionState, setExtensionState] = useState('');
const {
apiKey: apiKeyFromRedux,
@@ -18,7 +19,12 @@ export default function WakaTime(): JSX.Element {
}: ApiKeyReducer = useSelector((selector: ReduxSelector) => selector.config);
useEffect(() => {
- void fetchUserData(apiKeyFromRedux, dispatch);
+ const fetchData = async () => {
+ await fetchUserData(apiKeyFromRedux, dispatch);
+ const items = await browser.storage.sync.get({ extensionState: '' });
+ setExtensionState(items.extensionState as string);
+ };
+ void fetchData();
}, []);
const isApiKeyValid = apiKeyInvalid(apiKeyFromRedux) === '';
@@ -26,6 +32,14 @@ export default function WakaTime(): JSX.Element {
return (
+ {isApiKeyValid && extensionState === 'notSignedIn' && (
+
browser.runtime.openOptionsPage()}
+ style={{ cursor: 'pointer' }}
+ />
+ )}
{!isApiKeyValid && (
{
- browser = window;
- });
- it('should be a function', function () {
- expect(changeExtensionState).to.be.a('function');
- });
-});
diff --git a/tests/helpers/in_array.spec.js b/tests/helpers/in_array.spec.js
deleted file mode 100644
index 68c046a..0000000
--- a/tests/helpers/in_array.spec.js
+++ /dev/null
@@ -1,18 +0,0 @@
-var chai = require('chai');
-var expect = chai.expect;
-
-var in_array = require('../../assets/js/helpers/in_array');
-
-describe('in_array', function () {
- it('should be a function', function () {
- expect(in_array).to.be.a('function');
- });
-
- it('should find the needle and return true', function () {
- expect(in_array('4', ['4', '3', '2', '1'])).to.equal(true);
- });
-
- it('should not find the needle and it should return false', function () {
- expect(in_array('5', ['4', '3', '2', '1'])).to.equal(false);
- });
-});
diff --git a/tests/helpers/Chrome.spec.js b/tests/utils/Chrome.spec.ts
similarity index 62%
rename from tests/helpers/Chrome.spec.js
rename to tests/utils/Chrome.spec.ts
index bdc0e89..5c19081 100644
--- a/tests/helpers/Chrome.spec.js
+++ b/tests/utils/Chrome.spec.ts
@@ -1,7 +1,8 @@
-var chai = require('chai');
-var sinon = require('sinon');
-var chrome = require('sinon-chrome');
-var expect = chai.expect;
+import chai from 'chai';
+import sinon from 'sinon';
+import chrome from 'sinon-chrome';
+
+const expect = chai.expect;
describe('Chrome Dev Tools', function () {
it('should work', function () {
diff --git a/tests/utils/changeExtensionIcon.spec.ts b/tests/utils/changeExtensionIcon.spec.ts
new file mode 100644
index 0000000..7934aaa
--- /dev/null
+++ b/tests/utils/changeExtensionIcon.spec.ts
@@ -0,0 +1,20 @@
+import chai from 'chai';
+import changeExtensionIcon from '../../src/utils/changeExtensionIcon';
+
+const expect = chai.expect;
+
+jest.mock('webextension-polyfill', () => {
+ return {
+ runtime: {
+ getManifest: () => {
+ return { version: 'test-version' };
+ },
+ },
+ };
+});
+
+describe('changeExtensionIcon', function () {
+ it('should be a function', function () {
+ expect(changeExtensionIcon).to.be.a('function');
+ });
+});
diff --git a/tests/utils/changeExtensionState.spec.ts b/tests/utils/changeExtensionState.spec.ts
new file mode 100644
index 0000000..17b3340
--- /dev/null
+++ b/tests/utils/changeExtensionState.spec.ts
@@ -0,0 +1,20 @@
+import chai from 'chai';
+import changeExtensionState from '../../src/utils/changeExtensionState';
+
+const expect = chai.expect;
+
+jest.mock('webextension-polyfill', () => {
+ return {
+ runtime: {
+ getManifest: () => {
+ return { version: 'test-version' };
+ },
+ },
+ };
+});
+
+describe('changeExtensionState', function () {
+ it('should be a function', function () {
+ expect(changeExtensionState).to.be.a('function');
+ });
+});
diff --git a/tests/helpers/changeExtensionTooltip.spec.js b/tests/utils/changeExtensionTooltip.spec.ts
similarity index 60%
rename from tests/helpers/changeExtensionTooltip.spec.js
rename to tests/utils/changeExtensionTooltip.spec.ts
index b244f80..d564f2e 100644
--- a/tests/helpers/changeExtensionTooltip.spec.js
+++ b/tests/utils/changeExtensionTooltip.spec.ts
@@ -1,9 +1,18 @@
-var chai = require('chai');
-var sinon = require('sinon-chai');
-var chrome = require('sinon-chrome');
-var expect = chai.expect;
+import chai from 'chai';
+import changeExtensionTooltip from '../../src/utils/changeExtensionTooltip';
+
+const expect = chai.expect;
+
+jest.mock('webextension-polyfill', () => {
+ return {
+ runtime: {
+ getManifest: () => {
+ return { version: 'test-version' };
+ },
+ },
+ };
+});
-var changeExtensionTooltip = require('../../assets/js/helpers/changeExtensionTooltip');
describe('changeExtensionTooltip', function () {
it('should be a function', function () {
expect(changeExtensionTooltip).to.be.a('function');
diff --git a/tests/helpers/contains.spec.js b/tests/utils/contains.spec.ts
similarity index 59%
rename from tests/helpers/contains.spec.js
rename to tests/utils/contains.spec.ts
index 017fd7d..b319869 100644
--- a/tests/helpers/contains.spec.js
+++ b/tests/utils/contains.spec.ts
@@ -1,7 +1,7 @@
-var chai = require('chai');
-var expect = chai.expect;
+import chai from 'chai';
+import contains from '../../src/utils/contains';
-var contains = require('../../assets/js/helpers/contains');
+const expect = chai.expect;
describe('contains', function () {
it('should be a function', function () {
@@ -9,16 +9,16 @@ describe('contains', function () {
});
it('should match url against blacklist and return true', function () {
- var list = 'localhost\ntest.com';
+ const list = 'localhost\ntest.com';
- var url = 'http://localhost/fooapp';
+ const url = 'http://localhost/fooapp';
expect(contains(url, list)).to.equal(true);
});
it('should not match url against blacklist and return false', function () {
- var list = 'localhost2\ntest.com';
+ const list = 'localhost2\ntest.com';
- var url = 'http://localhost/fooapp';
+ const url = 'http://localhost/fooapp';
expect(contains(url, list)).to.equal(false);
});
});
diff --git a/tests/helpers/getDomainFromUrl.spec.js b/tests/utils/getDomainFromUrl.spec.ts
similarity index 82%
rename from tests/helpers/getDomainFromUrl.spec.js
rename to tests/utils/getDomainFromUrl.spec.ts
index 7233c20..8ccc97a 100644
--- a/tests/helpers/getDomainFromUrl.spec.js
+++ b/tests/utils/getDomainFromUrl.spec.ts
@@ -1,7 +1,7 @@
-var chai = require('chai');
-var expect = chai.expect;
+import chai from 'chai';
+import getDomainFromUrl from '../../src/utils/getDomainFromUrl';
-var getDomainFromUrl = require('../../assets/js/helpers/getDomainFromUrl');
+const expect = chai.expect;
describe('getDomainFromUrl', function () {
it('should be a function', function () {
diff --git a/tests/utils/in_array.spec.ts b/tests/utils/in_array.spec.ts
new file mode 100644
index 0000000..4455294
--- /dev/null
+++ b/tests/utils/in_array.spec.ts
@@ -0,0 +1,18 @@
+import chai from 'chai';
+import inArray from '../../src/utils/inArray';
+
+const expect = chai.expect;
+
+describe('inArray', function () {
+ it('should be a function', function () {
+ expect(inArray).to.be.a('function');
+ });
+
+ it('should find the needle and return true', function () {
+ expect(inArray('4', ['4', '3', '2', '1'])).to.equal(true);
+ });
+
+ it('should not find the needle and it should return false', function () {
+ expect(inArray('5', ['4', '3', '2', '1'])).to.equal(false);
+ });
+});
diff --git a/xclap.ts b/xclap.ts
index 21e62dc..cb8bc47 100644
--- a/xclap.ts
+++ b/xclap.ts
@@ -69,22 +69,17 @@ load({
serial('postinstall'),
'webpack',
concurrent(
- exec('web-ext build'),
- exec(`web-ext build -a dist/firefox/web-ext-artifacts --source-dir ${ffNextBuildFolder}`),
+ exec(
+ `web-ext build --artifacts-dir dist/chrome/web-ext-artifacts --source-dir ${chromeNextBuildFolder}`,
+ ),
+ exec(
+ `web-ext build --artifacts-dir dist/firefox/web-ext-artifacts --source-dir ${ffNextBuildFolder}`,
+ ),
),
],
clean: [exec('rimraf public coverage vendor web-ext-artifacts'), 'clean:webpack'],
'clean:webpack': exec('rimraf dist'),
- dev: [
- 'clean',
- 'postinstall',
- concurrent('watch', 'web-ext:run:firefox-next', 'web-ext:run:chrome-next'),
- ],
- 'dev:legacy': [
- 'clean',
- 'postinstall',
- concurrent(exec('gulp watch'), 'web-ext:run:firefox-legacy', 'web-ext:run:chrome-legacy'),
- ],
+ dev: ['clean', 'postinstall', concurrent('watch', 'web-ext:run:firefox', 'web-ext:run:chrome')],
eslint: exec('eslint src . --fix'),
less: exec('lessc assets/less/app.less public/css/app.css'),
lint: ['prettier', 'eslint'],
@@ -93,34 +88,16 @@ load({
'remotedev-server': exec('remotedev --hostname=localhost --port=8000'),
test: ['build', 'lint', 'test-jest'],
'test-jest': [exec('jest --clearCache'), exec('jest --verbose --coverage')],
- 'test-jest-update': exec('jest -u'),
- 'wait:legacy-files': waitForFilesTask(
- 'manifest.json',
- 'public/js/browser-polyfill.min.js',
- 'public/js/events.js',
- 'options.html',
- ),
watch: concurrent('watch-jest', 'webpack:watch'),
'watch-jest': exec('jest --watch'),
- 'web-ext:run:chrome': concurrent('web-ext:run:chrome-next', 'web-ext:run:chrome-legacy'),
- 'web-ext:run:chrome-legacy': [
- 'wait:legacy-files',
- exec('web-ext run -t chromium --source-dir .'),
- ],
- 'web-ext:run:chrome-next': [
+ 'web-ext:run:chrome': [
chromeNextBuildFileWaitTask,
exec('web-ext run -t chromium --source-dir dist/chrome'),
],
- 'web-ext:run:firefox': concurrent('web-ext:run:firefox-next', 'web-ext:run:firefox-legacy'),
- 'web-ext:run:firefox-legacy': [
- 'wait:legacy-files',
- exec('web-ext run -t firefox-desktop --source-dir .'),
- ],
- 'web-ext:run:firefox-next': [
+ 'web-ext:run:firefox': [
ffNextBuildFileWaitTask,
exec('web-ext run -t firefox-desktop --source-dir dist/firefox'),
],
webpack: ['clean:webpack', exec('webpack --mode production')],
- 'webpack:dev': ['clean:webpack', exec('webpack --mode development')],
'webpack:watch': ['clean:webpack', exec('webpack --mode development --watch')],
});