Implement search on web interface

This commit is contained in:
Radhi Fadlillah 2018-02-23 14:30:58 +07:00
parent 0a4850eacb
commit 529b089fd9
5 changed files with 74 additions and 9 deletions

View file

@ -153,12 +153,17 @@ func apiLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
}
func apiGetBookmarks(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Get query parameter
keyword := r.URL.Query().Get("keyword")
strTags := r.URL.Query().Get("tags")
tags := strings.Fields(strTags)
// Check token
err := checkAPIToken(r)
checkError(err)
// Fetch all bookmarks
bookmarks, err := DB.GetBookmarks(false)
bookmarks, err := DB.SearchBookmarks(keyword, tags...)
checkError(err)
err = json.NewEncoder(w).Encode(&bookmarks)

File diff suppressed because one or more lines are too long

View file

@ -21,13 +21,13 @@
<a id="logo" href="/">
<span></span>shiori</a>
<div id="search-box">
<input type="text" name="keyword" id="input-search" placeholder="Search tags, title or content">
<a class="button">
<input type="text" name="keyword" v-model.trim="search.query" @keyup.enter="loadData" placeholder="Search url, tags, title or content">
<a class="button" @click="loadData">
<i class="fas fa-search fa-fw"></i>
</a>
</div>
<div id="header-menu" v-if="!loading">
<a @click="loadData">
<a @click="reloadData">
<i class="fas fa-cloud fa-fw"></i> Reload
</a>
<a @click="toggleImage">
@ -75,6 +75,10 @@
</div>
</template>
</div>
<div v-if="search.query !== '' && !loading" id="search-parameter">
<a v-if="search.keyword !== ''" @click="removeSearchParam(search.keyword)">{{search.keyword}}</a>
<a v-for="tag in search.tags" @click="removeSearchParam('#'+tag)">#{{tag}}</a>
</div>
<div id="grid">
<div v-for="column in gridColumns" class="column">
<div v-for="item in column" class="bookmark" :class="{checked: isBookmarkChecked(item.index)}" :ref="'bookmark-'+item.index">
@ -142,10 +146,15 @@
data: {
windowWidth: 0,
error: "",
loading: true,
loading: false,
bookmarks: [],
checkedBookmarks: [],
showImage: true,
search: {
query: "",
keyword: "",
tags: []
},
inputBookmark: {
index: -1,
id: -1,
@ -169,10 +178,40 @@
}
},
methods: {
removeSearchParam: function (param) {
if (this.loading) return;
this.search.query = this.search.query.replace(param, ' ').trim().replace(/\s+/g, ' ');
this.loadData();
},
reloadData: function () {
if (this.loading) return;
this.search.query = '';
this.loadData();
},
loadData: function () {
if (this.loading) return;
// Parse search query
var rxTags = /(^|\s+)#(\S+)/g,
tags = [];
while ((result = rxTags.exec(this.search.query)) !== null) {
tags.push(result[2]);
}
var keyword = this.search.query.replace(/(^|\s+)#(\S+)/g, ' ').trim().replace(/\s+/g, ' ');
// Fetch data
this.error = '';
this.loading = true;
instance.get('/api/bookmarks')
this.search.tags = tags;
this.search.keyword = keyword;
instance.get('/api/bookmarks', {
params: {
keyword: this.search.keyword,
tags: this.search.tags.join(" ")
}
})
.then(function (response) {
app.loading = false;
app.bookmarks = response.data;
@ -460,7 +499,7 @@
}
return finalContent;
}
},
},
watch: {
'inputBookmark.url': function (newURL) {

View file

@ -263,6 +263,25 @@
}
}
}
#search-parameter {
display: flex;
flex-flow: row wrap;
padding: 0 8px;
a {
display: block;
margin: 8px;
padding: 8px;
font-size: 0.9em;
background-color: @fontLightColor;
color: white;
border-radius: 16px;
cursor: pointer;
&:hover {
background-color: @main;
text-decoration: line-through;
}
}
}
#grid {
display: flex;
flex-flow: row;
@ -299,6 +318,8 @@
display: flex;
flex-flow: column nowrap;
align-items: center;
height: auto;
min-height: 100vh;
a {
color: @main;
&:visited {

View file

@ -31,7 +31,7 @@
</div>
<div class="input-field">
<p>Password: </p>
<input type="password" name="password" v-model.trim="password" placeholder="Password">
<input type="password" name="password" v-model.trim="password" placeholder="Password" @keyup.enter="login">
</div>
<div class="input-field">
<a @click="toggleRemember">