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

Commit e726e6a

Browse files
committed
Make IAccount usable in TrackingCollection
Types used in `ITrackingCollection` need to implement `ICopyable`, `IEquatable` and `IComparable`, so they can be sorted and replaced in the collection.
1 parent 9850b9e commit e726e6a

File tree

5 files changed

+184
-72
lines changed

5 files changed

+184
-72
lines changed

src/GitHub.App/GitHub.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
<DesignTime>True</DesignTime>
129129
<DependentUpon>Resources.resx</DependentUpon>
130130
</Compile>
131+
<Compile Include="SampleData\AccountDesigner.cs" />
131132
<Compile Include="SampleData\PullRequestCreationViewModelDesigner.cs" />
132133
<Compile Include="SampleData\PullRequestDetailViewModelDesigner.cs" />
133134
<Compile Include="SampleData\PullRequestListViewModelDesigner.cs" />

src/GitHub.App/Models/Account.cs

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace GitHub.Models
1111
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1212
public class Account : ReactiveObject, IAccount
1313
{
14-
readonly ObservableAsPropertyHelper<BitmapSource> avatar;
14+
BitmapSource avatar;
1515

1616
public Account(
1717
string login,
@@ -29,8 +29,8 @@ public Account(
2929
IsOnFreePlan = privateRepositoryInPlanCount == 0;
3030
HasMaximumPrivateRepositories = OwnedPrivateRepos >= PrivateReposInPlan;
3131

32-
avatar = bitmapSource.ObserveOn(RxApp.MainThreadScheduler)
33-
.ToProperty(this, a => a.Avatar);
32+
bitmapSource.ObserveOn(RxApp.MainThreadScheduler)
33+
.Subscribe(x => avatar = x);
3434
}
3535

3636
public bool IsOnFreePlan { get; private set; }
@@ -47,13 +47,80 @@ public Account(
4747

4848
public long PrivateReposInPlan { get; private set; }
4949

50+
[AllowNull]
5051
public BitmapSource Avatar
5152
{
5253
[return: AllowNull]
53-
get
54-
{
55-
return avatar.Value;
56-
}
54+
get { return avatar; }
55+
set { avatar = value; this.RaisePropertyChanged(); }
56+
}
57+
58+
#region Equality things
59+
public void CopyFrom(IAccount other)
60+
{
61+
if (!Equals(other))
62+
throw new ArgumentException("Instance to copy from doesn't match this instance. this:(" + this + ") other:(" + other + ")", nameof(other));
63+
OwnedPrivateRepos = other.OwnedPrivateRepos;
64+
PrivateReposInPlan = other.PrivateReposInPlan;
65+
IsOnFreePlan = other.IsOnFreePlan;
66+
HasMaximumPrivateRepositories = other.HasMaximumPrivateRepositories;
67+
Avatar = other.Avatar;
68+
}
69+
70+
public override bool Equals([AllowNull]object obj)
71+
{
72+
if (ReferenceEquals(this, obj))
73+
return true;
74+
var other = obj as Account;
75+
return other != null && Login == other.Login && IsUser == other.IsUser && IsEnterprise == other.IsEnterprise;
76+
}
77+
78+
public override int GetHashCode()
79+
{
80+
return (Login?.GetHashCode() ?? 0) ^ IsUser .GetHashCode() ^ IsEnterprise.GetHashCode();
81+
}
82+
83+
bool IEquatable<IAccount>.Equals([AllowNull]IAccount other)
84+
{
85+
if (ReferenceEquals(this, other))
86+
return true;
87+
return other != null && Login == other.Login && IsUser == other.IsUser && IsEnterprise == other.IsEnterprise;
88+
}
89+
90+
public int CompareTo([AllowNull]IAccount other)
91+
{
92+
return other != null ? String.Compare(Login, other.Login, StringComparison.CurrentCulture) : 1;
93+
}
94+
95+
public static bool operator >([AllowNull]Account lhs, [AllowNull]Account rhs)
96+
{
97+
if (ReferenceEquals(lhs, rhs))
98+
return false;
99+
return lhs?.CompareTo(rhs) > 0;
100+
}
101+
102+
public static bool operator <([AllowNull]Account lhs, [AllowNull]Account rhs)
103+
{
104+
if (ReferenceEquals(lhs, rhs))
105+
return false;
106+
return (object)lhs == null || lhs.CompareTo(rhs) < 0;
107+
}
108+
109+
public static bool operator ==([AllowNull]Account lhs, [AllowNull]Account rhs)
110+
{
111+
return Equals(lhs, rhs) && ((object)lhs == null || lhs.CompareTo(rhs) == 0);
112+
}
113+
114+
public static bool operator !=([AllowNull]Account lhs, [AllowNull]Account rhs)
115+
{
116+
return !(lhs == rhs);
117+
}
118+
#endregion
119+
120+
[return: AllowNull]
121+
public override string ToString()
122+
{
123+
return Login;
57124
}
58125

59126
internal string DebuggerDisplay
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Windows.Media.Imaging;
4+
using GitHub.Models;
5+
using GitHub.Services;
6+
7+
namespace GitHub.SampleData
8+
{
9+
[ExcludeFromCodeCoverage]
10+
public sealed class AccountDesigner : IAccount
11+
{
12+
public AccountDesigner()
13+
{
14+
Login = "octocat";
15+
IsUser = true;
16+
}
17+
18+
public BitmapSource Avatar
19+
{
20+
get
21+
{
22+
return IsUser
23+
? AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png")
24+
: AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_org_avatar.png");
25+
}
26+
}
27+
28+
public bool HasMaximumPrivateRepositories { get; set; }
29+
public bool IsEnterprise { get; set; }
30+
public bool IsOnFreePlan { get; set; }
31+
public bool IsUser { get; set; }
32+
public string Login { get; set; }
33+
public int OwnedPrivateRepos { get; set; }
34+
public long PrivateReposInPlan { get; set; }
35+
36+
public override string ToString()
37+
{
38+
return Login;
39+
}
40+
41+
#region Equality things
42+
public void CopyFrom(IAccount other)
43+
{
44+
if (!Equals(other))
45+
throw new ArgumentException("Instance to copy from doesn't match this instance. this:(" + this + ") other:(" + other + ")", nameof(other));
46+
OwnedPrivateRepos = other.OwnedPrivateRepos;
47+
PrivateReposInPlan = other.PrivateReposInPlan;
48+
IsOnFreePlan = other.IsOnFreePlan;
49+
HasMaximumPrivateRepositories = other.HasMaximumPrivateRepositories;
50+
}
51+
52+
public override bool Equals(object obj)
53+
{
54+
if (ReferenceEquals(this, obj))
55+
return true;
56+
var other = obj as Account;
57+
return other != null && Login == other.Login && IsUser == other.IsUser && IsEnterprise == other.IsEnterprise;
58+
}
59+
60+
public override int GetHashCode()
61+
{
62+
return (Login?.GetHashCode() ?? 0) ^ IsUser.GetHashCode() ^ IsEnterprise.GetHashCode();
63+
}
64+
65+
bool IEquatable<IAccount>.Equals(IAccount other)
66+
{
67+
if (ReferenceEquals(this, other))
68+
return true;
69+
return other != null && Login == other.Login && IsUser == other.IsUser && IsEnterprise == other.IsEnterprise;
70+
}
71+
72+
public int CompareTo(IAccount other)
73+
{
74+
return other != null ? String.Compare(Login, other.Login, StringComparison.CurrentCulture) : 1;
75+
}
76+
77+
public static bool operator >(AccountDesigner lhs, AccountDesigner rhs)
78+
{
79+
if (ReferenceEquals(lhs, rhs))
80+
return false;
81+
return lhs?.CompareTo(rhs) > 0;
82+
}
83+
84+
public static bool operator <(AccountDesigner lhs, AccountDesigner rhs)
85+
{
86+
if (ReferenceEquals(lhs, rhs))
87+
return false;
88+
return (object)lhs == null || lhs.CompareTo(rhs) < 0;
89+
}
90+
91+
public static bool operator ==(AccountDesigner lhs, AccountDesigner rhs)
92+
{
93+
return Equals(lhs, rhs) && ((object)lhs == null || lhs.CompareTo(rhs) == 0);
94+
}
95+
96+
public static bool operator !=(AccountDesigner lhs, AccountDesigner rhs)
97+
{
98+
return !(lhs == rhs);
99+
}
100+
#endregion
101+
102+
}
103+
}

src/GitHub.App/SampleData/SampleViewModels.cs

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Diagnostics.CodeAnalysis;
44
using System.Reactive;
55
using System.Windows.Input;
6-
using System.Windows.Media.Imaging;
76
using GitHub.Api;
87
using GitHub.Authentication;
98
using GitHub.Extensions;
@@ -18,6 +17,7 @@
1817
using GitHub.VisualStudio.TeamExplorer.Connect;
1918
using System.Collections.ObjectModel;
2019
using System.Linq;
20+
using System.Reactive.Linq;
2121

2222
namespace GitHub.SampleData
2323
{
@@ -329,68 +329,6 @@ public IObservable<Unit> LogOut()
329329
}
330330
}
331331

332-
[ExcludeFromCodeCoverage]
333-
public sealed class AccountDesigner : IAccount
334-
{
335-
public AccountDesigner()
336-
{
337-
Login = "octocat";
338-
IsUser = true;
339-
}
340-
341-
public BitmapSource Avatar
342-
{
343-
get
344-
{
345-
return IsUser
346-
? AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png")
347-
: AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_org_avatar.png");
348-
}
349-
}
350-
351-
public bool HasMaximumPrivateRepositories
352-
{
353-
get;
354-
set;
355-
}
356-
357-
public bool IsEnterprise
358-
{
359-
get;
360-
set;
361-
}
362-
363-
public bool IsOnFreePlan
364-
{
365-
get;
366-
set;
367-
}
368-
369-
public bool IsUser
370-
{
371-
get;
372-
set;
373-
}
374-
375-
public string Login
376-
{
377-
get;
378-
set;
379-
}
380-
381-
public int OwnedPrivateRepos
382-
{
383-
get;
384-
set;
385-
}
386-
387-
public long PrivateReposInPlan
388-
{
389-
get;
390-
set;
391-
}
392-
}
393-
394332
[ExcludeFromCodeCoverage]
395333
public class RepositoryModelDesigner : NotificationAwareObject, IRepositoryModel
396334
{

src/GitHub.Exports/Models/IAccount.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
using System.Windows.Media.Imaging;
1+
using System;
2+
using System.Windows.Media.Imaging;
3+
using GitHub.Collections;
24

35
namespace GitHub.Models
46
{
5-
public interface IAccount
7+
public interface IAccount : ICopyable<IAccount>,
8+
IEquatable<IAccount>, IComparable<IAccount>
69
{
710
bool IsEnterprise { get; }
811
bool IsOnFreePlan { get; }

0 commit comments

Comments
 (0)