77 :download =" download_percentage"
88 :extraction =" extraction_percentage"
99 :statustext =" status_text"
10+ :cancelable =" !!active_abort_controller"
11+ @cancel =" cancelInstallOperation"
1012 />
1113 <v-dialog
1214 v-model =" show_dialog"
@@ -538,6 +540,7 @@ export default Vue.extend({
538540 install_from_file_last_level: - 1 ,
539541 active_operation_identifier: localStorage .getItem (ACTIVE_OPERATION_KEY ) as null | string ,
540542 active_operation_type: (localStorage .getItem (ACTIVE_OPERATION_TYPE_KEY ) ?? null ) as null | ' install' | ' update' ,
543+ active_abort_controller: null as null | AbortController ,
541544 }
542545 },
543546 computed: {
@@ -666,11 +669,32 @@ export default Vue.extend({
666669 destroyed() {
667670 clearInterval (this .metrics_interval )
668671 this .stopUploadKeepAlive ()
672+ this .active_abort_controller ?.abort ()
673+ this .active_abort_controller = null
669674 },
670675 methods: {
671676 clearEditedExtension() {
672677 this .edited_extension = null
673678 },
679+ beginInstallOperation(): AbortController {
680+ this .active_abort_controller ?.abort ()
681+ const controller = new AbortController ()
682+ this .active_abort_controller = controller
683+ return controller
684+ },
685+ cancelInstallOperation(): void {
686+ this .active_abort_controller ?.abort ()
687+ },
688+ showAlertError(error : unknown ): void {
689+ this .alerter = true
690+ this .alerter_error = String (error )
691+ },
692+ finishInstallOperation(): void {
693+ this .active_abort_controller = null
694+ this .clearInstallingState ()
695+ this .resetPullOutput ()
696+ this .fetchInstalledExtensions ()
697+ },
674698 setInstallFromFilePhase(phase : TarInstallPhase ) {
675699 this .install_from_file_phase = phase
676700 if (phase !== ' error' ) {
@@ -827,24 +851,30 @@ export default Vue.extend({
827851 async update(extension : InstalledExtensionData , version : string ) {
828852 this .setInstallingState (extension .identifier , ' update' )
829853 this .show_pull_output = true
830- const tracker = this .getTracker ()
854+ const controller = this .beginInstallOperation ()
855+ const tracker = this .getTracker (controller .signal )
831856 kraken .updateExtensionToVersion (
832857 extension .identifier ,
833858 version ,
834859 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
860+ controller .signal ,
835861 )
836862 .then (() => {
837- this .fetchInstalledExtensions ()
838863 notifier .pushSuccess (' EXTENSION_UPDATE_SUCCESS' , ` ${extension .name } updated successfully. ` , true )
839864 })
840- .catch ((error ) => {
841- this .alerter = true
842- this .alerter_error = String (error )
865+ .catch (async (error ) => {
866+ if (axios .isCancel (error )) {
867+ if (controller !== this .active_abort_controller ) return
868+ await kraken .uninstallExtensionVersion (extension .identifier , version )
869+ .catch (() => { /* version may not be registered yet */ })
870+ notifier .pushInfo (' EXTENSION_UPDATE_CANCELLED' , ' Extension update was cancelled.' , true )
871+ return
872+ }
873+ this .showAlertError (error )
843874 notifier .pushBackError (' EXTENSION_UPDATE_FAIL' , error )
844875 })
845876 .finally (() => {
846- this .clearInstallingState ()
847- this .resetPullOutput ()
877+ if (controller === this .active_abort_controller ) this .finishInstallOperation ()
848878 })
849879 },
850880 metricsFor(extension : InstalledExtensionData ): { cpu: number , memory: number } | Record <string , never > {
@@ -993,24 +1023,30 @@ export default Vue.extend({
9931023 this .setInstallingState (extension .identifier , ' install' )
9941024 this .show_dialog = false
9951025 this .show_pull_output = true
996- const tracker = this .getTracker ()
1026+ const controller = this .beginInstallOperation ()
1027+ const tracker = this .getTracker (controller .signal )
9971028
9981029 kraken .installExtension (
9991030 extension ,
10001031 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
1032+ controller .signal ,
10011033 )
10021034 .then (() => {
1003- this .fetchInstalledExtensions ()
10041035 notifier .pushSuccess (' EXTENSION_INSTALL_SUCCESS' , ` ${extension .name } installed successfully. ` , true )
10051036 })
1006- .catch ((error ) => {
1007- this .alerter = true
1008- this .alerter_error = String (error )
1009- notifier .pushBackError (' EXTENSIONS_INSTALL_FAIL' , error )
1037+ .catch (async (error ) => {
1038+ if (axios .isCancel (error )) {
1039+ if (controller !== this .active_abort_controller ) return
1040+ await kraken .uninstallExtensionVersion (extension .identifier , extension .tag )
1041+ .catch (() => { /* version may not be registered yet */ })
1042+ notifier .pushInfo (' EXTENSION_INSTALL_CANCELLED' , ' Extension install was cancelled.' , true )
1043+ return
1044+ }
1045+ this .showAlertError (error )
1046+ notifier .pushBackError (' EXTENSION_INSTALL_FAIL' , error )
10101047 })
10111048 .finally (() => {
1012- this .clearInstallingState ()
1013- this .resetPullOutput ()
1049+ if (controller === this .active_abort_controller ) this .finishInstallOperation ()
10141050 })
10151051 },
10161052 async performActionFromModal(
@@ -1119,17 +1155,17 @@ export default Vue.extend({
11191155 temp [extension .identifier ].loading = loading
11201156 this .installed_extensions = temp
11211157 },
1122- getTracker(): PullTracker {
1158+ getTracker(signal : AbortSignal ): PullTracker {
11231159 return new PullTracker (
11241160 () => {
11251161 setTimeout (() => {
11261162 this .show_pull_output = false
11271163 }, 1000 )
11281164 },
11291165 (error ) => {
1130- this . alerter = true
1131- this .alerter_error = String (error )
1132- notifier .pushBackError (' EXTENSIONS_INSTALL_FAIL ' , error )
1166+ if ( signal . aborted ) return
1167+ this .showAlertError (error )
1168+ notifier .pushBackError (' EXTENSION_INSTALL_FAIL ' , error )
11331169 this .show_pull_output = false
11341170 },
11351171 )
@@ -1206,7 +1242,8 @@ export default Vue.extend({
12061242 }
12071243
12081244 this .show_pull_output = true
1209- const tracker = this .getTracker ()
1245+ const controller = this .beginInstallOperation ()
1246+ const tracker = this .getTracker (controller .signal )
12101247 this .setInstallFromFilePhase (' installing' )
12111248 this .install_from_file_install_progress = 0
12121249 this .install_from_file_status_text = ' Starting installation...'
@@ -1216,20 +1253,29 @@ export default Vue.extend({
12161253 extension ,
12171254 this .upload_temp_tag ,
12181255 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
1256+ controller .signal ,
12191257 )
12201258 this .setInstallFromFilePhase (' success' )
12211259 this .install_from_file_status_text = ' Extension installed successfully'
12221260 this .stopUploadKeepAlive ()
12231261 this .upload_temp_tag = null
12241262 this .upload_metadata = null
1225- this .fetchInstalledExtensions ()
12261263 } catch (error ) {
1227- this .applyInstallFromFileError (String (error ))
1228- this .alerter = true
1229- this .alerter_error = String (error )
1230- notifier .pushBackError (' EXTENSION_FINALIZE_FAIL' , error )
1264+ if (axios .isCancel (error )) {
1265+ if (controller === this .active_abort_controller ) {
1266+ this .setInstallFromFilePhase (' ready' )
1267+ this .install_from_file_status_text = ' '
1268+ await kraken .uninstallExtensionVersion (extension .identifier , extension .tag )
1269+ .catch (() => { /* version may not be registered yet */ })
1270+ notifier .pushInfo (' EXTENSION_INSTALL_CANCELLED' , ' Installation from file was cancelled.' , true )
1271+ }
1272+ } else {
1273+ this .applyInstallFromFileError (String (error ))
1274+ this .showAlertError (error )
1275+ notifier .pushBackError (' EXTENSION_FINALIZE_FAIL' , error )
1276+ }
12311277 } finally {
1232- this .resetPullOutput ()
1278+ if ( controller === this .active_abort_controller ) this . finishInstallOperation ()
12331279 }
12341280 },
12351281 setInstallingState(identifier : string , action : ' install' | ' update' ): void {
0 commit comments