diff --git a/.env b/.env
index a107ed3..d3ebf86 100644
--- a/.env
+++ b/.env
@@ -1,4 +1,6 @@
-VITE_APP_APP_VERSION=10.0.12
+VITE_APP_APP_VERSION=10.0.14
+VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT=60
+VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT=5000
VITE_APP_TOKEN_CREATION_LINK=https://github.com/settings/tokens/new?scopes=repo&description=Github%20GLOC
VITE_APP_APPLICATION_REPO=https://github.com/kas-elvirov/gloc
VITE_APP_DEVELOPER_WEBSITE=https://kas-elvirov.com
diff --git a/.env.development b/.env.development
index a107ed3..d3ebf86 100644
--- a/.env.development
+++ b/.env.development
@@ -1,4 +1,6 @@
-VITE_APP_APP_VERSION=10.0.12
+VITE_APP_APP_VERSION=10.0.14
+VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT=60
+VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT=5000
VITE_APP_TOKEN_CREATION_LINK=https://github.com/settings/tokens/new?scopes=repo&description=Github%20GLOC
VITE_APP_APPLICATION_REPO=https://github.com/kas-elvirov/gloc
VITE_APP_DEVELOPER_WEBSITE=https://kas-elvirov.com
diff --git a/.env.production b/.env.production
index a107ed3..d3ebf86 100644
--- a/.env.production
+++ b/.env.production
@@ -1,4 +1,6 @@
-VITE_APP_APP_VERSION=10.0.12
+VITE_APP_APP_VERSION=10.0.14
+VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT=60
+VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT=5000
VITE_APP_TOKEN_CREATION_LINK=https://github.com/settings/tokens/new?scopes=repo&description=Github%20GLOC
VITE_APP_APPLICATION_REPO=https://github.com/kas-elvirov/gloc
VITE_APP_DEVELOPER_WEBSITE=https://kas-elvirov.com
diff --git a/manifest.json b/manifest.json
index f1c9025..50c99fc 100644
--- a/manifest.json
+++ b/manifest.json
@@ -4,7 +4,7 @@
"short_name": "Gloc",
"author": "Kas Elvirov",
"description": "Github Gloc - counts locs on GitHub pages",
- "version": "10.0.12",
+ "version": "10.0.14",
"action": {
"default_icon": {
"16": "img/icon16.png",
diff --git a/package-lock.json b/package-lock.json
index ecaf611..d1d3735 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "gloc",
- "version": "10.0.12",
+ "version": "10.0.14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gloc",
- "version": "10.0.12",
+ "version": "10.0.14",
"license": "GPL-2.0",
"dependencies": {
"@crxjs/vite-plugin": "^2.0.0-beta.26",
diff --git a/package.json b/package.json
index 100aeee..41c5b15 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gloc",
- "version": "10.0.12",
+ "version": "10.0.14",
"engines": {
"node": "16.19.0",
"npm": "0.39.3"
diff --git a/scripts/upAppVersion.js b/scripts/upAppVersion.js
index 462e185..0a95cd9 100644
--- a/scripts/upAppVersion.js
+++ b/scripts/upAppVersion.js
@@ -10,6 +10,8 @@ const manifest = require('../manifest.json');
const env = '.env';
let parsedEnv = envfile.parse(env);
parsedEnv.VITE_APP_APP_VERSION = package.version;
+parsedEnv.VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT = 60;
+parsedEnv.VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT = 5000;
parsedEnv.VITE_APP_TOKEN_CREATION_LINK = 'https://github.com/settings/tokens/new?scopes=repo&description=Github%20GLOC';
parsedEnv.VITE_APP_APPLICATION_REPO = 'https://github.com/kas-elvirov/gloc';
parsedEnv.VITE_APP_DEVELOPER_WEBSITE = 'https://kas-elvirov.com';
@@ -22,6 +24,8 @@ fs.writeFileSync('./.env', envfile.stringify(parsedEnv));
const envDevelopment = '.env.development';
let parsedEnvDevelopment = envfile.parse(envDevelopment);
parsedEnvDevelopment.VITE_APP_APP_VERSION = package.version;
+parsedEnvDevelopment.VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT = 60;
+parsedEnvDevelopment.VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT = 5000;
parsedEnvDevelopment.VITE_APP_TOKEN_CREATION_LINK =
'https://github.com/settings/tokens/new?scopes=repo&description=Github%20GLOC';
parsedEnvDevelopment.VITE_APP_APPLICATION_REPO = 'https://github.com/kas-elvirov/gloc';
@@ -35,6 +39,8 @@ fs.writeFileSync('./.env.development', envfile.stringify(parsedEnvDevelopment));
const envProduction = '.env.production';
let parsedEnvProduction = envfile.parse(envProduction);
parsedEnvProduction.VITE_APP_APP_VERSION = package.version;
+parsedEnvProduction.VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT = 60;
+parsedEnvProduction.VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT = 5000;
parsedEnvProduction.VITE_APP_TOKEN_CREATION_LINK =
'https://github.com/settings/tokens/new?scopes=repo&description=Github%20GLOC';
parsedEnvProduction.VITE_APP_APPLICATION_REPO = 'https://github.com/kas-elvirov/gloc';
diff --git a/src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel.tsx b/src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel.tsx
new file mode 100644
index 0000000..1766cd1
--- /dev/null
+++ b/src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel.tsx
@@ -0,0 +1,27 @@
+import { FC } from 'react';
+
+import { Box, LinearProgress, Typography } from '@mui/material';
+
+import { LinearProgressWithLabelProps } from './LinearProgressWithLabel.types';
+
+export const LinearProgressWithLabel: FC<
+ LinearProgressWithLabelProps
+> = props => {
+ return (
+
+
+
+
+
+ {`${Math.round(props.value)}%`}
+
+
+ );
+};
diff --git a/src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel.types.ts b/src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel.types.ts
new file mode 100644
index 0000000..df47aac
--- /dev/null
+++ b/src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel.types.ts
@@ -0,0 +1,5 @@
+import { LinearProgressProps } from '@mui/material';
+
+export type LinearProgressWithLabelProps = LinearProgressProps & {
+ value: number;
+};
diff --git a/src/_modules/Content/containers/LocIndicator/LocIndicator.utils.ts b/src/_modules/Content/containers/LocIndicator/LocIndicator.utils.ts
index 8756c0c..76a2712 100644
--- a/src/_modules/Content/containers/LocIndicator/LocIndicator.utils.ts
+++ b/src/_modules/Content/containers/LocIndicator/LocIndicator.utils.ts
@@ -25,12 +25,6 @@ const getErrorMessage = ({
let isError = false;
const castedError = error as ErrorType;
- console.group('getErrorMessage');
- console.log('data', data);
- console.log('error', error);
- console.log('isObjectValid(data)', isObjectValid(data));
- console.groupEnd();
-
if (castedError?.status === 202 && !isObjectValid(data)) {
return {
errorMessage: 'Needs to retry',
diff --git a/src/_modules/Popup/components/PopupPage/PopupPage.tsx b/src/_modules/Popup/components/PopupPage/PopupPage.tsx
index 5876d99..7d483c3 100644
--- a/src/_modules/Popup/components/PopupPage/PopupPage.tsx
+++ b/src/_modules/Popup/components/PopupPage/PopupPage.tsx
@@ -1,4 +1,10 @@
+/* eslint-disable max-len */
+import { LinearProgressWithLabel } from 'src/_lib/components/LinearProgressWithLabel/LinearProgressWithLabel';
import { SYSTEM_DEFAULTS } from 'src/_shared/consts/defaults';
+import {
+ TrackEventsService,
+ TrackEventsState,
+} from 'src/_shared/services/TrackEvent/TrackEvent';
import React, { FC, useEffect } from 'react';
@@ -31,14 +37,50 @@ export const PopupPage: FC = () => {
SYSTEM_DEFAULTS.STORAGE.APP_MODE.DEFAULT_VALUE,
);
+ const [freeHourlyApiUsage, setFreeHourlyApiStatPercent] = React.useState(0);
+ const [tokenizedHourlyApiUsage, setTokenizedHourlyApiStatPercent] =
+ React.useState(0);
+
useEffect(() => {
chrome?.storage?.sync?.get(
{
[SYSTEM_DEFAULTS.STORAGE.APP_MODE.KEY]:
SYSTEM_DEFAULTS.STORAGE.APP_MODE.DEFAULT_VALUE,
+ [SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY]:
+ SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.DEFAULT_VALUE,
},
result => {
setAppStatus(result[SYSTEM_DEFAULTS.STORAGE.APP_MODE.KEY]);
+
+ if (
+ (result[SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY] as TrackEventsState)[
+ SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.REQUESTS_STAT
+ ]
+ ) {
+ setFreeHourlyApiStatPercent(
+ TrackEventsService.calculatePercentOfHourlyEventUsage({
+ state: result[
+ SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY
+ ] as TrackEventsState,
+ eventName: SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.REQUESTS_STAT,
+ limit: Number(
+ import.meta.env.VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT,
+ ),
+ }),
+ );
+
+ setTokenizedHourlyApiStatPercent(
+ TrackEventsService.calculatePercentOfHourlyEventUsage({
+ state: result[
+ SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY
+ ] as TrackEventsState,
+ eventName: SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.REQUESTS_STAT,
+ limit: Number(
+ import.meta.env.VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT,
+ ),
+ }),
+ );
+ }
},
);
}, []);
@@ -102,6 +144,26 @@ export const PopupPage: FC = () => {
title={`App is ${isAppEnabled ? 'enabled' : 'disabled'}`}
/>
+
+
+ Free requests (GitHub limit -{' '}
+ {Number(import.meta.env.VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT)} per
+ hour)
+
+
+
+
+
+
+
+ Tokenised requests (GitHub limit -{' '}
+ {Number(import.meta.env.VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT)}{' '}
+ per hour)
+
+
+
+
+
diff --git a/src/_shared/api/github/endpoints.ts b/src/_shared/api/github/endpoints.ts
index 37b9e03..22058b1 100644
--- a/src/_shared/api/github/endpoints.ts
+++ b/src/_shared/api/github/endpoints.ts
@@ -1,3 +1,6 @@
+import { SYSTEM_DEFAULTS } from 'src/_shared/consts/defaults';
+import { TrackEventsService } from 'src/_shared/services/TrackEvent/TrackEvent';
+
import {
BaseQueryFn,
FetchBaseQueryError,
@@ -76,6 +79,11 @@ export const githubApi = createApi({
Accept: 'application/vnd.github.v3+json',
},
}),
+ onQueryStarted: () => {
+ TrackEventsService.trackEvent(
+ SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.REQUESTS_STAT,
+ );
+ },
extraOptions: { maxRetries: 5 },
}),
getAllUserRepos: builder.query<
diff --git a/src/_shared/consts/defaults.ts b/src/_shared/consts/defaults.ts
index 176b00e..e556aab 100644
--- a/src/_shared/consts/defaults.ts
+++ b/src/_shared/consts/defaults.ts
@@ -13,6 +13,11 @@ export const SYSTEM_DEFAULTS = {
KEY: 'x-github-token',
DEFAULT_VALUE: '',
},
+ EVENTS_STAT: {
+ REQUESTS_STAT: 'getRepoCodeFrequency',
+ KEY: 'x-events-stat',
+ DEFAULT_VALUE: {},
+ },
},
DEBOUNCE: {
300: 300,
diff --git a/src/_shared/services/TrackEvent/TrackEvent.ts b/src/_shared/services/TrackEvent/TrackEvent.ts
new file mode 100644
index 0000000..8746e97
--- /dev/null
+++ b/src/_shared/services/TrackEvent/TrackEvent.ts
@@ -0,0 +1,106 @@
+import { SYSTEM_DEFAULTS } from '../../consts/defaults';
+
+export type TrackEventsEventName = string;
+/**
+ * YYYY-MM-DD format
+ */
+export type TrackEventsCurrentDate = string;
+
+export type TrackEventsState = Record<
+ TrackEventsEventName,
+ {
+ hourly: [
+ number,
+ number,
+ number,
+ number,
+ number,
+
+ number,
+ number,
+ number,
+ number,
+ number,
+
+ number,
+ number,
+ number,
+ number,
+ number,
+
+ number,
+ number,
+ number,
+ number,
+ number,
+
+ number,
+ number,
+ number,
+ number,
+ ];
+ daily: Record;
+ }
+>;
+
+class TrackEvents {
+ state: TrackEventsState = {};
+
+ trackEvent = (eventName: TrackEventsEventName) => {
+ chrome?.storage?.sync?.get(
+ {
+ [SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY]:
+ SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.DEFAULT_VALUE,
+ },
+ result => {
+ const now = new Date();
+
+ const currentHour = now.getHours();
+ const currentDate = now.toISOString().split('T')[0];
+
+ const state = {
+ ...result[SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY],
+ } as TrackEventsState;
+
+ if (!state[eventName]) {
+ state[eventName] = {
+ // @ts-expect-error Everything is okay (array sizes are compatible. I think)
+ hourly: Array(25).fill(0),
+ daily: {},
+ };
+ }
+
+ state[eventName].hourly[currentHour]++;
+
+ if (!state[eventName].daily[currentDate]) {
+ state[eventName].daily[currentDate] = 0;
+ }
+
+ state[eventName].daily[currentDate]++;
+
+ chrome.storage?.sync?.set?.(
+ { [SYSTEM_DEFAULTS.STORAGE.EVENTS_STAT.KEY]: state },
+ () => {},
+ );
+ },
+ );
+ };
+
+ calculatePercentOfHourlyEventUsage = ({
+ state,
+ eventName,
+ limit,
+ }: {
+ state: TrackEventsState;
+ eventName: TrackEventsEventName;
+ limit: number;
+ }): number => {
+ const now = new Date();
+
+ const currentHour = now.getHours();
+
+ return state[eventName].hourly[currentHour] / (limit / 100);
+ };
+}
+
+export const TrackEventsService = new TrackEvents();
diff --git a/src/environment.d.ts b/src/environment.d.ts
index f951f13..100b5dd 100644
--- a/src/environment.d.ts
+++ b/src/environment.d.ts
@@ -5,6 +5,10 @@ declare global {
*/
VITE_APP_APP_VERSION: string;
+ VITE_APP_GITHUB_API_FREE_HOURLY_LIMIT: string;
+
+ VITE_APP_GITHUB_API_TOKENIZED_HOURLY_LIMIT: string;
+
/**
* # Base of chrome extension settings link
*/