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' ) {
@@ -824,27 +848,44 @@ export default Vue.extend({
824848 this .file_uploading = false
825849 }
826850 },
851+ swapExtensionVersion(identifier : string , enableTag : string , removeTag : string ): Promise <void > {
852+ return kraken .enableExtension (identifier , enableTag )
853+ .then (() => kraken .uninstallExtensionVersion (identifier , removeTag ))
854+ .catch ((error ) => notifier .pushBackError (' EXTENSION_VERSION_SWAP_FAIL' , error ))
855+ },
827856 async update(extension : InstalledExtensionData , version : string ) {
857+ const installedExt = this .installed_extensions [extension .identifier ]
858+ const previousTag = installedExt ?.tag ?? extension .tag
828859 this .setInstallingState (extension .identifier , ' update' )
829860 this .show_pull_output = true
830- const tracker = this .getTracker ()
861+ const controller = this .beginInstallOperation ()
862+ const tracker = this .getTracker (controller .signal )
831863 kraken .updateExtensionToVersion (
832864 extension .identifier ,
833865 version ,
834866 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
867+ controller .signal ,
835868 )
836- .then (() => {
837- this .fetchInstalledExtensions ()
869+ .then (async () => {
838870 notifier .pushSuccess (' EXTENSION_UPDATE_SUCCESS' , ` ${extension .name } updated successfully. ` , true )
871+ if (previousTag !== version ) {
872+ await this .swapExtensionVersion (extension .identifier , version , previousTag )
873+ }
839874 })
840- .catch ((error ) => {
841- this .alerter = true
842- this .alerter_error = String (error )
875+ .catch (async (error ) => {
876+ if (axios .isCancel (error )) {
877+ if (controller !== this .active_abort_controller ) return
878+ if (previousTag !== version ) {
879+ await this .swapExtensionVersion (extension .identifier , previousTag , version )
880+ }
881+ notifier .pushInfo (' EXTENSION_UPDATE_CANCELLED' , ' Extension update was cancelled.' , true )
882+ return
883+ }
884+ this .showAlertError (error )
843885 notifier .pushBackError (' EXTENSION_UPDATE_FAIL' , error )
844886 })
845887 .finally (() => {
846- this .clearInstallingState ()
847- this .resetPullOutput ()
888+ if (controller === this .active_abort_controller ) this .finishInstallOperation ()
848889 })
849890 },
850891 metricsFor(extension : InstalledExtensionData ): { cpu: number , memory: number } | Record <string , never > {
@@ -993,24 +1034,30 @@ export default Vue.extend({
9931034 this .setInstallingState (extension .identifier , ' install' )
9941035 this .show_dialog = false
9951036 this .show_pull_output = true
996- const tracker = this .getTracker ()
1037+ const controller = this .beginInstallOperation ()
1038+ const tracker = this .getTracker (controller .signal )
9971039
9981040 kraken .installExtension (
9991041 extension ,
10001042 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
1043+ controller .signal ,
10011044 )
10021045 .then (() => {
1003- this .fetchInstalledExtensions ()
10041046 notifier .pushSuccess (' EXTENSION_INSTALL_SUCCESS' , ` ${extension .name } installed successfully. ` , true )
10051047 })
1006- .catch ((error ) => {
1007- this .alerter = true
1008- this .alerter_error = String (error )
1009- notifier .pushBackError (' EXTENSIONS_INSTALL_FAIL' , error )
1048+ .catch (async (error ) => {
1049+ if (axios .isCancel (error )) {
1050+ if (controller !== this .active_abort_controller ) return
1051+ await kraken .uninstallExtension (extension .identifier )
1052+ .catch ((err ) => notifier .pushBackError (' EXTENSION_UNINSTALL_FAIL' , err ))
1053+ notifier .pushInfo (' EXTENSION_INSTALL_CANCELLED' , ' Extension install was cancelled.' , true )
1054+ return
1055+ }
1056+ this .showAlertError (error )
1057+ notifier .pushBackError (' EXTENSION_INSTALL_FAIL' , error )
10101058 })
10111059 .finally (() => {
1012- this .clearInstallingState ()
1013- this .resetPullOutput ()
1060+ if (controller === this .active_abort_controller ) this .finishInstallOperation ()
10141061 })
10151062 },
10161063 async performActionFromModal(
@@ -1119,17 +1166,17 @@ export default Vue.extend({
11191166 temp [extension .identifier ].loading = loading
11201167 this .installed_extensions = temp
11211168 },
1122- getTracker(): PullTracker {
1169+ getTracker(signal : AbortSignal ): PullTracker {
11231170 return new PullTracker (
11241171 () => {
11251172 setTimeout (() => {
11261173 this .show_pull_output = false
11271174 }, 1000 )
11281175 },
11291176 (error ) => {
1130- this . alerter = true
1131- this .alerter_error = String (error )
1132- notifier .pushBackError (' EXTENSIONS_INSTALL_FAIL ' , error )
1177+ if ( signal . aborted ) return
1178+ this .showAlertError (error )
1179+ notifier .pushBackError (' EXTENSION_INSTALL_FAIL ' , error )
11331180 this .show_pull_output = false
11341181 },
11351182 )
@@ -1206,7 +1253,8 @@ export default Vue.extend({
12061253 }
12071254
12081255 this .show_pull_output = true
1209- const tracker = this .getTracker ()
1256+ const controller = this .beginInstallOperation ()
1257+ const tracker = this .getTracker (controller .signal )
12101258 this .setInstallFromFilePhase (' installing' )
12111259 this .install_from_file_install_progress = 0
12121260 this .install_from_file_status_text = ' Starting installation...'
@@ -1216,20 +1264,29 @@ export default Vue.extend({
12161264 extension ,
12171265 this .upload_temp_tag ,
12181266 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
1267+ controller .signal ,
12191268 )
12201269 this .setInstallFromFilePhase (' success' )
12211270 this .install_from_file_status_text = ' Extension installed successfully'
12221271 this .stopUploadKeepAlive ()
12231272 this .upload_temp_tag = null
12241273 this .upload_metadata = null
1225- this .fetchInstalledExtensions ()
12261274 } catch (error ) {
1227- this .applyInstallFromFileError (String (error ))
1228- this .alerter = true
1229- this .alerter_error = String (error )
1230- notifier .pushBackError (' EXTENSION_FINALIZE_FAIL' , error )
1275+ if (axios .isCancel (error )) {
1276+ if (controller === this .active_abort_controller ) {
1277+ this .setInstallFromFilePhase (' ready' )
1278+ this .install_from_file_status_text = ' '
1279+ await kraken .uninstallExtension (extension .identifier )
1280+ .catch ((err ) => notifier .pushBackError (' EXTENSION_UNINSTALL_FAIL' , err ))
1281+ notifier .pushInfo (' EXTENSION_INSTALL_CANCELLED' , ' Installation from file was cancelled.' , true )
1282+ }
1283+ } else {
1284+ this .applyInstallFromFileError (String (error ))
1285+ this .showAlertError (error )
1286+ notifier .pushBackError (' EXTENSION_FINALIZE_FAIL' , error )
1287+ }
12311288 } finally {
1232- this .resetPullOutput ()
1289+ if ( controller === this .active_abort_controller ) this . finishInstallOperation ()
12331290 }
12341291 },
12351292 setInstallingState(identifier : string , action : ' install' | ' update' ): void {
0 commit comments