@@ -53,6 +53,56 @@ async function enableDisableReplaceTab() {
53
53
await browser . storage . local . set ( { replaceTabEnabled : ! ! checkbox . checked } ) ;
54
54
}
55
55
56
+ async function backupContainers ( ) {
57
+ const backupLink = document . getElementById ( "containers-save-link" ) ;
58
+ const backupResult = document . getElementById ( "containers-save-result" ) ;
59
+ try {
60
+ const content = JSON . stringify (
61
+ await browser . runtime . sendMessage ( {
62
+ method : "backupIdentitiesState"
63
+ } )
64
+ ) ;
65
+ backupLink . href = `data:application/json;base64,${ btoa ( content ) } ` ;
66
+ backupLink . download = `containers-backup-${ ( new Date ( ) ) . toISOString ( ) } .json` ;
67
+ backupLink . click ( ) ;
68
+ backupResult . textContent = "" ;
69
+ } catch ( err ) {
70
+ backupResult . textContent = browser . i18n . getMessage ( "backupFailure" , [ String ( err . message || err ) ] ) ;
71
+ backupResult . style . color = "red" ;
72
+ }
73
+ }
74
+
75
+ async function restoreContainers ( event ) {
76
+ const restoreInput = event . currentTarget ;
77
+ const restoreResult = document . getElementById ( "containers-restore-result" ) ;
78
+ event . preventDefault ( ) ;
79
+ if ( restoreInput . files . length ) {
80
+ const reader = new FileReader ( ) ;
81
+ reader . onloadend = async ( ) => {
82
+ try {
83
+ const identitiesState = JSON . parse ( reader . result ) ;
84
+ const { created : restoredCount , incomplete } = await browser . runtime . sendMessage ( {
85
+ method : "restoreIdentitiesState" ,
86
+ identities : identitiesState
87
+ } ) ;
88
+ if ( incomplete . length === 0 ) {
89
+ restoreResult . textContent = browser . i18n . getMessage ( "containersRestored" , [ String ( restoredCount ) ] ) ;
90
+ restoreResult . style . color = "green" ;
91
+ } else {
92
+ restoreResult . textContent = browser . i18n . getMessage ( "containersPartiallyRestored" , [ String ( restoredCount ) , String ( incomplete . join ( ", " ) ) ] ) ;
93
+ restoreResult . style . color = "orange" ;
94
+ }
95
+ } catch ( err ) {
96
+ console . error ( "Cannot restore containers list: %s" , err . message || err ) ;
97
+ restoreResult . textContent = browser . i18n . getMessage ( "containersRestorationFailed" ) ;
98
+ restoreResult . style . color = "red" ;
99
+ }
100
+ } ;
101
+ reader . readAsText ( restoreInput . files . item ( 0 ) ) ;
102
+ }
103
+ restoreInput . value = "" ;
104
+ }
105
+
56
106
async function setupOptions ( ) {
57
107
const { syncEnabled } = await browser . storage . local . get ( "syncEnabled" ) ;
58
108
const { replaceTabEnabled } = await browser . storage . local . get ( "replaceTabEnabled" ) ;
@@ -114,15 +164,20 @@ browser.permissions.onRemoved.addListener(resetPermissionsUi);
114
164
document . addEventListener ( "DOMContentLoaded" , setupOptions ) ;
115
165
document . querySelector ( "#syncCheck" ) . addEventListener ( "change" , enableDisableSync ) ;
116
166
document . querySelector ( "#replaceTabCheck" ) . addEventListener ( "change" , enableDisableReplaceTab ) ;
167
+ document . querySelector ( "#containersRestoreInput" ) . addEventListener ( "change" , restoreContainers ) ;
117
168
maybeShowPermissionsWarningIcon ( ) ;
118
169
for ( let i = 0 ; i < NUMBER_OF_KEYBOARD_SHORTCUTS ; i ++ ) {
119
170
document . querySelector ( "#open_container_" + i )
120
171
. addEventListener ( "change" , storeShortcutChoice ) ;
121
172
}
122
173
123
174
document . querySelectorAll ( "[data-btn-id]" ) . forEach ( btn => {
124
- btn . addEventListener ( "click" , ( ) => {
175
+ btn . addEventListener ( "click" , e => {
125
176
switch ( btn . dataset . btnId ) {
177
+ case "containers-save-button" :
178
+ e . preventDefault ( ) ;
179
+ backupContainers ( ) ;
180
+ break ;
126
181
case "reset-onboarding" :
127
182
resetOnboarding ( ) ;
128
183
break ;
0 commit comments