Skip to content

Commit

Permalink
feat: add batch markasread action
Browse files Browse the repository at this point in the history
  • Loading branch information
0x2E committed Mar 12, 2024
1 parent 4e9bdbd commit f9f17f7
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 37 deletions.
3 changes: 2 additions & 1 deletion api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ func Run() {
itemAPIHandler := newItemAPI(server.NewItem(repo.NewItem(repo.DB)))
items.GET("", itemAPIHandler.List)
items.GET("/:id", itemAPIHandler.Get)
items.PATCH("/:id", itemAPIHandler.Update)
items.PATCH("/:id/bookmark", itemAPIHandler.UpdateBookmark)
items.PATCH("/-/unread", itemAPIHandler.UpdateUnread)
items.DELETE("/:id", itemAPIHandler.Delete)

r.Logger.Fatal(r.Start(fmt.Sprintf("%s:%d", conf.Conf.Host, conf.Conf.Port)))
Expand Down
25 changes: 19 additions & 6 deletions api/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,39 @@ func (i itemAPI) Get(c echo.Context) error {
return c.JSON(http.StatusOK, resp)
}

func (i itemAPI) Update(c echo.Context) error {
var req server.ReqItemUpdate
func (i itemAPI) Delete(c echo.Context) error {
var req server.ReqItemDelete
if err := bindAndValidate(&req, c); err != nil {
return err
}

if err := i.srv.Update(&req); err != nil {
if err := i.srv.Delete(&req); err != nil {
return err
}

return c.NoContent(http.StatusNoContent)
}

func (i itemAPI) Delete(c echo.Context) error {
var req server.ReqItemDelete
func (i itemAPI) UpdateUnread(c echo.Context) error {
var req server.ReqItemUpdateUnread
if err := bindAndValidate(&req, c); err != nil {
return err
}

if err := i.srv.Delete(&req); err != nil {
if err := i.srv.UpdateUnread(&req); err != nil {
return err
}

return c.NoContent(http.StatusNoContent)
}

func (i itemAPI) UpdateBookmark(c echo.Context) error {
var req server.ReqItemUpdateBookmark
if err := bindAndValidate(&req, c); err != nil {
return err
}

if err := i.srv.UpdateBookmark(&req); err != nil {
return err
}

Expand Down
23 changes: 14 additions & 9 deletions frontend/src/lib/api/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ export async function getItem(id: number) {
return api.get('items/' + id).json<Item>();
}

export async function updateItem(
id: number,
data: {
unread?: boolean;
bookmark?: boolean;
}
) {
return api.patch('items/' + id, {
json: data
export async function updateUnread(ids: number[], unread: boolean) {
return api.patch('items/-/unread', {
json: {
ids: ids,
unread: unread
}
});
}

export async function updateBookmark(id: number, bookmark: boolean) {
return api.patch('items/' + id + '/bookmark', {
json: {
bookmark: bookmark
}
});
}
6 changes: 3 additions & 3 deletions frontend/src/lib/components/ItemAction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import type { Icon } from 'lucide-svelte';
import * as Tooltip from '$lib/components/ui/tooltip';
import Button from './ui/button/button.svelte';
import { updateItem } from '$lib/api/item';
import { toast } from 'svelte-sonner';
import type { Item } from '$lib/api/model';
import { updateBookmark, updateUnread } from '$lib/api/item';
export let data: Item;
Expand Down Expand Up @@ -40,7 +40,7 @@
async function handleToggleUnread(e: Event) {
e.preventDefault();
try {
await updateItem(data.id, { unread: !data.unread });
await updateUnread([data.id], !data.unread);
data.unread = !data.unread;
} catch (e) {
toast.error((e as Error).message);
Expand All @@ -50,7 +50,7 @@
async function handleToggleBookmark(e: Event) {
e.preventDefault();
try {
await updateItem(data.id, { bookmark: !data.bookmark });
await updateBookmark(data.id, !data.bookmark);
data.bookmark = !data.bookmark;
} catch (e) {
toast.error((e as Error).message);
Expand Down
39 changes: 37 additions & 2 deletions frontend/src/lib/components/ItemList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import { Button } from './ui/button';
import ItemAction from './ItemAction.svelte';
import * as Select from '$lib/components/ui/select';
import * as Tooltip from '$lib/components/ui/tooltip';
import * as Pagination from '$lib/components/ui/pagination';
import type { Feed, Item } from '$lib/api/model';
import { listItems, type ListFilter } from '$lib/api/item';
import { listItems, type ListFilter, updateUnread } from '$lib/api/item';
import { toast } from 'svelte-sonner';
import { allFeeds as fetchAllFeeds } from '$lib/api/feed';
import type { ComponentType } from 'svelte';
import { CheckCheckIcon, type Icon } from 'lucide-svelte';
export let filter: ListFilter = { offset: 0, count: 10 };
Expand Down Expand Up @@ -41,9 +44,24 @@
toast.error((e as Error).message);
}
}
async function handleMarkAllAsRead() {
try {
const ids = data.map((v) => v.id);
await updateUnread(ids, false);
toast.success('Update successfully');
data.forEach((v) => (v.unread = false));
data = data;
} catch (e) {
toast.error((e as Error).message);
}
}
const actions: { icon: ComponentType<Icon>; tooltip: string; handler: () => void }[] = [
{ icon: CheckCheckIcon, tooltip: 'Mark All As Read', handler: handleMarkAllAsRead }
];
</script>

<div>
<div class="flex justify-between items-center w-full">
<Select.Root
items={allFeeds.map((v) => {
return { value: v.id.toString(), label: v.name };
Expand All @@ -65,6 +83,23 @@
{/each}
</Select.Content>
</Select.Root>

{#if data.length > 0}
<div>
{#each actions as action}
<Tooltip.Root>
<Tooltip.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" on:click={action.handler} size="icon">
<svelte:component this={action.icon} size="20" />
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{action.tooltip}</p>
</Tooltip.Content>
</Tooltip.Root>
{/each}
</div>
{/if}
</div>

<ul class="mt-4">
Expand Down
10 changes: 9 additions & 1 deletion repo/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (i Item) DeleteByFeed(feedID uint) error {
return i.db.Where("feed_id = ?", feedID).Delete(&model.Item{}).Error
}

func (i Item) IdentityExist(feedID uint, guid, link, title string) (bool, error) {
func (i Item) IdentityExist(feedID uint, guid, link, title string) (bool, error) { // TODO: optimize
err := i.db.Model(&model.Item{}).
Where("feed_id = ? AND (guid = ? OR link = ? OR title = ?)", feedID, guid, link, title).
First(&model.Item{}).Error
Expand All @@ -89,3 +89,11 @@ func (i Item) IdentityExist(feedID uint, guid, link, title string) (bool, error)
}
return true, err
}

func (i Item) UpdateUnread(ids []uint, unread *bool) error {
return i.db.Model(&model.Item{}).Where("id IN ?", ids).Update("unread", unread).Error
}

func (i Item) UpdateBookmark(id uint, bookmark *bool) error {
return i.db.Model(&model.Item{}).Where("id = ?", id).Update("bookmark", bookmark).Error
}
2 changes: 1 addition & 1 deletion server/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (g Group) Update(req *ReqGroupUpdate) error {
err := g.groupRepo.Update(req.ID, &model.Group{
Name: req.Name,
})
if errors.Is(err, gorm.ErrDuplicatedKey) {
if errors.Is(err, gorm.ErrDuplicatedKey) { // TODO: errDup should throw 400 http code
err = errors.New("name is not allowed to be the same as other groups")
}
return err
Expand Down
18 changes: 10 additions & 8 deletions server/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
type ItemRepo interface {
List(filter repo.ItemFilter, offset, count *int) ([]*model.Item, int, error)
Get(id uint) (*model.Item, error)
Update(id uint, item *model.Item) error
Delete(id uint) error
UpdateUnread(ids []uint, unread *bool) error
UpdateBookmark(id uint, bookmark *bool) error
}

type Item struct {
Expand Down Expand Up @@ -82,13 +83,14 @@ func (i Item) Get(req *ReqItemGet) (*RespItemGet, error) {
}, nil
}

func (i Item) Update(req *ReqItemUpdate) error {
return i.repo.Update(req.ID, &model.Item{
Unread: req.Unread,
Bookmark: req.Bookmark,
})
}

func (i Item) Delete(req *ReqItemDelete) error {
return i.repo.Delete(req.ID)
}

func (i Item) UpdateUnread(req *ReqItemUpdateUnread) error {
return i.repo.UpdateUnread(req.IDs, req.Unread)
}

func (i Item) UpdateBookmark(req *ReqItemUpdateBookmark) error {
return i.repo.UpdateBookmark(req.ID, req.Bookmark)
}
16 changes: 10 additions & 6 deletions server/item_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ type ReqItemGet struct {

type RespItemGet ItemForm

type ReqItemUpdate struct {
ID uint `param:"id" validate:"required"`
Unread *bool `json:"unread"`
Bookmark *bool `json:"bookmark"`
}

type ReqItemDelete struct {
ID uint `param:"id" validate:"required"`
}

type ReqItemUpdateUnread struct {
IDs []uint `json:"ids" validate:"required"`
Unread *bool `json:"unread" validate:"required"`
}

type ReqItemUpdateBookmark struct {
ID uint `param:"id" validate:"required"`
Bookmark *bool `json:"bookmark" validate:"required"`
}

0 comments on commit f9f17f7

Please sign in to comment.