summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/email/folders.go132
-rw-r--r--utils/format/date.go99
-rw-r--r--utils/format/html.go73
3 files changed, 304 insertions, 0 deletions
diff --git a/utils/email/folders.go b/utils/email/folders.go
index 3acffae..1ae2bf4 100644
--- a/utils/email/folders.go
+++ b/utils/email/folders.go
@@ -1,11 +1,32 @@
package email
import (
+ "lain/models"
"lain/types"
+ "net/url"
+ "strings"
"github.com/emersion/go-imap"
+ "github.com/gofiber/fiber/v2"
)
+func GetDisplayName(imapName string) string {
+ if strings.Contains(imapName, "/") {
+ parts := strings.Split(imapName, "/")
+ lastPart := parts[len(parts)-1]
+ if strings.ToLower(lastPart) == "inbox" {
+ return "Inbox"
+ }
+ return lastPart
+ }
+
+ if strings.ToLower(imapName) == "inbox" {
+ return "Inbox"
+ }
+
+ return imapName
+}
+
func FetchFolders(client *types.EmailClient) ([]types.IMAPFolder, error) {
mailboxes := make(chan *imap.MailboxInfo, 10)
done := make(chan error, 1)
@@ -28,3 +49,114 @@ func FetchFolders(client *types.EmailClient) ([]types.IMAPFolder, error) {
return folders, nil
}
+
+func GetFolderType(folderName string, iconMap map[string]types.FolderIconVariant) string {
+ nameLower := strings.ToLower(folderName)
+
+ if strings.Contains(folderName, "/") {
+ parts := strings.Split(folderName, "/")
+ nameLower = strings.ToLower(parts[len(parts)-1])
+ }
+
+ for iconType := range iconMap {
+ if iconType == "default" {
+ continue
+ }
+ if strings.Contains(nameLower, iconType) {
+ return iconType
+ }
+ }
+
+ return "default"
+}
+
+func SortFolders(folders []models.Folder) {
+ for i := 0; i < len(folders)-1; i++ {
+ for j := 0; j < len(folders)-i-1; j++ {
+ if folders[j].SortOrder > folders[j+1].SortOrder {
+ folders[j], folders[j+1] = folders[j+1], folders[j]
+ } else if folders[j].SortOrder == folders[j+1].SortOrder {
+ if strings.ToLower(folders[j].IMAPName) > strings.ToLower(folders[j+1].IMAPName) {
+ folders[j], folders[j+1] = folders[j+1], folders[j]
+ }
+ }
+ }
+ }
+}
+
+func GetSortOrder(folderName string, index int) int {
+ nameLower := strings.ToLower(folderName)
+
+ if nameLower == "inbox" {
+ return 0
+ }
+ if strings.Contains(nameLower, "draft") {
+ return 1
+ }
+ if strings.Contains(nameLower, "sent") {
+ return 2
+ }
+ if strings.Contains(nameLower, "archive") {
+ return 3
+ }
+ if strings.Contains(nameLower, "trash") || strings.Contains(nameLower, "deleted") {
+ return 4
+ }
+ if strings.Contains(nameLower, "spam") || strings.Contains(nameLower, "junk") {
+ return 5
+ }
+
+ if strings.Contains(folderName, "/") {
+ parts := strings.Split(folderName, "/")
+ baseOrder := GetSortOrder(parts[0], index)
+ return baseOrder + 1000 + (index * 10)
+ }
+
+ return 100 + index
+}
+
+func CopyFolderMap(folder fiber.Map) fiber.Map {
+ copy := fiber.Map{}
+ for k, v := range folder {
+ if k == "Subfolders" {
+ if subfolders, ok := v.([]fiber.Map); ok {
+ subfoldersCopy := make([]fiber.Map, len(subfolders))
+ for i, sf := range subfolders {
+ subfoldersCopy[i] = CopyFolderMap(sf)
+ }
+ copy[k] = subfoldersCopy
+ }
+ } else {
+ copy[k] = v
+ }
+ }
+ return copy
+}
+
+func IsVirtualFolder(folderName string) bool {
+ return strings.HasPrefix(folderName, "Virtual") || strings.Contains(folderName, "/Virtual")
+}
+
+func UpdateActiveFolder(folders []fiber.Map, activeFolder string) []fiber.Map {
+ decodedActive, _ := url.QueryUnescape(activeFolder)
+ activeLower := strings.ToLower(decodedActive)
+
+ foldersCopy := make([]fiber.Map, len(folders))
+ for i, f := range folders {
+ foldersCopy[i] = CopyFolderMap(f)
+ }
+
+ var updateActive func([]fiber.Map)
+ updateActive = func(folderList []fiber.Map) {
+ for i := range folderList {
+ imapNameLower := strings.ToLower(folderList[i]["IMAPName"].(string))
+ folderList[i]["Active"] = imapNameLower == activeLower
+ if subfolders, ok := folderList[i]["Subfolders"].([]fiber.Map); ok && len(subfolders) > 0 {
+ updateActive(subfolders)
+ }
+ }
+ }
+
+ updateActive(foldersCopy)
+ return foldersCopy
+}
diff --git a/utils/format/date.go b/utils/format/date.go
new file mode 100644
index 0000000..8ef95cb
--- /dev/null
+++ b/utils/format/date.go
@@ -0,0 +1,99 @@
+package format
+
+import (
+ "html"
+ "lain/types"
+ "time"
+)
+
+func FormatEmailDate(date time.Time, dateFormat types.DateFormat, timeFormat types.TimeFormat, prettyDates bool, timezone string) string {
+ loc, err := time.LoadLocation(timezone)
+ if err != nil {
+ loc = time.UTC
+ }
+
+ date = date.In(loc)
+ now := time.Now().In(loc)
+
+ if prettyDates {
+ return formatPrettyDate(date, now, timeFormat)
+ }
+
+ return formatFullDate(date, dateFormat, timeFormat)
+}
+
+func formatPrettyDate(date, now time.Time, timeFormat types.TimeFormat) string {
+ diff := now.Sub(date)
+
+ // Today - show time only
+ if date.Year() == now.Year() && date.YearDay() == now.YearDay() {
+ return formatTime(date, timeFormat)
+ }
+
+ // Yesterday
+ yesterday := now.AddDate(0, 0, -1)
+ if date.Year() == yesterday.Year() && date.YearDay() == yesterday.YearDay() {
+ return "Yesterday"
+ }
+
+ // This week - show day name and time
+ if diff.Hours() < 168 { // 7 days
+ dayName := date.Format("Mon")
+ timeStr := formatTime(date, timeFormat)
+ return dayName + " " + timeStr
+ }
+
+ // This year - show month and day
+ if date.Year() == now.Year() {
+ return date.Format("Jan 2")
+ }
+
+ // Older - show full date
+ return date.Format("Jan 2, 2006")
+}
+
+func formatFullDate(date time.Time, dateFormat types.DateFormat, timeFormat types.TimeFormat) string {
+ dateStr := formatDate(date, dateFormat)
+ timeStr := formatTime(date, timeFormat)
+ return dateStr + " " + timeStr
+}
+
+func formatDate(date time.Time, dateFormat types.DateFormat) string {
+ switch dateFormat {
+ case types.YearMonthDayDashed:
+ return date.Format("2006-01-02")
+ case types.YearMonthDaySlashed:
+ return date.Format("2006/01/02")
+ case types.YearMonthDayDotted:
+ return date.Format("2006.01.02")
+ case types.DayMonthYearDashed:
+ return date.Format("02-01-2006")
+ case types.DayMonthYearSlashed:
+ return date.Format("02/01/2006")
+ case types.DayMonthYearDotted:
+ return date.Format("02.01.2006")
+ case types.DayMonthYearDottedShort:
+ return date.Format("2.1.06")
+ default:
+ return date.Format("2006-01-02")
+ }
+}
+
+func formatTime(date time.Time, timeFormat types.TimeFormat) string {
+ switch timeFormat {
+ case types.ShortHoursAndMinutes24Hours:
+ return date.Format("15:4")
+ case types.FullHoursAndMinutes24Hours:
+ return date.Format("15:04")
+ case types.ShortHoursAndMinutes12Hours:
+ return date.Format("3:4 PM")
+ case types.FullHoursAndMinutes12Hours:
+ return date.Format("03:04 PM")
+ default:
+ return date.Format("15:04")
+ }
+}
+
+func DecodeHTML(text string) string {
+ return html.UnescapeString(text)
+}
diff --git a/utils/format/html.go b/utils/format/html.go
new file mode 100644
index 0000000..36e2425
--- /dev/null
+++ b/utils/format/html.go
@@ -0,0 +1,73 @@
+package format
+
+import (
+ "regexp"
+ "strings"
+)
+
+func GenerateSnippet(bodyText, bodyHTML string) string {
+ text := bodyText
+ if text == "" && bodyHTML != "" {
+ text = StripHTML(bodyHTML)
+ }
+
+ text = strings.TrimSpace(text)
+ if len(text) > 150 {
+ text = text[:150] + "..."
+ }
+
+ return text
+}
+
+func StripHTML(html string) string {
+ text := html
+
+ styleRegex := regexp.MustCompile(`(?i)<style[^>]*>[\s\S]*?</style>`)
+ text = styleRegex.ReplaceAllString(text, "")
+
+ scriptRegex := regexp.MustCompile(`(?i)<script[^>]*>[\s\S]*?</script>`)
+ text = scriptRegex.ReplaceAllString(text, "")
+
+ headRegex := regexp.MustCompile(`(?i)<head[^>]*>[\s\S]*?</head>`)
+ text = headRegex.ReplaceAllString(text, "")
+
+ text = strings.ReplaceAll(text, "<br>", "\n")
+ text = strings.ReplaceAll(text, "<br/>", "\n")
+ text = strings.ReplaceAll(text, "<br />", "\n")
+ text = strings.ReplaceAll(text, "</p>", "\n\n")
+ text = strings.ReplaceAll(text, "</div>", "\n")
+ text = strings.ReplaceAll(text, "</tr>", "\n")
+ text = strings.ReplaceAll(text, "</h1>", "\n")
+ text = strings.ReplaceAll(text, "</h2>", "\n")
+ text = strings.ReplaceAll(text, "</h3>", "\n")
+ text = strings.ReplaceAll(text, "</li>", "\n")
+
+ inTag := false
+ var result strings.Builder
+ for _, char := range text {
+ if char == '<' {
+ inTag = true
+ continue
+ }
+ if char == '>' {
+ inTag = false
+ continue
+ }
+ if !inTag {
+ result.WriteRune(char)
+ }
+ }
+
+ cleanText := result.String()
+
+ lines := strings.Split(cleanText, "\n")
+ var cleanLines []string
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line != "" {
+ cleanLines = append(cleanLines, line)
+ }
+ }
+
+ return strings.TrimSpace(strings.Join(cleanLines, " "))
+}