feat(mail): 본문 HTML 우선(메일앱처럼) + 스레드 순번(threadIndex/Count) 제공
All checks were successful
build-and-push / build (push) Successful in 33s
All checks were successful
build-and-push / build (push) Successful in 33s
- GetFull: text/plain 대신 text/html 우선 → 서식 있는 본문 렌더 - handleListProjectMails: 스레드별 ts 정렬 순번 계산해 threadIndex(1=원문)/threadCount 반환 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e0cd216800
commit
d9ab9934c0
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -569,6 +570,8 @@ type mailItem struct {
|
|||||||
models.ProjectMailMsg
|
models.ProjectMailMsg
|
||||||
Note string `json:"note"` // 공동 메모 본문
|
Note string `json:"note"` // 공동 메모 본문
|
||||||
NoteEditedBy string `json:"noteEditedBy"` // 메모 마지막 수정자
|
NoteEditedBy string `json:"noteEditedBy"` // 메모 마지막 수정자
|
||||||
|
ThreadIndex int `json:"threadIndex"` // 스레드 내 순번(1=원문)
|
||||||
|
ThreadCount int `json:"threadCount"` // 스레드 총 메일 수
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleListProjectMails returns the stored client-domain mail for the project,
|
// handleListProjectMails returns the stored client-domain mail for the project,
|
||||||
@ -623,6 +626,25 @@ func (s *Server) handleListProjectMails(w http.ResponseWriter, r *http.Request)
|
|||||||
notes[n.MessageID] = n
|
notes[n.MessageID] = n
|
||||||
}
|
}
|
||||||
me := s.email(r)
|
me := s.email(r)
|
||||||
|
// 스레드 내 순번 계산(저장된 전체 메일 기준, ts 오름차순). 1=원문.
|
||||||
|
type tpos struct{ idx, cnt int }
|
||||||
|
byThread := map[string][]models.ProjectMailMsg{}
|
||||||
|
for _, row := range rows {
|
||||||
|
byThread[row.ThreadID] = append(byThread[row.ThreadID], row)
|
||||||
|
}
|
||||||
|
pos := map[string]tpos{}
|
||||||
|
for tid, group := range byThread {
|
||||||
|
if tid == "" {
|
||||||
|
for _, g := range group {
|
||||||
|
pos[g.MessageID] = tpos{1, 1}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sort.Slice(group, func(i, j int) bool { return group[i].TS < group[j].TS })
|
||||||
|
for i, g := range group {
|
||||||
|
pos[g.MessageID] = tpos{i + 1, len(group)}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 가시성: 단일 메일 기준 — 그 메일의 from/to/cc에 내가 있으면 보인다(답장도 각각 개별 판정).
|
// 가시성: 단일 메일 기준 — 그 메일의 from/to/cc에 내가 있으면 보인다(답장도 각각 개별 판정).
|
||||||
items := make([]mailItem, 0, len(rows))
|
items := make([]mailItem, 0, len(rows))
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
@ -630,7 +652,7 @@ func (s *Server) handleListProjectMails(w http.ResponseWriter, r *http.Request)
|
|||||||
if !m.Involves(me) {
|
if !m.Involves(me) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
it := mailItem{ProjectMailMsg: row}
|
it := mailItem{ProjectMailMsg: row, ThreadIndex: pos[row.MessageID].idx, ThreadCount: pos[row.MessageID].cnt}
|
||||||
if n, ok := notes[row.MessageID]; ok {
|
if n, ok := notes[row.MessageID]; ok {
|
||||||
it.Note = n.Body
|
it.Note = n.Body
|
||||||
it.NoteEditedBy = n.LastEditedBy
|
it.NoteEditedBy = n.LastEditedBy
|
||||||
|
|||||||
@ -364,11 +364,12 @@ func (s *Service) GetFull(ctx context.Context, mailbox, rfc822 string) (FullMess
|
|||||||
}
|
}
|
||||||
walk(out.Payload)
|
walk(out.Payload)
|
||||||
fm := FullMessage{GmailID: gid, Attachments: atts}
|
fm := FullMessage{GmailID: gid, Attachments: atts}
|
||||||
if plain != "" {
|
// 메일앱처럼 보이도록 HTML 본문 우선, 없으면 텍스트.
|
||||||
fm.Body = plain
|
if html != "" {
|
||||||
} else {
|
|
||||||
fm.Body = html
|
fm.Body = html
|
||||||
fm.IsHTML = true
|
fm.IsHTML = true
|
||||||
|
} else {
|
||||||
|
fm.Body = plain
|
||||||
}
|
}
|
||||||
return fm, nil
|
return fm, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user