Skip to content

Commit 91edd7b

Browse files
v2.1.0 add support for tags editing (#167) [skip ci]
Add ability to add/edit/delete tags on create, edit and new version screens. * cleanup, DRY for tags logic * styling fixes * feat: add key to tag, use item repeater. * update style of tag * add support for tab navigation * add missing bad request message * fix: copilot comments with duplicate tags, throwing an error for duplicates, and remove unused code * don't show close and dismiss button on new item page * fix split view width over emitting to events * increase standard height for secret
1 parent a78691e commit 91edd7b

20 files changed

Lines changed: 369 additions & 158 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,4 @@ $RECYCLE.BIN/
457457
/KeyVaultExplorer.Desktop/mac
458458
/mpdev
459459
.DS_Store
460+
/src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/msbuild.ps1

src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/App.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ protected override async void OnLaunched(LaunchActivatedEventArgs args)
7070
LogLevel.Error)
7171

7272
// Default filters for core Uno Platform namespaces
73-
.CoreLogLevel(LogLevel.Warning);
73+
.CoreLogLevel(LogLevel.Critical);
7474

7575
#if DEBUG
7676
//Uno Platform namespace filter groups

src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/AzureKeyVaultStudio.csproj

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<AssemblyName>AzureKeyVaultStudio</AssemblyName>
1313
<ApplicationId>io.github.cricketthomas.AzureKeyVaultExplorer</ApplicationId>
1414

15-
<ApplicationDisplayVersion>2.0.5.0</ApplicationDisplayVersion>
15+
<ApplicationDisplayVersion>2.1.0.0</ApplicationDisplayVersion>
1616
<ApplicationVersion>2</ApplicationVersion>
1717

1818
<ApplicationPublisher>cricketthomas</ApplicationPublisher>
@@ -77,6 +77,7 @@
7777
</PropertyGroup>
7878

7979

80+
8081
<ItemGroup>
8182
<PackageReference Include="Azure.ResourceManager.KeyVault" />
8283
<PackageReference Include="Azure.Security.KeyVault.Certificates" />
@@ -114,11 +115,4 @@
114115
<PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.DataGrid" />
115116
</ItemGroup>
116117

117-
118-
<ItemGroup>
119-
<Content Update="Assets\Images\kv-gray-3.png">
120-
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
121-
</Content>
122-
</ItemGroup>
123-
124118
</Project>

src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/Models/KeyVaultValuesAmalgamation.cs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.ObjectModel;
12
using System.Diagnostics.CodeAnalysis;
23
using Azure.Security.KeyVault.Certificates;
34
using Azure.Security.KeyVault.Keys;
@@ -33,6 +34,7 @@ public sealed class KeyVaultItemProperties
3334
public string[] TagValues => Tags is not null ? [.. Tags.Values] : [];
3435
public string[] TagKeys => Tags is not null ? [.. Tags.Keys] : [];
3536
public string TagValuesString => string.Join(", ", Tags?.Values ?? []);
37+
public ObservableCollection<TagItem> EditableTags { get; set; } = new ObservableCollection<TagItem>();
3638

3739
public DateTimeOffset? LastModifiedDate => UpdatedOn.HasValue ? UpdatedOn.Value.ToLocalTime() : CreatedOn?.ToLocalTime();
3840
public string? WhenLastModified => LastModifiedDate.HasValue ? FormatRelativeDate(LastModifiedDate.Value, true) : null;
@@ -108,15 +110,7 @@ public SecretProperties ToSecretProperties()
108110
properties.Enabled = Enabled;
109111
properties.NotBefore = NotBefore;
110112
properties.ExpiresOn = ExpiresOn;
111-
112-
if (Tags != null && Tags.Count > 0)
113-
{
114-
foreach (var tag in Tags)
115-
{
116-
properties.Tags[tag.Key] = tag.Value;
117-
}
118-
}
119-
113+
ApplyEditableTags(properties.Tags);
120114
return properties;
121115
}
122116

@@ -126,30 +120,15 @@ public KeyProperties ToKeyProperties()
126120
properties.Enabled = Enabled;
127121
properties.NotBefore = NotBefore;
128122
properties.ExpiresOn = ExpiresOn;
129-
130-
if (Tags != null && Tags.Count > 0)
131-
{
132-
foreach (var tag in Tags)
133-
{
134-
properties.Tags[tag.Key] = tag.Value;
135-
}
136-
}
137-
123+
ApplyEditableTags(properties.Tags);
138124
return properties;
139125
}
140126

141127
public CertificateProperties ToCertificateProperties()
142128
{
143129
var properties = new CertificateProperties(Id);
144130
properties.Enabled = Enabled;
145-
if (Tags != null && Tags.Count > 0)
146-
{
147-
foreach (var tag in Tags)
148-
{
149-
properties.Tags[tag.Key] = tag.Value;
150-
}
151-
}
152-
131+
ApplyEditableTags(properties.Tags);
153132
return properties;
154133
}
155134

@@ -188,7 +167,8 @@ private static KeyVaultItemProperties Create(
188167
RecoveryLevel = recoveryLevel,
189168
Managed = managed,
190169
Tags = tags is null ? new Dictionary<string, string>() : new Dictionary<string, string>(tags),
191-
Type = type
170+
Type = type,
171+
EditableTags = tags is null ? []: new ObservableCollection<TagItem>(tags.Select(t => new TagItem { Key = t.Key, Value = t.Value }))
192172
};
193173
}
194174

@@ -225,6 +205,32 @@ private static KeyVaultItemProperties Create(
225205
( < 366, _) => $"{(isPast ? string.Empty : "in ")}{months} {(months == 1 ? "month" : "months")}{(isPast ? " ago" : string.Empty)}",
226206
(_, _) => $"{(isPast ? string.Empty : "in ")}{years} {(years == 1 ? "year" : "years")}{(isPast ? " ago" : string.Empty)}"
227207
};
208+
209+
210+
211+
}
212+
213+
public void ApplyEditableTags(IDictionary<string, string> targetTags)
214+
{
215+
targetTags.Clear();
216+
217+
if (EditableTags is null || EditableTags.Count == 0)
218+
return;
219+
220+
var seenKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
221+
222+
foreach (var tag in EditableTags)
223+
{
224+
var key = tag.Key?.Trim();
225+
226+
if (string.IsNullOrWhiteSpace(key))
227+
continue;
228+
229+
if (!seenKeys.Add(key))
230+
throw new InvalidOperationException("Duplicate tag keys are not allowed.");
231+
232+
targetTags[key] = tag.Value;
233+
}
228234
}
229235
}
230236

@@ -235,3 +241,12 @@ public enum KeyVaultItemType
235241
Key = 2,
236242
All = 3
237243
}
244+
245+
public partial class TagItem : ObservableObject
246+
{
247+
[ObservableProperty]
248+
public partial string Key { get; set; } = string.Empty;
249+
250+
[ObservableProperty]
251+
public partial string Value { get; set; } = string.Empty;
252+
}

src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/Package.appxmanifest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
xmlns:uap18="http://schemas.microsoft.com/appx/manifest/uap/windows10/18"
77
IgnorableNamespaces="uap rescap uap18">
88

9-
<Identity Version="2.0.5.0" Publisher="CN=FE9032D7-9FA7-4DED-9087-469BC45B4D99" Name="ArthurThomasIV.AzureKeyVaultExplorer-forAzure"/>
9+
<Identity Version="2.1.0.0" Publisher="CN=FE9032D7-9FA7-4DED-9087-469BC45B4D99" Name="ArthurThomasIV.AzureKeyVaultExplorer-forAzure"/>
1010
<Properties>
1111
<DisplayName>Key Vault Explorer</DisplayName>
1212
<PublisherDisplayName>Arthur Thomas IV</PublisherDisplayName>

src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/Presentation/MainViewModel.cs

Lines changed: 53 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,16 @@ namespace AzureKeyVaultStudio.Presentation;
77

88
public partial class MainViewModel : ObservableObject
99
{
10+
private const double PaneMaxWidth = 800;
11+
private const double PaneMinWidth = 120;
1012
private readonly IAuthenticationService _authentication;
13+
private readonly AuthService _authService;
14+
private readonly IDispatcher _dispatcher;
15+
private readonly KeyVaultTreeViewModel _keyVaultTreeViewModel;
1116
private readonly ILocalSettingsService _localSettings;
1217
private readonly INavigator _navigator;
13-
private readonly IDispatcher _dispatcher;
14-
private readonly VaultService _vaultService;
15-
private readonly AuthService _authService;
1618
private readonly IServiceProvider _serviceProvider;
17-
18-
private readonly KeyVaultTreeViewModel _keyVaultTreeViewModel;
19-
public KeyVaultTreeViewModel KeyVaultTreeViewModel => _keyVaultTreeViewModel;
20-
21-
19+
private readonly VaultService _vaultService;
2220
public MainViewModel(
2321
IDispatcher dispatcher,
2422
IStringLocalizer localizer,
@@ -37,7 +35,7 @@ public MainViewModel(
3735
_dispatcher = dispatcher;
3836
_vaultService = vaultService;
3937
_serviceProvider = serviceProvider;
40-
keyVaultTreeViewModel.SetDispatcher(dispatcher); // HACK
38+
keyVaultTreeViewModel.SetDispatcher(dispatcher); // HACK
4139
_keyVaultTreeViewModel = keyVaultTreeViewModel;
4240
_authService = authService;
4341

@@ -56,30 +54,33 @@ public MainViewModel(
5654
});
5755
}
5856

59-
public string? Title { get; }
60-
57+
public string FontIconType => PanePlacement == SplitViewPanePlacement.Left ? "\uE8E4" : "\uE8E2";
6158
public ICommand GoToSecond { get; }
62-
63-
public ICommand Logout { get; }
64-
65-
//private async Task GoToSecondView()
66-
//{
67-
// await _navigator.NavigateViewModelAsync<SecondViewModel>(this, data: new Entity(Name!));
68-
//}
69-
70-
public async Task DoLogout(CancellationToken token)
59+
public double InvertedSplitViewWidth
7160
{
72-
await _authentication.LogoutAsync(token);
73-
}
61+
get => PaneMaxWidth - (SplitViewWidth - PaneMinWidth);
62+
set
63+
{
64+
var clamped = Math.Clamp(value, PaneMinWidth, PaneMaxWidth);
65+
var newActual = PaneMaxWidth - (clamped - PaneMinWidth);
7466

75-
[ObservableProperty]
76-
public partial SplitViewDisplayMode SplitViewDisplay { get; set; } = SplitViewDisplayMode.Inline;
67+
if (SplitViewWidth != newActual)
68+
{
69+
SplitViewWidth = newActual;
70+
}
71+
}
72+
}
7773

74+
public bool IsPaneLeft => PanePlacement == SplitViewPanePlacement.Left;
7875
[ObservableProperty]
7976
public partial bool IsPaneOpen { get; set; } = true;
8077

78+
public KeyVaultTreeViewModel KeyVaultTreeViewModel => _keyVaultTreeViewModel;
79+
public ICommand Logout { get; }
8180
[ObservableProperty]
82-
public partial double SplitViewWidth { get; set; } = 220;
81+
[NotifyPropertyChangedFor(nameof(IsPaneLeft))]
82+
[NotifyPropertyChangedFor(nameof(FontIconType))]
83+
public partial SplitViewPanePlacement PanePlacement { get; set; } = SplitViewPanePlacement.Right;
8384

8485
[ObservableProperty]
8586
public partial string SearchQuery { get; set; } = string.Empty;
@@ -88,43 +89,24 @@ public async Task DoLogout(CancellationToken token)
8889
public partial object? SelectedKeyVaultItem { get; set; }
8990

9091
[ObservableProperty]
91-
[NotifyPropertyChangedFor(nameof(IsPaneLeft))]
92-
[NotifyPropertyChangedFor(nameof(FontIconType))]
93-
public partial SplitViewPanePlacement PanePlacement { get; set; } = SplitViewPanePlacement.Right;
94-
95-
public bool IsPaneLeft => PanePlacement == SplitViewPanePlacement.Left;
96-
97-
public string FontIconType => PanePlacement == SplitViewPanePlacement.Left ? "\uE8E4" : "\uE8E2";
98-
//public FontIcon FontIconType => PanePlacement == SplitViewPanePlacement.Left ? new FontIcon() { Glyph = "&#xE8E4;" } : new FontIcon() { Glyph = "&#xE8E2;" };
99-
100-
private void LoadSplitViewSettings()
101-
{
102-
SplitViewDisplay = GetSettingsValue(nameof(SplitViewDisplay), SplitViewDisplayMode.Inline);
103-
SplitViewWidth = _localSettings.GetValue(nameof(SplitViewWidth), 300d);
104-
IsPaneOpen = _localSettings.GetValue(nameof(IsPaneOpen), true);
105-
PanePlacement = GetSettingsValue(nameof(PanePlacement), SplitViewPanePlacement.Right);
106-
}
107-
92+
public partial SplitViewDisplayMode SplitViewDisplay { get; set; } = SplitViewDisplayMode.Inline;
10893

94+
[ObservableProperty]
95+
public partial double SplitViewWidth { get; set; } = 220;
10996

97+
public string? Title { get; }
11098
[RelayCommand]
111-
public void ToggleSplitViewDisplay()
99+
public void ClosePane()
112100
{
113-
SplitViewDisplay = SplitViewDisplay == SplitViewDisplayMode.Overlay ? SplitViewDisplayMode.Inline : SplitViewDisplayMode.Overlay;
101+
IsPaneOpen = false;
102+
WeakReferenceMessenger.Default.Send(new PaneStateChangedMessage(false));
114103
}
115104

116-
[RelayCommand]
117-
public void TogglePaneLocation()
105+
public async Task DoLogout(CancellationToken token)
118106
{
119-
PanePlacement = PanePlacement == SplitViewPanePlacement.Right ? SplitViewPanePlacement.Left : SplitViewPanePlacement.Right;
107+
await _authentication.LogoutAsync(token);
120108
}
121109

122-
123-
124-
private void Save<T>(string key, T value)
125-
{
126-
_localSettings.SetValue(key, value);
127-
}
128110
[RelayCommand]
129111
public void SaveAllSettings()
130112
{
@@ -134,35 +116,33 @@ public void SaveAllSettings()
134116
Save(nameof(IsPaneOpen), IsPaneOpen);
135117
}
136118

137-
138119
[RelayCommand]
139-
public void ClosePane()
120+
public void TogglePaneLocation()
140121
{
141-
IsPaneOpen = false;
142-
WeakReferenceMessenger.Default.Send(new PaneStateChangedMessage(false));
122+
PanePlacement = PanePlacement == SplitViewPanePlacement.Right ? SplitViewPanePlacement.Left : SplitViewPanePlacement.Right;
143123
}
144124

145-
private const double PaneMinWidth = 100;
146-
private const double PaneMaxWidth = 1000;
147-
148-
public double InvertedSplitViewWidth
125+
[RelayCommand]
126+
public void ToggleSplitViewDisplay()
149127
{
150-
get => PaneMaxWidth - (SplitViewWidth - PaneMinWidth);
151-
set
152-
{
153-
var clamped = Math.Clamp(value, PaneMinWidth, PaneMaxWidth);
154-
var newActual = PaneMaxWidth - (clamped - PaneMinWidth);
155-
156-
if (SplitViewWidth != newActual)
157-
{
158-
SplitViewWidth = newActual;
159-
OnPropertyChanged(nameof(SplitViewWidth));
160-
}
161-
}
128+
SplitViewDisplay = SplitViewDisplay == SplitViewDisplayMode.Overlay ? SplitViewDisplayMode.Inline : SplitViewDisplayMode.Overlay;
162129
}
130+
163131
private T GetSettingsValue<T>(string key, T defaultValue) where T : struct, Enum
164132
{
165133
var stringValue = _localSettings.GetValue(key, defaultValue.ToString());
166134
return Enum.TryParse<T>(stringValue, out var result) ? result : defaultValue;
167135
}
136+
137+
private void LoadSplitViewSettings()
138+
{
139+
SplitViewDisplay = GetSettingsValue(nameof(SplitViewDisplay), SplitViewDisplayMode.Inline);
140+
SplitViewWidth = _localSettings.GetValue(nameof(SplitViewWidth), 300d);
141+
IsPaneOpen = _localSettings.GetValue(nameof(IsPaneOpen), true);
142+
PanePlacement = GetSettingsValue(nameof(PanePlacement), SplitViewPanePlacement.Right);
143+
}
144+
private void Save<T>(string key, T value)
145+
{
146+
_localSettings.SetValue(key, value);
147+
}
168148
}

src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/Presentation/VaultTabContentPage.xaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,11 @@
278278
Height="20"
279279
MaxWidth="80"
280280
Padding="5,2"
281-
Background="{ThemeResource SystemAccentColor}"
281+
Background="{ThemeResource AccentAcrylicBackgroundFillColorDefaultBrush}"
282282
CornerRadius="2">
283283
<TextBlock
284284
VerticalAlignment="Center"
285285
FontSize="11"
286-
Foreground="White"
287286
IsTextSelectionEnabled="False"
288287
Text="{x:Bind Mode=OneTime}"
289288
TextTrimming="CharacterEllipsis" />

0 commit comments

Comments
 (0)