-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtui.go
194 lines (163 loc) · 4.39 KB
/
tui.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
This file contains the TUI code for Chaker.
*/
package main
import (
"log"
"os"
"os/exec"
"runtime"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/olekukonko/ts"
)
// Help line
var help string = lipgloss.NewStyle().
Faint(true).
Bold(true).
Render("↑ - up · ↓ - down · q - quit · ⏎ - open · c - comment section · m - more · p - prev")
// Terminal size
var size, _ = ts.GetSize()
// Style
var style = lipgloss.NewStyle().
PaddingLeft(4).
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#ff6600")).
Align(lipgloss.Center).
Width(size.Col())
// Shortcuts
var render = style.Render
// The Model struct
type Model struct {
cursor int // Which submission is our cursor pointing at
selected string // Which submission is selected
}
// Open the browser with the URL
func openBrowserWithURL(url string) {
// To make sure that this works on other platforms, we need to use different commands
var browserCmd string
// Detect the OS
os := runtime.GOOS
switch os {
case "windows":
browserCmd = "explorer"
case "darwin":
browserCmd = "open" // Darwin aka Mac
case "linux":
browserCmd = "browse"
default:
panic("Unknown OS: " + os)
}
// Open the default browser with the URL
cmd := exec.Command(browserCmd, url)
_, err := cmd.Output()
// Report the error!
if err != nil {
log.Fatal(err)
}
}
// Make a custom title (and extra information for fainting effect) from the submission
func returnCustomTitle(submission Submission) (string, string) {
// The submission time (Similiar to Hacker News)
submissionTime := int64(time.Since(time.Unix(int64(submission.Time), 0)).Hours())
if submission.Type == "job" {
// If the submission is a 'job', then we don't need to print unnecessary information,
// we will just show the title, how old is it and the URL
return submission.Title, spf("(%d hour ago)", submissionTime)
}
return submission.Title, spf(
"(%d points by %s | %d hour ago | %d comments)",
submission.Score,
submission.By,
submissionTime,
submission.Descendants,
)
}
// The main function
func tui() {
// Initial model
var initialModel Model = Model{
cursor: 1,
selected: "",
}
p := tea.NewProgram(
initialModel,
tea.WithAltScreen(),
)
if err := p.Start(); err != nil {
log.Fatalf("We got an error! %v", err)
os.Exit(1)
}
}
// Initialize the app
func (m Model) Init() tea.Cmd {
return nil
}
// Update the app
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
// Handle keyboard event
case tea.KeyMsg:
switch msg.String() {
// It is 'q', quit the program!
case "q":
return m, tea.Quit
// An Up? Fly up!
case "up":
if m.cursor > 1 {
m.cursor--
}
// A Down? Fall down!
case "down":
if m.cursor < len(submissions)-1 {
m.cursor++
}
// Enter? Enter the web!
case "enter":
openBrowserWithURL(submissions[m.cursor].URL)
// 'c'? Open the comment section of the title in the cursor!
case "c":
openBrowserWithURL(spf("https://news.ycombinator.com/item?id=%d", submissions[m.cursor].ID))
// 'm'? Next page, please!
case "m":
pageNum++
submissions = []Submission{}
submissions = Scrape(pageNum) // Scrape fresh data
// Same as 'm', but previous page, and also checks if pageNum is larger than 1 to prevent go to page 0
case "p":
if pageNum > 1 {
pageNum--
submissions = []Submission{}
submissions = Scrape(pageNum) // Scrape fresh data
}
}
}
return m, nil
}
// The UI of the app
func (m Model) View() string {
// Header
s := render(spf("Today is %s\n", time.Now().Format("Monday, January 2, 2006, at 15:04 PM")))
for i := range submissions {
// Is the cursor pointing at this title?
cursor := " " // No cursor
if m.cursor == i {
cursor = ">" // Yes cursor!
}
// Render the row
title, extraInfo := returnCustomTitle(submissions[i]) // Get the title and extra info
urlHost := parseURLHost(submissions[i].URL) // Get the host of the URL
// If the cursor is not pointing to this title, we won't need the extraInfo
if m.cursor != i {
extraInfo = ""
} else {
extraInfo = lipgloss.NewStyle().Faint(true).Render(spf("%s %s", urlHost, extraInfo))
}
s += spf("%s %s %s\n", cursor, title, extraInfo)
}
// Footer (the page where the user are in and help)
s += spf("You are at page %d %s", pageNum, help)
// Send the UI for rendering
return s
}