Skip to content

Commit d5affdd

Browse files
committed
added new project update view and notification flow
- updated share project webview with list of commit history - notification now will be show at the interval of 24 hours, in case new commit is detected
1 parent 898f578 commit d5affdd

10 files changed

Lines changed: 199 additions & 50 deletions

File tree

src/@types/types.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Commit } from "./git";
2+
13
type Project = {
24
id: string;
35
creatorUser: {
@@ -33,6 +35,7 @@ type UserInfo = {
3335
email: string;
3436
id: string;
3537
name: string;
38+
commits: Commit[];
3639
};
3740

3841
type RegisterCommand = {

src/authProviders/auth0AuthProvider.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import {
77
Uri,
88
window,
99
} from "vscode";
10+
import {
11+
AuthDetails,
12+
CommitPollAccessTokenResponse,
13+
UserDeviceCode,
14+
UserInfo,
15+
} from "../@types/types";
1016
import { AuthProvider } from "../common/authProviderInterface";
1117
import {
1218
COMMIT_API_BASE_URL,
@@ -69,7 +75,7 @@ export class Auth0AuthenticationProvider extends AuthProvider {
6975
throw new Error(`Commit login failed`);
7076
}
7177

72-
const userInfo = await this._getUserInfo(accessToken);
78+
const userInfo: UserInfo = await this._getUserInfo(accessToken);
7379

7480
const session: AuthenticationSession = {
7581
id: "commit-" + uuid(),
@@ -128,9 +134,10 @@ export class Auth0AuthenticationProvider extends AuthProvider {
128134

129135
const data = (await response.json()) as any;
130136
return {
131-
email: data.email,
137+
132138
id: data.id,
133139
name: data.name,
140+
commits: [],
134141
};
135142
} catch (e) {
136143
throw new Error(`Get user info failed: ${e}`);

src/commands/commit/setDefaultProject.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as vscode from "vscode";
2+
import { Project, RegisterCommand } from "../../@types/types";
23
import { CommitAPI } from "../../commitAPI";
34

45
const setDefaultProject = (

src/commands/commit/shareProjectUpdate.ts

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as vscode from "vscode";
2-
import { API, Commit } from "../../@types/git";
2+
import { Commit } from "../../@types/git";
3+
import { Project, WebViewMessageSend } from "../../@types/types";
34
import { CommitAPI } from "../../commitAPI";
4-
import { getWebviewContent } from "../../utils";
5+
import { getGitCommits, getWebviewContent } from "../../utils";
56

67
const shareProjectUpdate = (context: vscode.ExtensionContext) => {
78
return {
@@ -138,34 +139,4 @@ const processWebviewMessage = async (
138139
}
139140
};
140141

141-
const getGitCommits: (
142-
context: vscode.ExtensionContext
143-
) => Promise<Commit[] | undefined> = async (
144-
context: vscode.ExtensionContext,
145-
maxEntries: number = 10
146-
) => {
147-
const gitAPI = context.workspaceState.get<API>("gitAPI");
148-
149-
if (!gitAPI) {
150-
return [];
151-
}
152-
153-
// Get Worspace folder
154-
const workspaceFolders = vscode.workspace.workspaceFolders;
155-
156-
if (!workspaceFolders || workspaceFolders.length === 0) {
157-
return [];
158-
}
159-
160-
// Get the respository in the first workspace folder
161-
// TODO: At this point this will always pick the the first workspace folder
162-
// TODO: Add support for multiple workspace folders
163-
const workspaceFolder = workspaceFolders[0];
164-
const repository = gitAPI.getRepository(workspaceFolder.uri);
165-
166-
return await repository?.log({
167-
maxEntries,
168-
});
169-
};
170-
171142
export default shareProjectUpdate;

src/commands/commit/subscriptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as vscode from "vscode";
2+
import { RegisterCommand } from "../../@types/types";
23
import { SubscriptionType } from "../../commitAPI";
34
const addSubscriptions = (
45
context: vscode.ExtensionContext

src/commitAPI.ts

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { ApolloClient, gql, NormalizedCacheObject } from "@apollo/client/core";
22

33
import * as vscode from "vscode";
4-
import { API } from "./@types/git";
4+
import { API, Commit } from "./@types/git";
5+
import { Project, UserInfo } from "./@types/types";
56
import { COMMIT_PROJECT_UDPATE_NOTIFICATION_INTERVAL } from "./common/constants";
7+
import { getGitCommits } from "./utils";
68

79
export enum SubscriptionType {
810
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -220,6 +222,92 @@ export class CommitAPI {
220222

221223
public async showAddProjectUpdateNotification(
222224
context: vscode.ExtensionContext
225+
) {
226+
// If it was shown in the last configured minutes, don't show the notification
227+
const lastNotificationShownTime = context.workspaceState.get(
228+
"lastNotificationShownTime"
229+
) as number;
230+
231+
if (
232+
lastNotificationShownTime &&
233+
Date.now() - lastNotificationShownTime <
234+
COMMIT_PROJECT_UDPATE_NOTIFICATION_INTERVAL * 1000 * 20
235+
) {
236+
console.log("Notification already shown");
237+
return;
238+
}
239+
240+
// Get commit history from workspace state
241+
const userInfo = context.workspaceState.get("commitUserInfo") as UserInfo;
242+
243+
if (userInfo === undefined) {
244+
return;
245+
}
246+
247+
// Get the recent commit history for the project
248+
const gitCommitHistory = (await getGitCommits(context)) as Commit[];
249+
250+
if (gitCommitHistory.length === 0) {
251+
return;
252+
}
253+
254+
if (userInfo.commits.length === 0) {
255+
context.workspaceState.update("commitUserInfo", {
256+
...userInfo,
257+
commits: gitCommitHistory,
258+
});
259+
return;
260+
}
261+
262+
// Get the first commits from the user and git history, to compare the dates and hashes
263+
const userLastCommit = userInfo.commits[0];
264+
const gitHistoryLastCommit = gitCommitHistory[0];
265+
266+
if (
267+
userLastCommit.hash === gitHistoryLastCommit.hash ||
268+
gitHistoryLastCommit.commitDate === undefined
269+
) {
270+
return;
271+
}
272+
273+
// If user last commit date is undefined, update the workspace state commit history for user and return
274+
if (userLastCommit.commitDate === undefined) {
275+
context.workspaceState.update("userInfo", {
276+
...userInfo,
277+
commits: gitCommitHistory,
278+
});
279+
return;
280+
}
281+
282+
// Check if gitHistoryLastCommit.commitDate is greater than userLastCommit.commitDate
283+
if (gitHistoryLastCommit.commitDate > userLastCommit.commitDate) {
284+
// Show notification
285+
const promptResult = await vscode.window.showInformationMessage(
286+
"Looks like you've been hard at work, how about sharing a project update?",
287+
"Yes",
288+
"No"
289+
);
290+
291+
if (promptResult === "No") {
292+
return;
293+
}
294+
295+
// Update the workspace state commit history for user
296+
context.workspaceState.update("commitUserInfo", {
297+
...userInfo,
298+
commits: gitCommitHistory,
299+
});
300+
301+
// Update the last notification shown time
302+
context.workspaceState.update("lastNotificationShownTime", Date.now());
303+
304+
// Open the Commit Project view
305+
vscode.commands.executeCommand("commit-extension.shareProjectUpdate");
306+
}
307+
}
308+
309+
public async showAddProjectUpdateNotificationOld(
310+
context: vscode.ExtensionContext
223311
): Promise<void> {
224312
// Get Git API from workspace state
225313
const gitAPI = context.workspaceState.get("gitAPI") as API;
@@ -255,7 +343,7 @@ export class CommitAPI {
255343
if (worktreeChanges?.length >= 1) {
256344
// Check if the notification to add Project update should be shown
257345
const commitAPI = context.workspaceState.get("commitAPI") as CommitAPI;
258-
if (await commitAPI.showAddProjectUpdateNotification) {
346+
if (await commitAPI.showAddProjectUpdateNotificationOld) {
259347
// Show notification with yes and no buttons
260348
vscode.window
261349
.showInformationMessage(

src/common/authProviderInterface.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Event,
99
EventEmitter,
1010
} from "vscode";
11+
import { DecodedToken } from "../@types/types";
1112

1213
export abstract class AuthProvider
1314
implements AuthenticationProvider, Disposable
@@ -83,6 +84,20 @@ export abstract class AuthProvider
8384

8485
await this.context.secrets.delete(this.getSecretKey());
8586

87+
// Remove all workspace state settings for commit
88+
await this.getContext().workspaceState.update("defaultProject", undefined);
89+
await this.getContext().workspaceState.update(
90+
"commitNotificationInterval",
91+
undefined
92+
);
93+
await this.getContext().workspaceState.update(
94+
"commitLastNotificationShown",
95+
undefined
96+
);
97+
await this.getContext().workspaceState.update("commitAPI", undefined);
98+
await this.getContext().workspaceState.update("gitAPI", undefined);
99+
await this.getContext().workspaceState.update("commitUserInfo", undefined);
100+
86101
this._onDidChangeSessions.fire({
87102
added: [],
88103
removed: [session],

src/common/projectProvider.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Project } from "../@types/types";
2+
13
abstract class ProjectProvider {
24
abstract getAllProjects(): Promise<Project[]>;
35
abstract getProject(projecdId: string): Promise<Project>;

src/extension.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as vscode from "vscode";
2-
import { API, GitExtension, PublishEvent } from "./@types/git";
2+
import { API, GitExtension } from "./@types/git";
3+
import { UserInfo } from "./@types/types";
34
import {
45
Auth0AuthenticationProvider,
56
AUTH_TYPE as AUTH0_AUTH_TYPE,
@@ -46,8 +47,12 @@ export async function activate(this: any, context: vscode.ExtensionContext) {
4647
// Get Git API from workspace state
4748
const gitAPI = context.workspaceState.get("gitAPI") as API;
4849

50+
if (gitAPI === undefined) {
51+
return;
52+
}
53+
4954
// Get repository
50-
const repository = gitAPI?.repositories[0];
55+
const repository = gitAPI.repositories[0];
5156

5257
// Subscribe to on Git status change
5358
context.subscriptions.push(
@@ -57,24 +62,30 @@ export async function activate(this: any, context: vscode.ExtensionContext) {
5762
})
5863
);
5964

60-
// Subscribe to on Git publish event
61-
context.subscriptions.push(
62-
gitAPI?.onDidPublish((e: PublishEvent) => {
63-
// Get repository
64-
const repository = e.repository;
65-
})
66-
);
65+
// Try showing the add project update notification
66+
const commitAPI = context.workspaceState.get("commitAPI") as CommitAPI;
67+
if (commitAPI) {
68+
await commitAPI.showAddProjectUpdateNotification(context);
69+
}
6770
}
6871

6972
const handleAuth0SessionChange = async (context: vscode.ExtensionContext) => {
7073
const commitSession = await getCommitSessions();
7174

7275
if (!commitSession) {
73-
// Remove the project from workspace state
74-
context.workspaceState.update("defaultProject", undefined);
75-
76-
// Remove the commitAPI from global state
77-
context.workspaceState.update("commitAPI", undefined);
76+
// Remove all workspace state settings for commit
77+
await context.workspaceState.update("defaultProject", undefined);
78+
await context.workspaceState.update(
79+
"commitNotificationInterval",
80+
undefined
81+
);
82+
await context.workspaceState.update(
83+
"commitLastNotificationShown",
84+
undefined
85+
);
86+
await context.workspaceState.update("commitAPI", undefined);
87+
await context.workspaceState.update("gitAPI", undefined);
88+
await context.workspaceState.update("commitUserInfo", undefined);
7889

7990
return;
8091
}
@@ -132,6 +143,16 @@ const getCommitAPI = async (
132143
// Add the commitAPI to the workspace state
133144
context.workspaceState.update("commitAPI", commitAPI);
134145

146+
// Add userInfo to workspace state
147+
// extract email from commit session label "name <email>"
148+
const userInfo: UserInfo = {
149+
email: commitSession.account.label.split("<")[1].split(">")[0],
150+
name: commitSession.account.label.split("<")[0].trim(),
151+
id: commitSession.account.id,
152+
commits: [],
153+
};
154+
context.workspaceState.update("commitUserInfo", userInfo);
155+
135156
return commitAPI;
136157
};
137158

src/utils.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { createClient } from "graphql-ws";
1414
import fetch from "node-fetch";
1515
import * as vscode from "vscode";
1616
import { WebSocket } from "ws";
17+
import { API, Commit } from "./@types/git";
18+
import { RegisterCommand, UserInfo } from "./@types/types";
1719
import {
1820
COMMIT_API_BASE_URL,
1921
COMMIT_GRAPHQL_API_URL,
@@ -107,3 +109,41 @@ export const getWebviewContent = (context: vscode.ExtensionContext) => {
107109

108110
return fs.readFileSync(html.fsPath, "utf8");
109111
};
112+
113+
export const getGitCommits: (
114+
context: vscode.ExtensionContext
115+
) => Promise<Commit[] | undefined> = async (
116+
context: vscode.ExtensionContext,
117+
maxEntries: number = 10
118+
) => {
119+
const gitAPI = context.workspaceState.get<API>("gitAPI");
120+
const userInfo = context.workspaceState.get<UserInfo>("commitUserInfo");
121+
122+
if (!gitAPI) {
123+
return [];
124+
}
125+
126+
// Get Worspace folder
127+
const workspaceFolders = vscode.workspace.workspaceFolders;
128+
129+
if (!workspaceFolders || workspaceFolders.length === 0) {
130+
return [];
131+
}
132+
133+
// Get the respository in the first workspace folder
134+
// TODO: At this point this will always pick the the first workspace folder
135+
// TODO: Add support for multiple workspace folders
136+
const workspaceFolder = workspaceFolders[0];
137+
const repository = gitAPI.getRepository(workspaceFolder.uri);
138+
139+
if (!repository) {
140+
return [];
141+
}
142+
const userCommits = (await repository?.log({ maxEntries })).filter(
143+
(commit) => {
144+
return commit.authorEmail === userInfo?.email;
145+
}
146+
);
147+
148+
return userCommits;
149+
};

0 commit comments

Comments
 (0)