mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Merge pull request #1597 from blockscout/suggest-ideas-button
Add suggest ideas button
This commit is contained in:
@@ -6,6 +6,7 @@ import { getEnvValue, getExternalAssetFilePath } from '../utils';
|
||||
// config file will be downloaded at run-time and saved in the public folder
|
||||
const configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL');
|
||||
const submitFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM');
|
||||
const suggestIdeasFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM');
|
||||
const categoriesUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL');
|
||||
const adminServiceApiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST');
|
||||
|
||||
@@ -14,7 +15,7 @@ const title = 'Marketplace';
|
||||
const config: Feature<(
|
||||
{ configUrl: string } |
|
||||
{ api: { endpoint: string; basePath: string } }
|
||||
) & { submitFormUrl: string; categoriesUrl: string | undefined }
|
||||
) & { submitFormUrl: string; categoriesUrl: string | undefined; suggestIdeasFormUrl: string | undefined }
|
||||
> = (() => {
|
||||
if (chain.rpcUrl && submitFormUrl) {
|
||||
if (configUrl) {
|
||||
@@ -24,6 +25,7 @@ const config: Feature<(
|
||||
configUrl,
|
||||
submitFormUrl,
|
||||
categoriesUrl,
|
||||
suggestIdeasFormUrl,
|
||||
});
|
||||
} else if (adminServiceApiHost) {
|
||||
return Object.freeze({
|
||||
@@ -31,6 +33,7 @@ const config: Feature<(
|
||||
isEnabled: true,
|
||||
submitFormUrl,
|
||||
categoriesUrl,
|
||||
suggestIdeasFormUrl,
|
||||
api: {
|
||||
endpoint: adminServiceApiHost,
|
||||
basePath: '',
|
||||
|
||||
@@ -48,6 +48,7 @@ NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace-categories/default.json
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
|
||||
NEXT_PUBLIC_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info-test.k8s-dev.blockscout.com
|
||||
|
||||
@@ -95,6 +95,14 @@ const marketplaceSchema = yup
|
||||
// eslint-disable-next-line max-len
|
||||
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM cannot not be used without NEXT_PUBLIC_MARKETPLACE_CONFIG_URL or NEXT_PUBLIC_ADMIN_SERVICE_API_HOST'),
|
||||
}),
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: yup
|
||||
.string()
|
||||
.when([ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'NEXT_PUBLIC_ADMIN_SERVICE_API_HOST' ], {
|
||||
is: (config: Array<unknown>, apiHost: string) => config.length > 0 || Boolean(apiHost),
|
||||
then: (schema) => schema.test(urlTest),
|
||||
// eslint-disable-next-line max-len
|
||||
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM cannot not be used without NEXT_PUBLIC_MARKETPLACE_CONFIG_URL or NEXT_PUBLIC_ADMIN_SERVICE_API_HOST'),
|
||||
}),
|
||||
});
|
||||
|
||||
const beaconChainSchema = yup
|
||||
|
||||
@@ -25,6 +25,7 @@ NEXT_PUBLIC_IS_TESTNET=true
|
||||
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://example.com
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://example.com
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://example.com
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://example.com
|
||||
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE='<a href="#">Hello</a>'
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
|
||||
|
||||
@@ -180,6 +180,7 @@ frontend:
|
||||
NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/base.svg
|
||||
NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/base-goerli.json
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
|
||||
NEXT_PUBLIC_LOGOUT_URL: https://blockscoutcom.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace-categories/default.json
|
||||
|
||||
@@ -149,6 +149,7 @@ frontend:
|
||||
|
||||
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
|
||||
NEXT_PUBLIC_APP_ENV: development
|
||||
NEXT_PUBLIC_APP_INSTANCE: main
|
||||
NEXT_PUBLIC_STATS_API_HOST: https://stats-test.k8s-dev.blockscout.com/
|
||||
|
||||
@@ -53,6 +53,7 @@ frontend:
|
||||
NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/base-goerli.json
|
||||
NEXT_PUBLIC_API_HOST: blockscout-optimism-goerli.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
|
||||
NEXT_PUBLIC_LOGOUT_URL: https://blockscoutcom.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_STATS_API_HOST: https://stats-optimism-goerli.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
|
||||
|
||||
@@ -59,6 +59,7 @@ frontend:
|
||||
NEXT_PUBLIC_NAME_SERVICE_API_HOST: https://bens-rs-test.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_AUTH_URL: https://blockscout-main.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
|
||||
NEXT_PUBLIC_LOGOUT_URL: https://blockscoutcom.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_HOMEPAGE_CHARTS: "['daily_txs','coin_price','market_cap']"
|
||||
NEXT_PUBLIC_NETWORK_RPC_URL: https://rpc.ankr.com/eth_goerli
|
||||
|
||||
@@ -432,6 +432,7 @@ This feature is **always enabled**, but you can configure its behavior by passin
|
||||
| NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | `string` | URL of configuration file (`.json` format only) which contains list of apps that will be shown on the marketplace page. See [below](#marketplace-app-configuration-properties) list of available properties for an app. Can be replaced with NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | Required | - | `https://example.com/marketplace_config.json` |
|
||||
| NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url. Can be used instead of NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | - | - | `https://admin-rs.services.blockscout.com` |
|
||||
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | Required | - | `https://airtable.com/shrqUAcjgGJ4jU88C` |
|
||||
| NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM | `string` | Link to form where users can suggest ideas for the marketplace | - | - | `https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form` |
|
||||
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `https://core.poa.network` |
|
||||
| NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL | `string` | URL of configuration file (`.json` format only) which contains the list of categories to be displayed on the markeplace page in the specified order. If no URL is provided, then the list of categories will be compiled based on the `categories` fields from the marketplace (apps) configuration file | - | - | `https://example.com/marketplace_categories.json` |
|
||||
|
||||
|
||||
+5
-23
@@ -4,31 +4,13 @@ import React from 'react';
|
||||
|
||||
import PageNextJs from 'nextjs/PageNextJs';
|
||||
|
||||
import config from 'configs/app';
|
||||
import LinkExternal from 'ui/shared/LinkExternal';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
|
||||
const feature = config.features.marketplace;
|
||||
|
||||
const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false });
|
||||
|
||||
const Page: NextPage = () => {
|
||||
return (
|
||||
<PageNextJs pathname="/apps">
|
||||
<>
|
||||
<PageTitle
|
||||
title="DAppscout"
|
||||
contentAfter={ feature.isEnabled && (
|
||||
<LinkExternal href={ feature.submitFormUrl } variant="subtle" fontSize="sm" lineHeight={ 5 } ml="auto">
|
||||
Submit app
|
||||
</LinkExternal>
|
||||
) }
|
||||
/>
|
||||
<Marketplace/>
|
||||
</>
|
||||
</PageNextJs>
|
||||
);
|
||||
};
|
||||
const Page: NextPage = () => (
|
||||
<PageNextJs pathname="/apps">
|
||||
<Marketplace/>
|
||||
</PageNextJs>
|
||||
);
|
||||
|
||||
export default Page;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box, Menu, MenuButton, MenuItem, MenuList, Flex, IconButton } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { MarketplaceCategory } from 'types/client/marketplace';
|
||||
@@ -7,18 +7,40 @@ import type { TabItem } from 'ui/shared/Tabs/types';
|
||||
import config from 'configs/app';
|
||||
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
||||
import useFeatureValue from 'lib/growthbook/useFeatureValue';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import MarketplaceAppModal from 'ui/marketplace/MarketplaceAppModal';
|
||||
import MarketplaceCategoriesMenu from 'ui/marketplace/MarketplaceCategoriesMenu';
|
||||
import MarketplaceDisclaimerModal from 'ui/marketplace/MarketplaceDisclaimerModal';
|
||||
import MarketplaceList from 'ui/marketplace/MarketplaceList';
|
||||
import FilterInput from 'ui/shared/filters/FilterInput';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import type { IconName } from 'ui/shared/IconSvg';
|
||||
import LinkExternal from 'ui/shared/LinkExternal';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
|
||||
import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
|
||||
|
||||
import useMarketplace from '../marketplace/useMarketplace';
|
||||
const feature = config.features.marketplace;
|
||||
|
||||
const links: Array<{ label: string; href: string; icon: IconName }> = [];
|
||||
if (feature.isEnabled) {
|
||||
if (feature.submitFormUrl) {
|
||||
links.push({
|
||||
label: 'Submit app',
|
||||
href: feature.submitFormUrl,
|
||||
icon: 'plus' as IconName,
|
||||
});
|
||||
}
|
||||
if (feature.suggestIdeasFormUrl) {
|
||||
links.push({
|
||||
label: 'Suggest ideas',
|
||||
href: feature.suggestIdeasFormUrl,
|
||||
icon: 'edit' as IconName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const Marketplace = () => {
|
||||
const {
|
||||
isPlaceholderData,
|
||||
@@ -41,7 +63,7 @@ const Marketplace = () => {
|
||||
appsTotal,
|
||||
isCategoriesPlaceholderData,
|
||||
} = useMarketplace();
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const { value: isExperiment } = useFeatureValue('marketplace_exp', false);
|
||||
|
||||
const categoryTabs = React.useMemo(() => {
|
||||
@@ -88,6 +110,39 @@ const Marketplace = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle
|
||||
title="DAppscout"
|
||||
contentAfter={ (isMobile && links.length > 1) ? (
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={ IconButton }
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme="gray"
|
||||
px="9px"
|
||||
ml="auto"
|
||||
icon={ <IconSvg name="dots" boxSize="18px"/> }
|
||||
/>
|
||||
<MenuList minW="max-content">
|
||||
{ links.map(({ label, href, icon }) => (
|
||||
<MenuItem key={ label } as="a" href={ href } target="_blank" py={ 2 } px={ 4 }>
|
||||
<IconSvg name={ icon } boxSize={ 4 } mr={ 2.5 }/>
|
||||
{ label }
|
||||
<IconSvg name="arrows/north-east" boxSize={ 4 } color="gray.400" ml={ 2 }/>
|
||||
</MenuItem>
|
||||
)) }
|
||||
</MenuList>
|
||||
</Menu>
|
||||
) : (
|
||||
<Flex ml="auto">
|
||||
{ links.map(({ label, href }) => (
|
||||
<LinkExternal key={ label } href={ href } variant="subtle" fontSize="sm" lineHeight={ 5 } ml={ 2 }>
|
||||
{ label }
|
||||
</LinkExternal>
|
||||
)) }
|
||||
</Flex>
|
||||
) }
|
||||
/>
|
||||
{ isExperiment && (
|
||||
<Box marginTop={{ base: 0, lg: 8 }}>
|
||||
{ (isCategoriesPlaceholderData) ? (
|
||||
|
||||
Reference in New Issue
Block a user