Skip to content

Commit e42fa32

Browse files
committed
enhance: group selector while cloning/opening repository (#2663)
- Add `Auto (Based-on Default Clone Dir)` option and make it the default one - For existing groups, display the full-name instead of pure name Signed-off-by: leo <longshuang@msn.cn>
1 parent 14cf80e commit e42fa32

6 files changed

Lines changed: 140 additions & 67 deletions

File tree

src/ViewModels/Clone.cs

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace SourceGit.ViewModels
99
{
10+
public record TargetGroup(string Id, string Name, string Description);
11+
1012
public class Clone : Popup
1113
{
1214
[Required(ErrorMessage = "Remote URL is required")]
@@ -47,15 +49,21 @@ public string Local
4749
set => SetProperty(ref _local, value);
4850
}
4951

50-
public List<RepositoryNode> Groups
52+
public List<TargetGroup> Groups
5153
{
5254
get;
5355
}
5456

55-
public RepositoryNode SelectedGroup
57+
public int SelectedGroupIndex
58+
{
59+
get => _selectedGroupIndex;
60+
set => SetProperty(ref _selectedGroupIndex, value);
61+
}
62+
63+
public bool CanAutoSelectGroup
5664
{
57-
get => _selectedGroup;
58-
set => SetProperty(ref _selectedGroup, value);
65+
get => _canAutoSelectGroup;
66+
private set => SetProperty(ref _canAutoSelectGroup, value);
5967
}
6068

6169
public int Bookmark
@@ -81,15 +89,17 @@ public Clone(string pageId)
8189
_pageId = pageId;
8290
CanTerminate = true;
8391

84-
Groups = new List<RepositoryNode>();
85-
Groups.Add(new RepositoryNode { Name = "No Group (Uncategorized)", Id = string.Empty });
86-
SelectedGroup = Groups[0];
92+
Groups = new List<TargetGroup>();
93+
Groups.Add(new TargetGroup(string.Empty, "Auto", "Based-on Default Clone Dir"));
94+
Groups.Add(new TargetGroup(string.Empty, "No Group", "Uncategorized"));
8795
CollectGroups(Groups, Preferences.Instance.RepositoryNodes);
8896

8997
var activeWorkspace = Preferences.Instance.GetActiveWorkspace();
90-
_parentFolder = activeWorkspace?.DefaultCloneDir;
91-
if (string.IsNullOrEmpty(ParentFolder))
92-
_parentFolder = Preferences.Instance.GitDefaultCloneDir;
98+
_defaultCloneDir = activeWorkspace?.DefaultCloneDir;
99+
if (string.IsNullOrEmpty(_defaultCloneDir))
100+
_defaultCloneDir = Preferences.Instance.GitDefaultCloneDir;
101+
102+
ParentFolder = _defaultCloneDir;
93103
}
94104

95105
public static ValidationResult ValidateRemote(string remote, ValidationContext _)
@@ -164,7 +174,26 @@ public override async Task<bool> Sure()
164174

165175
log.Complete();
166176

167-
var parent = _selectedGroup is { Id: not "" } ? _selectedGroup : null;
177+
RepositoryNode parent = null;
178+
if (_selectedGroupIndex == 0) // Auto (Based-on Default Clone Dir)
179+
{
180+
if (!string.IsNullOrEmpty(_defaultCloneDir))
181+
{
182+
var normalizedDefaultCloneDir = _defaultCloneDir.Replace('\\', '/').TrimEnd('/') + "/";
183+
var normalizedParentFolder = _parentFolder.Replace('\\', '/').TrimEnd('/') + "/";
184+
if (normalizedParentFolder.Length > normalizedDefaultCloneDir.Length &&
185+
normalizedParentFolder.StartsWith(normalizedDefaultCloneDir, StringComparison.Ordinal))
186+
{
187+
var relativePath = normalizedParentFolder.Substring(normalizedDefaultCloneDir.Length);
188+
parent = Preferences.Instance.FindOrCreateGroupRecursive(relativePath.TrimEnd('/'));
189+
}
190+
}
191+
}
192+
else if (_selectedGroupIndex > 0 && _selectedGroupIndex < Groups.Count) // Existing group
193+
{
194+
parent = Preferences.Instance.FindNode(Groups[_selectedGroupIndex].Id);
195+
}
196+
168197
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, parent, true);
169198
node.Bookmark = _bookmark;
170199
await node.UpdateStatusAsync(false, null);
@@ -193,14 +222,15 @@ public override void Terminate()
193222
var _ = _cancellation?.CancelAsync();
194223
}
195224

196-
private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> collections)
225+
private void CollectGroups(List<TargetGroup> outs, List<RepositoryNode> collections, string prefix = null)
197226
{
198227
foreach (var node in collections)
199228
{
200229
if (!node.IsRepository)
201230
{
202-
outs.Add(node);
203-
CollectGroups(outs, node.SubNodes);
231+
var displayName = prefix != null ? $"{prefix}/{node.Name}" : node.Name;
232+
outs.Add(new(node.Id, displayName, string.Empty));
233+
CollectGroups(outs, node.SubNodes, displayName);
204234
}
205235
}
206236
}
@@ -210,9 +240,11 @@ private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> colle
210240
private bool _useSSH = false;
211241
private string _sshKey = string.Empty;
212242
private string _parentFolder = string.Empty;
243+
private string _defaultCloneDir = string.Empty;
213244
private string _local = string.Empty;
214245
private string _extraArgs = string.Empty;
215-
private RepositoryNode _selectedGroup = null;
246+
private int _selectedGroupIndex = 0;
247+
private bool _canAutoSelectGroup = false;
216248
private int _bookmark = 0;
217249
private CancellationTokenSource _cancellation = null;
218250
}

src/ViewModels/OpenLocalRepository.cs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ public string RepoPath
1616
set => SetProperty(ref _repoPath, value, true);
1717
}
1818

19-
public List<RepositoryNode> Groups
19+
public List<TargetGroup> Groups
2020
{
2121
get;
2222
}
2323

24-
public RepositoryNode Group
24+
public int SelectedGroupIndex
2525
{
26-
get => _group;
27-
set => SetProperty(ref _group, value);
26+
get => _selectedGroupIndex;
27+
set => SetProperty(ref _selectedGroupIndex, value);
2828
}
2929

3030
public int Bookmark
@@ -37,9 +37,9 @@ public OpenLocalRepository(string pageId, RepositoryNode group)
3737
{
3838
_pageId = pageId;
3939

40-
Groups = new List<RepositoryNode>();
41-
Groups.Add(new RepositoryNode { Name = "No Group (Uncategorized)", Id = string.Empty });
42-
Group = group ?? Groups[0];
40+
Groups = new List<TargetGroup>();
41+
Groups.Add(new TargetGroup(string.Empty, "Auto", "Based-on Default Clone Dir"));
42+
Groups.Add(new TargetGroup(string.Empty, "No Group", "Uncategorized"));
4343
CollectGroups(Groups, Preferences.Instance.RepositoryNodes);
4444
}
4545

@@ -52,8 +52,32 @@ public static ValidationResult ValidateRepoPath(string folder, ValidationContext
5252

5353
public override async Task<bool> Sure()
5454
{
55+
RepositoryNode parent = null;
56+
if (_selectedGroupIndex == 0) // Auto (Based-on Default Clone Dir)
57+
{
58+
var activeWorkspace = Preferences.Instance.GetActiveWorkspace();
59+
var defaultCloneDir = activeWorkspace?.DefaultCloneDir;
60+
if (string.IsNullOrEmpty(defaultCloneDir))
61+
defaultCloneDir = Preferences.Instance.GitDefaultCloneDir;
62+
63+
if (!string.IsNullOrEmpty(defaultCloneDir))
64+
{
65+
var normalizedParentFolder = new DirectoryInfo(RepoPath).Parent!.FullName.Replace('\\', '/').TrimEnd('/') + "/";
66+
var normalizedDefaultCloneDir = defaultCloneDir.Replace('\\', '/').TrimEnd('/') + "/";
67+
if (normalizedParentFolder.Length > normalizedDefaultCloneDir.Length &&
68+
normalizedParentFolder.StartsWith(normalizedDefaultCloneDir, StringComparison.Ordinal))
69+
{
70+
var relativePath = normalizedParentFolder.Substring(normalizedDefaultCloneDir.Length);
71+
parent = Preferences.Instance.FindOrCreateGroupRecursive(relativePath.TrimEnd('/'));
72+
}
73+
}
74+
}
75+
else if (_selectedGroupIndex > 1 && _selectedGroupIndex < Groups.Count) // Existing group
76+
{
77+
parent = Preferences.Instance.FindNode(Groups[_selectedGroupIndex].Id);
78+
}
79+
5580
var isBare = await new Commands.IsBareRepository(_repoPath).GetResultAsync();
56-
var parent = _group is { Id: not "" } ? _group : null;
5781
var repoRoot = _repoPath;
5882
if (!isBare)
5983
{
@@ -86,21 +110,22 @@ public override async Task<bool> Sure()
86110
return true;
87111
}
88112

89-
private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> collections)
113+
private void CollectGroups(List<TargetGroup> outs, List<RepositoryNode> collections, string prefix = null)
90114
{
91115
foreach (var node in collections)
92116
{
93117
if (!node.IsRepository)
94118
{
95-
outs.Add(node);
96-
CollectGroups(outs, node.SubNodes);
119+
var displayName = prefix != null ? $"{prefix}/{node.Name}" : node.Name;
120+
outs.Add(new(node.Id, displayName, string.Empty));
121+
CollectGroups(outs, node.SubNodes, displayName);
97122
}
98123
}
99124
}
100125

101126
private string _pageId = string.Empty;
102127
private string _repoPath = string.Empty;
103-
private RepositoryNode _group = null;
128+
private int _selectedGroupIndex = 0;
104129
private int _bookmark = 0;
105130
}
106131
}

src/ViewModels/Preferences.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,20 @@ public RepositoryNode FindNode(string id)
557557
return FindNodeRecursive(id, RepositoryNodes);
558558
}
559559

560+
public RepositoryNode FindOrCreateGroupRecursive(string path)
561+
{
562+
List<RepositoryNode> collection = RepositoryNodes;
563+
RepositoryNode node = null;
564+
565+
foreach (var name in path.Split('/'))
566+
{
567+
node = FindOrCreateGroupInCollection(collection, name);
568+
collection = node.SubNodes;
569+
}
570+
571+
return node;
572+
}
573+
560574
public RepositoryNode FindOrAddNodeByRepositoryPath(string repo, RepositoryNode parent, bool shouldMoveNode, bool save = true)
561575
{
562576
var normalized = repo.Replace('\\', '/').TrimEnd('/');
@@ -733,6 +747,27 @@ private RepositoryNode FindNodeRecursive(string id, List<RepositoryNode> collect
733747
return null;
734748
}
735749

750+
private RepositoryNode FindOrCreateGroupInCollection(List<RepositoryNode> collection, string name)
751+
{
752+
foreach (var node in collection)
753+
{
754+
if (!node.IsRepository && node.Name.Equals(name, StringComparison.Ordinal))
755+
return node;
756+
}
757+
758+
var added = new RepositoryNode()
759+
{
760+
Id = Guid.NewGuid().ToString(),
761+
Name = name,
762+
IsRepository = false,
763+
IsExpanded = true,
764+
};
765+
collection.Add(added);
766+
767+
SortNodes(collection);
768+
return added;
769+
}
770+
736771
private List<RepositoryNode> FindNodeContainer(RepositoryNode node, List<RepositoryNode> collection)
737772
{
738773
foreach (var sub in collection)

src/ViewModels/ScanRepositories.cs

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public override async Task<bool> Sure()
106106
else if (parent.StartsWith(normalizedRoot, StringComparison.OrdinalIgnoreCase))
107107
{
108108
var relative = parent.Substring(normalizedRoot.Length).TrimStart('/');
109-
var group = FindOrCreateGroupRecursive(Preferences.Instance.RepositoryNodes, relative);
109+
var group = Preferences.Instance.FindOrCreateGroupRecursive(relative);
110110
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(f, group, false, false);
111111
await node.UpdateStatusAsync(false, null);
112112
}
@@ -170,39 +170,6 @@ private async Task GetUnmanagedRepositoriesAsync(DirectoryInfo dir, List<string>
170170
}
171171
}
172172

173-
private RepositoryNode FindOrCreateGroupRecursive(List<RepositoryNode> collection, string path)
174-
{
175-
RepositoryNode node = null;
176-
foreach (var name in path.Split('/'))
177-
{
178-
node = FindOrCreateGroup(collection, name);
179-
collection = node.SubNodes;
180-
}
181-
182-
return node;
183-
}
184-
185-
private RepositoryNode FindOrCreateGroup(List<RepositoryNode> collection, string name)
186-
{
187-
foreach (var node in collection)
188-
{
189-
if (node.Name.Equals(name, StringComparison.Ordinal))
190-
return node;
191-
}
192-
193-
var added = new RepositoryNode()
194-
{
195-
Id = Guid.NewGuid().ToString(),
196-
Name = name,
197-
IsRepository = false,
198-
IsExpanded = true,
199-
};
200-
collection.Add(added);
201-
202-
Preferences.Instance.SortNodes(collection);
203-
return added;
204-
}
205-
206173
private bool IsManaged(string path)
207174
{
208175
if (OperatingSystem.IsLinux())

src/Views/Clone.axaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,17 @@
111111
Height="28" Padding="8,0"
112112
VerticalAlignment="Center" HorizontalAlignment="Stretch"
113113
ItemsSource="{Binding Groups}"
114-
SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
114+
SelectedIndex="{Binding SelectedGroupIndex, Mode=TwoWay}">
115115
<ComboBox.ItemTemplate>
116-
<DataTemplate DataType="vm:RepositoryNode">
117-
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
116+
<DataTemplate DataType="vm:TargetGroup">
117+
<Grid ColumnDefinitions="*,Auto">
118+
<TextBlock Grid.Column="0"
119+
Text="{Binding Name, Mode=OneWay}"/>
120+
<TextBlock Grid.Column="1"
121+
Text="{Binding Description, Mode=OneWay}"
122+
Margin="8,0,0,0"
123+
Foreground="{DynamicResource Brush.FG2}"/>
124+
</Grid>
118125
</DataTemplate>
119126
</ComboBox.ItemTemplate>
120127
</ComboBox>

src/Views/OpenLocalRepository.axaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,17 @@
4444
Height="28" Padding="8,0"
4545
VerticalAlignment="Center" HorizontalAlignment="Stretch"
4646
ItemsSource="{Binding Groups}"
47-
SelectedItem="{Binding Group, Mode=TwoWay}">
47+
SelectedIndex="{Binding SelectedGroupIndex, Mode=TwoWay}">
4848
<ComboBox.ItemTemplate>
49-
<DataTemplate DataType="vm:RepositoryNode">
50-
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
49+
<DataTemplate DataType="vm:TargetGroup">
50+
<Grid ColumnDefinitions="*,Auto">
51+
<TextBlock Grid.Column="0"
52+
Text="{Binding Name, Mode=OneWay}"/>
53+
<TextBlock Grid.Column="1"
54+
Text="{Binding Description, Mode=OneWay}"
55+
Margin="8,0,0,0"
56+
Foreground="{DynamicResource Brush.FG2}"/>
57+
</Grid>
5158
</DataTemplate>
5259
</ComboBox.ItemTemplate>
5360
</ComboBox>

0 commit comments

Comments
 (0)