journal.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// 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 := lunatask.NewJournalEntry(lunatask.Today()).
43//		WithContent("Shipped the new feature!").
44//		Create(ctx, client)
45type JournalEntryBuilder struct {
46	req createJournalEntryRequest
47}
48
49// NewJournalEntry starts building a journal entry for the given date.
50func NewJournalEntry(date Date) *JournalEntryBuilder {
51	return &JournalEntryBuilder{req: createJournalEntryRequest{DateOn: date}} //nolint:exhaustruct
52}
53
54// WithName sets the entry's title. Defaults to the weekday name if omitted.
55func (b *JournalEntryBuilder) WithName(name string) *JournalEntryBuilder {
56	b.req.Name = &name
57
58	return b
59}
60
61// WithContent sets the Markdown body.
62func (b *JournalEntryBuilder) WithContent(content string) *JournalEntryBuilder {
63	b.req.Content = &content
64
65	return b
66}
67
68// Create sends the journal entry to Lunatask. Returns the created entry's metadata;
69// Name and Content won't round-trip due to E2EE.
70func (b *JournalEntryBuilder) Create(ctx context.Context, c *Client) (*JournalEntry, error) {
71	if b.req.DateOn.IsZero() {
72		return nil, fmt.Errorf("%w: date_on is required", ErrBadRequest)
73	}
74
75	resp, _, err := doJSON[journalEntryResponse](ctx, c, http.MethodPost, "/journal_entries", b.req)
76	if err != nil {
77		return nil, err
78	}
79
80	return &resp.JournalEntry, nil
81}