From 74c11dc53c737ea0932f879a50227f27bce97a34 Mon Sep 17 00:00:00 2001 From: ysandler Date: Fri, 18 Sep 2020 10:05:38 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=20refact:=20commands=20imported=20?= =?UTF-8?q?to=20extention.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 18 +++++-- src/UseCases/Commands/Logout.ts | 15 ++++++ src/UseCases/Commands/PunchTime.ts | 45 ++++++++++++++++++ .../Commands/SetUserAuthentication.ts | 42 ++++++++++++++++ src/UseCases/getUser.ts | 15 +++--- src/extension.ts | 35 ++++++-------- src/media/harvestLogo.png | Bin 0 -> 11515 bytes 7 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 src/UseCases/Commands/Logout.ts create mode 100644 src/UseCases/Commands/PunchTime.ts create mode 100644 src/UseCases/Commands/SetUserAuthentication.ts create mode 100644 src/media/harvestLogo.png diff --git a/package.json b/package.json index 70aec81..1716df9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,9 @@ "name": "harvest-vscode", "displayName": "harvest-vscode", "description": "Varvest Integration to VS Code", - "version": "0.0.1", + "version": "0.1.0", + "publisher": "Tzedakah", + "repository": "https://github.com/joshuashoemaker/harvest-vscode", "engines": { "vscode": "^1.49.0" }, @@ -10,14 +12,22 @@ "Other" ], "activationEvents": [ - "onCommand:harvest-vscode.helloWorld" + "*" ], "main": "./out/extension.js", "contributes": { "commands": [ { - "command": "harvest-vscode.helloWorld", - "title": "Hello World" + "command": "harvest-vscode.punchTime", + "title": "Harvest: Punch Time" + }, + { + "command": "harvest-vscode.login", + "title": "Harvest: Login" + }, + { + "command": "harvest-vscode.logout", + "title": "Harvest: Logout" } ] }, diff --git a/src/UseCases/Commands/Logout.ts b/src/UseCases/Commands/Logout.ts new file mode 100644 index 0000000..352f23c --- /dev/null +++ b/src/UseCases/Commands/Logout.ts @@ -0,0 +1,15 @@ +import * as vscode from "vscode"; +import Harvest from "../../Entities/Harvest" +import User from "../../Entities/User" + +function Logout (context: vscode.ExtensionContext): vscode.Disposable { + return vscode.commands.registerCommand('harvest-vscode.logout', async () => { + new Harvest().destructor() + new User().destructor() + + await context.globalState.update('accountId', '') + await context.globalState.update('accessToken', '') + }) +} + +export default Logout \ No newline at end of file diff --git a/src/UseCases/Commands/PunchTime.ts b/src/UseCases/Commands/PunchTime.ts new file mode 100644 index 0000000..a02564f --- /dev/null +++ b/src/UseCases/Commands/PunchTime.ts @@ -0,0 +1,45 @@ +import * as vscode from "vscode"; +import Harvest from "../../Entities/Harvest" +import UserInterface from "../../Entities/Interfaces/UserInterface"; +import User from "../../Entities/User" +import getUser from "../getUser"; + +function PunchTime (context: vscode.ExtensionContext): vscode.Disposable { + return vscode.commands.registerCommand('harvest-vscode.punchTime', async () => { + const harvest = new Harvest() + let user = new User() + + const accountId: string = context.globalState.get('accountId') || '' + const accessToken: string = context.globalState.get('accessToken') || '' + + if (!accountId || !accessToken) { + vscode.window.showErrorMessage('Run "Harvest: Login" Command before trying to puch time') + return + } + + harvest.accountId = accountId + harvest.accessToken = accessToken + + if (!user.id) { + let userProps: UserInterface + try { + userProps = await getUser() + } catch (err) { + console.log(err) + vscode.window.showErrorMessage('Could not retrieve user data from Harvest') + return + } + if (!userProps.id) { + vscode.window.showErrorMessage('Could not retrieve user data from Harvest') + return + } + user.destructor() + user = new User(userProps) + vscode.window.showInformationMessage('Successfully authenticated with Harvest') + } + + console.log(user) + }) +} + +export default PunchTime \ No newline at end of file diff --git a/src/UseCases/Commands/SetUserAuthentication.ts b/src/UseCases/Commands/SetUserAuthentication.ts new file mode 100644 index 0000000..d878e4f --- /dev/null +++ b/src/UseCases/Commands/SetUserAuthentication.ts @@ -0,0 +1,42 @@ +import * as vscode from "vscode"; +import Harvest from "../../Entities/Harvest" +import User from "../../Entities/User" + +function SetUserAuthentication (context: vscode.ExtensionContext): vscode.Disposable { + return vscode.commands.registerCommand('harvest-vscode.login', async () => { + new Harvest().destructor() + new User().destructor() + + const accountId = await vscode.window.showInputBox({ + ignoreFocusOut: true, + placeHolder: 'Harvest Acount Id' + }) + + if (!accountId) { + vscode.window.showErrorMessage('No Account Id Proveded') + return + } + + const accessToken = await vscode.window.showInputBox({ + ignoreFocusOut: true, + placeHolder: 'Harvest Access Token', + password: true + }) + + if (!accessToken) { + vscode.window.showErrorMessage('No Access Token Proveded') + return + } + + await context.globalState.update('accountId', accountId) + await context.globalState.update('accessToken', accessToken) + + new Harvest({ + accountId: accountId, + accessToken: accessToken + }) + + }) +} + +export default SetUserAuthentication \ No newline at end of file diff --git a/src/UseCases/getUser.ts b/src/UseCases/getUser.ts index 8210260..1497a49 100644 --- a/src/UseCases/getUser.ts +++ b/src/UseCases/getUser.ts @@ -1,10 +1,8 @@ import axios from 'axios' import Harvest from '../Entities/Harvest' -import ErrorMessage from '../Constants/ErrorMessageInterface' -import ErrorMessages from '../Constants/ErrorMessages' import UserInterface from '../Entities/Interfaces/UserInterface' -const getUser = async (): Promise => { +const getUser = async (): Promise => { const harvest = new Harvest() let userResponse: any try { @@ -14,15 +12,14 @@ const getUser = async (): Promise => { ) } catch (err) { console.log(err) - return ErrorMessages[0] } const user = { - id: userResponse.data.id || '', - firstName: userResponse.data.first_name || '', - lastName: userResponse.data.last_name || '', - email: userResponse.data.email || '', - avatar: userResponse.data.avatar_url || '', + id: userResponse?.data?.id || '', + firstName: userResponse?.data?.first_name || '', + lastName: userResponse?.data?.last_name || '', + email: userResponse?.data?.email || '', + avatar: userResponse?.data?.avatar_url || '', } return user diff --git a/src/extension.ts b/src/extension.ts index 7d62826..9ce613e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,27 +1,22 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; +import * as vscode from 'vscode' +import Logout from './UseCases/Commands/Logout' +import PunchTime from './UseCases/Commands/PunchTime' +import SetUserAuthentication from './UseCases/Commands/SetUserAuthentication' -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { + const statusbar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100) + statusbar.command = 'harvest-vscode.punchTime' + + statusbar.text = 'Harvest' + statusbar.show() - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log('Congratulations, your extension "harvest-vscode" is now active!'); + const commands: vscode.Disposable[] = [ + SetUserAuthentication(context), + Logout(context), + PunchTime(context) + ] - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - let disposable = vscode.commands.registerCommand('harvest-vscode.helloWorld', () => { - // The code you place here will be executed every time your command is executed - - // Display a message box to the user - vscode.window.showInformationMessage('Hello World from harvest-vscode!'); - }); - - context.subscriptions.push(disposable); + context.subscriptions.push(...commands) } -// this method is called when your extension is deactivated export function deactivate() {} diff --git a/src/media/harvestLogo.png b/src/media/harvestLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..2bb19ee773d9ac6440ebc5f80858a96ab83e29d6 GIT binary patch literal 11515 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