add group tree display

This commit is contained in:
2026-05-03 15:24:37 +03:00
parent 1cb50c5b04
commit 69934338f2
5 changed files with 83 additions and 16 deletions

View File

@@ -20,6 +20,7 @@ public abstract class PassStoreEntry
return $"avares://KeyKeeper/Assets/builtin-entry-icon-{IconType}.svg"; return $"avares://KeyKeeper/Assets/builtin-entry-icon-{IconType}.svg";
} }
} }
public virtual string DisplayName => Name;
public void WriteToStream(Stream str) public void WriteToStream(Stream str)
{ {

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using static KeyKeeper.PasswordStore.FileFormatConstants; using static KeyKeeper.PasswordStore.FileFormatConstants;
namespace KeyKeeper.PasswordStore; namespace KeyKeeper.PasswordStore;
@@ -12,6 +13,16 @@ public class PassStoreEntryGroup : PassStoreEntry, IPassStoreDirectory
public Guid? CustomGroupSubtype { get; set; } public Guid? CustomGroupSubtype { get; set; }
public List<PassStoreEntry> ChildEntries { get; set; } public List<PassStoreEntry> ChildEntries { get; set; }
public override string DisplayName => GroupType switch
{
GROUP_TYPE_DEFAULT => "All Passwords",
GROUP_TYPE_FAVOURITES => "Favourites",
GROUP_TYPE_ROOT => ":root:",
_ => Name
};
public IEnumerable<PassStoreEntryGroup> ChildGroups => ChildEntries.OfType<PassStoreEntryGroup>();
public PassStoreEntryGroup(Guid id, DateTime createdAt, DateTime modifiedAt, public PassStoreEntryGroup(Guid id, DateTime createdAt, DateTime modifiedAt,
Guid iconType, string name, byte groupType, Guid iconType, string name, byte groupType,
List<PassStoreEntry>? children = null, List<PassStoreEntry>? children = null,

View File

@@ -269,6 +269,15 @@ public class PassStoreFileAccessor : IPassStore
"", "",
GROUP_TYPE_DEFAULT GROUP_TYPE_DEFAULT
); );
PassStoreEntryGroup favourites = new(
Guid.NewGuid(),
DateTime.UtcNow,
DateTime.UtcNow,
Guid.Empty,
"",
GROUP_TYPE_FAVOURITES
);
PassStoreEntryGroup root = new( PassStoreEntryGroup root = new(
Guid.NewGuid(), Guid.NewGuid(),
DateTime.UtcNow, DateTime.UtcNow,
@@ -276,9 +285,10 @@ public class PassStoreFileAccessor : IPassStore
Guid.Empty, Guid.Empty,
"", "",
GROUP_TYPE_ROOT, GROUP_TYPE_ROOT,
[defaultGroup] [defaultGroup, favourites]
); );
defaultGroup.Parent = root; defaultGroup.Parent = root;
favourites.Parent = root;
root.WriteToStream(w); root.WriteToStream(w);
return root; return root;
} }

View File

@@ -11,6 +11,7 @@ public class UnlockedRepositoryViewModel : ViewModelBase
{ {
private IPassStore passStore; private IPassStore passStore;
private IPassStoreDirectory currentDirectory; private IPassStoreDirectory currentDirectory;
private PassStoreEntryGroup? rootDirectory;
private bool hasUnsavedChanges; private bool hasUnsavedChanges;
private DispatcherTimer? _totpRefreshTimer; private DispatcherTimer? _totpRefreshTimer;
private Dictionary<Guid, string> _totpCodes = new(); private Dictionary<Guid, string> _totpCodes = new();
@@ -25,6 +26,31 @@ public class UnlockedRepositoryViewModel : ViewModelBase
} }
} }
public IEnumerable<PassStoreEntryGroup> PasswordGroups
{
get
{
if (rootDirectory == null) return [];
return rootDirectory
.Where(entry => entry is PassStoreEntryGroup)
.Select(entry => (entry as PassStoreEntryGroup)!);
}
}
public PassStoreEntryGroup SelectedPasswordGroup
{
get
{
return PasswordGroups.First(group => group == currentDirectory);
}
set
{
if (PasswordGroups.Any(group => group == value))
{
ChangeDirectory(value);
}
}
}
public bool HasUnsavedChanges public bool HasUnsavedChanges
{ {
get => hasUnsavedChanges; get => hasUnsavedChanges;
@@ -39,6 +65,7 @@ public class UnlockedRepositoryViewModel : ViewModelBase
{ {
passStore = store; passStore = store;
currentDirectory = directory; currentDirectory = directory;
rootDirectory = (directory as PassStoreEntryGroup)?.Parent;
HasUnsavedChanges = false; HasUnsavedChanges = false;
InitializeTotpCodes(); InitializeTotpCodes();
StartTotpRefreshTimer(); StartTotpRefreshTimer();
@@ -88,6 +115,19 @@ public class UnlockedRepositoryViewModel : ViewModelBase
HasUnsavedChanges = false; HasUnsavedChanges = false;
} }
private void ChangeDirectory(PassStoreEntryGroup newDir)
{
if (newDir == currentDirectory)
return;
currentDirectory = newDir;
InitializeTotpCodes();
StartTotpRefreshTimer();
OnPropertyChanged(nameof(SelectedPasswordGroup));
OnPropertyChanged(nameof(Passwords));
}
private void InitializeTotpCodes() private void InitializeTotpCodes()
{ {
_totpCodes.Clear(); _totpCodes.Clear();
@@ -102,6 +142,10 @@ public class UnlockedRepositoryViewModel : ViewModelBase
// Calculate time until next TOTP period boundary // Calculate time until next TOTP period boundary
int secondsUntilNextCode = CalculateSecondsUntilNextTotpRefresh(); int secondsUntilNextCode = CalculateSecondsUntilNextTotpRefresh();
if (_totpRefreshTimer != null)
{
_totpRefreshTimer.Stop();
}
_totpRefreshTimer = new DispatcherTimer _totpRefreshTimer = new DispatcherTimer
{ {
Interval = TimeSpan.FromSeconds(secondsUntilNextCode) Interval = TimeSpan.FromSeconds(secondsUntilNextCode)

View File

@@ -56,22 +56,23 @@
</StackPanel> </StackPanel>
</Border> </Border>
<!-- Рамочка --> <!-- Группы паролей -->
<!-- <Border BorderBrush="Gray" <TreeView x:Name="GroupTree"
BorderThickness="1" ItemsSource="{Binding PasswordGroups}"
CornerRadius="5" SelectedItem="{Binding SelectedPasswordGroup}"
Padding="20" Background="#FFFFFFFF"
Background="#F5F5F5" SelectionMode="Single" >
HorizontalAlignment="Left"> <TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding ChildGroups}">
<Border Background="Transparent"
DoubleTapped="Entry_DoubleTapped">
<TextBlock Text="{Binding DisplayName}"
Foreground="Black" />
</Border>
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel HorizontalAlignment="Left">
<Button Content="All Passwords"
Width="120"
Height="30"
HorizontalAlignment="Left"/>
</StackPanel>
</Border> -->
<!-- Save Passwords --> <!-- Save Passwords -->
<Button Content="Save Passwords" <Button Content="Save Passwords"
Classes="accentSidebarButton" Classes="accentSidebarButton"