diff --git a/client/packages/lowcoder-design/src/icons/icon-lowcoder-marketplace-active.svg b/client/packages/lowcoder-design/src/icons/icon-lowcoder-marketplace-active.svg
new file mode 100644
index 000000000..6550e65dc
--- /dev/null
+++ b/client/packages/lowcoder-design/src/icons/icon-lowcoder-marketplace-active.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/client/packages/lowcoder-design/src/icons/icon-lowcoder-marketplace.svg b/client/packages/lowcoder-design/src/icons/icon-lowcoder-marketplace.svg
new file mode 100644
index 000000000..903bcc0f1
--- /dev/null
+++ b/client/packages/lowcoder-design/src/icons/icon-lowcoder-marketplace.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts
index 1862d85ae..6fdc18332 100644
--- a/client/packages/lowcoder-design/src/icons/index.ts
+++ b/client/packages/lowcoder-design/src/icons/index.ts
@@ -177,12 +177,14 @@ export { ReactComponent as HomeQueryLibraryIcon } from "./icon-application-query
export { ReactComponent as HomeDataSourceIcon } from "./icon-application-datasource.svg";
export { ReactComponent as RecyclerIcon } from "./icon-application-recycler.svg";
export { ReactComponent as MarketplaceIcon } from "./icon-application-marketplace.svg";
+export { ReactComponent as LowcoderMarketplaceIcon } from "./icon-lowcoder-marketplace.svg";
export { ReactComponent as HomeActiveIcon } from "./icon-application-home-active.svg";
export { ReactComponent as HomeModuleActiveIcon } from "./icon-application-module-active.svg";
export { ReactComponent as HomeQueryLibraryActiveIcon } from "./icon-application-query-library-active.svg";
export { ReactComponent as HomeDataSourceActiveIcon } from "./icon-application-datasource-active.svg";
export { ReactComponent as RecyclerActiveIcon } from "./icon-application-recycler-active.svg";
export { ReactComponent as MarketplaceActiveIcon } from "./icon-application-marketplace-active.svg";
+export { ReactComponent as LowcoderMarketplaceActiveIcon } from "./icon-lowcoder-marketplace-active.svg";
export { ReactComponent as FavoritesIcon } from "./icon-application-favorites.svg";
export { ReactComponent as HomeSettingIcon } from "./icon-application-setting.svg";
export { ReactComponent as FolderIcon } from "./icon-application-folder.svg";
diff --git a/client/packages/lowcoder/src/app.tsx b/client/packages/lowcoder/src/app.tsx
index 8b3a944f7..ec2aaa008 100644
--- a/client/packages/lowcoder/src/app.tsx
+++ b/client/packages/lowcoder/src/app.tsx
@@ -14,6 +14,7 @@ import {
IMPORT_APP_FROM_TEMPLATE_URL,
INVITE_LANDING_URL,
isAuthUnRequired,
+ MARKETPLACE_TYPE_URL,
MARKETPLACE_URL,
ORG_AUTH_LOGIN_URL,
ORG_AUTH_REGISTER_URL,
diff --git a/client/packages/lowcoder/src/constants/applicationConstants.ts b/client/packages/lowcoder/src/constants/applicationConstants.ts
index 40b38ee52..90130e18b 100644
--- a/client/packages/lowcoder/src/constants/applicationConstants.ts
+++ b/client/packages/lowcoder/src/constants/applicationConstants.ts
@@ -115,3 +115,5 @@ export type AppSnapshotList = {
count: number; // total count
list: AppSnapshot[];
};
+
+export type MarketplaceType = "local" | "lowcoder";
diff --git a/client/packages/lowcoder/src/constants/routesURL.ts b/client/packages/lowcoder/src/constants/routesURL.ts
index 1c21036ab..f2f2e7664 100644
--- a/client/packages/lowcoder/src/constants/routesURL.ts
+++ b/client/packages/lowcoder/src/constants/routesURL.ts
@@ -1,4 +1,4 @@
-import { AppViewMode } from "constants/applicationConstants";
+import { AppViewMode, MarketplaceType } from "constants/applicationConstants";
import { LocationDescriptor } from "history";
import { UserGuideLocationState } from "pages/tutorials/tutorialsConstant";
import { DatasourceType } from "@lowcoder-ee/constants/queryConstants";
@@ -43,10 +43,14 @@ export const LDAP_AUTH_LOGIN_URL = `${USER_AUTH_URL}/ldap/login`;
export const INVITE_LANDING_URL = "/invite/:invitationId";
export const ORG_AUTH_LOGIN_URL = `/org/:orgId/auth/login`;
export const ORG_AUTH_REGISTER_URL = `/org/:orgId/auth/register`;
+export const MARKETPLACE_TYPE_URL = `${MARKETPLACE_URL}/:marketplaceType`;
export const APPLICATION_VIEW_URL = (appId: string, viewMode: AppViewMode) =>
`${ALL_APPLICATIONS_URL}/${appId}/${viewMode}`;
+export const MARKETPLACE_URL_BY_TYPE = (type: MarketplaceType) =>
+ `${MARKETPLACE_URL}/${type}`;
+
export const isAuthUnRequired = (pathname: string): boolean => {
return (
pathname.startsWith("/invite/") ||
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx
index d518250d1..f8091174b 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx
@@ -1,34 +1,66 @@
import { useEffect, useState } from "react";
-import { useDispatch, useSelector } from "react-redux";
import { HomeLayout } from "./HomeLayout";
-import { MARKETPLACE_URL } from "constants/routesURL";
-import { marketplaceSelector } from "redux/selectors/applicationSelector";
-import { fetchAllMarketplaceApps } from "redux/reduxActions/applicationActions";
+import { MARKETPLACE_TYPE_URL, MARKETPLACE_URL } from "constants/routesURL";
import { trans } from "../../i18n";
+import axios, { AxiosResponse } from "axios";
+import ApplicationApi from "@lowcoder-ee/api/applicationApi";
+import { ApplicationMeta, MarketplaceType } from "@lowcoder-ee/constants/applicationConstants";
+import { GenericApiResponse } from "@lowcoder-ee/api/apiResponses";
+import { validateResponse } from "@lowcoder-ee/api/apiUtils";
+import { messageInstance } from "lowcoder-design";
+import { matchPath } from "react-router";
+import log from "loglevel";
export function MarketplaceView() {
- const [haveFetchedApps, setHaveFetchApps] = useState(false);
+ const [ marketplaceApps, setMarketplaceApps ] = useState>([]);
+ const marketplaceType = matchPath<{marketplaceType?: MarketplaceType}>(window.location.pathname, MARKETPLACE_TYPE_URL)?.params
+ .marketplaceType;
+ const isLowcoderMarketplace = marketplaceType === 'lowcoder';
+ const marketplaceBreadcrumbText = !marketplaceType?.length
+ ? trans("home.marketplace")
+ : marketplaceType === 'lowcoder'
+ ? `${trans("home.marketplace")} (Lowcoder)`
+ : `${trans("home.marketplace")} (Local)`;
- const dispatch = useDispatch();
- const marketplaceApps = useSelector(marketplaceSelector);
+ const fetchLowcoderMarketplaceApps = () => {
+ const http = axios.create({
+ baseURL: 'https://api-service.lowcoder.cloud',
+ withCredentials: false,
+ });
+ return http.get(`/api/v1/applications/marketplace-apps`);
+ };
- useEffect(() => {
- if (!marketplaceApps.length && !haveFetchedApps) {
- dispatch(fetchAllMarketplaceApps());
- setHaveFetchApps(true);
+ const fetchLocalMarketplaceApps = () => {
+ return ApplicationApi.fetchAllMarketplaceApps()
+ }
+
+ const fetchMarketplaceApps = async () => {
+ try {
+ let response: AxiosResponse>;
+ if(isLowcoderMarketplace) {
+ response = await fetchLowcoderMarketplaceApps();
+ } else {
+ response = await fetchLocalMarketplaceApps();
+ }
+
+ const isValidResponse: boolean = validateResponse(response);
+ if (isValidResponse) {
+ setMarketplaceApps(response.data.data);
+ }
+ } catch (error: any) {
+ messageInstance.error(error.message);
+ log.debug("fetch marketplace apps error: ", error);
}
- }, []);
+ }
useEffect(() => {
- if (marketplaceApps.length) {
- setHaveFetchApps(true);
- }
- }, [marketplaceApps])
+ fetchMarketplaceApps();
+ }, [marketplaceType]);
return (
);
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
index 2baac3259..c2d3c435e 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
@@ -5,6 +5,7 @@ import {
FOLDER_URL_PREFIX,
FOLDERS_URL,
MARKETPLACE_URL,
+ MARKETPLACE_URL_BY_TYPE,
MODULE_APPLICATIONS_URL,
QUERY_LIBRARY_URL,
SETTING,
@@ -33,6 +34,8 @@ import {
RecyclerIcon,
MarketplaceIcon,
MarketplaceActiveIcon,
+ LowcoderMarketplaceActiveIcon,
+ LowcoderMarketplaceIcon,
} from "lowcoder-design";
import React, { useEffect, useState } from "react";
import { fetchAllApplications, fetchHomeData } from "redux/reduxActions/applicationActions";
@@ -245,6 +248,7 @@ export default function ApplicationHome() {
const allAppCount = allApplications.length;
const allFoldersCount = allFolders.length;
const orgHomeId = "root";
+ const isSelfHost = window.location.host !== 'app.lowcoder.cloud';
const handleFolderCreate = useCreateFolder();
@@ -357,8 +361,16 @@ export default function ApplicationHome() {
visible: ({ user }) => user.orgDev,
},
{
- text: {trans("home.marketplace")},
- routePath: MARKETPLACE_URL,
+ text: (
+
+ {
+ isSelfHost
+ ? `${trans("home.marketplace")} (Local)`
+ : trans("home.marketplace")
+ }
+
+ ),
+ routePath: isSelfHost ? MARKETPLACE_URL_BY_TYPE('local') : MARKETPLACE_URL,
routePathExact: false,
routeComp: MarketplaceView,
icon: ({ selected, ...otherProps }) =>
@@ -369,6 +381,19 @@ export default function ApplicationHome() {
),
visible: ({ user }) => user.orgDev,
},
+ {
+ text: {`${trans("home.marketplace")} (Lowcoder)`},
+ routePath: MARKETPLACE_URL_BY_TYPE('lowcoder'),
+ routePathExact: false,
+ routeComp: MarketplaceView,
+ icon: ({ selected, ...otherProps }) =>
+ selected ? (
+
+ ) : (
+
+ ),
+ visible: ({ user }) => user.orgDev && isSelfHost,
+ },
{
text: {trans("home.trash")},
routePath: TRASH_URL,