mirror of
https://github.com/zoriya/vex.git
synced 2025-12-05 22:56:09 +00:00
wip: Add user entries status
This commit is contained in:
@@ -1,11 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/zoriya/vex"
|
||||
)
|
||||
|
||||
type ChangeEntryStatusDto struct {
|
||||
Id uuid.UUID `json:"id" validate:"required"`
|
||||
IsRead bool `json:"isRead"`
|
||||
IsBookmarked bool `json:"isBookmarked"`
|
||||
IsReadLater bool `json:"isReadLater"`
|
||||
IsIgnored bool `json:"isIgnored"`
|
||||
}
|
||||
|
||||
func (h *Handler) GetEntries(c echo.Context) error {
|
||||
ret, err := h.entries.ListEntries()
|
||||
user, err := GetCurrentUserId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := h.entries.ListEntries(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(200, ret)
|
||||
}
|
||||
|
||||
func (h *Handler) ChangeUserStatus(c echo.Context) error {
|
||||
user, err := GetCurrentUserId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req ChangeEntryStatusDto
|
||||
err = c.Bind(&req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if err = c.Validate(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.entries.ChangeStatus(vex.ChangeStatusDao{
|
||||
Id: req.Id,
|
||||
User: user,
|
||||
IsRead: req.IsRead,
|
||||
IsBookmarked: req.IsBookmarked,
|
||||
IsReadLater: req.IsReadLater,
|
||||
IsIgnored: req.IsIgnored,
|
||||
})
|
||||
ret, err := h.entries.GetEntry(req.Id, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -13,5 +59,6 @@ func (h *Handler) GetEntries(c echo.Context) error {
|
||||
}
|
||||
|
||||
func (h *Handler) RegisterEntriesRoutes(e *echo.Echo, r *echo.Group) {
|
||||
e.GET("/entries", h.GetEntries)
|
||||
r.GET("/entries", h.GetEntries)
|
||||
r.PATCH("/entries", h.ChangeUserStatus)
|
||||
}
|
||||
|
||||
103
api/entries.go
103
api/entries.go
@@ -9,14 +9,18 @@ import (
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"`
|
||||
Date time.Time `json:"date"`
|
||||
Content string `json:"content"`
|
||||
Authors []string `json:"authors"`
|
||||
FeedId uuid.UUID `json:"feedId"`
|
||||
Feed Feed `json:"feed,omitempty"`
|
||||
Id uuid.UUID `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"`
|
||||
Date time.Time `json:"date"`
|
||||
Content string `json:"content"`
|
||||
Authors []string `json:"authors"`
|
||||
FeedId uuid.UUID `json:"feedId"`
|
||||
Feed Feed `json:"feed,omitempty"`
|
||||
IsRead bool `json:"isRead"`
|
||||
IsBookmarked bool `json:"isBookmarked"`
|
||||
IsReadLater bool `json:"isReadLater"`
|
||||
IsIgnored bool `json:"isIgnored"`
|
||||
}
|
||||
|
||||
type EntryDao struct {
|
||||
@@ -28,18 +32,37 @@ type EntryDao struct {
|
||||
Authors pq.StringArray
|
||||
FeedId uuid.UUID `db:"feed_id"`
|
||||
Feed FeedDao `db:"feed"`
|
||||
|
||||
EntryId *uuid.UUID `db:"entry_id"`
|
||||
UserId *uuid.UUID `db:"user_id"`
|
||||
IsRead *bool `db:"is_read"`
|
||||
IsBookmarked *bool `db:"is_bookmarked"`
|
||||
IsReadLater *bool `db:"is_read_later"`
|
||||
IsIgnored *bool `db:"is_ignored"`
|
||||
}
|
||||
|
||||
func OrDefault[T any](val *T) T {
|
||||
if val != nil {
|
||||
return *val
|
||||
}
|
||||
var ret T
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e *EntryDao) ToEntry() Entry {
|
||||
return Entry{
|
||||
Id: e.Id,
|
||||
Title: e.Title,
|
||||
Link: e.Link,
|
||||
Date: e.Date,
|
||||
Content: e.Content,
|
||||
Authors: e.Authors,
|
||||
FeedId: e.FeedId,
|
||||
Feed: e.Feed.ToFeed(),
|
||||
Id: e.Id,
|
||||
Title: e.Title,
|
||||
Link: e.Link,
|
||||
Date: e.Date,
|
||||
Content: e.Content,
|
||||
Authors: e.Authors,
|
||||
FeedId: e.FeedId,
|
||||
Feed: e.Feed.ToFeed(),
|
||||
IsRead: OrDefault(e.IsRead),
|
||||
IsBookmarked: OrDefault(e.IsBookmarked),
|
||||
IsReadLater: OrDefault(e.IsReadLater),
|
||||
IsIgnored: OrDefault(e.IsIgnored),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,18 +83,60 @@ func (s EntryService) Add(entries []EntryDao) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s EntryService) ListEntries() ([]Entry, error) {
|
||||
func (s EntryService) GetEntry(id uuid.UUID, userId uuid.UUID) (Entry, error) {
|
||||
var ret EntryDao
|
||||
err := s.database.Select(
|
||||
&ret,
|
||||
`select e.*, s.*,
|
||||
f.id as "feed.id", f.name as "feed.name", f.link as "feed.link", f.favicon_url as "feed.favicon_url",
|
||||
f.tags as "feed.tags", f.submitter_id as "feed.submitter_id", f.added_date as "feed.added_date"
|
||||
from entries as e
|
||||
left join entries_users as s on s.entry_id = e.id and s.user_id = $1
|
||||
left join feeds as f on f.id = e.feed_id
|
||||
where e.id = $2`,
|
||||
userId,
|
||||
id,
|
||||
)
|
||||
if err != nil {
|
||||
return Entry{}, err
|
||||
}
|
||||
return ret.ToEntry(), nil
|
||||
}
|
||||
|
||||
func (s EntryService) ListEntries(userId uuid.UUID) ([]Entry, error) {
|
||||
ret := []EntryDao{}
|
||||
err := s.database.Select(
|
||||
&ret,
|
||||
`select e.*, f.id as "feed.id", f.name as "feed.name", f.link as "feed.link", f.favicon_url as "feed.favicon_url",
|
||||
`select e.*, s.*,
|
||||
f.id as "feed.id", f.name as "feed.name", f.link as "feed.link", f.favicon_url as "feed.favicon_url",
|
||||
f.tags as "feed.tags", f.submitter_id as "feed.submitter_id", f.added_date as "feed.added_date"
|
||||
from entries as e
|
||||
left join entries_users as s on s.entry_id = e.id and s.user_id = $1
|
||||
left join feeds as f on f.id = e.feed_id
|
||||
order by e.date`,
|
||||
order by e.date desc`,
|
||||
userId,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Map(ret, func(e EntryDao, _ int) Entry { return e.ToEntry() }), nil
|
||||
}
|
||||
|
||||
type ChangeStatusDao struct {
|
||||
Id uuid.UUID
|
||||
User uuid.UUID
|
||||
IsRead bool `db:"is_read"`
|
||||
IsBookmarked bool `db:"is_bookmarked"`
|
||||
IsReadLater bool `db:"is_read_later"`
|
||||
IsIgnored bool `db:"is_ignored"`
|
||||
}
|
||||
|
||||
func (s EntryService) ChangeStatus(status ChangeStatusDao) error {
|
||||
_, err := s.database.NamedExec(
|
||||
`insert into entries_users (entry_id, user_id, is_read, is_bookmared, is_read_later, is_ignored)
|
||||
values (:id, :user, :is_read, :is_bookmared, :is_read_later, :is_ignored)
|
||||
on conflict(entry_id, user_id) do update set is_read = :is_read, is_bookmarked = :is_bookmarked, is_read_later = :is_read_later, is_ignored = :is_ignored`,
|
||||
status,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ func (s FeedService) AddFeed(feed Feed) (Feed, error) {
|
||||
if err != nil {
|
||||
return Feed{}, err
|
||||
}
|
||||
|
||||
return feed, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,18 @@ create table if not exists entries(
|
||||
|
||||
create table if not exists entries_users(
|
||||
user_id uuid not null references users(id),
|
||||
feed_id uuid not null references feeds(id),
|
||||
entry_id uuid not null references entries(id),
|
||||
is_read bool not null,
|
||||
is_bookmarked bool not null,
|
||||
is_read_later bool not null,
|
||||
is_ignored bool not null,
|
||||
constraint entries_users_pk primary key (user_id, entry_id)
|
||||
);
|
||||
|
||||
create table if not exists feeds_users(
|
||||
user_id uuid not null references users(id),
|
||||
feed_id uuid not null references feeds(id),
|
||||
is_ignored bool not null,
|
||||
constraint entries_users_pk primary key (user_id, feed_id)
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user