Commit 6f8ae2e2 authored by Max Alekseenko's avatar Max Alekseenko

post-review fixes

parent add54ac4
......@@ -10,7 +10,6 @@ export { default as googleAnalytics } from './googleAnalytics';
export { default as graphqlApiDocs } from './graphqlApiDocs';
export { default as growthBook } from './growthBook';
export { default as marketplace } from './marketplace';
export { default as marketplaceCategories } from './marketplaceCategories';
export { default as mixpanel } from './mixpanel';
export { default as nameService } from './nameService';
export { default as restApiDocs } from './restApiDocs';
......
......@@ -6,10 +6,11 @@ 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 categoriesUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL');
const title = 'Marketplace';
const config: Feature<{ configUrl: string; submitFormUrl: string }> = (() => {
const config: Feature<{ configUrl: string; submitFormUrl: string; categoriesUrl: string | undefined }> = (() => {
if (
chain.rpcUrl &&
configUrl &&
......@@ -20,6 +21,7 @@ const config: Feature<{ configUrl: string; submitFormUrl: string }> = (() => {
isEnabled: true,
configUrl,
submitFormUrl,
categoriesUrl,
});
}
......
import type { Feature } from './types';
import { getExternalAssetFilePath } from '../utils';
// config file will be downloaded at run-time and saved in the public folder
const configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL');
const title = 'Marketplace categories';
const config: Feature<{ configUrl: string }> = (() => {
if (configUrl) {
return Object.freeze({
title,
isEnabled: true,
configUrl,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
......@@ -83,7 +83,8 @@ const marketplaceSchema = yup
.of(marketplaceAppSchema),
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL: yup
.array()
.json(),
.json()
.of(yup.string()),
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: yup
.string()
.when('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', {
......
......@@ -421,7 +421,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 | Required | - | `https://example.com/marketplace_config.json` |
| 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_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 the list of categories to be displayed on the markeplace page in the specified order | - | - | `https://example.com/marketplace_categories.json` |
| 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` |
#### Marketplace app configuration properties
......@@ -440,6 +440,8 @@ This feature is **always enabled**, but you can configure its behavior by passin
| twitter | `string` | Displayed twitter link | - | `'https://twitter.com/blockscoutcom'` |
| telegram | `string` | Displayed telegram link | - | `'https://t.me/poa_network'` |
| github | `string` | Displayed github link | - | `'https://github.com/blockscout'` |
| internalWallet | `boolean` | `true` means that the application can automatically connect to the Blockscout wallet. | - | `true` |
| priority | `number` | The higher the priority, the higher the app will appear in the list on the Marketplace page. | - | `7` |
#### Marketplace categories ids
......
import _groudBy from 'lodash/groupBy';
import _pickBy from 'lodash/pickBy';
import { useRouter } from 'next/router';
import React from 'react';
import { MarketplaceCategory } from 'types/client/marketplace';
import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useDebounce from 'lib/hooks/useDebounce';
import * as mixpanel from 'lib/mixpanel/index';
import getQueryParamString from 'lib/router/getQueryParamString';
......@@ -74,24 +72,7 @@ export default function useMarketplace() {
}, []);
const { isPlaceholderData, isError, error, data, displayedApps } = useMarketplaceApps(debouncedFilterQuery, selectedCategoryId, favoriteApps);
const { value: isExperiment } = useFeatureValue('marketplace_exp', false);
const { isPlaceholderData: isCategoriesPlaceholderData, data: categoriesData } = useMarketplaceCategories();
const categories = React.useMemo(() => {
let categoryNames: Array<string> = [];
const grouped = _groudBy(data, app => app.categories);
if (categoriesData?.length && isExperiment) {
categoryNames = categoriesData;
} else {
categoryNames = Object.keys(grouped);
}
return categoryNames
.map(category => ({ name: category, count: grouped[category]?.length || 0 }))
.filter(c => c.count > 0);
}, [ data, categoriesData, isExperiment ]);
const { isPlaceholderData: isCategoriesPlaceholderData, data: categories } = useMarketplaceCategories(data);
React.useEffect(() => {
setFavoriteApps(getFavoriteApps());
......
import { useQuery } from '@tanstack/react-query';
import _groudBy from 'lodash/groupBy';
import React from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useApiFetch from 'lib/hooks/useFetch';
const feature = config.features.marketplaceCategories;
const configUrl = feature.isEnabled ? feature.configUrl : '';
const feature = config.features.marketplace;
const categoriesUrl = (feature.isEnabled && feature.categoriesUrl) || '';
export default function useMarketplaceCategories() {
export default function useMarketplaceCategories(apps: Array<MarketplaceAppOverview>) {
const apiFetch = useApiFetch();
return useQuery<unknown, ResourceError<unknown>, Array<string>>({
const { value: isExperiment } = useFeatureValue('marketplace_exp', true);
const { isPlaceholderData, data } = useQuery<unknown, ResourceError<unknown>, Array<string>>({
queryKey: [ 'marketplace-categories' ],
queryFn: async() => apiFetch(configUrl, undefined, { resource: 'marketplace-categories' }),
select: (data) => (data as Array<string>),
placeholderData: feature.isEnabled ? Array(9).fill('Bridge').map((c, i) => c + i) : undefined,
queryFn: async() => apiFetch(categoriesUrl, undefined, { resource: 'marketplace-categories' }),
placeholderData: categoriesUrl ? Array(9).fill('Bridge').map((c, i) => c + i) : undefined,
staleTime: Infinity,
enabled: feature.isEnabled,
enabled: Boolean(categoriesUrl),
});
const categories = React.useMemo(() => {
let categoryNames: Array<string> = [];
const grouped = _groudBy(apps, app => app.categories);
if (data?.length && isExperiment) {
categoryNames = data;
} else {
categoryNames = Object.keys(grouped);
}
return categoryNames
.map(category => ({ name: category, count: grouped[category]?.length || 0 }))
.filter(c => c.count > 0);
}, [ apps, data, isExperiment ]);
return React.useMemo(() => ({
isPlaceholderData,
data: categories,
}), [ isPlaceholderData, categories ]);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment