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 "time"
10)
11
12// Note is a note in Lunatask. Name and Content are encrypted client-side
13// and will be null when read back from the API.
14type Note struct {
15 ID string `json:"id"`
16 NotebookID *string `json:"notebook_id"`
17 DateOn *Date `json:"date_on"`
18 Pinned bool `json:"pinned"`
19 Sources []Source `json:"sources"`
20 CreatedAt time.Time `json:"created_at"`
21 UpdatedAt time.Time `json:"updated_at"`
22}
23
24// createNoteRequest defines a new note for JSON serialization.
25type createNoteRequest struct {
26 Name *string `json:"name,omitempty"`
27 Content *string `json:"content,omitempty"`
28 NotebookID *string `json:"notebook_id,omitempty"`
29 Source *string `json:"source,omitempty"`
30 SourceID *string `json:"source_id,omitempty"`
31}
32
33// updateNoteRequest specifies which fields to change on a note.
34type updateNoteRequest struct {
35 Name *string `json:"name,omitempty"`
36 Content *string `json:"content,omitempty"`
37 NotebookID *string `json:"notebook_id,omitempty"`
38 DateOn *Date `json:"date_on,omitempty"`
39}
40
41// noteResponse wraps a single note from the API.
42type noteResponse struct {
43 Note Note `json:"note"`
44}
45
46// notesResponse wraps a list of notes from the API.
47type notesResponse struct {
48 Notes []Note `json:"notes"`
49}
50
51// ListNotesOptions filters notes by source integration.
52type ListNotesOptions struct {
53 Source *string
54 SourceID *string
55}
56
57// GetSource implements [SourceFilter].
58func (o *ListNotesOptions) GetSource() *string { return o.Source }
59
60// GetSourceID implements [SourceFilter].
61func (o *ListNotesOptions) GetSourceID() *string { return o.SourceID }
62
63// ListNotes returns all notes, optionally filtered. Pass nil for all notes.
64func (c *Client) ListNotes(ctx context.Context, opts *ListNotesOptions) ([]Note, error) {
65 var filter SourceFilter
66 if opts != nil {
67 filter = opts
68 }
69
70 return list(ctx, c, "/notes", filter, func(r notesResponse) []Note { return r.Notes })
71}
72
73// GetNote fetches a note by ID. Name and Content will be null (E2EE).
74func (c *Client) GetNote(ctx context.Context, noteID string) (*Note, error) {
75 return get(ctx, c, "/notes", noteID, "note", func(r noteResponse) Note { return r.Note })
76}
77
78// DeleteNote removes a note and returns its final state.
79func (c *Client) DeleteNote(ctx context.Context, noteID string) (*Note, error) {
80 return del(ctx, c, "/notes", noteID, "note", func(r noteResponse) Note { return r.Note })
81}
82
83// NoteBuilder constructs and creates a note via method chaining.
84// Note fields are encrypted client-side by Lunatask; the API accepts them
85// on create but returns null on read.
86//
87// note, err := lunatask.NewNote().
88// WithName("Meeting notes").
89// WithContent("# Summary\n\n...").
90// InNotebook(notebookID).
91// Create(ctx, client)
92type NoteBuilder struct {
93 req createNoteRequest
94}
95
96// NewNote starts building a note.
97func NewNote() *NoteBuilder {
98 return &NoteBuilder{} //nolint:exhaustruct
99}
100
101// WithName sets the note's title.
102func (b *NoteBuilder) WithName(name string) *NoteBuilder {
103 b.req.Name = &name
104
105 return b
106}
107
108// WithContent sets the Markdown body.
109func (b *NoteBuilder) WithContent(content string) *NoteBuilder {
110 b.req.Content = &content
111
112 return b
113}
114
115// InNotebook places the note in a notebook. IDs are in the notebook's settings in the app.
116func (b *NoteBuilder) InNotebook(notebookID string) *NoteBuilder {
117 b.req.NotebookID = ¬ebookID
118
119 return b
120}
121
122// FromSource tags the note with a free-form origin identifier, useful for
123// tracking notes created by scripts or external integrations.
124func (b *NoteBuilder) FromSource(source, sourceID string) *NoteBuilder {
125 b.req.Source = &source
126 b.req.SourceID = &sourceID
127
128 return b
129}
130
131// Create sends the note to Lunatask. Returns (nil, nil) if a duplicate exists
132// in the same notebook with matching source/source_id.
133func (b *NoteBuilder) Create(ctx context.Context, c *Client) (*Note, error) {
134 return create(ctx, c, "/notes", b.req, func(r noteResponse) Note { return r.Note })
135}
136
137// NoteUpdateBuilder constructs and updates a note via method chaining.
138// Only fields you set will be modified; others remain unchanged.
139//
140// note, err := lunatask.NewNoteUpdate(noteID).
141// WithContent("# Updated content").
142// Update(ctx, client)
143type NoteUpdateBuilder struct {
144 noteID string
145 req updateNoteRequest
146}
147
148// NewNoteUpdate starts building a note update for the given note ID.
149func NewNoteUpdate(noteID string) *NoteUpdateBuilder {
150 return &NoteUpdateBuilder{noteID: noteID} //nolint:exhaustruct
151}
152
153// WithName sets the note's title.
154func (b *NoteUpdateBuilder) WithName(name string) *NoteUpdateBuilder {
155 b.req.Name = &name
156
157 return b
158}
159
160// WithContent sets the Markdown body.
161func (b *NoteUpdateBuilder) WithContent(content string) *NoteUpdateBuilder {
162 b.req.Content = &content
163
164 return b
165}
166
167// InNotebook moves the note to a notebook.
168func (b *NoteUpdateBuilder) InNotebook(notebookID string) *NoteUpdateBuilder {
169 b.req.NotebookID = ¬ebookID
170
171 return b
172}
173
174// OnDate sets the date for the note.
175func (b *NoteUpdateBuilder) OnDate(date Date) *NoteUpdateBuilder {
176 b.req.DateOn = &date
177
178 return b
179}
180
181// Update sends the changes to Lunatask.
182func (b *NoteUpdateBuilder) Update(ctx context.Context, c *Client) (*Note, error) {
183 return update(ctx, c, "/notes", b.noteID, "note", b.req, func(r noteResponse) Note { return r.Note })
184}