@@ -17,8 +17,10 @@ import {
1717 ReopenedEvent ,
1818 ReviewEvent ,
1919 TimelineEvent ,
20+ UnassignEvent ,
2021} from '../../src/common/timelineEvent' ;
2122import { groupBy , UnreachableCaseError } from '../../src/common/utils' ;
23+ import { IAccount , IActor } from '../../src/github/interface' ;
2224import { ReviewType } from '../../src/github/views' ;
2325import PullRequestContext from '../common/context' ;
2426import { CommentView } from './comment' ;
@@ -28,16 +30,32 @@ import { nbsp } from './space';
2830import { Timestamp } from './timestamp' ;
2931import { AuthorLink , Avatar } from './user' ;
3032
33+ function isAssignUnassignEvent ( event : TimelineEvent | ConsolidatedAssignUnassignEvent ) : event is AssignEvent | UnassignEvent {
34+ return event . event === EventType . Assigned || event . event === EventType . Unassigned ;
35+ }
36+
37+ interface ConsolidatedAssignUnassignEvent {
38+ id : number ;
39+ event : EventType . Assigned | EventType . Unassigned ;
40+ assignees ?: IAccount [ ] ;
41+ unassignees ?: IAccount [ ] ;
42+ actor : IActor ;
43+ createdAt : string ;
44+ }
45+
3146export const Timeline = ( { events, isIssue } : { events : TimelineEvent [ ] , isIssue : boolean } ) => {
32- const consolidatedEvents : TimelineEvent [ ] = [ ] ;
47+ const consolidatedEvents : ( TimelineEvent | ConsolidatedAssignUnassignEvent ) [ ] = [ ] ;
3348 for ( let i = 0 ; i < events . length ; i ++ ) {
34- if ( ( i > 0 ) && ( events [ i ] . event === EventType . Assigned ) && ( consolidatedEvents [ consolidatedEvents . length - 1 ] . event === EventType . Assigned ) ) {
35- const lastEvent = consolidatedEvents [ consolidatedEvents . length - 1 ] as AssignEvent ;
36- const newEvent = events [ i ] as AssignEvent ;
37- if ( new Date ( lastEvent . createdAt ) . getTime ( ) + ( 1000 * 60 * 10 ) > new Date ( newEvent . createdAt ) . getTime ( ) ) { // within 10 minutes
38- if ( lastEvent . assignees . every ( a => a . id !== newEvent . assignees [ 0 ] . id ) ) {
39- lastEvent . assignees = [ ...lastEvent . assignees , ...newEvent . assignees ] ;
40- }
49+ if ( ( i > 0 ) && isAssignUnassignEvent ( events [ i ] ) && isAssignUnassignEvent ( consolidatedEvents [ consolidatedEvents . length - 1 ] ) ) {
50+ const lastEvent = consolidatedEvents [ consolidatedEvents . length - 1 ] as ConsolidatedAssignUnassignEvent ;
51+ const newEvent = events [ i ] as ConsolidatedAssignUnassignEvent ;
52+ if ( ( lastEvent . actor . login === newEvent . actor . login ) && ( new Date ( lastEvent . createdAt ) . getTime ( ) + ( 1000 * 60 * 10 ) > new Date ( newEvent . createdAt ) . getTime ( ) ) ) { // within 10 minutes
53+ const assignees = lastEvent . assignees || [ ] ;
54+ const unassignees = lastEvent . unassignees || [ ] ;
55+ const newAssignees = newEvent . assignees ?. filter ( a => ! assignees . some ( b => b . id === a . id ) ) ?? [ ] ;
56+ const newUnassignees = newEvent . unassignees ?. filter ( a => ! unassignees . some ( b => b . id === a . id ) ) ?? [ ] ;
57+ lastEvent . assignees = [ ...assignees , ...newAssignees ] ;
58+ lastEvent . unassignees = [ ...unassignees , ...newUnassignees ] ;
4159 lastEvent . createdAt = newEvent . createdAt ;
4260 } else {
4361 consolidatedEvents . push ( newEvent ) ;
@@ -58,7 +76,9 @@ export const Timeline = ({ events, isIssue }: { events: TimelineEvent[], isIssue
5876 case EventType . Merged :
5977 return < MergedEventView key = { `merged${ event . id } ` } { ...event } /> ;
6078 case EventType . Assigned :
61- return < AssignEventView key = { `assign${ event . id } ` } event = { event } isIssue = { isIssue } /> ;
79+ return < AssignUnassignEventView key = { `assign${ event . id } ` } event = { event } /> ;
80+ case EventType . Unassigned :
81+ return < AssignUnassignEventView key = { `unassign${ event . id } ` } event = { event } /> ;
6282 case EventType . HeadRefDeleted :
6383 return < HeadDeleteEventView key = { `head${ event . id } ` } { ...event } /> ;
6484 case EventType . CrossReferenced :
@@ -344,9 +364,22 @@ function joinWithAnd(arr: JSX.Element[]): JSX.Element {
344364 return < > { arr . slice ( 0 , - 1 ) . map ( item => < > { item } , </ > ) } and { arr [ arr . length - 1 ] } </ > ;
345365}
346366
347- const AssignEventView = ( { event, isIssue } : { event : AssignEvent , isIssue : boolean } ) => {
348- const { actor, assignees } = event ;
367+ const AssignUnassignEventView = ( { event } : { event : AssignEvent | UnassignEvent | ConsolidatedAssignUnassignEvent } ) => {
368+ const { actor } = event ;
369+ const assignees = ( event as AssignEvent ) . assignees || [ ] ;
370+ const unassignees = ( event as UnassignEvent ) . unassignees || [ ] ;
349371 const joinedAssignees = joinWithAnd ( assignees . map ( a => < AuthorLink key = { a . id } for = { a } /> ) ) ;
372+ const joinedUnassignees = joinWithAnd ( unassignees . map ( a => < AuthorLink key = { a . id } for = { a } /> ) ) ;
373+
374+ let message : JSX . Element ;
375+ if ( assignees . length > 0 && unassignees . length > 0 ) {
376+ message = < > assigned { joinedAssignees } and unassigned { joinedUnassignees } </ > ;
377+ } else if ( assignees . length > 0 ) {
378+ message = < > assigned { joinedAssignees } </ > ;
379+ } else {
380+ message = < > unassigned { joinedUnassignees } </ > ;
381+ }
382+
350383 return (
351384 < div className = "comment-container commit" >
352385 < div className = "commit-message" >
@@ -355,9 +388,7 @@ const AssignEventView = ({ event, isIssue }: { event: AssignEvent, isIssue: bool
355388 </ div >
356389 < AuthorLink for = { actor } />
357390 < div className = "message" >
358- { isIssue
359- ? < > assigned { joinedAssignees } </ >
360- : < > assigned { joinedAssignees } to this pull request</ > }
391+ { message }
361392 </ div >
362393 </ div >
363394 < Timestamp date = { event . createdAt } />
0 commit comments