1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package person
6
7import (
8 "fmt"
9 "os"
10 "strings"
11
12 "git.secluded.site/go-lunatask"
13 "git.secluded.site/lune/internal/client"
14 "git.secluded.site/lune/internal/dateutil"
15 "git.secluded.site/lune/internal/ui"
16 "git.secluded.site/lune/internal/validate"
17 "github.com/spf13/cobra"
18)
19
20// TimelineCmd adds a timeline note to a person. Exported for potential use by shortcuts.
21var TimelineCmd = &cobra.Command{
22 Use: "timeline ID",
23 Short: "Add a timeline note",
24 Long: `Add a timeline note to a person's memory timeline.
25
26Use "-" as content flag value to read from stdin.`,
27 Args: cobra.ExactArgs(1),
28 RunE: runTimeline,
29}
30
31func init() {
32 TimelineCmd.Flags().StringP("content", "c", "", "Note content (use - for stdin)")
33 TimelineCmd.Flags().StringP("date", "d", "", "Date of interaction (natural language)")
34}
35
36func runTimeline(cmd *cobra.Command, args []string) error {
37 personID, err := validate.Reference(args[0])
38 if err != nil {
39 return err
40 }
41
42 apiClient, err := client.New()
43 if err != nil {
44 return err
45 }
46
47 builder := apiClient.NewTimelineNote(personID)
48
49 if err := applyTimelineContent(cmd, builder); err != nil {
50 return err
51 }
52
53 if err := applyTimelineDate(cmd, builder); err != nil {
54 return err
55 }
56
57 note, err := ui.Spin("Creating timeline note…", func() (*lunatask.PersonTimelineNote, error) {
58 return builder.Create(cmd.Context())
59 })
60 if err != nil {
61 return err
62 }
63
64 fmt.Fprintln(cmd.OutOrStdout(), ui.Success.Render("Created timeline note: "+note.ID))
65
66 return nil
67}
68
69func applyTimelineContent(cmd *cobra.Command, builder *lunatask.TimelineNoteBuilder) error {
70 content, _ := cmd.Flags().GetString("content")
71 if content == "" {
72 return nil
73 }
74
75 resolved, err := resolveContent(content)
76 if err != nil {
77 return err
78 }
79
80 builder.WithContent(resolved)
81
82 return nil
83}
84
85func resolveContent(content string) (string, error) {
86 if content != "-" {
87 return content, nil
88 }
89
90 data, err := os.ReadFile("/dev/stdin")
91 if err != nil {
92 return "", fmt.Errorf("reading stdin: %w", err)
93 }
94
95 return strings.TrimSpace(string(data)), nil
96}
97
98func applyTimelineDate(cmd *cobra.Command, builder *lunatask.TimelineNoteBuilder) error {
99 dateStr, _ := cmd.Flags().GetString("date")
100 if dateStr == "" {
101 return nil
102 }
103
104 date, err := dateutil.Parse(dateStr)
105 if err != nil {
106 return err
107 }
108
109 builder.OnDate(date)
110
111 return nil
112}