1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package lunatask
6
7import (
8 "context"
9 "fmt"
10 "net/http"
11 "time"
12)
13
14// PersonTimelineNote is a note on a person's memory timeline.
15// Content is encrypted client-side and will be null when read.
16type PersonTimelineNote struct {
17 ID string `json:"id"`
18 DateOn *Date `json:"date_on"`
19 CreatedAt time.Time `json:"created_at"`
20 UpdatedAt time.Time `json:"updated_at"`
21}
22
23// createPersonTimelineNoteRequest defines a timeline note for JSON serialization.
24type createPersonTimelineNoteRequest struct {
25 // PersonID is the ID of the person to add the note to (required).
26 PersonID string `json:"person_id"`
27 // DateOn is the date for the note (optional, defaults to today).
28 DateOn *Date `json:"date_on,omitempty"`
29 // Content is the Markdown content of the note (optional but impractical if empty).
30 Content *string `json:"content,omitempty"`
31}
32
33// personTimelineNoteResponse wraps a single timeline note from the API.
34type personTimelineNoteResponse struct {
35 PersonTimelineNote PersonTimelineNote `json:"person_timeline_note"`
36}
37
38// TimelineNoteBuilder constructs and creates a timeline note via method chaining.
39// Content is encrypted client-side; the API accepts it on create but returns null on read.
40//
41// note, err := lunatask.NewTimelineNote(personID).
42// OnDate(lunatask.Today()).
43// WithContent("Had coffee, discussed the project.").
44// Create(ctx, client)
45type TimelineNoteBuilder struct {
46 req createPersonTimelineNoteRequest
47}
48
49// NewTimelineNote starts building a timeline note for the given person.
50// Get person IDs from [Client.ListPeople].
51func NewTimelineNote(personID string) *TimelineNoteBuilder {
52 return &TimelineNoteBuilder{req: createPersonTimelineNoteRequest{PersonID: personID}} //nolint:exhaustruct
53}
54
55// OnDate sets when this interaction occurred.
56func (b *TimelineNoteBuilder) OnDate(date Date) *TimelineNoteBuilder {
57 b.req.DateOn = &date
58
59 return b
60}
61
62// WithContent sets the Markdown body describing the interaction.
63func (b *TimelineNoteBuilder) WithContent(content string) *TimelineNoteBuilder {
64 b.req.Content = &content
65
66 return b
67}
68
69// Create sends the timeline note to Lunatask.
70// Get person IDs from [Client.ListPeople].
71func (b *TimelineNoteBuilder) Create(ctx context.Context, c *Client) (*PersonTimelineNote, error) {
72 if b.req.PersonID == "" {
73 return nil, fmt.Errorf("%w: person_id is required", ErrBadRequest)
74 }
75
76 resp, _, err := doJSON[personTimelineNoteResponse](ctx, c, http.MethodPost, "/person_timeline_notes", b.req)
77 if err != nil {
78 return nil, err
79 }
80
81 return &resp.PersonTimelineNote, nil
82}