Skip to content

Commit

Permalink
More welcoming onboarding experience (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahnl authored Feb 22, 2024
1 parent 5898f14 commit 055c463
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 29 deletions.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# testaustime-vscode
# Testaustime Visual Studio Code extension

Official Testaustime VSCode extension.
<center>
<img src="media/hero.png" height="250" alt="Visual Studio Code editor on left side with extension enabled. Testaustime.fi statistics page on the right.">
</center>

Testaustime is the ultimate tool for tracking time of your coding sessions. Show the world how dedicated you are to your projects, now available for Visual Studio Code!

Sign up for an account at https://testaustime.fi/register.

## How to use?

You need to open the command palette `CTRL+SHIFT+P` and select `Testaustime: Set API key` and then enter your API key.
1. Open the command palette `CTRL+SHIFT+P`
2. Select `Testaustime: Log in using browser`
3. Log in

Green check mark in status bar indicates that Testaustime is actively tracking your coding session. Click on the indicator to open testaustime.fi in your browser.

You can configure more by searching `testaustime` in VSCode settings.
You can configure more by searching `testaustime` in Visual Studio Code settings.
Binary file added media/hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 37 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "testaustime",
"displayName": "Testaustime",
"version": "0.0.13",
"version": "0.1.0",
"description": "The VSCode extension of Testaustime",
"main": "dist/index.js",
"publisher": "testausserveri-ry",
Expand Down Expand Up @@ -48,7 +48,42 @@
"commands": [
{
"command": "testaustime.setapikey",
"title": "Testaustime: Set API key"
"title": "Testaustime: Log in manually using API key"
},
{
"command": "testaustime.openauthorize",
"title": "Testaustime: Log in using browser"
}
],
"walkthroughs": [
{
"id": "testaustime",
"title": "Get Started with Testaustime Visual Studio Code extension",
"description": "Log in to the extension and start tracking your coding time",
"steps": [
{
"id": "log-in",
"title": "Log In",
"description": "You must have a Testaustime account to track your coding time. You can register at https://testaustime.fi/register.\n[Log in with your browser](command:testaustime.openauthorize)\nIf the browser login does not work for you, you can manually retrieve a token from the testaustime.fi settings page and enter it in the add-on:\n[Log in using a token](command:testaustime.setapikey)",
"completionEvents": [
"onContext:testaustime.apikey"
],
"media": {
"image": "media/hero.png",
"altText": "Visual Studio Code editor on left side with extension enabled. Testaustime.fi statistics page on the right."
}
},
{
"id": "code",
"title": "Code",
"description": "Green check mark in status bar indicates that Testaustime is actively tracking your coding session. \nClick on the indicator to open testaustime.fi in your browser.",
"completionEvents": [],
"media": {
"image": "media/hero.png",
"altText": "Visual Studio Code editor on left side with extension enabled. Testaustime.fi statistics page on the right."
}
}
]
}
],
"configuration": {
Expand Down
96 changes: 73 additions & 23 deletions src/testaustime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,25 @@ import { createHash } from "crypto";

type Pointer = `${number},${number}`;

class TestaustimeUriHandler implements vscode.UriHandler {
private testaustime: Testaustime;

constructor(testaustime: Testaustime) {
this.testaustime = testaustime;
}

handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
if (uri.path != "/authorize") return;
const queryParameters = new URLSearchParams(uri.query);
const apikey = queryParameters.get('token');
if (!apikey) return;
this.testaustime.updateApikey(apikey);
}
}

class Testaustime {
apikey!: string;
username!: string;
endpoint: string;
config: vscode.WorkspaceConfiguration;
interval!: NodeJS.Timeout;
Expand All @@ -26,7 +43,6 @@ class Testaustime {

setApikeyInvalidText() {
this.statusbar.text = "Testaustime: API key invalid!";
this.statusbar.command = "testaustime.setapikey";
}

setActiveText() {
Expand All @@ -40,13 +56,11 @@ class Testaustime {
},
}).then(response => {
const totalSeconds = response.data.reduce((acc, cur) => acc + cur.duration, 0);
this.statusbar.tooltip = `You have coded ${prettyDuration(totalSeconds)} today`;
this.statusbar.tooltip = `Username: ${this.username}\nYou have coded ${prettyDuration(totalSeconds)} today`;
this.statusbar.text = `Testaustime: ${prettyDuration(totalSeconds)} ✅`;
}).catch(() => {
this.statusbar.text = "Testaustime: Error";
});

this.statusbar.command = undefined;
}

data() {
Expand Down Expand Up @@ -80,12 +94,30 @@ class Testaustime {
}).catch(() => null);
}

async validateApikey(key: string): Promise<boolean> {
return await axios.get(`${this.endpoint}/users/@me`, {
async validateApikey(key: string): Promise<MeModel | null> {
return await axios.get<MeModel>(`${this.endpoint}/users/@me`, {
headers: {
Authorization: `Bearer ${key}`
}
}).then(() => true).catch(() => false);
}).then(res => res.data).catch(() => null);
}

async updateApikey(key: string): Promise<boolean> {
const isValid = await this.validateApikey(key);

if (!isValid) {
this.setApikeyInvalidText();
vscode.window.showErrorMessage("Invalid API key!");
return false;
}

this.username = isValid.username;
this.apikey = key;
this.apikeyValid = true;
this.setActiveText();
this.context.globalState.update("apikey", key);
vscode.window.showInformationMessage("API key set!");
return true;
}

commands() {
Expand All @@ -96,23 +128,28 @@ class Testaustime {
});

if (result) {
const isValid = await this.validateApikey(result);

if (!isValid) {
this.setApikeyInvalidText();
vscode.window.showErrorMessage("Invalid API key!");
return;
}

this.apikey = result;
this.apikeyValid = true;
this.setActiveText();
this.context.globalState.update("apikey", result);
vscode.window.showInformationMessage("API key set!");
this.updateApikey(result);
}
});

const openauthorize = vscode.commands.registerCommand("testaustime.openauthorize", async () => {
const url = vscode.Uri.parse('https://testaustime.fi/authorize?editor=vscode');
vscode.env.openExternal(url);
});

const openstatusbar = vscode.commands.registerCommand("testaustime.openstatusbar", async () => {
let url;
if (!this.apikeyValid) {
url = vscode.Uri.parse('https://testaustime.fi/authorize?editor=vscode');
} else {
url = vscode.Uri.parse('https://testaustime.fi');
}
vscode.env.openExternal(url);
});

this.context.subscriptions.push(setapikey);
this.context.subscriptions.push(openauthorize);
this.context.subscriptions.push(openstatusbar);
}

async activate() {
Expand All @@ -121,13 +158,26 @@ class Testaustime {
this.commands();

this.statusbar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
this.setActiveText();

if (!(await this.validateApikey(this.apikey))) {
if (this.apikey == "") {
this.statusbar.text = "Testaustime: Click to Setup";
this.apikeyValid = false;
this.setApikeyInvalidText();
} else {
const isApikeyValid = await this.validateApikey(this.apikey);
if (!isApikeyValid) {
this.apikeyValid = false;
this.setApikeyInvalidText();
} else {
this.username = isApikeyValid.username;
}
}

this.setActiveText();

const uriHandler = new TestaustimeUriHandler(this);
this.context.subscriptions.push(vscode.window.registerUriHandler(uriHandler))

this.statusbar.command = "testaustime.openstatusbar"
this.statusbar.show();

this.interval = setInterval(() => {
Expand Down
7 changes: 7 additions & 0 deletions src/utils/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface MeModel {
id: number;
friend_code: string;
username: string;
registration_time: string;
is_public: boolean;
}

0 comments on commit 055c463

Please sign in to comment.