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,43 @@ 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+ await kraken .uninstallExtensionVersion (extension .identifier , version )
879+ .catch (() => { /* version may not be registered yet */ })
880+ notifier .pushInfo (' EXTENSION_UPDATE_CANCELLED' , ' Extension update was cancelled.' , true )
881+ return
882+ }
883+ this .showAlertError (error )
843884 notifier .pushBackError (' EXTENSION_UPDATE_FAIL' , error )
844885 })
845886 .finally (() => {
846- this .clearInstallingState ()
847- this .resetPullOutput ()
887+ if (controller === this .active_abort_controller ) this .finishInstallOperation ()
848888 })
849889 },
850890 metricsFor(extension : InstalledExtensionData ): { cpu: number , memory: number } | Record <string , never > {
@@ -993,24 +1033,30 @@ export default Vue.extend({
9931033 this .setInstallingState (extension .identifier , ' install' )
9941034 this .show_dialog = false
9951035 this .show_pull_output = true
996- const tracker = this .getTracker ()
1036+ const controller = this .beginInstallOperation ()
1037+ const tracker = this .getTracker (controller .signal )
9971038
9981039 kraken .installExtension (
9991040 extension ,
10001041 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
1042+ controller .signal ,
10011043 )
10021044 .then (() => {
1003- this .fetchInstalledExtensions ()
10041045 notifier .pushSuccess (' EXTENSION_INSTALL_SUCCESS' , ` ${extension .name } installed successfully. ` , true )
10051046 })
1006- .catch ((error ) => {
1007- this .alerter = true
1008- this .alerter_error = String (error )
1009- notifier .pushBackError (' EXTENSIONS_INSTALL_FAIL' , error )
1047+ .catch (async (error ) => {
1048+ if (axios .isCancel (error )) {
1049+ if (controller !== this .active_abort_controller ) return
1050+ await kraken .uninstallExtensionVersion (extension .identifier , extension .tag )
1051+ .catch (() => { /* version may not be registered yet */ })
1052+ notifier .pushInfo (' EXTENSION_INSTALL_CANCELLED' , ' Extension install was cancelled.' , true )
1053+ return
1054+ }
1055+ this .showAlertError (error )
1056+ notifier .pushBackError (' EXTENSION_INSTALL_FAIL' , error )
10101057 })
10111058 .finally (() => {
1012- this .clearInstallingState ()
1013- this .resetPullOutput ()
1059+ if (controller === this .active_abort_controller ) this .finishInstallOperation ()
10141060 })
10151061 },
10161062 async performActionFromModal(
@@ -1119,17 +1165,17 @@ export default Vue.extend({
11191165 temp [extension .identifier ].loading = loading
11201166 this .installed_extensions = temp
11211167 },
1122- getTracker(): PullTracker {
1168+ getTracker(signal : AbortSignal ): PullTracker {
11231169 return new PullTracker (
11241170 () => {
11251171 setTimeout (() => {
11261172 this .show_pull_output = false
11271173 }, 1000 )
11281174 },
11291175 (error ) => {
1130- this . alerter = true
1131- this .alerter_error = String (error )
1132- notifier .pushBackError (' EXTENSIONS_INSTALL_FAIL ' , error )
1176+ if ( signal . aborted ) return
1177+ this .showAlertError (error )
1178+ notifier .pushBackError (' EXTENSION_INSTALL_FAIL ' , error )
11331179 this .show_pull_output = false
11341180 },
11351181 )
@@ -1206,7 +1252,8 @@ export default Vue.extend({
12061252 }
12071253
12081254 this .show_pull_output = true
1209- const tracker = this .getTracker ()
1255+ const controller = this .beginInstallOperation ()
1256+ const tracker = this .getTracker (controller .signal )
12101257 this .setInstallFromFilePhase (' installing' )
12111258 this .install_from_file_install_progress = 0
12121259 this .install_from_file_status_text = ' Starting installation...'
@@ -1216,20 +1263,29 @@ export default Vue.extend({
12161263 extension ,
12171264 this .upload_temp_tag ,
12181265 (progressEvent ) => this .handleDownloadProgress (progressEvent .event , tracker ),
1266+ controller .signal ,
12191267 )
12201268 this .setInstallFromFilePhase (' success' )
12211269 this .install_from_file_status_text = ' Extension installed successfully'
12221270 this .stopUploadKeepAlive ()
12231271 this .upload_temp_tag = null
12241272 this .upload_metadata = null
1225- this .fetchInstalledExtensions ()
12261273 } catch (error ) {
1227- this .applyInstallFromFileError (String (error ))
1228- this .alerter = true
1229- this .alerter_error = String (error )
1230- notifier .pushBackError (' EXTENSION_FINALIZE_FAIL' , error )
1274+ if (axios .isCancel (error )) {
1275+ if (controller === this .active_abort_controller ) {
1276+ this .setInstallFromFilePhase (' ready' )
1277+ this .install_from_file_status_text = ' '
1278+ await kraken .uninstallExtensionVersion (extension .identifier , extension .tag )
1279+ .catch (() => { /* version may not be registered yet */ })
1280+ notifier .pushInfo (' EXTENSION_INSTALL_CANCELLED' , ' Installation from file was cancelled.' , true )
1281+ }
1282+ } else {
1283+ this .applyInstallFromFileError (String (error ))
1284+ this .showAlertError (error )
1285+ notifier .pushBackError (' EXTENSION_FINALIZE_FAIL' , error )
1286+ }
12311287 } finally {
1232- this .resetPullOutput ()
1288+ if ( controller === this .active_abort_controller ) this . finishInstallOperation ()
12331289 }
12341290 },
12351291 setInstallingState(identifier : string , action : ' install' | ' update' ): void {
0 commit comments