chore: implement new ts components
This commit is contained in:
2501
package-lock.json
generated
2501
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -95,8 +95,8 @@
|
|||||||
"jsxhint": "^0.15.1",
|
"jsxhint": "^0.15.1",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"lint-staged": "^13.1.0",
|
"lint-staged": "^13.1.0",
|
||||||
"mocha": "^5.0.0",
|
"mocha": "^10.2.0",
|
||||||
"mocha-sinon": "^2.0.0",
|
"mocha-sinon": "^2.1.2",
|
||||||
"node-gyp": "^8.3.0",
|
"node-gyp": "^8.3.0",
|
||||||
"prettier": "^2.8.2",
|
"prettier": "^2.8.2",
|
||||||
"prettier-plugin-packagejson": "^2.3.0",
|
"prettier-plugin-packagejson": "^2.3.0",
|
||||||
@@ -104,9 +104,9 @@
|
|||||||
"remote-redux-devtools": "^0.5.16",
|
"remote-redux-devtools": "^0.5.16",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"shelljs": "^0.8.5",
|
"shelljs": "^0.8.5",
|
||||||
"sinon": "^4.2.2",
|
"sinon": "^15.0.1",
|
||||||
"sinon-chai": "^2.8.0",
|
"sinon-chai": "^3.7.0",
|
||||||
"sinon-chrome": "^2.2.4",
|
"sinon-chrome": "^3.0.1",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"ts-loader": "^9.4.2",
|
"ts-loader": "^9.4.2",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
|
|||||||
169
src/components/NavBar.tsx
Normal file
169
src/components/NavBar.tsx
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import config from '../config/config';
|
||||||
|
import apiKeyInvalid from '../utils/apiKey';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
loggedIn: boolean;
|
||||||
|
user: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NavBar({ loggedIn, user }: Props) {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
apiKey: '',
|
||||||
|
apiKeyError: '',
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
const fetch = async () => {
|
||||||
|
const { apiKey } = await browser.storage.sync.get({ apiKey: config.apiKey });
|
||||||
|
setState({ ...state, apiKey });
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const signedInAs = () => {
|
||||||
|
if (loggedIn === true) {
|
||||||
|
return (
|
||||||
|
<p className="navbar-text">
|
||||||
|
Signed in as <b>{user.full_name}</b>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const customRules = () => {
|
||||||
|
if (loggedIn === true) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://wakatime.com/settings/rules" rel="noreferrer">
|
||||||
|
<i className="fa fa-fw fa-filter"></i>
|
||||||
|
Custom Rules
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dashboard = () => {
|
||||||
|
if (loggedIn === true) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://wakatime.com/dashboard" rel="noreferrer">
|
||||||
|
<i className="fa fa-fw fa-tachometer"></i>
|
||||||
|
Dashboard
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="navbar navbar-default" role="navigation">
|
||||||
|
<div className="container-fluid">
|
||||||
|
<div className="navbar-header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="navbar-toggle collapsed"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#bs-example-navbar-collapse-1"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Toggle navigation</span>
|
||||||
|
<i className="fa fa-fw fa-cogs"></i>
|
||||||
|
</button>
|
||||||
|
<a target="_blank" className="navbar-brand" href="https://wakatime.com" rel="noreferrer">
|
||||||
|
WakaTime
|
||||||
|
<img src="graphics/wakatime-logo-48.png" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||||
|
{signedInAs()}
|
||||||
|
<ul className="nav navbar-nav">
|
||||||
|
{customRules()}
|
||||||
|
{dashboard()}
|
||||||
|
<li className="dropdown">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="dropdown-toggle"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
role="button"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<i className="fa fa-fw fa-info"></i>
|
||||||
|
About
|
||||||
|
<span className="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul className="dropdown-menu" role="menu">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/wakatime/chrome-wakatime/issues"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<i className="fa fa-fw fa-bug"></i>
|
||||||
|
Report an Issue
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/wakatime/chrome-wakatime"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<i className="fa fa-fw fa-github"></i>
|
||||||
|
View on GitHub
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div className="container-fluid">
|
||||||
|
{state.apiKeyError && (
|
||||||
|
<div className="alert alert-danger" role="alert">
|
||||||
|
{state.apiKeyError}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="input-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="API key"
|
||||||
|
value={state.apiKey}
|
||||||
|
onChange={(e) => {
|
||||||
|
const key = e.target.value;
|
||||||
|
const isApiKeyInvalid = apiKeyInvalid(key);
|
||||||
|
setState({ ...state, apiKey: key, apiKeyError: isApiKeyInvalid });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="input-group-btn">
|
||||||
|
<button
|
||||||
|
className={`btn btn-default ${state.loading ? 'disabled' : ''}`}
|
||||||
|
disabled={state.loading}
|
||||||
|
type="button"
|
||||||
|
data-loading-text="Loading..."
|
||||||
|
onClick={async () => {
|
||||||
|
if (state.apiKeyError === '' && state.apiKey !== '') {
|
||||||
|
setState({ ...state, loading: true });
|
||||||
|
await browser.storage.sync.set({ apiKey: state.apiKey });
|
||||||
|
setState({ ...state, loading: false });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
126
src/components/WakaTime.tsx
Normal file
126
src/components/WakaTime.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import WakaTimeCore from '../core/WakaTimeCore';
|
||||||
|
import config from '../config/config';
|
||||||
|
import changeExtensionState from '../utils/changeExtensionState';
|
||||||
|
import NavBar from './NavBar';
|
||||||
|
import MainList from './MainList';
|
||||||
|
|
||||||
|
const API_KEY = 'waka_3766d693-bff3-4c63-8bf5-b439f3e12301';
|
||||||
|
|
||||||
|
export default function WakaTime() {
|
||||||
|
const defaultState = {
|
||||||
|
apiKey: '',
|
||||||
|
loading: true,
|
||||||
|
loggedIn: false,
|
||||||
|
loggingEnabled: config.loggingEnabled,
|
||||||
|
totalTimeLoggedToday: '0 minutes',
|
||||||
|
user: {
|
||||||
|
email: '',
|
||||||
|
full_name: '',
|
||||||
|
photo: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const [state, setState] = useState(defaultState);
|
||||||
|
|
||||||
|
const fetchUserData = async () => {
|
||||||
|
// await browser.storage.sync.set({ apiKey: API_KEY });
|
||||||
|
const { apiKey } = await browser.storage.sync.get({ apiKey: config.apiKey });
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
changeExtensionState('notSignedIn');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await WakaTimeCore.checkAuth(apiKey as string);
|
||||||
|
const items = await browser.storage.sync.get({ loggingEnabled: config.loggingEnabled });
|
||||||
|
|
||||||
|
if (items.loggingEnabled === true) {
|
||||||
|
changeExtensionState('allGood');
|
||||||
|
} else {
|
||||||
|
changeExtensionState('notLogging');
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalTimeLoggedToday = await WakaTimeCore.getTotalTimeLoggedToday(apiKey as string);
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
apiKey,
|
||||||
|
loading: false,
|
||||||
|
loggedIn: true,
|
||||||
|
loggingEnabled: items.loggingEnabled,
|
||||||
|
totalTimeLoggedToday: totalTimeLoggedToday.text,
|
||||||
|
user: {
|
||||||
|
email: data.email,
|
||||||
|
full_name: data.full_name,
|
||||||
|
photo: data.photo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await WakaTimeCore.recordHeartbeat();
|
||||||
|
} catch (err) {
|
||||||
|
changeExtensionState('notSignedIn');
|
||||||
|
setState({ ...defaultState, loading: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchUserData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const disableLogging = () => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
loggingEnabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
changeExtensionState('notLogging');
|
||||||
|
|
||||||
|
browser.storage.sync.set({
|
||||||
|
loggingEnabled: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const enableLogging = () => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
loggingEnabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
changeExtensionState('allGood');
|
||||||
|
|
||||||
|
browser.storage.sync.set({
|
||||||
|
loggingEnabled: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoutUser = async () => {
|
||||||
|
await browser.storage.sync.set({ apiKey: '' });
|
||||||
|
|
||||||
|
setState(defaultState);
|
||||||
|
|
||||||
|
changeExtensionState('notSignedIn');
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (state.loading === true) {
|
||||||
|
// return <div>Loading</div>
|
||||||
|
// }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<NavBar user={state.user} loggedIn={state.loggedIn} />
|
||||||
|
<div className="container">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
<MainList
|
||||||
|
disableLogging={disableLogging}
|
||||||
|
enableLogging={enableLogging}
|
||||||
|
loggingEnabled={state.loggingEnabled}
|
||||||
|
totalTimeLoggedToday={state.totalTimeLoggedToday}
|
||||||
|
logoutUser={logoutUser}
|
||||||
|
loggedIn={state.loggedIn}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ describe('wakatime config', () => {
|
|||||||
"type": "success",
|
"type": "success",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"apiKey": "",
|
||||||
"colors": {
|
"colors": {
|
||||||
"allGood": "",
|
"allGood": "",
|
||||||
"lightTheme": "white",
|
"lightTheme": "white",
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ interface Tooltips {
|
|||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
alert: Alert;
|
alert: Alert;
|
||||||
|
/**
|
||||||
|
* API key use to query wakatime api
|
||||||
|
*/
|
||||||
|
apiKey: '';
|
||||||
colors: Colors;
|
colors: Colors;
|
||||||
/**
|
/**
|
||||||
* Url from which to detect if the user is logged in
|
* Url from which to detect if the user is logged in
|
||||||
@@ -104,6 +108,8 @@ const config: Config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apiKey: '',
|
||||||
|
|
||||||
colors: {
|
colors: {
|
||||||
allGood: '',
|
allGood: '',
|
||||||
lightTheme: 'white',
|
lightTheme: 'white',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Tabs } from 'webextension-polyfill-ts';
|
import { Tabs } from 'webextension-polyfill-ts';
|
||||||
import { User } from '../types/user';
|
import { AxiosUserResponse, User } from '../types/user';
|
||||||
import config from '../config/config';
|
import config from '../config/config';
|
||||||
import { SummariesPayload, GrandTotal } from '../types/summaries';
|
import { SummariesPayload, GrandTotal } from '../types/summaries';
|
||||||
|
|
||||||
@@ -10,15 +10,18 @@ class WakaTimeCore {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.tabsWithDevtoolsOpen = [];
|
this.tabsWithDevtoolsOpen = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
setTabsWithDevtoolsOpen(tabs: Tabs.Tab[]): void {
|
setTabsWithDevtoolsOpen(tabs: Tabs.Tab[]): void {
|
||||||
this.tabsWithDevtoolsOpen = tabs;
|
this.tabsWithDevtoolsOpen = tabs;
|
||||||
}
|
}
|
||||||
async getTotalTimeLoggedToday(): Promise<GrandTotal> {
|
|
||||||
|
async getTotalTimeLoggedToday(api_key = ''): Promise<GrandTotal> {
|
||||||
const today = moment().format('YYYY-MM-DD');
|
const today = moment().format('YYYY-MM-DD');
|
||||||
const summariesAxiosPayload: AxiosResponse<SummariesPayload> = await axios.get(
|
const summariesAxiosPayload: AxiosResponse<SummariesPayload> = await axios.get(
|
||||||
config.summariesApiUrl,
|
config.summariesApiUrl,
|
||||||
{
|
{
|
||||||
data: {
|
params: {
|
||||||
|
api_key,
|
||||||
end: today,
|
end: today,
|
||||||
start: today,
|
start: today,
|
||||||
},
|
},
|
||||||
@@ -26,9 +29,23 @@ class WakaTimeCore {
|
|||||||
);
|
);
|
||||||
return summariesAxiosPayload.data.data[0].grand_total;
|
return summariesAxiosPayload.data.data[0].grand_total;
|
||||||
}
|
}
|
||||||
async checkAuth(): Promise<User> {
|
|
||||||
const userPayload: AxiosResponse<User> = await axios.get(config.currentUserApiUrl);
|
async checkAuth(api_key = ''): Promise<User> {
|
||||||
return userPayload.data;
|
console.log('api_keyapi_keyapi_keyapi_key', api_key);
|
||||||
|
const userPayload: AxiosResponse<AxiosUserResponse> = await axios.get(
|
||||||
|
config.currentUserApiUrl,
|
||||||
|
{ params: { api_key } },
|
||||||
|
);
|
||||||
|
return userPayload.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async recordHeartbeat(): Promise<void> {
|
||||||
|
const items = await browser.storage.sync.get({
|
||||||
|
blacklist: '',
|
||||||
|
loggingEnabled: config.loggingEnabled,
|
||||||
|
loggingStyle: config.loggingStyle,
|
||||||
|
whitelist: '',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,2 @@
|
|||||||
// // Create a connection to the background page
|
|
||||||
// const 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,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
browser.devtools.panels.create('Wakatime', 'test.png', 'WakatimeDevPanel.html');
|
browser.devtools.panels.create('Wakatime', 'test.png', 'WakatimeDevPanel.html');
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
import WakaTime from './components/WakaTime';
|
||||||
import createStore from './stores/createStore';
|
import createStore from './stores/createStore';
|
||||||
import checkCurrentUser from './utils/checkCurrentUser';
|
import checkCurrentUser from './utils/checkCurrentUser';
|
||||||
const container = document.getElementById('wakatime');
|
|
||||||
|
|
||||||
|
/* This is a fix for Bootstrap requiring jQuery */
|
||||||
|
global.jQuery = require('jquery');
|
||||||
|
require('bootstrap');
|
||||||
|
|
||||||
|
const container = document.getElementById('wakatime');
|
||||||
|
const root = createRoot(container!);
|
||||||
const store = createStore('WakaTime-Options');
|
const store = createStore('WakaTime-Options');
|
||||||
checkCurrentUser(store)(30 * 1000);
|
checkCurrentUser(store)(30 * 1000);
|
||||||
|
|
||||||
@@ -12,10 +18,9 @@ const openOptions = async (): Promise<void> => {
|
|||||||
await browser.runtime.openOptionsPage();
|
await browser.runtime.openOptionsPage();
|
||||||
};
|
};
|
||||||
|
|
||||||
ReactDOM.render(
|
root.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<h1>POPUP GO HERE</h1>
|
<WakaTime />
|
||||||
<div onClick={openOptions}>Open options</div>
|
<div onClick={openOptions}>Open options</div>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
container,
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ export interface UserPayload {
|
|||||||
data: User;
|
data: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AxiosUserResponse {
|
||||||
|
data: User;
|
||||||
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
bio: null;
|
bio: null;
|
||||||
color_scheme: string;
|
color_scheme: string;
|
||||||
|
|||||||
10
src/utils/apiKey.ts
Normal file
10
src/utils/apiKey.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export default function apiKeyInvalid(key?: string): string {
|
||||||
|
const err = 'Invalid api key... check https://wakatime.com/settings for your key';
|
||||||
|
if (!key) return err;
|
||||||
|
const re = new RegExp(
|
||||||
|
'^(waka_)?[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$',
|
||||||
|
'i',
|
||||||
|
);
|
||||||
|
if (!re.test(key)) return err;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user