Skip to content

Commit e2658f6

Browse files
073124.02/subscribe (Azure#8886)
* Work on Subscribe Button * Subscribe Switch
1 parent 58a0759 commit e2658f6

File tree

9 files changed

+90
-8
lines changed

9 files changed

+90
-8
lines changed

src/dotnet/APIView/APIViewWeb/LeanControllers/ReviewsController.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ public class ReviewsController : BaseApiController
2727
public readonly UserPreferenceCache _preferenceCache;
2828
private readonly ICosmosUserProfileRepository _userProfileRepository;
2929
private readonly IHubContext<SignalRHub> _signalRHubContext;
30+
private readonly INotificationManager _notificationManager;
3031
private readonly IWebHostEnvironment _env;
3132

3233
public ReviewsController(ILogger<ReviewsController> logger,
3334
IAPIRevisionsManager reviewRevisionsManager, IReviewManager reviewManager,
3435
ICommentsManager commentManager, IBlobCodeFileRepository codeFileRepository,
3536
IConfiguration configuration, UserPreferenceCache preferenceCache,
36-
ICosmosUserProfileRepository userProfileRepository, IHubContext<SignalRHub> signalRHub, IWebHostEnvironment env)
37+
ICosmosUserProfileRepository userProfileRepository, IHubContext<SignalRHub> signalRHub,
38+
INotificationManager notificationManager, IWebHostEnvironment env)
3739
{
3840
_logger = logger;
3941
_apiRevisionsManager = reviewRevisionsManager;
@@ -44,6 +46,7 @@ public ReviewsController(ILogger<ReviewsController> logger,
4446
_preferenceCache = preferenceCache;
4547
_userProfileRepository = userProfileRepository;
4648
_signalRHubContext = signalRHub;
49+
_notificationManager = notificationManager;
4750
_env = env;
4851
}
4952

@@ -117,6 +120,20 @@ public async Task<ActionResult> ToggleReviewApprovalAsync(string reviewId, strin
117120
return new LeanJsonResult(updatedReview, StatusCodes.Status200OK);
118121
}
119122

123+
/// <summary>
124+
/// Endpoint used by Client SPA toggling Subscription to a review
125+
/// </summary>
126+
/// <param name="reviewId"></param>
127+
/// <param name="state"></param> true = subscribe, false = unsubscribe
128+
/// <returns></returns>
129+
[HttpPost("{reviewId}/toggleSubscribe", Name = "ToggleSubscribe")]
130+
public async Task<ActionResult<APIRevisionListItemModel>> ToggleSubscribeAsync(string reviewId, [FromQuery] bool state)
131+
{
132+
string userName = User.GetGitHubLogin();
133+
await _notificationManager.ToggleSubscribedAsync(User, reviewId, state);
134+
return Ok();
135+
}
136+
120137
///<summary>
121138
///Retrieve the Content (codeLines and Navigation) of a review
122139
///</summary>

src/dotnet/APIView/APIViewWeb/Managers/Interfaces/INotificationManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface INotificationManager
1212
public Task NotifyUserOnCommentTag(CommentItemModel comment);
1313
public Task NotifyApproversOfReview(ClaimsPrincipal user, string apiRevisionId, HashSet<string> reviewers);
1414
public Task NotifySubscribersOnNewRevisionAsync(ReviewListItemModel review, APIRevisionListItemModel revision, ClaimsPrincipal user);
15-
public Task ToggleSubscribedAsync(ClaimsPrincipal user, string reviewId);
15+
public Task ToggleSubscribedAsync(ClaimsPrincipal user, string reviewId, bool? state = null);
1616
public Task SubscribeAsync(ReviewListItemModel review, ClaimsPrincipal user);
1717
public Task UnsubscribeAsync(ReviewListItemModel review, ClaimsPrincipal user);
1818
}

src/dotnet/APIView/APIViewWeb/Managers/NotificationManager.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,27 @@ public async Task NotifySubscribersOnNewRevisionAsync(ReviewListItemModel review
8787
/// </summary>
8888
/// <param name="user"></param>
8989
/// <param name="reviewId"></param>
90+
/// <param name="state"></param> true = subscribe, false = unsubscribe
9091
/// <returns></returns>
91-
public async Task ToggleSubscribedAsync(ClaimsPrincipal user, string reviewId)
92+
public async Task ToggleSubscribedAsync(ClaimsPrincipal user, string reviewId, bool? state = null)
9293
{
9394
var review = await _reviewRepository.GetReviewAsync(reviewId);
9495
if (PageModelHelpers.IsUserSubscribed(user, review.Subscribers))
9596
{
97+
if (state == true)
98+
{
99+
return; // already subscribed
100+
}
101+
96102
await UnsubscribeAsync(review, user);
97103
}
98104
else
99105
{
106+
if (state == false)
107+
{
108+
return; // already unsubscribed
109+
}
110+
100111
await SubscribeAsync(review, user);
101112
}
102113
}

src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@
154154
</ul>
155155
</app-page-options-section>
156156

157+
<app-page-options-section sectionName="Review Options">
158+
<ul class="list-group">
159+
<li class="list-group-item">
160+
<p-inputSwitch [(ngModel)]="subscribeSwitch"
161+
(onChange)="onSubscribeSwitchChange($event)" />
162+
<label class="ms-2">Subscribe</label>
163+
</li>
164+
</ul>
165+
</app-page-options-section>
166+
157167
<app-page-options-section sectionName="Change History" [collapsedInput]="true">
158168
<ul class="list-group">
159169
<li class="list-group-item change-history">

src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges{
3838
@Output() showLeftNavigationEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
3939
@Output() disableCodeLinesLazyLoadingEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
4040
@Output() markAsViewedEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
41+
@Output() subscribeEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
4142
@Output() showLineNumbersEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
4243
@Output() apiRevisionApprovalEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
4344
@Output() reviewApprovalEmitter : EventEmitter<boolean> = new EventEmitter<boolean>();
@@ -50,6 +51,7 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges{
5051
showHiddenAPISwitch : boolean = false;
5152
showLeftNavigationSwitch : boolean = true;
5253
markedAsViewSwitch : boolean = false;
54+
subscribeSwitch : boolean = false;
5355
showLineNumbersSwitch : boolean = true;
5456
disableCodeLinesLazyLoading: boolean = false;
5557

@@ -104,26 +106,29 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges{
104106
}
105107

106108
ngOnChanges(changes: SimpleChanges) {
107-
if (changes['diffStyleInput']) {
109+
if (changes['diffStyleInput'] && changes['diffStyleInput'].currentValue != undefined) {
108110
this.setSelectedDiffStyle();
109111
}
110112

111-
if (changes['userProfile']) {
113+
if (changes['userProfile'] && changes['userProfile'].currentValue != undefined) {
114+
this.setSubscribeSwitch();
115+
this.setMarkedAsViewSwitch();
112116
this.setPageOptionValues();
113117
}
114118

115119
if (changes['activeAPIRevision'] && changes['activeAPIRevision'].currentValue != undefined) {
116-
this.markedAsViewSwitch = this.activeAPIRevision!.viewedBy.includes(this.userProfile?.userName!);
120+
this.setMarkedAsViewSwitch();
117121
this.selectedApprovers = this.activeAPIRevision!.assignedReviewers.map(reviewer => reviewer.assingedTo);
118122
this.setAPIRevisionApprovalStates();
119123
this.setPullRequestsInfo();
120124
}
121125

122-
if (changes['diffAPIRevision']) {
126+
if (changes['diffAPIRevision'] && changes['diffAPIRevision'].currentValue != undefined) {
123127
this.setAPIRevisionApprovalStates();
124128
}
125129

126-
if (changes['review']) {
130+
if (changes['review'] && changes['review'].currentValue != undefined) {
131+
this.setSubscribeSwitch();
127132
this.setReviewApprovalStatus();
128133
}
129134
}
@@ -193,6 +198,14 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges{
193198
this.markAsViewedEmitter.emit(event.checked);
194199
}
195200

201+
/**
202+
* Callback for markedAsViewSwitch Change
203+
* @param event the Filter event
204+
*/
205+
onSubscribeSwitchChange(event: InputSwitchOnChangeEvent) {
206+
this.subscribeEmitter.emit(event.checked);
207+
}
208+
196209
/**
197210
* Callback for showLineNumbersSwitch Change
198211
* @param event the Filter event
@@ -287,6 +300,14 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges{
287300
});
288301
}
289302
}
303+
304+
setSubscribeSwitch() {
305+
this.subscribeSwitch = (this.userProfile && this.review) ? this.review!.subscribers.includes(this.userProfile?.email!) : this.subscribeSwitch;
306+
}
307+
308+
setMarkedAsViewSwitch() {
309+
this.markedAsViewSwitch = (this.activeAPIRevision && this.userProfile)? this.activeAPIRevision!.viewedBy.includes(this.userProfile?.userName!): this.markedAsViewSwitch;
310+
}
290311

291312
handleAPIRevisionApprovalAction() {
292313
if (!this.activeAPIRevisionIsApprovedByCurrentUser && (this.hasActiveConversation || this.hasFatalDiagnostics)) {

src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
(showLeftNavigationEmitter)="handleShowLeftNavigationEmitter($event)"
6565
(diffStyleEmitter)="handleDiffStyleEmitter($event)"
6666
(markAsViewedEmitter)="handleMarkAsViewedEmitter($event)"
67+
(subscribeEmitter)="handleSubscribeEmitter($event)"
6768
(showLineNumbersEmitter)="handleShowLineNumbersEmitter($event)"
6869
(apiRevisionApprovalEmitter)="handleApiRevisionApprovalEmitter($event)"
6970
(reviewApprovalEmitter)="handleReviewApprovalEmitter($event)"

src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ export class ReviewPageComponent implements OnInit {
387387
});
388388
}
389389

390+
handleSubscribeEmitter(state: boolean) {
391+
this.reviewsService.toggleReviewSubscriptionByUser(this.reviewId!, state).pipe(take(1)).subscribe();
392+
}
393+
390394
handleApiRevisionApprovalEmitter(value: boolean) {
391395
if (value) {
392396
this.apiRevisionsService.toggleAPIRevisionApproval(this.reviewId!, this.activeApiRevisionId!).pipe(take(1)).subscribe({

src/dotnet/APIView/ClientSPA/src/app/_models/review.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export class Review {
1414
isDeleted: boolean
1515
isApproved: boolean
1616
changeHistory: ChangeHistory[]
17+
subscribers: string[]
1718

1819
constructor() {
1920
this.id = ''
@@ -23,6 +24,7 @@ export class Review {
2324
this.isDeleted = false
2425
this.isApproved = false
2526
this.changeHistory = []
27+
this.subscribers = []
2628
}
2729
}
2830

src/dotnet/APIView/ClientSPA/src/app/_services/reviews/reviews.service.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ export class ReviewsService {
106106
});
107107
}
108108

109+
toggleReviewSubscriptionByUser(reviewId: string, state: boolean) {
110+
let params = new HttpParams();
111+
params = params.append('state', state.toString());
112+
113+
const headers = new HttpHeaders({
114+
'Content-Type': 'application/json',
115+
});
116+
117+
return this.http.post<APIRevision>(this.baseUrl + `/${reviewId}/toggleSubscribe`, {},
118+
{
119+
headers: headers,
120+
params: params,
121+
withCredentials: true
122+
});
123+
}
124+
109125
getReviewContent(reviewId: string, activeApiRevisionId: string | null = null, diffApiRevisionId: string | null = null) : Observable<ArrayBuffer>{
110126
let params = new HttpParams();
111127
if (activeApiRevisionId) {

0 commit comments

Comments
 (0)