mirror of
https://github.com/go-shiori/shiori.git
synced 2025-09-06 13:05:30 +08:00
Add method for renaming tag
This commit is contained in:
parent
690bda85f7
commit
ea73eaff64
6 changed files with 128 additions and 17 deletions
|
@ -61,6 +61,9 @@ type DB interface {
|
|||
// GetTags fetch list of tags and its frequency from database.
|
||||
GetTags() ([]model.Tag, error)
|
||||
|
||||
// RenameTag change the name of a tag.
|
||||
RenameTag(id int, newName string) error
|
||||
|
||||
// CreateNewID creates new id for specified table.
|
||||
CreateNewID(table string) (int, error)
|
||||
}
|
||||
|
|
|
@ -523,6 +523,12 @@ func (db *SQLiteDatabase) GetTags() ([]model.Tag, error) {
|
|||
return tags, nil
|
||||
}
|
||||
|
||||
// RenameTag change the name of a tag.
|
||||
func (db *SQLiteDatabase) RenameTag(id int, newName string) error {
|
||||
_, err := db.Exec(`UPDATE tag SET name = ? WHERE id = ?`, newName, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateNewID creates new ID for specified table
|
||||
func (db *SQLiteDatabase) CreateNewID(table string) (int, error) {
|
||||
var tableID int
|
||||
|
|
|
@ -61,7 +61,7 @@ var template = `
|
|||
<p class="empty-message" v-if="!loading && listIsEmpty">No saved bookmarks yet :(</p>
|
||||
<div class="loading-overlay" v-if="loading"><i class="fas fa-fw fa-spin fa-spinner"></i></div>
|
||||
<custom-dialog id="dialog-tags" v-bind="dialogTags">
|
||||
<a v-for="tag in tags" @click="filterTag(tag.name)">
|
||||
<a v-for="(tag, idx) in tags" @click="tagClicked(idx, tag)">
|
||||
{{tag.name}}<span>{{tag.nBookmarks}}</span>
|
||||
</a>
|
||||
</custom-dialog>
|
||||
|
@ -95,11 +95,24 @@ export default {
|
|||
|
||||
dialogTags: {
|
||||
visible: false,
|
||||
editMode: false,
|
||||
title: 'Existing Tags',
|
||||
mainText: 'OK',
|
||||
secondText: 'Rename Tags',
|
||||
mainClick: () => {
|
||||
this.dialogTags.visible = false;
|
||||
if (this.dialogTags.editMode) {
|
||||
this.dialogTags.editMode = false;
|
||||
} else {
|
||||
this.dialogTags.visible = false;
|
||||
}
|
||||
},
|
||||
secondClick: () => {
|
||||
this.dialogTags.editMode = true;
|
||||
},
|
||||
escPressed: () => {
|
||||
this.dialogTags.visible = false;
|
||||
this.dialogTags.editMode = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -108,6 +121,19 @@ export default {
|
|||
return this.bookmarks.length <= 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"dialogTags.editMode"(editMode) {
|
||||
if (editMode) {
|
||||
this.dialogTags.title = "Rename Tags";
|
||||
this.dialogTags.mainText = "Cancel";
|
||||
this.dialogTags.secondText = "";
|
||||
} else {
|
||||
this.dialogTags.title = "Existing Tags";
|
||||
this.dialogTags.mainText = "OK";
|
||||
this.dialogTags.secondText = "Rename Tags";
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reloadData() {
|
||||
if (this.loading) return;
|
||||
|
@ -223,15 +249,6 @@ export default {
|
|||
this.$refs.bookmarksGrid.scrollTop = 0;
|
||||
this.loadData();
|
||||
},
|
||||
filterTag(tagName) {
|
||||
var rxSpace = /\s+/g,
|
||||
newTag = rxSpace.test(tagName) ? `"#${tagName}"` : `#${tagName}`;
|
||||
|
||||
if (!this.search.includes(newTag)) {
|
||||
this.search += ` ${newTag}`;
|
||||
this.loadData();
|
||||
}
|
||||
},
|
||||
toggleEditMode() {
|
||||
this.selection = [];
|
||||
this.editMode = !this.editMode;
|
||||
|
@ -244,6 +261,23 @@ export default {
|
|||
isSelected(bookId) {
|
||||
return this.selection.findIndex(el => el.id === bookId) > -1;
|
||||
},
|
||||
tagClicked(idx, tag) {
|
||||
if (!this.dialogTags.editMode) {
|
||||
this.filterTag(tag.name);
|
||||
} else {
|
||||
this.dialogTags.visible = false;
|
||||
this.showDialogRenameTag(idx, tag);
|
||||
}
|
||||
},
|
||||
filterTag(tagName) {
|
||||
var rxSpace = /\s+/g,
|
||||
newTag = rxSpace.test(tagName) ? `"#${tagName}"` : `#${tagName}`;
|
||||
|
||||
if (!this.search.includes(newTag)) {
|
||||
this.search += ` ${newTag}`;
|
||||
this.loadData();
|
||||
}
|
||||
},
|
||||
showDialogAdd() {
|
||||
this.showDialog({
|
||||
title: "New Bookmark",
|
||||
|
@ -631,7 +665,55 @@ export default {
|
|||
},
|
||||
showDialogTags() {
|
||||
this.dialogTags.visible = true;
|
||||
}
|
||||
},
|
||||
showDialogRenameTag(idx, tag) {
|
||||
this.showDialog({
|
||||
title: "Rename Tag",
|
||||
content: `Change the name for tag "#${tag.name}"`,
|
||||
fields: [{
|
||||
name: "newName",
|
||||
label: "New tag name",
|
||||
value: tag.name,
|
||||
}],
|
||||
mainText: "OK",
|
||||
secondText: "Cancel",
|
||||
secondClick: () => {
|
||||
this.dialog.visible = false;
|
||||
this.dialogTags.visible = true;
|
||||
},
|
||||
mainClick: (data) => {
|
||||
// Send data
|
||||
tag.name = data.newName;
|
||||
|
||||
this.dialog.loading = true;
|
||||
fetch("/api/tag", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(tag),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw response;
|
||||
return response.json();
|
||||
})
|
||||
.then(() => {
|
||||
this.dialog.loading = false;
|
||||
this.dialog.visible = false;
|
||||
this.dialogTags.visible = true;
|
||||
this.tags.splice(idx, 1, tag);
|
||||
})
|
||||
.catch(err => {
|
||||
this.dialog.loading = false;
|
||||
this.dialogTags.visible = false;
|
||||
this.dialogTags.editMode = false;
|
||||
err.text().then(msg => {
|
||||
this.showErrorDialog(msg, err.status);
|
||||
})
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Prepare history state watcher
|
||||
|
@ -658,8 +740,9 @@ export default {
|
|||
initialPage = url.hash.replace(/^([^?]+).*$/, "$1");
|
||||
|
||||
if (initialPage === "home") {
|
||||
var search = url.hash.replace(/^.*(\?|&)search=([^?&]*).*$/, "$2"),
|
||||
page = url.hash.replace(/^.*(\?|&)page=(\d+).*$/, "$2");
|
||||
var urlHash = url.hash.replace(initialPage, ""),
|
||||
search = urlHash.replace(/^.*(\?|&)search=([^?&]*).*$/, "$2"),
|
||||
page = urlHash.replace(/^.*(\?|&)page=(\d+).*$/, "$2");
|
||||
|
||||
this.search = decodeURIComponent(search) || "";
|
||||
this.page = parseInt(page) || 1;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -194,6 +194,24 @@ func (h *handler) apiGetTags(w http.ResponseWriter, r *http.Request, ps httprout
|
|||
checkError(err)
|
||||
}
|
||||
|
||||
// apiRenameTag is handler for PUT /api/tag
|
||||
func (h *handler) apiRenameTag(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// Make sure session still valid
|
||||
err := h.validateSession(r)
|
||||
checkError(err)
|
||||
|
||||
// Decode request
|
||||
tag := model.Tag{}
|
||||
err = json.NewDecoder(r.Body).Decode(&tag)
|
||||
checkError(err)
|
||||
|
||||
// Update name
|
||||
err = h.DB.RenameTag(tag.ID, tag.Name)
|
||||
checkError(err)
|
||||
|
||||
fmt.Fprint(w, 1)
|
||||
}
|
||||
|
||||
// apiInsertBookmark is handler for POST /api/bookmark
|
||||
func (h *handler) apiInsertBookmark(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// Make sure session still valid
|
||||
|
|
|
@ -48,6 +48,7 @@ func ServeApp(DB database.DB, dataDir string, port int) error {
|
|||
router.POST("/api/logout", hdl.apiLogout)
|
||||
router.GET("/api/bookmarks", hdl.apiGetBookmarks)
|
||||
router.GET("/api/tags", hdl.apiGetTags)
|
||||
router.PUT("/api/tag", hdl.apiRenameTag)
|
||||
router.POST("/api/bookmarks", hdl.apiInsertBookmark)
|
||||
router.DELETE("/api/bookmarks", hdl.apiDeleteBookmark)
|
||||
router.PUT("/api/bookmarks", hdl.apiUpdateBookmark)
|
||||
|
|
Loading…
Add table
Reference in a new issue