implement edit bookmark dialog
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import type { Bookmark, BookmarkId, BookmarkTag } from "@markone/core/bookmark"
|
||||
import type { Bookmark, BookmarkId, Tag, TaggedBookmark } from "@markone/core"
|
||||
import type { User } from "@markone/core/user"
|
||||
import { Readability } from "@mozilla/readability"
|
||||
import { JSDOM } from "jsdom"
|
||||
import { db } from "~/database.ts"
|
||||
import { findTagsByNames, insertTags } from "~/tag/tag.js"
|
||||
import { DEMO_BOOKMARKS } from "./demo-bookmarks.ts"
|
||||
|
||||
class LinkUnreachable {}
|
||||
@@ -52,7 +53,7 @@ function findBookmarkCachedContent(id: string, user: User): Buffer | null {
|
||||
}
|
||||
|
||||
function findBookmark(id: string, user: User): Bookmark | null {
|
||||
const bookmarkQuery = db.query<Bookmark, { id: string; userId: string }>(
|
||||
const bookmarkQuery = db.query<TaggedBookmark, { id: string; userId: string }>(
|
||||
"SELECT id, title, url FROM bookmarks WHERE id = $id AND user_id = $userId",
|
||||
)
|
||||
const bookmark = bookmarkQuery.get({ id, userId: user.id })
|
||||
@@ -60,7 +61,7 @@ function findBookmark(id: string, user: User): Bookmark | null {
|
||||
return null
|
||||
}
|
||||
|
||||
const tagsQuery = db.query<BookmarkTag, { bookmarkId: string }>(`
|
||||
const tagsQuery = db.query<Tag, { bookmarkId: string }>(`
|
||||
SELECT tags.id, tags.name FROM tags
|
||||
INNER JOIN bookmark_tags
|
||||
ON bookmark_tags.tag_id = tags.id AND bookmark_tags.bookmark_id = $bookmarkId
|
||||
@@ -72,6 +73,15 @@ function findBookmark(id: string, user: User): Bookmark | null {
|
||||
return bookmark
|
||||
}
|
||||
|
||||
function updateBookmarkTitle(bookmark: Bookmark, newTitle: string, user: User) {
|
||||
const query = db.query("UPDATE bookmarks SET title = $title WHERE id = $id AND user_id = $userId")
|
||||
query.run({
|
||||
title: newTitle,
|
||||
id: bookmark.id,
|
||||
userId: user.id,
|
||||
})
|
||||
}
|
||||
|
||||
function deleteBookmark(id: BookmarkId, user: User) {
|
||||
db.query("DELETE FROM bookmarks WHERE user_id = $userId AND id = $id").run({
|
||||
id,
|
||||
@@ -141,9 +151,9 @@ async function cacheContent(url: string): Promise<CachedContent | null> {
|
||||
throw new UnsupportedLink()
|
||||
}
|
||||
|
||||
function assignTagsToBookmark(tags: BookmarkTag[], bookmark: Bookmark) {
|
||||
function assignTagsToBookmark(tags: Tag[], bookmark: Bookmark) {
|
||||
const query = db.query(`
|
||||
INSERT INTO bookmark_tags (tag_id, bookmark_id)
|
||||
INSERT OR IGNORE INTO bookmark_tags (tag_id, bookmark_id)
|
||||
VALUES ${Array(tags.length).fill("(?,?)").join(",")}
|
||||
`)
|
||||
|
||||
@@ -156,8 +166,8 @@ function assignTagsToBookmark(tags: BookmarkTag[], bookmark: Bookmark) {
|
||||
query.run(...args)
|
||||
}
|
||||
|
||||
function findBookmarkTags(bookmark: Bookmark): BookmarkTag[] {
|
||||
const query = db.query<BookmarkTag, { bookmarkId: string }>(`
|
||||
function findBookmarkTags(bookmark: Bookmark): Tag[] {
|
||||
const query = db.query<Tag, { bookmarkId: string }>(`
|
||||
SELECT tags.name as name, tags.id as id FROM bookmark_tags
|
||||
INNER JOIN tags
|
||||
ON tags.id = bookmark_tags.tag_id
|
||||
@@ -167,14 +177,40 @@ function findBookmarkTags(bookmark: Bookmark): BookmarkTag[] {
|
||||
return tags
|
||||
}
|
||||
|
||||
function updateBookmarkTags(bookmark: Bookmark, tagNames: string[], user: User) {
|
||||
const tags = findTagsByNames(tagNames, user)
|
||||
const existingTagNames = new Set<string>()
|
||||
for (const tag of tags) {
|
||||
existingTagNames.add(tag.name)
|
||||
}
|
||||
|
||||
const newTagNames: string[] = []
|
||||
for (const name of tagNames) {
|
||||
if (!existingTagNames.has(name)) {
|
||||
newTagNames.push(name)
|
||||
}
|
||||
}
|
||||
|
||||
if (newTagNames.length > 0) {
|
||||
const newTags = insertTags(newTagNames, user)
|
||||
tags.push(...newTags)
|
||||
}
|
||||
|
||||
assignTagsToBookmark(tags, bookmark)
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
export {
|
||||
insertDemoBookmarks,
|
||||
insertBookmark,
|
||||
updateBookmarkTitle,
|
||||
deleteBookmark,
|
||||
findBookmark,
|
||||
findBookmarkCachedContent,
|
||||
cacheContent,
|
||||
assignTagsToBookmark,
|
||||
findBookmarkTags,
|
||||
updateBookmarkTags,
|
||||
}
|
||||
export { LinkUnreachable, UnsupportedLink }
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { Bookmark } from "@markone/core/bookmark"
|
||||
import type { TaggedBookmark } from "@markone/core"
|
||||
|
||||
const DEMO_BOOKMARKS: Bookmark[] = [
|
||||
const DEMO_BOOKMARKS: TaggedBookmark[] = [
|
||||
{
|
||||
id: "01HYN4G66K0000000000000000",
|
||||
title: "Google",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { Bookmark, BookmarkTag } from "@markone/core/bookmark"
|
||||
import type { Bookmark, Tag, TaggedBookmark } from "@markone/core"
|
||||
import { DEMO_USER } from "@markone/core/user"
|
||||
import { type } from "arktype"
|
||||
import { ulid } from "ulid"
|
||||
@@ -15,8 +15,11 @@ import {
|
||||
findBookmarkCachedContent,
|
||||
findBookmarkTags,
|
||||
insertBookmark,
|
||||
updateBookmarkTags,
|
||||
updateBookmarkTitle,
|
||||
} from "./bookmark.ts"
|
||||
import { insertTags } from "./tag.ts"
|
||||
|
||||
import { insertTags } from "~/tag/tag.js"
|
||||
|
||||
const BOOKMARK_PAGINATION_LIMIT = 100
|
||||
|
||||
@@ -33,6 +36,11 @@ const AddBookmarkRequestBody = type({
|
||||
"force?": "boolean",
|
||||
})
|
||||
|
||||
const UpdateBookmarkRequestBody = type({
|
||||
"title?": "string",
|
||||
"tags?": "string[]",
|
||||
})
|
||||
|
||||
const AddTagRequestBody = type({
|
||||
name: "string",
|
||||
})
|
||||
@@ -93,7 +101,6 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
||||
if (user.id !== DEMO_USER.id) {
|
||||
const body = AddBookmarkRequestBody(await request.json())
|
||||
if (body instanceof type.errors) {
|
||||
console.log(body)
|
||||
throw new HttpError(400)
|
||||
}
|
||||
|
||||
@@ -118,7 +125,7 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
||||
}
|
||||
}
|
||||
|
||||
const bookmark: Bookmark = {
|
||||
const bookmark: TaggedBookmark = {
|
||||
id: ulid(),
|
||||
title: "",
|
||||
url: body.url,
|
||||
@@ -134,7 +141,7 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
||||
insertBookmark(bookmark, cachedContent, user)
|
||||
|
||||
if (tagNames.size > 0) {
|
||||
const tagQuery = db.query<Partial<BookmarkTag>, string[]>(`
|
||||
const tagQuery = db.query<Partial<Tag>, string[]>(`
|
||||
SELECT id, name FROM tags
|
||||
WHERE user_id = ? AND name IN (${Array(tagNames.size).fill("?").join(",")})
|
||||
`)
|
||||
@@ -142,7 +149,7 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.id && tag.name) {
|
||||
bookmark.tags.push(tag as BookmarkTag)
|
||||
bookmark.tags.push(tag as Tag)
|
||||
tagNames.delete(tag.name)
|
||||
}
|
||||
}
|
||||
@@ -190,7 +197,7 @@ async function fetchBookmark(request: Bun.BunRequest<"/api/bookmarks/:id">, user
|
||||
}
|
||||
|
||||
async function listUserTags(request: Bun.BunRequest<"/api/tags">, user: User) {
|
||||
const query = db.query<BookmarkTag, { id: string }>("SELECT id, name FROM tags WHERE user_id = $id")
|
||||
const query = db.query<Tag, { id: string }>("SELECT id, name FROM tags WHERE user_id = $id")
|
||||
const tags = query.all({ id: user.id })
|
||||
return Response.json(tags, { status: 200 })
|
||||
}
|
||||
@@ -208,7 +215,7 @@ async function createUserTag(request: Bun.BunRequest<"/api/tags">, user: User) {
|
||||
throw new HttpError(400)
|
||||
}
|
||||
|
||||
const tag: BookmarkTag = {
|
||||
const tag: Tag = {
|
||||
id: ulid(),
|
||||
name: body.name,
|
||||
}
|
||||
@@ -230,6 +237,44 @@ async function listBookmarkTags(request: Bun.BunRequest<"/api/bookmarks/:id/tags
|
||||
return Response.json(tags, { status: 200 })
|
||||
}
|
||||
|
||||
async function updateBookmark(request: Bun.BunRequest<"/api/bookmarks/:id">, user: User) {
|
||||
const bodyJson = await request.json().catch(() => {
|
||||
throw new HttpError(400)
|
||||
})
|
||||
const body = UpdateBookmarkRequestBody(bodyJson)
|
||||
if (body instanceof type.errors) {
|
||||
throw new HttpError(400)
|
||||
}
|
||||
if (!body.title || !body.tags) {
|
||||
return Response.json(undefined, { status: 204 })
|
||||
}
|
||||
|
||||
const bookmark = findBookmark(request.params.id, user)
|
||||
if (!bookmark) {
|
||||
throw new HttpError(404)
|
||||
}
|
||||
|
||||
if (body.title) {
|
||||
updateBookmarkTitle(bookmark, body.title, user)
|
||||
bookmark.title = body.title
|
||||
}
|
||||
|
||||
if (body.tags) {
|
||||
const taggedBookmark = bookmark as TaggedBookmark
|
||||
|
||||
for (const tag of body.tags) {
|
||||
if (tag.length === 0 || /[\s#]/g.test(tag)) {
|
||||
throw new HttpError(400, "InvalidTag", "Tags cannot contain '#' or whitespaces")
|
||||
}
|
||||
}
|
||||
taggedBookmark.tags = updateBookmarkTags(bookmark, body.tags, user)
|
||||
|
||||
return Response.json(taggedBookmark, { status: 200 })
|
||||
}
|
||||
|
||||
return Response.json(bookmark, { status: 200 })
|
||||
}
|
||||
|
||||
export {
|
||||
addBookmark,
|
||||
fetchBookmark,
|
||||
@@ -238,4 +283,5 @@ export {
|
||||
listUserTags,
|
||||
createUserTag,
|
||||
listBookmarkTags,
|
||||
updateBookmark,
|
||||
}
|
||||
|
@@ -1,29 +0,0 @@
|
||||
import type { BookmarkTag } from "@markone/core/bookmark"
|
||||
import type { User } from "@markone/core/user"
|
||||
import { ulid } from "ulid"
|
||||
import { db } from "~/database.ts"
|
||||
|
||||
function insertTags(names: string[], user: User): BookmarkTag[] {
|
||||
console.log("======== insert tags", names)
|
||||
const insertTags = db.query(`
|
||||
INSERT INTO tags (id, name, user_id)
|
||||
VALUES ${Array(names.length).fill("(?,?,?)").join(",")}
|
||||
`)
|
||||
|
||||
const args: Parameters<typeof insertTags.run> = []
|
||||
const tags: BookmarkTag[] = []
|
||||
for (const name of names) {
|
||||
const tag: BookmarkTag = {
|
||||
id: ulid(),
|
||||
name,
|
||||
}
|
||||
args.push(tag.id, tag.name, user.id)
|
||||
tags.push(tag)
|
||||
}
|
||||
|
||||
insertTags.run(...args)
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
export { insertTags }
|
||||
|
Reference in New Issue
Block a user