Skip to content

Commit

Permalink
fix: concurrent publish.
Browse files Browse the repository at this point in the history
  • Loading branch information
gdethier committed Oct 24, 2023
1 parent f7371e7 commit afd463c
Show file tree
Hide file tree
Showing 13 changed files with 12,297 additions and 3,166 deletions.
90 changes: 57 additions & 33 deletions src/ClientExtrinsicSubmitter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ISubmittableResult } from '@logion/client';
import { useEffect, useState } from 'react';
import { flushSync } from 'react-dom';

import ExtrinsicSubmissionResult, { isSuccessful } from './ExtrinsicSubmissionResult';

Expand All @@ -16,62 +15,87 @@ export interface Props {
slim?: boolean,
}

interface State {
result: ISubmittableResult | null;
error: any;
submitted: boolean;
notified: boolean;
callEnded: boolean;
call?: Call;
setState: (newState: State) => void;
}

const INITIAL_STATE: State = {
result: null,
error: null,
submitted: false,
notified: false,
callEnded: false,
setState: () => {},
};

let persistentState: State = INITIAL_STATE;

function setPersistentState(stateUpdate: Partial<State>) {
persistentState = {
...persistentState,
...stateUpdate
};
persistentState.setState(persistentState);
}

export function resetPersistenState() {
persistentState = INITIAL_STATE;
}

export default function ClientExtrinsicSubmitter(props: Props) {
const [ result, setResult ] = useState<ISubmittableResult | null>(null);
const [ error, setError ] = useState<any>(null);
const [ submitted, setSubmitted ] = useState<boolean>(false);
const [ notified, setNotified ] = useState<boolean>(false);
const [ callEnded, setCallEnded ] = useState<boolean>(false);
const [ state, setState ] = useState<State>(persistentState);
if(setState !== persistentState.setState) {
persistentState.setState = setState;
}

useEffect(() => {
if(!submitted && props.call !== undefined) {
flushSync(() => setSubmitted(true));
if(props.call !== undefined && (!state.submitted || (state.notified && props.call !== state.call))) {
setPersistentState({
...INITIAL_STATE,
call: props.call,
submitted: true,
setState,
});
(async function() {
setResult(null);
setError(null);
setCallEnded(false);
try {
await props.call!((callbackResult: ISubmittableResult) => setResult(callbackResult));
setCallEnded(true);
await props.call!((callbackResult: ISubmittableResult) => setPersistentState({ result: callbackResult }));
setPersistentState({ callEnded: true });
} catch(e) {
console.log(e);
setError(e);
setPersistentState({ callEnded: true, error: e});
}
})();
}
}, [ setResult, setError, props, submitted ]);
}, [ state, props ]);

useEffect(() => {
if (result !== null && isSuccessful(result) && !notified && props.onSuccess && callEnded) {
setNotified(true);
if (state.result !== null && isSuccessful(state.result) && !state.notified && props.onSuccess && state.callEnded) {
setPersistentState({ notified: true });
props.onSuccess();
}
}, [ result, notified, setNotified, props, callEnded ]);

useEffect(() => {
if (error !== null && !notified && props.onError) {
setNotified(true);
props.onError!();
}
}, [ notified, setNotified, props, error ]);
}, [ state, props ]);

useEffect(() => {
if(submitted && props.call === undefined) {
setSubmitted(false);
setNotified(false);
setResult(null);
setError(null);
if (state.error !== null && !state.notified && props.onError) {
setPersistentState({ notified: true });
props.onError();
}
}, [ setResult, setError, props, submitted ]);
}, [ state, props ]);

if(props.call === undefined) {
return null;
}

return (
<ExtrinsicSubmissionResult
result={result}
error={error}
result={state.result}
error={state.error}
successMessage={ props.successMessage }
slim={ props.slim }
/>
Expand Down
17 changes: 17 additions & 0 deletions src/components/blocknone/BlockNone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ReactNode } from "react";

export interface Props {
show: boolean;
children: ReactNode;
}

export default function BlockNone(props: Props) {

return (
<div style={{
display: props.show ? "block" : "none"
}}>
{ props.children }
</div>
);
}
5 changes: 3 additions & 2 deletions src/loc/CustomItemActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ContributionMode } from "./types";
import { useLogionChain } from "src/logion-chain";
import DeleteButton from "./DeleteButton";
import PublishItemButton from "./PublishItemButton";
import BlockNone from "src/components/blocknone/BlockNone";

export interface Props {
locItem: LocItem;
Expand All @@ -35,9 +36,9 @@ export default function CustomItemActions(props: Props) {
<ButtonGroup>
{ canRequestReviewMemo && <Button onClick={ () => requestReview(locItem) }>Request review</Button> }
{ canReviewMemo && <ReviewItemButtons locItem={ locItem }/> }
{ canPublishMemo && <PublishItemButton item={ locItem } /> }
<BlockNone show={ canPublishMemo }><PublishItemButton item={ locItem }/></BlockNone>
{ canDeleteMemo && <DeleteButton locItem={ locItem } /> }
{ canAcknowledgeMemo && <AcknowledgeButton locItem={ locItem } /> }
<BlockNone show={ canAcknowledgeMemo }><AcknowledgeButton locItem={ locItem } /></BlockNone>
{ locItem.status === "PUBLISHED" && !canAcknowledgeMemo && <StatusCell
icon={ { id: 'published' } }
text="Published"
Expand Down
14 changes: 5 additions & 9 deletions src/loc/LocPublishLinkButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OpenLoc } from "@logion/client";
import { UUID } from "@logion/node-api";
import { UUID, Fees } from "@logion/node-api";

import { LinkItem } from "./LocItem";
import LocPublishButton from "./LocPublishButton";
Expand All @@ -13,19 +13,15 @@ export interface Props {
}

export default function LocPublishLinkButton(props: Props) {
const { signer, client } = useLogionChain();
const { signer } = useLogionChain();
const { locState } = useLocContext();
const estimateFees = useCallback((target: UUID | undefined) => {
const estimateFees = useCallback(async (target: UUID | undefined) => {
if (target && locState instanceof OpenLoc) {
return locState.estimateFeesPublishLink({ target });
} else {
throw Error("Invalid type");
return new Fees({ inclusionFee: 0n });
}
}, [ locState ])

if(!client) {
return null;
}
}, [ locState ]);

return (
<LocPublishButton
Expand Down
6 changes: 3 additions & 3 deletions src/loc/LocPublishPrivateFileButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OpenLoc } from "@logion/client";
import { UUID, Hash } from "@logion/node-api";
import { UUID, Hash, Fees } from "@logion/node-api";

import { FileItem } from "./LocItem";
import LocPublishButton from "./LocPublishButton";
Expand All @@ -15,11 +15,11 @@ export interface Props {
export default function LocPublishPrivateFileButton(props: Props) {
const { signer, client } = useLogionChain();
const { locState } = useLocContext();
const estimateFees = useCallback((hash: Hash | undefined) => {
const estimateFees = useCallback(async (hash: Hash | undefined) => {
if (hash && locState instanceof OpenLoc) {
return locState.estimateFeesPublishFile({ hash });
} else {
throw Error("Invalid type");
return new Fees({ inclusionFee: 0n });
}
}, [ locState ])

Expand Down
14 changes: 5 additions & 9 deletions src/loc/LocPublishPublicDataButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OpenLoc } from "@logion/client";
import { UUID, Hash } from "@logion/node-api";
import { UUID, Hash, Fees } from "@logion/node-api";

import { MetadataItem } from "./LocItem";
import LocPublishButton from "./LocPublishButton";
Expand All @@ -13,19 +13,15 @@ export interface Props {
}

export default function LocPublishPublicDataButton(props: Props) {
const { signer, client } = useLogionChain();
const { signer } = useLogionChain();
const { locState } = useLocContext();
const estimateFees = useCallback((nameHash: Hash | undefined) => {
const estimateFees = useCallback(async (nameHash: Hash | undefined) => {
if (nameHash && locState instanceof OpenLoc) {
return locState.estimateFeesPublishMetadata({ nameHash });
} else {
throw Error("Invalid type");
return new Fees({ inclusionFee: 0n });
}
}, [ locState ])

if(!client) {
return null;
}
}, [ locState ]);

return (
<LocPublishButton
Expand Down
2 changes: 1 addition & 1 deletion src/loc/RequestVoteButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe("RequestVoteButton", () => {
const locState = new ClosedLoc();
setLocState(locState);
locState.legalOfficer.requestVote = async (params: any) => {
params.callback(mockSubmittableResult(true, "ExtrinsicFailed", true));
params.callback(mockSubmittableResult(false, "ExtrinsicFailed", true));
throw new Error();
};
render(<RequestVoteButton />);
Expand Down
5 changes: 3 additions & 2 deletions src/loc/TemplateItemActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ButtonGroup from "src/common/ButtonGroup";
import ClearItemButton from "./ClearItemButton";
import PublishItemButton from "./PublishItemButton";
import SetItemButton from "./SetItemButton";
import BlockNone from "src/components/blocknone/BlockNone";

export interface Props {
item: LocItem;
Expand Down Expand Up @@ -40,9 +41,9 @@ export default function TemplateItemActions(props: Props) {
{ !item.isSet && canAddMemo && <SetItemButton item={ item } /> }
{ item.isSet && canRequestReviewMemo && <Button onClick={ () => requestReview(item) }>Request review</Button> }
{ item.isSet && canReviewMemo && <ReviewItemButtons locItem={ item }/> }
{ item.isSet && canPublishMemo && <PublishItemButton item={ item } /> }
<BlockNone show={ (item.isSet || false) && canPublishMemo }><PublishItemButton item={ item } /></BlockNone>
{ item.isSet && canDeleteMemo && <ClearItemButton item={ item } /> }
{ item.isSet && canAcknowledgeMemo && <AcknowledgeButton locItem={ item } /> }
<BlockNone show={ (item.isSet || false) && canAcknowledgeMemo }><AcknowledgeButton locItem={ item } /></BlockNone>
{ item.isSet && item.status === "PUBLISHED" && !canAcknowledgeMemo && <StatusCell
icon={ { id: 'published' } }
text="Published"
Expand Down
Loading

0 comments on commit afd463c

Please sign in to comment.