@@ -111,6 +111,91 @@ function initializeAPI() {
111111 return false ;
112112}
113113
114+ function openResetP2PModal ( ) {
115+ return new Promise ( ( resolve ) => {
116+ const $ = ( h ) => {
117+ const t = document . createElement ( 'template' ) ;
118+ t . innerHTML = h . trim ( ) ;
119+ return t . content . firstChild ;
120+ } ;
121+
122+ const backdrop = $ ( '<div class="modal-backdrop"></div>' ) ;
123+ const modal = $ ( `
124+ <div class="modal" role="dialog" aria-modal="true" aria-labelledby="reset-title">
125+ <div class="modal-header">
126+ <h3 id="reset-title" class="modal-title">Reset P2P Data</h3>
127+ </div>
128+
129+ <div class="modal-body">
130+ <div class="copy">
131+ This will clear all IPFS/Hyper data while <span class="emphasis">preserving</span> your peer identities by default.
132+ </div>
133+
134+ <label class="checkbox-row">
135+ <input type="checkbox" id="modal-reset-identities">
136+ <span>
137+ Also reset identities (<code>libp2p-key</code> & <code>swarm-keypair.json</code>) — not recommended
138+ </span>
139+ </label>
140+ </div>
141+
142+ <div class="modal-actions">
143+ <button class="btn-ghost" id="modal-cancel">Cancel <span class="kbd">Esc</span></button>
144+ <button class="btn-warning-solid" id="modal-confirm">Reset</button>
145+ </div>
146+ </div>
147+ ` ) ;
148+
149+ document . body . append ( backdrop , modal ) ;
150+
151+ const confirmBtn = modal . querySelector ( '#modal-confirm' ) ;
152+ const cancelBtn = modal . querySelector ( '#modal-cancel' ) ;
153+ const idsCb = modal . querySelector ( '#modal-reset-identities' ) ;
154+
155+ // Toggle confirm button style (warning ↔ danger) based on checkbox
156+ const updateConfirmStyle = ( ) => {
157+ confirmBtn . classList . toggle ( 'btn-danger-solid' , idsCb . checked ) ;
158+ confirmBtn . classList . toggle ( 'btn-warning-solid' , ! idsCb . checked ) ;
159+ } ;
160+ idsCb . addEventListener ( 'change' , updateConfirmStyle ) ;
161+ updateConfirmStyle ( ) ;
162+
163+ const cleanup = ( ) => {
164+ modal . remove ( ) ;
165+ backdrop . remove ( ) ;
166+ document . removeEventListener ( 'keydown' , onKey ) ;
167+ } ;
168+
169+ const onKey = ( e ) => {
170+ if ( e . key === 'Escape' ) { cleanup ( ) ; resolve ( { confirmed : false } ) ; }
171+ if ( e . key === 'Enter' ) { doConfirm ( ) ; }
172+ if ( e . key === 'Tab' ) {
173+ // simple focus trap
174+ const order = [ idsCb , cancelBtn , confirmBtn ] ;
175+ const idx = order . indexOf ( document . activeElement ) ;
176+ const next = ( idx + ( e . shiftKey ? - 1 : 1 ) + order . length ) % order . length ;
177+ order [ next ] . focus ( ) ;
178+ e . preventDefault ( ) ;
179+ }
180+ } ;
181+ document . addEventListener ( 'keydown' , onKey ) ;
182+
183+ const doConfirm = ( ) => {
184+ confirmBtn . disabled = true ;
185+ const resetIdentities = ! ! idsCb . checked ;
186+ cleanup ( ) ;
187+ resolve ( { confirmed : true , resetIdentities } ) ;
188+ } ;
189+
190+ cancelBtn . addEventListener ( 'click' , ( ) => { cleanup ( ) ; resolve ( { confirmed : false } ) ; } ) ;
191+ confirmBtn . addEventListener ( 'click' , doConfirm ) ;
192+
193+ // initial focus
194+ setTimeout ( ( ) => idsCb . focus ( ) , 10 ) ;
195+ } ) ;
196+ }
197+
198+
114199// Create fallback API wrapper
115200function createFallbackAPI ( ipc ) {
116201 const wrapCallback = ( eventName , callback ) => {
@@ -125,7 +210,8 @@ function createFallbackAPI(ipc) {
125210 get : ( key ) => ipc . invoke ( 'settings-get' , key ) ,
126211 set : ( key , value ) => ipc . invoke ( 'settings-set' , key , value ) ,
127212 reset : ( ) => ipc . invoke ( 'settings-reset' ) ,
128- clearCache : ( ) => ipc . invoke ( 'settings-clear-cache' ) ,
213+ clearBrowserCache : ( ) => ipc . invoke ( 'settings-clear-cache' ) ,
214+ resetP2P : ( opts = { } ) => ipc . invoke ( 'settings-reset-p2p' , opts ) ,
129215 uploadWallpaper : ( filePath ) => ipc . invoke ( 'settings-upload-wallpaper' , filePath )
130216 } ,
131217 onThemeChanged : ( callback ) => wrapCallback ( 'theme-changed' , callback ) ,
@@ -233,8 +319,8 @@ document.addEventListener('DOMContentLoaded', async () => {
233319 const wallpaperBrowse = document . getElementById ( 'wallpaper-browse' ) ;
234320 const wallpaperRemove = document . getElementById ( 'wallpaper-remove' ) ;
235321 const wallpaperPreview = document . getElementById ( 'wallpaper-preview' ) ;
236- const clearCache = document . getElementById ( 'clear-cache' ) ;
237-
322+ const clearBrowserCacheBtn = document . getElementById ( 'clear-browser -cache' ) ;
323+ const resetP2PBtn = document . getElementById ( 'reset-p2p' ) ;
238324 // Handle built-in wallpaper selector change
239325 wallpaperSelector ?. addEventListener ( 'change' , async ( e ) => {
240326 const selectedValue = e . target . value ;
@@ -315,23 +401,35 @@ document.addEventListener('DOMContentLoaded', async () => {
315401 } ) ;
316402
317403 // Handle clear cache button
318- clearCache ?. addEventListener ( 'click' , async ( ) => {
319- if ( confirm ( 'Are you sure you want to clear the browser cache? This action cannot be undone.' ) ) {
320- try {
321- console . log ( 'Clear cache requested' ) ;
322- const result = await settingsAPI . settings . clearCache ( ) ;
323-
324- if ( result . success ) {
325- alert ( 'Cache cleared successfully!' ) ;
326- console . log ( 'Cache clearing completed:' , result . message ) ;
327- } else {
328- alert ( 'Cache clearing failed. Please try again.' ) ;
329- console . error ( 'Cache clearing failed:' , result ) ;
330- }
331- } catch ( error ) {
332- console . error ( 'Failed to clear cache:' , error ) ;
333- alert ( `Failed to clear cache: ${ error . message } ` ) ;
334- }
404+ clearBrowserCacheBtn ?. addEventListener ( 'click' , async ( ) => {
405+ if ( ! settingsAPI ?. settings ?. clearBrowserCache ) return ;
406+ if ( ! confirm ( 'Clear browser cache? This removes website cache, cookies, and temporary storage.' ) ) return ;
407+
408+ try {
409+ clearBrowserCacheBtn . disabled = true ;
410+ const res = await settingsAPI . settings . clearBrowserCache ( ) ;
411+ showSettingsSavedMessage ( res ?. message || 'Browser cache cleared' , 'success' ) ;
412+ } catch ( err ) {
413+ console . error ( err ) ;
414+ showSettingsSavedMessage ( `Failed to clear browser cache: ${ err . message } ` , 'error' ) ;
415+ } finally {
416+ clearBrowserCacheBtn . disabled = false ;
417+ }
418+ } ) ;
419+
420+ resetP2PBtn ?. addEventListener ( 'click' , async ( ) => {
421+ const choice = await openResetP2PModal ( ) ; // { confirmed, resetIdentities }
422+ if ( ! choice ?. confirmed ) return ;
423+
424+ try {
425+ resetP2PBtn . disabled = true ;
426+ const res = await settingsAPI . settings . resetP2P ( { resetIdentities : ! ! choice . resetIdentities } ) ;
427+ showSettingsSavedMessage ( res ?. message || 'P2P data reset' , 'success' ) ;
428+ } catch ( err ) {
429+ console . error ( err ) ;
430+ showSettingsSavedMessage ( `Failed to reset P2P data: ${ err . message } ` , 'error' ) ;
431+ } finally {
432+ resetP2PBtn . disabled = false ;
335433 }
336434 } ) ;
337435
@@ -933,5 +1031,24 @@ function cleanup() {
9331031 eventCleanupFunctions = [ ] ;
9341032}
9351033
1034+ // Gracefully detach webviews before clearing
1035+ window . detachWebviews = ( ) => {
1036+ const webviews = document . querySelectorAll ( 'webview' ) ;
1037+ webviews . forEach ( wv => {
1038+ try {
1039+ wv . remove ( ) ;
1040+ } catch ( err ) {
1041+ console . warn ( 'Failed to detach webview:' , err ) ;
1042+ }
1043+ } ) ;
1044+ console . log ( 'All WebViews detached for safe cache clearing' ) ;
1045+ } ;
1046+
1047+ // Reinitialize after clear
1048+ window . electronAPI . on ( 'reload-ui-after-cache' , ( ) => {
1049+ console . log ( 'Reloading UI after cache clear...' ) ;
1050+ window . location . reload ( ) ;
1051+ } ) ;
1052+
9361053// Cleanup on page unload
9371054window . addEventListener ( 'beforeunload' , cleanup ) ;
0 commit comments