diff --git a/e2e-tests/cypress/tests/integration/channels/plugins/marketplace/ui_spec.js b/e2e-tests/cypress/tests/integration/channels/plugins/marketplace/ui_spec.js index d983ebaaab7..1faa0c55a8a 100644 --- a/e2e-tests/cypress/tests/integration/channels/plugins/marketplace/ui_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/plugins/marketplace/ui_spec.js @@ -17,7 +17,6 @@ describe('Plugin Marketplace', () => { before(() => { cy.shouldNotRunOnCloudEdition(); - cy.shouldHaveFeatureFlag('StreamlinedMarketplace', 'false'); // https://mattermost.atlassian.net/browse/MM-54230 cy.shouldHavePluginUploadEnabled(); cy.apiInitSetup().then(({team}) => { diff --git a/e2e-tests/playwright/lib/src/server/default_config.ts b/e2e-tests/playwright/lib/src/server/default_config.ts index 7e9f40a4f8d..929a3e34a0b 100644 --- a/e2e-tests/playwright/lib/src/server/default_config.ts +++ b/e2e-tests/playwright/lib/src/server/default_config.ts @@ -799,7 +799,6 @@ const defaultServerConfig: AdminConfig = { OnboardingTourTips: true, EnableExportDirectDownload: false, MoveThreadsEnabled: false, - StreamlinedMarketplace: true, CloudDedicatedExportUI: false, WebSocketEventScope: true, ExperimentalAuditSettingsSystemConsoleUI: true, diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go index 8e4f79192de..ff7cfc91de5 100644 --- a/server/public/model/feature_flags.go +++ b/server/public/model/feature_flags.go @@ -36,8 +36,6 @@ type FeatureFlags struct { MoveThreadsEnabled bool - StreamlinedMarketplace bool - CloudDedicatedExportUI bool WebSocketEventScope bool @@ -156,7 +154,6 @@ func (f *FeatureFlags) SetDefaults() { f.OnboardingTourTips = true f.EnableExportDirectDownload = false f.MoveThreadsEnabled = false - f.StreamlinedMarketplace = true f.CloudDedicatedExportUI = false f.WebSocketEventScope = true f.ExperimentalAuditSettingsSystemConsoleUI = true diff --git a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx index 0b62f25f2a1..112e424ada2 100644 --- a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx +++ b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx @@ -486,7 +486,6 @@ describe('components/PluginManagement', () => { }, }, appsFeatureFlagEnabled: false, - streamlinedMarketplaceFlagEnabled: false, actions: { uploadPlugin: jest.fn(), installPluginFromUrl: jest.fn(), diff --git a/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap b/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap index 261bd2da6ca..d611364bae4 100644 --- a/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap +++ b/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`components/marketplace/ doesn't show web marketplace banner in FeatureFlags.StreamlinedMarketplace for Cloud 1`] = ` +exports[`components/marketplace/ doesn't show web marketplace banner for Cloud 1`] = ` `; -exports[`components/marketplace/ hides search, shows web marketplace banner in FeatureFlags.StreamlinedMarketplace 1`] = ` +exports[`components/marketplace/ hides search and shows web marketplace banner 1`] = ` - + + - - +

+ Connect the tools you use most to Mattermost +

+
+ + + +
+ + @@ -567,7 +475,7 @@ exports[`components/marketplace/ should render with error banner 1`] = ` tabindex="-1" > - + + - - +

+ Connect the tools you use most to Mattermost +

+
+ + + +
+ + @@ -831,7 +647,7 @@ exports[`components/marketplace/ should render with no plugins available 1`] = ` tabindex="-1" > - + + - - +

+ Connect the tools you use most to Mattermost +

+
+ + + +
+ + @@ -1105,7 +829,7 @@ exports[`components/marketplace/ should render with plugins available 1`] = ` tabindex="-1" > - - - + + + + + + +
+
- + + @@ -1423,7 +1053,7 @@ exports[`components/marketplace/ should render with plugins installed 1`] = ` tabindex="-1" > - - - - + - + + + + + + +
+
- + + diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx index 28b4a9d14c1..6c43a11cd91 100644 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx +++ b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx @@ -72,9 +72,7 @@ describe('components/marketplace/', () => { entities: { general: { firstAdminCompleteSetup: false, - config: { - FeatureFlagStreamlinedMarketplace: 'false', - }, + config: {}, license: { Cloud: 'false', }, @@ -169,7 +167,7 @@ describe('components/marketplace/', () => { } }); - test('hides search, shows web marketplace banner in FeatureFlags.StreamlinedMarketplace', () => { + test('hides search and shows web marketplace banner', () => { const setState = jest.fn(); const useStateSpy = jest.spyOn(React, 'useState'); useStateSpy.mockImplementation(() => [true, setState]); @@ -179,8 +177,6 @@ describe('components/marketplace/', () => { sampleInstalledPlugin, ]; - (mockState.entities.general.config as any).FeatureFlagStreamlinedMarketplace = 'true'; - // Suppress expected errors from useState mock affecting all child components const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); try { @@ -197,12 +193,11 @@ describe('components/marketplace/', () => { } }); - test("doesn't show web marketplace banner in FeatureFlags.StreamlinedMarketplace for Cloud", () => { + test("doesn't show web marketplace banner for Cloud", () => { const setState = jest.fn(); const useStateSpy = jest.spyOn(React, 'useState'); useStateSpy.mockImplementation(() => [true, setState]); - (mockState.entities.general.config as any).FeatureFlagStreamlinedMarketplace = 'true'; mockState.entities.general.license.Cloud = 'true'; // Suppress expected errors from useState mock affecting all child components diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx index 82f80aac400..7775450cf6d 100644 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx +++ b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx @@ -2,31 +2,25 @@ // See LICENSE.txt for license information. import classNames from 'classnames'; -import debounce from 'lodash/debounce'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import type {ReactNode} from 'react'; -import {Tabs, Tab} from 'react-bootstrap'; -import type {SelectCallback} from 'react-bootstrap'; import {useIntl} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import {Link} from 'react-router-dom'; -import {MagnifyIcon} from '@mattermost/compass-icons/components'; import {FooterPagination, GenericModal} from '@mattermost/components'; import {getPluginStatuses} from 'mattermost-redux/actions/admin'; import {setFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/actions/general'; import {getFirstAdminVisitMarketplaceStatus, getLicense} from 'mattermost-redux/selectors/entities/general'; -import {streamlinedMarketplaceEnabled} from 'mattermost-redux/selectors/entities/preferences'; -import {fetchListing, filterListing} from 'actions/marketplace'; +import {fetchListing} from 'actions/marketplace'; import {closeModal} from 'actions/views/modals'; -import {getListing, getInstalledListing} from 'selectors/views/marketplace'; +import {getListing} from 'selectors/views/marketplace'; import {isModalOpen} from 'selectors/views/modals'; import usePluginStatusesSync from 'components/common/hooks/usePluginStatusesSync'; import LoadingScreen from 'components/loading_screen'; -import Input, {SIZE} from 'components/widgets/inputs/input/input'; import {ModalIdentifiers} from 'utils/constants'; import {isCloudLicense} from 'utils/license_utils'; @@ -38,13 +32,6 @@ import WebMarketplaceBanner from './web_marketplace_banner'; import './marketplace_modal.scss'; -const MarketplaceTabs = { - ALL_LISTING: 'all', - INSTALLED_LISTING: 'installed', -}; - -const SEARCH_TIMEOUT_MILLISECONDS = 200; - const linkConsole = (msg: ReactNode[]): ReactNode => ( {msg} @@ -58,17 +45,13 @@ const MarketplaceModal = () => { const show = useSelector((state: GlobalState) => isModalOpen(state, ModalIdentifiers.PLUGIN_MARKETPLACE)); const listing = useSelector(getListing); - const installedListing = useSelector(getInstalledListing); // Refetch plugin statuses while the modal is open whenever the server signals a change. const pluginStatuses = usePluginStatusesSync(); const hasFirstAdminVisitedMarketplace = useSelector(getFirstAdminVisitMarketplaceStatus); - const isStreamlinedMarketplaceEnabled = useSelector(streamlinedMarketplaceEnabled); const license = useSelector(getLicense); const isCloud = isCloudLicense(license); - const [tabKey, setTabKey] = useState(MarketplaceTabs.ALL_LISTING); - const [filter, setFilter] = useState(''); const [page, setPage] = useState(0); const [hasLoaded, setHasLoaded] = useState(false); const [loading, setLoading] = React.useState(true); @@ -84,16 +67,6 @@ const MarketplaceModal = () => { setLoading(false); }, []); - const doSearch = useCallback(async () => { - const {error} = await dispatch(filterListing(filter)); - - if (error) { - setServerError(true); - } - }, [filter]); - - const debouncedSearch = debounce(doSearch, SEARCH_TIMEOUT_MILLISECONDS); - useEffect(() => { async function doFetch() { await dispatch(getPluginStatuses()); @@ -115,11 +88,11 @@ const MarketplaceModal = () => { }, [pluginStatuses]); useEffect(() => { - if (hasLoaded) { - debouncedSearch(); - setPage(0); + const maxPage = Math.max(0, Math.ceil(listing.length / ITEMS_PER_PAGE) - 1); + if (page > maxPage) { + setPage(maxPage); } - }, [filter]); + }, [listing.length, page]); const scrollListToTop = useCallback(() => { if (listRef.current) { @@ -131,20 +104,6 @@ const MarketplaceModal = () => { dispatch(closeModal(ModalIdentifiers.PLUGIN_MARKETPLACE)); }; - const handleChangeTab: SelectCallback = useCallback((tabKey) => { - setTabKey(tabKey); - setPage(0); - scrollListToTop(); - }, [scrollListToTop]); - - const handleOnChange = useCallback((event: React.ChangeEvent) => { - setFilter(event.target.value); - }, []); - - const handleOnClear = useCallback(() => { - setFilter(''); - }, []); - const handleOnNextPage = useCallback(() => { setPage(page + 1); scrollListToTop(); @@ -155,65 +114,35 @@ const MarketplaceModal = () => { scrollListToTop(); }, [page, scrollListToTop]); - const handleNoResultsButtonClick = useCallback(() => { - handleChangeTab(MarketplaceTabs.ALL_LISTING); - }, [handleChangeTab]); - - const getHeaderInput = useCallback(() => { - if (isStreamlinedMarketplaceEnabled) { - return null; - } - - return ( - } - placeholder={formatMessage({id: 'marketplace_modal.search', defaultMessage: 'Search marketplace'})} - useLegend={false} - autoFocus={true} - clearable={true} - value={filter} - onChange={handleOnChange} - onClear={handleOnClear} - /> - ); - }, [filter, handleOnChange, handleOnClear]); - const getFooterContent = useCallback(() => { - if (isStreamlinedMarketplaceEnabled && listing.length <= ITEMS_PER_PAGE) { + if (listing.length <= ITEMS_PER_PAGE) { return null; } return ( ); - }, [installedListing.length, listing.length, page, handleOnNextPage, handleOnPreviousPage, tabKey, isStreamlinedMarketplaceEnabled]); + }, [listing.length, page, handleOnNextPage, handleOnPreviousPage]); const getAppendedContent = useCallback(() => { - if (!isStreamlinedMarketplaceEnabled || isCloud) { + if (isCloud) { return null; } return ; - }, [isStreamlinedMarketplaceEnabled, isCloud]); + }, [isCloud]); return ( { show={show} compassDesign={true} bodyPadding={false} - bodyDivider={isStreamlinedMarketplaceEnabled} + bodyDivider={true} footerDivider={true} onExited={handleOnClose} footerContent={getFooterContent()} appendedContent={getAppendedContent()} - headerInput={getHeaderInput()} > - {isStreamlinedMarketplaceEnabled ? ( - <> - {loading ? ( - - ) : ( - - )} - + {loading ? ( + ) : ( - - - {loading ? ( - - ) : ( - - )} - - - - - + )} ); diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index ae75563eb97..d3fddd7647a 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -5795,7 +5795,6 @@ "marketplace_command.no_permission": "You do not have the appropriate permissions to access the marketplace.", "marketplace_modal_list.no_plugins_filter": "No results for \"{filter}\"", "marketplace_modal.app_error": "Error connecting to the marketplace server. Please check your settings in the System Console.", - "marketplace_modal.install_plugins": "Install plugins", "marketplace_modal.installing": "Installing...", "marketplace_modal.list.configure": "Configure", "marketplace_modal.list.install": "Install", @@ -5811,10 +5810,6 @@ "marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "This update may contain breaking changes. Consult the release notes before upgrading.", "marketplace_modal.list.update_confirmation.title": "Confirm Plugin Update", "marketplace_modal.no_plugins": "No plugins found", - "marketplace_modal.no_plugins_installed": "No plugins installed found", - "marketplace_modal.search": "Search marketplace", - "marketplace_modal.tabs.all_listing": "All", - "marketplace_modal.tabs.installed_listing": "Installed ({count})", "marketplace_modal.title": "App Marketplace", "marketplace_modal.web_marketplace_link.desc": "Connect the tools you use most to Mattermost", "marketplace_modal.web_marketplace_link.title": "Discover community integrations", diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts index 9a059665979..bdffd79687e 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts @@ -330,10 +330,6 @@ export function moveThreadsEnabled(state: GlobalState): boolean { return getFeatureFlagValue(state, 'MoveThreadsEnabled') === 'true' && getLicense(state).IsLicensed === 'true'; } -export function streamlinedMarketplaceEnabled(state: GlobalState): boolean { - return getFeatureFlagValue(state, 'StreamlinedMarketplace') === 'true'; -} - export function getWysiwygEditorPreference(state: GlobalState): boolean { const featureEnabled = getFeatureFlagValue(state, 'WysiwygEditor') === 'true'; if (!featureEnabled) {