mirror of
https://gitlab.com/aviortheking/code-stats-vscode.git
synced 2025-04-22 02:42:11 +00:00
feat: Allow the plugin to work under the browser context
Signed-off-by: Avior <f.bouillon@aptatio.com>
This commit is contained in:
parent
e42e5271db
commit
cef14d1038
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
.DS_Store
|
||||
out
|
||||
node_modules
|
||||
.vscode-test/
|
||||
.vscode-test*/
|
||||
*.vsix
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -6,5 +6,8 @@
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
|
||||
// username for internal browser tests
|
||||
"codestats.username": "Aviortheking"
|
||||
}
|
14
esbuild.js
Normal file
14
esbuild.js
Normal file
@ -0,0 +1,14 @@
|
||||
const { NodeModulesPolyfillPlugin } = require('@esbuild-plugins/node-modules-polyfill')
|
||||
const { build } = require('esbuild')
|
||||
build({
|
||||
entryPoints: ['./src/code-stats.ts'],
|
||||
plugins: [NodeModulesPolyfillPlugin()],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
target: ['es2016', 'chrome90', 'firefox78', 'safari14', 'edge90'],
|
||||
external: ['vscode', 'node-fetch'],
|
||||
outfile: 'out/browser.js',
|
||||
format: 'cjs',
|
||||
platform: 'node'
|
||||
})
|
3268
package-lock.json
generated
3268
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -31,6 +31,7 @@
|
||||
"*"
|
||||
],
|
||||
"main": "./out/src/code-stats",
|
||||
"browser": "./out/browser",
|
||||
"extensionKind": [
|
||||
"ui",
|
||||
"workspace"
|
||||
@ -66,19 +67,25 @@
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "tsc -p ./",
|
||||
"compile": "tsc -watch -p ./"
|
||||
"compile": "tsc -watch -p ./",
|
||||
"test:browser": "vscode-test-web --extensionDevelopmentPath=. .",
|
||||
"compile:browser": "node esbuild.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^13.9.8",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/vscode": "^1.43.2",
|
||||
"@vscode/test-web": "^0.0.34",
|
||||
"esbuild": "^0.17.3",
|
||||
"growl": "^1.10.5",
|
||||
"mocha": "^7.1.1",
|
||||
"typescript": "^3.8.3",
|
||||
"vscode-test": "^1.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"lodash.template": "^4.5.0"
|
||||
"lodash.template": "^4.5.0",
|
||||
"node-fetch": "^2.6.8"
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,24 @@
|
||||
import { Pulse } from "./pulse";
|
||||
import { getISOTimestamp, getLanguageName } from "./utils";
|
||||
import * as axios from "axios";
|
||||
import type fetch from 'node-fetch'
|
||||
import { Pulse } from "./pulse"
|
||||
import { getISOTimestamp, getLanguageName } from "./utils"
|
||||
|
||||
// Please do not look at this
|
||||
let realFetch: typeof fetch
|
||||
// @ts-expect-error VSCode is not including fetch everytime
|
||||
if (typeof fetch === 'undefined') {
|
||||
realFetch = require('node-fetch')
|
||||
} else {
|
||||
// @ts-expect-error VSCode is not including fetch everytime
|
||||
realFetch = fetch
|
||||
}
|
||||
|
||||
export class CodeStatsAPI {
|
||||
private API_KEY = null;
|
||||
private USER_NAME = null;
|
||||
private UPDATE_URL = "https://codestats.net/api/";
|
||||
|
||||
private axios = null;
|
||||
private UPDATE_URL = "https://codestats.net/api";
|
||||
private headers: Record<string, string> = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
constructor(apiKey: string, apiURL: string, userName: string) {
|
||||
this.updateSettings(apiKey, apiURL, userName);
|
||||
@ -27,19 +38,12 @@ export class CodeStatsAPI {
|
||||
return;
|
||||
}
|
||||
|
||||
this.axios = axios.default.create({
|
||||
baseURL: this.UPDATE_URL,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
"X-API-Token": this.API_KEY,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
this.headers["X-API-Token"] = this.API_KEY
|
||||
}
|
||||
|
||||
public sendUpdate(pulse: Pulse): axios.AxiosPromise {
|
||||
public async sendUpdate(pulse: Pulse): Promise<any> {
|
||||
// If we did not have API key, don't try to update
|
||||
if (this.axios === null) {
|
||||
if (this.API_KEY === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -55,29 +59,28 @@ export class CodeStatsAPI {
|
||||
let json: string = JSON.stringify(data);
|
||||
console.log(`JSON: ${json}`);
|
||||
|
||||
return this.axios
|
||||
.post("my/pulses", json)
|
||||
.then(response => {
|
||||
console.log(response);
|
||||
try {
|
||||
const response = await realFetch(`${this.UPDATE_URL}/my/pulses`, {
|
||||
method: 'POST',
|
||||
body: json,
|
||||
headers: this.headers
|
||||
})
|
||||
.then(() => {
|
||||
pulse.reset();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
console.log(response)
|
||||
pulse.reset()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
public getProfile(): axios.AxiosPromise {
|
||||
return this.axios
|
||||
.get(`users/${this.USER_NAME}`)
|
||||
.then(response => {
|
||||
return response.data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
return null;
|
||||
});
|
||||
public async getProfile(): Promise<any> {
|
||||
try {
|
||||
const resp = await realFetch(`${this.UPDATE_URL}/users/${this.USER_NAME}`)
|
||||
const response = await resp.json()
|
||||
return response
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
"use strict";
|
||||
import { ExtensionContext } from "vscode";
|
||||
import { XpCounter } from "./xp-counter";
|
||||
import { ExtensionContext } from "vscode"
|
||||
import { XpCounter } from "./xp-counter"
|
||||
|
||||
export function activate(context: ExtensionContext): void {
|
||||
let controller: XpCounter = new XpCounter(context);
|
||||
|
@ -1,8 +1,7 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { CancellationToken, Event, ExtensionContext, TextDocumentContentProvider, Uri } from "vscode";
|
||||
import { CodeStatsAPI } from "./code-stats-api";
|
||||
import * as path from 'path';
|
||||
import { CancellationToken, Event, ExtensionContext, TextDocumentContentProvider, Uri, workspace } from "vscode"
|
||||
import { CodeStatsAPI } from "./code-stats-api"
|
||||
import profileHtmlEex from './profile.html.eex'
|
||||
|
||||
import template = require('lodash.template');
|
||||
|
||||
@ -19,8 +18,7 @@ export class ProfileProvider implements TextDocumentContentProvider {
|
||||
|
||||
provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable<string> {
|
||||
|
||||
if( token.isCancellationRequested )
|
||||
return;
|
||||
if (token.isCancellationRequested) return;
|
||||
|
||||
const LEVEL_FACTOR = 0.025;
|
||||
|
||||
@ -47,10 +45,9 @@ export class ProfileProvider implements TextDocumentContentProvider {
|
||||
}
|
||||
|
||||
function getSortedArray(obj: any): any[] {
|
||||
|
||||
let items = [];
|
||||
|
||||
for( let prop in obj) {
|
||||
for (const prop in obj) {
|
||||
let item = obj[prop];
|
||||
let percents = getLevelProgress(item.xps, item.new_xps);
|
||||
items.push(
|
||||
@ -68,14 +65,15 @@ export class ProfileProvider implements TextDocumentContentProvider {
|
||||
return items.sort( (a,b) => {return b.xp - a.xp;});
|
||||
}
|
||||
|
||||
return this.api.getProfile().then(profile => {
|
||||
return this.api.getProfile().then(async (profile) => {
|
||||
|
||||
if( profile === null )
|
||||
{
|
||||
return `<h1>Can't fetch profile. Please try again later</h1> Make sure <strong>codestats.username</strong> setting is set to correct user name.`;
|
||||
}
|
||||
|
||||
let htmlTemplate = fs.readFileSync(this.context.asAbsolutePath("assets/profile.html.eex"));
|
||||
// don't look at this file please
|
||||
let htmlTemplate = profileHtmlEex
|
||||
|
||||
profile["level"] = getLevel(profile["total_xp"]);
|
||||
|
||||
@ -89,10 +87,7 @@ export class ProfileProvider implements TextDocumentContentProvider {
|
||||
|
||||
let html = template(htmlTemplate);
|
||||
|
||||
const stylePath = Uri.file(path.join(this.context.extensionPath, 'assets', 'profile.css'));
|
||||
const styleSrc = stylePath.with({scheme: 'vscode-resource'});
|
||||
|
||||
return html({profile: profile, languages: languages, machines: machines, style: styleSrc});
|
||||
return html({profile: profile, languages: languages, machines: machines});
|
||||
|
||||
});
|
||||
}
|
||||
|
191
src/profile.html.eex.ts
Normal file
191
src/profile.html.eex.ts
Normal file
@ -0,0 +1,191 @@
|
||||
export default `
|
||||
<style>
|
||||
/* Dark theme color palette */
|
||||
.vscode-dark {
|
||||
--color-language: #dbd5b9;
|
||||
--color-primary: #E3C23D;
|
||||
--color-secondary: #5D5535;
|
||||
--color-bar-background: #303030;
|
||||
--color-bar-border: #424242;
|
||||
}
|
||||
|
||||
/* Light theme color palette */
|
||||
.vscode-light {
|
||||
--color-language: #1B2334;
|
||||
--color-primary: #5D78B3;
|
||||
--color-secondary: #354567;
|
||||
--color-bar-background: #939DB3;
|
||||
--color-bar-border: #697080;
|
||||
}
|
||||
|
||||
.profile {
|
||||
color: var(--color-language);
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
font-size: 75%;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.language-progress {
|
||||
float: left;
|
||||
position: relative;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.language-progress .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background-color: var(--color-bar-border);
|
||||
color: var(--color-language);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.language-progress:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.language-progress svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
circle {
|
||||
stroke-width: 5;
|
||||
fill:transparent;
|
||||
}
|
||||
|
||||
circle.backg {
|
||||
stroke: var(--color-bar-background);
|
||||
}
|
||||
|
||||
circle.newxp {
|
||||
stroke: var(--color-primary);
|
||||
}
|
||||
|
||||
circle.oldxp {
|
||||
stroke: var(--color-secondary);
|
||||
}
|
||||
|
||||
.language {
|
||||
padding-top: 2rem;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
color: var(--color-language);
|
||||
}
|
||||
|
||||
.language span {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.machines {
|
||||
clear: both;
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
.machine {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 20px;
|
||||
margin-bottom: 20px;
|
||||
background-color: var(--color-bar-background);
|
||||
border-radius: 4px;
|
||||
border: solid 1px;
|
||||
border-color: var(--color-bar-border);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.progress-bar-new {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
<div class="profile">
|
||||
<h3> \${profile.user} <sup>\${profile.level}</sup> \${profile.total_xp} xp
|
||||
<% if( profile.new_xp > 0 ) { %>
|
||||
<sup>+<%= profile.new_xp %></sup>
|
||||
<% } %>
|
||||
</h3>
|
||||
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style='width:\${profile.progress}%;'>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-new" style='width:\${profile.new_progress}%;'>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="languages">
|
||||
<% for( let l in languages) { %>
|
||||
<div class="language-progress">
|
||||
<span class="tooltiptext">
|
||||
<strong><%=languages[l].xp %> xp</strong>
|
||||
<% if( languages[l].new_xp > 0 ) { %>
|
||||
<sup>+<%= languages[l].new_xp %></sup>
|
||||
<% } %>
|
||||
</span>
|
||||
<svg viewBox="0 0 100 100">
|
||||
<circle class="backg" cx="50" cy="50" r="45" ></circle>
|
||||
<circle class="newxp" cx="50" cy="50" r="45" stroke-dasharray="282.6" stroke-dashoffset='\${ ((100-languages[l].progress) * 282.6 / 100) }'></circle>
|
||||
<circle class="oldxp" cx="50" cy="50" r="45" stroke-dasharray="282.6" stroke-dashoffset='\${ ((100-languages[l].progress + languages[l].new_progress) * 282.6 / 100) }'></circle>
|
||||
</svg>
|
||||
<div class="language">
|
||||
<%= languages[l].name %>
|
||||
<span>
|
||||
<%= languages[l].level %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<p/>
|
||||
|
||||
<div class="machines">
|
||||
<% for( let m in machines) { %>
|
||||
<div class="machine">
|
||||
<strong>
|
||||
<%= machines[m].name %>
|
||||
</strong>
|
||||
<sup> <%= machines[m].level %> </sup>
|
||||
<%= machines[m].xp %> xp
|
||||
<% if( machines[m].new_xp > 0 ) { %>
|
||||
<sup>+<%= machines[m].new_xp %></sup>
|
||||
<% } %>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style='width:\${machines[m].progress}%;'>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-new" style='width:\${machines[m].new_progress}%;'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<div>`
|
@ -1,22 +1,12 @@
|
||||
import * as path from 'path'
|
||||
import {
|
||||
Disposable,
|
||||
workspace,
|
||||
window,
|
||||
Uri,
|
||||
ViewColumn,
|
||||
commands,
|
||||
StatusBarItem,
|
||||
TextDocument,
|
||||
StatusBarAlignment,
|
||||
TextDocumentChangeEvent,
|
||||
WorkspaceConfiguration,
|
||||
ExtensionContext
|
||||
} from "vscode";
|
||||
import { Pulse } from "./pulse";
|
||||
import { CodeStatsAPI } from "./code-stats-api";
|
||||
import { ProfileProvider } from "./profile-provider";
|
||||
import * as path from 'path';
|
||||
|
||||
commands, Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem,
|
||||
TextDocument, TextDocumentChangeEvent, Uri,
|
||||
ViewColumn, window, workspace, WorkspaceConfiguration
|
||||
} from "vscode"
|
||||
import { CodeStatsAPI } from "./code-stats-api"
|
||||
import { ProfileProvider } from "./profile-provider"
|
||||
import { Pulse } from "./pulse"
|
||||
export class XpCounter {
|
||||
private combinedDisposable: Disposable;
|
||||
private statusBarItem: StatusBarItem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user