rate review
This commit is contained in:
parent
997efe2e4c
commit
eaa920abf5
|
@ -162,3 +162,67 @@ func (e *EMDB) GetReviews(movieID string) ([]moviestore.Review, error) {
|
||||||
|
|
||||||
return reviews, nil
|
return reviews, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EMDB) GetNextUnratedReview() (moviestore.Review, error) {
|
||||||
|
url := fmt.Sprintf("%s/review/unrated/next", e.baseURL)
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", e.apiKey)
|
||||||
|
|
||||||
|
resp, err := e.c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return moviestore.Review{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var review moviestore.Review
|
||||||
|
if err := json.Unmarshal(body, &review); err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return review, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EMDB) UpdateReview(review moviestore.Review) (moviestore.Review, error) {
|
||||||
|
body, err := json.Marshal(review)
|
||||||
|
if err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s/review/%s", e.baseURL, review.ID)
|
||||||
|
req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", e.apiKey)
|
||||||
|
|
||||||
|
resp, err := e.c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
||||||
|
return moviestore.Review{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
newBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var newReview moviestore.Review
|
||||||
|
if err := json.Unmarshal(newBody, &newReview); err != nil {
|
||||||
|
return moviestore.Review{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newReview, nil
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ func (reviewAPI *ReviewAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
subPath, subTrail := ShiftPath(r.URL.Path)
|
subPath, subTrail := ShiftPath(r.URL.Path)
|
||||||
subSubPath, _ := ShiftPath(subTrail)
|
subSubPath, _ := ShiftPath(subTrail)
|
||||||
switch {
|
switch {
|
||||||
|
case r.Method == http.MethodGet && subPath != "":
|
||||||
|
reviewAPI.Get(w, r, subPath)
|
||||||
case r.Method == http.MethodGet && subPath == "unrated" && subSubPath == "":
|
case r.Method == http.MethodGet && subPath == "unrated" && subSubPath == "":
|
||||||
reviewAPI.ListUnrated(w, r)
|
reviewAPI.ListUnrated(w, r)
|
||||||
case r.Method == http.MethodGet && subPath == "unrated" && subSubPath == "next":
|
case r.Method == http.MethodGet && subPath == "unrated" && subSubPath == "next":
|
||||||
|
@ -38,6 +40,21 @@ func (reviewAPI *ReviewAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (reviewAPI *ReviewAPI) Get(w http.ResponseWriter, r *http.Request, id string) {
|
||||||
|
logger := reviewAPI.logger.With("method", "get")
|
||||||
|
|
||||||
|
review, err := reviewAPI.repo.FindOne(id)
|
||||||
|
if err != nil {
|
||||||
|
Error(w, http.StatusInternalServerError, "could not get review", err, logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(review); err != nil {
|
||||||
|
Error(w, http.StatusInternalServerError, "could not encode review", err, logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (reviewAPI *ReviewAPI) ListUnrated(w http.ResponseWriter, r *http.Request) {
|
func (reviewAPI *ReviewAPI) ListUnrated(w http.ResponseWriter, r *http.Request) {
|
||||||
logger := reviewAPI.logger.With("method", "listUnrated")
|
logger := reviewAPI.logger.With("method", "listUnrated")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
type Review struct {
|
||||||
|
ID string
|
||||||
|
MovieID string
|
||||||
|
Source string
|
||||||
|
URL string
|
||||||
|
Review string
|
||||||
|
Quality int
|
||||||
|
Mentions []string
|
||||||
|
}
|
|
@ -147,15 +147,6 @@ func (m tabEMDB) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, tea.Batch(cmds...)
|
return m, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *tabEMDB) updateFormInputs(msg tea.Msg) tea.Cmd {
|
|
||||||
cmds := make([]tea.Cmd, 3)
|
|
||||||
m.inputWatchedOn, cmds[0] = m.inputWatchedOn.Update(msg)
|
|
||||||
m.inputRating, cmds[1] = m.inputRating.Update(msg)
|
|
||||||
m.inputComment, cmds[2] = m.inputComment.Update(msg)
|
|
||||||
|
|
||||||
return tea.Batch(cmds...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m tabEMDB) View() string {
|
func (m tabEMDB) View() string {
|
||||||
colLeft := lipgloss.NewStyle().
|
colLeft := lipgloss.NewStyle().
|
||||||
Width(m.colWidth - 2).
|
Width(m.colWidth - 2).
|
||||||
|
@ -182,6 +173,58 @@ func (m *tabEMDB) UpdateForm() {
|
||||||
m.Log(fmt.Sprintf("showing movie %s", movie.m.ID))
|
m.Log(fmt.Sprintf("showing movie %s", movie.m.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *tabEMDB) updateFormInputs(msg tea.Msg) tea.Cmd {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
|
||||||
|
switch m.formFocus {
|
||||||
|
case 0:
|
||||||
|
m.inputWatchedOn, cmd = m.inputWatchedOn.Update(msg)
|
||||||
|
case 1:
|
||||||
|
m.inputRating, cmd = m.inputRating.Update(msg)
|
||||||
|
case 2:
|
||||||
|
m.inputComment, cmd = m.inputComment.Update(msg)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabEMDB) NavigateForm(key string) []tea.Cmd {
|
||||||
|
order := []string{"Watched on", "Rating", "Comment"}
|
||||||
|
|
||||||
|
var cmds []tea.Cmd
|
||||||
|
if key == "up" || key == "shift+tab" {
|
||||||
|
m.formFocus--
|
||||||
|
} else {
|
||||||
|
m.formFocus++
|
||||||
|
}
|
||||||
|
if m.formFocus >= len(order) {
|
||||||
|
m.formFocus = 0
|
||||||
|
}
|
||||||
|
if m.formFocus < 0 {
|
||||||
|
m.formFocus = len(order) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
switch order[m.formFocus] {
|
||||||
|
case "Watched on":
|
||||||
|
m.inputWatchedOn.PromptStyle = focusedStyle
|
||||||
|
m.inputWatchedOn.TextStyle = focusedStyle
|
||||||
|
cmds = append(cmds, m.inputWatchedOn.Focus())
|
||||||
|
m.inputRating.Blur()
|
||||||
|
m.inputComment.Blur()
|
||||||
|
case "Rating":
|
||||||
|
m.inputRating.PromptStyle = focusedStyle
|
||||||
|
m.inputRating.TextStyle = focusedStyle
|
||||||
|
cmds = append(cmds, m.inputRating.Focus())
|
||||||
|
m.inputWatchedOn.Blur()
|
||||||
|
m.inputComment.Blur()
|
||||||
|
case "Comment":
|
||||||
|
cmds = append(cmds, m.inputComment.Focus())
|
||||||
|
m.inputWatchedOn.Blur()
|
||||||
|
m.inputRating.Blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
func (m *tabEMDB) ViewForm() string {
|
func (m *tabEMDB) ViewForm() string {
|
||||||
movie, ok := m.list.SelectedItem().(Movie)
|
movie, ok := m.list.SelectedItem().(Movie)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -215,44 +258,6 @@ func (m *tabEMDB) ViewForm() string {
|
||||||
return lipgloss.JoinHorizontal(lipgloss.Top, labelView, fieldsView)
|
return lipgloss.JoinHorizontal(lipgloss.Top, labelView, fieldsView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *tabEMDB) NavigateForm(key string) []tea.Cmd {
|
|
||||||
order := []string{"Watched on", "Rating", "Comment"}
|
|
||||||
|
|
||||||
var cmds []tea.Cmd
|
|
||||||
if key == "up" || key == "shift+tab" {
|
|
||||||
m.formFocus--
|
|
||||||
} else {
|
|
||||||
m.formFocus++
|
|
||||||
}
|
|
||||||
if m.formFocus > len(order) {
|
|
||||||
m.formFocus = 0
|
|
||||||
}
|
|
||||||
if m.formFocus < 0 {
|
|
||||||
m.formFocus = len(order)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch order[m.formFocus] {
|
|
||||||
case "Watched on":
|
|
||||||
m.inputWatchedOn.PromptStyle = focusedStyle
|
|
||||||
m.inputWatchedOn.TextStyle = focusedStyle
|
|
||||||
cmds = append(cmds, m.inputWatchedOn.Focus())
|
|
||||||
m.inputRating.Blur()
|
|
||||||
m.inputComment.Blur()
|
|
||||||
case "Rating":
|
|
||||||
m.inputRating.PromptStyle = focusedStyle
|
|
||||||
m.inputRating.TextStyle = focusedStyle
|
|
||||||
cmds = append(cmds, m.inputRating.Focus())
|
|
||||||
m.inputWatchedOn.Blur()
|
|
||||||
m.inputComment.Blur()
|
|
||||||
case "Comment":
|
|
||||||
cmds = append(cmds, m.inputComment.Focus())
|
|
||||||
m.inputWatchedOn.Blur()
|
|
||||||
m.inputRating.Blur()
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *tabEMDB) StoreMovie() tea.Cmd {
|
func (m *tabEMDB) StoreMovie() tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
updatedMovie := m.list.SelectedItem().(Movie)
|
updatedMovie := m.list.SelectedItem().(Movie)
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"ewintr.nl/emdb/client"
|
"ewintr.nl/emdb/client"
|
||||||
|
"ewintr.nl/emdb/cmd/api-service/moviestore"
|
||||||
|
"github.com/charmbracelet/bubbles/textarea"
|
||||||
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
@ -11,12 +18,29 @@ type tabReview struct {
|
||||||
emdb *client.EMDB
|
emdb *client.EMDB
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
mode string
|
||||||
|
selectedReview moviestore.Review
|
||||||
|
inputQuality textinput.Model
|
||||||
|
inputMentions textarea.Model
|
||||||
|
formFocus int
|
||||||
logger *Logger
|
logger *Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTabReview(emdb *client.EMDB, logger *Logger) (tea.Model, tea.Cmd) {
|
func NewTabReview(emdb *client.EMDB, logger *Logger) (tea.Model, tea.Cmd) {
|
||||||
|
inputQuality := textinput.New()
|
||||||
|
inputQuality.Prompt = ""
|
||||||
|
inputQuality.Width = 50
|
||||||
|
inputQuality.CharLimit = 500
|
||||||
|
inputMentions := textarea.New()
|
||||||
|
inputMentions.SetWidth(30)
|
||||||
|
inputMentions.SetHeight(5)
|
||||||
|
inputMentions.CharLimit = 500
|
||||||
|
|
||||||
return &tabReview{
|
return &tabReview{
|
||||||
emdb: emdb,
|
emdb: emdb,
|
||||||
|
mode: "view",
|
||||||
|
inputQuality: inputQuality,
|
||||||
|
inputMentions: inputMentions,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -35,6 +59,20 @@ func (m *tabReview) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.height = msg.Height
|
m.height = msg.Height
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
switch m.mode {
|
||||||
|
case "edit":
|
||||||
|
switch msg.String() {
|
||||||
|
case "tab", "shift+tab", "up", "down":
|
||||||
|
cmds = append(cmds, m.NavigateForm(msg.String())...)
|
||||||
|
case "esc":
|
||||||
|
m.mode = "view"
|
||||||
|
case "enter":
|
||||||
|
m.mode = "view"
|
||||||
|
cmds = append(cmds, m.StoreReview())
|
||||||
|
default:
|
||||||
|
cmds = append(cmds, m.updateFormInputs(msg))
|
||||||
|
}
|
||||||
|
default:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "q", "esc", "ctrl+c":
|
case "q", "esc", "ctrl+c":
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
|
@ -42,16 +80,134 @@ func (m *tabReview) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
cmds = append(cmds, SelectNextTab())
|
cmds = append(cmds, SelectNextTab())
|
||||||
case "left", "shift+tab":
|
case "left", "shift+tab":
|
||||||
cmds = append(cmds, SelectPrevTab())
|
cmds = append(cmds, SelectPrevTab())
|
||||||
|
case "e":
|
||||||
|
m.mode = "edit"
|
||||||
|
m.formFocus = 0
|
||||||
|
cmds = append(cmds, m.inputQuality.Focus())
|
||||||
|
case "n":
|
||||||
|
m.mode = "edit"
|
||||||
|
m.formFocus = 0
|
||||||
|
m.logger.Log("fetching next unrated review")
|
||||||
|
cmds = append(cmds, m.inputQuality.Focus())
|
||||||
|
cmds = append(cmds, FetchNextUnratedReview(m.emdb))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case moviestore.Review:
|
||||||
|
m.logger.Log(fmt.Sprintf("got review %s", msg.ID))
|
||||||
|
m.selectedReview = msg
|
||||||
|
m.UpdateForm()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return m, tea.Batch(cmds...)
|
return m, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *tabReview) View() string {
|
func (m *tabReview) View() string {
|
||||||
return lipgloss.NewStyle().
|
colReviewWidth := m.width / 2
|
||||||
Width(m.width - 2).
|
colRateWidth := m.width - colReviewWidth
|
||||||
|
|
||||||
|
colReview := lipgloss.NewStyle().
|
||||||
|
Width(colReviewWidth - 2).
|
||||||
Height(m.height - 2).
|
Height(m.height - 2).
|
||||||
Padding(1).
|
Padding(1).
|
||||||
Render("Review")
|
Render(m.ViewReview())
|
||||||
|
colRate := lipgloss.NewStyle().
|
||||||
|
Width(colRateWidth - 2).
|
||||||
|
Height(m.height - 2).
|
||||||
|
Padding(1).
|
||||||
|
Render(m.ViewForm())
|
||||||
|
|
||||||
|
return lipgloss.JoinHorizontal(lipgloss.Top, colRate, colReview)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabReview) UpdateForm() {
|
||||||
|
mentions := strings.Join(m.selectedReview.Mentions, ",")
|
||||||
|
m.inputQuality.SetValue(fmt.Sprintf("%d", m.selectedReview.Quality))
|
||||||
|
m.inputMentions.SetValue(mentions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabReview) updateFormInputs(msg tea.Msg) tea.Cmd {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
switch m.formFocus {
|
||||||
|
case 0:
|
||||||
|
m.inputQuality, cmd = m.inputQuality.Update(msg)
|
||||||
|
case 1:
|
||||||
|
m.inputMentions, cmd = m.inputMentions.Update(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabReview) NavigateForm(key string) []tea.Cmd {
|
||||||
|
order := []string{"quality", "mentions"}
|
||||||
|
|
||||||
|
var cmds []tea.Cmd
|
||||||
|
if key == "up" || key == "shift+tab" {
|
||||||
|
m.formFocus--
|
||||||
|
} else {
|
||||||
|
m.formFocus++
|
||||||
|
}
|
||||||
|
if m.formFocus >= len(order) {
|
||||||
|
m.formFocus = 0
|
||||||
|
}
|
||||||
|
if m.formFocus < 0 {
|
||||||
|
m.formFocus = len(order) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
switch order[m.formFocus] {
|
||||||
|
case "quality":
|
||||||
|
m.inputQuality.PromptStyle = focusedStyle
|
||||||
|
m.inputQuality.TextStyle = focusedStyle
|
||||||
|
cmds = append(cmds, m.inputQuality.Focus())
|
||||||
|
m.inputMentions.Blur()
|
||||||
|
case "mentions":
|
||||||
|
cmds = append(cmds, m.inputMentions.Focus())
|
||||||
|
m.inputQuality.Blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabReview) ViewForm() string {
|
||||||
|
labels := "Quality:\nMentions:"
|
||||||
|
fields := fmt.Sprintf("%s\n%s", m.inputQuality.View(), m.inputMentions.View())
|
||||||
|
|
||||||
|
return lipgloss.JoinHorizontal(lipgloss.Left, labels, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabReview) ViewReview() string {
|
||||||
|
review := strings.ReplaceAll(m.selectedReview.Review, "\n", "\n\n")
|
||||||
|
|
||||||
|
return review
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tabReview) StoreReview() tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
quality, err := strconv.Atoi(m.inputQuality.Value())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mentions := m.inputMentions.Value()
|
||||||
|
|
||||||
|
m.selectedReview.Quality = quality
|
||||||
|
m.selectedReview.Mentions = strings.Split(mentions, ",")
|
||||||
|
|
||||||
|
review, err := m.emdb.UpdateReview(m.selectedReview)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return review
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchNextUnratedReview(emdb *client.EMDB) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
review, err := emdb.GetNextUnratedReview()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return review
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue