mirror of
https://github.com/go-shiori/shiori.git
synced 2025-02-22 15:06:04 +08:00
Add edit function in web view
This commit is contained in:
parent
59208fb377
commit
5a2fed1888
6 changed files with 221 additions and 135 deletions
|
@ -88,5 +88,6 @@ func addBookmark(url, title, excerpt string, tags []string, offline bool) (book
|
||||||
return book, err
|
return book, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bookmark.Modified = time.Now().UTC().Format("2006-01-02 15:04:05")
|
||||||
return bookmark, nil
|
return bookmark, nil
|
||||||
}
|
}
|
||||||
|
|
23
cmd/serve.go
23
cmd/serve.go
|
@ -28,6 +28,7 @@ var (
|
||||||
router.GET("/webfonts/*filepath", serveFiles)
|
router.GET("/webfonts/*filepath", serveFiles)
|
||||||
router.GET("/api/bookmarks", apiGetBookmarks)
|
router.GET("/api/bookmarks", apiGetBookmarks)
|
||||||
router.POST("/api/bookmarks", apiInsertBookmarks)
|
router.POST("/api/bookmarks", apiInsertBookmarks)
|
||||||
|
router.PUT("/api/bookmarks", apiUpdateBookmarks)
|
||||||
|
|
||||||
// Route for panic
|
// Route for panic
|
||||||
router.PanicHandler = func(w http.ResponseWriter, r *http.Request, arg interface{}) {
|
router.PanicHandler = func(w http.ResponseWriter, r *http.Request, arg interface{}) {
|
||||||
|
@ -80,3 +81,25 @@ func apiInsertBookmarks(w http.ResponseWriter, r *http.Request, ps httprouter.Pa
|
||||||
err = json.NewEncoder(w).Encode(&book)
|
err = json.NewEncoder(w).Encode(&book)
|
||||||
checkError(err)
|
checkError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiUpdateBookmarks(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
// Decode request
|
||||||
|
request := model.Bookmark{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&request)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
|
// Convert tags and ID
|
||||||
|
id := []string{fmt.Sprintf("%d", request.ID)}
|
||||||
|
tags := make([]string, len(request.Tags))
|
||||||
|
for i, tag := range request.Tags {
|
||||||
|
tags[i] = tag.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update bookmark
|
||||||
|
bookmarks, err := updateBookmarks(id, request.URL, request.Title, request.Excerpt, tags, false)
|
||||||
|
checkError(err)
|
||||||
|
|
||||||
|
// Return new saved result
|
||||||
|
err = json.NewEncoder(w).Encode(&bookmarks[0])
|
||||||
|
checkError(err)
|
||||||
|
}
|
||||||
|
|
|
@ -58,16 +58,37 @@ var (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read bookmarks from database
|
// Update bookmarks
|
||||||
bookmarks, err := DB.GetBookmarks(db.GetBookmarksOptions{WithContents: true}, args...)
|
bookmarks, err := updateBookmarks(args, url, title, excerpt, tags, offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cError.Println(err)
|
cError.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printBookmark(bookmarks...)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
updateCmd.Flags().StringP("url", "u", "", "New URL for this bookmark.")
|
||||||
|
updateCmd.Flags().StringP("title", "i", "", "New title for this bookmark.")
|
||||||
|
updateCmd.Flags().StringP("excerpt", "e", "", "New excerpt for this bookmark.")
|
||||||
|
updateCmd.Flags().StringSliceP("tags", "t", []string{}, "Comma-separated tags for this bookmark.")
|
||||||
|
updateCmd.Flags().BoolP("offline", "o", false, "Update bookmark without fetching data from internet.")
|
||||||
|
updateCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and update ALL bookmarks")
|
||||||
|
rootCmd.AddCommand(updateCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBookmarks(indices []string, url, title, excerpt string, tags []string, offline bool) ([]model.Bookmark, error) {
|
||||||
|
// Read bookmarks from database
|
||||||
|
bookmarks, err := DB.GetBookmarks(db.GetBookmarksOptions{WithContents: true}, indices...)
|
||||||
|
if err != nil {
|
||||||
|
return []model.Bookmark{}, err
|
||||||
|
}
|
||||||
|
|
||||||
if len(bookmarks) == 0 {
|
if len(bookmarks) == 0 {
|
||||||
cError.Println("No matching index found")
|
return []model.Bookmark{}, fmt.Errorf("No matching index found")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if url != "" && len(bookmarks) == 1 {
|
if url != "" && len(bookmarks) == 1 {
|
||||||
|
@ -157,21 +178,8 @@ var (
|
||||||
|
|
||||||
err = DB.UpdateBookmarks(bookmarks)
|
err = DB.UpdateBookmarks(bookmarks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cError.Println("Failed to update bookmarks:", err)
|
return []model.Bookmark{}, fmt.Errorf("Failed to update bookmarks: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printBookmark(bookmarks...)
|
return bookmarks, nil
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
updateCmd.Flags().StringP("url", "u", "", "New URL for this bookmark.")
|
|
||||||
updateCmd.Flags().StringP("title", "i", "", "New title for this bookmark.")
|
|
||||||
updateCmd.Flags().StringP("excerpt", "e", "", "New excerpt for this bookmark.")
|
|
||||||
updateCmd.Flags().StringSliceP("tags", "t", []string{}, "Comma-separated tags for this bookmark.")
|
|
||||||
updateCmd.Flags().BoolP("offline", "o", false, "Update bookmark without fetching data from internet.")
|
|
||||||
updateCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and update ALL bookmarks")
|
|
||||||
rootCmd.AddCommand(updateCmd)
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
107
view/index.html
107
view/index.html
|
@ -26,19 +26,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div id="new-bookmark">
|
<div id="input-bookmark">
|
||||||
<input type="text" v-model="newBookmark.url" placeholder="URL for the bookmark">
|
<p v-if="inputBookmark.url !== ''">{{inputBookmark.id === -1 ? 'New bookmark' : 'Edit bookmark'}}</p>
|
||||||
<template v-if="newBookmark.url !== ''">
|
<input type="text" ref="inputURL" v-model="inputBookmark.url" placeholder="URL for the bookmark">
|
||||||
<input type="text" v-model="newBookmark.title" placeholder="Custom bookmark title (optional)">
|
<template v-if="inputBookmark.url !== ''">
|
||||||
<input type="text" v-model="newBookmark.tags" placeholder="Space separated tags for this bookmark (optional)">
|
<input type="text" v-model="inputBookmark.title" placeholder="Custom bookmark title (optional)">
|
||||||
<textarea name="excerpt" v-model="newBookmark.excerpt" placeholder="Excerpt for this bookmark (optional)"></textarea>
|
<input type="text" v-model="inputBookmark.tags" placeholder="Space separated tags for this bookmark (optional)">
|
||||||
|
<textarea name="excerpt" v-model="inputBookmark.excerpt" placeholder="Excerpt for this bookmark (optional)"></textarea>
|
||||||
<div class="button-area">
|
<div class="button-area">
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<a v-if="newBookmark.loading">
|
<a v-if="inputBookmark.loading">
|
||||||
<i class="fas fa-fw fa-spinner fa-spin"></i>
|
<i class="fas fa-fw fa-spinner fa-spin"></i>
|
||||||
</a>
|
</a>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a class="button">Cancel</a>
|
<a class="button" @click="clearInputBookmark">Cancel</a>
|
||||||
<a class="button" @click="saveBookmark">Done</a>
|
<a class="button" @click="saveBookmark">Done</a>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="grid">
|
<div id="grid">
|
||||||
<div v-for="column in gridColumns" class="column">
|
<div v-for="column in gridColumns" class="column">
|
||||||
<div v-for="item in column" class="bookmark">
|
<div v-for="item in column" class="bookmark" :ref="'bookmark-'+item.index">
|
||||||
<a href="#" class="checkbox">
|
<a href="#" class="checkbox">
|
||||||
<i class="fas fa-check"></i>
|
<i class="fas fa-check"></i>
|
||||||
</a>
|
</a>
|
||||||
|
@ -60,7 +61,7 @@
|
||||||
<a v-for="tag in item.tags" href="#">{{tag.name}}</a>
|
<a v-for="tag in item.tags" href="#">{{tag.name}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="bookmark-menu">
|
<div class="bookmark-menu">
|
||||||
<a href="#">
|
<a href="#" @click="editBookmark(item.index)">
|
||||||
<i class="fas fa-pencil-alt"></i>
|
<i class="fas fa-pencil-alt"></i>
|
||||||
</a>
|
</a>
|
||||||
<a href="#">
|
<a href="#">
|
||||||
|
@ -89,7 +90,9 @@
|
||||||
windowWidth: 0,
|
windowWidth: 0,
|
||||||
loading: true,
|
loading: true,
|
||||||
bookmarks: [],
|
bookmarks: [],
|
||||||
newBookmark: {
|
inputBookmark: {
|
||||||
|
index: -1,
|
||||||
|
id: -1,
|
||||||
url: "",
|
url: "",
|
||||||
title: "",
|
title: "",
|
||||||
tags: "",
|
tags: "",
|
||||||
|
@ -111,12 +114,12 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
saveBookmark: function () {
|
saveBookmark: function () {
|
||||||
if (this.newBookmark.loading) return;
|
if (this.inputBookmark.loading) return;
|
||||||
this.newBookmark.loading = true;
|
this.inputBookmark.loading = true;
|
||||||
|
|
||||||
if (this.newBookmark.url === "") return;
|
if (this.inputBookmark.url === "") return;
|
||||||
|
|
||||||
var tags = this.newBookmark.tags.replace(/\s+/g, ""),
|
var tags = this.inputBookmark.tags.replace(/\s+/g, " "),
|
||||||
listTag = tags === "" ? [] : listTag = tags.split(/\s+/g),
|
listTag = tags === "" ? [] : listTag = tags.split(/\s+/g),
|
||||||
finalTag = [];
|
finalTag = [];
|
||||||
|
|
||||||
|
@ -126,29 +129,64 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.post('/api/bookmarks', {
|
instance.request({
|
||||||
url: this.newBookmark.url,
|
method: this.inputBookmark.id === -1 ? 'post' : 'put',
|
||||||
title: this.newBookmark.title,
|
url: '/api/bookmarks',
|
||||||
excerpt: this.newBookmark.excerpt,
|
timeout: 15000,
|
||||||
|
data: {
|
||||||
|
id: this.inputBookmark.id,
|
||||||
|
url: this.inputBookmark.url,
|
||||||
|
title: this.inputBookmark.title,
|
||||||
|
excerpt: this.inputBookmark.excerpt,
|
||||||
tags: finalTag
|
tags: finalTag
|
||||||
}, {
|
}
|
||||||
timeout: 15000
|
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
app.clearNewBookmark();
|
var idx = app.inputBookmark.index;
|
||||||
app.bookmarks.unshift(response.data);
|
|
||||||
|
if (idx === -1) app.bookmarks.unshift(response.data);
|
||||||
|
else app.bookmarks.splice(idx, 1, response.data);
|
||||||
|
|
||||||
|
app.clearInputBookmark();
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
app.clearNewBookmark();
|
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
clearNewBookmark: function () {
|
clearInputBookmark: function () {
|
||||||
this.newBookmark.url = "";
|
var idx = this.inputBookmark.index;
|
||||||
this.newBookmark.title = "";
|
|
||||||
this.newBookmark.tags = "";
|
this.inputBookmark.index = -1;
|
||||||
this.newBookmark.excerpt = "";
|
this.inputBookmark.id = -1;
|
||||||
this.newBookmark.loading = false;
|
this.inputBookmark.url = "";
|
||||||
|
this.inputBookmark.title = "";
|
||||||
|
this.inputBookmark.tags = "";
|
||||||
|
this.inputBookmark.excerpt = "";
|
||||||
|
this.inputBookmark.loading = false;
|
||||||
|
|
||||||
|
if (idx !== -1) app.$nextTick(function () {
|
||||||
|
var bookmarkItem = app.$refs['bookmark-' + idx];
|
||||||
|
bookmarkItem[0].scrollIntoView();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
editBookmark: function (idx) {
|
||||||
|
var bookmark = this.bookmarks[idx],
|
||||||
|
tags = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < bookmark.tags.length; i++) {
|
||||||
|
tags.push(bookmark.tags[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inputBookmark.index = idx;
|
||||||
|
this.inputBookmark.id = bookmark.id;
|
||||||
|
this.inputBookmark.url = bookmark.url;
|
||||||
|
this.inputBookmark.title = bookmark.title;
|
||||||
|
this.inputBookmark.tags = tags.join(" ");
|
||||||
|
this.inputBookmark.excerpt = bookmark.excerpt;
|
||||||
|
|
||||||
|
this.$nextTick(function () {
|
||||||
|
app.$refs.inputURL.focus();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
bookmarkTime: function (book) {
|
bookmarkTime: function (book) {
|
||||||
var time = book.modified,
|
var time = book.modified,
|
||||||
|
@ -199,6 +237,7 @@
|
||||||
|
|
||||||
for (var i = 0; i < this.bookmarks.length; i++) {
|
for (var i = 0; i < this.bookmarks.length; i++) {
|
||||||
var bookmark = this.bookmarks[i];
|
var bookmark = this.bookmarks[i];
|
||||||
|
bookmark.index = i;
|
||||||
finalContent[currentColumn].push(bookmark);
|
finalContent[currentColumn].push(bookmark);
|
||||||
|
|
||||||
currentColumn += 1;
|
currentColumn += 1;
|
||||||
|
@ -208,6 +247,14 @@
|
||||||
return finalContent;
|
return finalContent;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
'inputBookmark.url': function (newURL) {
|
||||||
|
if (newURL === "") this.clearInputBookmark();
|
||||||
|
else this.$nextTick(function () {
|
||||||
|
app.$refs.inputURL.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
this.windowWidth = window.innerWidth;
|
this.windowWidth = window.innerWidth;
|
||||||
window.addEventListener('resize', function () {
|
window.addEventListener('resize', function () {
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
margin-top: @headerHeight;
|
margin-top: @headerHeight;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
#new-bookmark {
|
#input-bookmark {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -89,6 +89,12 @@
|
||||||
margin: 32px 8px 20px;
|
margin: 32px 8px 20px;
|
||||||
background-color: @headerInputBg;
|
background-color: @headerInputBg;
|
||||||
outline: 1px solid @border;
|
outline: 1px solid @border;
|
||||||
|
>p {
|
||||||
|
color: @fontColor;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
input[type=text],
|
input[type=text],
|
||||||
textarea {
|
textarea {
|
||||||
outline: 1px solid @border;
|
outline: 1px solid @border;
|
||||||
|
@ -177,6 +183,7 @@
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
|
border-bottom: 1px solid @border;
|
||||||
.bookmark-time {
|
.bookmark-time {
|
||||||
color: @fontLightColor;
|
color: @fontLightColor;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
@ -232,15 +239,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.bookmark-excerpt {
|
.bookmark-excerpt {
|
||||||
padding: 16px;
|
padding: 16px 16px 0;
|
||||||
color: @fontColor;
|
color: @fontColor;
|
||||||
border-top: 1px solid @border;
|
|
||||||
}
|
}
|
||||||
.bookmark-tags {
|
.bookmark-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
padding: 0 12px 12px;
|
padding: 12px 12px 0;
|
||||||
margin-top: -4px;
|
margin-bottom: -4px;
|
||||||
a {
|
a {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
@ -258,6 +264,7 @@
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
border-top: 1px solid @border;
|
border-top: 1px solid @border;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
margin-top: 16px;
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: block;
|
||||||
flex: 1 0;
|
flex: 1 0;
|
||||||
|
|
Loading…
Reference in a new issue