mirror of
https://github.com/Damillora/Shioriko.git
synced 2024-11-24 13:27:31 +00:00
feat: refactor post browse api
This commit is contained in:
parent
79fe33496b
commit
a09058ceb2
@ -3,7 +3,9 @@ package app
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Damillora/Shioriko/pkg/database"
|
||||
"github.com/Damillora/Shioriko/pkg/middleware"
|
||||
"github.com/Damillora/Shioriko/pkg/models"
|
||||
"github.com/Damillora/Shioriko/pkg/services"
|
||||
@ -16,7 +18,6 @@ func InitializePostRoutes(g *gin.Engine) {
|
||||
{
|
||||
unprotected.GET("/", postGet)
|
||||
unprotected.GET("/:id", postGetOne)
|
||||
unprotected.GET("/tag/:id", postGetTag)
|
||||
}
|
||||
protected := g.Group("/api/post").Use(middleware.AuthMiddleware())
|
||||
{
|
||||
@ -30,7 +31,22 @@ func InitializePostRoutes(g *gin.Engine) {
|
||||
func postGet(c *gin.Context) {
|
||||
pageParam := c.Query("page")
|
||||
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
|
||||
for _, post := range posts {
|
||||
var tagStrings []string
|
||||
@ -44,35 +60,6 @@ func postGet(c *gin.Context) {
|
||||
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{
|
||||
CurrentPage: page,
|
||||
TotalPage: postPages,
|
||||
|
@ -1,6 +1,9 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
|
||||
"github.com/Damillora/Shioriko/pkg/database"
|
||||
"github.com/Damillora/Shioriko/pkg/models"
|
||||
"github.com/google/uuid"
|
||||
@ -10,17 +13,18 @@ const perPage = 20
|
||||
|
||||
func GetPostAll(page int) []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
|
||||
}
|
||||
|
||||
func GetPostTag(page int, tagSyntax string) []database.Post {
|
||||
tag, err := GetTag(tagSyntax)
|
||||
func GetPostTags(page int, tagSyntax []string) []database.Post {
|
||||
tags, err := ParseReadTags(tagSyntax)
|
||||
log.Println(tags)
|
||||
if err != nil {
|
||||
return []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
|
||||
}
|
||||
|
||||
@ -59,34 +63,38 @@ func UpdatePost(id string, model models.PostUpdateModel) (*database.Post, error)
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
database.DB.Model(&post).Association("Tags").Replace(tags)
|
||||
|
||||
post.SourceURL = model.SourceURL
|
||||
post.Tags = tags
|
||||
|
||||
result = database.DB.Save(&post)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return &post, nil
|
||||
|
||||
}
|
||||
func CountPostPages() int {
|
||||
var count int64
|
||||
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 {
|
||||
tag, err := GetTag(tagSyntax)
|
||||
func CountPostPagesTag(tagSyntax []string) int {
|
||||
tags, err := ParseReadTags(tagSyntax)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
var count int64
|
||||
count = database.DB.Model(&tag).Joins("Blob").Preload("Tags").Preload("Tags.TagType").Association("Posts").Count()
|
||||
return int(count/perPage) + 1
|
||||
count = database.DB.Model(&tags).Distinct().Joins("Blob").Preload("Tags").Preload("Tags.TagType").Association("Posts").Count()
|
||||
|
||||
return int(math.Abs(float64(count-1))/perPage) + 1
|
||||
}
|
||||
|
||||
func DeletePost(id string) error {
|
||||
|
@ -14,27 +14,15 @@ func GetTagAll() []database.Tag {
|
||||
return tags
|
||||
}
|
||||
|
||||
func CreateOrUpdateTagGeneric(tagName string) (*database.Tag, error) {
|
||||
func FindTagGeneric(tagName string) (*database.Tag, error) {
|
||||
var tag database.Tag
|
||||
result := database.DB.Where("name = ?", tagName).First(&tag)
|
||||
if result.Error != 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 nil, result.Error
|
||||
}
|
||||
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 tagType database.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)
|
||||
|
||||
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(),
|
||||
Name: tagName,
|
||||
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) {
|
||||
tagFields := strings.Split(tagSyntax, ":")
|
||||
@ -73,30 +94,20 @@ func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) {
|
||||
return nil, errors.New("Malformed tag syntax")
|
||||
}
|
||||
}
|
||||
|
||||
func GetTag(tagSyntax string) (*database.Tag, error) {
|
||||
func FindTag(tagSyntax string) (*database.Tag, error) {
|
||||
tagFields := strings.Split(tagSyntax, ":")
|
||||
var tagName string
|
||||
var tagType database.TagType
|
||||
var tagType string
|
||||
if len(tagFields) == 1 {
|
||||
tagName = tagFields[0]
|
||||
database.DB.Where("name = ?", "general").First(&tagType)
|
||||
return FindTagGeneric(tagName)
|
||||
} else if len(tagFields) == 2 {
|
||||
tagType = tagFields[0]
|
||||
tagName = tagFields[1]
|
||||
result := database.DB.Where("name = ?", tagFields[0]).First(&tagType)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
return FindTagComplex(tagName, tagType)
|
||||
} else {
|
||||
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) {
|
||||
@ -110,3 +121,15 @@ func ParseTags(tags []string) ([]database.Tag, error) {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
import Post from "./routes/Post.svelte";
|
||||
import Login from "./routes/Login.svelte";
|
||||
import Logout from "./routes/Logout.svelte";
|
||||
import Tag from "./routes/Tag.svelte";
|
||||
import Upload from "./routes/Upload.svelte";
|
||||
import Edit from "./routes/Edit.svelte";
|
||||
|
||||
export let url = "";
|
||||
let baseURL = window.BASE_URL;
|
||||
@ -20,8 +20,8 @@
|
||||
<div>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/posts" component={Posts} />
|
||||
<Route path="/tag/:id" component={Tag} />
|
||||
<Route path="/post/:id" component={Post} />
|
||||
<Route path="/post/edit/:id" component={Edit} />
|
||||
<Route path="/auth/login" component={Login} />
|
||||
<Route path="/auth/logout" component={Logout} />
|
||||
<Route path="/upload" component={Upload} />
|
||||
|
@ -1,88 +1,101 @@
|
||||
<script>
|
||||
import { Link } from "svelte-routing";
|
||||
import TagLink from "./TagLink.svelte";
|
||||
|
||||
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 totalPages = 1;
|
||||
export let handlePage = (i) => { };
|
||||
export let handlePage = (i) => {};
|
||||
export let url = "/posts";
|
||||
</script>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||
{#if page > 1}
|
||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||
{#if page > 1}
|
||||
<Link
|
||||
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
|
||||
on:click={handlePage(page - 1)}
|
||||
to="{url}?page={page - 1}"
|
||||
class="pagination-previous"
|
||||
aria-label="Previous">Previous</Link
|
||||
on:click={handlePage(1)}
|
||||
to="{url}page={1}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page 1">1</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>
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</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(1)}
|
||||
to="{url}?page={1}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page 1">1</Link
|
||||
on:click={handlePage(i)}
|
||||
to="{url}page={i}"
|
||||
class="pagination-link is-current"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</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">…</span>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(totalPages)}
|
||||
to="{url}?page={totalPages}"
|
||||
on:click={handlePage(i)}
|
||||
to="{url}page={i}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {totalPages}"
|
||||
>{totalPages}</Link
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="columns is-multiline">
|
||||
{#each posts as post (post.id)}
|
||||
<div class="column is-one-quarter card">
|
||||
{/if}
|
||||
{/each}
|
||||
{#if totalPages - page > 2}
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</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">
|
||||
<figure class="image">
|
||||
<Link to="/post/{post.id}">
|
||||
@ -91,16 +104,16 @@
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
{#if post.tags}
|
||||
{#each post.tags as tag (tag)}
|
||||
<p>
|
||||
<Link to="/tag/{tag}">{tag}</Link>
|
||||
</p>
|
||||
<TagLink {tag} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
None
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
10
web/app/src/TagLink.svelte
Normal file
10
web/app/src/TagLink.svelte
Normal 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>
|
@ -27,10 +27,16 @@ export async function getPosts({ page }) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function getPostsTag({ page, tag }) {
|
||||
const endpoint = url + "/api/post/tag/" + tag + "?page=" + page;
|
||||
const response = await axios(endpoint);
|
||||
return response.data;
|
||||
export async function getPostSearchTag({ page, q }) {
|
||||
if (q) {
|
||||
const endpoint = url + "/api/post?tags=" + q + "&page=" + page;
|
||||
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 }) {
|
||||
@ -76,3 +82,19 @@ export async function postCreate({ blob_id, source_url, tags }) {
|
||||
})
|
||||
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;
|
||||
}
|
@ -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';
|
||||
|
||||
.tile.is-multiline {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@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";
|
||||
|
90
web/app/src/routes/Edit.svelte
Normal file
90
web/app/src/routes/Edit.svelte
Normal 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>
|
@ -1,59 +1,73 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import TagLink from "../TagLink.svelte";
|
||||
import { getPost, postCreate } from "../api.js";
|
||||
import { Link } from "svelte-routing";
|
||||
import {getPost } from "../api.js";
|
||||
export let id;
|
||||
let post;
|
||||
const getData = async() => {
|
||||
const data = await getPost({id});
|
||||
const getData = async () => {
|
||||
const data = await getPost({ id });
|
||||
post = data;
|
||||
}
|
||||
};
|
||||
|
||||
const trimUrl = (str) => {
|
||||
if(str.length > 30) {
|
||||
return str.substring(0,30) + "...";
|
||||
if (str.length > 30) {
|
||||
return str.substring(0, 30) + "...";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {getData()});
|
||||
onMount(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<section class="hero is-primary">
|
||||
<div class="hero-body">
|
||||
{#if post}
|
||||
<p class="title">
|
||||
Post ID: {post.id}
|
||||
</p>
|
||||
{/if}
|
||||
<p class="title">
|
||||
Post ID: {post.id}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
{#if post}
|
||||
<div class="container">
|
||||
<section class="section">
|
||||
<div class="columns">
|
||||
<div class="column is-one-third box">
|
||||
<p>
|
||||
Source URL: <a href="{post.source_url}">{trimUrl(post.source_url)}</a>
|
||||
</p>
|
||||
<p>
|
||||
Tags:
|
||||
{#each post.tags as tag (tag)}
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/tag/{tag}">{tag}</Link>
|
||||
</li>
|
||||
</ul>
|
||||
{/each}
|
||||
</p>
|
||||
<div class="container">
|
||||
<section class="section">
|
||||
<div class="columns">
|
||||
<div class="column is-one-third box">
|
||||
<div class="content">
|
||||
<p>
|
||||
<Link
|
||||
class="button is-primary"
|
||||
to="/post/edit/{post.id}">Edit</Link
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
Source URL: <a href={post.source_url}
|
||||
>{trimUrl(post.source_url)}</a
|
||||
>
|
||||
</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 class="column">
|
||||
<figure class="image">
|
||||
<img alt="{post.id}" src="{post.image_path}">
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{/if}
|
@ -1,30 +1,45 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { getPosts } from "../api.js";
|
||||
import { Link } from "svelte-routing";
|
||||
import queryString from "query-string";
|
||||
import { getPostSearchTag } from "../api.js";
|
||||
import { navigate } from "svelte-routing";
|
||||
import PostPaginator from "../PostPaginator.svelte";
|
||||
import queryString from "query-string";
|
||||
import Tags from "svelte-tags-input";
|
||||
|
||||
export let location;
|
||||
|
||||
let searchTerms = [];
|
||||
|
||||
let page = 1;
|
||||
let totalPages = 1;
|
||||
let posts = [];
|
||||
const getData = async () => {
|
||||
const data = await getPosts({ page });
|
||||
const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
|
||||
if (Array.isArray(data.posts)) {
|
||||
posts = data.posts;
|
||||
totalPages = data.totalPage;
|
||||
}
|
||||
};
|
||||
onMount(() => {
|
||||
let queryParams;
|
||||
|
||||
let queryParams;
|
||||
|
||||
const onTagChange = (value) => {
|
||||
searchTerms = value.detail.tags;
|
||||
};
|
||||
|
||||
$: {
|
||||
queryParams = queryString.parse(location.search);
|
||||
console.log(queryParams);
|
||||
if (queryParams.page) {
|
||||
page = parseInt(queryParams.page);
|
||||
}
|
||||
if (queryParams.tags) {
|
||||
searchTerms = queryParams.tags.split(" ");
|
||||
} else {
|
||||
searchTerms = [];
|
||||
}
|
||||
getData();
|
||||
});
|
||||
}
|
||||
|
||||
const handlePage = (i) => {
|
||||
return () => {
|
||||
@ -32,6 +47,14 @@
|
||||
getData();
|
||||
};
|
||||
};
|
||||
|
||||
const onSearch = (i) => {
|
||||
if (searchTerms.length > 0) {
|
||||
navigate(`/posts?tags=${searchTerms.join("+")}`);
|
||||
} else {
|
||||
navigate(`/posts`);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="hero is-primary">
|
||||
@ -40,4 +63,36 @@
|
||||
</div>
|
||||
</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>
|
||||
|
@ -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} />
|
@ -88,7 +88,7 @@
|
||||
<div class="field">
|
||||
<label for="tags" class="label">Tags</label>
|
||||
<div class="control" id="tags">
|
||||
<Tags on:tags={onTagChange} />
|
||||
<Tags addKeys={[9,32]} on:tags={onTagChange} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
|
6307
web/static/bundle.js
6307
web/static/bundle.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user