In the previous chapter, Terminal UI Rendering (Ink), we learned how to draw beautiful interface components like the Upsell Dialog.
However, just because we can draw a dialog doesn't mean we should show it every single time. Imagine if every time you opened your terminal, a popup asked you to buy something. You would uninstall that tool immediately!
In this chapter, we will build the Logic Layerβthe "brain" that decides whether the user is eligible to see the Upsell Dialog.
Think of your application as an exclusive nightclub. The Upsell Dialog is a special VIP party inside. Not everyone gets in.
We need a Bouncer (or Doorman) at the entrance. Before opening the door (rendering the UI), the Bouncer checks three things in a specific order:
If any of these checks fail, the Bouncer says "Move along," and the app starts normally without the popup.
We implement this logic in a function called shouldShowDesktopUpsellStartup. Let's build it layer by layer.
First, we check if the user's computer can actually run the software we are promoting. There is no point in upsizing a Linux user if the desktop app only runs on macOS and Windows.
function isSupportedPlatform(): boolean {
// Check for macOS OR Windows (x64 architecture)
return (
process.platform === 'darwin' ||
(process.platform === 'win32' && process.arch === 'x64')
);
}
Explanation:
process.platform: Built-in Node.js information about the OS.false, the Bouncer stops here. The user never sees the dialog.Even if the user has the right hardware, we might want to turn the feature off globally (perhaps we found a bug, or we are only rolling it out to 10% of users).
export function shouldShowDesktopUpsellStartup(): boolean {
// 1. Check Hardware
if (!isSupportedPlatform()) return false;
// 2. Check Remote Configuration
if (!getDesktopUpsellConfig().enable_startup_dialog) return false;
// ... continued below
Explanation:
getDesktopUpsellConfig(): This fetches a configuration file from a server (we will build this in Dynamic Configuration (Feature Flags)).enable_startup_dialog is false, the club is closed.Finally, we check the local user history. We want to be polite.
// 3. Load Local User Config
const config = getGlobalConfig();
// Did they click "Don't ask again"?
if (config.desktopUpsellDismissed) return false;
// Have they seen it 3 times already?
if ((config.desktopUpsellSeenCount ?? 0) >= 3) return false;
return true; // All checks passed!
}
Explanation:
getGlobalConfig(): Reads a local file where we save user choices (covered in Global User State Persistence).Here is how our "Bouncer" makes the decision in milliseconds before the app starts.
Now, let's look at how we use this in our main application file. We wrap the UI rendering code from Chapter 1 inside this check.
// Inside the main entry point
import { shouldShowDesktopUpsellStartup } from './DesktopUpsellStartup';
export async function run() {
// The Bouncer Check
const showUpsell = shouldShowDesktopUpsellStartup();
if (showUpsell) {
// Render the Ink UI (Chapter 1)
render(<DesktopUpsellStartup />);
} else {
// Skip UI, run normal CLI command
runMainCommand();
}
}
What happens here?
shouldShowDesktopUpsellStartup.false, the user sees absolutely nothing different; the command simply runs.true, the CLI pauses and renders the Ink dialog.While the logic seems simple, it relies on two other powerful systems to work correctly.
In the code snippets above, you saw getGlobalConfig(). This is not just a variable in memory. If it were just a variable, the "Seen Count" would reset to 0 every time you closed the terminal.
This function actually reads from a JSON file stored on the user's hard drive.
getGlobalConfig(){ desktopUpsellSeenCount: 2, desktopUpsellDismissed: false }
You also saw getDesktopUpsellConfig(). This connects to a remote service.
In this chapter, we created a smart gating systemβour "Bouncer."
Right now, our "Remote Check" is just reading a hardcoded default value. In the next chapter, we will connect this to a real remote configuration system so we can control our CLI tool from the cloud.
Next Chapter: Dynamic Configuration (Feature Flags)
Generated by Code IQ