diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e8fc39..b86a5f9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Follows [Semantic Versioning](https://semver.org/). ## Next version +## 1.23.0 + +### Added + +- If no primary author set handle error inline and ability to set new author through vscode inputs. [Issue 27](https://github.com/rkotze/git-mob-vs-code/issues/27) + ## 1.22.0 ### Added diff --git a/package.json b/package.json index 730ddad1..fe7c2970 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,12 @@ "category": "Git Mob", "icon": "$(add)" }, + { + "command": "gitmob.addMainAuthor", + "title": "New primary author", + "category": "Git Mob", + "icon": "$(add)" + }, { "command": "gitmob.searchRepositoryUsers", "title": "Search Git repository for co-authors", @@ -222,6 +228,11 @@ "when": "viewItem == add-repo-author", "group": "inline" }, + { + "command": "gitmob.addMainAuthor", + "when": "viewItem == error-author || viewItem == primary-author", + "group": "inline" + }, { "command": "gitmob.removeCoAuthor", "when": "viewItem == remove-author", @@ -229,7 +240,7 @@ }, { "command": "gitmob.changePrimaryAuthor", - "when": "viewItem == primary-author", + "when": "viewItem == error-author || viewItem == primary-author", "group": "inline" }, { diff --git a/src/build-co-author-groups.js b/src/build-co-author-groups.js index f861295e..704ad3c3 100644 --- a/src/build-co-author-groups.js +++ b/src/build-co-author-groups.js @@ -23,7 +23,9 @@ exports.buildCoAuthorGroups = async function buildCoAuthorGroups() { const allAuthors = await getAllAuthors(); const selectedCoAuthors = await getSelectedCoAuthors(allAuthors); unselected = authorListToMap( - allAuthors.filter((author) => mainAuthor.email != author.email), + allAuthors.filter( + (author) => mainAuthor === undefined || mainAuthor.email != author.email + ), (author) => new CoAuthor(author.name, author.email, false, author.key) ); selected = authorListToMap( @@ -45,7 +47,7 @@ exports.buildCoAuthorGroups = async function buildCoAuthorGroups() { if (mainAuthor) { return new PrimaryAuthor(mainAuthor.name, mainAuthor.email); } - return new ErrorAuthor("Missing Git author"); + return new ErrorAuthor("Error: Missing main Git author."); }, getUnselected() { return sortAuthors(Array.from(unselected.values())); diff --git a/src/build-co-author-groups.spec.js b/src/build-co-author-groups.spec.js index c7735dc2..29ae0e01 100644 --- a/src/build-co-author-groups.spec.js +++ b/src/build-co-author-groups.spec.js @@ -1,6 +1,7 @@ const { workspace } = require("../__mocks__/vscode"); const { buildCoAuthorGroups } = require("./build-co-author-groups"); const { CoAuthor } = require("./co-author-tree-provider/co-authors"); +const { ErrorAuthor } = require("./co-author-tree-provider/error-author"); const { getAllAuthors, setCoAuthors, @@ -13,7 +14,7 @@ const { Author } = jest.requireActual("git-mob-core"); jest.mock("git-mob-core"); describe("Co-author list", function () { - const author = new Author("Richard Kotze", "rkotze@email.com"); + const author = new Author("rk", "Richard Kotze", "rkotze@email.com"); beforeAll(function () { workspace.getConfiguration.mockReturnValue({ @@ -30,6 +31,21 @@ describe("Co-author list", function () { repoAuthorList.mockReset(); }); + it("No main author email should not throw error", async function () { + getPrimaryAuthor.mockResolvedValueOnce(undefined); + getAllAuthors.mockResolvedValueOnce([ + { key: "rk", name: "Richard Kotze", email: "rkotze@email.com" }, + { key: "ts", name: "Tony Stark", email: "tony@stark.com" }, + ]); + getSelectedCoAuthors.mockResolvedValueOnce([]); + + const coAuthorGroups = await buildCoAuthorGroups(); + + expect(coAuthorGroups.getMainAuthor()).toEqual( + new ErrorAuthor("Missing main Git author") + ); + }); + it("Repo authors should not contain co-authors with same email", async function () { repoAuthorList.mockResolvedValueOnce([ new Author("ts", "Tony Stark", "tony@stark.com"), diff --git a/src/co-author-tree-provider/error-author.js b/src/co-author-tree-provider/error-author.js index 946e5b76..11ed183f 100644 --- a/src/co-author-tree-provider/error-author.js +++ b/src/co-author-tree-provider/error-author.js @@ -2,15 +2,16 @@ const vscode = require("vscode"); const { None } = vscode.TreeItemCollapsibleState; class ErrorAuthor extends vscode.TreeItem { - constructor() { - super("Error", None); + constructor(name, contextValue = "error-author") { + super(name || "Error: missing author!", None); this.email = "e@r.ror"; this.selected = false; this.commandKey = ""; + this.contextValue = contextValue; } get tooltip() { - return "Error"; + return "Error: missing author!"; } } diff --git a/src/commands/add-co-author.js b/src/commands/add-co-author.js index 08a56d16..8b026597 100644 --- a/src/commands/add-co-author.js +++ b/src/commands/add-co-author.js @@ -2,6 +2,7 @@ const vscode = require("vscode"); const { CoAuthor } = require("../co-author-tree-provider/co-authors"); const { moveToCoAuthoring } = require("../ext-config/config"); const { saveNewCoAuthors } = require("git-mob-core"); +const { inputAuthorData } = require("./shared/input-author-data"); function addRepoAuthorToCoauthors({ coAuthorProvider }) { return vscode.commands.registerCommand( @@ -22,7 +23,11 @@ function addRepoAuthorToCoauthors({ coAuthorProvider }) { await coAuthorProvider.toggleCoAuthor(coAuthor, true); } } else { - const newAuthor = await inputAuthorData(); + const newAuthor = await inputAuthorData([ + { key: "name", label: "Author name" }, + { key: "email", label: "Author email" }, + { key: "key", label: "Author initials" }, + ]); if (newAuthor) { await saveNewCoAuthors([newAuthor]); const coAuthor = new CoAuthor( @@ -45,37 +50,4 @@ function addRepoAuthorToCoauthors({ coAuthorProvider }) { ); } -async function inputAuthorData() { - const name = await vscode.window.showInputBox({ - prompt: "Author name (Required)", - validateInput: isRequired("Author name"), - }); - const email = await vscode.window.showInputBox({ - prompt: "Author email (Required)", - validateInput: isRequired("Author email"), - }); - const key = await vscode.window.showInputBox({ - prompt: "Author initials (Required)", - validateInput: isRequired("Author initials"), - }); - - if (anyUndefined(name, email, key)) return null; - - return { - name, - email, - key, - }; -} - -function isRequired(fieldName) { - return (value) => { - if (value.length === 0) return `${fieldName} is required.`; - }; -} - -function anyUndefined(...args) { - return args.some((value) => typeof value === "undefined"); -} - exports.addRepoAuthorToCoauthors = addRepoAuthorToCoauthors; diff --git a/src/commands/add-main-author.js b/src/commands/add-main-author.js new file mode 100644 index 00000000..a8725d02 --- /dev/null +++ b/src/commands/add-main-author.js @@ -0,0 +1,23 @@ +const vscode = require("vscode"); +const { Author, setPrimaryAuthor } = require("git-mob-core"); +const { inputAuthorData } = require("./shared/input-author-data"); + +function addMainAuthor() { + return vscode.commands.registerCommand( + "gitmob.addMainAuthor", + async function () { + const newAuthor = await inputAuthorData([ + { key: "name", label: "Author name" }, + { key: "email", label: "Author email" }, + ]); + if (newAuthor) { + await setPrimaryAuthor( + new Author("author", newAuthor.name, newAuthor.email) + ); + await vscode.commands.executeCommand("gitmob.reload"); + } + } + ); +} + +exports.addMainAuthor = addMainAuthor; diff --git a/src/commands/shared/input-author-data.js b/src/commands/shared/input-author-data.js new file mode 100644 index 00000000..482f2990 --- /dev/null +++ b/src/commands/shared/input-author-data.js @@ -0,0 +1,24 @@ +const vscode = require("vscode"); + +async function inputAuthorData(fields) { + const authorData = {}; + + for (const field of fields) { + const value = await vscode.window.showInputBox({ + prompt: `${field.label} (Required)`, + validateInput: isRequired(field.label), + }); + if (typeof value === "undefined") return null; + authorData[field.key] = value; + } + + return authorData; +} + +function isRequired(fieldName) { + return (value) => { + if (value.length === 0) return `${fieldName} is required.`; + }; +} + +exports.inputAuthorData = inputAuthorData; diff --git a/src/setup-git-mob.js b/src/setup-git-mob.js index 94e0040e..f1625416 100644 --- a/src/setup-git-mob.js +++ b/src/setup-git-mob.js @@ -30,6 +30,7 @@ const { buildCoAuthorGroups } = require("./build-co-author-groups"); const { InputCompletionProvider, } = require("./co-author-tree-provider/input-completion-provider"); +const { addMainAuthor } = require("./commands/add-main-author"); async function setupGitMob(context, gitExt) { return bootGitMob(context, gitExt); @@ -66,6 +67,7 @@ async function bootGitMob(context, gitExt) { new CountDecorationProvider(coAuthorProvider), new InputCompletionProvider(coAuthorProvider), copyCoAuthor(), + addMainAuthor({ coAuthorProvider }), ]; disposables.forEach((dispose) => context.subscriptions.push(dispose));