diff --git a/ReadMe.md b/ReadMe.md index 2786593..1cf0763 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -144,6 +144,20 @@ Possible values for the `-AutomaticallyWritePowerShellTip` parameter are `Never` Tips will only be automatically shown in interactive PowerShell sessions. This prevents them from appearing unexpectedly when running scripts or other automated processes. +#### Do not show the same tips twice + +By default, tiPS will cycle through all available tips, so once all tips have been shown it will restart and display them again. +If you prefer to only see tips that haven't been shown before, you can configure tiPS to only show unshown tips by running: + +```powershell +Set-TiPSConfiguration -AllTipsShownBehaviour DoNotShowTips +``` + +Possible values for the `-AllTipsShownBehaviour` parameter are `ClearShownTipsList` (default) and `DoNotShowTips`. + +When `DoNotShowTips` is used, tiPS will only automatically display a tip if there are tips that have not been shown yet. +Once all tips have been shown, no more tips will be displayed until new tips are added (by updating the module). + ### ⬆️ Automatic updates New tips are obtained by updating the tiPS module. diff --git a/src/CSharpClasses/tiPSClasses/Configuration.cs b/src/CSharpClasses/tiPSClasses/Configuration.cs index 95dd6d3..1841b15 100644 --- a/src/CSharpClasses/tiPSClasses/Configuration.cs +++ b/src/CSharpClasses/tiPSClasses/Configuration.cs @@ -26,17 +26,25 @@ public enum TipRetrievalOrder Random = 2 } + public enum AllTipsShownBehaviours + { + ClearShownTipsList = 0, + DoNotShowTips = 1 + } + public class Configuration { public ModuleAutoUpdateCadence AutoUpdateCadence { get; set; } public WritePowerShellTipCadence AutoWritePowerShellTipCadence { get; set; } public TipRetrievalOrder TipRetrievalOrder { get; set; } + public AllTipsShownBehaviours AllTipsShownBehaviour { get; set; } public Configuration() { AutoUpdateCadence = ModuleAutoUpdateCadence.Never; AutoWritePowerShellTipCadence = WritePowerShellTipCadence.Never; TipRetrievalOrder = TipRetrievalOrder.NewestFirst; + AllTipsShownBehaviour = AllTipsShownBehaviours.ClearShownTipsList; } } } diff --git a/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.Tests.ps1 b/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.Tests.ps1 index 3196ba0..fb4bea5 100644 --- a/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.Tests.ps1 +++ b/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.Tests.ps1 @@ -141,6 +141,30 @@ InModuleScope -ModuleName tiPS { # Must use InModuleScope to call private functi Assert-MockCalled WriteAutomaticPowerShellTip -Times 0 -Exactly } } + + Context 'When the AllTipsShownBehaviour is DoNotShowTips' { + It 'Should show a tip when there are unseen tips' { + $config = [tiPS.Configuration]::new() + $config.AutoWritePowerShellTipCadence = [tiPS.WritePowerShellTipCadence]::EverySession + $config.AllTipsShownBehaviour = [tiPS.AllTipsShownBehaviours]::DoNotShowTips + Mock -CommandName TestIfUnseenTipsExist -MockWith { return $true } + + WriteAutomaticPowerShellTipIfNeeded -Config $config + + Assert-MockCalled WriteAutomaticPowerShellTip -Times 1 -Exactly + } + + It 'Should not show a tip when all tips have been shown' { + $config = [tiPS.Configuration]::new() + $config.AutoWritePowerShellTipCadence = [tiPS.WritePowerShellTipCadence]::EverySession + $config.AllTipsShownBehaviour = [tiPS.AllTipsShownBehaviours]::DoNotShowTips + Mock -CommandName TestIfUnseenTipsExist -MockWith { return $false } + + WriteAutomaticPowerShellTipIfNeeded -Config $config + + Assert-MockCalled WriteAutomaticPowerShellTip -Times 0 -Exactly + } + } } Describe 'Calling WriteLastAutomaticTipWrittenDate' { @@ -164,4 +188,51 @@ InModuleScope -ModuleName tiPS { # Must use InModuleScope to call private functi $lastAutomaticTipWrittenDate | Should -Be $today } } + + Describe 'Calling TestIfUnseenTipsExist' { + BeforeEach { + # Use a temp configuration data directory instead of reading/overwriting the current user's configuration. + Mock -CommandName Get-TiPSDataDirectoryPath -MockWith { + [string] $directoryPath = "$TestDrive/tiPS" # Use $TestDrive variable so .NET methods can resolve the path. + if (-not (Test-Path -Path $directoryPath -PathType Container)) + { + New-Item -Path $directoryPath -ItemType Directory -Force > $null + } + return $directoryPath + } + } + + It 'Should return true when no tips have been shown yet' { + # Ensure the file is empty + ClearTipIdsAlreadyShown + + $result = TestIfUnseenTipsExist + + $result | Should -Be $true + } + + It 'Should return true when some tips have been shown but not all' { + # Get all tips and mark only one as shown + [hashtable] $allTips = ReadAllPowerShellTipsFromJsonFile + [string] $firstTipId = $allTips.Keys | Select-Object -First 1 + AppendTipIdToTipIdsAlreadyShown -TipId $firstTipId + + $result = TestIfUnseenTipsExist + + $result | Should -Be $true + } + + It 'Should return false when all tips have been shown' { + # Mark all tips as shown + [hashtable] $allTips = ReadAllPowerShellTipsFromJsonFile + foreach ($tipId in $allTips.Keys) + { + AppendTipIdToTipIdsAlreadyShown -TipId $tipId + } + + $result = TestIfUnseenTipsExist + + $result | Should -Be $false + } + } } diff --git a/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.ps1 b/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.ps1 index b7005a2..1c52430 100644 --- a/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.ps1 +++ b/src/tiPS/Private/AutomaticWritePowerShellTipFunctions.ps1 @@ -30,6 +30,12 @@ function WriteAutomaticPowerShellTipIfNeeded ([tiPS.WritePowerShellTipCadence]::Monthly) { $shouldShowTip = $daysSinceLastAutomaticTipWritten -ge 30; break } } + # If the cadence says we should show a tip, check if we should only show unseen tips + if ($shouldShowTip -and $Config.AllTipsShownBehaviour -eq [tiPS.AllTipsShownBehaviours]::DoNotShowTips) + { + $shouldShowTip = TestIfUnseenTipsExist + } + if ($shouldShowTip) { [bool] $isSessionInteractive = TestPowerShellSessionIsInteractive @@ -137,3 +143,31 @@ function GetLastAutomaticTipWrittenDateFilePath [string] $lastAutomaticTipWrittenDateFilePath = Join-Path -Path $appDataDirectoryPath -ChildPath 'LastAutomaticTipWrittenDate.txt' return $lastAutomaticTipWrittenDateFilePath } + +function TestIfUnseenTipsExist +{ + [CmdletBinding()] + [OutputType([bool])] + Param() + + [hashtable] $allTips = ReadAllPowerShellTipsFromJsonFile + [string[]] $tipIdsAlreadyShown = ReadTipIdsAlreadyShownOrDefault + + # If no tips have been shown yet, there are definitely unseen tips + if ($tipIdsAlreadyShown.Count -eq 0) + { + return $true + } + + # Check if there are any tips that haven't been shown yet + foreach ($tipId in $allTips.Keys) + { + if ($tipId -notin $tipIdsAlreadyShown) + { + return $true + } + } + + # All tips have been shown + return $false +} diff --git a/src/tiPS/Public/Set-TiPSConfiguration.ps1 b/src/tiPS/Public/Set-TiPSConfiguration.ps1 index 438cc58..4cde6c4 100644 --- a/src/tiPS/Public/Set-TiPSConfiguration.ps1 +++ b/src/tiPS/Public/Set-TiPSConfiguration.ps1 @@ -24,6 +24,11 @@ function Set-TiPSConfiguration Whether to automatically write a PowerShell tip at session startup. Valid values are Never, EverySession, Daily, Weekly, Biweekly, and Monthly. Default is Never. + .PARAMETER AllTipsShownBehaviour + Controls how tips are selected when automatically showing tips. + Valid values are ClearShownTipsList (default) and DoNotShowTips. + DoNotShowTips will only show tips that have not been shown before. + .PARAMETER TipRetrievalOrder The order in which to retrieve PowerShell tips. Valid values are NewestFirst, OldestFirst, and Random. Default is NewestFirst. @@ -60,6 +65,11 @@ function Set-TiPSConfiguration Set-TiPSConfiguration -TipRetrievalOrder Random Set the tiPS configuration to retrieve PowerShell tips in random order. + + .EXAMPLE + Set-TiPSConfiguration -AutomaticallyWritePowerShellTip Daily -AllTipsShownBehaviour DoNotShowTips + + Set the tiPS configuration to automatically write a PowerShell tip every day, but only if there are tips that have not been shown yet. #> [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'PartialConfiguration')] [OutputType([void])] @@ -75,6 +85,9 @@ function Set-TiPSConfiguration [Parameter(Mandatory = $false, ParameterSetName = 'PartialConfiguration', ValueFromPipelineByPropertyName = $true)] [tiPS.WritePowerShellTipCadence] $AutomaticallyWritePowerShellTip = [tiPS.WritePowerShellTipCadence]::Never, + [Parameter(Mandatory = $false, ParameterSetName = 'PartialConfiguration', ValueFromPipelineByPropertyName = $true)] + [tiPS.AllTipsShownBehaviours] $AllTipsShownBehaviour = [tiPS.AllTipsShownBehaviours]::ClearShownTipsList, + [Parameter(Mandatory = $false, ParameterSetName = 'PartialConfiguration', ValueFromPipelineByPropertyName = $true)] [Alias('TipOrder')] [tiPS.TipRetrievalOrder] $TipRetrievalOrder = [tiPS.TipRetrievalOrder]::NewestFirst @@ -118,6 +131,15 @@ function Set-TiPSConfiguration } } + # If the AllTipsShownBehaviour parameter is passed in, set it. + if ($PSBoundParameters.ContainsKey('AllTipsShownBehaviour')) + { + if ($PSCmdlet.ShouldProcess('tiPS configuration AllTipsShownBehaviour property', 'Set')) + { + $script:TiPSConfiguration.AllTipsShownBehaviour = $AllTipsShownBehaviour + } + } + Write-Debug "Saving the tiPS configuration to the configuration file." WriteConfigurationToFile -Config $script:TiPSConfiguration