feat(tracking): enhance event tracking for repository actions
This update significantly improves the event tracking system within the application. Various user interactions, such as toggling favorite repositories, managing branches, and handling file operations, are now logged with detailed event tracking, providing better insights into user behavior and application usage. - Added tracking for repository management actions and file operations - Enhanced tracking for branch and tag management events - Implemented tracking for explorer interactions and conflict resolutions
This commit is contained in:
+190
-4
@@ -1043,10 +1043,14 @@
|
|||||||
function toggleFavoriteRepo(path: string, event?: MouseEvent) {
|
function toggleFavoriteRepo(path: string, event?: MouseEvent) {
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
favoriteRepoPaths = isFavoriteRepo(path)
|
const wasFavorite = isFavoriteRepo(path);
|
||||||
|
favoriteRepoPaths = wasFavorite
|
||||||
? favoriteRepoPaths.filter((favoritePath) => !sameRepoPath(favoritePath, path))
|
? favoriteRepoPaths.filter((favoritePath) => !sameRepoPath(favoritePath, path))
|
||||||
: uniqueRepoPaths([path, ...favoriteRepoPaths]);
|
: uniqueRepoPaths([path, ...favoriteRepoPaths]);
|
||||||
persistRepoLists();
|
persistRepoLists();
|
||||||
|
trackEvent(wasFavorite ? "repository_favorite_removed" : "repository_favorite_added", {
|
||||||
|
favorite_repositories: favoriteRepoPaths.length,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function upsertRepoTab(path: string, nextStatus?: GitStatus | null) {
|
function upsertRepoTab(path: string, nextStatus?: GitStatus | null) {
|
||||||
@@ -1311,6 +1315,7 @@
|
|||||||
});
|
});
|
||||||
if (typeof selected !== "string") return;
|
if (typeof selected !== "string") return;
|
||||||
repoPath = selected;
|
repoPath = selected;
|
||||||
|
trackEvent("repository_folder_selected");
|
||||||
await openRepo(selected);
|
await openRepo(selected);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage = errorToMessage(error);
|
errorMessage = errorToMessage(error);
|
||||||
@@ -1373,6 +1378,10 @@
|
|||||||
credDialogKey = null;
|
credDialogKey = null;
|
||||||
}
|
}
|
||||||
lastRepoSwitchAt = Date.now();
|
lastRepoSwitchAt = Date.now();
|
||||||
|
trackEvent("repository_cloned", {
|
||||||
|
changed_files: bundle.status.files.length,
|
||||||
|
has_upstream: bundle.status.upstream ? 1 : 0,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const rawMessage = errorToMessage(error);
|
const rawMessage = errorToMessage(error);
|
||||||
const message = stripAuthPrefix(rawMessage);
|
const message = stripAuthPrefix(rawMessage);
|
||||||
@@ -1401,17 +1410,26 @@
|
|||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
setCloneDialogError("");
|
setCloneDialogError("");
|
||||||
cloneDialogOpen = true;
|
cloneDialogOpen = true;
|
||||||
|
trackEvent("clone_dialog_opened");
|
||||||
}
|
}
|
||||||
|
|
||||||
function openRepoManagement() {
|
function openRepoManagement() {
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
activeView = "management";
|
activeView = "management";
|
||||||
|
trackEvent("repository_management_opened", {
|
||||||
|
open_repositories: repoTabs.length,
|
||||||
|
recent_repositories: recentRepoPaths.length,
|
||||||
|
favorite_repositories: favoriteRepoPaths.length,
|
||||||
|
});
|
||||||
void backgroundRepoStatusTick(false);
|
void backgroundRepoStatusTick(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectRepoTab(path: string) {
|
async function selectRepoTab(path: string) {
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
if (activeView === "repository" && sameRepoPath(activeRepoPath, path)) return;
|
if (activeView === "repository" && sameRepoPath(activeRepoPath, path)) return;
|
||||||
|
trackEvent("repository_tab_selected", {
|
||||||
|
open_repositories: repoTabs.length,
|
||||||
|
});
|
||||||
await openRepo(path);
|
await openRepo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1422,10 +1440,15 @@
|
|||||||
const index = repoTabs.findIndex((tab) => sameRepoPath(tab.path, path));
|
const index = repoTabs.findIndex((tab) => sameRepoPath(tab.path, path));
|
||||||
const remaining = repoTabs.filter((tab) => !sameRepoPath(tab.path, path));
|
const remaining = repoTabs.filter((tab) => !sameRepoPath(tab.path, path));
|
||||||
const next = remaining[index] ?? remaining[index - 1] ?? null;
|
const next = remaining[index] ?? remaining[index - 1] ?? null;
|
||||||
|
const wasActive = sameRepoPath(activeRepoPath, path);
|
||||||
repoTabs = remaining;
|
repoTabs = remaining;
|
||||||
persistRepoLists();
|
persistRepoLists();
|
||||||
|
trackEvent("repository_tab_closed", {
|
||||||
|
open_repositories: repoTabs.length,
|
||||||
|
was_active: wasActive ? 1 : 0,
|
||||||
|
});
|
||||||
|
|
||||||
if (!sameRepoPath(activeRepoPath, path)) return;
|
if (!wasActive) return;
|
||||||
if (next) {
|
if (next) {
|
||||||
await openRepo(next.path);
|
await openRepo(next.path);
|
||||||
} else {
|
} else {
|
||||||
@@ -1437,15 +1460,23 @@
|
|||||||
async function removeRepoFromManagement(path: string, event?: MouseEvent) {
|
async function removeRepoFromManagement(path: string, event?: MouseEvent) {
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
|
const wasOpen = repoTabs.some((tab) => sameRepoPath(tab.path, path));
|
||||||
|
const wasFavorite = isFavoriteRepo(path);
|
||||||
recentRepoPaths = recentRepoPaths.filter((recentPath) => !sameRepoPath(recentPath, path));
|
recentRepoPaths = recentRepoPaths.filter((recentPath) => !sameRepoPath(recentPath, path));
|
||||||
favoriteRepoPaths = favoriteRepoPaths.filter((favoritePath) => !sameRepoPath(favoritePath, path));
|
favoriteRepoPaths = favoriteRepoPaths.filter((favoritePath) => !sameRepoPath(favoritePath, path));
|
||||||
persistRepoLists();
|
persistRepoLists();
|
||||||
|
trackEvent("repository_removed_from_management", {
|
||||||
|
was_open: wasOpen ? 1 : 0,
|
||||||
|
was_favorite: wasFavorite ? 1 : 0,
|
||||||
|
recent_repositories: recentRepoPaths.length,
|
||||||
|
favorite_repositories: favoriteRepoPaths.length,
|
||||||
|
});
|
||||||
if (repoStatusCache[repoKey(path)]) {
|
if (repoStatusCache[repoKey(path)]) {
|
||||||
const { [repoKey(path)]: _removed, ...rest } = repoStatusCache;
|
const { [repoKey(path)]: _removed, ...rest } = repoStatusCache;
|
||||||
repoStatusCache = rest;
|
repoStatusCache = rest;
|
||||||
persistRepoStatusCache();
|
persistRepoStatusCache();
|
||||||
}
|
}
|
||||||
if (repoTabs.some((tab) => sameRepoPath(tab.path, path))) {
|
if (wasOpen) {
|
||||||
await closeRepoTab(path);
|
await closeRepoTab(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1454,6 +1485,7 @@
|
|||||||
if (!activeRepoPath || isBusy) return;
|
if (!activeRepoPath || isBusy) return;
|
||||||
try {
|
try {
|
||||||
await openRepoInExplorer(activeRepoPath);
|
await openRepoInExplorer(activeRepoPath);
|
||||||
|
trackEvent("repository_opened_in_explorer");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage = errorToMessage(error);
|
errorMessage = errorToMessage(error);
|
||||||
}
|
}
|
||||||
@@ -1468,6 +1500,9 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("repository_refreshed", {
|
||||||
|
changed_files: status?.files.length ?? 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1479,6 +1514,9 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_checked_out", {
|
||||||
|
remote: branch.remote ? 1 : 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1491,12 +1529,14 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_created");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renameLocalBranch(branch: GitBranchInfo) {
|
function renameLocalBranch(branch: GitBranchInfo) {
|
||||||
if (!activeRepoPath || branch.remote) return;
|
if (!activeRepoPath || branch.remote) return;
|
||||||
renameBranchTarget = branch;
|
renameBranchTarget = branch;
|
||||||
|
trackEvent("branch_rename_dialog_opened");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitRenameBranch(branchName: string) {
|
async function submitRenameBranch(branchName: string) {
|
||||||
@@ -1511,6 +1551,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_renamed");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1523,6 +1564,7 @@
|
|||||||
|
|
||||||
deleteBranchTarget = branch;
|
deleteBranchTarget = branch;
|
||||||
deleteBranchForce = false;
|
deleteBranchForce = false;
|
||||||
|
trackEvent("branch_delete_dialog_opened");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmDeleteBranch() {
|
async function confirmDeleteBranch() {
|
||||||
@@ -1532,6 +1574,7 @@
|
|||||||
operation = `${deleteBranchForce ? "Force deleting" : "Deleting"} ${branch.name}`;
|
operation = `${deleteBranchForce ? "Force deleting" : "Deleting"} ${branch.name}`;
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
try {
|
try {
|
||||||
|
const forceDelete = deleteBranchForce;
|
||||||
applyStatus(await deleteBranch(activeRepoPath, branch.name, deleteBranchForce));
|
applyStatus(await deleteBranch(activeRepoPath, branch.name, deleteBranchForce));
|
||||||
deleteBranchTarget = null;
|
deleteBranchTarget = null;
|
||||||
deleteBranchForce = false;
|
deleteBranchForce = false;
|
||||||
@@ -1539,6 +1582,9 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_deleted", {
|
||||||
|
force: forceDelete ? 1 : 0,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = errorToMessage(error);
|
const message = errorToMessage(error);
|
||||||
if (deleteBranchForce || !isBranchNotFullyMergedError(message)) {
|
if (deleteBranchForce || !isBranchNotFullyMergedError(message)) {
|
||||||
@@ -1561,6 +1607,7 @@
|
|||||||
function openNewBranchDialog(commit: GitCommit) {
|
function openNewBranchDialog(commit: GitCommit) {
|
||||||
if (!activeRepoPath || isBusy) return;
|
if (!activeRepoPath || isBusy) return;
|
||||||
newBranchCommit = commit;
|
newBranchCommit = commit;
|
||||||
|
trackEvent("branch_from_commit_dialog_opened");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createBranchFromCommit(branchName: string) {
|
async function createBranchFromCommit(branchName: string) {
|
||||||
@@ -1574,6 +1621,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_created_from_commit");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1585,6 +1633,9 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_merged", {
|
||||||
|
remote: branch.remote ? 1 : 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1596,6 +1647,9 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("branch_rebased", {
|
||||||
|
remote: branch.remote ? 1 : 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1607,6 +1661,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("rebase_continued");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1625,6 +1680,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("rebase_aborted");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1633,6 +1689,9 @@
|
|||||||
if (!activeRepoPath || !trimmed) return;
|
if (!activeRepoPath || !trimmed) return;
|
||||||
await runOperation(`Creating tag ${trimmed}`, async () => {
|
await runOperation(`Creating tag ${trimmed}`, async () => {
|
||||||
await refreshTags(activeRepoPath, await createTag(activeRepoPath, trimmed, undefined, message));
|
await refreshTags(activeRepoPath, await createTag(activeRepoPath, trimmed, undefined, message));
|
||||||
|
trackEvent("tag_created", {
|
||||||
|
annotated: message.trim() ? 1 : 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1643,6 +1702,7 @@
|
|||||||
|
|
||||||
await runOperation(`Deleting tag ${tag.name}`, async () => {
|
await runOperation(`Deleting tag ${tag.name}`, async () => {
|
||||||
await refreshTags(activeRepoPath, await deleteTag(activeRepoPath, tag.name));
|
await refreshTags(activeRepoPath, await deleteTag(activeRepoPath, tag.name));
|
||||||
|
trackEvent("tag_deleted");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1663,6 +1723,7 @@
|
|||||||
: stripAuthPrefix(message),
|
: stripAuthPrefix(message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
trackEvent("tag_pushed");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1674,6 +1735,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("commit_cherry_picked");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1685,6 +1747,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("cherry_pick_continued");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1703,6 +1766,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("cherry_pick_aborted");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1732,6 +1796,9 @@
|
|||||||
credDialogAction = action;
|
credDialogAction = action;
|
||||||
credDialogKey = key === undefined && action !== "clone" ? await currentCredKey() : (key ?? null);
|
credDialogKey = key === undefined && action !== "clone" ? await currentCredKey() : (key ?? null);
|
||||||
credDialogOpen = true;
|
credDialogOpen = true;
|
||||||
|
trackEvent("credential_dialog_opened", {
|
||||||
|
action,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post-process a pull/push result: surface errors, and on rejected/expired
|
// Post-process a pull/push result: surface errors, and on rejected/expired
|
||||||
@@ -1776,6 +1843,10 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("repository_pulled", {
|
||||||
|
from_stored_credential: fromStore ? 1 : 0,
|
||||||
|
changed_files: status?.files.length ?? 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
handleRemoteResult("pull", key, fromStore);
|
handleRemoteResult("pull", key, fromStore);
|
||||||
}
|
}
|
||||||
@@ -1789,6 +1860,11 @@
|
|||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
await runOperation("Fetching", async () => {
|
await runOperation("Fetching", async () => {
|
||||||
applyStatus(await fetchRemote(activeRepoPath, username, password));
|
applyStatus(await fetchRemote(activeRepoPath, username, password));
|
||||||
|
trackEvent("repository_fetched", {
|
||||||
|
from_stored_credential: fromStore ? 1 : 0,
|
||||||
|
ahead: status?.ahead ?? 0,
|
||||||
|
behind: status?.behind ?? 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
handleRemoteResult("fetch", key, fromStore);
|
handleRemoteResult("fetch", key, fromStore);
|
||||||
}
|
}
|
||||||
@@ -1805,6 +1881,10 @@
|
|||||||
await refreshBranchList(activeRepoPath);
|
await refreshBranchList(activeRepoPath);
|
||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("repository_pushed", {
|
||||||
|
from_stored_credential: fromStore ? 1 : 0,
|
||||||
|
changed_files: status?.files.length ?? 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errorMessage && !isAuthError(errorMessage) && isNonFastForwardPushError(errorMessage)) {
|
if (errorMessage && !isAuthError(errorMessage) && isNonFastForwardPushError(errorMessage)) {
|
||||||
@@ -1847,6 +1927,10 @@
|
|||||||
await refreshBranchList(activeRepoPath);
|
await refreshBranchList(activeRepoPath);
|
||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("repository_pushed_after_pull", {
|
||||||
|
from_stored_credential: fromStore ? 1 : 0,
|
||||||
|
changed_files: status?.files.length ?? 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1887,6 +1971,9 @@
|
|||||||
|
|
||||||
async function startRemoteAction(action: "push" | "pull" | "fetch") {
|
async function startRemoteAction(action: "push" | "pull" | "fetch") {
|
||||||
if (!activeRepoPath) return;
|
if (!activeRepoPath) return;
|
||||||
|
trackEvent("remote_action_started", {
|
||||||
|
action,
|
||||||
|
});
|
||||||
const key = await currentCredKey();
|
const key = await currentCredKey();
|
||||||
const stored = await loadStoredCredential(key);
|
const stored = await loadStoredCredential(key);
|
||||||
|
|
||||||
@@ -1916,11 +2003,16 @@
|
|||||||
|
|
||||||
async function saveStash(message: string, includeUntracked: boolean) {
|
async function saveStash(message: string, includeUntracked: boolean) {
|
||||||
if (!activeRepoPath || changedFiles.length === 0) return;
|
if (!activeRepoPath || changedFiles.length === 0) return;
|
||||||
|
const stashedFiles = changedFiles.length;
|
||||||
await runOperation("Stashing changes", async () => {
|
await runOperation("Stashing changes", async () => {
|
||||||
applyStatus(await stashPush(activeRepoPath, message, includeUntracked));
|
applyStatus(await stashPush(activeRepoPath, message, includeUntracked));
|
||||||
await refreshStashes(activeRepoPath);
|
await refreshStashes(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("stash_saved", {
|
||||||
|
include_untracked: includeUntracked ? 1 : 0,
|
||||||
|
changed_files: stashedFiles,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1931,6 +2023,7 @@
|
|||||||
await refreshStashes(activeRepoPath);
|
await refreshStashes(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("stash_applied");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1941,6 +2034,7 @@
|
|||||||
await refreshStashes(activeRepoPath);
|
await refreshStashes(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("stash_popped");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1952,6 +2046,7 @@
|
|||||||
await runOperation(`Dropping ${stash.selector}`, async () => {
|
await runOperation(`Dropping ${stash.selector}`, async () => {
|
||||||
applyStatus(await stashDrop(activeRepoPath, stash.selector));
|
applyStatus(await stashDrop(activeRepoPath, stash.selector));
|
||||||
await refreshStashes(activeRepoPath);
|
await refreshStashes(activeRepoPath);
|
||||||
|
trackEvent("stash_dropped");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1961,6 +2056,9 @@
|
|||||||
await runOperation(`Staging ${file.path}`, async () => {
|
await runOperation(`Staging ${file.path}`, async () => {
|
||||||
applyStatus(await stageFiles(activeRepoPath, [file.path]));
|
applyStatus(await stageFiles(activeRepoPath, [file.path]));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
|
trackEvent("file_staged", {
|
||||||
|
status: file.unstaged ?? file.staged ?? "unknown",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1968,12 +2066,19 @@
|
|||||||
await runOperation(`Unstaging ${file.path}`, async () => {
|
await runOperation(`Unstaging ${file.path}`, async () => {
|
||||||
applyStatus(await unstageFiles(activeRepoPath, [file.path]));
|
applyStatus(await unstageFiles(activeRepoPath, [file.path]));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
|
trackEvent("file_unstaged", {
|
||||||
|
status: file.staged ?? file.unstaged ?? "unknown",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function discardFile(file: GitFileStatus, staged: boolean) {
|
function discardFile(file: GitFileStatus, staged: boolean) {
|
||||||
if (!activeRepoPath || isBusy) return;
|
if (!activeRepoPath || isBusy) return;
|
||||||
pendingDiscard = { kind: "file", file, staged };
|
pendingDiscard = { kind: "file", file, staged };
|
||||||
|
trackEvent("discard_confirm_opened", {
|
||||||
|
kind: "file",
|
||||||
|
staged: staged ? 1 : 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runDiscardFile(file: GitFileStatus, staged: boolean) {
|
async function runDiscardFile(file: GitFileStatus, staged: boolean) {
|
||||||
@@ -1981,6 +2086,9 @@
|
|||||||
applyStatus(await restoreFiles(activeRepoPath, [file.path], staged));
|
applyStatus(await restoreFiles(activeRepoPath, [file.path], staged));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("file_discarded", {
|
||||||
|
staged: staged ? 1 : 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1992,6 +2100,9 @@
|
|||||||
linePatchText = "";
|
linePatchText = "";
|
||||||
linePatchError = "";
|
linePatchError = "";
|
||||||
linePatchLoading = true;
|
linePatchLoading = true;
|
||||||
|
trackEvent("line_patch_opened", {
|
||||||
|
staged: staged ? 1 : 0,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
linePatchText = await getFilePatch(activeRepoPath, file.path, staged);
|
linePatchText = await getFilePatch(activeRepoPath, file.path, staged);
|
||||||
@@ -2083,6 +2194,9 @@
|
|||||||
linePatchFile = null;
|
linePatchFile = null;
|
||||||
linePatchText = "";
|
linePatchText = "";
|
||||||
}
|
}
|
||||||
|
trackEvent("line_patch_applied", {
|
||||||
|
action,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
linePatchError = errorToMessage(error);
|
linePatchError = errorToMessage(error);
|
||||||
errorMessage = linePatchError;
|
errorMessage = linePatchError;
|
||||||
@@ -2098,6 +2212,10 @@
|
|||||||
|
|
||||||
if (isDiscardPatchAction(action)) {
|
if (isDiscardPatchAction(action)) {
|
||||||
pendingDiscard = { kind: "hunk", file, staged, action, patch };
|
pendingDiscard = { kind: "hunk", file, staged, action, patch };
|
||||||
|
trackEvent("discard_confirm_opened", {
|
||||||
|
kind: "hunk",
|
||||||
|
staged: staged ? 1 : 0,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2128,6 +2246,9 @@
|
|||||||
await runOperation("Staging all", async () => {
|
await runOperation("Staging all", async () => {
|
||||||
applyStatus(await stageFiles(activeRepoPath, paths));
|
applyStatus(await stageFiles(activeRepoPath, paths));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
|
trackEvent("all_files_staged", {
|
||||||
|
files: paths.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2137,6 +2258,9 @@
|
|||||||
await runOperation("Unstaging all", async () => {
|
await runOperation("Unstaging all", async () => {
|
||||||
applyStatus(await unstageFiles(activeRepoPath, paths));
|
applyStatus(await unstageFiles(activeRepoPath, paths));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
|
trackEvent("all_files_unstaged", {
|
||||||
|
files: paths.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2227,6 +2351,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("commit_undone");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2242,6 +2367,7 @@
|
|||||||
await refreshCommitHistory(activeRepoPath);
|
await refreshCommitHistory(activeRepoPath);
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("commit_restored");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2253,6 +2379,7 @@
|
|||||||
applyStatus(await restoreFileFromCommit(activeRepoPath, target.hash, file.path));
|
applyStatus(await restoreFileFromCommit(activeRepoPath, target.hash, file.path));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("commit_file_restored");
|
||||||
});
|
});
|
||||||
return !errorMessage;
|
return !errorMessage;
|
||||||
}
|
}
|
||||||
@@ -2268,6 +2395,10 @@
|
|||||||
selectedDiffPath = matchingFile?.path ?? result.files[0]?.path ?? file.path;
|
selectedDiffPath = matchingFile?.path ?? result.files[0]?.path ?? file.path;
|
||||||
pendingRestoreFile = { commit: target, file };
|
pendingRestoreFile = { commit: target, file };
|
||||||
compareDialogOpen = true;
|
compareDialogOpen = true;
|
||||||
|
trackEvent("diff_opened", {
|
||||||
|
source: "commit_file",
|
||||||
|
files: result.files.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2276,16 +2407,25 @@
|
|||||||
function toggleExplorerFolder(node: ExplorerNode) {
|
function toggleExplorerFolder(node: ExplorerNode) {
|
||||||
if (node.kind !== "folder") return;
|
if (node.kind !== "folder") return;
|
||||||
const next = new Set(expandedExplorerPaths);
|
const next = new Set(expandedExplorerPaths);
|
||||||
if (next.has(node.path)) next.delete(node.path); else next.add(node.path);
|
const wasExpanded = next.has(node.path);
|
||||||
|
if (wasExpanded) next.delete(node.path); else next.add(node.path);
|
||||||
expandedExplorerPaths = next;
|
expandedExplorerPaths = next;
|
||||||
|
trackEvent("explorer_folder_toggled", {
|
||||||
|
expanded: wasExpanded ? 0 : 1,
|
||||||
|
expanded_folders: expandedExplorerPaths.size,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function expandAllExplorerFolders() {
|
function expandAllExplorerFolders() {
|
||||||
expandedExplorerPaths = allExplorerFolderPaths(repoFiles);
|
expandedExplorerPaths = allExplorerFolderPaths(repoFiles);
|
||||||
|
trackEvent("explorer_folders_expanded", {
|
||||||
|
folders: expandedExplorerPaths.size,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapseAllExplorerFolders() {
|
function collapseAllExplorerFolders() {
|
||||||
expandedExplorerPaths = new Set();
|
expandedExplorerPaths = new Set();
|
||||||
|
trackEvent("explorer_folders_collapsed");
|
||||||
}
|
}
|
||||||
|
|
||||||
function explorerParentFolders(path: string): string[] {
|
function explorerParentFolders(path: string): string[] {
|
||||||
@@ -2311,6 +2451,10 @@
|
|||||||
selectedExplorerPath = node.path;
|
selectedExplorerPath = node.path;
|
||||||
selectedExplorerKind = node.kind;
|
selectedExplorerKind = node.kind;
|
||||||
void loadSelectedFileHistory(node.path);
|
void loadSelectedFileHistory(node.path);
|
||||||
|
trackEvent("explorer_node_selected", {
|
||||||
|
kind: node.kind,
|
||||||
|
tracked: node.tracked ? 1 : 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectFileFromSearch(file: GitRepositoryFile) {
|
async function selectFileFromSearch(file: GitRepositoryFile) {
|
||||||
@@ -2320,6 +2464,10 @@
|
|||||||
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
||||||
|
|
||||||
void loadSelectedFileHistory(file.path);
|
void loadSelectedFileHistory(file.path);
|
||||||
|
trackEvent("explorer_file_selected", {
|
||||||
|
source: "search",
|
||||||
|
tracked: file.tracked ? 1 : 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFileFromStatus(file: GitFileStatus) {
|
function selectFileFromStatus(file: GitFileStatus) {
|
||||||
@@ -2329,6 +2477,10 @@
|
|||||||
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
||||||
|
|
||||||
void loadSelectedFileHistory(file.path);
|
void loadSelectedFileHistory(file.path);
|
||||||
|
trackEvent("explorer_file_selected", {
|
||||||
|
source: "status",
|
||||||
|
status: file.unstaged ?? file.staged ?? "unknown",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openFileFromExplorer(node: ExplorerNode) {
|
async function openFileFromExplorer(node: ExplorerNode) {
|
||||||
@@ -2339,6 +2491,9 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await openRepositoryFile(activeRepoPath, node.path);
|
await openRepositoryFile(activeRepoPath, node.path);
|
||||||
|
trackEvent("explorer_file_opened", {
|
||||||
|
tracked: node.tracked ? 1 : 0,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage = errorToMessage(error);
|
errorMessage = errorToMessage(error);
|
||||||
}
|
}
|
||||||
@@ -2353,6 +2508,9 @@
|
|||||||
applyStatus(await restoreFileFromCommit(activeRepoPath, target.hash, selectedExplorerPath));
|
applyStatus(await restoreFileFromCommit(activeRepoPath, target.hash, selectedExplorerPath));
|
||||||
await refreshExplorerFiles(activeRepoPath);
|
await refreshExplorerFiles(activeRepoPath);
|
||||||
await refreshFileHistory(activeRepoPath);
|
await refreshFileHistory(activeRepoPath);
|
||||||
|
trackEvent("selected_file_restored_from_commit", {
|
||||||
|
kind,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2379,6 +2537,9 @@
|
|||||||
pendingRestoreFile = null;
|
pendingRestoreFile = null;
|
||||||
compareSelectOpen = false;
|
compareSelectOpen = false;
|
||||||
compareDialogOpen = true;
|
compareDialogOpen = true;
|
||||||
|
trackEvent("compare_completed", {
|
||||||
|
files: result.files.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2391,6 +2552,10 @@
|
|||||||
diffHighlightQuery = "";
|
diffHighlightQuery = "";
|
||||||
pendingRestoreFile = null;
|
pendingRestoreFile = null;
|
||||||
compareDialogOpen = true;
|
compareDialogOpen = true;
|
||||||
|
trackEvent("diff_opened", {
|
||||||
|
source: "file_history",
|
||||||
|
files: result.files.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2403,12 +2568,17 @@
|
|||||||
diffHighlightQuery = lastSearchQuery;
|
diffHighlightQuery = lastSearchQuery;
|
||||||
pendingRestoreFile = null;
|
pendingRestoreFile = null;
|
||||||
compareDialogOpen = true;
|
compareDialogOpen = true;
|
||||||
|
trackEvent("diff_opened", {
|
||||||
|
source: "search_hit",
|
||||||
|
files: result.files.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeCompareDialog() {
|
function closeCompareDialog() {
|
||||||
compareDialogOpen = false;
|
compareDialogOpen = false;
|
||||||
pendingRestoreFile = null;
|
pendingRestoreFile = null;
|
||||||
|
trackEvent("compare_closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function restorePreviewedCommitFile() {
|
async function restorePreviewedCommitFile() {
|
||||||
@@ -2419,6 +2589,10 @@
|
|||||||
|
|
||||||
function selectDiffFile(file: GitDiffFile) {
|
function selectDiffFile(file: GitDiffFile) {
|
||||||
selectedDiffPath = file.path;
|
selectedDiffPath = file.path;
|
||||||
|
trackEvent("diff_file_selected", {
|
||||||
|
additions: file.additions,
|
||||||
|
deletions: file.deletions,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runGlobalSearch(query: string, caseSensitive: boolean, limit: number) {
|
async function runGlobalSearch(query: string, caseSensitive: boolean, limit: number) {
|
||||||
@@ -2479,6 +2653,9 @@
|
|||||||
preparedResolutions = {};
|
preparedResolutions = {};
|
||||||
resolveDialogOpen = true;
|
resolveDialogOpen = true;
|
||||||
await loadConflict(first);
|
await loadConflict(first);
|
||||||
|
trackEvent("resolve_dialog_opened", {
|
||||||
|
conflicts: conflictedFiles.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2486,11 +2663,16 @@
|
|||||||
if (path === conflictTarget || isBusy) return;
|
if (path === conflictTarget || isBusy) return;
|
||||||
await runOperation(`Loading ${path}`, async () => {
|
await runOperation(`Loading ${path}`, async () => {
|
||||||
await loadConflict(path);
|
await loadConflict(path);
|
||||||
|
trackEvent("conflict_file_selected");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleMarkResolved(path: string, resolution: PreparedResolution) {
|
async function handleMarkResolved(path: string, resolution: PreparedResolution) {
|
||||||
preparedResolutions = { ...preparedResolutions, [path]: resolution };
|
preparedResolutions = { ...preparedResolutions, [path]: resolution };
|
||||||
|
trackEvent("conflict_resolution_prepared", {
|
||||||
|
kind: resolution.kind,
|
||||||
|
prepared_files: Object.keys(preparedResolutions).length,
|
||||||
|
});
|
||||||
|
|
||||||
const next = conflictedFiles.find((f) => f.path !== path && preparedResolutions[f.path] == null);
|
const next = conflictedFiles.find((f) => f.path !== path && preparedResolutions[f.path] == null);
|
||||||
if (next) {
|
if (next) {
|
||||||
@@ -2525,6 +2707,10 @@
|
|||||||
} else {
|
} else {
|
||||||
await loadConflict(remaining[0].path);
|
await loadConflict(remaining[0].path);
|
||||||
}
|
}
|
||||||
|
trackEvent("conflicts_resolved", {
|
||||||
|
files: entries.length,
|
||||||
|
remaining: remaining.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user