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// JournalEntry is a daily journal entry. Name and Content are encrypted
15// client-side and will be null when read back from the API.
16type JournalEntry 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// createJournalEntryRequest defines a new journal entry for JSON serialization.
24type createJournalEntryRequest struct {
25 // DateOn is the date for the journal entry (required).
26 DateOn Date `json:"date_on"`
27 // Name is the title for the entry (optional, defaults to weekday name like "Tuesday, July 1st").
28 Name *string `json:"name,omitempty"`
29 // Content is the Markdown content of the entry (optional).
30 Content *string `json:"content,omitempty"`
31}
32
33// journalEntryResponse wraps a single journal entry from the API.
34type journalEntryResponse struct {
35 JournalEntry JournalEntry `json:"journal_entry"`
36}
37
38// JournalEntryBuilder constructs and creates a journal entry via method chaining.
39// Journal content is encrypted client-side; the API accepts it on create but
40// returns null on read.
41//
42// entry, err := client.NewJournalEntry(lunatask.Today()).
43// WithContent("Shipped the new feature!").
44// Create(ctx)
45type JournalEntryBuilder struct {
46 client *Client
47 req createJournalEntryRequest
48}
49
50// NewJournalEntry starts building a journal entry for the given date.
51func (c *Client) NewJournalEntry(date Date) *JournalEntryBuilder {
52 return &JournalEntryBuilder{client: c, req: createJournalEntryRequest{DateOn: date}}
53}
54
55// WithName sets the entry's title. Defaults to the weekday name if omitted.
56func (b *JournalEntryBuilder) WithName(name string) *JournalEntryBuilder {
57 b.req.Name = &name
58
59 return b
60}
61
62// WithContent sets the Markdown body.
63func (b *JournalEntryBuilder) WithContent(content string) *JournalEntryBuilder {
64 b.req.Content = &content
65
66 return b
67}
68
69// Create sends the journal entry to Lunatask. Returns the created entry's metadata;
70// Name and Content won't round-trip due to E2EE.
71func (b *JournalEntryBuilder) Create(ctx context.Context) (*JournalEntry, error) {
72 if b.req.DateOn.IsZero() {
73 return nil, fmt.Errorf("%w: date_on is required", ErrBadRequest)
74 }
75
76 resp, _, err := doJSON[journalEntryResponse](ctx, b.client, http.MethodPost, "/journal_entries", b.req)
77 if err != nil {
78 return nil, err
79 }
80
81 return &resp.JournalEntry, nil
82}