Pārlūkot izejas kodu

Fix hooks issue, use "queueTx" in admin controller

Leszek Wiesner 4 gadi atpakaļ
vecāks
revīzija
265d628963

+ 1 - 1
pioneer/packages/joy-roles/src/index.tsx

@@ -60,7 +60,7 @@ export const App: React.FC<Props> = (props: Props) => {
   const oppsCtrl = new OpportunitiesController(transport, props.myMemberId);
   const [applyCtrl] = useState(new ApplyController(transport));
   const myRolesCtrl = new MyRolesController(transport, props.myAddress);
-  const [adminCtrl] = useState(new AdminController(transport, api));
+  const [adminCtrl] = useState(new AdminController(transport, api, queueExtrinsic));
 
   useEffect(() => {
     return () => {

+ 72 - 113
pioneer/packages/joy-roles/src/tabs/Admin.controller.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useRef } from 'react';
 import { Link } from 'react-router-dom';
 import { formatBalance } from '@polkadot/util';
 
@@ -7,8 +7,9 @@ import { GenericAccountId, Option, Text, Vec, u32, u128 } from '@polkadot/types'
 import { Balance } from '@polkadot/types/interfaces';
 
 import { SingleLinkedMapEntry, Controller, View } from '@polkadot/joy-utils/index';
-import { MyAccountProvider, useMyAccount } from '@polkadot/joy-utils/MyAccountContext';
+import { useMyAccount } from '@polkadot/joy-utils/MyAccountContext';
 import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
+import { QueueTxExtrinsicAdd } from '@polkadot/react-components/Status/types';
 
 import {
   Accordion,
@@ -367,53 +368,35 @@ const newEmptyState = (): State => {
 };
 
 export class AdminController extends Controller<State, ITransport> {
-  api: ApiPromise
-  constructor (transport: ITransport, api: ApiPromise, initialState: State = newEmptyState()) {
+  api: ApiPromise;
+  queueExtrinsic: QueueTxExtrinsicAdd;
+
+  constructor (transport: ITransport, api: ApiPromise, queueExtrinsic: QueueTxExtrinsicAdd, initialState: State = newEmptyState()) {
     super(transport, initialState);
     this.api = api;
+    this.queueExtrinsic = queueExtrinsic;
     this.state.currentDescriptor = stockOpenings[0];
     this.updateState();
   }
 
-  newOpening (creatorAddress: string, desc: openingDescriptor) {
+  onTxSuccess = () => { this.updateState() }
+
+  newOpening (accountId: string, desc: openingDescriptor) {
     const tx = this.api.tx.contentWorkingGroup.addCuratorOpening(
       desc.start,
       desc.policy,
       desc.text
     ) as unknown as SubmittableExtrinsic;
 
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    // FIXME: Normally we would keep it open in case of errror, but due to bad design
+    // the values in the form are reset at this point anyway, so there is no point
+    this.closeModal();
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
-  startAcceptingApplications (creatorAddress: string, id = 0) {
+  startAcceptingApplications (accountId: string, id = 0) {
     const tx = this.api.tx.contentWorkingGroup.acceptCuratorApplications(id);
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
   async applyAsACurator (creatorAddress: string, openingId: number) {
@@ -430,60 +413,21 @@ export class AdminController extends Controller<State, ITransport> {
       new Option(u128, 400),
       new Text('This is my application')
     ) as unknown as SubmittableExtrinsic;
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId: creatorAddress });
   }
 
-  beginApplicantReview (creatorAddress: string, openingId: number) {
+  beginApplicantReview (accountId: string, openingId: number) {
     const tx = this.api.tx.contentWorkingGroup.beginCuratorApplicantReview(openingId);
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
-  acceptCuratorApplications (creatorAddress: string, openingId: number, applications: Array<number>) {
+  acceptCuratorApplications (accountId: string, openingId: number, applications: Array<number>) {
     const tx = this.api.tx.contentWorkingGroup.fillCuratorOpening(
       openingId,
       applications,
       null
     ) as unknown as SubmittableExtrinsic;
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
   protected async profile (id: MemberId): Promise<Option<Profile>> {
@@ -595,44 +539,59 @@ export class AdminController extends Controller<State, ITransport> {
   }
 }
 
+type AdminContainerProps = {
+  state: State;
+  controller: AdminController;
+}
+const AdminContainer = ({state, controller}: AdminContainerProps) => {
+  const address = useMyAccount().state.address;
+  const containerRef = useRef<HTMLDivElement>(null);
+  return (
+    <div ref={containerRef}>
+      <Container className="admin">
+        <Card fluid color='orange'>
+          <Card.Content>
+            <Dropdown text='Create new opening...'>
+              <Dropdown.Menu>
+                {
+                  stockOpenings.map((value, key) => {
+                    return (
+                      <Dropdown.Item
+                        key={value.title}
+                        text={value.title}
+                        onClick={() => controller.showNewOpeningModal(value)}
+                      />
+                    );
+                  })
+                }
+              </Dropdown.Menu>
+            </Dropdown>
+            <Modal
+              open={state.modalOpen}
+              onClose={() => controller.closeModal()}
+              mountNode={containerRef.current} // Prevent conflicts with tx-modal (after form values reset issue is fixed, see FIXME: above)
+              >
+              <Modal.Content image>
+                <Modal.Description>
+                  <NewOpening desc={state.currentDescriptor} fn={(desc) => address && controller.newOpening(address, desc)} />
+                </Modal.Description>
+              </Modal.Content>
+            </Modal>
+          </Card.Content>
+        </Card>
+        {
+          [...state.openings.keys()].map(key => <OpeningView key={key} opening={state.openings.get(key) as opening} controller={controller} />)
+        }
+        <br />
+      </Container>
+    </div>
+  )
+}
+
 export const AdminView = View<AdminController, State>(
   (state, controller) => {
-    const address = useMyAccount().state.address as string;
     return (
-      <MyAccountProvider>
-        <Container className="admin">
-          <Card fluid color='orange'>
-            <Card.Content>
-              <Dropdown text='Create new opening...'>
-                <Dropdown.Menu>
-                  {
-                    stockOpenings.map((value, key) => {
-                      return (
-                        <Dropdown.Item
-                          key={value.title}
-                          text={value.title}
-                          onClick={() => controller.showNewOpeningModal(value)}
-                        />
-                      );
-                    })
-                  }
-                </Dropdown.Menu>
-              </Dropdown>
-              <Modal open={state.modalOpen} onClose={() => controller.closeModal()}>
-                <Modal.Content image>
-                  <Modal.Description>
-                    <NewOpening desc={state.currentDescriptor} fn={(desc) => controller.newOpening(address, desc)} />
-                  </Modal.Description>
-                </Modal.Content>
-              </Modal>
-            </Card.Content>
-          </Card>
-          {
-            [...state.openings.keys()].map(key => <OpeningView key={key} opening={state.openings.get(key) as opening} controller={controller} />)
-          }
-          <br />
-        </Container>
-      </MyAccountProvider>
+        <AdminContainer state={state} controller={controller} />
     );
   }
 );