Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download bundle UX improvement #2453

Merged
19 changes: 19 additions & 0 deletions src/actions/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1730,3 +1730,22 @@ export const onFetchCommentsTimestamps = (token, commentsids) =>
// Ticket Vote actions
export const onFetchTicketVoteTimestamps = (token, votespage) =>
withCsrf((__, _, csrf) => api.ticketVoteTimestamps(csrf, token, votespage));

// Votes Bundle
lukebp marked this conversation as resolved.
Show resolved Hide resolved
export const onFetchVotesBundle = (token, serverpublickey) =>
withCsrf(async (dispatch, _, csrf) => {
dispatch(act.REQUEST_VOTES_BUNDLE());
try {
return await Promise.all([
api.proposalVoteDetails(csrf, token),
api.proposalVoteResults(csrf, token)
]).then(([{ auths, vote }, { votes }]) => {
const response = { auths, details: vote, votes, serverpublickey };
dispatch(act.RECEIVE_VOTES_BUNDLE(response));
return response;
});
} catch (error) {
dispatch(act.RECEIVE_VOTES_BUNDLE(null, error));
throw error;
}
});
3 changes: 3 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,6 @@ export const REQUEST_VERIFY_TOTP = "API_REQUEST_VERIFY_TOTP";
export const RECEIVE_VERIFY_TOTP = "API_RECEIVE_VERIFY_TOTP";

export const SET_HAS_TOTP = "SET_HAS_TOTP";

export const REQUEST_VOTES_BUNDLE = "API_REQUEST_VOTES_BUNDLE";
export const RECEIVE_VOTES_BUNDLE = "API_RECEIVE_VOTES_BUNDLE";
27 changes: 17 additions & 10 deletions src/components/DownloadJSON.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import React, { useState } from "react";
import PropTypes from "prop-types";
import fileDownload from "js-file-download";
import { Link } from "pi-ui";
import { Link, Spinner } from "pi-ui";

const isPromise = (obj) => obj && typeof obj.then === "function";

Expand All @@ -24,13 +24,16 @@ const DownloadJSON = ({
beforeDownload,
...props
}) => {
const [asyncLoading, setAsyncLoading] = useState(false);
function onDownload() {
const data = JSON.stringify(content, null, 2);
fileDownload(data, `${fileName}.json`);
}

function handleDownload() {
setAsyncLoading(true);
beforeDownload().then((response) => {
setAsyncLoading(false);
const data = JSON.stringify(response, null, 2);
fileDownload(data, `${fileName}.json`);
});
Expand All @@ -41,14 +44,18 @@ const DownloadJSON = ({
) : (
<Link
{...props}
customComponent={(props) => (
<span
style={{ cursor: "pointer" }}
{...props}
onClick={!isAsync ? onDownload : handleDownload}>
{label}
</span>
)}
customComponent={(props) =>
isAsync && asyncLoading ? (
<Spinner invert />
) : (
<span
style={{ cursor: "pointer" }}
{...props}
onClick={!isAsync ? onDownload : handleDownload}>
{label}
</span>
)
}
/>
);
};
Expand Down
6 changes: 3 additions & 3 deletions src/components/Proposal/Proposal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -431,22 +431,22 @@ const Proposal = React.memo(function Proposal({
)}
{isPublic && commentsCount > 0 && (
<DownloadCommentsTimestamps
label="Load Comments Timestamps"
label="Comments Timestamps"
recordToken={proposalToken}
commentsCount={commentsCount}
/>
)}
{votesCount > 0 && (
<DownloadVotes
label="Votes Bundle"
voteSummary={voteSummary}
fileName={`${proposalToken}-votes`}
serverpublickey={apiInfo.pubkey}
token={proposalToken}
/>
)}
{votesCount > 0 && (
<DownloadVotesTimestamps
label="Load Votes Timestamp"
label="Votes Timestamp"
votesCount={votesCount}
recordToken={proposalToken}
/>
Expand Down
24 changes: 11 additions & 13 deletions src/components/RecordWrapper/RecordWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,19 +333,17 @@ export const DownloadTimestamps = ({ token, version, label }) => {
);
};

export const DownloadVotes = ({
label,
voteSummary,
fileName,
serverpublickey
}) => {
const bundle = {
auths: voteSummary?.details?.auths,
details: voteSummary?.details?.details,
votes: voteSummary?.votes,
serverpublickey
};
return <DownloadJSON fileName={fileName} label={label} content={bundle} />;
export const DownloadVotes = ({ label, fileName, serverpublickey, token }) => {
const { onFetchVotesBundle } = useTimestamps();
return (
<DownloadJSON
label={label}
fileName={fileName}
isAsync={true}
content={[]}
beforeDownload={() => onFetchVotesBundle(token, serverpublickey)}
/>
);
};

export const LinkSection = ({ children, className, title }) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ const DownloadCommentsTimestampsWrapper = ({
};

const DownloadCommentsTimestamps = ({ recordToken }) => {
const { loading, progress, timestamps } =
const { loading, progress, timestamps, multiPage } =
useDownloadCommentsTimestamps(recordToken);

return loading ? (
<div>
<span style={{ marginRight: 10 }}>{progress}%</span>
<>
{multiPage && <span style={{ marginRight: 10 }}>{progress}%</span>}
<Spinner invert />
</div>
</>
) : timestamps ? (
<DownloadJSON
label={"Comments Timestamps"}
Expand Down
10 changes: 6 additions & 4 deletions src/containers/Comments/Download/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export function useDownloadCommentsTimestamps(recordToken) {
[recordToken]
);
const comments = useSelector(commentsSelector);
const commentsLength = comments.length || 0;
const commentsLength = comments?.length || 0;
const multiPage = commentsLength > TIMESTAMPS_PAGE_SIZE;
const onFetchCommentsTimestamps = useAction(act.onFetchCommentsTimestamps);

useEffect(() => {
Expand All @@ -59,7 +60,7 @@ export function useDownloadCommentsTimestamps(recordToken) {
const getProgressPercentage = useCallback(
(timestamps) =>
timestamps
? (Object.keys(timestamps.length * 100) / commentsLength).toFixed(2)
? ((Object.keys(timestamps).length * 100) / commentsLength).toFixed(0)
: 0,
[commentsLength]
);
Expand Down Expand Up @@ -94,7 +95,7 @@ export function useDownloadCommentsTimestamps(recordToken) {
},
start: () => {
// fetch first page of comments timestamps
const [fetch, next] = getCommentIdsForPagination(remaining);
const [fetch, next] = getCommentIdsForPagination(remaining || []);
onFetchCommentsTimestamps(recordToken, fetch)
.then(({ comments }) => {
setTimestamps({ comments });
Expand Down Expand Up @@ -147,6 +148,7 @@ export function useDownloadCommentsTimestamps(recordToken) {
progress: progress,
timestamps: {
comments: state.timestamps?.comments
}
},
multiPage: multiPage
};
}
12 changes: 5 additions & 7 deletions src/containers/Proposal/Download/DownloadVotesTimestamps.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,14 @@ const DownloadVotesTimestampsWrapper = ({ label, recordToken, votesCount }) => {
};

const DownloadVotesTimestamps = ({ recordToken, votesCount }) => {
const { timestamps, progress, loading, error } = useDownloadVoteTimestamps(
recordToken,
votesCount
);
const { timestamps, progress, loading, error, multiPage } =
useDownloadVoteTimestamps(recordToken, votesCount);

return loading ? (
<div>
<span style={{ marginRight: 10 }}>{progress}%</span>
<>
{multiPage && <span style={{ marginRight: 10 }}>{progress}%</span>}
<Spinner invert />
</div>
</>
) : timestamps ? (
<DownloadJSON
label={"Votes Timestamps"}
Expand Down
8 changes: 6 additions & 2 deletions src/containers/Proposal/Download/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ import {
loadVotesTimestamps
} from "src/lib/local_storage";

const TIMESTAMPS_PAGE_SIZE = 100;
export function useDownloadVoteTimestamps(token, votesCount) {
const [votes, setVotes] = useState(null);
const [auths, setAuths] = useState(null);
const [details, setDetails] = useState(null);
const [page, setPage] = useState(1);
const [progress, setProgress] = useState(0);
const multiPage = votesCount > TIMESTAMPS_PAGE_SIZE;
const onFetchTicketVoteTimestamps = useAction(
act.onFetchTicketVoteTimestamps
);

const getProgressPercentage = useCallback(
(total) => (total ? ((total * 100) / votesCount).toFixed(2) : 0),
(total) =>
total ? ((total * TIMESTAMPS_PAGE_SIZE) / votesCount).toFixed(0) : 0,
[votesCount]
);

Expand Down Expand Up @@ -104,6 +107,7 @@ export function useDownloadVoteTimestamps(token, votesCount) {
auths: state.auths,
details: state.details,
votes: state.votes
}
},
multiPage: multiPage
};
}
4 changes: 3 additions & 1 deletion src/hooks/api/useTimestamps.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export default function useTimestamps() {
const onFetchTicketVoteTimestamps = useAction(
act.onFetchTicketVoteTimestamps
);
const onFetchVotesBundle = useAction(act.onFetchVotesBundle);
return {
onFetchRecordTimestamps,
onFetchCommentsTimestamps,
onFetchTicketVoteTimestamps
onFetchTicketVoteTimestamps,
onFetchVotesBundle
};
}
2 changes: 2 additions & 0 deletions src/reducers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ const api = (state = DEFAULT_STATE, action) =>
[act.RECEIVE_SET_TOTP]: () => receive("setTotp", state, action),
[act.REQUEST_VERIFY_TOTP]: () => request("verifyTotp", state, action),
[act.RECEIVE_VERIFY_TOTP]: () => receive("verifyTotp", state, action),
[act.REQUEST_VOTES_BUNDLE]: () => request("votesBundle", state, action),
[act.RECEIVE_VOTES_BUNDLE]: () => receive("votesBundle", state, action),
// == CMS START ==
[act.REQUEST_GENERATE_PAYOUTS]: () => request("payouts", state, action),
[act.RECEIVE_GENERATE_PAYOUTS]: () => receive("payouts", state, action),
Expand Down