From 13cf4eb99f169e3665ee71f2e2c58208959adc1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=B9=96=DB=A3=DB=9CDadidou?= <50441633+WaDadidou@users.noreply.github.com> Date: Sat, 23 Nov 2024 10:20:54 -0500 Subject: [PATCH 1/4] fix(feed): Add loader when Gno react (#1393) * fix(feed): Add loader when Gno react, add toasts error * chore(feed): Rename react hook * chore(feed): Rollback renaming hook * chore(feed): remvoe console.log * chore(feed): Revert a refacto out of scope * chore(feed): Revert a refacto out of scope * chore(feed): remove out of scope code, FIX BIG MISTAKE in postMutate identifier * fix(feed): Add try catch when react to post on gno --------- Co-authored-by: n0izn0iz --- packages/hooks/feed/useSocialReactions.ts | 71 ++++++++++++++--------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/packages/hooks/feed/useSocialReactions.ts b/packages/hooks/feed/useSocialReactions.ts index 3dc42f3c6b..11e2642899 100644 --- a/packages/hooks/feed/useSocialReactions.ts +++ b/packages/hooks/feed/useSocialReactions.ts @@ -1,5 +1,5 @@ import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; -import { Dispatch, SetStateAction } from "react"; +import { Dispatch, SetStateAction, useEffect, useState } from "react"; import { useFeedPosting } from "./useFeedPosting"; import useSelectedWallet from "../useSelectedWallet"; @@ -47,11 +47,16 @@ export const useSocialReactions = ({ post.reactions, variables.msg.icon, ); - setPost({ ...post, reactions }); }, }); - const cosmosReaction = async (emoji: string, walletAddress: string) => { + const [isLoading, setLoading] = useState(false); + + useEffect(() => { + setLoading(isPostMutationLoading); + }, [isPostMutationLoading]); + + const reactOnCosmos = async (emoji: string, walletAddress: string) => { const client = await signingSocialFeedClient({ networkId: post.networkId, walletAddress, @@ -66,7 +71,8 @@ export const useSocialReactions = ({ }, }); }; - const gnoReaction = async (emoji: string, rpcEndpoint: string) => { + + const reactOnGno = async (emoji: string, rpcEndpoint: string) => { const gnoNetwork = mustGetGnoNetwork(post.networkId); const vmCall = { caller: selectedWallet?.address || "", @@ -75,30 +81,39 @@ export const useSocialReactions = ({ func: "ReactPost", args: [TERITORI_FEED_ID, post.id.split("-")[1], emoji, "true"], }; - const txHash = await adenaDoContract( - post.networkId, - [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], - { - gasWanted: 2_000_000, - }, - ); - const provider = new GnoJSONRPCProvider(rpcEndpoint); - // Wait for tx done - await provider.waitForTransaction(txHash); - const reactions = [...post.reactions]; - const currentReactionIdx = reactions.findIndex((r) => r.icon === emoji); - if (currentReactionIdx > -1) { - reactions[currentReactionIdx].count++; - } else { - reactions.push({ - icon: emoji, - count: 1, - ownState: true, - }); + try { + setLoading(true); + const txHash = await adenaDoContract( + post.networkId, + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], + { + gasWanted: 2_000_000, + }, + ); + const provider = new GnoJSONRPCProvider(rpcEndpoint); + // Wait for tx done + await provider.waitForTransaction(txHash); + const reactions = [...post.reactions]; + const currentReactionIdx = reactions.findIndex((r) => r.icon === emoji); + + if (currentReactionIdx > -1) { + reactions[currentReactionIdx].count++; + } else { + reactions.push({ + icon: emoji, + count: 1, + ownState: true, + }); + } + setPost({ ...post, reactions }); + } catch (err) { + console.error(err); + } finally { + setLoading(false); } - setPost({ ...post, reactions }); }; + const handleReaction = async (emoji: string) => { const action = emoji === LIKE_EMOJI @@ -126,11 +141,11 @@ export const useSocialReactions = ({ } const network = getNetwork(post.networkId); if (network?.kind === NetworkKind.Gno) { - gnoReaction(emoji, network?.endpoint || ""); + reactOnGno(emoji, network?.endpoint || ""); } else { - cosmosReaction(emoji, selectedWallet.address); + reactOnCosmos(emoji, selectedWallet.address); } }; - return { handleReaction, isPostMutationLoading }; + return { handleReaction, isPostMutationLoading: isLoading }; }; From a3f89ead37c4056feb4e92cc9c14c3355f796346 Mon Sep 17 00:00:00 2001 From: hthieu1110 Date: Sat, 23 Nov 2024 13:42:45 -0800 Subject: [PATCH 2/4] feat(gno): move from test4 => test5 (#1415) * feat: remove test4 network config + add test 5 network config * fix: adjust makefile to deploy teritori pkgs on gno * fix: use correct profile pkg path Signed-off-by: Norman Meier --------- Signed-off-by: Norman Meier Co-authored-by: Norman Meier Co-authored-by: n0izn0iz --- gno/Makefile | 8 +++---- networks.json | 20 ++++++++--------- .../{gno-test4 => gno-test5}/currencies.ts | 0 .../{gno-test4 => gno-test5}/index.ts | 22 +++++++++---------- packages/networks/index.ts | 6 ++--- 5 files changed, 28 insertions(+), 28 deletions(-) rename packages/networks/{gno-test4 => gno-test5}/currencies.ts (100%) rename packages/networks/{gno-test4 => gno-test5}/index.ts (79%) diff --git a/gno/Makefile b/gno/Makefile index fc86495579..b5b29d6e4c 100644 --- a/gno/Makefile +++ b/gno/Makefile @@ -1,7 +1,7 @@ -KEY = teritori-test4-seed +KEY = teritori BASE = teritori -REMOTE = https://rpc.gno.land:443 -CHAIN_ID = portal-loop +REMOTE = https://rpc.test5.gno.land +CHAIN_ID = test5 .PHONY: add_social_feeds_realm add_utf16_pkg add_ujson_pkg add_flags_index_pkg add_dao_interfaces_pkg add_social_feed all @@ -21,7 +21,7 @@ add_social_feed: -pkgpath "gno.land/r/${BASE}/social_feeds" \ -func="CreateFeed" \ -gas-fee="1000000ugnot" \ - -gas-wanted="3000000" \ + -gas-wanted="5000000" \ -remote="${REMOTE}" \ -chainid="${CHAIN_ID}" \ -broadcast \ diff --git a/networks.json b/networks.json index f6d967ea00..38a7a5c383 100644 --- a/networks.json +++ b/networks.json @@ -4615,9 +4615,9 @@ "txIndexerURL": "https://indexer.test3.gno.testnet.teritori.com" }, { - "id": "gno-test4", + "id": "gno-test5", "kind": "Gno", - "displayName": "Gno Test4", + "displayName": "Gno Test5", "icon": "gno.svg", "features": [ "SocialFeed", @@ -4645,12 +4645,12 @@ } ], "stakeCurrency": "ugnot", - "idPrefix": "gnotest4", - "chainId": "test4", - "endpoint": "https://rpc.test4.gno.land:443", - "txExplorer": "https://gnoscan.io/transactions/details?txhash=$hash&chainId=test4", - "accountExplorer": "https://gnoscan.io/accounts/$address?chainId=test4", - "contractExplorer": "https://gnoscan.io/realms/details?path=$address&chainId=test4", + "idPrefix": "gnotest5", + "chainId": "test5", + "endpoint": "https://rpc.test5.gno.land", + "txExplorer": "https://gnoscan.io/transactions/details?txhash=$hash&chainId=test5", + "accountExplorer": "https://gnoscan.io/accounts/$address?chainId=test5", + "contractExplorer": "https://gnoscan.io/realms/details?path=$address&chainId=test5", "testnet": true, "backendEndpoint": "https://dapp-backend.testnet.teritori.com", "gnowebURL": "https://gno.land", @@ -4660,8 +4660,8 @@ "socialFeedsPkgPath": "gno.land/r/teritori/social_feeds", "socialFeedsDAOPkgPath": "gno.land/r/teritori/social_feeds_dao", "daoInterfacesPkgPath": "gno.land/p/teritori/dao_interfaces", - "profilePkgPath": "gno.land/r/teritori/profile", - "txIndexerURL": "https://indexer.test4.gno.testnet.teritori.com" + "profilePkgPath": "gno.land/r/demo/profile", + "txIndexerURL": "https://indexer.test5.gno.testnet.teritori.com" }, { "id": "cosmos-registry:gravitybridge", diff --git a/packages/networks/gno-test4/currencies.ts b/packages/networks/gno-test5/currencies.ts similarity index 100% rename from packages/networks/gno-test4/currencies.ts rename to packages/networks/gno-test5/currencies.ts diff --git a/packages/networks/gno-test4/index.ts b/packages/networks/gno-test5/index.ts similarity index 79% rename from packages/networks/gno-test4/index.ts rename to packages/networks/gno-test5/index.ts index 187cf7d985..e40cb116d6 100644 --- a/packages/networks/gno-test4/index.ts +++ b/packages/networks/gno-test5/index.ts @@ -1,10 +1,10 @@ import { gnoCurrencies } from "./currencies"; import { GnoNetworkInfo, NetworkFeature, NetworkKind } from "../types"; -export const gnoTest4Network: GnoNetworkInfo = { - id: "gno-test4", +export const gnoTest5Network: GnoNetworkInfo = { + id: "gno-test5", kind: NetworkKind.Gno, - displayName: "Gno Test4", + displayName: "Gno Test5", icon: "gno.svg", features: [ NetworkFeature.SocialFeed, @@ -21,14 +21,14 @@ export const gnoTest4Network: GnoNetworkInfo = { ], currencies: gnoCurrencies, stakeCurrency: "ugnot", - idPrefix: "gnotest4", - chainId: "test4", - endpoint: "https://rpc.test4.gno.land:443", + idPrefix: "gnotest5", + chainId: "test5", + endpoint: "https://rpc.test5.gno.land", txExplorer: - "https://gnoscan.io/transactions/details?txhash=$hash&chainId=test4", - accountExplorer: "https://gnoscan.io/accounts/$address?chainId=test4", + "https://gnoscan.io/transactions/details?txhash=$hash&chainId=test5", + accountExplorer: "https://gnoscan.io/accounts/$address?chainId=test5", contractExplorer: - "https://gnoscan.io/realms/details?path=$address&chainId=test4", + "https://gnoscan.io/realms/details?path=$address&chainId=test5", testnet: true, backendEndpoint: "https://dapp-backend.testnet.teritori.com", gnowebURL: "https://gno.land", @@ -39,6 +39,6 @@ export const gnoTest4Network: GnoNetworkInfo = { socialFeedsPkgPath: "gno.land/r/teritori/social_feeds", socialFeedsDAOPkgPath: "gno.land/r/teritori/social_feeds_dao", daoInterfacesPkgPath: "gno.land/p/teritori/dao_interfaces", - profilePkgPath: "gno.land/r/teritori/profile", - txIndexerURL: "https://indexer.test4.gno.testnet.teritori.com", + profilePkgPath: "gno.land/r/demo/profile", + txIndexerURL: "https://indexer.test5.gno.testnet.teritori.com", }; diff --git a/packages/networks/index.ts b/packages/networks/index.ts index d2ec9de5eb..6b33f052dc 100644 --- a/packages/networks/index.ts +++ b/packages/networks/index.ts @@ -14,7 +14,7 @@ import { NetworkFeature, NetworkFeatureObject } from "./features"; import { gnoDevNetwork } from "./gno-dev"; import { gnoPortalNetwork } from "./gno-portal"; import { gnoTest3Network } from "./gno-test3"; -import { gnoTest4Network } from "./gno-test4"; +import { gnoTest5Network } from "./gno-test5"; import { osmosisNetwork } from "./osmosis"; import { osmosisTestnetNetwork } from "./osmosis-testnet"; // import { solanaNetwork } from "./solana"; @@ -49,7 +49,7 @@ const packageNetworks = [ osmosisTestnetNetwork, gnoPortalNetwork, gnoTest3Network, - gnoTest4Network, + gnoTest5Network, gnoDevNetwork, polygonMumbaiNetwork, polygonNetwork, @@ -67,7 +67,7 @@ export const defaultEnabledNetworks = [ "cosmos-hub", "osmosis", "gno-portal", - "gno-test4", + "gno-test5", "cosmos-registry:juno", "cosmos-registry:kujira", "cosmos-registry:axelar", From 16b2f0149d28e0df6fbb3291b7bf40da84da8bbf Mon Sep 17 00:00:00 2001 From: n0izn0iz Date: Sat, 23 Nov 2024 23:18:32 +0100 Subject: [PATCH 3/4] fix: dao proposals query key (#1412) Signed-off-by: Norman Meier --- packages/hooks/dao/useDAOProposals.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hooks/dao/useDAOProposals.ts b/packages/hooks/dao/useDAOProposals.ts index 6ff82b99b0..0e1f284962 100644 --- a/packages/hooks/dao/useDAOProposals.ts +++ b/packages/hooks/dao/useDAOProposals.ts @@ -51,7 +51,7 @@ export const useDAOProposals = (daoId: string | undefined) => { const { daoProposals: cosmWasmDAOProposals, ...cosmWasmOther } = useCosmWasmDAOProposals(daoId); const { data: gnoDAOProposals, ...gnoOther } = useQuery( - daoProposalsQueryKey(daoId), + [...daoProposalsQueryKey(daoId), "gno"], async () => { if (network?.kind !== NetworkKind.Gno) return []; const provider = new GnoJSONRPCProvider(network.endpoint); @@ -93,7 +93,7 @@ const useCosmWasmDAOProposals = (daoId: string | undefined) => { const proposalModuleAddress = daoFirstProposalModule?.address; const { data, ...other } = useQuery( - daoProposalsQueryKey(daoId), + [...daoProposalsQueryKey(daoId), "cosmwasm"], async () => { if (!networkId || !proposalModuleAddress) return null; From 2470e80cce3be71b9c3fcc307383b524d751462f Mon Sep 17 00:00:00 2001 From: n0izn0iz Date: Mon, 25 Nov 2024 10:39:57 +0100 Subject: [PATCH 4/4] feat: gno dapp (#1420) * feat: allow for specialized apps Signed-off-by: Norman Meier * chore: use teritori-dapp as default Signed-off-by: Norman Meier * fix: teritori-dapp import Signed-off-by: Norman Meier * feat: configure gno dapp Signed-off-by: Norman Meier * feat: improve gno app Signed-off-by: Norman Meier * feat: improve gno dapp Signed-off-by: Norman Meier * feat: per-app netlify config Signed-off-by: Norman Meier * fix: remove root netlify.toml Signed-off-by: Norman Meier * fix: sed fail Signed-off-by: Norman Meier * fix: hard noobing Signed-off-by: Norman Meier * fix: restore teritori-dapp Signed-off-by: Norman Meier * fix: rm root netlify again Signed-off-by: Norman Meier * chore: fix-typo Signed-off-by: Norman Meier * fix: teritori-dapp index Signed-off-by: Norman Meier * feat: custom home Signed-off-by: Norman Meier * fix: commit fail Signed-off-by: Norman Meier * fix: teritori-dapp home config Signed-off-by: Norman Meier * fix: include RedirectHome screen Signed-off-by: Norman Meier * fix: home config Signed-off-by: Norman Meier --------- Signed-off-by: Norman Meier --- .eslintrc.js | 11 + Root.tsx | 247 -------------- apps/gno-dapp/App.tsx | 15 + apps/gno-dapp/index.js | 8 + apps/gno-dapp/netlify.toml | 9 + apps/teritori-dapp/App.tsx | 11 + apps/teritori-dapp/index.js | 8 + .../teritori-dapp/netlify.toml | 0 package.json | 2 +- .../NetworkSelector/NetworkSelectorMenu.tsx | 40 ++- .../components/Search/SearchBarResults.tsx | 64 ++-- .../components/modals/NetworksListModal.tsx | 4 +- packages/components/navigation/Navigator.tsx | 4 +- packages/components/navigation/Sidebar.tsx | 36 +- .../navigation/components/TopLogo.tsx | 31 +- packages/context/AppConfigProvider.tsx | 22 ++ packages/context/SidebarProvider.tsx | 83 +++-- .../dapp-root/App.native.tsx | 0 App.tsx => packages/dapp-root/App.tsx | 0 packages/dapp-root/Root.tsx | 265 +++++++++++++++ packages/hooks/useAreTestnetsEnabled.ts | 8 + packages/hooks/useEnabledNetworks.ts | 18 +- packages/hooks/useSelectedNetwork.ts | 4 +- packages/screens/Feed/FeedScreen.tsx | 14 +- packages/screens/Settings/SettingsScreen.tsx | 72 ++-- .../screens/Swap/components/ConnectModal.tsx | 5 +- packages/utils/navigation.ts | 320 +++++++++--------- 27 files changed, 746 insertions(+), 555 deletions(-) delete mode 100644 Root.tsx create mode 100644 apps/gno-dapp/App.tsx create mode 100644 apps/gno-dapp/index.js create mode 100644 apps/gno-dapp/netlify.toml create mode 100644 apps/teritori-dapp/App.tsx create mode 100644 apps/teritori-dapp/index.js rename netlify.toml => apps/teritori-dapp/netlify.toml (100%) create mode 100644 packages/context/AppConfigProvider.tsx rename App.native.tsx => packages/dapp-root/App.native.tsx (100%) rename App.tsx => packages/dapp-root/App.tsx (100%) create mode 100644 packages/dapp-root/Root.tsx create mode 100644 packages/hooks/useAreTestnetsEnabled.ts diff --git a/.eslintrc.js b/.eslintrc.js index 7107dc958c..118bd9c63c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -177,5 +177,16 @@ module.exports = { }, files: ["packages/components/navigation/**"], }, + { + rules: { + "no-restricted-imports": [ + "error", + { + patterns: [disallowScriptsImports], + }, + ], + }, + files: ["packages/dapp-root/**", "apps/**"], + }, ], }; diff --git a/Root.tsx b/Root.tsx deleted file mode 100644 index 27ac7f307b..0000000000 --- a/Root.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { - Exo_500Medium, - Exo_600SemiBold, - Exo_700Bold, - useFonts, -} from "@expo-google-fonts/exo"; -import { NavigationContainer } from "@react-navigation/native"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { StatusBar } from "expo-status-bar"; -import { MetaMaskProvider } from "metamask-react"; -import Plausible from "plausible-tracker"; -import React, { memo, ReactNode, useEffect } from "react"; -import { FormProvider, useForm } from "react-hook-form"; -import { Platform, Text, TextStyle, View } from "react-native"; -import { ClickOutsideProvider as DropdownsProvider } from "react-native-click-outside"; -import { - enableLegacyWebImplementation, - GestureHandlerRootView, -} from "react-native-gesture-handler"; -import { MenuProvider } from "react-native-popup-menu"; -import { SafeAreaProvider } from "react-native-safe-area-context"; -import { Provider as ReduxProvider } from "react-redux"; -import { PersistGate } from "redux-persist/integration/react"; - -import { MultisigDeauth } from "./packages/components/multisig/MultisigDeauth"; -import { Navigator } from "./packages/components/navigation/Navigator"; -import { FeedbacksContextProvider } from "./packages/context/FeedbacksProvider"; -import { MediaPlayerContextProvider } from "./packages/context/MediaPlayerProvider"; -import { MessageContextProvider } from "./packages/context/MessageProvider"; -import { SearchBarContextProvider } from "./packages/context/SearchBarProvider"; -import { TNSMetaDataListContextProvider } from "./packages/context/TNSMetaDataListProvider"; -import { TNSContextProvider } from "./packages/context/TNSProvider"; -import { TransactionModalsProvider } from "./packages/context/TransactionModalsProvider"; -import { WalletControlContextProvider } from "./packages/context/WalletControlProvider"; -import { - useWallets, - WalletsProvider, -} from "./packages/context/WalletsProvider"; -import { useSelectedNetworkId } from "./packages/hooks/useSelectedNetwork"; -import useSelectedWallet from "./packages/hooks/useSelectedWallet"; -import { getAvailableApps } from "./packages/screens/DAppStore/query/getFromFile"; -import { setAvailableApps } from "./packages/store/slices/dapps-store"; -import { setSelectedWalletId } from "./packages/store/slices/settings"; -import { persistor, store, useAppDispatch } from "./packages/store/store"; -import { isElectron } from "./packages/utils/isElectron"; -import { linking } from "./packages/utils/navigation"; - -if (!globalThis.Buffer) { - globalThis.Buffer = require("buffer").Buffer; -} - -if (Platform.OS === "web") { - const plausible = Plausible({ - domain: "app.teritori.com", - }); - plausible.enableAutoPageviews(); -} - -const queryClient = new QueryClient(); - -// it's here just to fix a TS2589 error -type DefaultForm = { - novalue: string; -}; -// this is required for react-native-gesture-handler to work on web -enableLegacyWebImplementation(true); -// ^ required for drog and drop on the dAppStore - -export default function App() { - const methods = useForm(); - const [fontsLoaded] = useFonts({ - Exo_500Medium, - Exo_600SemiBold, - Exo_700Bold, - }); - - // FIXME: Fonts don't load on electron - if (isElectron() && !fontsLoaded) { - return null; - } - - return ( - - - - - } - persistor={persistor} - > - - {...methods}> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -} - -class ErrorBoundary extends React.Component<{ children: ReactNode }> { - state: { - hasError: boolean; - error?: unknown; - catchError?: unknown; - catchInfo?: React.ErrorInfo; - }; - - constructor(props: { children: ReactNode }) { - super(props); - this.state = { hasError: false }; - } - - static getDerivedStateFromError(error: unknown) { - console.log("derived state from error"); - return { hasError: true, error }; - } - - componentDidCatch(error: unknown, info: React.ErrorInfo) { - console.log("did catch"); - console.error(error, info); - this.setState({ catchError: error, catchInfo: info }); - } - - render() { - if (this.state.hasError) { - console.log("rendering error boundary"); - // You can render any custom fallback UI - return ( - - {`${this.state.error}`} - {this.state.error !== this.state.catchError && ( - {`${this.state.catchError}`} - )} - - {this.state.catchInfo?.componentStack} - - - ); - } - - return this.props.children; - } -} - -const errorBoundaryTextCStyle: TextStyle = { color: "white" }; - -const WalletSyncer: React.FC = memo(() => { - const selectedWallet = useSelectedWallet(); - const selectedNetworkId = useSelectedNetworkId(); - const { wallets } = useWallets(); - const dispatch = useAppDispatch(); - useEffect(() => { - if (!selectedWallet || selectedWallet.networkId !== selectedNetworkId) { - const newWallet = wallets.find((w) => w.networkId === selectedNetworkId); - dispatch(setSelectedWalletId(newWallet?.id)); - } - }, [dispatch, selectedNetworkId, selectedWallet, wallets]); - return null; -}); - -const DappStoreApps: React.FC = () => { - const dispatch = useAppDispatch(); - - useEffect(() => { - const dAppStoreValues = getAvailableApps(); - if (Platform.OS !== "web") { - // setSelectedApps([]); - // setAvailableApps({}); - const allowedApps = [ - "marketplace", - "staking", - "social-feed", - "organizations", - "governance", - "toriwallet", - "namespace", - "toripunks", - "teritori-staking", - "teritori-explorer", - "mintscan", - ]; - delete dAppStoreValues.bookmarks; - delete dAppStoreValues["coming-soon"]; - - Object.values(dAppStoreValues).map((group) => { - Object.values(group.options).map((app) => { - if (!allowedApps.includes(app.id)) { - delete dAppStoreValues[app.groupKey].options[app.id]; - } - }); - }); - } - dispatch(setAvailableApps(dAppStoreValues)); - }, [dispatch]); - - return null; -}; diff --git a/apps/gno-dapp/App.tsx b/apps/gno-dapp/App.tsx new file mode 100644 index 0000000000..dc629459ca --- /dev/null +++ b/apps/gno-dapp/App.tsx @@ -0,0 +1,15 @@ +import { AppConfig } from "@/context/AppConfigProvider"; +import AppRoot from "@/dapp-root/App"; + +const config: AppConfig = { + disableBuyTokensButton: true, + disableDAppStore: true, + forceNetworkList: ["gno-test5", "gno-portal"], + forceDAppsList: ["feed", "organizations"], + defaultNetworkId: "gno-test5", + homeScreen: "Feed", +}; + +export const App: React.FC = () => { + return ; +}; diff --git a/apps/gno-dapp/index.js b/apps/gno-dapp/index.js new file mode 100644 index 0000000000..61df421147 --- /dev/null +++ b/apps/gno-dapp/index.js @@ -0,0 +1,8 @@ +import { registerRootComponent } from "expo"; + +import { App } from "./App"; + +// registerRootComponent calls AppRegistry.registerComponent('main', () => App); +// It also ensures that whether you load the app in Expo Go or in a native build, +// the environment is set up appropriately +registerRootComponent(App); diff --git a/apps/gno-dapp/netlify.toml b/apps/gno-dapp/netlify.toml new file mode 100644 index 0000000000..1736587b69 --- /dev/null +++ b/apps/gno-dapp/netlify.toml @@ -0,0 +1,9 @@ +[build] +command = 'sed -i "s/teritori-dapp/gno-dapp/" package.json && npm i -g sharp-cli && npx expo-optimize && npx expo export -p web' +publish = '/dist' +[build.environment] +NODE_OPTIONS = "--max_old_space_size=4096" +[[redirects]] +from = "/*" +to = "/index.html" +status = 200 diff --git a/apps/teritori-dapp/App.tsx b/apps/teritori-dapp/App.tsx new file mode 100644 index 0000000000..15ddac3f08 --- /dev/null +++ b/apps/teritori-dapp/App.tsx @@ -0,0 +1,11 @@ +import { AppConfig } from "@/context/AppConfigProvider"; +import AppRoot from "@/dapp-root/App"; + +const config: AppConfig = { + defaultNetworkId: "teritori", + homeScreen: "Home", +}; + +export const App: React.FC = () => { + return ; +}; diff --git a/apps/teritori-dapp/index.js b/apps/teritori-dapp/index.js new file mode 100644 index 0000000000..61df421147 --- /dev/null +++ b/apps/teritori-dapp/index.js @@ -0,0 +1,8 @@ +import { registerRootComponent } from "expo"; + +import { App } from "./App"; + +// registerRootComponent calls AppRegistry.registerComponent('main', () => App); +// It also ensures that whether you load the app in Expo Go or in a native build, +// the environment is set up appropriately +registerRootComponent(App); diff --git a/netlify.toml b/apps/teritori-dapp/netlify.toml similarity index 100% rename from netlify.toml rename to apps/teritori-dapp/netlify.toml diff --git a/package.json b/package.json index 32c17bdb66..e8ed206254 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "teritori-dapp", "version": "1.0.0", - "main": "node_modules/expo/AppEntry.js", + "main": "apps/teritori-dapp/index.js", "scripts": { "start": "expo start", "android": "expo run:android", diff --git a/packages/components/NetworkSelector/NetworkSelectorMenu.tsx b/packages/components/NetworkSelector/NetworkSelectorMenu.tsx index 7b502ba11c..4721da3af4 100644 --- a/packages/components/NetworkSelector/NetworkSelectorMenu.tsx +++ b/packages/components/NetworkSelector/NetworkSelectorMenu.tsx @@ -23,6 +23,8 @@ import { LegacyTertiaryBox } from "../boxes/LegacyTertiaryBox"; import { TertiaryButton } from "../buttons/TertiaryButton"; import { NetworksListModal } from "../modals/NetworksListModal"; +import { useAppConfig } from "@/context/AppConfigProvider"; + export const NetworkSelectorMenu: FC<{ forceNetworkId?: string; forceNetworkKind?: NetworkKind; @@ -46,6 +48,7 @@ export const NetworkSelectorMenu: FC<{ const [networksModalVisible, setNetworksModalVisible] = useState(false); const enabledNetworks = useEnabledNetworks(); const isMobile = useIsMobile(); + const { forceNetworkList } = useAppConfig(); const onPressNetwork = (networkId: string) => { onSelect(); @@ -136,22 +139,27 @@ export const NetworkSelectorMenu: FC<{ ); })} - { - setNetworksModalVisible(true); - }} - fullWidth - /> - { - setNetworksModalVisible(false); - }} - /> + {!forceNetworkList && ( + <> + {" "} + { + setNetworksModalVisible(true); + }} + fullWidth + /> + { + setNetworksModalVisible(false); + }} + /> + + )} ); }; diff --git a/packages/components/Search/SearchBarResults.tsx b/packages/components/Search/SearchBarResults.tsx index 06db89f717..5ee434f933 100644 --- a/packages/components/Search/SearchBarResults.tsx +++ b/packages/components/Search/SearchBarResults.tsx @@ -4,7 +4,10 @@ import { StyleProp, View, ViewStyle } from "react-native"; import { MintState } from "../../api/marketplace/v1/marketplace"; import { useSearchBar } from "../../context/SearchBarProvider"; import { useIsMobile } from "../../hooks/useIsMobile"; -import { useSelectedNetworkId } from "../../hooks/useSelectedNetwork"; +import { + useSelectedNetworkId, + useSelectedNetworkInfo, +} from "../../hooks/useSelectedNetwork"; import { neutral22, neutralA3 } from "../../utils/style/colors"; import { fontSemibold12 } from "../../utils/style/fonts"; import { layout } from "../../utils/style/layout"; @@ -13,6 +16,7 @@ import { CollectionView } from "../CollectionView"; import { AvatarWithName } from "../user/AvatarWithName"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; +import { NetworkFeature } from "@/networks"; const SEARCH_RESULTS_NAMES_MARGIN = layout.spacing_x1; export const SEARCH_RESULTS_MARGIN = layout.spacing_x2; @@ -20,6 +24,7 @@ export const SEARCH_RESULTS_COLLECTIONS_MARGIN = layout.spacing_x0_5; export const SearchBarResults: FC = () => { const selectedNetworkId = useSelectedNetworkId(); + const selectedNetwork = useSelectedNetworkInfo(); const { hasCollections, hasNames, @@ -33,7 +38,7 @@ export const SearchBarResults: FC = () => { <> {hasNames && ( @@ -60,34 +65,35 @@ export const SearchBarResults: FC = () => { )} - {hasCollections && ( - - - {collections.map((c) => { - return ( - - + + {collections.map((c) => { + return ( + setSearchModalMobileOpen(false)} - /> - - ); - })} - - - )} + style={{ margin: SEARCH_RESULTS_COLLECTIONS_MARGIN }} + > + setSearchModalMobileOpen(false)} + /> + + ); + })} + + + )} ); }; diff --git a/packages/components/modals/NetworksListModal.tsx b/packages/components/modals/NetworksListModal.tsx index b7848a0706..e4fa8d9265 100644 --- a/packages/components/modals/NetworksListModal.tsx +++ b/packages/components/modals/NetworksListModal.tsx @@ -11,7 +11,6 @@ import { useSelector } from "react-redux"; import ModalBase from "./ModalBase"; import { allNetworks, getNetwork } from "../../networks"; import { - selectAreTestnetsEnabled, selectNetworkEnabled, toggleNetwork, } from "../../store/slices/settings"; @@ -32,6 +31,7 @@ import { LegacyTertiaryBox } from "../boxes/LegacyTertiaryBox"; import { TextInputCustom } from "../inputs/TextInputCustom"; import { SpacerColumn, SpacerRow } from "../spacer"; +import { useAreTestnetsEnabled } from "@/hooks/useAreTestnetsEnabled"; import { joinElements } from "@/utils/react"; export const NetworksListModal: FC<{ @@ -53,7 +53,7 @@ export const NetworksListModal: FC<{ const NetworksSettings: FC<{ style?: StyleProp }> = ({ style }) => { const [searchTerm, setSearchTerm] = useState(""); - const areTestnetsEnabled = useSelector(selectAreTestnetsEnabled); + const areTestnetsEnabled = useAreTestnetsEnabled(); const results = useMemo(() => { const searchTerms = searchTerm .split(" ") diff --git a/packages/components/navigation/Navigator.tsx b/packages/components/navigation/Navigator.tsx index 88cea43b2b..814e7439c4 100644 --- a/packages/components/navigation/Navigator.tsx +++ b/packages/components/navigation/Navigator.tsx @@ -5,6 +5,7 @@ import { getMiniModeScreens } from "./getMiniModeScreens"; import { getNormalModeScreens } from "./getNormalModeScreens"; import { getNav } from "./util"; +import { useAppConfig } from "@/context/AppConfigProvider"; import { useAppMode } from "@/hooks/useAppMode"; import { useOnboardedStatus } from "@/hooks/useOnboardStatus"; import { AppMode } from "@/utils/types/app-mode"; @@ -12,6 +13,7 @@ import { AppMode } from "@/utils/types/app-mode"; export const Navigator: React.FC = () => { const [appMode] = useAppMode(); const [isLoading] = useOnboardedStatus(); + const { homeScreen } = useAppConfig(); const { Nav, navigatorScreenOptions } = getNav(appMode as AppMode); @@ -21,7 +23,7 @@ export const Navigator: React.FC = () => { return ( Platform.OS === "web" || appMode === "mini" ? null : } diff --git a/packages/components/navigation/Sidebar.tsx b/packages/components/navigation/Sidebar.tsx index 580ecad652..adc546bd3a 100644 --- a/packages/components/navigation/Sidebar.tsx +++ b/packages/components/navigation/Sidebar.tsx @@ -33,6 +33,7 @@ import ToggleButton from "../buttons/ToggleButton"; import { Separator } from "../separators/Separator"; import { SpacerColumn, SpacerRow } from "../spacer"; +import { useAppConfig } from "@/context/AppConfigProvider"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; import { useAppMode } from "@/hooks/useAppMode"; @@ -66,6 +67,7 @@ export const Sidebar: React.FC = () => { const { name: currentRouteName } = useRoute(); const { isSidebarExpanded, dynamicSidebar } = useSidebar(); const [appMode, handleSet] = useAppMode(); + const { disableDAppStore, disableBuyTokensButton } = useAppConfig(); const layoutStyle = useAnimatedStyle( () => ({ @@ -156,26 +158,32 @@ export const Sidebar: React.FC = () => { /> )} - navigation.navigate("DAppStore")} - /> + {!disableDAppStore && ( + navigation.navigate("DAppStore")} + /> + )} } /> - - + {!disableBuyTokensButton && ( + <> + + + + )} {selectedNetworkInfo?.features.includes(NetworkFeature.UPP) && connected && diff --git a/packages/components/navigation/components/TopLogo.tsx b/packages/components/navigation/components/TopLogo.tsx index e183159306..31c04283b3 100644 --- a/packages/components/navigation/components/TopLogo.tsx +++ b/packages/components/navigation/components/TopLogo.tsx @@ -1,14 +1,34 @@ import React from "react"; -import { View, TouchableOpacity } from "react-native"; +import { View, TouchableOpacity, ViewStyle } from "react-native"; import logoTopVersionSVG from "../../../../assets/logos/logo-hexagon-version-alpha.svg"; import { layout } from "../../../utils/style/layout"; import { SVG } from "../../SVG"; +import { useAppConfig } from "@/context/AppConfigProvider"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; export const TopLogo = () => { const navigation = useAppNavigation(); + const { homeScreen } = useAppConfig(); + + const logo = ; + + const style: ViewStyle = { + marginHorizontal: layout.spacing_x0_5, + }; + + const content = + homeScreen === "Home" ? ( + navigation.navigate(homeScreen as any)} + > + {logo} + + ) : ( + {logo} + ); return ( { justifyContent: "center", }} > - navigation.navigate("Home")} - > - - + {content} ); }; diff --git a/packages/context/AppConfigProvider.tsx b/packages/context/AppConfigProvider.tsx new file mode 100644 index 0000000000..06b6f12e94 --- /dev/null +++ b/packages/context/AppConfigProvider.tsx @@ -0,0 +1,22 @@ +import { createContext, useContext } from "react"; + +import { RootStackParamList } from "@/utils/navigation"; + +export interface AppConfig { + disableBuyTokensButton?: boolean; + disableDAppStore?: boolean; + forceNetworkList?: string[]; + forceDAppsList?: string[]; + defaultNetworkId: string; + homeScreen: keyof RootStackParamList; +} +const defaultValue: AppConfig = { + defaultNetworkId: "teritori", + homeScreen: "Home", +}; + +const AppConfigContext = createContext(defaultValue); + +export const AppConfigProvider = AppConfigContext.Provider; + +export const useAppConfig = () => useContext(AppConfigContext); diff --git a/packages/context/SidebarProvider.tsx b/packages/context/SidebarProvider.tsx index 5f99f59074..8cef47019c 100644 --- a/packages/context/SidebarProvider.tsx +++ b/packages/context/SidebarProvider.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo } from "react"; import { Platform } from "react-native"; import { useSelector } from "react-redux"; +import { useAppConfig } from "./AppConfigProvider"; import { useIsMobile } from "../hooks/useIsMobile"; import { selectAvailableApps, @@ -31,6 +32,7 @@ export const useSidebar = () => { dispatch(setSidebarExpanded(false)); } }, [dispatch, isMobile]); + const { forceDAppsList } = useAppConfig(); useEffect(() => { if (selectedApps.length === 0 && Object.values(availableApps).length > 0) { @@ -53,46 +55,57 @@ export const useSidebar = () => { [key: string]: any; }; - selectedApps - .filter((element) => { - const { appId, groupKey } = getValuesFromId(element); - if (!availableApps[groupKey]) { - return false; - } - const option = availableApps[groupKey].options[appId]; - if (option === undefined) { - return false; - } - if (option.devOnly && !developerMode) { - return false; - } - return true; - }) - .map((element) => { - const { appId, groupKey } = getValuesFromId(element); - if (!availableApps[groupKey]) { - return; - } - const option = availableApps[groupKey].options[appId]; - if (option === undefined) { - return; - } + if (forceDAppsList) { + return forceDAppsList + .filter((element) => { + return !!SIDEBAR_LIST[element]; + }) + .map((element) => { + return SIDEBAR_LIST[element]; + }); + } + + forceDAppsList || + selectedApps + .filter((element) => { + const { appId, groupKey } = getValuesFromId(element); + if (!availableApps[groupKey]) { + return false; + } + const option = availableApps[groupKey].options[appId]; + if (option === undefined) { + return false; + } + if (option.devOnly && !developerMode) { + return false; + } + return true; + }) + .map((element) => { + const { appId, groupKey } = getValuesFromId(element); + if (!availableApps[groupKey]) { + return; + } + const option = availableApps[groupKey].options[appId]; + if (option === undefined) { + return; + } - dynamicAppsSelection[element] = SIDEBAR_LIST[option.id] - ? SIDEBAR_LIST[option.id] - : { - id: option.id, - title: option.title, - route: option.route, - url: option.url, - icon: option.icon, - }; - }); + dynamicAppsSelection[element] = SIDEBAR_LIST[option.id] + ? SIDEBAR_LIST[option.id] + : { + id: option.id, + title: option.title, + route: option.route, + url: option.url, + icon: option.icon, + }; + }); dynamicAppsSelection["dappstore"] = SIDEBAR_LIST["DAppsStore"]; return dynamicAppsSelection; - }, [availableApps, selectedApps, developerMode]); + }, [forceDAppsList, availableApps, selectedApps, developerMode]); const toggleSidebar = () => { dispatch(setSidebarExpanded(!isSidebarExpanded)); diff --git a/App.native.tsx b/packages/dapp-root/App.native.tsx similarity index 100% rename from App.native.tsx rename to packages/dapp-root/App.native.tsx diff --git a/App.tsx b/packages/dapp-root/App.tsx similarity index 100% rename from App.tsx rename to packages/dapp-root/App.tsx diff --git a/packages/dapp-root/Root.tsx b/packages/dapp-root/Root.tsx new file mode 100644 index 0000000000..147749fb8d --- /dev/null +++ b/packages/dapp-root/Root.tsx @@ -0,0 +1,265 @@ +import { + Exo_500Medium, + Exo_600SemiBold, + Exo_700Bold, + useFonts, +} from "@expo-google-fonts/exo"; +import { NavigationContainer } from "@react-navigation/native"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { StatusBar } from "expo-status-bar"; +import { MetaMaskProvider } from "metamask-react"; +import Plausible from "plausible-tracker"; +import React, { memo, ReactNode, useEffect, useMemo } from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import { Platform, Text, TextStyle, View } from "react-native"; +import { ClickOutsideProvider as DropdownsProvider } from "react-native-click-outside"; +import { + enableLegacyWebImplementation, + GestureHandlerRootView, +} from "react-native-gesture-handler"; +import { MenuProvider } from "react-native-popup-menu"; +import { SafeAreaProvider } from "react-native-safe-area-context"; +import { Provider as ReduxProvider } from "react-redux"; +import { PersistGate } from "redux-persist/integration/react"; + +import { MultisigDeauth } from "@/components/multisig/MultisigDeauth"; +import { Navigator } from "@/components/navigation/Navigator"; +import { + AppConfig, + AppConfigProvider, + useAppConfig, +} from "@/context/AppConfigProvider"; +import { FeedbacksContextProvider } from "@/context/FeedbacksProvider"; +import { MediaPlayerContextProvider } from "@/context/MediaPlayerProvider"; +import { MessageContextProvider } from "@/context/MessageProvider"; +import { SearchBarContextProvider } from "@/context/SearchBarProvider"; +import { TNSMetaDataListContextProvider } from "@/context/TNSMetaDataListProvider"; +import { TNSContextProvider } from "@/context/TNSProvider"; +import { TransactionModalsProvider } from "@/context/TransactionModalsProvider"; +import { WalletControlContextProvider } from "@/context/WalletControlProvider"; +import { useWallets, WalletsProvider } from "@/context/WalletsProvider"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { getAvailableApps } from "@/screens/DAppStore/query/getFromFile"; +import { setAvailableApps } from "@/store/slices/dapps-store"; +import { setSelectedWalletId } from "@/store/slices/settings"; +import { persistor, store, useAppDispatch } from "@/store/store"; +import { isElectron } from "@/utils/isElectron"; +import { getLinking } from "@/utils/navigation"; + +if (!globalThis.Buffer) { + globalThis.Buffer = require("buffer").Buffer; +} + +if (Platform.OS === "web") { + const plausible = Plausible({ + domain: "app.teritori.com", + }); + plausible.enableAutoPageviews(); +} + +const queryClient = new QueryClient(); + +// it's here just to fix a TS2589 error +type DefaultForm = { + novalue: string; +}; +// this is required for react-native-gesture-handler to work on web +enableLegacyWebImplementation(true); +// ^ required for drog and drop on the dAppStore + +const App: React.FC<{ config: AppConfig }> = ({ config }) => { + const methods = useForm(); + const [fontsLoaded] = useFonts({ + Exo_500Medium, + Exo_600SemiBold, + Exo_700Bold, + }); + + // FIXME: Fonts don't load on electron + if (isElectron() && !fontsLoaded) { + return null; + } + + return ( + + + + + + } + persistor={persistor} + > + + {...methods}> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +const AppNavigationContainer: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const { homeScreen } = useAppConfig(); + const linking = useMemo(() => { + return getLinking(homeScreen); + }, [homeScreen]); + return ( + {children} + ); +}; + +class ErrorBoundary extends React.Component<{ children: ReactNode }> { + state: { + hasError: boolean; + error?: unknown; + catchError?: unknown; + catchInfo?: React.ErrorInfo; + }; + + constructor(props: { children: ReactNode }) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: unknown) { + console.log("derived state from error"); + return { hasError: true, error }; + } + + componentDidCatch(error: unknown, info: React.ErrorInfo) { + console.log("did catch"); + console.error(error, info); + this.setState({ catchError: error, catchInfo: info }); + } + + render() { + if (this.state.hasError) { + console.log("rendering error boundary"); + // You can render any custom fallback UI + return ( + + {`${this.state.error}`} + {this.state.error !== this.state.catchError && ( + {`${this.state.catchError}`} + )} + + {this.state.catchInfo?.componentStack} + + + ); + } + + return this.props.children; + } +} + +const errorBoundaryTextCStyle: TextStyle = { color: "white" }; + +const WalletSyncer: React.FC = memo(() => { + const selectedWallet = useSelectedWallet(); + const selectedNetworkId = useSelectedNetworkId(); + const { wallets } = useWallets(); + const dispatch = useAppDispatch(); + useEffect(() => { + if (!selectedWallet || selectedWallet.networkId !== selectedNetworkId) { + const newWallet = wallets.find((w) => w.networkId === selectedNetworkId); + dispatch(setSelectedWalletId(newWallet?.id)); + } + }, [dispatch, selectedNetworkId, selectedWallet, wallets]); + return null; +}); + +const DappStoreApps: React.FC = () => { + const dispatch = useAppDispatch(); + + useEffect(() => { + const dAppStoreValues = getAvailableApps(); + if (Platform.OS !== "web") { + // setSelectedApps([]); + // setAvailableApps({}); + const allowedApps = [ + "marketplace", + "staking", + "social-feed", + "organizations", + "governance", + "toriwallet", + "namespace", + "toripunks", + "teritori-staking", + "teritori-explorer", + "mintscan", + ]; + delete dAppStoreValues.bookmarks; + delete dAppStoreValues["coming-soon"]; + + Object.values(dAppStoreValues).map((group) => { + Object.values(group.options).map((app) => { + if (!allowedApps.includes(app.id)) { + delete dAppStoreValues[app.groupKey].options[app.id]; + } + }); + }); + } + dispatch(setAvailableApps(dAppStoreValues)); + }, [dispatch]); + + return null; +}; + +export default App; diff --git a/packages/hooks/useAreTestnetsEnabled.ts b/packages/hooks/useAreTestnetsEnabled.ts new file mode 100644 index 0000000000..60b8ce9428 --- /dev/null +++ b/packages/hooks/useAreTestnetsEnabled.ts @@ -0,0 +1,8 @@ +import { useSelector } from "react-redux"; + +import { selectAreTestnetsEnabled } from "@/store/slices/settings"; + +export const useAreTestnetsEnabled = () => { + const areTestnetsEnabled = useSelector(selectAreTestnetsEnabled); + return areTestnetsEnabled; +}; diff --git a/packages/hooks/useEnabledNetworks.ts b/packages/hooks/useEnabledNetworks.ts index 950558b71f..0b91699f6a 100644 --- a/packages/hooks/useEnabledNetworks.ts +++ b/packages/hooks/useEnabledNetworks.ts @@ -1,17 +1,21 @@ import { useMemo } from "react"; import { useSelector } from "react-redux"; -import { getNetwork, NetworkInfo } from "@/networks"; -import { - selectAreTestnetsEnabled, - selectNetworksSettings, -} from "@/store/slices/settings"; +import { useAreTestnetsEnabled } from "./useAreTestnetsEnabled"; + +import { useAppConfig } from "@/context/AppConfigProvider"; +import { allNetworks, getNetwork, NetworkInfo } from "@/networks"; +import { selectNetworksSettings } from "@/store/slices/settings"; export const useEnabledNetworks = () => { const networksSettings = useSelector(selectNetworksSettings); - const areTestnetsEnabled = useSelector(selectAreTestnetsEnabled); + const areTestnetsEnabled = useAreTestnetsEnabled(); + const { forceNetworkList } = useAppConfig(); const enabledNetworks = useMemo(() => { + if (forceNetworkList) { + return allNetworks.filter((n) => forceNetworkList?.includes(n.id)); + } return Object.values(networksSettings) .map((ns) => { if (!ns?.enabled) { @@ -24,7 +28,7 @@ export const useEnabledNetworks = () => { return undefined; }) .filter((n): n is NetworkInfo => !!n); - }, [areTestnetsEnabled, networksSettings]); + }, [forceNetworkList, areTestnetsEnabled, networksSettings]); return enabledNetworks; }; diff --git a/packages/hooks/useSelectedNetwork.ts b/packages/hooks/useSelectedNetwork.ts index cbf0f4c259..51decee40d 100644 --- a/packages/hooks/useSelectedNetwork.ts +++ b/packages/hooks/useSelectedNetwork.ts @@ -1,11 +1,13 @@ import { useSelector } from "react-redux"; +import { useAppConfig } from "@/context/AppConfigProvider"; import { getNetwork } from "@/networks"; import { selectSelectedNetworkId } from "@/store/slices/settings"; export const useSelectedNetworkId = () => { + const { defaultNetworkId } = useAppConfig(); const currentNetworkId = useSelector(selectSelectedNetworkId); - const networkId = currentNetworkId || "teritori"; + const networkId = currentNetworkId || defaultNetworkId; return networkId; }; diff --git a/packages/screens/Feed/FeedScreen.tsx b/packages/screens/Feed/FeedScreen.tsx index 57cb4043fd..af8174ae87 100644 --- a/packages/screens/Feed/FeedScreen.tsx +++ b/packages/screens/Feed/FeedScreen.tsx @@ -1,4 +1,5 @@ -import React, { useMemo } from "react"; +import { useFocusEffect } from "@react-navigation/native"; +import React, { useCallback, useMemo } from "react"; import { ArticlesFeed } from "./components/ArticlesFeed"; import { FeedHeader } from "./components/FeedHeader"; @@ -19,11 +20,20 @@ import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; import { NetworkFeature } from "@/networks"; import { ScreenFC } from "@/utils/navigation"; -export const FeedScreen: ScreenFC<"Feed"> = ({ route: { params } }) => { +export const FeedScreen: ScreenFC<"Feed"> = ({ + route: { params }, + navigation: { setParams }, +}) => { useForceNetworkSelection(params?.network); const isMobile = useIsMobile(); const selectedNetworkId = useSelectedNetworkId(); + const updateParams = useCallback(() => { + setParams({ network: selectedNetworkId }); + }, [setParams, selectedNetworkId]); + + useFocusEffect(updateParams); + const defaultFeedRequest = useMemo(() => { return getDefaultFeedRequest(selectedNetworkId); }, [selectedNetworkId]); diff --git a/packages/screens/Settings/SettingsScreen.tsx b/packages/screens/Settings/SettingsScreen.tsx index 5deec15b2e..31d3a63b23 100644 --- a/packages/screens/Settings/SettingsScreen.tsx +++ b/packages/screens/Settings/SettingsScreen.tsx @@ -19,10 +19,11 @@ import { PrimaryButton } from "@/components/buttons/PrimaryButton"; import { TertiaryButton } from "@/components/buttons/TertiaryButton"; import { NetworksListModal } from "@/components/modals/NetworksListModal"; import { SpacerColumn } from "@/components/spacer"; +import { useAppConfig } from "@/context/AppConfigProvider"; +import { useAreTestnetsEnabled } from "@/hooks/useAreTestnetsEnabled"; import { useDeveloperMode } from "@/hooks/useDeveloperMode"; import { useIsKeplrConnected } from "@/hooks/useIsKeplrConnected"; import { - selectAreTestnetsEnabled, selectIsLightTheme, selectNFTStorageAPI, setAreTestnetsEnabled, @@ -77,47 +78,52 @@ export const SettingsScreen: ScreenFC<"Settings"> = () => { const navigation = useAppNavigation(); const commonStyles = useCommonStyles(); const isKeplrConnected = useIsKeplrConnected(); - const testnetEnabled = useSelector(selectAreTestnetsEnabled); + const testnetEnabled = useAreTestnetsEnabled(); const dispatch = useAppDispatch(); const [networksModalVisible, setNetworksModalVisible] = React.useState(false); const isLightTheme = useSelector(selectIsLightTheme); const [developerMode, setDeveloperMode] = useDeveloperMode(); + const appConfig = useAppConfig(); return ( - - { - dispatch(setAreTestnetsEnabled(!item.state)); - }} - item={{ - title: "Display all Testnet Networks", - description: "", - state: testnetEnabled, - }} - testID="testnet-switch" - /> - - - - - { - setNetworksModalVisible(true); - }} - fullWidth - /> - { - setNetworksModalVisible(false); - }} - /> + {!appConfig.forceNetworkList && ( + <> + + { + dispatch(setAreTestnetsEnabled(!item.state)); + }} + item={{ + title: "Display all Testnet Networks", + description: "", + state: testnetEnabled, + }} + testID="testnet-switch" + /> + - + + + { + setNetworksModalVisible(true); + }} + fullWidth + /> + { + setNetworksModalVisible(false); + }} + /> + + + + )} diff --git a/packages/screens/Swap/components/ConnectModal.tsx b/packages/screens/Swap/components/ConnectModal.tsx index 05eaa549d6..7a533623ff 100644 --- a/packages/screens/Swap/components/ConnectModal.tsx +++ b/packages/screens/Swap/components/ConnectModal.tsx @@ -1,6 +1,5 @@ import React, { Suspense } from "react"; import { Image, StyleSheet, View } from "react-native"; -import { useSelector } from "react-redux"; import osmosisIllustration from "../../../../assets/osmosis-illustration.png"; import ModalBase from "../../../components/modals/ModalBase"; @@ -8,7 +7,7 @@ import ModalBase from "../../../components/modals/ModalBase"; import { BrandText } from "@/components/BrandText"; import { SecondaryButton } from "@/components/buttons/SecondaryButton"; import { SpacerColumn } from "@/components/spacer"; -import { selectAreTestnetsEnabled } from "@/store/slices/settings"; +import { useAreTestnetsEnabled } from "@/hooks/useAreTestnetsEnabled"; import { neutral00, neutral77, secondaryColor } from "@/utils/style/colors"; import { fontSemibold14 } from "@/utils/style/fonts"; import { layout } from "@/utils/style/layout"; @@ -26,7 +25,7 @@ export const ConnectModal: React.FC = ({ onPressConnectTestnet, onClose, }) => { - const testnetsEnabled = useSelector(selectAreTestnetsEnabled); + const testnetsEnabled = useAreTestnetsEnabled(); const ModalHeader = React.lazy(() => import("./SwapView/SwapHeader").then((module) => ({ default: module.SwapHeader, diff --git a/packages/utils/navigation.ts b/packages/utils/navigation.ts index def597c6a3..b46228241b 100644 --- a/packages/utils/navigation.ts +++ b/packages/utils/navigation.ts @@ -15,6 +15,7 @@ type MiniTabsScreen = { export type RootStackParamList = { Home?: { network?: string }; + RedirectHome?: undefined; MyCollection: undefined; Activity: undefined; Guardians: undefined; @@ -181,162 +182,171 @@ export type AppRouteType = RouteProp< export const useAppNavigation = () => useNavigation(); -const navConfig: { +type NavConfig = { screens: { [Name in keyof RootStackParamList]: string }; -} = { - screens: { - Home: "", - MyCollection: "my-collection", - Activity: "activity", - Guardians: "guardians", - WalletManager: "wallet-manager", - WalletManagerWallets: "wallet-manager/wallets", - WalletManagerChains: "wallet-manager/chains", - Governance: "governance", - GovernanceProposal: "governance/:id", - UserPublicProfile: "user/:id/:tab?", - - // === RiotGame - RiotGame: "riot-game", - RiotGameEnroll: "riot-game/enroll", - RiotGameFight: "riot-game/fight", - RiotGameBreeding: "riot-game/breeding", - RiotGameMemories: "riot-game/memories", - RiotGameBridge: "riot-game/bridge", - RiotGameMarketplace: "riot-game/marketplace", - RiotGameLeaderboard: "riot-game/leaderboard", - RiotGameInventory: "riot-game/inventory", - - // ==== Launchpad - Launchpad: "launchpad", - LaunchpadApply: "launchpad/apply", - - // ==== Launchpad ERC20 - LaunchpadERC20: "launchpad-erc20", - LaunchpadERC20Tokens: "launchpad-erc20/tokens", - LaunchpadERC20CreateToken: "launchpad-erc20/create-token", - LaunchpadERC20Airdrops: "launchpad-erc20/airdrops", - LaunchpadERC20CreateAirdrop: "launchpad-erc20/create-airdrop", - LaunchpadERC20Sales: "launchpad-erc20/sales", - LaunchpadERC20CreateSale: "launchpad-erc20/create-sale", - - // Mint NFT collection - MintCollection: "collection/:id/mint", - // ==== Teritori Name Service - TNSHome: "tns/:modal?", - - // ==== Marketplace - Marketplace: "marketplace", - MarketplaceLeaderboard: "marketplace/leaderboard", - Collection: "collection/:id", - CollectionTools: "collection/:id/tools", - NFTDetail: "nft/:id", - Feed: "feed/:tab?", - FeedNewArticle: "feed/new", - FeedPostView: "feed/post/:id", - HashtagFeed: "feed/tag/:hashtag", - - // ==== Staking - Staking: "staking", - - // === Organizations - OrganizationDeployer: "create-org", - Organizations: "orgs", - CoreDAO: "core-dao", - - // === Projects Program - Projects: "projects", - ProjectsPayment: "projects/payment", - ProjectsCompleteMilestone: - "projects/completeMilestone/:projectId/:milestoneId", - ProjectsManager: "projects/manager/:view", - ProjectsMakeRequest: "projects/make-request", - ProjectsDetail: "projects/:id", - ProjectsConflictSolving: "projects/:projectId/conflicts", - // === Organization - - OrganizationGetStarted: "organization-get-started", - - // === Multisig - Multisig: "multisig", - MultisigCreate: "multisig/create", - MultisigWalletDashboard: "multisig/:id", - - // ==== Swap - Swap: "swap", - // ==== ComingSoon - ComingSoon: "coming-soon", - Settings: "settings", - // ==== DAppStore - DAppStore: "dapp-store", - // === DApps - ToriPunks: "dapp/tori-punks/:route?", - // === Metrics - Metrics: "stats", - - // ==== Message - Message: "message/:view?", - ChatSection: "message/chat", - FriendshipManager: "/friends", - - // ==== Native Wallet - NativeWallet: "native-wallet", - ViewSeed: "native-wallet/view-seed", - ImportWallet: "native-wallet/import", - CreatePassword: "native-wallet/create-password", - CreatePasswordWallet: "native-wallet/create-password-wallet", - SuccessScreen: "native-wallet/success", - - // ==== Mini nav - MiniTabs: "mini-tabs", - MiniChats: "mini-chat", - MiniWallets: "mini-wallet", - MiniFeeds: "mini-feed", - MiniCreatePost: "mini-create-post", - Conversation: "mini-conversation", - MiniProfile: "mini-profile", - MiniProfileDetail: "mini-profile-detail", - MiniDAppStore: "mini-dApp-store", - MiniNewConversation: "mini-new-conversation", - MiniFriend: "mini-friend", - MiniAddFriend: "mini-add-friend", - MiniNewGroup: "mini-new-group", - MiniChatSetting: "mini-chat-setting", - MiniPreferencesSetting: "mini-preferences-setting", - MiniChatCreateAccount: "mini-chat-create-account", - MiniSettings: "mini-settings", - MiniAccountDetails: "mini-account-details", - MiniAddAccount: "mini-add-account", - Notifications: "notifications", - AddressBook: "address-book", - AddAddressBook: "add-address-book", - EditAddressBook: "edit-address-book/:addressId", - MiniSecurityAndPrivacy: "mini-security-and-privacy", - MiniChangePassword: "mini-change-password", - MiniFaceLogin: "mini-face-login", - MiniRevealSeedPhrase: "mini-reveal-seed-phrase", - MiniExportPrivateKey: "mini-export-private-key", - MiniResetWallet: "mini-reset-wallet", - ChangeNetwork: "change-network", - About: "about", - MiniManageTokens: "mini-manage-tokens", - MiniAddCustomToken: "mini-add-custom-token", - MiniSelectToken: "mini-select-token", - MiniDepositTORI: "mini-deposit-tori", - ModeSelection: "mode-selection", - ChatActivation: "chat-activation", - MiniSendTori: "mini-send-tori", - MiniSendingTori: "mini-sending-tori", - MiniTransactionDetail: "mini-transaction-detail", - ConnectLedger: "connect-ledger", - CreateWallet: "create-wallet", - MiniChatProfile: "mini-chat-profile", - MiniGroupActions: "mini-group-actions", - BurnCapital: "burn-capital", - }, }; -export const linking = { - prefixes: [], - config: navConfig, +const getNavConfig: (homeScreen: keyof RootStackParamList) => NavConfig = ( + homeScreen, +) => { + const navConfig: NavConfig = { + screens: { + MyCollection: "my-collection", + Activity: "activity", + Guardians: "guardians", + WalletManager: "wallet-manager", + WalletManagerWallets: "wallet-manager/wallets", + WalletManagerChains: "wallet-manager/chains", + Governance: "governance", + GovernanceProposal: "governance/:id", + UserPublicProfile: "user/:id/:tab?", + + // === RiotGame + RiotGame: "riot-game", + RiotGameEnroll: "riot-game/enroll", + RiotGameFight: "riot-game/fight", + RiotGameBreeding: "riot-game/breeding", + RiotGameMemories: "riot-game/memories", + RiotGameBridge: "riot-game/bridge", + RiotGameMarketplace: "riot-game/marketplace", + RiotGameLeaderboard: "riot-game/leaderboard", + RiotGameInventory: "riot-game/inventory", + + // ==== Launchpad + Launchpad: "launchpad", + LaunchpadApply: "launchpad/apply", + + // ==== Launchpad ERC20 + LaunchpadERC20: "launchpad-erc20", + LaunchpadERC20Tokens: "launchpad-erc20/tokens", + LaunchpadERC20CreateToken: "launchpad-erc20/create-token", + LaunchpadERC20Airdrops: "launchpad-erc20/airdrops", + LaunchpadERC20CreateAirdrop: "launchpad-erc20/create-airdrop", + LaunchpadERC20Sales: "launchpad-erc20/sales", + LaunchpadERC20CreateSale: "launchpad-erc20/create-sale", + + // Mint NFT collection + MintCollection: "collection/:id/mint", + // ==== Teritori Name Service + TNSHome: "tns/:modal?", + + // ==== Marketplace + Marketplace: "marketplace", + MarketplaceLeaderboard: "marketplace/leaderboard", + Collection: "collection/:id", + CollectionTools: "collection/:id/tools", + NFTDetail: "nft/:id", + Feed: "feed/:tab?", + FeedNewArticle: "feed/new", + FeedPostView: "feed/post/:id", + HashtagFeed: "feed/tag/:hashtag", + + // ==== Staking + Staking: "staking", + + // === Organizations + OrganizationDeployer: "create-org", + Organizations: "orgs", + CoreDAO: "core-dao", + + // === Projects Program + Projects: "projects", + ProjectsPayment: "projects/payment", + ProjectsCompleteMilestone: + "projects/completeMilestone/:projectId/:milestoneId", + ProjectsManager: "projects/manager/:view", + ProjectsMakeRequest: "projects/make-request", + ProjectsDetail: "projects/:id", + ProjectsConflictSolving: "projects/:projectId/conflicts", + // === Organization + + OrganizationGetStarted: "organization-get-started", + + // === Multisig + Multisig: "multisig", + MultisigCreate: "multisig/create", + MultisigWalletDashboard: "multisig/:id", + + // ==== Swap + Swap: "swap", + // ==== ComingSoon + ComingSoon: "coming-soon", + Settings: "settings", + // ==== DAppStore + DAppStore: "dapp-store", + // === DApps + ToriPunks: "dapp/tori-punks/:route?", + // === Metrics + Metrics: "stats", + + // ==== Message + Message: "message/:view?", + ChatSection: "message/chat", + FriendshipManager: "/friends", + + // ==== Native Wallet + NativeWallet: "native-wallet", + ViewSeed: "native-wallet/view-seed", + ImportWallet: "native-wallet/import", + CreatePassword: "native-wallet/create-password", + CreatePasswordWallet: "native-wallet/create-password-wallet", + SuccessScreen: "native-wallet/success", + + // ==== Mini nav + MiniTabs: "mini-tabs", + MiniChats: "mini-chat", + MiniWallets: "mini-wallet", + MiniFeeds: "mini-feed", + MiniCreatePost: "mini-create-post", + Conversation: "mini-conversation", + MiniProfile: "mini-profile", + MiniProfileDetail: "mini-profile-detail", + MiniDAppStore: "mini-dApp-store", + MiniNewConversation: "mini-new-conversation", + MiniFriend: "mini-friend", + MiniAddFriend: "mini-add-friend", + MiniNewGroup: "mini-new-group", + MiniChatSetting: "mini-chat-setting", + MiniPreferencesSetting: "mini-preferences-setting", + MiniChatCreateAccount: "mini-chat-create-account", + MiniSettings: "mini-settings", + MiniAccountDetails: "mini-account-details", + MiniAddAccount: "mini-add-account", + Notifications: "notifications", + AddressBook: "address-book", + AddAddressBook: "add-address-book", + EditAddressBook: "edit-address-book/:addressId", + MiniSecurityAndPrivacy: "mini-security-and-privacy", + MiniChangePassword: "mini-change-password", + MiniFaceLogin: "mini-face-login", + MiniRevealSeedPhrase: "mini-reveal-seed-phrase", + MiniExportPrivateKey: "mini-export-private-key", + MiniResetWallet: "mini-reset-wallet", + ChangeNetwork: "change-network", + About: "about", + MiniManageTokens: "mini-manage-tokens", + MiniAddCustomToken: "mini-add-custom-token", + MiniSelectToken: "mini-select-token", + MiniDepositTORI: "mini-deposit-tori", + ModeSelection: "mode-selection", + ChatActivation: "chat-activation", + MiniSendTori: "mini-send-tori", + MiniSendingTori: "mini-sending-tori", + MiniTransactionDetail: "mini-transaction-detail", + ConnectLedger: "connect-ledger", + CreateWallet: "create-wallet", + MiniChatProfile: "mini-chat-profile", + MiniGroupActions: "mini-group-actions", + BurnCapital: "burn-capital", + }, + }; + if (homeScreen === "Home") { + navConfig.screens["Home"] = ""; + } + return navConfig; }; + +export const getLinking = (homeScreen: keyof RootStackParamList) => ({ + prefixes: [], + config: getNavConfig(homeScreen), +});