Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 6819bea

Browse files
committed
Implement filtering of the pr list view
1 parent 9f0f2d1 commit 6819bea

File tree

4 files changed

+398
-63
lines changed

4 files changed

+398
-63
lines changed

src/GitHub.App/SampleData/PullRequestListViewModelDesigner.cs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
using GitHub.Collections;
66
using GitHub.Models;
77
using GitHub.ViewModels;
8+
using System.Collections.Generic;
9+
using ReactiveUI;
10+
using System.Collections.ObjectModel;
11+
using System.Linq;
812

913
namespace GitHub.SampleData
1014
{
@@ -14,14 +18,43 @@ public class PullRequestListViewModelDesigner : BaseViewModelDesigner, IPullRequ
1418
public PullRequestListViewModelDesigner()
1519
{
1620
var prs = new TrackingCollection<IPullRequestModel>(Observable.Empty<IPullRequestModel>());
17-
prs.Add(new PullRequestModel(399, "Let's try doing this differently", new AccountDesigner { Login = "shana", IsUser = true }, DateTimeOffset.Now - TimeSpan.FromDays(1)));
18-
prs.Add(new PullRequestModel(389, "Build system upgrade", new AccountDesigner { Login = "haacked", IsUser = true }, DateTimeOffset.Now - TimeSpan.FromMinutes(2)) { CommentCount = 4, HasNewComments = false });
19-
prs.Add(new PullRequestModel(409, "Fix publish button style", new AccountDesigner { Login = "shana", IsUser = false }, DateTimeOffset.Now - TimeSpan.FromHours(5)) { CommentCount = 27, HasNewComments = true });
21+
prs.Add(new PullRequestModel(399, "Let's try doing this differently",
22+
new AccountDesigner { Login = "shana", IsUser = true },
23+
new AccountDesigner { Login = "shana", IsUser = true },
24+
DateTimeOffset.Now - TimeSpan.FromDays(1)));
25+
prs.Add(new PullRequestModel(389, "Build system upgrade",
26+
new AccountDesigner { Login = "haacked", IsUser = true },
27+
new AccountDesigner { Login = "shana", IsUser = true },
28+
DateTimeOffset.Now - TimeSpan.FromMinutes(2)) { CommentCount = 4, HasNewComments = false });
29+
prs.Add(new PullRequestModel(409, "Fix publish button style",
30+
new AccountDesigner { Login = "shana", IsUser = true },
31+
new AccountDesigner { Login = "Haacked", IsUser = true },
32+
DateTimeOffset.Now - TimeSpan.FromHours(5)) { CommentCount = 27, HasNewComments = true });
2033
PullRequests = prs;
34+
35+
States = new List<PullRequestState> {
36+
new PullRequestState { IsOpen = true, Name = "Open" },
37+
new PullRequestState { IsOpen = false, Name = "Closed" },
38+
new PullRequestState { Name = "All" }
39+
};
40+
SelectedState = States[0];
41+
Assignees = new ObservableCollection<IAccount>(prs.Select(x => x.Assignee));
42+
Authors = new ObservableCollection<IAccount>(prs.Select(x => x.Author));
43+
SelectedAssignee = Assignees.ElementAt(1);
44+
SelectedAuthor = Authors.ElementAt(1);
2145
}
2246

23-
public ITrackingCollection<IPullRequestModel> PullRequests { get; set; }
47+
public ObservableCollection<IPullRequestModel> PullRequests { get; set; }
2448
public IPullRequestModel SelectedPullRequest { get; set; }
2549
public ICommand OpenPullRequest { get; set; }
50+
51+
public IReadOnlyList<PullRequestState> States { get; set; }
52+
public PullRequestState SelectedState { get; set; }
53+
54+
public ObservableCollection<IAccount> Authors { get; set; }
55+
public IAccount SelectedAuthor { get; set; }
56+
57+
public ObservableCollection<IAccount> Assignees { get; set; }
58+
public IAccount SelectedAssignee { get; set; }
2659
}
2760
}

src/GitHub.App/ViewModels/PullRequestListViewModel.cs

Lines changed: 156 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
using System.Reactive.Linq;
1414
using GitHub.Extensions.Reactive;
1515
using System.Windows.Data;
16-
using System.ComponentModel;
1716
using GitHub.Collections;
1817
using System.Windows.Input;
1918
using GitHub.UI;
19+
using System.Windows.Media.Imaging;
2020

2121
namespace GitHub.ViewModels
2222
{
@@ -27,6 +27,8 @@ public class PullRequestListViewModel : BaseViewModel, IPullRequestListViewModel
2727
readonly ReactiveCommand<object> openPullRequestCommand;
2828
readonly IRepositoryHost repositoryHost;
2929
readonly ISimpleRepositoryModel repository;
30+
readonly TrackingCollection<IAccount> trackingAuthors;
31+
readonly TrackingCollection<IAccount> trackingAssignees;
3032

3133
[ImportingConstructor]
3234
PullRequestListViewModel(
@@ -46,30 +48,68 @@ public PullRequestListViewModel(IRepositoryHost repositoryHost, ISimpleRepositor
4648
VisualStudio.Services.DefaultExportProvider.GetExportedValue<IVisualStudioBrowser>().OpenUrl(repositoryHost.Address.WebUri);
4749
});
4850

49-
var list = new TrackingCollection<IPullRequestModel>();
50-
list.Comparer = OrderedComparer<IPullRequestModel>.OrderByDescending(x => x.UpdatedAt).Compare;
51-
list.Filter = (pr, i, l) => pr.IsOpen;
52-
PullRequests = list;
51+
States = new List<PullRequestState> {
52+
new PullRequestState { IsOpen = true, Name = "Open" },
53+
new PullRequestState { IsOpen = false, Name = "Closed" },
54+
new PullRequestState { Name = "All" }
55+
};
56+
SelectedState = States[0];
57+
58+
this.WhenAny(x => x.SelectedState, x => x.Value)
59+
.Where(x => PullRequests != null)
60+
.Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor));
61+
62+
this.WhenAny(x => x.SelectedAssignee, x => x.Value)
63+
.Where(x => PullRequests != null)
64+
.Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor));
65+
66+
this.WhenAny(x => x.SelectedAuthor, x => x.Value)
67+
.Where(x => PullRequests != null)
68+
.Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a));
69+
70+
trackingAuthors = new TrackingCollection<IAccount>(Observable.Empty<IAccount>(),
71+
OrderedComparer<IAccount>.OrderByDescending(x => x.Login).Compare);
72+
trackingAssignees = new TrackingCollection<IAccount>(Observable.Empty<IAccount>(),
73+
OrderedComparer<IAccount>.OrderByDescending(x => x.Login).Compare);
74+
trackingAuthors.Subscribe();
75+
trackingAssignees.Subscribe();
76+
77+
Authors = trackingAuthors.CreateListenerCollection(new List<IAccount> { EmptyUser });
78+
Assignees = trackingAssignees.CreateListenerCollection(new List<IAccount> { EmptyUser });
79+
80+
PullRequests = new TrackingCollection<IPullRequestModel>();
81+
pullRequests.Comparer = OrderedComparer<IPullRequestModel>.OrderByDescending(x => x.UpdatedAt).Compare;
82+
pullRequests.Filter = (pr, i, l) => pr.IsOpen;
5383
}
5484

5585
public override void Initialize([AllowNull] ViewWithData data)
5686
{
5787
base.Initialize(data);
5888

59-
var old = PullRequests;
60-
var list = repositoryHost.ModelService.GetPullRequests(repository);
61-
list.Comparer = old.Comparer;
62-
list.Filter = old.Filter;
63-
PullRequests = list;
64-
list.Subscribe();
65-
old.Dispose();
89+
repositoryHost.ModelService.GetPullRequests(repository, pullRequests);
90+
pullRequests.Subscribe(pr =>
91+
{
92+
trackingAssignees.AddItem(pr.Assignee);
93+
trackingAuthors.AddItem(pr.Author);
94+
}, () => { });
6695
}
6796

68-
ITrackingCollection<IPullRequestModel> pullRequests;
69-
public ITrackingCollection<IPullRequestModel> PullRequests
97+
void UpdateFilter(PullRequestState state, [AllowNull]IAccount ass, [AllowNull]IAccount aut)
7098
{
99+
if (PullRequests == null)
100+
return;
101+
pullRequests.Filter = (pr, i, l) =>
102+
(!state.IsOpen.HasValue || state.IsOpen == pr.IsOpen) &&
103+
(ass == null || pr.Assignee == ass) &&
104+
(aut == null || pr.Author == aut);
105+
}
106+
107+
TrackingCollection<IPullRequestModel> pullRequests;
108+
public ObservableCollection<IPullRequestModel> PullRequests
109+
{
110+
[return: AllowNull]
71111
get { return pullRequests; }
72-
private set { this.RaiseAndSetIfChanged(ref pullRequests, value); }
112+
private set { this.RaiseAndSetIfChanged(ref pullRequests, (TrackingCollection<IPullRequestModel>)value); }
73113
}
74114

75115
IPullRequestModel selectedPullRequest;
@@ -85,5 +125,106 @@ public ICommand OpenPullRequest
85125
{
86126
get { return openPullRequestCommand; }
87127
}
128+
129+
IReadOnlyList<PullRequestState> states;
130+
public IReadOnlyList<PullRequestState> States
131+
{
132+
get { return states; }
133+
set { this.RaiseAndSetIfChanged(ref states, value); }
134+
}
135+
136+
PullRequestState selectedState;
137+
public PullRequestState SelectedState
138+
{
139+
get { return selectedState; }
140+
set { this.RaiseAndSetIfChanged(ref selectedState, value); }
141+
}
142+
143+
ObservableCollection<IAccount> assignees;
144+
public ObservableCollection<IAccount> Assignees
145+
{
146+
get { return assignees; }
147+
set { this.RaiseAndSetIfChanged(ref assignees, value); }
148+
}
149+
150+
ObservableCollection<IAccount> authors;
151+
public ObservableCollection<IAccount> Authors
152+
{
153+
get { return authors; }
154+
set { this.RaiseAndSetIfChanged(ref authors, value); }
155+
}
156+
157+
IAccount selectedAuthor;
158+
[AllowNull]
159+
public IAccount SelectedAuthor
160+
{
161+
[return: AllowNull]
162+
get { return selectedAuthor; }
163+
set { this.RaiseAndSetIfChanged(ref selectedAuthor, value); }
164+
}
165+
166+
IAccount selectedAssignee;
167+
[AllowNull]
168+
public IAccount SelectedAssignee
169+
{
170+
[return: AllowNull]
171+
get { return selectedAssignee; }
172+
set { this.RaiseAndSetIfChanged(ref selectedAssignee, value); }
173+
}
174+
175+
public IAccount EmptyUser
176+
{
177+
get { return new Account("[None]", false, false, 0, 0, Observable.Empty<BitmapSource>()); }
178+
}
179+
}
180+
181+
// doing this as an extension because I get the feeling it might be useful in other places
182+
public static class TrackingCollectionExtensions
183+
{
184+
public static ObservableCollection<T> CreateListenerCollection<T>(this ITrackingCollection<T> tcol,
185+
IList<T> stickieItemsOnTop = null)
186+
where T : ICopyable<T>
187+
{
188+
var col = new ObservableCollection<T>();
189+
tcol.CollectionChanged += (s, e) =>
190+
{
191+
var offset = 0;
192+
if (stickieItemsOnTop != null)
193+
{
194+
foreach (var item in stickieItemsOnTop)
195+
{
196+
if (col.Contains(item))
197+
offset++;
198+
}
199+
}
200+
201+
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move)
202+
{
203+
for (int i = 0, oldIdx = e.OldStartingIndex, newIdx = e.NewStartingIndex;
204+
i < e.OldItems.Count; i++, oldIdx++, newIdx++)
205+
col.Move(oldIdx + offset, newIdx + offset);
206+
}
207+
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
208+
foreach (T item in e.NewItems)
209+
col.Add(item);
210+
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
211+
foreach (T item in e.OldItems)
212+
col.Remove(item);
213+
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
214+
for (int i = 0, idx = e.OldStartingIndex;
215+
i < e.OldItems.Count; i++, idx++)
216+
col[idx + offset] = (T)e.NewItems[i];
217+
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
218+
{
219+
col.Clear();
220+
if (stickieItemsOnTop != null)
221+
{
222+
foreach (var item in stickieItemsOnTop)
223+
col.Add(item);
224+
}
225+
}
226+
};
227+
return col;
228+
}
88229
}
89230
}
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
1-
using GitHub.Collections;
1+
using System.Collections.Generic;
2+
using GitHub.Collections;
23
using GitHub.Models;
34
using System.Windows.Input;
5+
using ReactiveUI;
6+
using System.Collections.ObjectModel;
47

58
namespace GitHub.ViewModels
69
{
10+
public class PullRequestState
11+
{
12+
public bool? IsOpen;
13+
public string Name;
14+
public override string ToString()
15+
{
16+
return Name;
17+
}
18+
}
19+
720
public interface IPullRequestListViewModel : IViewModel
821
{
9-
ITrackingCollection<IPullRequestModel> PullRequests { get; }
22+
ObservableCollection<IPullRequestModel> PullRequests { get; }
1023
IPullRequestModel SelectedPullRequest { get; }
1124
ICommand OpenPullRequest { get; }
25+
IReadOnlyList<PullRequestState> States { get; set; }
26+
PullRequestState SelectedState { get; set; }
27+
ObservableCollection<IAccount> Authors { get; }
28+
IAccount SelectedAuthor { get; set; }
29+
ObservableCollection<IAccount> Assignees { get; }
30+
IAccount SelectedAssignee { get; set; }
1231
}
1332
}

0 commit comments

Comments
 (0)