diff --git a/CHANGELOG.md b/CHANGELOG.md index 5af714a..d72d23f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,18 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ### Removed: - Interval for updating +## [0.0.3] - 2019-04-10 +### Added: +- Implemented [Create issue icon](./resources/dark/create.svg) +- Interval for updating (Issuelist now gets updated every ten minutes) +- Closed Issues View +- SSL server support +- License +### Refactored: +- [IssueProvider](./src/issueProvider.ts) splitted up in two parts: OpenIssuesProvider and ClosedIssuesProvider +- class Issue is now in [issue.ts](./src/issue.ts) +- organizing imports +- renaming ## [Unreleased] - Initial release \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..66d3e45 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Panov Alexander + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 32af58e..e70e6e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitea-vscode", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -252,9 +252,12 @@ "dev": true }, "configparser": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/configparser/-/configparser-0.2.6.tgz", - "integrity": "sha512-u0k0Xs6CBDUcCfHHX0rMgZ4QoRQm6V3sb9AuNYkKK+4kwTlBophxBQjuVbI61vTQSRTDx4Ds7CWDQTGusf5q0A==" + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/configparser/-/configparser-0.3.6.tgz", + "integrity": "sha512-qCYjKDEK69qRtm3n+RYM55b8XiH2uKfnI6qOKg+ZLrKSG5vY59RBcZhLxxVd18+B5C5Zc7Jy8KpFsB5YOAD+hw==", + "requires": { + "mkdirp": "^0.5.1" + } }, "core-util-is": { "version": "1.0.2", @@ -614,14 +617,12 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } diff --git a/package.json b/package.json index c5a5e29..12138bb 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Gitea-VSCode", "description": "Gitea Issue Tracker for vs-code", "publisher": "IJustDev", - "version": "0.0.2", + "version": "0.0.3", "engines": { "vscode": "^1.32.0" }, @@ -11,7 +11,9 @@ "Other" ], "activationEvents": [ - "onView:open-issues" + "onView:open-issues", + "onCommand:giteaIssues.initRepo", + "onCommand:giteaIssues.refreshIssues" ], "main": "./out/extension.js", "contributes": { @@ -29,6 +31,10 @@ { "id": "open-issues", "name": "Open Issues" + }, + { + "id": "closed-issues", + "name": "Closed Issues" } ] }, @@ -77,7 +83,7 @@ }, "dependencies": { "axios": "^0.18.0", - "configparser": "^0.2.6" + "configparser": "^0.3.6" }, "repository": { "type": "github", diff --git a/resources/dark/create.svg b/resources/dark/create.svg new file mode 100644 index 0000000..032e0ed --- /dev/null +++ b/resources/dark/create.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/light/create.svg b/resources/light/create.svg new file mode 100644 index 0000000..41ae886 --- /dev/null +++ b/resources/light/create.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index 95124f3..b36409a 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -1,8 +1,5 @@ const ConfigParser = require("configparser"); - -// import * as path from "path"; const path = require("path"); -import * as fs from "fs"; export class RepositoryInformationManager { public token(rootPath: string | undefined) { @@ -16,15 +13,12 @@ export class RepositoryInformationManager { const domain = config.get("REPO", "domain"); const repo_owner = config.get("REPO", "repo_owner"); const repo_name = config.get("REPO", "repo_name"); - return "http://" + domain + "/api/v1/repos/" + repo_owner + "/" + repo_name + "/issues"; + const ssl = config.get("REPO", "ssl"); + const prefix = ssl ? "https" : "http"; + return prefix + "://" + domain + "/api/v1/repos/" + repo_owner + "/" + repo_name + "/issues"; } public saveRepoInformation(rootPath: string | undefined, repoInformations: any) { const file_path = path.join(rootPath as string, ".gitea/config.ini"); - try { - fs.mkdirSync(path.join(rootPath as string, ".gitea/")); - } catch (error) { - - } const config = new ConfigParser(); config.addSection("PRIVATE"); config.addSection("REPO"); @@ -32,6 +26,7 @@ export class RepositoryInformationManager { config.set("REPO", "domain", repoInformations.domain); config.set("REPO", "repo_owner", repoInformations.repo_owner); config.set("REPO", "repo_name", repoInformations.repo_name); - config.write(file_path); + config.set("REPO", "ssl", true); + config.write(file_path, true); } } diff --git a/src/extension.ts b/src/extension.ts index 250b5bf..9a6720c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,68 +1,75 @@ -import { html } from './template.html'; -// 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 { IssueProvider, Issue } from './issueProvider'; -import { RepositoryInformationManager } from './configurationProvider'; -// 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 issueProvider = new IssueProvider(); - // 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 - let disposable = vscode.commands.registerCommand('giteaIssues.initRepo', async () => { - if (vscode.workspace.rootPath === undefined) { - return vscode.window.showErrorMessage("No project opened!"); - } - let Token, Domain, Owner, Name; - await vscode.window.showInputBox({ - placeHolder: "Enter your gitea token here", - }).then(token => { - Token = token === undefined ? "" : token; - }); - await vscode.window.showInputBox({ - placeHolder: "Domain from your gitea server without 'https://'", - }).then(domain => { - Domain = domain === undefined ? "" : domain; - }); - await vscode.window.showInputBox({ - placeHolder: "Repository Owner", - }).then(owner => { - Owner = owner === undefined ? "" : owner; - }); - await vscode.window.showInputBox({ - placeHolder: "Repository Name", - }).then(name => { - Name = name === undefined ? "" : name; - }); - const repoInfo = { - token: Token, - domain: Domain, - repo_owner: Owner, - repo_name: Name, - }; - const rpim = new RepositoryInformationManager(); - rpim.saveRepoInformation(vscode.workspace.rootPath, repoInfo); +import { showIssueHTML } from './template.html'; +import { Issue } from "./issue"; +import { RepositoryInformationManager } from './configurationProvider'; +import { OpenIssuesProvider, ClosedIssuesProvider } from './issueProvider'; + +export function activate(context: vscode.ExtensionContext) { + let disposable = vscode.commands.registerCommand('giteaIssues.initRepo', () => { + initializeIssueTracker(); }); context.subscriptions.push(disposable); - vscode.window.registerTreeDataProvider('open-issues', issueProvider); + const openIssuesProvider = new OpenIssuesProvider(); + const closedIssuesProvider = new ClosedIssuesProvider(); + + vscode.window.registerTreeDataProvider('open-issues', openIssuesProvider); + vscode.window.registerTreeDataProvider('closed-issues', closedIssuesProvider); + + // TODO: Implement in next version. + // vscode.commands.registerCommand('giteaIssues.createIssue', async () => { + // const panel = vscode.window.createWebviewPanel('createIssue', 'Create an new Issue', vscode.ViewColumn.Active, {}); + // panel.webview.html = ""; + // }); + vscode.commands.registerCommand('giteaIssues.openIssue', (issue: Issue) => { const panel = vscode.window.createWebviewPanel('issue', issue.label, vscode.ViewColumn.Active, {}); - panel.webview.html = html(issue); + panel.webview.html = showIssueHTML(issue); }); vscode.commands.registerCommand('giteaIssues.refreshIssues', () => { - issueProvider.refresh(); + openIssuesProvider.refresh(); + closedIssuesProvider.refresh(); }); - // 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 } -// this method is called when your extension is deactivated export function deactivate() { } + +export async function initializeIssueTracker() { + if (vscode.workspace.rootPath === undefined) { + return vscode.window.showErrorMessage("No project opened!"); + } + let Token, Domain, Owner, Name; + await vscode.window.showInputBox({ + placeHolder: "Enter your gitea token here", + }).then(token => { + Token = token === undefined ? "" : token; + }); + await vscode.window.showInputBox({ + placeHolder: "Domain from your gitea server without 'https://'", + }).then(domain => { + Domain = domain === undefined ? "" : domain; + }); + await vscode.window.showInputBox({ + placeHolder: "Repository Owner", + }).then(owner => { + Owner = owner === undefined ? "" : owner; + }); + await vscode.window.showInputBox({ + placeHolder: "Repository Name", + }).then(name => { + Name = name === undefined ? "" : name; + }); + const repoInfo = { + token: Token, + domain: Domain, + repo_owner: Owner, + repo_name: Name, + }; + const rpim = new RepositoryInformationManager(); + rpim.saveRepoInformation(vscode.workspace.rootPath, repoInfo); +} \ No newline at end of file diff --git a/src/issue.ts b/src/issue.ts new file mode 100644 index 0000000..61fc5e8 --- /dev/null +++ b/src/issue.ts @@ -0,0 +1,20 @@ +import * as vscode from "vscode"; + +export class Issue extends vscode.TreeItem { + + constructor(public readonly label: string, + public issueId: number, + public body: string, + public issueState: string, + public assignee: string, + public firstlabel: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly command?: vscode.Command) { + super(label, collapsibleState); + } + + get tooltip() { + return this.label + " - " + this.assignee; + } + contextValue = 'issue'; +} \ No newline at end of file diff --git a/src/issueProvider.ts b/src/issueProvider.ts index 6fd10a6..8c909b8 100644 --- a/src/issueProvider.ts +++ b/src/issueProvider.ts @@ -1,8 +1,10 @@ -import * as vscode from "vscode"; import axios from "axios"; +import * as vscode from "vscode"; + +import { Issue } from "./issue"; import { RepositoryInformationManager } from "./configurationProvider"; -export class IssueProvider implements vscode.TreeDataProvider { +export class OpenIssuesProvider implements vscode.TreeDataProvider { private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; @@ -14,17 +16,24 @@ export class IssueProvider implements vscode.TreeDataProvider { this._onDidChangeTreeData.fire(); } - constructor() { } + constructor() { + // Auto update the issuelist after 10 minutes + setInterval(() => { + this.refresh(); + }, 10 * 60 * 1000); + } getTreeItem(element: Issue): vscode.TreeItem | Thenable { return element; } + /** + * Returns a list of all open issues; + */ async getChildrenAsync() { this.issueList = []; const repoMng = new RepositoryInformationManager(); const repoUri = repoMng.repoApiUrl(vscode.workspace.rootPath); - console.log(repoUri); const token = repoMng.token(vscode.workspace.rootPath); let stop = false; for (let i = 0; i !== 10; i++) { @@ -53,8 +62,7 @@ export class IssueProvider implements vscode.TreeDataProvider { }); this.issueList.push(issueForList); } - }).catch(err => { - console.log(err); + }).catch(() => { stop = true; vscode.window.showErrorMessage("Can't fetch issues; HTTP Error!"); return; @@ -63,8 +71,6 @@ export class IssueProvider implements vscode.TreeDataProvider { return; } } - - console.log(this.issueList); } getChildren(element?: Issue): vscode.ProviderResult { return this.issueList; @@ -73,21 +79,73 @@ export class IssueProvider implements vscode.TreeDataProvider { } -export class Issue extends vscode.TreeItem { +export class ClosedIssuesProvider implements vscode.TreeDataProvider { - constructor(public readonly label: string, - public issueId: number, - public body: string, - public issueState: string, - public assignee: string, - public firstlabel: string, - public readonly collapsibleState: vscode.TreeItemCollapsibleState, - public readonly command?: vscode.Command) { - super(label, collapsibleState); + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + issueList: Issue[] = []; + + async refresh() { + await this.getChildrenAsync(); + this._onDidChangeTreeData.fire(); } - get tooltip() { - return this.label + " - " + this.assignee; + constructor() { + setInterval(() => { + this.refresh(); + }, 10 * 60 * 1000); } - contextValue = 'issue'; + + getTreeItem(element: Issue): vscode.TreeItem | Thenable { + return element; + } + + async getChildrenAsync() { + this.issueList = []; + const repoMng = new RepositoryInformationManager(); + const repoUri = repoMng.repoApiUrl(vscode.workspace.rootPath); + const token = repoMng.token(vscode.workspace.rootPath); + let stop = false; + for (let i = 0; i !== 10; i++) { + await axios.get(repoUri + "?state=closed&page=" + i, { headers: { Authorization: "token " + token } }).then(res => { + for (const issue of res.data) { + const id = issue["number"]; + let isAlreadyInList = false; + this.issueList.forEach((issueOfList) => { + if (id === issueOfList.issueId) { + isAlreadyInList = true; + } + }); + if (isAlreadyInList) { + continue; + } + const title = issue["title"]; + const body = issue["body"]; + const state = issue["state"]; + const assignee = issue["assignee"] === null ? "None" : issue["assignee"]["username"]; + const tmpIssue = new Issue("#" + id + " - " + title, id, body, state, assignee, "Frontend", vscode.TreeItemCollapsibleState.None); + const issueForList = new Issue(tmpIssue.label, tmpIssue.issueId, tmpIssue.body, tmpIssue.issueState, + tmpIssue.assignee, tmpIssue.firstlabel, tmpIssue.collapsibleState, { + command: 'giteaIssues.openIssue', + title: '', + arguments: [tmpIssue], + }); + this.issueList.push(issueForList); + } + }).catch(() => { + stop = true; + vscode.window.showErrorMessage("Can't fetch issues; HTTP Error!"); + return; + }); + if (stop) { + return; + } + } + } + getChildren(element?: Issue): vscode.ProviderResult { + return this.issueList; + } + + } \ No newline at end of file diff --git a/src/template.html.ts b/src/template.html.ts index 876b6d4..706f2ad 100644 --- a/src/template.html.ts +++ b/src/template.html.ts @@ -1,6 +1,6 @@ -import { Issue } from "./issueProvider"; +import { Issue } from "./issue"; -export function html(issue: Issue) { +export function showIssueHTML(issue: Issue) { return `

{{label}}

diff --git a/versions/gitea-vscode-0.0.3.vsix b/versions/gitea-vscode-0.0.3.vsix new file mode 100644 index 0000000..3d3c44d Binary files /dev/null and b/versions/gitea-vscode-0.0.3.vsix differ