feat: infinite scrolling

This commit is contained in:
Damillora 2021-05-11 17:06:25 +07:00
parent 63b4f89820
commit 578c7ca992
7 changed files with 86 additions and 153 deletions

View File

@ -62,7 +62,7 @@ func postGet(c *gin.Context) {
}
c.JSON(http.StatusOK, models.PostPaginationResponse{
CurrentPage: page,
TotalPage: postPages,
PostCount: postPages,
Posts: postResult,
})
}

View File

@ -20,6 +20,6 @@ type BlobResponse struct {
type PostPaginationResponse struct {
CurrentPage int `json:"currentPage"`
TotalPage int `json:"totalPage"`
PostCount int `json:"postCount"`
Posts []PostListItem `json:"posts"`
}

View File

@ -2,7 +2,6 @@ package services
import (
"log"
"math"
"github.com/Damillora/Shioriko/pkg/database"
"github.com/Damillora/Shioriko/pkg/models"
@ -83,7 +82,7 @@ func UpdatePost(id string, model models.PostUpdateModel) (*database.Post, error)
func CountPostPages() int {
var count int64
database.DB.Model(&database.Post{}).Count(&count)
return int(math.Abs(float64(count-1))/perPage) + 1
return int(count)
}
func CountPostPagesTag(tagSyntax []string) int {
tags, err := ParseReadTags(tagSyntax)
@ -93,8 +92,7 @@ func CountPostPagesTag(tagSyntax []string) int {
var count int64
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
return int(count)
}
func DeletePost(id string) error {

View File

@ -24,6 +24,7 @@
"postcss": "^8.2.14",
"query-string": "^7.0.0",
"sirv-cli": "^1.0.0",
"svelte-infinite-scroll": "^1.5.2",
"svelte-preprocess": "^4.7.3",
"svelte-routing": "^1.6.0",
"svelte-tags-input": "^2.7.1"

View File

@ -1,119 +0,0 @@
<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 url = "/posts";
</script>
<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(1)}
to="{url}page={1}"
class="pagination-link"
aria-label="Goto page 1">1</Link
>
</li>
<li>
<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>
<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}">
<img alt={post.id} src={post.image_path} />
</Link>
</figure>
</div>
<div class="card-content">
{#if post.tags}
{#each post.tags as tag (tag)}
<TagLink {tag} />
{/each}
{:else}
None
{/if}
</div>
</div>
{/each}
</div>
{/each}
</div>

View File

@ -1,52 +1,62 @@
<script>
import { onMount } from "svelte";
import { getPostSearchTag } from "../api.js";
import { navigate } from "svelte-routing";
import PostPaginator from "../PostPaginator.svelte";
import { Link, navigate } from "svelte-routing";
import InfiniteScroll from "svelte-infinite-scroll";
import TagLink from "../TagLink.svelte";
import queryString from "query-string";
import Tags from "svelte-tags-input";
import { add_attribute } from "svelte/internal";
export let location;
let searchTerms = [];
let page = 1;
let totalPages = 1;
let posts = [];
const getData = async () => {
const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
if (Array.isArray(data.posts)) {
posts = data.posts;
totalPages = data.totalPage;
let newBatch = [];
const splitToChunks = (array, parts) => {
let result = [];
for (let i = parts; i > 0; i--) {
result.push(array.slice(i * parts, i * parts + parts + 1));
}
return result;
};
let postChunks = [];
// split posts into 4 columns
$: {
postChunks = splitToChunks(posts, 4);
}
const getData = async () => {
console.log("page " + page);
const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
if (data.posts) {
newBatch = data.posts;
} else {
newBatch = [];
}
};
$: {
posts = [...posts, ...newBatch];
}
let queryParams;
const onTagChange = (value) => {
searchTerms = value.detail.tags;
};
$: {
onMount(() => {
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 () => {
page = i;
getData();
};
};
});
const onSearch = (i) => {
if (searchTerms.length > 0) {
@ -72,7 +82,7 @@
<div class="control" id="tags">
<Tags
tags={searchTerms}
addKeys={[9,32]}
addKeys={[9, 32]}
on:tags={onTagChange}
/>
</div>
@ -86,13 +96,51 @@
</form>
</div>
<div class="block">
<PostPaginator
url="/posts?tags={searchTerms.join('+')}&"
{posts}
{page}
{totalPages}
{handlePage}
<div class="columns">
{#each postChunks as postChunk}
<div class="column is-3">
{#each postChunk as post, i (post.id)}
<div class="block">
<div class="card">
<div class="card-image">
<figure class="image">
<Link to="/post/{post.id}">
<img
alt={post.id}
src={post.image_path}
/>
</Link>
</figure>
</div>
<div class="card-content">
{#if post.tags}
{#each post.tags as tag (tag)}
<TagLink {tag} />
{/each}
{:else}
None
{/if}
</div>
</div>
</div>
{/each}
</div>
{/each}
</div>
<InfiniteScroll
hasMore={newBatch.length}
elementScroll={document}
threshold={0}
on:loadMore={() => {
page++;
getData();
}}
/>
</div>
{#if newBatch.length == 0}
<div class="notification is-primary">
<p class="has-text-centered">End of posts</p>
</div>
{/if}
</div>
</section>

View File

@ -1759,6 +1759,11 @@ supports-color@^7.0.0:
dependencies:
has-flag "^4.0.0"
svelte-infinite-scroll@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/svelte-infinite-scroll/-/svelte-infinite-scroll-1.5.2.tgz#7563002645ae177d979ba0d5eee396ff3d924a6c"
integrity sha512-He1fly37Z0/4+oJZFnljTX1+TIUkjggvCWjNwBLtsWj2oiC8CIjcYT1FwJH5IoCZHVoBW+fdknxvG0xwad4MyQ==
svelte-preprocess@^4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz#454fa059c2400b15e7a3caeca18993cff9df0e96"