Private/UI/Show-AcceptConditionsDialog.ps1

# Copyright (c) 2026 Sandy Zeng. All rights reserved.
# Source-available. All rights reserved. See LICENSE file.

<#
    Show-AcceptConditionsDialog.ps1 — Displays the accept conditions dialog on first launch.
 
    Author: Sandy Zeng
    Project: IntuneDiff
 
    Version History:
    1.0.0 Initial release.
    1.0.1 Added accept conditions dialog shown on first launch.
#>


function Show-AcceptConditionsDialog {
    <#
    .SYNOPSIS
        Shows an Accept Conditions dialog before the main window launches.
        Returns $true if the user accepts (or has previously accepted), $false otherwise.
    #>

    [CmdletBinding()]
    param()

    # Check if user already accepted conditions previously
    $acceptedFile = Join-Path $env:LOCALAPPDATA 'IntuneDiff\accepted.flag'
    if (Test-Path -LiteralPath $acceptedFile) {
        return $true
    }

    Add-Type -AssemblyName PresentationFramework
    Add-Type -AssemblyName PresentationCore
    Add-Type -AssemblyName WindowsBase

    $xaml = @'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="IntuneDiff" Height="520" Width="620"
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
        Background="#111827">
  <Window.Resources>
    <Style TargetType="Button" x:Key="AcceptButton">
      <Setter Property="Padding" Value="20,10"/>
      <Setter Property="FontSize" Value="13"/>
      <Setter Property="FontWeight" Value="SemiBold"/>
      <Setter Property="Background" Value="#3498DB"/>
      <Setter Property="Foreground" Value="White"/>
      <Setter Property="BorderThickness" Value="0"/>
      <Setter Property="Cursor" Value="Hand"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Button">
            <Border x:Name="bd" CornerRadius="8" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
              <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="bd" Property="Background" Value="#2E86C1"/>
              </Trigger>
              <Trigger Property="IsPressed" Value="True">
                <Setter TargetName="bd" Property="Background" Value="#2471A3"/>
              </Trigger>
              <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Opacity" Value="0.4"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
    <Style TargetType="Button" x:Key="CancelButton">
      <Setter Property="Padding" Value="20,10"/>
      <Setter Property="FontSize" Value="13"/>
      <Setter Property="FontWeight" Value="Medium"/>
      <Setter Property="Background" Value="#374151"/>
      <Setter Property="Foreground" Value="#F3F4F6"/>
      <Setter Property="BorderThickness" Value="0"/>
      <Setter Property="Cursor" Value="Hand"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Button">
            <Border x:Name="bd" CornerRadius="8" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
              <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="bd" Property="Background" Value="#4B5563"/>
              </Trigger>
              <Trigger Property="IsPressed" Value="True">
                <Setter TargetName="bd" Property="Background" Value="#6B7280"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <Grid Margin="32">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
 
    <!-- Title -->
    <TextBlock Grid.Row="0" Text="IntuneDiff" FontSize="24" FontWeight="Bold" Foreground="#F3F4F6" Margin="0,0,0,4"/>
    <TextBlock Grid.Row="1" Text="Welcome!" FontSize="16" FontWeight="SemiBold" Foreground="#9CA3AF" Margin="0,0,0,16"/>
 
    <!-- Conditions text -->
    <Border Grid.Row="2" Background="#1F2937" CornerRadius="8" Padding="20" Margin="0,0,0,16">
      <ScrollViewer VerticalScrollBarVisibility="Auto">
        <TextBlock TextWrapping="Wrap" Foreground="#D1D5DB" FontSize="13" LineHeight="22">
          <Run FontWeight="SemiBold" Foreground="#F3F4F6">This software uses public APIs (e.g. Microsoft Graph) to read and compare Intune configuration policies.</Run>
          <LineBreak/><LineBreak/>
          <Run>It uses the BETA version of Microsoft Graph so some functionality might change or break.</Run>
          <LineBreak/><LineBreak/>
          <Run>Please report any issues to the GitHub repository. This is developed outside work hours so please respect that any support is based on best effort.</Run>
          <LineBreak/><LineBreak/>
          <Run FontWeight="SemiBold" Foreground="#F3F4F6">This software has NO association with Microsoft.</Run>
          <LineBreak/><LineBreak/>
          <Run>This software is provided AS IS and it is licensed under a Source-Available License. See the LICENSE file for more information.</Run>
          <LineBreak/><LineBreak/>
          <Run FontWeight="SemiBold" Foreground="#F3F4F6">Note:</Run>
          <LineBreak/>
          <Run>&#x2022; The 'Microsoft Graph PowerShell' Enterprise App in Azure is used by default for API calls.</Run>
          <LineBreak/>
          <Run>&#x2022; This software might use permissions not granted for your existing App.</Run>
          <LineBreak/>
          <Run>&#x2022; A consent request will be displayed if permissions are missing.</Run>
        </TextBlock>
      </ScrollViewer>
    </Border>
 
    <!-- Accept checkbox -->
    <CheckBox x:Name="AcceptCheckBox" Grid.Row="3" Margin="0,0,0,16" Foreground="#F3F4F6" FontSize="13">
      <CheckBox.Content>
        <TextBlock Text="I accept the conditions" Foreground="#F3F4F6"/>
      </CheckBox.Content>
    </CheckBox>
 
    <!-- Buttons -->
    <StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right">
      <Button x:Name="OkButton" Content="OK" Style="{StaticResource AcceptButton}" MinWidth="100" IsEnabled="False"/>
      <Button x:Name="CancelButton" Content="Cancel" Style="{StaticResource CancelButton}" MinWidth="100" Margin="12,0,0,0"/>
    </StackPanel>
  </Grid>
</Window>
'@


    $reader = New-Object System.Xml.XmlNodeReader ([xml]$xaml)
    $dlgWindow = [Windows.Markup.XamlReader]::Load($reader)

    $acceptCheckBox = $dlgWindow.FindName('AcceptCheckBox')
    $okButton       = $dlgWindow.FindName('OkButton')
    $cancelButton   = $dlgWindow.FindName('CancelButton')

    # Enable OK only when checkbox is checked
    $acceptCheckBox.Add_Checked({ $okButton.IsEnabled = $true })
    $acceptCheckBox.Add_Unchecked({ $okButton.IsEnabled = $false })

    # Set dialog result on button clicks
    $okButton.Add_Click({
        $dlgWindow.Tag = $true
        $dlgWindow.Close()
    })
    $cancelButton.Add_Click({
        $dlgWindow.Tag = $false
        $dlgWindow.Close()
    })

    # Load logo if available
    $assetsRoot = Join-Path $PSScriptRoot '..\..\Assets'
    $logoPath = Join-Path $assetsRoot 'intune-diff-logo.png'
    if (Test-Path -LiteralPath $logoPath) {
        $bmp = New-Object System.Windows.Media.Imaging.BitmapImage
        $bmp.BeginInit()
        $bmp.UriSource = [System.Uri]::new((Resolve-Path -LiteralPath $logoPath).Path)
        $bmp.CacheOption = [System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad
        $bmp.EndInit()
        $bmp.Freeze()
        $dlgWindow.Icon = $bmp
    }

    $dlgWindow.ShowDialog() | Out-Null

    $accepted = ($dlgWindow.Tag -eq $true)

    # Persist acceptance so the dialog won't show again
    if ($accepted) {
        $flagDir = Split-Path $acceptedFile -Parent
        if (-not (Test-Path $flagDir)) { New-Item -ItemType Directory -Path $flagDir -Force | Out-Null }
        [System.IO.File]::WriteAllText($acceptedFile, (Get-Date -Format 'o'))
    }

    return $accepted
}