Эх сурвалжийг харах

20230222: Migrated class component to function for App.tsx

mkbeefcake 1 жил өмнө
parent
commit
b3344109d1

+ 180 - 294
src/App.tsx

@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
 import "bootstrap/dist/css/bootstrap.min.css";
 import "./index.css";
 import { Modals, Routes, Loading, Footer, Status } from "./components";
@@ -25,280 +25,136 @@ import { apiLocation, wsLocation, historyDepth } from "./config";
 import { initialState } from "./state";
 import axios from "axios";
 
+import { useElectedCouncils } from '@/hooks';
+import { ElectedCouncil } from "@/types";
+
 // types
-import { Api, IState } from "./types";
+// import { Api, IState } from "./types";
 // import { types } from "@joystream/types";
 import { ApiPromise, WsProvider } from "@polkadot/api";
 import { Header } from "@polkadot/types/interfaces";
 
-interface IProps {}
-
-class App extends React.Component<IProps, IState> {
-  // initializeSocket() {
-  //   socket.on("disconnect", () => setTimeout(this.initializeSocket, 1000));
-  //   socket.on("connect", () => {
-  //     if (!socket.id) return console.log("no websocket connection");
-  //     console.log("my socketId:", socket.id);
-  //     socket.emit("get posts", this.state.posts.length);
-  //   });
-  //   socket.on("posts", (posts: Post[]) => {
-  //     console.log(`received ${posts.length} posts`);
-  //     this.setState({ posts });
-  //   });
-  // }
-
-  // sync via joystream-api
-
-  async updateStatus(api: ApiPromise, id: number): Promise<typeof Status> {
-    console.debug(`#${id}: Updating status`);
-    this.updateActiveProposals();
-    getMints(api).then((mints) => this.save(`mints`, mints));
-    getTokenomics().then((tokenomics) => this.save(`tokenomics`, tokenomics));
-
-    let { status, councils } = this.state;
-    status.election = await updateElection(api);
-    if (status.election?.stage) this.getElectionStatus(api);
-    councils.forEach((c) => {
-      if (c?.round > status.council) status.council = c;
-    });
-
-    let hash: string = await api.rpc.chain.getBlockHash(1);
-    if (hash)
-      status.startTime = (await api.query.timestamp.now.at(hash)).toNumber();
-
-    const nextMemberId = await await api.query.members.nextMemberId();
-    status.members = nextMemberId - 1;
-    status.proposals = await get.proposalCount(api);
-    status.posts = await get.currentPostId(api);
-    status.threads = await get.currentThreadId(api);
-    status.categories = await get.currentCategoryId(api);
-    status.proposalPosts = await api.query.proposalsDiscussion.postCount();
-    await this.updateEra(api, status.era).then(async (era) => {
-      status.era = era;
-      status.lastReward = await getLastReward(api, era);
-      status.validatorStake = await getTotalStake(api, era);
-      this.save("status", status);
-    });
-    return status;
-  }
-
-  async getElectionStatus(api: ApiPromise): Promise<IElectionState> {
-    getCouncilSize(api).then((councilSize) => {
-      let election = this.state.election;
-      election.councilSize = councilSize;
-      this.save("election", election);
-    });
-    getVotes(api).then((votes) => {
-      let election = this.state.election;
-      election.votes = votes;
-      this.save("election", election);
-    });
-    getCouncilApplicants(api).then((applicants) => {
-      let election = this.state.election;
-      election.applicants = applicants;
-      this.save("election", election);
-    });
-  }
-
-  updateActiveProposals() {
-    const active = this.state.proposals.filter((p) => p.result === "Pending");
-    if (!active.length) return;
-    const s = active.length > 1 ? `s` : ``;
-    console.log(`Updating ${active.length} active proposal${s}`);
-    active.forEach(async (a) => {
-      const { data } = await axios.get(`${apiLocation}/v2/proposals/${a.id}`);
-      if (!data || data.error)
-        return console.error(`failed to fetch proposal from API`);
-      this.save(
-        "proposals",
-        this.state.proposals.map((p) => (p.id === a.id ? data : p))
-      );
-    });
-  }
-
-  async updateEra(api: Api, old: number) {
-    const { status, validators } = this.state;
-    const era = Number(await api.query.staking.currentEra());
-    if (era === old) return era;
-    this.updateWorkingGroups(api);
-    this.updateValidatorPoints(api, status.era);
-    if (era > status.era || !validators.length) this.updateValidators(api);
-    return era;
-  }
-
-  async updateWorkingGroups(api: ApiPromise) {
-    const { members, openings, workers } = this.state;
-    updateWorkers(api, workers, members).then((workers) => {
-      this.save("workers", workers);
-      updateOpenings(api, openings, members).then((openings) =>
-        this.save("openings", openings)
-      );
-    });
-    return this.save("council", await api.query.council.activeCouncil());
-  }
-
-  updateValidators(api: ApiPromise) {
-    getValidators(api).then((validators) => {
-      this.save("validators", validators);
-      getNominators(api).then((nominators) => {
-        this.save("nominators", nominators);
-        getStashes(api).then((stashes) => {
-          this.save("stashes", stashes);
-          const { status, members } = this.state;
-          const { era } = status;
-          getValidatorStakes(api, era, stashes, members, this.save).then(
-            (stakes) => this.save("stakes", stakes)
-          );
-        });
-      });
-    });
-  }
-
-  async updateValidatorPoints(api: ApiPromise, currentEra: number) {
-    let points = this.state.rewardPoints;
-
-    const updateTotal = (eraTotals) => {
-      let total = 0;
-      Object.keys(eraTotals).forEach((era) => (total += eraTotals[era]));
-      return total;
-    };
-
-    for (let era = currentEra; era > currentEra - historyDepth; --era) {
-      if (era < currentEra && points.eraTotals[era]) continue;
-      getEraRewardPoints(api, era).then((eraPoints) => {
-        console.debug(`era ${era}: ${eraPoints.total} points`);
-        points.eraTotals[era] = eraPoints.total;
-        points.total = updateTotal(points.eraTotals);
-        Object.keys(eraPoints.individual).forEach((validator: string) => {
-          if (!points.validators[validator]) points.validators[validator] = {};
-          points.validators[validator][era] = eraPoints.individual[validator];
-        });
-        this.save("rewardPoints", points);
-      });
-    }
-  }
-
-  async updateCouncils() {
-    queryJstats(`v1/councils`).then((councils) => {
-      this.save(`councils`, councils);
-
-      // TODO OPTIMIZE find max round
-      let council = { round: 0 };
-      councils.forEach((c) => {
-        if (c.round > council.round) council = c;
-      });
-      let { status } = this.state;
-      status.council = council; // needed by dashboard
-      this.save("status", status);
-    });
-  }
-
-  // interface interactions
-
-  toggleStar(account: string) {
-    let { stars } = this.state;
-    stars[account] = !stars[account];
-    this.save("stars", stars);
-  }
-
-  toggleEditKpi(editKpi) {
-    this.setState({ editKpi });
-  }
-  toggleShowStatus() {
-    this.setState({ showStatus: !this.state.showStatus });
-  }
-  toggleFooter() {
-    this.setState({ hideFooter: !this.state.hideFooter });
-  }
-
-  getMember(handle: string) {
-    const { members } = this.state;
-    const member = members.find((m) => m.handle === handle);
-    if (member) return member;
-    return members.find((m) => m.rootKey === handle);
-  }
-
-  render() {
-    const { connected, fetching, loading, hideFooter } = this.state;
-    if (loading) return <Loading />;
-
-    return (
-      <>
-        <Routes
-          toggleEditKpi={this.toggleEditKpi}
-          toggleFooter={this.toggleFooter}
-          toggleStar={this.toggleStar}
-          getMember={this.getMember}
-          {...this.state}
-        />
-
-        <Modals
-          toggleEditKpi={this.toggleEditKpi}
-          toggleShowStatus={this.toggleShowStatus}
-          {...this.state}
-        />
-
-        <Footer show={!hideFooter} toggleHide={this.toggleFooter} />
-
-        <Status
-          toggleShowStatus={this.toggleShowStatus}
-          connected={connected}
-          fetching={fetching}
-        />
-      </>
-    );
-  }
-
-  async getStakesForValidators(api: ApiPromise) {
-    const era = Number(await api.query.staking.currentEra());
-    console.log('Era: ', era);
-    // await this.updateValidatorPoints(api, era);
-    // await this.updateValidators(api);
-
-    // console.log('LastReward', await getLastReward(api, era));
-    // console.log('getTotalStake', await getTotalStake(api, era));
-
-  }
-
-  // startup from bottom up
-  joyApi() {
-    console.debug(`Connecting to ${wsLocation}`);
-    const provider = new WsProvider(wsLocation);
-    ApiPromise.create({ provider/*, types*/ }).then(async (api) => {
-      await api.isReady;
-      console.log(`Connected to ${wsLocation}`);
-      
-      this.setState({ connected: true });
-      // this.updateWorkingGroups(api);
-
-      // For getting Reward
-      await this.getStakesForValidators(api);
-  
-      api.rpc.chain.subscribeNewHeads(async (header: Header) => {
-        let { blocks, status } = this.state;
-        const id = header.number.toNumber();
-
-        // console.log(`api.rpc.chain.subscribeNewHeads: ${id}`)
-
-        // const isEven = id / 50 === Math.floor(id / 50);
-        // if (isEven || status.block?.id + 50 < id) this.updateStatus(api, id);
-
-        // if (blocks.find((b) => b.id === id)) return;
-        // const timestamp = (await api.query.timestamp.now()).toNumber();
-        // const duration = status.block
-        //   ? timestamp - status.block.timestamp
-        //   : 6000;
-        // status.block = { id, timestamp, duration };
-        // this.save("status", status);
-
-        // blocks = blocks.filter((i) => i.id !== id).concat(status.block);
-        // this.setState({ blocks });
-      });
-    });
-  }
-
-  save(key: string, data: any) {
-    this.setState({ [key]: data });
+const App = (props: {}) => {
+
+  const [stars, setStars] = useState(initialState.stars);
+  const [editKpi, setEditKpi] = useState(initialState.editKpi);
+  const [showStatus, setShowStatus] = useState(initialState.showStatus);
+  const [hideFooter, setHideFooter] = useState(initialState.hideFooter);
+  const [members, setMembers] = useState(initialState.members);
+  const [connected, setConnected] = useState(initialState.connected);
+  const [fetching, setFetching] = useState(initialState.fetching);
+  const [status, setStatus] = useState(initialState.status);
+  const [assets, setAssets] = useState(initialState.assets);
+  const [providers, setProviders] = useState(initialState.providers);
+  const [councils, setCouncils] = useState(initialState.councils);
+  const [council, setCouncil] = useState<ElectedCouncil | undefined>(undefined);
+  const [election, setElection] = useState(initialState.election);
+  const [workers, setWorkers] = useState([]);
+  const [categories, setCategories] = useState(initialState.categories);
+  const [channels, setChannels] = useState(initialState.channels);
+  const [proposals, setProposals] = useState(initialState.proposals);
+  const [posts, setPosts] = useState(initialState.posts);
+  const [threads, setThreads] = useState(initialState.threads);
+  const [mints, setMints] = useState(initialState.mints);
+  const [openings, setOpenings] = useState(initialState.openings);
+  const [tokenomics, setTokenomics] = useState({});
+  const [transactions, setTransactions] = useState([]);
+  const [reports, setReports] = useState({});
+  const [validators, setValidators] = useState({});
+  const [nominators, setNominators] = useState([]);
+  const [stashes, setStashes] = useState([]);
+  const [stakes, setStakes] = useState({});
+  const [rewardPoints, setRewardPoints] = useState({});
+
+  const childProps = {
+    stars,
+    editKpi,
+    showStatus,
+    hideFooter,
+    members,
+    connected,
+    fetching,
+    status,
+    assets,
+    providers,    
+    councils,
+    council,
+    election,
+    workers,
+    categories,
+    channels,
+    proposals,
+    posts,
+    threads,
+    mints,
+    openings,
+    tokenomics,
+    transactions,
+    reports,
+    validators,
+    nominators,
+    stashes,
+    stakes,
+    rewardPoints
+  }
+
+  // Loading process
+  const { data } = useElectedCouncils({});
+
+	useEffect(() => {
+		if (!data) 
+      return
+
+    if (council && council.electionCycleId == data[0].electionCycleId) 
+      return
+
+    console.log(`App.ts`, data)
+    setCouncil(data[0]) 
+	}, [data])
+
+  // useEffect(() => {
+  //   loadData();
+  // }, [])
+
+  const loadData = async () => {
+    console.debug(`Loading data`)
+    setStars(load("stars"))
+    setEditKpi(load("editKpi"))
+    setShowStatus(load("showStatus"))
+    setHideFooter(load("hideFooter"))
+    setMembers(load("members"))
+    setConnected(load("connected"))
+    setFetching(load("fetching"))
+    setStatus(load("status"))
+    setAssets(load("assets"))
+    setProviders(load("providers"))
+    setCouncils(load("councils"))
+    setCouncil(load("council"))
+    setElection(load("election"))
+    setWorkers(load("workers"))
+    setCategories(load("categories"))
+    setChannels(load("channels"))
+    setProposals(load("proposals"))
+    setPosts(load("posts"))
+    setThreads(load("threads"))
+    setMints(load("mints"))
+    setOpenings(load("openings"))
+    setTokenomics(load("tokenomics"))
+    setTransactions(load("transactions"))
+    setReports(load("reports"))
+    setValidators(load("validators"))
+    setNominators(load("nominators"))
+    setStashes(load("stashes"))
+    setStakes(load("stakes"))
+    setRewardPoints(load("rewardPoints"))
+
+    // getTokenomics().then((tokenomics) => this.save(`tokenomics`, tokenomics));
+    // bootstrap(this.save); // axios requests
+    // this.updateCouncils();
+  }
+
+  // Save & Load data to local storage
+  const save = (key: string, data: any) => {
     const value = JSON.stringify(data);
     try {
       localStorage.setItem(key, value);
@@ -309,47 +165,77 @@ class App extends React.Component<IProps, IState> {
     return data;
   }
 
-  load(key: string) {
+  const load = (key: string) => {
     try {
       const data = localStorage.getItem(key);
       if (!data) return;
       const size = data.length;
       if (size > 10240)
         console.debug(` -${key}: ${(size / 1024).toFixed(1)} KB`);
-      this.setState({ [key]: JSON.parse(data) });
+
       return JSON.parse(data);
     } catch (e) {
       console.warn(`Failed to load ${key}`, e);
     }
+  }  
+
+  // Trigger functions
+  const toggleStar = (account: string) => {
+    let temp = stars;  
+    temp[account] = !temp[account];
+    setStars(temp);
+    save("stars", temp);
   }
 
-  async loadData() {
-    console.debug(`Loading data`);
-    "status members assets providers councils council election workers categories channels proposals posts threads mints openings tokenomics transactions reports validators nominators staches stakes rewardPoints stars"
-      .split(" ")
-      .map((key) => this.load(key));
-    getTokenomics().then((tokenomics) => this.save(`tokenomics`, tokenomics));
-    bootstrap(this.save); // axios requests
-    this.updateCouncils();
+  const toggleEditKpi = (_editKpi: boolean) => {
+    setEditKpi(_editKpi);
   }
 
-  componentDidMount() {
-    this.loadData(); // local storage + bootstrap
-    this.joyApi(); // joystream rpc connection
-    //this.initializeSocket() // jsstats socket.io
+  const toggleShowStatus = () => {
+    setShowStatus(!showStatus);
+  }
+  
+  const toggleFooter = () => {
+    setHideFooter(!hideFooter);
   }
 
-  constructor(props: IProps) {
-    super(props);
-    this.state = initialState;
-    this.save = this.save.bind(this);
-    this.load = this.load.bind(this);
-    this.toggleEditKpi = this.toggleEditKpi.bind(this);
-    this.toggleStar = this.toggleStar.bind(this);
-    this.toggleFooter = this.toggleFooter.bind(this);
-    this.toggleShowStatus = this.toggleShowStatus.bind(this);
-    this.getMember = this.getMember.bind(this);
+  const getMember = (handle: string) => {    
+    const member = members.find((m) => m.handle === handle);
+    if (member) return member;
+    return members.find((m) => m.rootKey === handle);
   }
+
+  // const { connected, fetching, loading, hideFooter } = this.state;
+  // if (loading) 
+  //   return <Loading />;
+
+  return (
+    <>
+      <Routes
+        toggleEditKpi={toggleEditKpi}
+        toggleFooter={toggleFooter}
+        toggleStar={toggleStar}
+        getMember={getMember}
+        {...childProps}
+      />
+
+      <Modals
+        toggleEditKpi={toggleEditKpi}
+        toggleShowStatus={toggleShowStatus}
+        {...childProps}
+      />
+
+      <Footer show={!hideFooter} toggleHide={toggleFooter} />
+
+      <Status
+        toggleShowStatus={toggleShowStatus}
+        connected={connected}
+        fetching={fetching}
+      />
+    </>
+  );
+
+
 }
 
 export default App;

+ 6 - 6
src/components/Dashboard/index.tsx

@@ -15,14 +15,14 @@ import { ElectedCouncil } from "@/types";
 import Proposals from "./Proposals";
 
 
-interface IProps extends IState {}
+interface IProps extends IState {
+  council: ElectedCouncil
+}
 const Dashboard = (props: IProps) => {
-  const { } = props;
-  const { data } = useElectedCouncils({});
+  const { council } = props;
 	const [description1, setDescription1] = useState('');
-	const [description2, setDescription2] = useState('');
 
-	const council: ElectedCouncil | undefined = data && data[0]
+  console.log(`Dashboard: `, council)
 
 	useEffect(() => {
 		if (!council) 
@@ -33,7 +33,7 @@ const Dashboard = (props: IProps) => {
 			", From: " + new Date(council.electedAt.timestamp) + 
 			", Councilors: [ " + council.councilors.map(each => each.member.handle).join(", ") + " ]")
 
-	}, [council])
+	}, [])
  
 
   return (

+ 2 - 0
src/components/Routes/index.tsx

@@ -3,6 +3,7 @@ import { BrowserRouter, Switch, Route } from "react-router-dom";
 import { AppBar, Election, Spinner } from "..";
 import { IState } from "../../types";
 import IssueTracker from "../IssueTracker";
+import { ElectedCouncil } from "@/types";
 
 const Calendar = React.lazy(() => import("../Calendar"));
 const { Council } = React.lazy(() => import(".."));
@@ -32,6 +33,7 @@ const Survey = React.lazy(() => import("../Survey"));
 interface IProps extends IState {
   toggleStar: (a: string) => void;
   toggleFooter: () => void;
+  council: ElectedCouncil
 }
 
 const Routes = (props: IProps) => {

+ 2 - 2
src/config.ts

@@ -5,9 +5,9 @@ export const wsLocation = "wss://rpc.joystream.org:9944";
 export const apiLocation = "https://api.joystreamstats.live/api"
 export const socketLocation = "/socket.io"
 export const hydraLocation = "https://hydra.joystream.org/graphql"
-export const queryNode= "https://tiguan08.com/graphql"
+// export const queryNode= "https://tiguan08.com/graphql"
 // export const queryNode= "https://hydra.joystream.org/graphql"
-// export const queryNode= "https://query.joystream.org/graphql"
+export const queryNode= "https://query.joystream.org/graphql"
 //export const alternativeBackendApis = "http://localhost:3000"
 export const alternativeBackendApis = "https://validators.joystreamstats.live"
 export const tasksEndpoint = "https://api.joystreamstats.live/tasks"