@@ -21,37 +21,53 @@ import { ReviewType } from '../../src/github/views';
2121import PullRequestContext from '../common/context' ;
2222import { CommentView } from './comment' ;
2323import Diff from './diff' ;
24- import { assigneeIcon , commitIcon , linkIcon , mergeIcon , plusIcon } from './icon' ;
24+ import { commitIcon , mergeIcon , plusIcon } from './icon' ;
2525import { nbsp } from './space' ;
2626import { Timestamp } from './timestamp' ;
2727import { AuthorLink , Avatar } from './user' ;
2828
29- export const Timeline = ( { events } : { events : TimelineEvent [ ] } ) => (
30- < >
31- { events . map ( event => {
32- switch ( event . event ) {
33- case EventType . Committed :
34- return < CommitEventView key = { `commit${ event . id } ` } { ...event } /> ;
35- case EventType . Reviewed :
36- return < ReviewEventView key = { `review${ event . id } ` } { ...event } /> ;
37- case EventType . Commented :
38- return < CommentEventView key = { `comment${ event . id } ` } { ...event } /> ;
39- case EventType . Merged :
40- return < MergedEventView key = { `merged${ event . id } ` } { ...event } /> ;
41- case EventType . Assigned :
42- return < AssignEventView key = { `assign${ event . id } ` } { ...event } /> ;
43- case EventType . HeadRefDeleted :
44- return < HeadDeleteEventView key = { `head${ event . id } ` } { ...event } /> ;
45- case EventType . CrossReferenced :
46- return < CrossReferencedEventView key = { `cross${ event . id } ` } { ...event } /> ;
47- case EventType . NewCommitsSinceReview :
48- return < NewCommitsSinceReviewEventView key = { `newCommits${ event . id } ` } /> ;
49- default :
50- throw new UnreachableCaseError ( event ) ;
29+ export const Timeline = ( { events } : { events : TimelineEvent [ ] } ) => {
30+ const consolidatedEvents : TimelineEvent [ ] = [ ] ;
31+ for ( let i = 0 ; i < events . length ; i ++ ) {
32+ if ( ( i > 0 ) && ( events [ i ] . event === EventType . Assigned ) && ( consolidatedEvents [ consolidatedEvents . length - 1 ] . event === EventType . Assigned ) ) {
33+ const lastEvent = consolidatedEvents [ consolidatedEvents . length - 1 ] as AssignEvent ;
34+ const newEvent = events [ i ] as AssignEvent ;
35+ if ( new Date ( lastEvent . createdAt ) . getTime ( ) + ( 1000 * 60 * 10 ) > new Date ( newEvent . createdAt ) . getTime ( ) ) { // within 10 minutes
36+ if ( lastEvent . assignees . every ( a => a . id !== newEvent . assignees [ 0 ] . id ) ) {
37+ lastEvent . assignees = [ ...lastEvent . assignees , ...newEvent . assignees ] ;
38+ }
39+ lastEvent . createdAt = newEvent . createdAt ;
40+ } else {
41+ consolidatedEvents . push ( newEvent ) ;
5142 }
52- } ) }
53- </ >
54- ) ;
43+ } else {
44+ consolidatedEvents . push ( events [ i ] ) ;
45+ }
46+ }
47+
48+ return < > { consolidatedEvents . map ( event => {
49+ switch ( event . event ) {
50+ case EventType . Committed :
51+ return < CommitEventView key = { `commit${ event . id } ` } { ...event } /> ;
52+ case EventType . Reviewed :
53+ return < ReviewEventView key = { `review${ event . id } ` } { ...event } /> ;
54+ case EventType . Commented :
55+ return < CommentEventView key = { `comment${ event . id } ` } { ...event } /> ;
56+ case EventType . Merged :
57+ return < MergedEventView key = { `merged${ event . id } ` } { ...event } /> ;
58+ case EventType . Assigned :
59+ return < AssignEventView key = { `assign${ event . id } ` } { ...event } /> ;
60+ case EventType . HeadRefDeleted :
61+ return < HeadDeleteEventView key = { `head${ event . id } ` } { ...event } /> ;
62+ case EventType . CrossReferenced :
63+ return < CrossReferencedEventView key = { `cross${ event . id } ` } { ...event } /> ;
64+ case EventType . NewCommitsSinceReview :
65+ return < NewCommitsSinceReviewEventView key = { `newCommits${ event . id } ` } /> ;
66+ default :
67+ throw new UnreachableCaseError ( event ) ;
68+ }
69+ } ) } </ > ;
70+ } ;
5571
5672export default Timeline ;
5773
@@ -63,7 +79,6 @@ const CommitEventView = (event: CommitEvent) => (
6379 < div className = "avatar-container" >
6480 < Avatar for = { event . author } />
6581 </ div >
66- < AuthorLink for = { event . author } />
6782 < div className = "message-container" >
6883 < a className = "message" href = { event . htmlUrl } title = { event . htmlUrl } >
6984 { event . message . substr ( 0 , event . message . indexOf ( '\n' ) > - 1 ? event . message . indexOf ( '\n' ) : event . message . length ) }
@@ -290,8 +305,6 @@ const CrossReferencedEventView = (event: CrossReferencedEvent) => {
290305 return (
291306 < div className = "comment-container commit" >
292307 < div className = "commit-message" >
293- { linkIcon }
294- { nbsp }
295308 < div className = "avatar-container" >
296309 < Avatar for = { event . actor } />
297310 </ div >
@@ -307,19 +320,24 @@ const CrossReferencedEventView = (event: CrossReferencedEvent) => {
307320 ) ;
308321} ;
309322
323+ function joinWithAnd ( arr : JSX . Element [ ] ) : JSX . Element {
324+ if ( arr . length === 0 ) return < > </ > ;
325+ if ( arr . length === 1 ) return arr [ 0 ] ;
326+ if ( arr . length === 2 ) return < > { arr [ 0 ] } and { arr [ 1 ] } </ > ;
327+ return < > { arr . slice ( 0 , - 1 ) . map ( item => < > { item } , </ > ) } and { arr [ arr . length - 1 ] } </ > ;
328+ }
329+
310330const AssignEventView = ( event : AssignEvent ) => {
311- const { actor, assignee } = event ;
331+ const { actor, assignees } = event ;
312332 return (
313333 < div className = "comment-container commit" >
314334 < div className = "commit-message" >
315- { assigneeIcon }
316- { nbsp }
317335 < div className = "avatar-container" >
318336 < Avatar for = { actor } />
319337 </ div >
320338 < AuthorLink for = { actor } />
321339 < div className = "message" >
322- assigned < AuthorLink for = { assignee } /> to this pull request
340+ assigned { joinWithAnd ( assignees . map ( a => < AuthorLink key = { a . id } for = { a } /> ) ) } to this pull request
323341 </ div >
324342 </ div >
325343 < Timestamp date = { event . createdAt } />
0 commit comments