Es6 cmp migration (#113)
* migrate Alert component * convert Mainlist component * add webpack watch task * update build script for different manifests * add types for api responses * convert changeExtensionIcon * convert inArray, getDomainParts, contains to ts * convert changeExtensionTooltip * convert changeExtensionState to ts
This commit is contained in:
@@ -13,6 +13,8 @@ module.exports = {
|
|||||||
'kentcdodds/import',
|
'kentcdodds/import',
|
||||||
'kentcdodds/jest',
|
'kentcdodds/jest',
|
||||||
'kentcdodds/possible-errors',
|
'kentcdodds/possible-errors',
|
||||||
|
'plugin:jest-dom/recommended',
|
||||||
|
'plugin:testing-library/recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'plugin:import/errors',
|
'plugin:import/errors',
|
||||||
'plugin:import/typescript',
|
'plugin:import/typescript',
|
||||||
|
|||||||
165
package-lock.json
generated
165
package-lock.json
generated
@@ -5897,6 +5897,144 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@testing-library/jest-dom": {
|
||||||
|
"version": "5.11.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz",
|
||||||
|
"integrity": "sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.9.2",
|
||||||
|
"@types/testing-library__jest-dom": "^5.9.1",
|
||||||
|
"aria-query": "^4.2.2",
|
||||||
|
"chalk": "^3.0.0",
|
||||||
|
"css": "^3.0.0",
|
||||||
|
"css.escape": "^1.5.1",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
|
"redent": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"source-map": "^0.6.1",
|
||||||
|
"source-map-resolve": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"indent-string": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"redent": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"indent-string": "^4.0.0",
|
||||||
|
"strip-indent": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"source-map-resolve": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"atob": "^2.1.2",
|
||||||
|
"decode-uri-component": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-indent": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"min-indent": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@testing-library/react": {
|
||||||
|
"version": "11.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.3.tgz",
|
||||||
|
"integrity": "sha512-BirBUGPkTW28ULuCwIbYo0y2+0aavHczBT6N9r3LrsswEW3pg25l1wgoE7I8QBIy1upXWkwKpYdWY7NYYP0Bxw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@testing-library/dom": "^7.28.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@testing-library/user-event": {
|
||||||
|
"version": "12.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.6.0.tgz",
|
||||||
|
"integrity": "sha512-FNEH/HLmOk5GO70I52tKjs7WvGYckeE/SrnLX/ip7z2IGbffyd5zOUM1tZ10vsTphqm+VbDFI0oaXu0wcfQsAQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.12.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/anymatch": {
|
"@types/anymatch": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
|
||||||
@@ -5960,6 +6098,12 @@
|
|||||||
"@types/har-format": "*"
|
"@types/har-format": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/classnames": {
|
||||||
|
"version": "2.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
|
||||||
|
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/copy-webpack-plugin": {
|
"@types/copy-webpack-plugin": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/copy-webpack-plugin/-/copy-webpack-plugin-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/copy-webpack-plugin/-/copy-webpack-plugin-6.4.0.tgz",
|
||||||
@@ -6278,6 +6422,15 @@
|
|||||||
"integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==",
|
"integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/testing-library__jest-dom": {
|
||||||
|
"version": "5.9.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz",
|
||||||
|
"integrity": "sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/jest": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/uglify-js": {
|
"@types/uglify-js": {
|
||||||
"version": "3.11.1",
|
"version": "3.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.1.tgz",
|
||||||
@@ -10645,6 +10798,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"css.escape": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
|
||||||
|
"integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"cssom": {
|
"cssom": {
|
||||||
"version": "0.4.4",
|
"version": "0.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||||
@@ -20413,6 +20572,12 @@
|
|||||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
|
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"min-indent": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"minimalistic-assert": {
|
"minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
|
|||||||
@@ -38,7 +38,12 @@
|
|||||||
"webextension-polyfill-ts": "^0.22.0"
|
"webextension-polyfill-ts": "^0.22.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@testing-library/dom": "^7.29.4",
|
||||||
|
"@testing-library/jest-dom": "^5.11.9",
|
||||||
|
"@testing-library/react": "^11.2.3",
|
||||||
|
"@testing-library/user-event": "^12.6.0",
|
||||||
"@types/chrome": "0.0.128",
|
"@types/chrome": "0.0.128",
|
||||||
|
"@types/classnames": "^2.2.11",
|
||||||
"@types/copy-webpack-plugin": "^6.4.0",
|
"@types/copy-webpack-plugin": "^6.4.0",
|
||||||
"@types/firefox-webext-browser": "^82.0.0",
|
"@types/firefox-webext-browser": "^82.0.0",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.20",
|
||||||
@@ -60,9 +65,11 @@
|
|||||||
"eslint-config-kentcdodds": "^17.3.0",
|
"eslint-config-kentcdodds": "^17.3.0",
|
||||||
"eslint-config-prettier": "^7.1.0",
|
"eslint-config-prettier": "^7.1.0",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
|
"eslint-plugin-jest-dom": "^3.6.5",
|
||||||
"eslint-plugin-prettier": "^3.3.1",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
"eslint-plugin-react": "^7.22.0",
|
"eslint-plugin-react": "^7.22.0",
|
||||||
"eslint-plugin-sort-keys-fix": "^1.1.1",
|
"eslint-plugin-sort-keys-fix": "^1.1.1",
|
||||||
|
"eslint-plugin-testing-library": "^3.10.1",
|
||||||
"eslint-plugin-typescript-sort-keys": "^1.5.0",
|
"eslint-plugin-typescript-sort-keys": "^1.5.0",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"husky": "^4.3.7",
|
"husky": "^4.3.7",
|
||||||
|
|||||||
34
src/components/Alert.test.tsx
Normal file
34
src/components/Alert.test.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import Alert from './Alert';
|
||||||
|
|
||||||
|
describe('Alert Component', () => {
|
||||||
|
it('should render with proper text on success type', () => {
|
||||||
|
const text = 'Test Text';
|
||||||
|
const { container } = render(<Alert text={text} type="success" />);
|
||||||
|
expect(screen.getByText(text)).toBeTruthy();
|
||||||
|
expect(container).toMatchInlineSnapshot(`
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="alert alert-success"
|
||||||
|
>
|
||||||
|
Test Text
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should render wtih proper text on danger type', () => {
|
||||||
|
const text = 'Test Text';
|
||||||
|
const { container } = render(<Alert text={text} type="danger" />);
|
||||||
|
expect(screen.getByText(text)).toBeTruthy();
|
||||||
|
expect(container).toMatchInlineSnapshot(`
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="alert alert-danger"
|
||||||
|
>
|
||||||
|
Test Text
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
11
src/components/Alert.tsx
Normal file
11
src/components/Alert.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { SuccessOrFailType } from '../config';
|
||||||
|
interface AlertProps {
|
||||||
|
text: string;
|
||||||
|
type: SuccessOrFailType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Alert({ type, text }: AlertProps): JSX.Element {
|
||||||
|
return <div className={classNames('alert', `alert-${type}`)}>{text}</div>;
|
||||||
|
}
|
||||||
63
src/components/MainList.test.tsx
Normal file
63
src/components/MainList.test.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import MainList from './MainList';
|
||||||
|
|
||||||
|
type onClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||||
|
describe('MainList', () => {
|
||||||
|
let disableLogging: onClick;
|
||||||
|
let enableLogging: onClick;
|
||||||
|
let loggedIn: boolean;
|
||||||
|
let loggingEnabled: boolean;
|
||||||
|
let logoutUser: onClick;
|
||||||
|
let totalTimeLoggedToday: string;
|
||||||
|
beforeEach(() => {
|
||||||
|
disableLogging = jest.fn();
|
||||||
|
enableLogging = jest.fn();
|
||||||
|
loggingEnabled = false;
|
||||||
|
loggedIn = false;
|
||||||
|
logoutUser = jest.fn();
|
||||||
|
totalTimeLoggedToday = '1/1/1999';
|
||||||
|
});
|
||||||
|
it('should render properly', () => {
|
||||||
|
const { container } = render(
|
||||||
|
<MainList
|
||||||
|
disableLogging={disableLogging}
|
||||||
|
enableLogging={enableLogging}
|
||||||
|
loggingEnabled={loggingEnabled}
|
||||||
|
loggedIn={loggedIn}
|
||||||
|
logoutUser={logoutUser}
|
||||||
|
totalTimeLoggedToday={totalTimeLoggedToday}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(container).toMatchInlineSnapshot(`
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="list-group"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="list-group-item"
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-fw fa-cogs"
|
||||||
|
/>
|
||||||
|
Options
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="list-group-item"
|
||||||
|
href="https://wakatime.com/login"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-fw fa-sign-in"
|
||||||
|
/>
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
87
src/components/MainList.tsx
Normal file
87
src/components/MainList.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
|
||||||
|
export interface MainListProps {
|
||||||
|
disableLogging: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||||
|
enableLogging: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||||
|
loggedIn: boolean;
|
||||||
|
loggingEnabled: boolean;
|
||||||
|
logoutUser: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
||||||
|
totalTimeLoggedToday?: string;
|
||||||
|
}
|
||||||
|
const openOptionsPage = async (): Promise<void> => {
|
||||||
|
await browser.runtime.openOptionsPage();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function MainList({
|
||||||
|
disableLogging,
|
||||||
|
enableLogging,
|
||||||
|
loggedIn,
|
||||||
|
loggingEnabled,
|
||||||
|
logoutUser,
|
||||||
|
totalTimeLoggedToday,
|
||||||
|
}: MainListProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{loggedIn && (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12">
|
||||||
|
<blockquote>
|
||||||
|
<p>{totalTimeLoggedToday}</p>
|
||||||
|
<small>
|
||||||
|
<cite>TOTAL TIME LOGGED TODAY</cite>
|
||||||
|
</small>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{loggingEnabled && loggedIn && (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12">
|
||||||
|
<p>
|
||||||
|
<a href="#" onClick={disableLogging} className="btn btn-danger btn-block">
|
||||||
|
Disable logging
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!loggingEnabled && loggedIn && (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12">
|
||||||
|
<p>
|
||||||
|
<a href="#" onClick={enableLogging} className="btn btn-success btn-block">
|
||||||
|
Enable logging
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="list-group">
|
||||||
|
<a href="#" className="list-group-item" onClick={openOptionsPage}>
|
||||||
|
<i className="fa fa-fw fa-cogs"></i>
|
||||||
|
Options
|
||||||
|
</a>
|
||||||
|
{loggedIn && (
|
||||||
|
<div>
|
||||||
|
<a href="#" className="list-group-item" onClick={logoutUser}>
|
||||||
|
<i className="fa fa-fw fa-sign-out"></i>
|
||||||
|
Logout
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!loggedIn && (
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
href="https://wakatime.com/login"
|
||||||
|
className="list-group-item"
|
||||||
|
>
|
||||||
|
<i className="fa fa-fw fa-sign-in"></i>
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,16 +3,16 @@ import { browser } from 'webextension-polyfill-ts';
|
|||||||
/**
|
/**
|
||||||
* Logging
|
* Logging
|
||||||
*/
|
*/
|
||||||
type ApiStates = 'allGood' | 'notLogging' | 'notSignedIn' | 'blacklisted' | 'whitelisted';
|
export type ApiStates = 'allGood' | 'notLogging' | 'notSignedIn' | 'blacklisted' | 'whitelisted';
|
||||||
/**
|
/**
|
||||||
* Supported logging style
|
* Supported logging style
|
||||||
*/
|
*/
|
||||||
type LoggingStyle = 'whitelist' | 'blacklist';
|
export type LoggingStyle = 'whitelist' | 'blacklist';
|
||||||
/**
|
/**
|
||||||
* Logging type
|
* Logging type
|
||||||
*/
|
*/
|
||||||
type LoggingType = 'domain' | 'url';
|
export type LoggingType = 'domain' | 'url';
|
||||||
type SuccessOrFailType = 'success' | 'danger';
|
export type SuccessOrFailType = 'success' | 'danger';
|
||||||
/**
|
/**
|
||||||
* Predefined alert type and text for success and failure.
|
* Predefined alert type and text for success and failure.
|
||||||
*/
|
*/
|
||||||
@@ -31,10 +31,10 @@ interface SuccessOrFailAlert {
|
|||||||
* Different colors for different states of the extension
|
* Different colors for different states of the extension
|
||||||
*/
|
*/
|
||||||
interface Colors {
|
interface Colors {
|
||||||
allGood: string;
|
allGood: '';
|
||||||
lightTheme: string;
|
lightTheme: 'white';
|
||||||
notLogging: string;
|
notLogging: 'gray';
|
||||||
notSignedIn: string;
|
notSignedIn: 'red';
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Tooltip messages
|
* Tooltip messages
|
||||||
|
|||||||
38
src/manifests/chrome.json
Normal file
38
src/manifests/chrome.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"background": {
|
||||||
|
"persistent": false,
|
||||||
|
"scripts": ["background.js"]
|
||||||
|
},
|
||||||
|
"browser_action": {
|
||||||
|
"default_icon": {
|
||||||
|
"19": "graphics/wakatime-logo-19.png",
|
||||||
|
"38": "graphics/wakatime-logo-38.png"
|
||||||
|
},
|
||||||
|
"default_popup": "popup.html",
|
||||||
|
"default_title": "WakaTime"
|
||||||
|
},
|
||||||
|
"browser_specific_settings": {},
|
||||||
|
"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": {
|
||||||
|
"chrome_style": false,
|
||||||
|
"page": "options.html"
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
"https://api.wakatime.com/*",
|
||||||
|
"https://wakatime.com/*",
|
||||||
|
"alarms",
|
||||||
|
"tabs",
|
||||||
|
"storage",
|
||||||
|
"idle"
|
||||||
|
],
|
||||||
|
"version": "2.0.1"
|
||||||
|
}
|
||||||
28
src/types/heartbeats.d.ts
vendored
Normal file
28
src/types/heartbeats.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Generated by https://quicktype.io
|
||||||
|
|
||||||
|
export interface HeartBeatsPayload {
|
||||||
|
data: Datum[];
|
||||||
|
end: string;
|
||||||
|
start: string;
|
||||||
|
timezone: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Datum {
|
||||||
|
branch: string;
|
||||||
|
category: string;
|
||||||
|
created_at: string;
|
||||||
|
cursorpos: null;
|
||||||
|
dependencies: string;
|
||||||
|
entity: string;
|
||||||
|
id: string;
|
||||||
|
is_write: boolean;
|
||||||
|
language: string;
|
||||||
|
lineno: null;
|
||||||
|
lines: number;
|
||||||
|
machine_name_id: string;
|
||||||
|
project: string;
|
||||||
|
time: number;
|
||||||
|
type: string;
|
||||||
|
user_agent_id: string;
|
||||||
|
user_id: string;
|
||||||
|
}
|
||||||
47
src/types/summaries.d.ts
vendored
Normal file
47
src/types/summaries.d.ts
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Generated by https://quicktype.io
|
||||||
|
|
||||||
|
export interface SummariesPayload {
|
||||||
|
data: Datum[];
|
||||||
|
end: string;
|
||||||
|
start: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Datum {
|
||||||
|
categories: Category[];
|
||||||
|
dependencies: Category[];
|
||||||
|
editors: Category[];
|
||||||
|
grand_total: GrandTotal;
|
||||||
|
languages: Category[];
|
||||||
|
machines: Category[];
|
||||||
|
operating_systems: Category[];
|
||||||
|
projects: Category[];
|
||||||
|
range: Range;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
digital: string;
|
||||||
|
hours: number;
|
||||||
|
machine_name_id?: string;
|
||||||
|
minutes: number;
|
||||||
|
name: string;
|
||||||
|
percent: number;
|
||||||
|
seconds: number;
|
||||||
|
text: string;
|
||||||
|
total_seconds: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GrandTotal {
|
||||||
|
digital: string;
|
||||||
|
hours: number;
|
||||||
|
minutes: number;
|
||||||
|
text: string;
|
||||||
|
total_seconds: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Range {
|
||||||
|
date: string;
|
||||||
|
end: string;
|
||||||
|
start: string;
|
||||||
|
text: string;
|
||||||
|
timezone: string;
|
||||||
|
}
|
||||||
44
src/types/user.d.ts
vendored
Normal file
44
src/types/user.d.ts
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Generated by https://quicktype.io
|
||||||
|
|
||||||
|
export interface UserPayload {
|
||||||
|
data: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
bio: null;
|
||||||
|
color_scheme: string;
|
||||||
|
created_at: string;
|
||||||
|
date_format: string;
|
||||||
|
default_dashboard_range: string;
|
||||||
|
display_name: string;
|
||||||
|
email: string;
|
||||||
|
full_name: string;
|
||||||
|
has_premium_features: boolean;
|
||||||
|
human_readable_website: string;
|
||||||
|
id: string;
|
||||||
|
is_email_confirmed: boolean;
|
||||||
|
is_email_public: boolean;
|
||||||
|
is_hireable: boolean;
|
||||||
|
is_onboarding_finished: boolean;
|
||||||
|
languages_used_public: boolean;
|
||||||
|
last_heartbeat_at: string;
|
||||||
|
last_plugin: string;
|
||||||
|
last_plugin_name: string;
|
||||||
|
last_project: string;
|
||||||
|
location: string;
|
||||||
|
logged_time_public: boolean;
|
||||||
|
modified_at: string;
|
||||||
|
needs_payment_method: boolean;
|
||||||
|
photo: string;
|
||||||
|
photo_public: boolean;
|
||||||
|
plan: string;
|
||||||
|
public_email: string;
|
||||||
|
show_machine_name_ip: boolean;
|
||||||
|
time_format_24hr: boolean;
|
||||||
|
timeout: number;
|
||||||
|
timezone: string;
|
||||||
|
username: string;
|
||||||
|
website: string;
|
||||||
|
weekday_start: number;
|
||||||
|
writes_only: boolean;
|
||||||
|
}
|
||||||
28
src/utils/changeExtensionIcon.ts
Normal file
28
src/utils/changeExtensionIcon.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
|
type ColorIconTypes = 'gray' | 'red' | 'white' | '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It changes the extension icon color.
|
||||||
|
*/
|
||||||
|
export default async function changeExtensionIcon(color?: ColorIconTypes): Promise<void> {
|
||||||
|
if (color) {
|
||||||
|
const path = `./graphics/wakatime-logo-38-${color}.png`;
|
||||||
|
|
||||||
|
await browser.browserAction.setIcon({
|
||||||
|
path: path,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const { theme } = await browser.storage.sync.get({
|
||||||
|
theme: config.theme,
|
||||||
|
});
|
||||||
|
const path =
|
||||||
|
theme === config.theme
|
||||||
|
? './graphics/wakatime-logo-38.png'
|
||||||
|
: './graphics/wakatime-logo-38-white.png';
|
||||||
|
await browser.browserAction.setIcon({
|
||||||
|
path: path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/utils/changeExtensionState.ts
Normal file
34
src/utils/changeExtensionState.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import config, { ApiStates } from '../config';
|
||||||
|
|
||||||
|
import changeExtensionIcon from './changeExtensionIcon';
|
||||||
|
import changeExtensionTooltip from './changeExtensionTooltip';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current state of the extension.
|
||||||
|
*/
|
||||||
|
export default async function changeExtensionState(state: ApiStates): Promise<void> {
|
||||||
|
switch (state) {
|
||||||
|
case 'allGood':
|
||||||
|
await changeExtensionIcon(config.colors.allGood);
|
||||||
|
await changeExtensionTooltip(config.tooltips.allGood);
|
||||||
|
break;
|
||||||
|
case 'notLogging':
|
||||||
|
await changeExtensionIcon(config.colors.notLogging);
|
||||||
|
await changeExtensionTooltip(config.tooltips.notLogging);
|
||||||
|
break;
|
||||||
|
case 'notSignedIn':
|
||||||
|
await changeExtensionIcon(config.colors.notSignedIn);
|
||||||
|
await changeExtensionTooltip(config.tooltips.notSignedIn);
|
||||||
|
break;
|
||||||
|
case 'blacklisted':
|
||||||
|
await changeExtensionIcon(config.colors.notLogging);
|
||||||
|
await changeExtensionTooltip(config.tooltips.blacklisted);
|
||||||
|
break;
|
||||||
|
case 'whitelisted':
|
||||||
|
await changeExtensionIcon(config.colors.notLogging);
|
||||||
|
await changeExtensionTooltip(config.tooltips.whitelisted);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/utils/changeExtensionTooltip.ts
Normal file
16
src/utils/changeExtensionTooltip.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It changes the extension title
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default async function changeExtensionTooltip(text: string): Promise<void> {
|
||||||
|
if (text === '') {
|
||||||
|
text = config.name;
|
||||||
|
} else {
|
||||||
|
text = `${config.name} - ${text}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.browserAction.setTitle({ title: text });
|
||||||
|
}
|
||||||
24
src/utils/contains.ts
Normal file
24
src/utils/contains.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Creates an array from list using \n as delimiter
|
||||||
|
* and checks if any element in list is contained in the url.
|
||||||
|
*/
|
||||||
|
export default function contains(url: string, list: string): boolean {
|
||||||
|
const lines = list.split('\n');
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
// Trim all lines from the list one by one
|
||||||
|
const cleanLine = lines[i].trim();
|
||||||
|
|
||||||
|
// If by any chance one line in the list is empty, ignore it
|
||||||
|
if (cleanLine === '') continue;
|
||||||
|
|
||||||
|
const lineRe = new RegExp(cleanLine.replace('.', '.').replace('*', '.*'));
|
||||||
|
|
||||||
|
// If url matches the current line return true
|
||||||
|
if (lineRe.test(url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
8
src/utils/getDomainFromUrl.ts
Normal file
8
src/utils/getDomainFromUrl.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Returns domain from given URL.
|
||||||
|
*/
|
||||||
|
export function getDomainFromUrl(url: string): string {
|
||||||
|
const parts = url.split('/');
|
||||||
|
|
||||||
|
return parts[0] + '//' + parts[2];
|
||||||
|
}
|
||||||
12
src/utils/inArray.ts
Normal file
12
src/utils/inArray.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Returns boolean if needle is found in haystack or not.
|
||||||
|
*/
|
||||||
|
export function in_array<T>(needle: T, haystack: T[]): boolean {
|
||||||
|
for (let i = 0; i < haystack.length; i++) {
|
||||||
|
if (needle == haystack[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ const fontFolder = join(publicFolder, 'fonts');
|
|||||||
const graphicsFolder = join(__dirname, 'graphics');
|
const graphicsFolder = join(__dirname, 'graphics');
|
||||||
const srcFolder = join(__dirname, 'src');
|
const srcFolder = join(__dirname, 'src');
|
||||||
const htmlFolder = join(srcFolder, 'html');
|
const htmlFolder = join(srcFolder, 'html');
|
||||||
const manifestFile = join(__dirname, 'manifest.json');
|
const manifestFolder = join(srcFolder, 'manifests');
|
||||||
|
|
||||||
const getConfigByBrowser = (isProd: boolean, browser: BrowserTypes): webpack.Configuration => {
|
const getConfigByBrowser = (isProd: boolean, browser: BrowserTypes): webpack.Configuration => {
|
||||||
const cfg: webpack.Configuration = {
|
const cfg: webpack.Configuration = {
|
||||||
@@ -43,7 +43,7 @@ const getConfigByBrowser = (isProd: boolean, browser: BrowserTypes): webpack.Con
|
|||||||
{ from: graphicsFolder, to: 'graphics' },
|
{ from: graphicsFolder, to: 'graphics' },
|
||||||
{ from: htmlFolder },
|
{ from: htmlFolder },
|
||||||
// TODO: Create a mechanism to have a firefox manifest vs chrome
|
// TODO: Create a mechanism to have a firefox manifest vs chrome
|
||||||
{ from: manifestFile },
|
{ from: join(manifestFolder, `${browser}.json`), to: 'manifest.json' },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
2
xclap.ts
2
xclap.ts
@@ -41,6 +41,8 @@ load({
|
|||||||
'test-jest': [exec('jest --clearCache'), exec('jest --verbose --coverage')],
|
'test-jest': [exec('jest --clearCache'), exec('jest --verbose --coverage')],
|
||||||
'test-jest-update': exec('jest -u'),
|
'test-jest-update': exec('jest -u'),
|
||||||
'test-js': 'phantomjs tests/run.js',
|
'test-js': 'phantomjs tests/run.js',
|
||||||
|
'watch-jest': exec('jest --watch'),
|
||||||
webpack: [exec('webpack --mode production')],
|
webpack: [exec('webpack --mode production')],
|
||||||
'webpack:dev': [exec('webpack --mode development')],
|
'webpack:dev': [exec('webpack --mode development')],
|
||||||
|
'webpack:watch': exec('webpack --mode development --watch'),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user