timeline.go

 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}