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
Branches
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_API_BASE_URL=https://cf2021.pir-test.eu/api
REACT_APP_MATOMO_ID=135 REACT_APP_MATOMO_ID=135
REACT_APP_SENTRY_DSN=https://aa80453ff4d54b9a9c1b49e79060498a@sentry.pir-test.eu/14 REACT_APP_SENTRY_DSN=https://aa80453ff4d54b9a9c1b49e79060498a@sentry.pir-test.eu/14
...@@ -36,8 +36,6 @@ export const loadPosts = createAsyncAction( ...@@ -36,8 +36,6 @@ export const loadPosts = createAsyncAction(
state.window = { state.window = {
items: filteredPosts.map(property("id")), items: filteredPosts.map(property("id")),
itemCount: filteredPosts.length, itemCount: filteredPosts.length,
page: 1,
perPage: 20,
}; };
}); });
} }
......
...@@ -113,7 +113,7 @@ export const steps = [ ...@@ -113,7 +113,7 @@ export const steps = [
</div> </div>
</> </>
), ),
placement: "right", placement: "center",
}, },
{ {
target: ".joyride-filters", target: ".joyride-filters",
...@@ -132,21 +132,6 @@ export const steps = [ ...@@ -132,21 +132,6 @@ export const steps = [
), ),
placement: "bottom", 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", target: ".joyride-announcements",
content: ( content: (
......
...@@ -23,6 +23,7 @@ const Post = ({ ...@@ -23,6 +23,7 @@ const Post = ({
currentUser, currentUser,
supportThreshold, supportThreshold,
canThumb, canThumb,
reportSeen = true,
onLike, onLike,
onDislike, onDislike,
onHide, onHide,
...@@ -36,20 +37,21 @@ const Post = ({ ...@@ -36,20 +37,21 @@ const Post = ({
onEdit, onEdit,
onArchive, onArchive,
onSeen, onSeen,
...props
}) => { }) => {
const { ref, inView } = useInView({ const { ref, inView } = useInView({
threshold: 0.8, threshold: 0.8,
trackVisibility: true, trackVisibility: true,
delay: 1500, delay: 1000,
skip: seen, skip: !reportSeen,
triggerOnce: true, triggerOnce: true,
}); });
useEffect(() => { useEffect(() => {
if (!seen && inView && onSeen) { if (inView && onSeen) {
onSeen(); onSeen();
} }
}); }, [inView, onSeen]);
const wrapperClassName = classNames( const wrapperClassName = classNames(
"flex items-start p-4 lg:p-2 lg:py-3 lg:-mx-2 transition duration-500", "flex items-start p-4 lg:p-2 lg:py-3 lg:-mx-2 transition duration-500",
...@@ -193,7 +195,7 @@ const Post = ({ ...@@ -193,7 +195,7 @@ const Post = ({
const thumbsVisible = !archived && (type === "post" || state === "announced"); const thumbsVisible = !archived && (type === "post" || state === "announced");
return ( return (
<div className={wrapperClassName} ref={ref}> <div className={wrapperClassName} ref={ref} {...props}>
<img <img
src={`https://a.pirati.cz/piratar/200/${author.username}.jpg`} 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" className="w-8 h-8 lg:w-14 lg:h-14 mr-3 rounded object-cover"
...@@ -327,7 +329,7 @@ const Post = ({ ...@@ -327,7 +329,7 @@ const Post = ({
</div> </div>
<div className="overflow-hidden"> <div className="overflow-hidden">
<div <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} dangerouslySetInnerHTML={htmlContent}
></div> ></div>
</div> </div>
......
import React from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import Post from "./Post"; import Post from "./Post";
...@@ -30,6 +30,9 @@ const PostList = ({ ...@@ -30,6 +30,9 @@ const PostList = ({
responderFn(post); responderFn(post);
}; };
const windowSize = 20;
const [window, setWindow] = useState(windowSize);
const onPostLike = buildHandler(onLike); const onPostLike = buildHandler(onLike);
const onPostDislike = buildHandler(onDislike); const onPostDislike = buildHandler(onDislike);
const onPostEdit = buildHandler(onEdit); const onPostEdit = buildHandler(onEdit);
...@@ -47,15 +50,32 @@ const PostList = ({ ...@@ -47,15 +50,32 @@ const PostList = ({
onRejectProcedureProposalByChairman 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); 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 ( return (
<div className={classNames("space-y-px", className)}> <div className={classNames("space-y-px", className)}>
{items {windowItems.map((item, idx) => (
.filter((item) => !item.hidden)
.map((item) => (
<Post <Post
key={item.id} key={item.id}
datetime={item.datetime} datetime={item.datetime}
...@@ -64,9 +84,9 @@ const PostList = ({ ...@@ -64,9 +84,9 @@ const PostList = ({
state={item.state} state={item.state}
content={item.contentHtml} content={item.contentHtml}
ranking={item.ranking} ranking={item.ranking}
historyLog={item.historyLog}
modified={item.modified} modified={item.modified}
seen={item.seen} seen={item.seen}
reportSeen={!item.seen || idx === window - 1}
archived={item.archived} archived={item.archived}
dimIfArchived={dimArchived} dimIfArchived={dimArchived}
currentUser={currentUser} currentUser={currentUser}
......
import React, { useCallback } from "react"; import React from "react";
import pick from "lodash/pick";
import Chip from "components/Chip";
import Dropdown from "components/Dropdown"; import Dropdown from "components/Dropdown";
import { PostStore } from "stores"; import { PostStore } from "stores";
import { updateWindowPosts } from "utils"; import { updateWindowPosts } from "utils";
const PostFilters = () => { const PostFilters = () => {
const { window, filters } = PostStore.useState((state) => const filters = PostStore.useState((state) => state.filters);
pick(state, ["window", "filters", "items"])
);
const flagsOptions = [ const flagsOptions = [
{ title: "Vše", value: "all" }, { title: "Vše", value: "all" },
...@@ -25,8 +21,6 @@ const PostFilters = () => { ...@@ -25,8 +21,6 @@ const PostFilters = () => {
{ title: "Jen návrhy", value: "proposalsOnly" }, { title: "Jen návrhy", value: "proposalsOnly" },
{ title: "Jen příspěvky", value: "discussionOnly" }, { 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) => { const setFilter = (prop, newValue, resetPage = true) => {
PostStore.update((state) => { PostStore.update((state) => {
...@@ -45,24 +39,6 @@ const PostFilters = () => { ...@@ -45,24 +39,6 @@ const PostFilters = () => {
const onSortChange = (newValue) => setFilter("sort", newValue, false); const onSortChange = (newValue) => setFilter("sort", newValue, false);
const onTypeChange = (newValue) => setFilter("type", newValue); 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 ( 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="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"> <div className="-mx-1 joyride-filters">
...@@ -85,29 +61,6 @@ const PostFilters = () => { ...@@ -85,29 +61,6 @@ const PostFilters = () => {
className="text-xs ml-1 mt-2 xl:mt-0" className="text-xs ml-1 mt-2 xl:mt-0"
/> />
</div> </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> </div>
); );
}; };
......
import React, { useCallback } from "react"; import React, { useCallback, useMemo } from "react";
import pick from "lodash/pick"; import pick from "lodash/pick";
import { import {
...@@ -191,9 +191,9 @@ const PostsContainer = ({ className, showAddPostCta }) => { ...@@ -191,9 +191,9 @@ const PostsContainer = ({ className, showAddPostCta }) => {
[setUserToInvite] [setUserToInvite]
); );
const sliceStart = (window.page - 1) * window.perPage; const windowItems = useMemo(() => {
const sliceEnd = window.page * window.perPage; return window.items.map((postId) => items[postId]);
const windowItems = window.items.map((postId) => items[postId]); }, [items, window.items]);
return ( return (
<> <>
...@@ -203,7 +203,7 @@ const PostsContainer = ({ className, showAddPostCta }) => { ...@@ -203,7 +203,7 @@ const PostsContainer = ({ className, showAddPostCta }) => {
</ErrorMessage> </ErrorMessage>
)} )}
<PostList <PostList
items={windowItems.slice(sliceStart, sliceEnd)} items={windowItems}
showAddPostCta={showAddPostCta} showAddPostCta={showAddPostCta}
canThumb={isAuthenticated} canThumb={isAuthenticated}
onLike={like.run} onLike={like.run}
......
...@@ -213,10 +213,10 @@ const Home = () => { ...@@ -213,10 +213,10 @@ const Home = () => {
<div className="pl-4 pt-1 lg:pt-5"> <div className="pl-4 pt-1 lg:pt-5">
<div className="space-x-4 inline-flex items-center"> <div className="space-x-4 inline-flex items-center">
<button <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." 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="Potřebuješ pomoc? Spusť si znovu nápovědu jak tuhle aplikaci používat."
data-tip-at="left" data-tip-at="top"
onClick={showTutorial} onClick={showTutorial}
/> />
{displayActions && ( {displayActions && (
......
...@@ -48,8 +48,6 @@ const postStoreInitial = { ...@@ -48,8 +48,6 @@ const postStoreInitial = {
window: { window: {
items: [], items: [],
itemCount: 0, itemCount: 0,
page: 1,
perPage: 20,
}, },
filters: { filters: {
flags: "active", flags: "active",
......
...@@ -158,8 +158,6 @@ declare namespace CF2021 { ...@@ -158,8 +158,6 @@ declare namespace CF2021 {
window: { window: {
items: string[]; items: string[];
itemCount: number; itemCount: number;
page: number;
perPage: number;
}; };
filters: PostStoreFilters; filters: PostStoreFilters;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment