mirror of
https://github.com/Damillora/Shioriko.git
synced 2024-11-22 04:17:33 +00:00
feat: infinite scrolling
This commit is contained in:
parent
63b4f89820
commit
578c7ca992
@ -62,7 +62,7 @@ func postGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, models.PostPaginationResponse{
|
c.JSON(http.StatusOK, models.PostPaginationResponse{
|
||||||
CurrentPage: page,
|
CurrentPage: page,
|
||||||
TotalPage: postPages,
|
PostCount: postPages,
|
||||||
Posts: postResult,
|
Posts: postResult,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,6 @@ type BlobResponse struct {
|
|||||||
|
|
||||||
type PostPaginationResponse struct {
|
type PostPaginationResponse struct {
|
||||||
CurrentPage int `json:"currentPage"`
|
CurrentPage int `json:"currentPage"`
|
||||||
TotalPage int `json:"totalPage"`
|
PostCount int `json:"postCount"`
|
||||||
Posts []PostListItem `json:"posts"`
|
Posts []PostListItem `json:"posts"`
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"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"
|
||||||
@ -83,7 +82,7 @@ func UpdatePost(id string, model models.PostUpdateModel) (*database.Post, error)
|
|||||||
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(math.Abs(float64(count-1))/perPage) + 1
|
return int(count)
|
||||||
}
|
}
|
||||||
func CountPostPagesTag(tagSyntax []string) int {
|
func CountPostPagesTag(tagSyntax []string) int {
|
||||||
tags, err := ParseReadTags(tagSyntax)
|
tags, err := ParseReadTags(tagSyntax)
|
||||||
@ -93,8 +92,7 @@ func CountPostPagesTag(tagSyntax []string) int {
|
|||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
count = database.DB.Model(&tags).Distinct().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)
|
||||||
return int(math.Abs(float64(count-1))/perPage) + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeletePost(id string) error {
|
func DeletePost(id string) error {
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"postcss": "^8.2.14",
|
"postcss": "^8.2.14",
|
||||||
"query-string": "^7.0.0",
|
"query-string": "^7.0.0",
|
||||||
"sirv-cli": "^1.0.0",
|
"sirv-cli": "^1.0.0",
|
||||||
|
"svelte-infinite-scroll": "^1.5.2",
|
||||||
"svelte-preprocess": "^4.7.3",
|
"svelte-preprocess": "^4.7.3",
|
||||||
"svelte-routing": "^1.6.0",
|
"svelte-routing": "^1.6.0",
|
||||||
"svelte-tags-input": "^2.7.1"
|
"svelte-tags-input": "^2.7.1"
|
||||||
|
@ -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">…</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>
|
|
||||||
<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>
|
|
@ -1,52 +1,62 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { getPostSearchTag } from "../api.js";
|
import { getPostSearchTag } from "../api.js";
|
||||||
import { navigate } from "svelte-routing";
|
import { Link, navigate } from "svelte-routing";
|
||||||
import PostPaginator from "../PostPaginator.svelte";
|
import InfiniteScroll from "svelte-infinite-scroll";
|
||||||
|
import TagLink from "../TagLink.svelte";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import Tags from "svelte-tags-input";
|
import Tags from "svelte-tags-input";
|
||||||
|
import { add_attribute } from "svelte/internal";
|
||||||
|
|
||||||
export let location;
|
export let location;
|
||||||
|
|
||||||
let searchTerms = [];
|
let searchTerms = [];
|
||||||
|
|
||||||
let page = 1;
|
let page = 1;
|
||||||
let totalPages = 1;
|
|
||||||
let posts = [];
|
let posts = [];
|
||||||
const getData = async () => {
|
let newBatch = [];
|
||||||
const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
|
|
||||||
if (Array.isArray(data.posts)) {
|
const splitToChunks = (array, parts) => {
|
||||||
posts = data.posts;
|
let result = [];
|
||||||
totalPages = data.totalPage;
|
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;
|
let queryParams;
|
||||||
|
|
||||||
const onTagChange = (value) => {
|
const onTagChange = (value) => {
|
||||||
searchTerms = value.detail.tags;
|
searchTerms = value.detail.tags;
|
||||||
};
|
};
|
||||||
|
|
||||||
$: {
|
onMount(() => {
|
||||||
queryParams = queryString.parse(location.search);
|
queryParams = queryString.parse(location.search);
|
||||||
console.log(queryParams);
|
|
||||||
if (queryParams.page) {
|
|
||||||
page = parseInt(queryParams.page);
|
|
||||||
}
|
|
||||||
if (queryParams.tags) {
|
if (queryParams.tags) {
|
||||||
searchTerms = queryParams.tags.split(" ");
|
searchTerms = queryParams.tags.split(" ");
|
||||||
} else {
|
} else {
|
||||||
searchTerms = [];
|
searchTerms = [];
|
||||||
}
|
}
|
||||||
getData();
|
getData();
|
||||||
}
|
});
|
||||||
|
|
||||||
const handlePage = (i) => {
|
|
||||||
return () => {
|
|
||||||
page = i;
|
|
||||||
getData();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSearch = (i) => {
|
const onSearch = (i) => {
|
||||||
if (searchTerms.length > 0) {
|
if (searchTerms.length > 0) {
|
||||||
@ -86,13 +96,51 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<PostPaginator
|
<div class="columns">
|
||||||
url="/posts?tags={searchTerms.join('+')}&"
|
{#each postChunks as postChunk}
|
||||||
{posts}
|
<div class="column is-3">
|
||||||
{page}
|
{#each postChunk as post, i (post.id)}
|
||||||
{totalPages}
|
<div class="block">
|
||||||
{handlePage}
|
<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>
|
</div>
|
||||||
|
{#if newBatch.length == 0}
|
||||||
|
<div class="notification is-primary">
|
||||||
|
<p class="has-text-centered">End of posts</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1759,6 +1759,11 @@ supports-color@^7.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
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:
|
svelte-preprocess@^4.7.3:
|
||||||
version "4.7.3"
|
version "4.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz#454fa059c2400b15e7a3caeca18993cff9df0e96"
|
resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz#454fa059c2400b15e7a3caeca18993cff9df0e96"
|
||||||
|
Loading…
Reference in New Issue
Block a user