feat: refactor post browse api

This commit is contained in:
Damillora 2021-05-11 03:25:33 +07:00
parent 79fe33496b
commit a09058ceb2
15 changed files with 3979 additions and 3017 deletions

View File

@ -3,7 +3,9 @@ package app
import ( import (
"net/http" "net/http"
"strconv" "strconv"
"strings"
"github.com/Damillora/Shioriko/pkg/database"
"github.com/Damillora/Shioriko/pkg/middleware" "github.com/Damillora/Shioriko/pkg/middleware"
"github.com/Damillora/Shioriko/pkg/models" "github.com/Damillora/Shioriko/pkg/models"
"github.com/Damillora/Shioriko/pkg/services" "github.com/Damillora/Shioriko/pkg/services"
@ -16,7 +18,6 @@ func InitializePostRoutes(g *gin.Engine) {
{ {
unprotected.GET("/", postGet) unprotected.GET("/", postGet)
unprotected.GET("/:id", postGetOne) unprotected.GET("/:id", postGetOne)
unprotected.GET("/tag/:id", postGetTag)
} }
protected := g.Group("/api/post").Use(middleware.AuthMiddleware()) protected := g.Group("/api/post").Use(middleware.AuthMiddleware())
{ {
@ -30,7 +31,22 @@ func InitializePostRoutes(g *gin.Engine) {
func postGet(c *gin.Context) { func postGet(c *gin.Context) {
pageParam := c.Query("page") pageParam := c.Query("page")
page, _ := strconv.Atoi(pageParam) page, _ := strconv.Atoi(pageParam)
posts := services.GetPostAll(page)
tag := c.Query("tags")
tags := strings.Split(tag, " ")
var posts []database.Post
var postPages int
if tag != "" {
posts = services.GetPostTags(page, tags)
postPages = services.CountPostPagesTag(tags)
} else {
posts = services.GetPostAll(page)
postPages = services.CountPostPages()
}
var postResult []models.PostListItem var postResult []models.PostListItem
for _, post := range posts { for _, post := range posts {
var tagStrings []string var tagStrings []string
@ -44,35 +60,6 @@ func postGet(c *gin.Context) {
Tags: tagStrings, Tags: tagStrings,
}) })
} }
postPages := services.CountPostPages()
c.JSON(http.StatusOK, models.PostPaginationResponse{
CurrentPage: page,
TotalPage: postPages,
Posts: postResult,
})
}
func postGetTag(c *gin.Context) {
pageParam := c.Query("page")
page, _ := strconv.Atoi(pageParam)
tag := c.Param("id")
posts := services.GetPostTag(page, tag)
var postResult []models.PostListItem
for _, post := range posts {
var tagStrings []string
for _, tag := range post.Tags {
tagStrings = append(tagStrings, tag.TagType.Name+":"+tag.Name)
}
postResult = append(postResult, models.PostListItem{
ID: post.ID,
ImagePath: "/data/" + post.Blob.FilePath,
Tags: tagStrings,
})
}
postPages := services.CountPostPagesTag(tag)
c.JSON(http.StatusOK, models.PostPaginationResponse{ c.JSON(http.StatusOK, models.PostPaginationResponse{
CurrentPage: page, CurrentPage: page,
TotalPage: postPages, TotalPage: postPages,

View File

@ -1,6 +1,9 @@
package services package services
import ( import (
"log"
"math"
"github.com/Damillora/Shioriko/pkg/database" "github.com/Damillora/Shioriko/pkg/database"
"github.com/Damillora/Shioriko/pkg/models" "github.com/Damillora/Shioriko/pkg/models"
"github.com/google/uuid" "github.com/google/uuid"
@ -10,17 +13,18 @@ const perPage = 20
func GetPostAll(page int) []database.Post { func GetPostAll(page int) []database.Post {
var posts []database.Post var posts []database.Post
database.DB.Joins("Blob").Preload("Tags").Preload("Tags.TagType").Offset((page - 1) * perPage).Limit(20).Find(&posts) database.DB.Joins("Blob").Preload("Tags").Preload("Tags.TagType").Order("created_at desc").Offset((page - 1) * perPage).Limit(20).Find(&posts)
return posts return posts
} }
func GetPostTag(page int, tagSyntax string) []database.Post { func GetPostTags(page int, tagSyntax []string) []database.Post {
tag, err := GetTag(tagSyntax) tags, err := ParseReadTags(tagSyntax)
log.Println(tags)
if err != nil { if err != nil {
return []database.Post{} return []database.Post{}
} }
var posts []database.Post var posts []database.Post
database.DB.Model(&tag).Joins("Blob").Preload("Tags").Preload("Tags.TagType").Offset((page - 1) * perPage).Limit(20).Association("Posts").Find(&posts) database.DB.Model(&tags).Distinct().Joins("Blob").Preload("Tags").Preload("Tags.TagType").Order("created_at desc").Offset((page - 1) * perPage).Limit(20).Association("Posts").Find(&posts)
return posts return posts
} }
@ -59,34 +63,38 @@ func UpdatePost(id string, model models.PostUpdateModel) (*database.Post, error)
} }
var post database.Post var post database.Post
result := database.DB.Where("id = ?", id).First(&post) result := database.DB.Preload("Tags").Where("id = ?", id).First(&post)
if result.Error != nil { if result.Error != nil {
return nil, result.Error return nil, result.Error
} }
database.DB.Model(&post).Association("Tags").Replace(tags)
post.SourceURL = model.SourceURL post.SourceURL = model.SourceURL
post.Tags = tags
result = database.DB.Save(&post) result = database.DB.Save(&post)
if result.Error != nil { if result.Error != nil {
return nil, result.Error return nil, result.Error
} }
return &post, nil return &post, nil
} }
func CountPostPages() int { func CountPostPages() int {
var count int64 var count int64
database.DB.Model(&database.Post{}).Count(&count) database.DB.Model(&database.Post{}).Count(&count)
return int(count/perPage) + 1 return int(math.Abs(float64(count-1))/perPage) + 1
} }
func CountPostPagesTag(tagSyntax string) int { func CountPostPagesTag(tagSyntax []string) int {
tag, err := GetTag(tagSyntax) tags, err := ParseReadTags(tagSyntax)
if err != nil { if err != nil {
return 0 return 0
} }
var count int64 var count int64
count = database.DB.Model(&tag).Joins("Blob").Preload("Tags").Preload("Tags.TagType").Association("Posts").Count() count = database.DB.Model(&tags).Distinct().Joins("Blob").Preload("Tags").Preload("Tags.TagType").Association("Posts").Count()
return int(count/perPage) + 1
return int(math.Abs(float64(count-1))/perPage) + 1
} }
func DeletePost(id string) error { func DeletePost(id string) error {

View File

@ -14,27 +14,15 @@ func GetTagAll() []database.Tag {
return tags return tags
} }
func CreateOrUpdateTagGeneric(tagName string) (*database.Tag, error) { func FindTagGeneric(tagName string) (*database.Tag, error) {
var tag database.Tag var tag database.Tag
result := database.DB.Where("name = ?", tagName).First(&tag) result := database.DB.Where("name = ?", tagName).First(&tag)
if result.Error != nil { if result.Error != nil {
var tagType database.TagType return nil, result.Error
database.DB.Where("name = ?", "general").First(&tagType)
tag = database.Tag{
ID: uuid.NewString(),
Name: tagName,
TagTypeID: tagType.ID,
}
result = database.DB.Create(&tag)
if result.Error != nil {
return nil, result.Error
}
} }
return &tag, nil return &tag, nil
} }
func CreateOrUpdateTagComplex(tagName string, tagTypeString string) (*database.Tag, error) { func FindTagComplex(tagName string, tagTypeString string) (*database.Tag, error) {
var tag database.Tag var tag database.Tag
var tagType database.TagType var tagType database.TagType
result := database.DB.Where("name = ?", tagTypeString).First(&tagType) result := database.DB.Where("name = ?", tagTypeString).First(&tagType)
@ -45,7 +33,40 @@ func CreateOrUpdateTagComplex(tagName string, tagTypeString string) (*database.T
result = database.DB.Where("name = ? AND tag_type_id = ? ", tagName, tagType.ID).First(&tag) result = database.DB.Where("name = ? AND tag_type_id = ? ", tagName, tagType.ID).First(&tag)
if result.Error != nil { if result.Error != nil {
tag = database.Tag{ return nil, result.Error
}
return &tag, nil
}
func CreateOrUpdateTagGeneric(tagName string) (*database.Tag, error) {
tag, err := FindTagGeneric(tagName)
if err != nil {
var tagType database.TagType
database.DB.Where("name = ?", "general").First(&tagType)
tag = &database.Tag{
ID: uuid.NewString(),
Name: tagName,
TagTypeID: tagType.ID,
}
result := database.DB.Create(&tag)
if result.Error != nil {
return nil, result.Error
}
}
return tag, nil
}
func CreateOrUpdateTagComplex(tagName string, tagTypeString string) (*database.Tag, error) {
tag, err := FindTagComplex(tagName, tagTypeString)
if err != nil {
var tagType database.TagType
result := database.DB.Where("name = ?", tagTypeString).First(&tagType)
if result.Error != nil {
return nil, result.Error
}
tag = &database.Tag{
ID: uuid.NewString(), ID: uuid.NewString(),
Name: tagName, Name: tagName,
TagTypeID: tagType.ID, TagTypeID: tagType.ID,
@ -56,7 +77,7 @@ func CreateOrUpdateTagComplex(tagName string, tagTypeString string) (*database.T
} }
} }
return &tag, nil return tag, nil
} }
func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) { func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) {
tagFields := strings.Split(tagSyntax, ":") tagFields := strings.Split(tagSyntax, ":")
@ -73,30 +94,20 @@ func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) {
return nil, errors.New("Malformed tag syntax") return nil, errors.New("Malformed tag syntax")
} }
} }
func FindTag(tagSyntax string) (*database.Tag, error) {
func GetTag(tagSyntax string) (*database.Tag, error) {
tagFields := strings.Split(tagSyntax, ":") tagFields := strings.Split(tagSyntax, ":")
var tagName string var tagName string
var tagType database.TagType var tagType string
if len(tagFields) == 1 { if len(tagFields) == 1 {
tagName = tagFields[0] tagName = tagFields[0]
database.DB.Where("name = ?", "general").First(&tagType) return FindTagGeneric(tagName)
} else if len(tagFields) == 2 { } else if len(tagFields) == 2 {
tagType = tagFields[0]
tagName = tagFields[1] tagName = tagFields[1]
result := database.DB.Where("name = ?", tagFields[0]).First(&tagType) return FindTagComplex(tagName, tagType)
if result.Error != nil {
return nil, result.Error
}
} else { } else {
return nil, errors.New("Malformed tag syntax") return nil, errors.New("Malformed tag syntax")
} }
var tag database.Tag
result := database.DB.Preload("Posts").Where("name = ? AND tag_type_id = ? ", tagName, tagType.ID).First(&tag)
if result.Error != nil {
return nil, result.Error
}
return &tag, nil
} }
func ParseTags(tags []string) ([]database.Tag, error) { func ParseTags(tags []string) ([]database.Tag, error) {
@ -110,3 +121,15 @@ func ParseTags(tags []string) ([]database.Tag, error) {
} }
return result, nil return result, nil
} }
func ParseReadTags(tags []string) ([]database.Tag, error) {
var result []database.Tag
for _, tagSyntax := range tags {
tag, err := FindTag(tagSyntax)
if err != nil {
return nil, err
}
result = append(result, *tag)
}
return result, nil
}

View File

@ -8,8 +8,8 @@
import Post from "./routes/Post.svelte"; import Post from "./routes/Post.svelte";
import Login from "./routes/Login.svelte"; import Login from "./routes/Login.svelte";
import Logout from "./routes/Logout.svelte"; import Logout from "./routes/Logout.svelte";
import Tag from "./routes/Tag.svelte";
import Upload from "./routes/Upload.svelte"; import Upload from "./routes/Upload.svelte";
import Edit from "./routes/Edit.svelte";
export let url = ""; export let url = "";
let baseURL = window.BASE_URL; let baseURL = window.BASE_URL;
@ -20,8 +20,8 @@
<div> <div>
<Route path="/" component={Home} /> <Route path="/" component={Home} />
<Route path="/posts" component={Posts} /> <Route path="/posts" component={Posts} />
<Route path="/tag/:id" component={Tag} />
<Route path="/post/:id" component={Post} /> <Route path="/post/:id" component={Post} />
<Route path="/post/edit/:id" component={Edit} />
<Route path="/auth/login" component={Login} /> <Route path="/auth/login" component={Login} />
<Route path="/auth/logout" component={Logout} /> <Route path="/auth/logout" component={Logout} />
<Route path="/upload" component={Upload} /> <Route path="/upload" component={Upload} />

View File

@ -1,88 +1,101 @@
<script> <script>
import { Link } from "svelte-routing"; import { Link } from "svelte-routing";
import TagLink from "./TagLink.svelte";
export let posts = []; export let posts = [];
let postChunks = [];
// split posts into 4 columns
$: postChunks = Array(Math.min(posts.length, 4))
.fill()
.map(function (_, i) {
let chunkSize = Math.floor(posts.length / 4);
if (chunkSize % 4 > i + 1) {
chunkSize += 1;
}
chunkSize = Math.max(chunkSize, 1);
return posts.slice(i * chunkSize, i * chunkSize + chunkSize);
});
export let page = 1; export let page = 1;
export let totalPages = 1; export let totalPages = 1;
export let handlePage = (i) => { }; export let handlePage = (i) => {};
export let url = "/posts"; export let url = "/posts";
</script> </script>
<section class="section"> <nav class="pagination" role="navigation" aria-label="pagination">
<div class="container"> {#if page > 1}
<nav class="pagination" role="navigation" aria-label="pagination"> <Link
{#if page > 1} on:click={handlePage(page - 1)}
to="{url}page={page - 1}"
class="pagination-previous"
aria-label="Previous">Previous</Link
>
{/if}
{#if page < totalPages}
<Link
on:click={handlePage(page + 1)}
to="{url}page={page + 1}"
class="pagination-next"
aria-label="Next">Next</Link
>
{/if}
<ul class="pagination-list">
{#if page > 3}
<li>
<Link <Link
on:click={handlePage(page - 1)} on:click={handlePage(1)}
to="{url}?page={page - 1}" to="{url}page={1}"
class="pagination-previous" class="pagination-link"
aria-label="Previous">Previous</Link aria-label="Goto page 1">1</Link
> >
{/if} </li>
{#if page < totalPages} <li>
<Link <span class="pagination-ellipsis">&hellip;</span>
on:click={handlePage(page + 1)} </li>
to="{url}?page={page + 1}" {/if}
class="pagination-next" {#each [...Array(5).keys()].map((x) => x + page - 2) as i}
aria-label="Next">Next</Link {#if i >= 1 && i <= totalPages}
> {#if i == page}
{/if}
<ul class="pagination-list">
{#if page > 3}
<li> <li>
<Link <Link
on:click={handlePage(1)} on:click={handlePage(i)}
to="{url}?page={1}" to="{url}page={i}"
class="pagination-link" class="pagination-link is-current"
aria-label="Goto page 1">1</Link aria-label="Goto page {i}">{i}</Link
> >
</li> </li>
<li> {:else}
<span class="pagination-ellipsis">&hellip;</span>
</li>
{/if}
{#each [...Array(5).keys()].map((x) => x + page - 2) as i}
{#if i >= 1 && i <= totalPages}
{#if i == page}
<li>
<Link
on:click={handlePage(i)}
to="{url}?page={i}"
class="pagination-link is-current"
aria-label="Goto page {i}">{i}</Link
>
</li>
{:else}
<li>
<Link
on:click={handlePage(i)}
to="{url}?page={i}"
class="pagination-link"
aria-label="Goto page {i}">{i}</Link
>
</li>
{/if}
{/if}
{/each}
{#if totalPages - page > 2}
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
<li> <li>
<Link <Link
on:click={handlePage(totalPages)} on:click={handlePage(i)}
to="{url}?page={totalPages}" to="{url}page={i}"
class="pagination-link" class="pagination-link"
aria-label="Goto page {totalPages}" aria-label="Goto page {i}">{i}</Link
>{totalPages}</Link
> >
</li> </li>
{/if} {/if}
</ul> {/if}
</nav> {/each}
<div class="columns is-multiline"> {#if totalPages - page > 2}
{#each posts as post (post.id)} <li>
<div class="column is-one-quarter card"> <span class="pagination-ellipsis">&hellip;</span>
</li>
<li>
<Link
on:click={handlePage(totalPages)}
to="{url}page={totalPages}"
class="pagination-link"
aria-label="Goto page {totalPages}">{totalPages}</Link
>
</li>
{/if}
</ul>
</nav>
<div class="tile is-multiline is-ancestor">
{#each postChunks as postChunk}
<div class="tile is-parent is-vertical is-3">
{#each postChunk as post, i (post.id)}
<div class="tile is-child is-vertical card">
<div class="card-image"> <div class="card-image">
<figure class="image"> <figure class="image">
<Link to="/post/{post.id}"> <Link to="/post/{post.id}">
@ -91,16 +104,16 @@
</figure> </figure>
</div> </div>
<div class="card-content"> <div class="card-content">
<div class="content"> {#if post.tags}
{#each post.tags as tag (tag)} {#each post.tags as tag (tag)}
<p> <TagLink {tag} />
<Link to="/tag/{tag}">{tag}</Link>
</p>
{/each} {/each}
</div> {:else}
None
{/if}
</div> </div>
</div> </div>
{/each} {/each}
</div> </div>
</div> {/each}
</section> </div>

View File

@ -0,0 +1,10 @@
<script>
import { Link } from "svelte-routing";
export let tag;
let tagType = tag.split(":")[0] ?? "";
let tagName = tag.split(":")[1] ?? "";
</script>
<Link class="button is-rounded is-primary is-small m-1" to="/posts?tags={tagName}">{tagName}</Link>

View File

@ -27,10 +27,16 @@ export async function getPosts({ page }) {
return response.data; return response.data;
} }
export async function getPostsTag({ page, tag }) { export async function getPostSearchTag({ page, q }) {
const endpoint = url + "/api/post/tag/" + tag + "?page=" + page; if (q) {
const response = await axios(endpoint); const endpoint = url + "/api/post?tags=" + q + "&page=" + page;
return response.data; const response = await axios(endpoint);
return response.data;
} else {
const endpoint = url + "/api/post?page=" + page;
const response = await axios(endpoint);
return response.data;
}
} }
export async function getPost({ id }) { export async function getPost({ id }) {
@ -75,4 +81,20 @@ export async function postCreate({ blob_id, source_url, tags }) {
} }
}) })
return response.data; return response.data;
}
export async function postUpdate(id, { source_url, tags }) {
const endpoint = url + "/api/post/"+id;
const response = await axios({
url: endpoint,
method: "POST",
headers: {
'Authorization': 'Bearer ' + current_token,
},
withCredentials: true,
data: {
source_url, tags
}
})
return response.data;
} }

View File

@ -1,9 +1,13 @@
@import '../node_modules/bulma/sass/utilities/_all';
@import '../node_modules/bulma/sass/base/_all';
@import '../node_modules/bulma/sass/elements/_all';
@import '../node_modules/bulma/sass/form/_all';
@import '../node_modules/bulma/sass/components/_all';
@import '../node_modules/bulma/sass/grid/_all';
@import '../node_modules/bulma/sass/helpers/_all';
@import '../node_modules/bulma/sass/layout/_all';
@import "../node_modules/bulma/sass/utilities/_all"; .tile.is-multiline {
@import "../node_modules/bulma/sass/base/_all"; flex-wrap: wrap;
@import "../node_modules/bulma/sass/elements/_all"; }
@import "../node_modules/bulma/sass/form/_all";
@import "../node_modules/bulma/sass/components/_all";
@import "../node_modules/bulma/sass/grid/_all";
@import "../node_modules/bulma/sass/helpers/_all";
@import "../node_modules/bulma/sass/layout/_all";

View File

@ -0,0 +1,90 @@
<script>
import { getPost, postUpdate } from "../api.js";
import { navigate } from "svelte-routing";
import Tags from "svelte-tags-input";
import { onMount } from "svelte";
import { Link } from "svelte-routing";
export let id;
let image_path = "";
let form = {
source_url: "",
tags: [],
};
const getData = async () => {
const data = await getPost({ id });
form.source_url = data.source_url;
form.tags = data.tags;
image_path = data.image_path;
};
const onTagChange = (value) => {
form.tags = value.detail.tags;
};
const onSubmit = async () => {
const response = await postUpdate(id, form);
navigate(`/post/${response.id}`);
};
onMount(() => {
getData();
});
</script>
<section class="hero is-primary">
<div class="hero-body">
<p class="title">Edit Post: {id}</p>
</div>
</section>
<div class="container">
<section class="section">
<div class="columns">
<div class="column is-one-third box">
<p>
<Link class="button is-primary" to="/post/{id}"
>Back</Link
>
</p>
<form on:submit|preventDefault={onSubmit}>
<div class="field">
<label for="source" class="label">Source URL</label>
<div class="control">
<input
id="source"
class="input"
type="url"
placeholder="Source URL"
bind:value={form.source_url}
/>
</div>
</div>
<div class="field">
<label for="tags" class="label">Tags</label>
<div class="control" id="tags">
<Tags
tags={form.tags}
addKeys={[9,32]}
on:tags={onTagChange}
/>
</div>
</div>
<div class="control">
<button type="submit" class="button is-primary"
>Submit</button
>
</div>
</form>
</div>
<div class="column">
<figure class="image">
<img alt={id} src={image_path} />
</figure>
</div>
</div>
</section>
</div>

View File

@ -1,59 +1,73 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import TagLink from "../TagLink.svelte";
import { getPost, postCreate } from "../api.js";
import { Link } from "svelte-routing"; import { Link } from "svelte-routing";
import {getPost } from "../api.js";
export let id; export let id;
let post; let post;
const getData = async() => { const getData = async () => {
const data = await getPost({id}); const data = await getPost({ id });
post = data; post = data;
} };
const trimUrl = (str) => { const trimUrl = (str) => {
if(str.length > 30) { if (str.length > 30) {
return str.substring(0,30) + "..."; return str.substring(0, 30) + "...";
} }
return str; return str;
} };
onMount(() => {getData()}); onMount(() => {
getData();
});
</script> </script>
<section class="hero is-primary"> <section class="hero is-primary">
<div class="hero-body"> <div class="hero-body">
{#if post} {#if post}
<p class="title"> <p class="title">
Post ID: {post.id} Post ID: {post.id}
</p> </p>
{/if} {/if}
</div> </div>
</section> </section>
{#if post} {#if post}
<div class="container"> <div class="container">
<section class="section"> <section class="section">
<div class="columns"> <div class="columns">
<div class="column is-one-third box"> <div class="column is-one-third box">
<p> <div class="content">
Source URL: <a href="{post.source_url}">{trimUrl(post.source_url)}</a> <p>
</p> <Link
<p> class="button is-primary"
Tags: to="/post/edit/{post.id}">Edit</Link
{#each post.tags as tag (tag)} >
<ul> </p>
<li> <p>
<Link to="/tag/{tag}">{tag}</Link> Source URL: <a href={post.source_url}
</li> >{trimUrl(post.source_url)}</a
</ul> >
{/each} </p>
</p> <p>
Tags:<br />
</p>
<p>
{#if post.tags}
{#each post.tags as tag (tag)}
<TagLink {tag} />
{/each}
{:else}
None
{/if}
</p>
</div>
</div>
<div class="column">
<figure class="image">
<img alt={post.id} src={post.image_path} />
</figure>
</div>
</div> </div>
<div class="column"> </section>
<figure class="image"> </div>
<img alt="{post.id}" src="{post.image_path}"> {/if}
</figure>
</div>
</div>
</section>
</div>
{/if}

View File

@ -1,30 +1,45 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getPosts } from "../api.js"; import { getPostSearchTag } from "../api.js";
import { Link } from "svelte-routing"; import { navigate } from "svelte-routing";
import queryString from "query-string";
import PostPaginator from "../PostPaginator.svelte"; import PostPaginator from "../PostPaginator.svelte";
import queryString from "query-string";
import Tags from "svelte-tags-input";
export let location; export let location;
let searchTerms = [];
let page = 1; let page = 1;
let totalPages = 1; let totalPages = 1;
let posts = []; let posts = [];
const getData = async () => { const getData = async () => {
const data = await getPosts({ page }); const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
if (Array.isArray(data.posts)) { if (Array.isArray(data.posts)) {
posts = data.posts; posts = data.posts;
totalPages = data.totalPage; totalPages = data.totalPage;
} }
}; };
onMount(() => {
let queryParams; let queryParams;
const onTagChange = (value) => {
searchTerms = value.detail.tags;
};
$: {
queryParams = queryString.parse(location.search); queryParams = queryString.parse(location.search);
console.log(queryParams);
if (queryParams.page) { if (queryParams.page) {
page = parseInt(queryParams.page); page = parseInt(queryParams.page);
} }
if (queryParams.tags) {
searchTerms = queryParams.tags.split(" ");
} else {
searchTerms = [];
}
getData(); getData();
}); }
const handlePage = (i) => { const handlePage = (i) => {
return () => { return () => {
@ -32,6 +47,14 @@
getData(); getData();
}; };
}; };
const onSearch = (i) => {
if (searchTerms.length > 0) {
navigate(`/posts?tags=${searchTerms.join("+")}`);
} else {
navigate(`/posts`);
}
};
</script> </script>
<section class="hero is-primary"> <section class="hero is-primary">
@ -40,4 +63,36 @@
</div> </div>
</section> </section>
<PostPaginator url="/posts" posts={posts} page={page} totalPages={totalPages} handlePage={handlePage} /> <section class="section">
<div class="container">
<div class="block">
<form on:submit|preventDefault={onSearch}>
<div class="field has-addons">
<div class="control is-expanded">
<div class="control" id="tags">
<Tags
tags={searchTerms}
addKeys={[9,32]}
on:tags={onTagChange}
/>
</div>
</div>
<div class="control">
<button type="submit" class="button is-primary">
Search
</button>
</div>
</div>
</form>
</div>
<div class="block">
<PostPaginator
url="/posts?tags={searchTerms.join('+')}&"
{posts}
{page}
{totalPages}
{handlePage}
/>
</div>
</div>
</section>

View File

@ -1,47 +0,0 @@
<script>
import { onMount } from "svelte";
import { getPostsTag } from "../api.js";
import PostPaginator from "../PostPaginator.svelte";
import queryString from "query-string";
export let location;
export let id;
let page = 1;
let totalPages = 1;
let posts = [];
const getData = async () => {
const data = await getPostsTag({ page, tag: id });
if (Array.isArray(data.posts)) {
posts = data.posts;
totalPages = data.totalPage;
}
};
onMount(() => {
let queryParams;
queryParams = queryString.parse(location.search);
if (queryParams.page) {
page = parseInt(queryParams.page);
}
getData();
});
const handlePage = (i) => {
return () => {
page = i;
getData();
};
};
</script>
<section class="hero is-primary">
<div class="hero-body">
<p class="title">
{id}
</p>
<p class="subtitle">Tag</p>
</div>
</section>
<PostPaginator url="/tag/{id}" posts={posts} page={page} totalPages={totalPages} handlePage={handlePage} />

View File

@ -88,7 +88,7 @@
<div class="field"> <div class="field">
<label for="tags" class="label">Tags</label> <label for="tags" class="label">Tags</label>
<div class="control" id="tags"> <div class="control" id="tags">
<Tags on:tags={onTagChange} /> <Tags addKeys={[9,32]} on:tags={onTagChange} />
</div> </div>
</div> </div>
<div class="control"> <div class="control">

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long