Skip to content

Commit

Permalink
Merge pull request #1613 from ecency/feature/polls-api-upd
Browse files Browse the repository at this point in the history
Polls update & Polls in waves
  • Loading branch information
feruzm authored Jun 7, 2024
2 parents dcd4ea3 + eed3027 commit aa670f1
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { DeckThreadItemHeader } from "./deck-thread-item-header";
import { dateToRelative } from "../../../../helper/parse-date";
import EntryMenu from "../../../../components/entry-menu";
import { Button } from "@ui/button";
import { useEntryPollExtractor } from "../../../../pages/entry/utils";
import { PollWidget } from "../../../polls";

export interface ThreadItemProps {
initialEntry: IdentifiableEntry;
Expand Down Expand Up @@ -62,6 +64,8 @@ export const ThreadItem = ({
const [status, setStatus] = useState<"default" | "pending">("default");
const [intervalStarted, setIntervalStarted] = useState(false);

const poll = useEntryPollExtractor(entry);

useEntryChecking(entry, intervalStarted, (nextEntry) => {
updateCache([
{ ...nextEntry, host: initialEntry.host, container: initialEntry.container } as Entry
Expand Down Expand Up @@ -126,6 +130,11 @@ export const ThreadItem = ({
setRenderInitiated={setRenderInitiated}
onResize={onResize}
/>
{poll && (
<div className="p-4">
<PollWidget entry={entry} compact={true} poll={poll} isReadOnly={false} />
</div>
)}
{entry.updated !== entry.created && (
<div className="px-3 pb-3 updated-label">
{_t("decks.columns.updated", { n: dateToRelative(entry.updated) })}
Expand Down
13 changes: 10 additions & 3 deletions src/common/features/decks/deck-threads-form/api/threads-api.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Entry } from "../../../../store/entries/types";
import { createReplyPermlink, makeJsonMetaDataReply } from "../../../../helper/posting";
import { createReplyPermlink } from "../../../../helper/posting";
import { comment } from "../../../../api/operations";
import tempEntry from "../../../../helper/temp-entry";
import { FullAccount } from "../../../../store/accounts/types";
import { version } from "../../../../../../package.json";
import { useMappedStore } from "../../../../store/use-mapped-store";
import { v4 } from "uuid";
import { ThreadItemEntry } from "../../columns/deck-threads-manager";
import { useContext } from "react";
import { EntriesCacheContext } from "../../../../core";
import { PollsContext } from "../../../../pages/submit/hooks/polls-manager";
import { EntryMetadataManagement } from "../../../entry-management";

export function useThreadsApi() {
const { activeUser } = useMappedStore();
const { addReply, updateRepliesCount } = useContext(EntriesCacheContext);
const { activePoll } = useContext(PollsContext);

const request = async (entry: Entry, raw: string, editingEntry?: ThreadItemEntry) => {
if (!activeUser || !activeUser.data.__loaded) {
Expand All @@ -24,7 +26,12 @@ export function useThreadsApi() {
const permlink = editingEntry?.permlink ?? createReplyPermlink(entry.author);
const tags = raw.match(/\#[a-zA-Z0-9]+/g)?.map((tag) => tag.replace("#", "")) ?? ["ecency"];

const jsonMeta = makeJsonMetaDataReply(tags, version);
const jsonMeta = EntryMetadataManagement.EntryMetadataManager.shared
.builder()
.default()
.withTags(tags)
.withPoll(activePoll)
.build();

await comment(author, parentAuthor, parentPermlink, permlink, "", raw, jsonMeta, null, true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { comment } from "../../../../api/operations";
import tempEntry from "../../../../helper/temp-entry";
import { Entry } from "../../../../store/entries/types";
import { EntryMetadataManagement } from "../../../entry-management";
import { useContext } from "react";
import { PollsContext } from "../../../../pages/submit/hooks/polls-manager";

export function useCommunityApi() {
const { activeUser, addEntry } = useMappedStore();
const { activePoll } = useContext(PollsContext);

const request = async (host: string, raw: string, editingEntry?: Entry) => {
if (!activeUser || !activeUser.data.__loaded) {
Expand Down Expand Up @@ -35,6 +38,7 @@ export function useCommunityApi() {
hostTag,
...(raw.match(/\#[a-zA-Z0-9]+/g)?.map((tag) => tag.replace("#", "")) ?? ["ecency"])
])
.withPoll(activePoll)
.build();

await comment(author, "", hostTag, permlink, "", cleanedRaw, jsonMeta, options, true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { _t } from "../../../i18n";
import React from "react";
import React, { useContext } from "react";
import TextareaAutosize from "react-textarea-autosize";
import { DeckThreadsFormToolbar } from "./deck-threads-form-toolbar";
import { closeSvg } from "../../../img/svg";
import { PollsContext } from "../../../pages/submit/hooks/polls-manager";
import { PollWidget } from "../../polls";

interface Props {
text: string;
Expand All @@ -27,6 +29,8 @@ export const DeckThreadsFormControl = ({
onAddVideo,
video
}: Props) => {
const { activePoll } = useContext(PollsContext);

return (
<>
<div className="comment-body">
Expand Down Expand Up @@ -65,6 +69,11 @@ export const DeckThreadsFormControl = ({
</div>
</div>
)}
{activePoll && (
<div className="py-4">
<PollWidget compact={true} poll={activePoll} isReadOnly={true} />
</div>
)}
<DeckThreadsFormToolbar
onAddImage={onAddImage}
onEmojiPick={(v) => setText(`${text}${v}`)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React from "react";
import React, { useContext, useState } from "react";
import { DeckThreadsFormToolbarImagePicker } from "./deck-threads-form-toolbar-image-picker";
import { DeckThreadsFormEmojiPicker } from "./deck-threads-form-emoji-picker";
import { PollsCreation } from "../../polls";
import { Button } from "@ui/button";
import { UilChart } from "@iconscout/react-unicons";
import { PollsContext } from "../../../pages/submit/hooks/polls-manager";

interface Props {
onAddImage: (url: string, name: string) => void;
Expand All @@ -9,11 +13,22 @@ interface Props {
}

export const DeckThreadsFormToolbar = ({ onAddImage, onEmojiPick, onAddVideo }: Props) => {
const { activePoll, setActivePoll, clearActivePoll } = useContext(PollsContext);
const [show, setShow] = useState(false);

return (
<div className="deck-threads-form-toolbar">
<DeckThreadsFormToolbarImagePicker onAddImage={onAddImage} />
<DeckThreadsFormEmojiPicker onPick={onEmojiPick} />
{/*<DeckThreadsFormToolbarVideoPicker onSelect={onAddVideo} />*/}
<Button appearance="gray-link" icon={<UilChart />} onClick={() => setShow(true)} />
<PollsCreation
existingPoll={activePoll}
show={show}
setShow={setShow}
onAdd={setActivePoll}
onDeletePoll={clearActivePoll}
/>
</div>
);
};
4 changes: 4 additions & 0 deletions src/common/features/decks/deck-threads-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { classNameObject } from "../../../helper/class-name-object";
import usePrevious from "react-use/lib/usePrevious";
import { Button } from "@ui/button";
import { Alert } from "@ui/alert";
import { PollsContext } from "../../../pages/submit/hooks/polls-manager";

interface Props {
className?: string;
Expand Down Expand Up @@ -46,6 +47,8 @@ export const DeckThreadsForm = ({

const { global, activeUser, toggleUIProp } = useMappedStore();
const { setShow, create, createReply } = useContext(DeckThreadsFormContext);
const { clearActivePoll } = useContext(PollsContext);

const location = useLocation();

const [localDraft, setLocalDraft] = useLocalStorage<Record<string, any>>(
Expand Down Expand Up @@ -188,6 +191,7 @@ export const DeckThreadsForm = ({
setText("");
setImage(null);
setImageName(null);
clearActivePoll();
_t("decks.threads-form.successfully-created");
} catch (e) {
console.error(e);
Expand Down
77 changes: 40 additions & 37 deletions src/common/features/decks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import SSRSuspense from "../../components/ssr-suspense";
import { classNameObject } from "../../helper/class-name-object";
import useMount from "react-use/lib/useMount";
import { DeckFloatingToolbarActivator } from "./deck-toolbar/deck-floating-toolbar-activator";
import { PollsManager } from "../../pages/submit/hooks/polls-manager";

interface Props {
history: History;
Expand All @@ -40,43 +41,45 @@ export const Decks = ({ history }: Props) => {
<DeckManager>
{({ isDecksLoading }) => (
<DeckThreadsManager>
<DeckThreadsFormManager>
{({ show: showThreadsForm }) => (
<div
className={classNameObject({
"decks grid grid-cols-deck items-center duration-300": true,
"w-full": !isCollapsed,
expanded: isExpanded,
"toolbar-collapsed translate-x-[-72px] w-[calc(100%+72px)] sm:translate-x-0 sm:w-[auto]":
isCollapsed,
"thread-form-showed": showThreadsForm
})}
>
<DeckFloatingToolbarActivator
open={!isCollapsed}
setOpen={(v) => setIsCollapsed(!v)}
/>
<DeckToolbar
history={history}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
/>
<DeckThreadsForm className={showThreadsForm ? "show" : ""} persistable={true} />
{isDecksLoading ? (
<DeckLoader />
) : (
<>
<div className="decks-container w-full overflow-hidden">
{/*<DeckSmoothScroller>*/}
<DeckGrid history={history} />
{/*</DeckSmoothScroller>*/}
</div>
<DeckFloatingManager />
</>
)}
</div>
)}
</DeckThreadsFormManager>
<PollsManager>
<DeckThreadsFormManager>
{({ show: showThreadsForm }) => (
<div
className={classNameObject({
"decks grid grid-cols-deck items-center duration-300": true,
"w-full": !isCollapsed,
expanded: isExpanded,
"toolbar-collapsed translate-x-[-72px] w-[calc(100%+72px)] sm:translate-x-0 sm:w-[auto]":
isCollapsed,
"thread-form-showed": showThreadsForm
})}
>
<DeckFloatingToolbarActivator
open={!isCollapsed}
setOpen={(v) => setIsCollapsed(!v)}
/>
<DeckToolbar
history={history}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
/>
<DeckThreadsForm className={showThreadsForm ? "show" : ""} persistable={true} />
{isDecksLoading ? (
<DeckLoader />
) : (
<>
<div className="decks-container w-full overflow-hidden">
{/*<DeckSmoothScroller>*/}
<DeckGrid history={history} />
{/*</DeckSmoothScroller>*/}
</div>
<DeckFloatingManager />
</>
)}
</div>
)}
</DeckThreadsFormManager>
</PollsManager>
</DeckThreadsManager>
)}
</DeckManager>
Expand Down
3 changes: 2 additions & 1 deletion src/common/features/polls/api/get-poll-details-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ export interface GetPollDetailsQueryResponse {
choice_text: string;
votes?: { total_votes: number; hive_hp_incl_proxied: number | null };
}[];
poll_stats: { total_voting_accounts_num: number; total_hive_hp_incl_proxied: number | null };
poll_stats?: { total_voting_accounts_num: number; total_hive_hp_incl_proxied: number | null };
poll_trx_id: string;
poll_voters?: { name: string; choice_num: number }[];
post_body: string;
post_title: string;
max_choices_voted?: number;
preferred_interpretation: string;
protocol_version: number;
question: string;
Expand Down
1 change: 1 addition & 0 deletions src/common/features/polls/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./get-poll-details-query";
export * from "./sign-poll-vote";
export * from "./polls-votes-management";
65 changes: 65 additions & 0 deletions src/common/features/polls/api/polls-votes-management.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { GetPollDetailsQueryResponse } from "./get-poll-details-query";
import { ActiveUser } from "../../../store/active-user/types";

export namespace PollsVotesManagement {
export function processVoting(
activeUser: ActiveUser | null,
data: GetPollDetailsQueryResponse,
choiceNums: number[]
): GetPollDetailsQueryResponse {
const existingVotes = data.poll_voters?.filter((pv) => pv.name === activeUser!!.username);
const existingUserChoices = data.poll_choices?.filter(
(pc) => !!existingVotes?.some((ev) => ev.choice_num === pc.choice_num)
);
const currentUserChoices = data.poll_choices?.filter((pc) =>
choiceNums.includes(pc.choice_num)
);

const notTouchedChoices = data.poll_choices?.filter(
(pc) =>
![
...existingUserChoices?.map((puc) => puc.choice_num),
...currentUserChoices?.map((c) => c.choice_num)
].includes(pc.choice_num)
);
const nonActiveUserVotes =
data.poll_voters?.filter((pv) => pv.name !== activeUser!!.username) ?? [];

return {
...data,
poll_choices: [
...notTouchedChoices,

...(existingUserChoices
.filter((choice) => currentUserChoices.every((c) => choice.choice_text !== c.choice_text))
.map((pv) => ({
...pv,
votes: {
hive_hp_incl_proxied: pv.votes?.hive_hp_incl_proxied!,
total_votes: (pv?.votes?.total_votes ?? 0) - 1
}
})) ?? []),

...currentUserChoices.map((choice) => ({
...choice,
votes: {
hive_hp_incl_proxied: choice.votes?.hive_hp_incl_proxied!,
total_votes:
(choice?.votes?.total_votes ?? 0) +
(existingUserChoices.every((pv) => pv.choice_text !== choice.choice_text) ? 1 : 0)
}
}))
],
poll_voters: [
...nonActiveUserVotes,
...choiceNums.map((num) => ({ name: activeUser!.username, choice_num: num }))
],
poll_stats: {
total_hive_hp_incl_proxied: data.poll_stats?.total_hive_hp_incl_proxied ?? 0,
total_voting_accounts_num:
(data.poll_stats?.total_voting_accounts_num ?? 0) +
(currentUserChoices.length - (existingVotes?.length ?? 0))
}
};
}
}
Loading

0 comments on commit aa670f1

Please sign in to comment.