Skip to content
Snippets Groups Projects
Commit b1580d75 authored by xaralis's avatar xaralis
Browse files

feat: drop traditional pagination in favor of auto window expansion

parent 27379b1b
No related branches found
No related tags found
No related merge requests found
Pipeline #2057 passed
REACT_APP_STYLEGUIDE_URL=https://styleguide.pir-test.eu/2.3.0
REACT_APP_STYLEGUIDE_URL=https://styleguide.pir-test.eu/2.3.1
REACT_APP_API_BASE_URL=https://cf2021.pir-test.eu/api
REACT_APP_MATOMO_ID=135
REACT_APP_SENTRY_DSN=https://aa80453ff4d54b9a9c1b49e79060498a@sentry.pir-test.eu/14
......@@ -36,8 +36,6 @@ export const loadPosts = createAsyncAction(
state.window = {
items: filteredPosts.map(property("id")),
itemCount: filteredPosts.length,
page: 1,
perPage: 20,
};
});
}
......
......@@ -113,7 +113,7 @@ export const steps = [
</div>
</>
),
placement: "right",
placement: "center",
},
{
target: ".joyride-filters",
......@@ -132,21 +132,6 @@ export const steps = [
),
placement: "bottom",
},
{
target: ".joyride-pagination",
content: (
<>
<h1 className="head-alt-sm mb-4">Stránkování příspěvků</h1>
<div className="leading-snug text-base space-y-2">
<p>
Příspěvky jsou stránkované <strong>po dvaceti</strong>. Stránky
můžeš přepínat těmito šipkami.
</p>
</div>
</>
),
placement: "bottom",
},
{
target: ".joyride-announcements",
content: (
......
......@@ -23,6 +23,7 @@ const Post = ({
currentUser,
supportThreshold,
canThumb,
reportSeen = true,
onLike,
onDislike,
onHide,
......@@ -36,20 +37,21 @@ const Post = ({
onEdit,
onArchive,
onSeen,
...props
}) => {
const { ref, inView } = useInView({
threshold: 0.8,
trackVisibility: true,
delay: 1500,
skip: seen,
delay: 1000,
skip: !reportSeen,
triggerOnce: true,
});
useEffect(() => {
if (!seen && inView && onSeen) {
if (inView && onSeen) {
onSeen();
}
});
}, [inView, onSeen]);
const wrapperClassName = classNames(
"flex items-start p-4 lg:p-2 lg:py-3 lg:-mx-2 transition duration-500",
......@@ -193,7 +195,7 @@ const Post = ({
const thumbsVisible = !archived && (type === "post" || state === "announced");
return (
<div className={wrapperClassName} ref={ref}>
<div className={wrapperClassName} ref={ref} {...props}>
<img
src={`https://a.pirati.cz/piratar/200/${author.username}.jpg`}
className="w-8 h-8 lg:w-14 lg:h-14 mr-3 rounded object-cover"
......@@ -327,7 +329,7 @@ const Post = ({
</div>
<div className="overflow-hidden">
<div
className="text-sm lg:text-base text-black leading-normal content-block overflow-x-scroll"
className="text-sm lg:text-base text-black leading-normal content-block overflow-x-scroll overflow-y-hidden"
dangerouslySetInnerHTML={htmlContent}
></div>
</div>
......
import React from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames";
import Post from "./Post";
......@@ -30,6 +30,9 @@ const PostList = ({
responderFn(post);
};
const windowSize = 20;
const [window, setWindow] = useState(windowSize);
const onPostLike = buildHandler(onLike);
const onPostDislike = buildHandler(onDislike);
const onPostEdit = buildHandler(onEdit);
......@@ -47,15 +50,32 @@ const PostList = ({
onRejectProcedureProposalByChairman
);
const onPostSeen = (post) => () => {
// Reset window when items change (possibly due to filtering).
useEffect(() => {
setWindow(windowSize);
}, [items, setWindow]);
const onPostSeen = useCallback(
(post) => () => {
if (!post.seen) {
onSeen(post);
};
}
// Once last post in window is reached, attempt show more.
if (items.indexOf(post) === window - 1) {
setWindow(window + windowSize);
}
},
[items, onSeen, window]
);
const windowItems = useMemo(() => {
return items.slice(0, window).filter((item) => !item.hidden);
}, [items, window]);
return (
<div className={classNames("space-y-px", className)}>
{items
.filter((item) => !item.hidden)
.map((item) => (
{windowItems.map((item, idx) => (
<Post
key={item.id}
datetime={item.datetime}
......@@ -64,9 +84,9 @@ const PostList = ({
state={item.state}
content={item.contentHtml}
ranking={item.ranking}
historyLog={item.historyLog}
modified={item.modified}
seen={item.seen}
reportSeen={!item.seen || idx === window - 1}
archived={item.archived}
dimIfArchived={dimArchived}
currentUser={currentUser}
......
import React, { useCallback } from "react";
import pick from "lodash/pick";
import React from "react";
import Chip from "components/Chip";
import Dropdown from "components/Dropdown";
import { PostStore } from "stores";
import { updateWindowPosts } from "utils";
const PostFilters = () => {
const { window, filters } = PostStore.useState((state) =>
pick(state, ["window", "filters", "items"])
);
const filters = PostStore.useState((state) => state.filters);
const flagsOptions = [
{ title: "Vše", value: "all" },
......@@ -25,8 +21,6 @@ const PostFilters = () => {
{ title: "Jen návrhy", value: "proposalsOnly" },
{ title: "Jen příspěvky", value: "discussionOnly" },
];
const hasNextPage = window.page * window.perPage < window.itemCount;
const hasPrevPage = window.page > 1;
const setFilter = (prop, newValue, resetPage = true) => {
PostStore.update((state) => {
......@@ -45,24 +39,6 @@ const PostFilters = () => {
const onSortChange = (newValue) => setFilter("sort", newValue, false);
const onTypeChange = (newValue) => setFilter("type", newValue);
const onNextPage = useCallback(() => {
if (hasNextPage) {
PostStore.update((state) => {
state.window.page = state.window.page + 1;
});
}
}, [hasNextPage]);
const onPrevPage = useCallback(() => {
if (hasPrevPage) {
PostStore.update((state) => {
state.window.page = state.window.page - 1;
});
}
}, [hasPrevPage]);
const enabledPaginatorClass = "cursor-pointer text-xs";
const disabledPaginatorClass = "opacity-25 cursor-not-allowed text-xs";
return (
<div className="flex flex-col space-y-2 xl:space-y-0 xl:space-x-8 xl:flex-row xl:items-center">
<div className="-mx-1 joyride-filters">
......@@ -85,29 +61,6 @@ const PostFilters = () => {
className="text-xs ml-1 mt-2 xl:mt-0"
/>
</div>
<div className="joyride-pagination">
<Chip
color="grey-125"
className={
hasPrevPage ? enabledPaginatorClass : disabledPaginatorClass
}
hoveractive
onClick={onPrevPage}
>
<span className="ico--chevron-left"></span>
</Chip>
<Chip
color="grey-125"
className={
hasNextPage ? enabledPaginatorClass : disabledPaginatorClass
}
hoveractive
onClick={onNextPage}
>
<span className="ico--chevron-right"></span>
</Chip>
</div>
</div>
);
};
......
import React, { useCallback } from "react";
import React, { useCallback, useMemo } from "react";
import pick from "lodash/pick";
import {
......@@ -191,9 +191,9 @@ const PostsContainer = ({ className, showAddPostCta }) => {
[setUserToInvite]
);
const sliceStart = (window.page - 1) * window.perPage;
const sliceEnd = window.page * window.perPage;
const windowItems = window.items.map((postId) => items[postId]);
const windowItems = useMemo(() => {
return window.items.map((postId) => items[postId]);
}, [items, window.items]);
return (
<>
......@@ -203,7 +203,7 @@ const PostsContainer = ({ className, showAddPostCta }) => {
</ErrorMessage>
)}
<PostList
items={windowItems.slice(sliceStart, sliceEnd)}
items={windowItems}
showAddPostCta={showAddPostCta}
canThumb={isAuthenticated}
onLike={like.run}
......
......@@ -213,10 +213,10 @@ const Home = () => {
<div className="pl-4 pt-1 lg:pt-5">
<div className="space-x-4 inline-flex items-center">
<button
className="ico--question text-grey-200 hover:text-black text-lg"
className="ico--question text-grey-200 hidden lg:block hover:text-black text-lg"
aria-label="Potřebuješ pomoc? Spusť si znovu nápovědu jak tuhle aplikaci používat."
data-tip="Potřebuješ pomoc? Spusť si znovu nápovědu jak tuhle aplikaci používat."
data-tip-at="left"
data-tip-at="top"
onClick={showTutorial}
/>
{displayActions && (
......
......@@ -48,8 +48,6 @@ const postStoreInitial = {
window: {
items: [],
itemCount: 0,
page: 1,
perPage: 20,
},
filters: {
flags: "active",
......
......@@ -158,8 +158,6 @@ declare namespace CF2021 {
window: {
items: string[];
itemCount: number;
page: number;
perPage: number;
};
filters: PostStoreFilters;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment