1// Package usecase contains application use cases that orchestrate domain logic.
2package usecase
3
4import (
5 "context"
6 "fmt"
7 "net/url"
8
9 "github.com/bnema/dumber/internal/application/port"
10 "github.com/bnema/dumber/internal/domain/entity"
11 "github.com/bnema/dumber/internal/domain/repository"
12 "github.com/bnema/dumber/internal/logging"
13)
14
15// ManageZoomUseCase handles per-domain zoom level operations.
16type ManageZoomUseCase struct {
17 zoomRepo repository.ZoomRepository
18 defaultZoom float64
19}
20
21// NewManageZoomUseCase creates a new zoom management use case.
22// defaultZoom is the zoom level to use when resetting (typically from config).
23func NewManageZoomUseCase(zoomRepo repository.ZoomRepository, defaultZoom float64) *ManageZoomUseCase {
24 if defaultZoom <= 0 {
25 defaultZoom = entity.ZoomDefault
26 }
27 return &ManageZoomUseCase{
28 zoomRepo: zoomRepo,
29 defaultZoom: defaultZoom,
30 }
31}
32
33// DefaultZoom returns the configured default zoom level.
34func (uc *ManageZoomUseCase) DefaultZoom() float64 {
35 return uc.defaultZoom
36}
37
38// GetZoom retrieves the zoom level for a domain.
39// Returns the configured default zoom level if none is set.
40func (uc *ManageZoomUseCase) GetZoom(ctx context.Context, domain string) (*entity.ZoomLevel, error) {
41 log := logging.FromContext(ctx)
42 log.Debug().Str("domain", domain).Msg("getting zoom level")
43
44 zoom, err := uc.zoomRepo.Get(ctx, domain)
45 if err != nil {
46 return nil, fmt.Errorf("failed to get zoom level: %w", err)
47 }
48
49 if zoom == nil {
50 zoom = entity.NewZoomLevel(domain, uc.defaultZoom)
51 log.Debug().Str("domain", domain).Float64("zoom", zoom.ZoomFactor).Msg("using default zoom")
52 }
53
54 return zoom, nil
55}
56
57// SetZoom saves a zoom level for a domain.
58func (uc *ManageZoomUseCase) SetZoom(ctx context.Context, domain string, factor float64) error {
59 log := logging.FromContext(ctx)
60 log.Debug().Str("domain", domain).Float64("factor", factor).Msg("setting zoom level")
61
62 zoom := entity.NewZoomLevel(domain, factor)
63 if err := uc.zoomRepo.Set(ctx, zoom); err != nil {
64 return fmt.Errorf("failed to set zoom level: %w", err)
65 }
66
67 log.Info().Str("domain", domain).Float64("factor", zoom.ZoomFactor).Msg("zoom level saved")
68 return nil
69}
70
71// ResetZoom removes the custom zoom level for a domain.
72func (uc *ManageZoomUseCase) ResetZoom(ctx context.Context, domain string) error {
73 log := logging.FromContext(ctx)
74 log.Debug().Str("domain", domain).Msg("resetting zoom level")
75
76 if err := uc.zoomRepo.Delete(ctx, domain); err != nil {
77 return fmt.Errorf("failed to reset zoom level: %w", err)
78 }
79
80 log.Info().Str("domain", domain).Msg("zoom level reset to default")
81 return nil
82}
83
84// ZoomIn increases the zoom level by one step (0.1).
85func (uc *ManageZoomUseCase) ZoomIn(ctx context.Context, domain string, current float64) (*entity.ZoomLevel, error) {
86 log := logging.FromContext(ctx)
87
88 zoom := entity.NewZoomLevel(domain, current)
89 zoom.ZoomIn()
90
91 log.Debug().
92 Str("domain", domain).
93 Float64("from", current).
94 Float64("to", zoom.ZoomFactor).
95 Msg("zooming in")
96
97 if err := uc.zoomRepo.Set(ctx, zoom); err != nil {
98 return nil, fmt.Errorf("failed to save zoom level: %w", err)
99 }
100
101 return zoom, nil
102}
103
104// ZoomOut decreases the zoom level by one step (0.1).
105func (uc *ManageZoomUseCase) ZoomOut(ctx context.Context, domain string, current float64) (*entity.ZoomLevel, error) {
106 log := logging.FromContext(ctx)
107
108 zoom := entity.NewZoomLevel(domain, current)
109 zoom.ZoomOut()
110
111 log.Debug().
112 Str("domain", domain).
113 Float64("from", current).
114 Float64("to", zoom.ZoomFactor).
115 Msg("zooming out")
116
117 if err := uc.zoomRepo.Set(ctx, zoom); err != nil {
118 return nil, fmt.Errorf("failed to save zoom level: %w", err)
119 }
120
121 return zoom, nil
122}
123
124// ApplyToWebView loads the saved zoom level and applies it to a webview.
125func (uc *ManageZoomUseCase) ApplyToWebView(ctx context.Context, webview port.WebView, domain string) error {
126 log := logging.FromContext(ctx)
127
128 zoom, err := uc.GetZoom(ctx, domain)
129 if err != nil {
130 return err
131 }
132
133 log.Debug().
134 Str("domain", domain).
135 Float64("factor", zoom.ZoomFactor).
136 Msg("applying zoom to webview")
137
138 if err := webview.SetZoomLevel(ctx, zoom.ZoomFactor); err != nil {
139 return fmt.Errorf("failed to set zoom level: %w", err)
140 }
141 return nil
142}
143
144// GetAll retrieves all saved zoom levels.
145func (uc *ManageZoomUseCase) GetAll(ctx context.Context) ([]*entity.ZoomLevel, error) {
146 log := logging.FromContext(ctx)
147 log.Debug().Msg("getting all zoom levels")
148
149 levels, err := uc.zoomRepo.GetAll(ctx)
150 if err != nil {
151 return nil, fmt.Errorf("failed to get zoom levels: %w", err)
152 }
153
154 log.Debug().Int("count", len(levels)).Msg("retrieved zoom levels")
155 return levels, nil
156}
157
158// ExtractDomain extracts the host from a URL string.
159func ExtractDomain(rawURL string) (string, error) {
160 u, err := url.Parse(rawURL)
161 if err != nil {
162 return "", fmt.Errorf("invalid URL: %w", err)
163 }
164 if u.Host == "" {
165 return "", fmt.Errorf("URL has no host: %s", rawURL)
166 }
167 return u.Host, nil
168}