browser_window_favorites_sidebar.go RawHistoryFolder
1package ui
2
3import (
4 "context"
5 "fmt"
6 "strings"
7
8 "github.com/bnema/dumber/internal/application/port"
9 "github.com/bnema/dumber/internal/application/usecase"
10 "github.com/bnema/dumber/internal/logging"
11 "github.com/bnema/dumber/internal/ui/component"
12)
13
14// initFavoritesSidebar creates the native favorites sidebar when favorites are configured.
15func (bw *browserWindow) initFavoritesSidebar(ctx context.Context, a *App) {
16 if bw == nil || a == nil || bw.mainWindow == nil || a.deps == nil || a.deps.FavoritesUC == nil {
17 return
18 }
19 log := logging.FromContext(ctx)
20
21 cfg := a.buildFavoritesSidebarConfig(bw)
22 sidebar := component.NewFavoritesSidebar(ctx, cfg)
23 if sidebar == nil {
24 log.Warn().Msg("failed to create favorites sidebar")
25 return
26 }
27
28 bw.favoritesSidebar = sidebar
29 bw.favoritesSidebar.Hide()
30 bw.applySidebarWidthConfig(a)
31 log.Debug().Msg("favorites sidebar initialized")
32}
33
34//nolint:dupl // Favorites and history sidebar configs intentionally mirror each other for separate component types.
35func (a *App) buildFavoritesSidebarConfig(bw *browserWindow) component.FavoritesSidebarConfig {
36 var favoritesUC port.FavoritesSidebarFavorites
37 if a.deps != nil {
38 favoritesUC = a.deps.FavoritesUC
39 }
40 return component.FavoritesSidebarConfig{
41 FavoritesUC: favoritesUC,
42 OnNavigate: func(navCtx context.Context, url string) error {
43 return a.navigateFavoritesSidebarSelection(navCtx, bw, url)
44 },
45 OnNavigateKeepOpen: func(navCtx context.Context, url string) error {
46 return a.navigateFavoritesSidebarSelection(navCtx, bw, url)
47 },
48 OnOpenInNewPane: func(splitCtx context.Context, url string) error {
49 if a.wsCoord == nil || !a.hasBrowserWindow(bw) {
50 return nil
51 }
52 a.activateBrowserWindow(bw)
53 return a.wsCoord.SplitWithURL(splitCtx, usecase.SplitRight, url)
54 },
55 OnClose: func() {
56 a.hideAndRestoreFocusForBrowserWindow(bw)
57 },
58 }
59}
60
61func (a *App) navigateFavoritesSidebarSelection(ctx context.Context, bw *browserWindow, url string) error {
62 if a == nil || bw == nil || !a.hasBrowserWindow(bw) {
63 return nil
64 }
65 return a.navigateFromBrowserWindow(ctx, bw, url)
66}
67
68func (bw *browserWindow) toggleFavoritesSidebar() {
69 if bw == nil || bw.favoritesSidebar == nil {
70 return
71 }
72 if bw.sidebarVisible && bw.activeSidebarKind == nativeSidebarFavorites {
73 bw.hideFavoritesSidebar()
74 return
75 }
76 bw.showFavoritesSidebar()
77}
78
79func (bw *browserWindow) showFavoritesSidebar() {
80 if bw == nil || bw.favoritesSidebar == nil || bw.mainWindow == nil {
81 return
82 }
83 bw.mountNativeSidebar(nativeSidebarFavorites)
84 bw.favoritesSidebar.Show()
85 bw.mainWindow.SetSidebarVisible(true)
86 bw.sidebarVisible = true
87 bw.activeSidebarKind = nativeSidebarFavorites
88}
89
90func (bw *browserWindow) hideFavoritesSidebar() {
91 if bw == nil || bw.favoritesSidebar == nil || bw.mainWindow == nil {
92 return
93 }
94 bw.favoritesSidebar.Hide()
95 bw.mainWindow.SetSidebarVisible(false)
96 bw.sidebarVisible = false
97 if bw.activeSidebarKind == nativeSidebarFavorites {
98 bw.activeSidebarKind = nativeSidebarNone
99 }
100}
101
102func (a *App) toggleFavoritesSidebarAction(ctx context.Context) error {
103 bw := a.lastFocusedBrowserWindow()
104 if bw == nil {
105 return fmt.Errorf("favorites sidebar unavailable: no focused browser window")
106 }
107 if bw.favoritesSidebar == nil {
108 return fmt.Errorf("favorites sidebar unavailable: native sidebar not initialized")
109 }
110 if bw.sidebarVisible && bw.activeSidebarKind == nativeSidebarFavorites {
111 a.hideAndRestoreFocusForBrowserWindow(bw)
112 return nil
113 }
114 bw.toggleFavoritesSidebar()
115 return nil
116}
117
118func (a *App) toggleCurrentPageFavoriteAction(ctx context.Context) error {
119 if a == nil || a.deps == nil || a.deps.FavoritesUC == nil {
120 return fmt.Errorf("favorites unavailable: usecase not configured")
121 }
122 bw := a.lastFocusedBrowserWindow()
123 _, wv := a.activeWebViewForBrowserWindow(bw)
124 if wv == nil {
125 return fmt.Errorf("favorites unavailable: no active webview")
126 }
127 uri := strings.TrimSpace(wv.URI())
128 if uri == "" {
129 return fmt.Errorf("favorites unavailable: active page has no URI")
130 }
131 result, err := a.deps.FavoritesUC.Toggle(ctx, uri, wv.Title())
132 if err != nil {
133 return err
134 }
135 if result != nil && strings.TrimSpace(result.Message) != "" {
136 a.showToastOnBrowserWindow(ctx, bw, result.Message, component.ToastSuccess,
137 component.WithDuration(component.ToastBriefDurationMs),
138 component.WithPosition(component.ToastPositionBottomRight),
139 )
140 }
141 a.reloadVisibleFavoritesSidebars("current-page-favorite-toggle")
142 return nil
143}
144
145func (a *App) reloadVisibleFavoritesSidebars(reason string) {
146 if a == nil {
147 return
148 }
149 for _, bw := range a.browserWindows {
150 if bw == nil || bw.favoritesSidebar == nil {
151 continue
152 }
153 bw.favoritesSidebar.RequestReloadIfVisible(reason)
154 }
155}