All files / src/commands auth-commands.ts

93.95% Statements 140/149
83.33% Branches 15/18
100% Functions 4/4
93.95% Lines 140/149

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 1501x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 1x 1x 1x 2x 2x 2x 2x 3x 1x 1x 1x 1x 1x 1x 1x 1x 3x       1x 3x     3x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 4x 4x 4x 4x 4x 4x 4x 1x 1x 1x 1x 1x 1x 1x 1x         4x 4x 4x 4x 4x 4x 4x 1x 12x 12x 1x 12x 12x 12x 1x 1x 12x 12x 12x  
import {
  getAuthContext,
  signInOrUpMenu,
  signOutMenu,
} from "../services/auth-service";
import * as vscode from "vscode";
import { getUserByID } from "../api/user-api";
import { setAuthContext } from "../services/auth-service";
import {
  errorNotification,
  showAuthNotification,
} from "../views/notifications";
 
/**
 * Registers the command to trigger the Sign In or Sign Up menu.
 */
export const signInCommand = vscode.commands.registerCommand(
  "clover.signIn",
  async () => signInOrUpMenu()
);
 
 
/**
 * Registers the command to trigger the Sign Out flow.
 */
export const signOutCommand = vscode.commands.registerCommand(
  "clover.signOut",
  async () => signOutMenu()
);
 
/**
 * Handles URIs sent back to the extension after OAuth authentication flow.
 *
 * Specifically listens for the `/auth-complete` URI, extracts the user token,
 * fetches the user, and updates the authentication context.
 */
export const handleAuthUri = async (uri: vscode.Uri) => {
  if (uri.path === "/auth-complete") {
    console.log("Received URI:", uri.toString());
    const urlParams = new URLSearchParams(uri.query);
    const token = urlParams.get("id"); // Extract token
    if (!token) {
      await errorNotification("No token found in URL.");
      return;
    }
 
    try {
      const { user, error } = await getUserByID(token);
 
      if (error || !user) {
        await errorNotification(`Failed to get user data: ${error}`);
        return;
      }
 
      user.isAuthenticated = true;
 
      const { error: authError } = await setAuthContext(user);
 
      if (authError) {
        await errorNotification(`Failed to set user context: ${authError}`);
        return;
      }
      await showAuthNotification(`Sign In successfully! 🎉`);
    } catch (err: any) {
      await errorNotification(`Unexpected error: ${err.message}`);
    }
  }
};
 
export const uriHandlerCommand = vscode.window.registerUriHandler({
  handleUri: handleAuthUri,
});
 
/**
 * Creates and manages the authentication status bar item for the extension.
 *
 * Dynamically updates the status bar based on the user's authentication state.
 *
 * @param context - The extension context for managing command subscriptions.
 * @returns A `StatusBarItem` representing the authentication action (sign in or sign out).
 */
export function createAuthStatusBarItem(context: vscode.ExtensionContext) {
  const authStatusBarItem = vscode.window.createStatusBarItem(
    vscode.StatusBarAlignment.Right,
    100
  );
 
  authStatusBarItem.name = "Clover Authentication";
  authStatusBarItem.backgroundColor = new vscode.ThemeColor(
    "statusBarItem.errorBackground"
  );
  authStatusBarItem.show();
 
  const updateAuthStatus = async () => {
    const { context: user } = await getAuthContext();
 
    if (user?.isAuthenticated) {
      authStatusBarItem.text = `$(sign-out) Sign Out`;
      authStatusBarItem.tooltip = `Signed in as ${user.email}`;
      authStatusBarItem.command = "clover.signOut";
      authStatusBarItem.backgroundColor = new vscode.ThemeColor(
        "statusBarItem.errorBackground"
      );
    } else {
      authStatusBarItem.text = `$(key) Sign In / Sign Up`;
      authStatusBarItem.tooltip = "Authenticate with Clover";
      authStatusBarItem.command = "clover.signIn";
      authStatusBarItem.backgroundColor = new vscode.ThemeColor(
        "statusBarItem.warningBackground"
      );
    }
  };
 
  updateAuthStatus();
 
  context.subscriptions.push(
    authStatusBarItem,
    vscode.commands.registerCommand("clover.showAuthOptions", async () => {
      const choice = await vscode.window.showQuickPick(
        ["Sign In with GitHub", "Sign In with Email", "Sign Up"],
        { placeHolder: "Select authentication method" }
      );
 
      if (choice === "Sign In with GitHub") {
        vscode.commands.executeCommand("clover.githubLogin");
      } else if (choice === "Sign In with Email") {
        vscode.commands.executeCommand("clover.emailLogin");
      } else if (choice === "Sign Up") {
        vscode.commands.executeCommand("clover.signUp");
      }
    }),
 
    vscode.commands.registerCommand("clover.authStateChanged", updateAuthStatus)
  );
 
  return authStatusBarItem;
}
 
export function registerAuthCommands() {
  vscode.commands.registerCommand("clover.signIn", async () =>
    signInOrUpMenu()
  );
  vscode.commands.registerCommand("clover.signOut", async () => signOutMenu());
  vscode.commands.registerCommand("clover.authStateChanged", async () => {
    const item = createAuthStatusBarItem({ subscriptions: [] } as any);
    item.show();
  });
  vscode.window.registerUriHandler({ handleUri: handleAuthUri });
}