@@ -22,6 +22,7 @@ import warn from '../../util/warn';
2222import error from '../../util/error' ;
2323import { ContributionSurveyRowStatus } from '../../models/ContributionSurveyRowStatus' ;
2424import dangerModeConfirm from '../../util/dangerModeConfirm' ;
25+ import any = jasmine . any ;
2526
2627export enum DeputyContributionSurveyRowState {
2728 /*
@@ -39,6 +40,31 @@ export enum DeputyContributionSurveyRowState {
3940 Closed
4041}
4142
43+ enum DiffExpandStateType {
44+ /**
45+ * Diffs are not expanded.
46+ */
47+ Unexpanded ,
48+ /**
49+ * Diffs are actively expanding.
50+ */
51+ Expanding ,
52+ /**
53+ * Diffs are completely expanded.
54+ */
55+ Expanded ,
56+ /**
57+ * The expansion was cancelled, but loaded diffs remain open.
58+ * The next click will unexpand diffs, which can then lead them to being expanded again.
59+ * Since the diff HTML is saved and cached, we don't have to worry about re-loading diffs.
60+ */
61+ Cancelled
62+ }
63+
64+ type DiffExpandState =
65+ | { type : Exclude < DiffExpandStateType , DiffExpandStateType . Expanding > }
66+ | { type : DiffExpandStateType . Expanding , progress : [ number , number ] }
67+
4268/**
4369 * A UI element used for denoting the following aspects of a page in the contribution
4470 * survey:
@@ -115,6 +141,14 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
115141 * Button that checks all revisions of this row
116142 */
117143 checkAllButton : OO . ui . ButtonWidget ;
144+ /**
145+ * Button that opens all diff previews for this row
146+ */
147+ expandAllButton : OO . ui . ButtonWidget ;
148+ /**
149+ * Progress bar for diff preview expansion. Only shown when diffs are actively expanding.
150+ */
151+ expandAllProgress : OO . ui . ProgressBarWidget ;
118152 /**
119153 * Message box displayed when a user has set a status but not yet cleared all diffs.
120154 */
@@ -128,6 +162,7 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
128162 * finished.
129163 */
130164 finishedRow : DeputyFinishedContributionSurveyRow ;
165+ private diffExpandState : DiffExpandState ;
131166
132167 /**
133168 * OOUI DropdownWidget for the current row status
@@ -664,15 +699,18 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
664699 < i > { mw . msg ( 'deputy.session.row.pageonly' ) } </ i >
665700 </ div > ) ;
666701 } else {
702+ let anyExpanded = false ;
667703 const cciConfig = window . deputy . config . cci ;
668704 const maxSize = cciConfig . maxSizeToAutoShowDiff . get ( ) ;
669705 for ( const revision of diffs . values ( ) ) {
706+ const expanded = cciConfig . autoShowDiff . get ( ) &&
707+ diffs . size < cciConfig . maxRevisionsToAutoShowDiff . get ( ) &&
708+ ( maxSize === - 1 || Math . abs ( revision . diffsize ) < maxSize ) ;
709+ if ( expanded ) {
710+ anyExpanded = true ;
711+ }
670712 const revisionUIEl = new DeputyContributionSurveyRevision (
671- revision , this , {
672- expanded : cciConfig . autoShowDiff . get ( ) &&
673- diffs . size < cciConfig . maxRevisionsToAutoShowDiff . get ( ) &&
674- ( maxSize === - 1 || Math . abs ( revision . diffsize ) < maxSize )
675- }
713+ revision , this , { expanded }
676714 ) ;
677715
678716 revisionUIEl . addEventListener (
@@ -687,6 +725,11 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
687725 revisionList . appendChild ( revisionUIEl . render ( ) ) ;
688726 this . revisions . push ( revisionUIEl ) ;
689727 }
728+ this . diffExpandState = {
729+ type : anyExpanded ?
730+ DiffExpandStateType . Expanded : DiffExpandStateType . Unexpanded
731+ } ;
732+ this . updateDiffExpandButton ( ) ;
690733 }
691734
692735 return revisionList ;
@@ -697,8 +740,8 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
697740 *
698741 * @return An HTML element
699742 */
700- renderLinks ( ) : JSX . Element {
701- return < span class = "dp-cs-row-links" >
743+ renderLinks ( ) : JSX . Element [ ] {
744+ return [
702745 < a
703746 class = "dp-cs-row-link dp-cs-row-edit"
704747 target = "_blank"
@@ -714,7 +757,7 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
714757 icon : 'edit' ,
715758 framed : false
716759 } ) ) }
717- </ a >
760+ </ a > ,
718761 < a
719762 class = "dp-cs-row-link dp-cs-row-talk"
720763 target = "_blank"
@@ -729,7 +772,7 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
729772 icon : 'speechBubbles' ,
730773 framed : false
731774 } ) ) }
732- </ a >
775+ </ a > ,
733776 < a
734777 class = "dp-cs-row-link dp-cs-row-history"
735778 target = "_blank"
@@ -746,7 +789,7 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
746789 framed : false
747790 } ) ) }
748791 </ a >
749- </ span > ;
792+ ] ;
750793 }
751794
752795 /**
@@ -875,6 +918,27 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
875918 ) ;
876919 } ) ;
877920
921+ // Build expand all button
922+ this . expandAllButton = new OO . ui . ButtonWidget ( {
923+ icon : 'viewDetails' ,
924+ label : mw . msg ( 'deputy.session.row.expandAll' ) ,
925+ title : mw . msg ( 'deputy.session.row.expandAll' ) ,
926+ invisibleLabel : true ,
927+ framed : false
928+ } ) ;
929+ this . expandAllButton . on ( 'click' , async ( ) => {
930+ await this . handleDiffExpansion ( ) ;
931+ } ) ;
932+ // TODO: @types /oojs-ui limitation
933+ // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/74902
934+ this . expandAllProgress = new ( OO . ui . ProgressBarWidget as any ) ( {
935+ progress : false ,
936+ inline : true
937+ } ) ;
938+ this . expandAllButton . $element . append ( this . expandAllProgress . $element ) ;
939+
940+ this . updateDiffExpandButton ( ) ;
941+
878942 // Build content toggler
879943 const contentToggle = new OO . ui . ButtonWidget ( {
880944 classes : [ 'dp-cs-row-toggle' ] ,
@@ -927,8 +991,13 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
927991 { this . row . title . getPrefixedText ( ) }
928992 </ a >
929993 { diffs && this . renderDetails ( diffs ) }
930- { this . renderLinks ( ) }
931- { ! this . wasFinished && diffs && diffs . size > 0 && unwrapWidget ( this . checkAllButton ) }
994+ < span class = "dp-cs-row-buttons" >
995+ { this . renderLinks ( ) }
996+ { ! this . wasFinished && diffs && diffs . size > 0 && [
997+ unwrapWidget ( this . checkAllButton ) ,
998+ unwrapWidget ( this . expandAllButton )
999+ ] }
1000+ </ span >
9321001 { ! contentContainer . classList . contains ( 'dp-cs-row-content-empty' ) &&
9331002 unwrapWidget ( contentToggle ) }
9341003 </ div > ;
@@ -1106,4 +1175,102 @@ export default class DeputyContributionSurveyRow extends EventTarget implements
11061175 }
11071176 }
11081177
1178+ /**
1179+ * Updates the expand all button based on the current state of diff expansion.
1180+ */
1181+ updateDiffExpandButton ( ) {
1182+ if ( ! this . expandAllButton || ! this . diffExpandState || ! this . expandAllProgress ) {
1183+ return ;
1184+ }
1185+ const state = this . diffExpandState ;
1186+ const type = state . type ;
1187+ switch ( type ) {
1188+ case DiffExpandStateType . Unexpanded :
1189+ this . expandAllButton . setTitle ( mw . msg ( 'deputy.session.row.expandAll' ) ) ;
1190+ this . expandAllButton . setLabel ( mw . msg ( 'deputy.session.row.expandAll' ) ) ;
1191+ this . expandAllButton . setIcon ( 'viewDetails' ) ;
1192+ this . expandAllButton . clearFlags ( ) ;
1193+ this . expandAllProgress . toggle ( false ) ;
1194+ break ;
1195+ case DiffExpandStateType . Expanding :
1196+ this . expandAllButton . setTitle ( mw . msg ( 'deputy.session.row.expandAll.cancel' ) ) ;
1197+ this . expandAllButton . setLabel ( mw . msg ( 'deputy.session.row.expandAll.cancel' ) ) ;
1198+ this . expandAllButton . setIcon ( 'cancel' ) ;
1199+ this . expandAllButton . setFlags ( [ 'destructive' ] ) ;
1200+ this . expandAllProgress . toggle ( true ) ;
1201+ this . expandAllProgress . setProgress (
1202+ ( state . progress [ 0 ] / state . progress [ 1 ] ) * 100
1203+ ) ;
1204+ unwrapWidget ( this . expandAllProgress ) . classList
1205+ . toggle ( 'dp-progress-failed' , false ) ;
1206+ break ;
1207+ case DiffExpandStateType . Expanded :
1208+ case DiffExpandStateType . Cancelled :
1209+ this . expandAllButton . setTitle ( mw . msg ( 'deputy.session.row.expandAll.close' ) ) ;
1210+ this . expandAllButton . setLabel ( mw . msg ( 'deputy.session.row.expandAll.close' ) ) ;
1211+ this . expandAllButton . setIcon ( {
1212+ [ DiffExpandStateType . Expanded ] : 'menu' ,
1213+ [ DiffExpandStateType . Cancelled ] : 'reload'
1214+ } [ type ] ) ;
1215+ this . expandAllButton . clearFlags ( ) ;
1216+ this . expandAllProgress . toggle ( type === DiffExpandStateType . Cancelled ) ;
1217+ this . expandAllProgress . setProgress ( 100 ) ;
1218+ unwrapWidget ( this . expandAllProgress ) . classList
1219+ . toggle ( 'dp-progress-failed' , type === DiffExpandStateType . Cancelled ) ;
1220+ break ;
1221+ }
1222+ }
1223+
1224+ /**
1225+ * Handle the diff expansion process.
1226+ */
1227+ async handleDiffExpansion ( ) {
1228+ if ( ! this . expandAllButton || ! this . diffExpandState ) {
1229+ return ;
1230+ }
1231+ const status = this . diffExpandState . type ;
1232+ switch ( status ) {
1233+ case DiffExpandStateType . Unexpanded :
1234+ if ( this . revisions ) {
1235+ this . diffExpandState = {
1236+ type : DiffExpandStateType . Expanding ,
1237+ progress : [ 0 , this . revisions . length ]
1238+ } ;
1239+ for ( const i in this . revisions ) {
1240+ this . updateDiffExpandButton ( ) ;
1241+ const revision = this . revisions [ i ] ;
1242+ await revision . handleDiffToggle ( true ) ;
1243+ // @ts -expect-error `this.diffExpandState` can change values in the middle
1244+ // of this loop, because the user can press the button and change the state
1245+ // mid-execution.
1246+ if ( this . diffExpandState . type === DiffExpandStateType . Cancelled ) {
1247+ // User cancelled the expansion. Stop expanding more diffs now.
1248+ return ;
1249+ }
1250+ this . diffExpandState = {
1251+ type : DiffExpandStateType . Expanding ,
1252+ progress : [ + i + 1 , this . revisions . length ]
1253+ } ;
1254+ }
1255+ this . diffExpandState = {
1256+ type : DiffExpandStateType . Expanded
1257+ } ;
1258+ this . updateDiffExpandButton ( ) ;
1259+ }
1260+ break ;
1261+ case DiffExpandStateType . Expanding :
1262+ this . diffExpandState = { type : DiffExpandStateType . Cancelled } ;
1263+ this . updateDiffExpandButton ( ) ;
1264+ break ;
1265+ case DiffExpandStateType . Expanded :
1266+ case DiffExpandStateType . Cancelled :
1267+ for ( const revision of this . revisions ) {
1268+ await revision . handleDiffToggle ( false ) ;
1269+ }
1270+ this . diffExpandState = { type : DiffExpandStateType . Unexpanded } ;
1271+ this . updateDiffExpandButton ( ) ;
1272+ break ;
1273+ }
1274+ }
1275+
11091276}
0 commit comments