diff --git a/cmd/serve/web-handler-api.go b/cmd/serve/web-handler-api.go
index 02d5e1ec..54a75c36 100644
--- a/cmd/serve/web-handler-api.go
+++ b/cmd/serve/web-handler-api.go
@@ -182,98 +182,48 @@ func (h *webHandler) apiUpdateBookmark(w http.ResponseWriter, r *http.Request, p
err := h.checkAPIToken(r)
checkError(err)
- // Get url queries
- _, dontOverwrite := r.URL.Query()["dont-overwrite"]
-
// Decode request
request := model.Bookmark{}
err = json.NewDecoder(r.Body).Decode(&request)
checkError(err)
- // Make sure URL valid
- parsedURL, err := nurl.ParseRequestURI(request.URL)
- if err != nil || parsedURL.Host == "" {
- panic(fmt.Errorf("URL is not valid"))
+ // Validate input
+ if request.Title != "" {
+ panic(fmt.Errorf("Title must not empty"))
}
- clearUTMParams(parsedURL)
// Get existing bookmark from database
bookmarks, err := h.db.GetBookmarks(true, fmt.Sprintf("%d", request.ID))
checkError(err)
-
if len(bookmarks) == 0 {
panic(fmt.Errorf("No bookmark with matching index"))
}
+ // Set new bookmark data
book := bookmarks[0]
- book.URL = parsedURL.String()
+ book.Title = request.Title
+ book.Excerpt = request.Excerpt
- // Fetch data from internet
- article, err := readability.Parse(parsedURL, 10*time.Second)
- checkError(err)
-
- book.ImageURL = article.Meta.Image
- book.Author = article.Meta.Author
- book.MinReadTime = article.Meta.MinReadTime
- book.MaxReadTime = article.Meta.MaxReadTime
- book.Content = article.Content
- book.HTML = article.RawContent
-
- if !dontOverwrite {
- book.Title = article.Meta.Title
- book.Excerpt = article.Meta.Excerpt
+ // Set new tags
+ for i := range book.Tags {
+ book.Tags[i].Deleted = true
}
- // Check if user submit his own title or excerpt
- if request.Title != "" {
- book.Title = request.Title
- }
-
- if request.Excerpt != "" {
- book.Excerpt = request.Excerpt
- }
-
- // Make sure title is not empty
- if book.Title == "" {
- book.Title = "Untitled"
- }
-
- // Create new tags from request
- addedTags := make(map[string]struct{})
- deletedTags := make(map[string]struct{})
- for _, tag := range request.Tags {
- tagName := strings.ToLower(tag.Name)
- tagName = strings.TrimSpace(tagName)
-
- if strings.HasPrefix(tagName, "-") {
- tagName = strings.TrimPrefix(tagName, "-")
- deletedTags[tagName] = struct{}{}
- } else {
- addedTags[tagName] = struct{}{}
- }
- }
-
- newTags := []model.Tag{}
- for _, tag := range book.Tags {
- if _, isDeleted := deletedTags[tag.Name]; isDeleted {
- tag.Deleted = true
+ for _, newTag := range request.Tags {
+ for i, oldTag := range book.Tags {
+ if newTag.Name == oldTag.Name {
+ newTag.ID = oldTag.ID
+ book.Tags[i].Deleted = false
+ break
+ }
}
- if _, alreadyExist := addedTags[tag.Name]; alreadyExist {
- delete(addedTags, tag.Name)
+ if newTag.ID == 0 {
+ book.Tags = append(book.Tags, newTag)
}
-
- newTags = append(newTags, tag)
}
- for tag := range addedTags {
- newTags = append(newTags, model.Tag{Name: tag})
- }
-
- book.Tags = newTags
-
// Update database
- book.Modified = time.Now().UTC().Format("2006-01-02 15:04:05")
res, err := h.db.UpdateBookmarks(book)
checkError(err)
diff --git a/database/sqlite.go b/database/sqlite.go
index 833a7afa..73a77416 100644
--- a/database/sqlite.go
+++ b/database/sqlite.go
@@ -408,6 +408,7 @@ func (db *SQLiteDatabase) UpdateBookmarks(bookmarks ...model.Bookmark) (result [
result = []model.Bookmark{}
for _, book := range bookmarks {
+ // Save bookmark
stmtUpdateBookmark.MustExec(
book.URL,
book.Title,
@@ -419,12 +420,14 @@ func (db *SQLiteDatabase) UpdateBookmarks(bookmarks ...model.Bookmark) (result [
book.Modified,
book.ID)
+ // Save bookmark content
stmtUpdateBookmarkContent.MustExec(
book.Title,
book.Content,
book.HTML,
book.ID)
+ // Save bookmark tags
newTags := []model.Tag{}
for _, tag := range book.Tags {
if tag.Deleted {
diff --git a/view/css/yla-dialog.css b/view/css/yla-dialog.css
index 198bf497..189b8f59 100644
--- a/view/css/yla-dialog.css
+++ b/view/css/yla-dialog.css
@@ -1 +1 @@
-.header-link{border-right:1px solid #E5E5E5;color:#232323;cursor:pointer;font-size:.9em;line-height:60px;overflow:hidden;padding:0 16px}.header-link:hover{color:#F44336}.full-overlay{position:fixed;z-index:101;display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column;background-color:rgba(0,0,0,0.5);top:0;left:0;right:0;bottom:0;overflow:hidden;-webkit-box-pack:center;justify-content:center;padding:32px}.yla-dialog__overlay{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;min-width:0;min-height:0;overflow:hidden;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:10001;background-color:rgba(0,0,0,0.6);padding:32px}.yla-dialog__overlay .yla-dialog{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;min-width:400px;min-height:0;max-height:100%;overflow:hidden;background-color:#FFF;font-size:16px}.yla-dialog__overlay .yla-dialog>.yla-dialog__header{display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row nowrap;padding:16px;min-height:0;background-color:#353535;color:#EEE;flex-shrink:0}.yla-dialog__overlay .yla-dialog>.yla-dialog__header>p{-webkit-box-flex:1;flex:1 0;font-weight:600;font-size:1em;text-transform:uppercase}.yla-dialog__overlay .yla-dialog>.yla-dialog__header>a:hover{color:#F44336}.yla-dialog__overlay .yla-dialog>.yla-dialog__body{padding:16px;display:grid;max-height:100%;min-height:80px;min-width:0;overflow:auto;grid-template-columns:max-content 1fr;-webkit-box-align:baseline;align-items:baseline;grid-gap:16px}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>.yla-dialog__content{grid-column-start:1;grid-column-end:3;align-self:baseline;font-size:.9em}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>input,.yla-dialog__overlay .yla-dialog>.yla-dialog__body>textarea{color:#232323;padding:8px;border:1px solid #E5E5E5;font-size:.9em;min-height:37px}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>.suggestion{position:absolute;display:block;padding:8px;background-color:#EEE;border:1px solid #E5E5E5}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer{padding:16px;display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row wrap;-webkit-box-pack:end;justify-content:flex-end;border-top:1px solid #E5E5E5}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>a{text-transform:uppercase;padding:0 8px;font-size:.9em;font-weight:600;border-bottom:1px dashed transparent}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>a:hover{color:#F44336}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>a:focus{outline:none;color:#F44336;border-bottom:1px dashed #F44336}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>i{width:19px;line-height:19px;text-align:center}
\ No newline at end of file
+.header-link{border-right:1px solid #E5E5E5;color:#232323;cursor:pointer;font-size:.9em;line-height:60px;overflow:hidden;padding:0 16px}.header-link:hover{color:#F44336}.full-overlay{position:fixed;z-index:101;display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column;background-color:rgba(0,0,0,0.5);top:0;left:0;right:0;bottom:0;overflow:hidden;-webkit-box-pack:center;justify-content:center;padding:32px}.yla-dialog__overlay{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;min-width:0;min-height:0;overflow:hidden;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:10001;background-color:rgba(0,0,0,0.6);padding:32px}.yla-dialog__overlay .yla-dialog{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;min-width:400px;min-height:0;max-height:100%;overflow:hidden;background-color:#FFF;font-size:16px}.yla-dialog__overlay .yla-dialog>.yla-dialog__header{display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row nowrap;padding:16px;min-height:0;background-color:#353535;color:#EEE;flex-shrink:0}.yla-dialog__overlay .yla-dialog>.yla-dialog__header>p{-webkit-box-flex:1;flex:1 0;font-weight:600;font-size:1em;text-transform:uppercase}.yla-dialog__overlay .yla-dialog>.yla-dialog__header>a:hover{color:#F44336}.yla-dialog__overlay .yla-dialog>.yla-dialog__body{padding:16px;display:grid;max-height:100%;min-height:80px;min-width:0;overflow:auto;grid-template-columns:max-content 1fr;-webkit-box-align:baseline;align-items:baseline;grid-gap:16px}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>p.yla-dialog__content{grid-column-start:1;grid-column-end:3;align-self:baseline;font-size:.9em}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>p.label{padding:8px 0;align-self:stretch;font-size:.9em}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>input,.yla-dialog__overlay .yla-dialog>.yla-dialog__body>textarea{color:#232323;padding:8px;border:1px solid #E5E5E5;font-size:.9em;min-height:37px}.yla-dialog__overlay .yla-dialog>.yla-dialog__body>.suggestion{position:absolute;display:block;padding:8px;background-color:#EEE;border:1px solid #E5E5E5}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer{padding:16px;display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row wrap;-webkit-box-pack:end;justify-content:flex-end;border-top:1px solid #E5E5E5}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>a{text-transform:uppercase;padding:0 8px;font-size:.9em;font-weight:600;border-bottom:1px dashed transparent}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>a:hover{color:#F44336}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>a:focus{outline:none;color:#F44336;border-bottom:1px dashed #F44336}.yla-dialog__overlay .yla-dialog>.yla-dialog__footer>i{width:19px;line-height:19px;text-align:center}
\ No newline at end of file
diff --git a/view/index.html b/view/index.html
index a1c5165d..005ee600 100644
--- a/view/index.html
+++ b/view/index.html
@@ -72,12 +72,9 @@
{{getHostname(book.url)}}
-
+
-
-
-
@@ -178,6 +175,63 @@
}
});
},
+ showDialogEdit(idx) {
+ var book = JSON.parse(JSON.stringify(this.bookmarks[idx])),
+ strTags = book.tags.map(tag => tag.name).join(' ');
+
+ this.showDialog({
+ title: 'Edit Bookmark',
+ content: 'Edit the bookmark\'s data',
+ showLabel: true,
+ fields: [{
+ name: 'tags',
+ label: 'Tags',
+ value: strTags,
+ }, {
+ name: 'title',
+ label: 'Title',
+ value: book.title,
+ }, {
+ name: 'excerpt',
+ label: 'Excerpt',
+ type: 'area',
+ value: book.excerpt,
+ }],
+ mainText: 'OK',
+ secondText: 'Cancel',
+ mainClick: (data) => {
+ // Validate input
+ if (data.title.trim() === '') return;
+
+ // Prepare tags
+ var tags = data.tags
+ .toLowerCase()
+ .split(/\s+/g).map(tag => {
+ return {
+ name: tag
+ };
+ });
+
+ // Set new data
+ book.title = data.title.trim();
+ book.excerpt = data.excerpt.trim();
+ book.tags = tags;
+
+ // Send data
+ this.dialog.loading = true;
+ rest.put('/api/bookmarks', book)
+ .then((response) => {
+ this.dialog.loading = false;
+ this.dialog.visible = false;
+ this.bookmarks.splice(idx, 1, response.data);
+ })
+ .catch((error) => {
+ var errorMsg = (error.response ? error.response.data : error.message).trim();
+ this.showErrorDialog(errorMsg);
+ });
+ }
+ });
+ },
showDialogDelete(indices) {
// Check and prepare indices
if (!(indices instanceof Array)) return;
diff --git a/view/js/component/yla-dialog.js b/view/js/component/yla-dialog.js
index 78dbdb12..836eda6b 100644
--- a/view/js/component/yla-dialog.js
+++ b/view/js/component/yla-dialog.js
@@ -10,7 +10,7 @@ var YlaDialog = function () {
{{content}}
- {{field.label}} :
+ {{field.label}} :