detached 3a58730

fix(cache): fetch favicon from DuckDuckGo when not cached on disk

commit
3a5873005f370911b1da55413c5c2107801f9fe7
author
bnema · · 2025-12-18 19:34 UTC
committer
bnema · · 2025-12-18 19:36 UTC
parent
c46672e
signature
unsigned
1 file · +29 -0
M internal/ui/cache/favicon_cache.go +29 -0
@@ -79,6 +79,7 @@ func NewFaviconCache(faviconDB *webkit.FaviconDatabase) *FaviconCache {
7979 }
8080
8181 // Set stores a favicon texture for the given domain.
82+// Also queues a disk write if the favicon is not already cached on disk.
8283 func (fc *FaviconCache) Set(domain string, texture *gdk.Texture) {
8384 if domain == "" || texture == nil {
8485 return
@@ -86,6 +87,34 @@ func (fc *FaviconCache) Set(domain string, texture *gdk.Texture) {
8687 fc.mu.Lock()
8788 fc.cache[domain] = texture
8889 fc.mu.Unlock()
90+
91+ // Also save to disk if not already cached
92+ fc.ensureDiskCache(domain)
93+}
94+
95+// ensureDiskCache fetches favicon from DuckDuckGo and saves to disk if not already cached.
96+func (fc *FaviconCache) ensureDiskCache(domain string) {
97+ if fc.diskCacheDir == "" || domain == "" {
98+ return
99+ }
100+
101+ // Check if already on disk
102+ filename := domainurl.SanitizeDomainForFilename(domain)
103+ path := filepath.Join(fc.diskCacheDir, filename)
104+ if _, err := os.Stat(path); err == nil {
105+ return // Already cached
106+ }
107+
108+ // Fetch from DuckDuckGo in background and save to disk
109+ go func() {
110+ ctx, cancel := context.WithTimeout(context.Background(), faviconFetchTimeout)
111+ defer cancel()
112+
113+ _, rawData := fc.fetchFromExternal(ctx, domain)
114+ if len(rawData) > 0 {
115+ fc.queueDiskWrite(domain, rawData)
116+ }
117+ }()
89118 }
90119
91120 // SetByURL stores a favicon texture for the domain extracted from the URL.
@@ -79,6 +79,7 @@ func NewFaviconCache(faviconDB *webkit.FaviconDatabase) *FaviconCache {
79 }79 }
80 80
81 // Set stores a favicon texture for the given domain.81 // Set stores a favicon texture for the given domain.
82+// Also queues a disk write if the favicon is not already cached on disk.
82 func (fc *FaviconCache) Set(domain string, texture *gdk.Texture) {83 func (fc *FaviconCache) Set(domain string, texture *gdk.Texture) {
83 if domain == "" || texture == nil {84 if domain == "" || texture == nil {
84 return85 return
@@ -86,6 +87,34 @@ func (fc *FaviconCache) Set(domain string, texture *gdk.Texture) {
86 fc.mu.Lock()87 fc.mu.Lock()
87 fc.cache[domain] = texture88 fc.cache[domain] = texture
88 fc.mu.Unlock()89 fc.mu.Unlock()
90+
91+ // Also save to disk if not already cached
92+ fc.ensureDiskCache(domain)
93+}
94+
95+// ensureDiskCache fetches favicon from DuckDuckGo and saves to disk if not already cached.
96+func (fc *FaviconCache) ensureDiskCache(domain string) {
97+ if fc.diskCacheDir == "" || domain == "" {
98+ return
99+ }
100+
101+ // Check if already on disk
102+ filename := domainurl.SanitizeDomainForFilename(domain)
103+ path := filepath.Join(fc.diskCacheDir, filename)
104+ if _, err := os.Stat(path); err == nil {
105+ return // Already cached
106+ }
107+
108+ // Fetch from DuckDuckGo in background and save to disk
109+ go func() {
110+ ctx, cancel := context.WithTimeout(context.Background(), faviconFetchTimeout)
111+ defer cancel()
112+
113+ _, rawData := fc.fetchFromExternal(ctx, domain)
114+ if len(rawData) > 0 {
115+ fc.queueDiskWrite(domain, rawData)
116+ }
117+ }()
89 }118 }
90 119
91 // SetByURL stores a favicon texture for the domain extracted from the URL.120 // SetByURL stores a favicon texture for the domain extracted from the URL.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close