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/jest',
|
||||
'kentcdodds/possible-errors',
|
||||
'plugin:jest-dom/recommended',
|
||||
'plugin:testing-library/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:import/errors',
|
||||
'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": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
|
||||
@@ -5960,6 +6098,12 @@
|
||||
"@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": {
|
||||
"version": "6.4.0",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "3.11.1",
|
||||
"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": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||
@@ -20413,6 +20572,12 @@
|
||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
|
||||
@@ -38,7 +38,12 @@
|
||||
"webextension-polyfill-ts": "^0.22.0"
|
||||
},
|
||||
"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/classnames": "^2.2.11",
|
||||
"@types/copy-webpack-plugin": "^6.4.0",
|
||||
"@types/firefox-webext-browser": "^82.0.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
@@ -60,9 +65,11 @@
|
||||
"eslint-config-kentcdodds": "^17.3.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jest-dom": "^3.6.5",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-sort-keys-fix": "^1.1.1",
|
||||
"eslint-plugin-testing-library": "^3.10.1",
|
||||
"eslint-plugin-typescript-sort-keys": "^1.5.0",
|
||||
"gulp": "^3.9.1",
|
||||
"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
|
||||
*/
|
||||
type ApiStates = 'allGood' | 'notLogging' | 'notSignedIn' | 'blacklisted' | 'whitelisted';
|
||||
export type ApiStates = 'allGood' | 'notLogging' | 'notSignedIn' | 'blacklisted' | 'whitelisted';
|
||||
/**
|
||||
* Supported logging style
|
||||
*/
|
||||
type LoggingStyle = 'whitelist' | 'blacklist';
|
||||
export type LoggingStyle = 'whitelist' | 'blacklist';
|
||||
/**
|
||||
* Logging type
|
||||
*/
|
||||
type LoggingType = 'domain' | 'url';
|
||||
type SuccessOrFailType = 'success' | 'danger';
|
||||
export type LoggingType = 'domain' | 'url';
|
||||
export type SuccessOrFailType = 'success' | 'danger';
|
||||
/**
|
||||
* Predefined alert type and text for success and failure.
|
||||
*/
|
||||
@@ -31,10 +31,10 @@ interface SuccessOrFailAlert {
|
||||
* Different colors for different states of the extension
|
||||
*/
|
||||
interface Colors {
|
||||
allGood: string;
|
||||
lightTheme: string;
|
||||
notLogging: string;
|
||||
notSignedIn: string;
|
||||
allGood: '';
|
||||
lightTheme: 'white';
|
||||
notLogging: 'gray';
|
||||
notSignedIn: 'red';
|
||||
}
|
||||
/**
|
||||
* 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 srcFolder = join(__dirname, 'src');
|
||||
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 cfg: webpack.Configuration = {
|
||||
@@ -43,7 +43,7 @@ const getConfigByBrowser = (isProd: boolean, browser: BrowserTypes): webpack.Con
|
||||
{ from: graphicsFolder, to: 'graphics' },
|
||||
{ from: htmlFolder },
|
||||
// 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-update': exec('jest -u'),
|
||||
'test-js': 'phantomjs tests/run.js',
|
||||
'watch-jest': exec('jest --watch'),
|
||||
webpack: [exec('webpack --mode production')],
|
||||
'webpack:dev': [exec('webpack --mode development')],
|
||||
'webpack:watch': exec('webpack --mode development --watch'),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user