Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions config/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,11 +422,13 @@ type CachedAttachment struct {

// CachedEmailBody stores the body and attachment metadata for a single email.
type CachedEmailBody struct {
UID uint32 `json:"uid"`
AccountID string `json:"account_id"`
Body string `json:"body"`
Attachments []CachedAttachment `json:"attachments,omitempty"`
CachedAt time.Time `json:"cached_at"`
UID uint32 `json:"uid"`
AccountID string `json:"account_id"`
Body string `json:"body"`
Attachments []CachedAttachment `json:"attachments,omitempty"`
CachedAt time.Time `json:"cached_at"`
LastAccessedAt time.Time `json:"last_accessed_at"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I see, this is never updated. Should be updated on GetCachedEmailBody.

SizeBytes int `json:"size_bytes"`
}

// EmailBodyCache stores cached email bodies for a folder.
Expand Down Expand Up @@ -503,14 +505,34 @@ func GetCachedEmailBody(folderName string, uid uint32, accountID string) *Cached
return nil
}

func calculateTotalCacheSize(cache *EmailBodyCache) int {
total := 0
for _, b := range cache.Bodies {
total += b.SizeBytes
}
return total
}

func evict(cache *EmailBodyCache, newSize int, threshold int) {
sort.Slice(cache.Bodies, func(i, j int) bool {
return cache.Bodies[i].LastAccessedAt.Before(cache.Bodies[j].LastAccessedAt)
})

for len(cache.Bodies) > 0 && calculateTotalCacheSize(cache)+newSize > threshold {
cache.Bodies = cache.Bodies[1:]
}
}

// SaveEmailBody saves or updates a cached email body for a folder.
func SaveEmailBody(folderName string, body CachedEmailBody) error {
func SaveEmailBody(folderName string, body CachedEmailBody, threshold int) error {
cache, err := LoadEmailBodyCache(folderName)
if err != nil {
cache = &EmailBodyCache{FolderName: folderName}
}

body.CachedAt = time.Now()
body.LastAccessedAt = time.Now()
body.SizeBytes = len(body.Body)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not count CachedAttachments, while it should. Will lead to a much larger cache, than set in config.


// Replace existing or append
found := false
Expand All @@ -522,6 +544,11 @@ func SaveEmailBody(folderName string, body CachedEmailBody) error {
}
}
if !found {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The least of my concerns, but also worth noting, that, if the email is changed (found=true still) the cache is not going to be evicted


if calculateTotalCacheSize(cache)+body.SizeBytes > threshold {
evict(cache, body.SizeBytes, threshold)
}

cache.Bodies = append(cache.Bodies, body)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If one new email is more than the threshold, evict will drain the cache and still append.

}

Expand Down
13 changes: 13 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ type Config struct {
MailingLists []MailingList `json:"mailing_lists,omitempty"`
DateFormat string `json:"date_format,omitempty"`
Language string `json:"language,omitempty"` // Language code (e.g., "en", "es", "de")
BodyCacheThresholdMB int `json:"body_cache_threshold_mb,omitempty"`
}

// GetBodyCacheThreshold returns the email body cache threshold in bytes.
// It defaults to 500MB if unset or zero.
func (c *Config) GetBodyCacheThreshold() int {
if c.BodyCacheThresholdMB <= 0 {
return 500 * 1024 * 1024
}
return c.BodyCacheThresholdMB * 1024 * 1024
}

// GetDateFormat returns the Go time reference layout translated from the
Expand Down Expand Up @@ -537,6 +547,7 @@ func LoadConfig() (*Config, error) {
MailingLists []MailingList `json:"mailing_lists,omitempty"`
DateFormat string `json:"date_format,omitempty"`
Language string `json:"language,omitempty"`
BodyCacheThresholdMB int `json:"body_cache_threshold_mb,omitempty"`
}

var raw diskConfig
Expand Down Expand Up @@ -572,6 +583,8 @@ func LoadConfig() (*Config, error) {
config.MailingLists = raw.MailingLists
config.DateFormat = raw.DateFormat
config.Language = raw.Language
config.BodyCacheThresholdMB = raw.BodyCacheThresholdMB

for _, rawAcc := range raw.Accounts {
acc := Account{
ID: rawAcc.ID,
Expand Down
5 changes: 4 additions & 1 deletion docs/docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ Configuration is stored in `~/.config/matcha/config.json`.
"theme": "Matcha",
"enable_split_pane": true,
"disable_images": true,
"hide_tips": true
"hide_tips": true,
"body_cache_threshold_mb": 500
}
```

`send_as_email` is optional. When set, Matcha uses it for the outgoing `From` header while continuing to authenticate with the account's login address.

`enable_split_pane` enables a side-by-side view where the email list and the selected email are shown on the same screen.

`body_cache_threshold_mb` sets the maximum size (in megabytes) for the local email body cache. When this limit is reached, older cached emails are evicted to make room for new ones. Defaults to `500` MB if not specified.

## Data Locations

Configuration and persistent data are stored in `~/.config/matcha/`:
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
AccountID: msg.AccountID,
Body: msg.Body,
Attachments: cachedAttachments,
})
}, m.config.GetBodyCacheThreshold())
if err != nil {
log.Printf("debug: error caching email body fails (disk full, permission denied) for UID: %d: %v", msg.UID, err)
}
Expand Down Expand Up @@ -1313,7 +1313,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
AccountID: msg.AccountID,
Body: msg.Body,
Attachments: cachedAttachments,
})
}, m.config.GetBodyCacheThreshold())

if err != nil {
log.Printf("debug: error caching email body fails (disk full, permission denied) for UID: %d: %v", msg.UID, err)
Expand Down