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

feat: pull posts and announcements from store, support posts filtering

parent d338cf53
No related branches found
No related tags found
No related merge requests found
Showing
with 651 additions and 355 deletions
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
......@@ -8,6 +8,7 @@
"classnames": "^2.2.6",
"date-fns": "^2.16.1",
"keycloak-js": "^10.0.2",
"lodash": "^4.17.20",
"pullstate": "^1.20.4",
"react": "^16.13.1",
"react-device-detect": "^1.13.1",
......@@ -51,7 +52,7 @@
"^@?\\w"
],
[
"^(components|containers|pages|utils)(/.*|$)"
"^(components|containers|pages|utils|stores|keycloak)(/.*|$)"
],
[
"^(test-utils)(/.*|$)"
......
import React, { useCallback, useEffect } from "react";
import React, { Suspense } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { KeycloakProvider } from "@react-keycloak/web";
import * as Sentry from "@sentry/react";
import { AuthStore } from "stores";
import { loadGroupMappings } from "actions/misc";
import Footer from "components/Footer";
import Navbar from "components/Navbar";
import Home from "pages/Home";
import Program from "pages/Program";
import { AuthStore } from "stores";
import keycloak from "./keycloak";
......@@ -30,6 +31,7 @@ const onKeycloakEvent = (event) => {
state.user = {
name: keycloak.tokenParsed.name,
groups: keycloak.tokenParsed.groups,
accessToken: keycloak.token,
};
});
}
......@@ -50,18 +52,7 @@ const LoadingComponent = (
);
const BaseApp = () => {
const loadGroupMappings = useCallback(async () => {
const resp = await fetch("https://iapi.pirati.cz/v1/groups");
const mappings = await resp.json();
AuthStore.update((state) => {
state.groupMappings = mappings;
});
}, []);
useEffect(() => {
loadGroupMappings();
}, [loadGroupMappings]);
loadGroupMappings.read();
return (
<Router>
......@@ -91,13 +82,16 @@ const AuthenticatedApp = () => {
LoadingComponent={LoadingComponent}
onEvent={onKeycloakEvent}
>
<Suspense fallback={LoadingComponent}>
<BaseApp />
</Suspense>
</KeycloakProvider>
</>
);
};
const ErrorBoundaryFallback = () => (
const ErrorBoundaryFallback = ({ error }) => {
return (
<div className="h-screen w-screen flex justify-center items-center">
<div className="text-center">
<h1 className="head-alt-xl text-red-600 mb-4">
......@@ -114,6 +108,7 @@ const ErrorBoundaryFallback = () => (
</div>
</div>
);
};
const App = Sentry.withProfiler(() => {
return (
......
import { createAsyncAction, errorResult, successResult } from "pullstate";
import { AuthStore } from "stores";
export const loadGroupMappings = createAsyncAction(
async () => {
try {
const resp = await fetch("https://iapi.pirati.cz/v1/groups");
const mappings = await resp.json();
return successResult(mappings);
} catch (err) {
return errorResult([], err.toString());
}
},
{
postActionHook: ({ result }) => {
if (!result.error) {
AuthStore.update((state) => {
state.groupMappings = result.payload;
});
}
},
}
);
import React from "react";
import classNames from "classnames";
const Chip = ({ className, color = "grey-125", condensed, children }) => {
const Chip = ({
className,
color = "grey-125",
condensed,
hoveractive = false,
children,
...props
}) => {
const chipClass = classNames(
"chip",
{
"chip--condensed": !!condensed,
"chip--hoveractive": !!hoveractive,
},
`chip--${color}`,
className
);
return <span className={chipClass}>{children}</span>;
return (
<span className={chipClass} {...props}>
{children}
</span>
);
};
export default Chip;
import React from "react";
import classNames from "classnames";
const Dropdown = ({ value, options, onChange, className }) => {
const onSelectChanged = (evt) => {
onChange(evt.target.value);
};
return (
<span
className={classNames(
"chip chip--grey-125 chip--select chip--hoveractive",
className
)}
>
<select onChange={onSelectChanged} value={value}>
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.title}
</option>
))}
</select>
<span className="chip__icon ico--chevron-down"></span>
</span>
);
};
export default Dropdown;
......@@ -2,9 +2,9 @@ import React, { useCallback, useState } from "react";
import { isBrowser } from "react-device-detect";
import { NavLink } from "react-router-dom";
import { useKeycloak } from "@react-keycloak/web";
import { AuthStore } from "stores";
import Button from "components/Button";
import { AuthStore } from "stores";
const Navbar = () => {
const [showMenu, setShowMenu] = useState(isBrowser);
......
import React from "react";
import classNames from "classnames";
const Thumbs = ({ likes, dislikes, onLike, onDislike, canThumb }) => {
return (
<div>
<div className="space-x-2 text-sm flex items-center">
<button
className={classNames("text-blue-300 flex items-center space-x-1", {
"cursor-not-allowed": !canThumb,
})}
disabled={!canThumb}
onClick={onLike}
>
<span className="font-bold">{likes}</span>
<i className="ico--thumbs-up"></i>
</button>
<button
className={classNames("text-red-600 flex items-center space-x-1", {
"cursor-not-allowed": !canThumb,
})}
disabled={!canThumb}
onClick={onDislike}
>
<i className="ico--thumbs-down transform -scale-x-1"></i>
<span className="font-bold">{dislikes}</span>
</button>
</div>
</div>
);
};
export default React.memo(Thumbs);
......@@ -34,7 +34,7 @@ const Announcement = ({
"accepted-procedure-proposal": "green-400",
voting: "red-600",
announcement: "cyan-500",
"user-bank": "black",
"user-ban": "black",
}[type];
const chipLabel = {
......
......@@ -3,6 +3,7 @@ import classNames from "classnames";
import { format } from "date-fns";
import Chip from "components/Chip";
import Thumbs from "components/Thumbs";
const Post = ({
className,
......@@ -15,12 +16,16 @@ const Post = ({
archived,
state,
historyLog,
onLike,
onDislike,
dimIfArchived = true,
}) => {
const wrapperClassName = classNames(
"flex items-start p-4 lg:p-2 lg:py-4 lg:-mx-2",
"flex items-start p-4 lg:p-2 lg:py-3 lg:-mx-2",
{
"bg-yellow-100 bg-opacity-50": !seen,
"opacity-25 hover:opacity-100 transition-opacity duration-200": !!archived,
"opacity-25 hover:opacity-100 transition-opacity duration-200":
dimIfArchived && !!archived,
},
className
);
......@@ -127,16 +132,13 @@ const Post = ({
</div>
</div>
<div className="flex items-center space-x-4">
<div className="space-x-2 text-sm flex items-center">
<button className="text-blue-300 flex items-center space-x-1">
<span className="font-bold">{ranking.likes}</span>
<i className="ico--thumbs-up"></i>
</button>
<button className="text-red-600 flex items-center space-x-1">
<i className="ico--thumbs-down transform -scale-x-1"></i>
<span className="font-bold">{ranking.dislikes}</span>
</button>
</div>
<Thumbs
likes={ranking.likes}
dislikes={ranking.dislikes}
onLike={onLike}
onDislike={onDislike}
canThumb={ranking.myVote === "none"}
/>
</div>
</div>
</div>
......
import React, { useCallback } from "react";
import pick from "lodash/pick";
import Chip from "components/Chip";
import Dropdown from "components/Dropdown";
import { PostStore } from "stores";
import { filterPosts } from "utils";
const PostFilters = () => {
const { window, filters, items } = PostStore.useState((state) =>
pick(state, ["window", "filters", "items"])
);
const flagsOptions = [
{ title: "Vše", value: "all" },
{ title: "Jen aktivní", value: "active" },
{ title: "Jen archivované", value: "archived" },
];
const sortOptions = [
{ title: "Podle času", value: "byDate" },
{ title: "Podle podpory", value: "byScore" },
];
const typeOptions = [
{ title: "Návrhy i příspěvky", value: "all" },
{ 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) => {
state.filters[prop] = newValue;
state.window.items = filterPosts(state.filters, items);
state.window.itemCount = state.window.items.length;
if (resetPage) {
state.window.page = 1;
}
});
};
const onFlagsChange = (newValue) => setFilter("flags", newValue);
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">
<Dropdown
value={filters.flags}
onChange={onFlagsChange}
options={flagsOptions}
className="text-xs ml-1 mt-2 xl:mt-0"
/>
<Dropdown
value={filters.sort}
onChange={onSortChange}
options={sortOptions}
className="text-xs ml-1 mt-2 xl:mt-0"
/>
<Dropdown
value={filters.type}
onChange={onTypeChange}
options={typeOptions}
className="text-xs ml-1 mt-2 xl:mt-0"
/>
</div>
<div>
<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>
);
};
export default PostFilters;
......@@ -3,7 +3,18 @@ import classNames from "classnames";
import Post from "./Post";
const PostList = ({ className, items }) => {
const PostList = ({ className, items, onLike, onDislike, dimArchived }) => {
const onPostLike = (post) => {
return (evt) => {
onLike(post);
};
};
const onPostDislike = (post) => {
return (evt) => {
onDislike(post);
};
};
return (
<div className={classNames("space-y-px", className)}>
{items
......@@ -20,6 +31,9 @@ const PostList = ({ className, items }) => {
historyLog={item.historyLog}
seen={item.seen}
archived={item.archived}
onLike={onPostLike(item)}
onDislike={onPostDislike(item)}
dimIfArchived={dimArchived}
/>
))}
</div>
......
import React from "react";
import AnnouncementList from "components/annoucements/AnnouncementList";
import { AnnouncementStore } from "stores";
const AnnoucementsContainer = () => {
/** @type {CF2021.Announcement[]} */
const items = [
{
id: "1",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: false,
type: "rejected-procedure-proposal",
},
{
id: "2",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: false,
type: "accepted-procedure-proposal",
},
{
id: "3",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "suggested-procedure-proposal",
},
{
id: "4",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "voting",
},
{
id: "5",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "announcement",
},
{
id: "6",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "user-ban",
},
];
const items = AnnouncementStore.useState((state) => state.items);
return <AnnouncementList items={items} />;
};
......
import React from "react";
import PostList from "components/posts/PostList";
import { PostStore } from "stores";
const PostsContainer = ({ className }) => {
/** @type {CF2021.Post[]} */
const items = [
{
id: "1",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "cf",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: false,
archived: false,
type: "post",
},
{
id: "2",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "cf",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: false,
archived: false,
type: "post",
},
{
id: "3",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "cf",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: false,
type: "post",
},
{
id: "4",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: false,
type: "post",
},
{
id: "5",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: false,
type: "post",
},
{
id: "6",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: true,
type: "post",
},
{
id: "7",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: true,
type: "procedure-proposal",
state: "pending",
},
{
id: "8",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: false,
type: "procedure-proposal",
state: "announced",
historyLog: [
{
attribute: "content",
datetime: new Date(),
newValue: "Lemme know",
originator: "chairman",
},
],
},
{
id: "9",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: false,
type: "procedure-proposal",
state: "accepted",
},
{
id: "10",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: false,
type: "procedure-proposal",
state: "rejected",
},
{
id: "11",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 1,
score: 4,
},
seen: true,
archived: true,
type: "procedure-proposal",
state: "rejected-by-chairman",
},
];
const window = PostStore.useState((state) => state.window);
const showingArchivedOnly = PostStore.useState(
(state) => state.filters.flags === "archived"
);
return <PostList items={items} className={className} />;
const onLike = (post) => console.log("like", post);
const onDislike = (post) => console.log("dislike", post);
const sliceStart = (window.page - 1) * window.perPage;
const sliceEnd = window.page * window.perPage;
return (
<PostList
items={window.items.slice(sliceStart, sliceEnd)}
onLike={onLike}
onDislike={onDislike}
className={className}
dimArchived={!showingArchivedOnly}
/>
);
};
export default PostsContainer;
import React from "react";
import PostFilters from "components/posts/PostFilters";
import AnnouncementsContainer from "containers/AnnoucementsContainer";
import PostsContainer from "containers/PostsContainer";
......@@ -60,44 +61,7 @@ const Home = () => {
<h2 className="head-heavy-sm whitespace-no-wrap">
Příspěvky v rozpravě
</h2>
<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">
<span className="chip chip--grey-125 chip--select chip--hoveractive text-xs ml-1 mt-2 xl:mt-0">
<select>
<option value="">Jen nezpracované</option>
<option value="">Vše</option>
<option value="">Jen aktivní</option>
<option value="">Jen archivované</option>
</select>
<span className="chip__icon ico--chevron-down"></span>
</span>
<span className="chip chip--grey-125 chip--select chip--hoveractive text-xs ml-1 mt-2 xl:mt-0">
<select>
<option value="">Podle času</option>
<option value="">Podle podpory</option>
</select>
<span className="chip__icon ico--chevron-down"></span>
</span>
<span className="chip chip--grey-125 chip--select chip--hoveractive text-xs ml-1 mt-2 xl:mt-0">
<select>
<option value="">Návrhy i příspěvky</option>
<option value="">Jen návrhy</option>
<option value="">Jen příspěvky</option>
</select>
<span className="chip__icon ico--chevron-down"></span>
</span>
</div>
<div>
<span className="chip chip--grey-125 chip--hoveractive text-xs">
<span className="ico--chevron-left"></span>
</span>
<span className="chip chip--grey-125 chip--hoveractive ml-1">
<span className="ico--chevron-right"></span>
</span>
</div>
</div>
<PostFilters />
</div>
<PostsContainer className="container-padding--zero lg:container-padding--auto" />
......
import memoize from "lodash/memoize";
import { Store } from "pullstate";
import { filterPosts } from "utils";
/** @type {CF2021.AuthStorePayload} */
const authStoreInitial = {
isAuthenticated: false,
......@@ -10,14 +13,315 @@ export const AuthStore = new Store(authStoreInitial);
/** @type {CF2021.AnnouncementStorePayload} */
const announcementStoreInitial = {
items: [],
items: [
{
id: "1",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: false,
type: "rejected-procedure-proposal",
},
{
id: "2",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: false,
type: "accepted-procedure-proposal",
},
{
id: "3",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "suggested-procedure-proposal",
},
{
id: "4",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "voting",
},
{
id: "5",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "announcement",
},
{
id: "6",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
seen: true,
type: "user-ban",
},
],
};
export const AnnouncementStore = new Store(announcementStoreInitial);
const allPosts = [
{
id: "1",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "cf",
},
ranking: {
likes: 0,
dislikes: 0,
score: 0,
myVote: "none",
},
seen: false,
archived: false,
type: "post",
},
{
id: "2",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "cf",
},
ranking: {
likes: 1,
dislikes: 0,
score: 1,
myVote: "none",
},
seen: false,
archived: false,
type: "post",
},
{
id: "3",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "cf",
},
ranking: {
likes: 5,
dislikes: 5,
score: 0,
myVote: "none",
},
seen: true,
archived: false,
type: "post",
},
{
id: "4",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 0,
dislikes: 10,
score: -10,
myVote: "none",
},
seen: true,
archived: false,
type: "post",
},
{
id: "5",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 1,
dislikes: 1,
score: 0,
myVote: "none",
},
seen: true,
archived: false,
type: "post",
},
{
id: "6",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 3,
score: 2,
myVote: "none",
},
seen: true,
archived: true,
type: "post",
},
{
id: "7",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 8,
score: -3,
myVote: "none",
},
seen: true,
archived: true,
type: "procedure-proposal",
state: "pending",
},
{
id: "8",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 2,
dislikes: 1,
score: 1,
myVote: "like",
},
seen: true,
archived: false,
type: "procedure-proposal",
state: "announced",
historyLog: [
{
attribute: "content",
datetime: new Date(),
newValue: "Lemme know",
originator: "chairman",
},
],
},
{
id: "9",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 0,
score: 5,
myVote: "dislike",
},
seen: true,
archived: false,
type: "procedure-proposal",
state: "accepted",
},
{
id: "10",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 5,
dislikes: 8,
score: -3,
myVote: "none",
},
seen: true,
archived: false,
type: "procedure-proposal",
state: "rejected",
},
{
id: "11",
content:
"Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
datetime: new Date(),
author: {
name: "John Doe",
group: "KS Pardubický kraj",
},
ranking: {
likes: 10,
dislikes: 1,
score: 9,
myVote: "none",
},
seen: true,
archived: true,
type: "procedure-proposal",
state: "rejected-by-chairman",
},
];
const initialPostFilters = {
flags: "all",
sort: "byDate",
type: "all",
};
const filteredPosts = filterPosts(initialPostFilters, allPosts);
/** @type {CF2021.PostStorePayload} */
const postStoreInitial = {
items: [],
items: allPosts,
itemCount: allPosts.length,
window: {
items: filteredPosts,
itemCount: filteredPosts.length,
page: 1,
perPage: 5,
},
filters: initialPostFilters,
};
export const PostStore = new Store(postStoreInitial);
export const getGroupByCode = memoize(
(groupMappings, groupCode) => {
return groupMappings.find((gm) => gm.code === groupCode);
},
(groupMappings, groupCode) => [groupMappings, groupCode]
);
export const getGroupsByCode = memoize((groupMappings, groupCodes) => {
return groupCodes.map((code) => getGroupByCode(groupMappings, code));
});
import filter from "lodash/filter";
export const filterPosts = (filters, allItems) => {
const predicate = {};
if (filters.flags === "active") {
predicate.archived = false;
}
if (filters.flags === "archived") {
predicate.archived = true;
}
if (filters.type === "proposalsOnly") {
predicate.type = "procedure-proposal";
}
if (filters.type === "discussionOnly") {
predicate.type = "post";
}
let filteredItems = filter(allItems, predicate);
if (filters.sort === "byDate") {
return filteredItems.sort((a, b) => b.datetime - a.datetime);
}
return filteredItems.sort((a, b) => b.ranking.score - a.ranking.score);
};
......@@ -29,6 +29,7 @@ declare namespace CF2021 {
user: {
name: string;
groups: string[];
accessToken: string;
};
}
......@@ -73,6 +74,7 @@ declare namespace CF2021 {
score: number;
likes: number;
dislikes: number;
myVote: "like" | "dislike" | "none";
};
historyLog: {
attribute: string;
......@@ -104,5 +106,17 @@ declare namespace CF2021 {
export interface PostStorePayload {
items: Post[];
itemCount: number;
window: {
items: Post[];
itemCount: number;
page: number;
perPage: number;
};
filters: {
flags: "all" | "active" | "archived";
sort: "byDate" | "byScore";
type: "all" | "proposalsOnly" | "discussionOnly";
};
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment