Skip to content

Commit

Permalink
feat(chat): support url parameters for Marketplace (Issue #2614)
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyaBondar committed Jan 20, 2025
1 parent 9501d17 commit 92c0bf6
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 54 deletions.
17 changes: 0 additions & 17 deletions apps/chat/src/components/Marketplace/Marketplace.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { FloatingOverlay } from '@floating-ui/react';
import { useEffect } from 'react';

import { useSearchParams } from 'next/navigation';
import { useRouter } from 'next/router';

import { useScreenState } from '@/src/hooks/useScreenState';
Expand All @@ -16,11 +15,6 @@ import {
import { ModelsSelectors } from '@/src/store/models/models.reducers';
import { UISelectors } from '@/src/store/ui/ui.reducers';

import {
MarketplaceQueryParams,
MarketplaceTabs,
} from '@/src/constants/marketplace';

import { Spinner } from '@/src/components/Common/Spinner';
import { TabRenderer } from '@/src/components/Marketplace/TabRenderer';

Expand All @@ -30,7 +24,6 @@ export const Marketplace = () => {
const dispatch = useAppDispatch();

const router = useRouter();
const searchParams = useSearchParams();

const isFilterbarOpen = useAppSelector(
UISelectors.selectShowMarketplaceFilterbar,
Expand All @@ -46,16 +39,6 @@ export const Marketplace = () => {
const showOverlay =
(isFilterbarOpen || isProfileOpen) && screenState === ScreenState.MOBILE;

useEffect(() => {
dispatch(
MarketplaceActions.setSelectedTab(
searchParams.get(MarketplaceQueryParams.fromConversation)
? MarketplaceTabs.MY_APPLICATIONS
: MarketplaceTabs.HOME,
),
);
}, [dispatch, searchParams]);

useEffect(() => {
if (applyModelStatus === UploadStatus.LOADED) {
dispatch(
Expand Down
4 changes: 4 additions & 0 deletions apps/chat/src/constants/marketplace.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export enum MarketplaceQueryParams {
fromConversation = 'fromConversation',
model = 'model',
workspace = 'workspace',
types = 'types',
topics = 'topics',
search = 'search',
}

export enum FilterTypes {
Expand Down
186 changes: 155 additions & 31 deletions apps/chat/src/store/marketplace/marketplace.epics.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,119 @@
import { EMPTY, filter, of, switchMap } from 'rxjs';
import { EMPTY, Observable, concat, filter, of, switchMap } from 'rxjs';

import { AnyAction } from '@reduxjs/toolkit';

import { combineEpics } from 'redux-observable';

import { AppEpic } from '@/src/types/store';

import { MarketplaceQueryParams } from '@/src/constants/marketplace';
import {
FilterTypes,
MarketplaceQueryParams,
MarketplaceTabs,
} from '@/src/constants/marketplace';

import { ModelsSelectors } from '../models/models.reducers';
import { UIActions } from '../ui/ui.reducers';
import { MarketplaceActions } from './marketplace.reducers';
import {
selectDetailsModel,
selectSearchTerm,
selectSelectedFilters,
selectSelectedTab,
} from './marketplace.selectors';

import isNil from 'lodash/isNil';
import { ParsedUrlQueryInput } from 'querystring';

// const setDetailsModelEpic: AppEpic = (action$, _, { router }) =>
// action$.pipe(
// filter(MarketplaceActions.setDetailsModel.match),
// switchMap(({ payload }) => {
// const reference = payload?.reference;
// const query = router.query;
// if (reference) {
// router.push({
// query: {
// ...query,
// [MarketplaceQueryParams.model]: reference,
// },
// });
// } else {
// delete query[MarketplaceQueryParams.model];
// router.push({
// query,
// });
// }
// return EMPTY;
// }),
// );

const setDetailsModelEpic: AppEpic = (action$, _, { router }) =>
const addToQuery = (
query: ParsedUrlQueryInput,
key: string,
value: string | undefined,
) => {
if (value !== undefined) {
query[key] = value;
} else {
delete query[key];
}
};

const setQueryParamsEpic: AppEpic = (action$, state$, { router }) =>
action$.pipe(
filter(MarketplaceActions.setDetailsModel.match),
switchMap(({ payload }) => {
const reference = payload?.reference;
filter(
(action) =>
MarketplaceActions.setSelectedTab.match(action) ||
MarketplaceActions.setDetailsModel.match(action) ||
MarketplaceActions.setSelectedFilters.match(action) ||
MarketplaceActions.setFilters.match(action) ||
MarketplaceActions.setSearchTerm.match(action),
),
switchMap(() => {
const state = state$.value;
const query = router.query;
if (reference) {
router.push({
query: {
...query,
[MarketplaceQueryParams.model]: reference,
},
});
} else {
delete query[MarketplaceQueryParams.model];
router.push({
// workspace tab
const selectedTab = selectSelectedTab(state);
addToQuery(
query,
MarketplaceQueryParams.workspace,
selectedTab === MarketplaceTabs.MY_APPLICATIONS ? '1' : undefined,
);
// application link
const reference = selectDetailsModel(state)?.reference;
addToQuery(
query,
MarketplaceQueryParams.model,
reference ? reference : undefined,
);
// filters
const filters = selectSelectedFilters(state);
addToQuery(
query,
MarketplaceQueryParams.types,
filters.Type.length ? filters.Type.join(',') : undefined,
);
addToQuery(
query,
MarketplaceQueryParams.topics,
filters.Topics.length ? filters.Topics.join(',') : undefined,
);
// search
const searchTerm = selectSearchTerm(state);
addToQuery(
query,
MarketplaceQueryParams.search,
searchTerm ? searchTerm : undefined,
);

router.push(
{
query,
});
}
},
undefined,
{ shallow: true },
);
return EMPTY;
}),
);
Expand All @@ -38,30 +123,69 @@ const initQueryParamsEpic: AppEpic = (action$, state$, { router }) =>
filter(MarketplaceActions.initQueryParams.match),
switchMap(() => {
const query = router.query;
const modelReference = query[MarketplaceQueryParams.model] as
| string
| undefined;
const modelsMap = ModelsSelectors.selectModelsMap(state$.value);
const model = modelReference ? modelsMap[modelReference] : undefined;

const actions: Observable<AnyAction>[] = [];
// application link
const modelReference = query[MarketplaceQueryParams.model];
const modelsMap = ModelsSelectors.selectModelsMap(state$.value);
const model =
typeof modelReference === 'string'
? modelsMap[modelReference]
: undefined;
if (modelReference) {
if (model) {
return of(
MarketplaceActions.setDetailsModel({
reference: modelReference,
isSuggested: false,
}),
actions.push(
of(
MarketplaceActions.setDetailsModel({
reference: modelReference as string,
isSuggested: false,
}),
),
);
} else {
return of(UIActions.showErrorToast('Agent by this link not found'));
actions.push(
of(UIActions.showErrorToast('Agent by this link not found')),
);
}
}
// workspace tab
const workSpaceTab =
query[MarketplaceQueryParams.fromConversation] ||
!isNil(query[MarketplaceQueryParams.workspace]);
actions.push(
of(
MarketplaceActions.setSelectedTab(
workSpaceTab
? MarketplaceTabs.MY_APPLICATIONS
: MarketplaceTabs.HOME,
),
),
);
// filters
const types = ((query[MarketplaceQueryParams.types] as string) ?? '')
.split(',')
.filter(Boolean);
const topics = ((query[MarketplaceQueryParams.topics] as string) ?? '')
.split(',')
.filter(Boolean);
actions.push(
of(
MarketplaceActions.setFilters({
[FilterTypes.ENTITY_TYPE]: types,
[FilterTypes.TOPICS]: topics,
}),
),
);
// search
const search = (query[MarketplaceQueryParams.search] as string) ?? '';
actions.push(of(MarketplaceActions.setSearchTerm(search)));

return EMPTY;
return concat(...actions);
}),
);

export const MarketplaceEpics = combineEpics(
initQueryParamsEpic,
setDetailsModelEpic,
// setDetailsModelEpic,
setQueryParamsEpic,
);
16 changes: 10 additions & 6 deletions apps/chat/src/store/marketplace/marketplace.reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import xor from 'lodash/xor';

export { MarketplaceSelectors };

interface Filters {
[FilterTypes.ENTITY_TYPE]: string[];
[FilterTypes.TOPICS]: string[];
// [FilterTypes.CAPABILITIES]: string[];
// [FilterTypes.ENVIRONMENT]: string[];
}
export interface MarketplaceState {
selectedFilters: {
[FilterTypes.ENTITY_TYPE]: string[];
[FilterTypes.TOPICS]: string[];
// [FilterTypes.CAPABILITIES]: string[];
// [FilterTypes.ENVIRONMENT]: string[];
};
selectedFilters: Filters;
searchTerm: string;
selectedTab: MarketplaceTabs;
applyModelStatus: UploadStatus;
Expand All @@ -42,6 +43,9 @@ export const marketplaceSlice = createSlice({
initialState,
reducers: {
initQueryParams: (state) => state,
setFilters: (state, { payload }: PayloadAction<Filters>) => {
state.selectedFilters = payload;
},
setSelectedFilters: (
state,
{ payload }: PayloadAction<{ filterType: FilterTypes; value: string }>,
Expand Down

0 comments on commit 92c0bf6

Please sign in to comment.