mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-07 13:07:22 +00:00
Added Recently viewed feature for Landing page (#440)
* Added Recently viewed feature for Landing page * Updated for fqn instead of id * Adding service type to storage with entity fqn
This commit is contained in:
parent
297fdaea8d
commit
3f447dc19a
@ -3821,6 +3821,12 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/reactjs-localstorage": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/reactjs-localstorage/-/reactjs-localstorage-1.0.0.tgz",
|
||||
"integrity": "sha512-0Y3f/vGR3yz46yfoqtyVljTfnKxYZVC87p2AXouO3Fc14/aBOUt8oKTLCDej8/sd5+yqx6WA2YBUsT2eSXx5cA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/scheduler": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||
@ -17555,6 +17561,11 @@
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"reactjs-localstorage": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/reactjs-localstorage/-/reactjs-localstorage-1.0.1.tgz",
|
||||
"integrity": "sha512-eNSwfiVyVDhfjw9h83PBvgqQcFxmyK6KGPrPlutuaaTlALRZVQLZ0MAN72iU9UT+bKtscwtdogHQ2ikZ7rjT+w=="
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
"react-select": "^3.1.1",
|
||||
"react-syntax-highlighter": "^15.4.4",
|
||||
"react-tippy": "^1.4.0",
|
||||
"reactjs-localstorage": "^1.0.1",
|
||||
"recharts": "^1.8.5",
|
||||
"rehype-raw": "^6.0.0",
|
||||
"remark-gfm": "^1.0.0",
|
||||
@ -122,6 +123,7 @@
|
||||
"@types/react-dom": "^16.9.9",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/react-test-renderer": "^17.0.0",
|
||||
"@types/reactjs-localstorage": "^1.0.0",
|
||||
"@types/testing-library__jest-dom": "^5.9.5",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
|
||||
@ -33,6 +33,15 @@ export const getDashboards: Function = (
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const getDashboardDetails: Function = (
|
||||
id: string,
|
||||
arrQueryFields: string
|
||||
): Promise<AxiosResponse> => {
|
||||
const url = getURLWithQueryFields(`/dashboards/${id}`, arrQueryFields);
|
||||
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const getDashboardByFqn: Function = (
|
||||
fqn: string,
|
||||
arrQueryFields: string
|
||||
|
||||
@ -33,6 +33,15 @@ export const getTopics: Function = (
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const getTopicDetails: Function = (
|
||||
id: string,
|
||||
arrQueryFields: string
|
||||
): Promise<AxiosResponse> => {
|
||||
const url = getURLWithQueryFields(`/topics/${id}`, arrQueryFields);
|
||||
|
||||
return APIClient.get(url);
|
||||
};
|
||||
|
||||
export const getTopicByFqn: Function = (
|
||||
fqn: string,
|
||||
arrQueryFields: string
|
||||
|
||||
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ColumnTags, FormatedTableData } from 'Models';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { getDashboardByFqn } from '../../axiosAPIs/dashboardAPI';
|
||||
import { getTableDetailsByFQN } from '../../axiosAPIs/tableAPI';
|
||||
import { getTopicByFqn } from '../../axiosAPIs/topicsAPI';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { SearchIndex } from '../../enums/search.enum';
|
||||
import { getRecentlyViewedData } from '../../utils/CommonUtils';
|
||||
import { getOwnerFromId, getTierFromTableTags } from '../../utils/TableUtils';
|
||||
import { getTableTags } from '../../utils/TagsUtils';
|
||||
import TableDataCard from '../common/table-data-card/TableDataCard';
|
||||
import Onboarding from '../onboarding/Onboarding';
|
||||
|
||||
const RecentlyViewed: FunctionComponent = () => {
|
||||
const recentlyViewedData = getRecentlyViewedData();
|
||||
const [data, setData] = useState<Array<FormatedTableData>>([]);
|
||||
|
||||
const fetchRecentlyViewedEntity = async () => {
|
||||
const arrData: Array<FormatedTableData> = [];
|
||||
|
||||
for (const oData of recentlyViewedData) {
|
||||
// for (let i = 0; i < recentlyViewedData.length; i++) {
|
||||
// const oData = recentlyViewedData[i];
|
||||
switch (oData.entityType) {
|
||||
case EntityType.DATASET: {
|
||||
const res = await getTableDetailsByFQN(
|
||||
oData.fqn,
|
||||
'database, usageSummary, tags, owner'
|
||||
);
|
||||
|
||||
const {
|
||||
description,
|
||||
id,
|
||||
name,
|
||||
columns,
|
||||
owner,
|
||||
usageSummary,
|
||||
fullyQualifiedName,
|
||||
} = res.data;
|
||||
const tableTags = getTableTags(columns || []);
|
||||
arrData.push({
|
||||
description,
|
||||
fullyQualifiedName,
|
||||
id,
|
||||
index: SearchIndex.TABLE,
|
||||
name,
|
||||
owner: getOwnerFromId(owner?.id)?.name || '--',
|
||||
serviceType: oData.serviceType,
|
||||
tags: tableTags.map((tag) => tag.tagFQN),
|
||||
tier: getTierFromTableTags(tableTags),
|
||||
weeklyPercentileRank: usageSummary?.weeklyStats.percentileRank || 0,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case EntityType.TOPIC: {
|
||||
const res = await getTopicByFqn(oData.fqn, 'owner, service, tags');
|
||||
|
||||
const { description, id, name, tags, owner, fullyQualifiedName } =
|
||||
res.data;
|
||||
arrData.push({
|
||||
description,
|
||||
fullyQualifiedName,
|
||||
id,
|
||||
index: SearchIndex.TOPIC,
|
||||
name,
|
||||
owner: getOwnerFromId(owner?.id)?.name || '--',
|
||||
serviceType: oData.serviceType,
|
||||
tags: (tags as Array<ColumnTags>).map((tag) => tag.tagFQN),
|
||||
tier: getTierFromTableTags(tags as Array<ColumnTags>),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case EntityType.DASHBOARD: {
|
||||
const res = await getDashboardByFqn(
|
||||
oData.fqn,
|
||||
'owner, service, tags, usageSummary'
|
||||
);
|
||||
|
||||
const {
|
||||
description,
|
||||
id,
|
||||
displayName,
|
||||
tags,
|
||||
owner,
|
||||
fullyQualifiedName,
|
||||
} = res.data;
|
||||
arrData.push({
|
||||
description,
|
||||
fullyQualifiedName,
|
||||
id,
|
||||
index: SearchIndex.DASHBOARD,
|
||||
name: displayName,
|
||||
owner: getOwnerFromId(owner?.id)?.name || '--',
|
||||
serviceType: oData.serviceType,
|
||||
tags: (tags as Array<ColumnTags>).map((tag) => tag.tagFQN),
|
||||
tier: getTierFromTableTags(tags as Array<ColumnTags>),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
setData(arrData);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (recentlyViewedData.length) {
|
||||
fetchRecentlyViewedEntity();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{data.length ? (
|
||||
data.map((item, index) => {
|
||||
return (
|
||||
<div className="tw-mb-3" key={index}>
|
||||
<TableDataCard
|
||||
description={item.description}
|
||||
fullyQualifiedName={item.fullyQualifiedName}
|
||||
indexType={item.index}
|
||||
name={item.name}
|
||||
owner={item.owner}
|
||||
serviceType={item.serviceType || '--'}
|
||||
tableType={item.tableType}
|
||||
tags={item.tags}
|
||||
tier={item.tier?.split('.')[1]}
|
||||
usage={item.weeklyPercentileRank}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Onboarding />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecentlyViewed;
|
||||
@ -42,6 +42,7 @@ type SearchedDataProp = {
|
||||
showResultCount?: boolean;
|
||||
searchText?: string;
|
||||
showOnboardingTemplate?: boolean;
|
||||
showOnlyChildren?: boolean;
|
||||
};
|
||||
const SearchedData: React.FC<SearchedDataProp> = ({
|
||||
children,
|
||||
@ -51,6 +52,7 @@ const SearchedData: React.FC<SearchedDataProp> = ({
|
||||
paginate,
|
||||
showResultCount = false,
|
||||
showOnboardingTemplate = false,
|
||||
showOnlyChildren = false,
|
||||
searchText,
|
||||
totalValue,
|
||||
fetchLeftPanel,
|
||||
@ -96,29 +98,33 @@ const SearchedData: React.FC<SearchedDataProp> = ({
|
||||
<Loader />
|
||||
) : (
|
||||
<>
|
||||
{totalValue > 0 || showOnboardingTemplate ? (
|
||||
{totalValue > 0 || showOnboardingTemplate || showOnlyChildren ? (
|
||||
<>
|
||||
{children}
|
||||
{showResultCount && searchText ? (
|
||||
<div className="tw-mb-1">
|
||||
{pluralize(totalValue, 'result')}
|
||||
</div>
|
||||
) : null}
|
||||
{data.length > 0 ? (
|
||||
<div className="tw-grid tw-grid-rows-1 tw-grid-cols-1">
|
||||
{highlightSearchResult()}
|
||||
{totalValue > PAGE_SIZE && data.length > 0 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
paginate={paginate}
|
||||
sizePerPage={PAGE_SIZE}
|
||||
totalNumberOfValues={totalValue}
|
||||
/>
|
||||
{!showOnlyChildren ? (
|
||||
<>
|
||||
{showResultCount && searchText ? (
|
||||
<div className="tw-mb-1">
|
||||
{pluralize(totalValue, 'result')}
|
||||
</div>
|
||||
) : null}
|
||||
{data.length > 0 ? (
|
||||
<div className="tw-grid tw-grid-rows-1 tw-grid-cols-1">
|
||||
{highlightSearchResult()}
|
||||
{totalValue > PAGE_SIZE && data.length > 0 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
paginate={paginate}
|
||||
sizePerPage={PAGE_SIZE}
|
||||
totalNumberOfValues={totalValue}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Onboarding />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Onboarding />
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<ErrorPlaceHolderES type="noData" />
|
||||
|
||||
@ -21,6 +21,7 @@ export const API_RES_MAX_SIZE = 100000;
|
||||
export const LIST_SIZE = 5;
|
||||
export const SIDEBAR_WIDTH_COLLAPSED = 290;
|
||||
export const SIDEBAR_WIDTH_EXPANDED = 290;
|
||||
export const LOCALSTORAGE_RECENTLY_VIEWED = 'recentlyViewedData';
|
||||
export const oidcTokenKey = 'oidcIdToken';
|
||||
export const imageTypes = {
|
||||
image: 's96-c',
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export enum EntityType {
|
||||
DATASET = 'dataset',
|
||||
TOPIC = 'topic',
|
||||
DASHBOARD = 'dashboard',
|
||||
}
|
||||
@ -201,11 +201,10 @@ declare module 'Models' {
|
||||
owner: string;
|
||||
tableType?: string;
|
||||
tags: string[];
|
||||
tableEntity: TableEntity;
|
||||
dailyStats: number;
|
||||
dailyPercentileRank: number;
|
||||
weeklyStats: number;
|
||||
weeklyPercentileRank: number;
|
||||
dailyStats?: number;
|
||||
dailyPercentileRank?: number;
|
||||
weeklyStats?: number;
|
||||
weeklyPercentileRank?: number;
|
||||
service?: string;
|
||||
serviceType?: string;
|
||||
tier: string;
|
||||
@ -377,4 +376,14 @@ declare module 'Models' {
|
||||
}
|
||||
|
||||
// topic interface end
|
||||
|
||||
interface RecentlyViewedData {
|
||||
entityType: 'dataset' | 'topic' | 'dashboard';
|
||||
fqn: string;
|
||||
serviceType?: string;
|
||||
timestamp: number;
|
||||
}
|
||||
export interface RecentlyViewed {
|
||||
data: Array<RecentlyViewedData>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,9 +24,11 @@ import ManageTab from '../../components/my-data-details/ManageTab';
|
||||
import TagsContainer from '../../components/tags-container/tags-container';
|
||||
import Tags from '../../components/tags/tags';
|
||||
import { getServiceDetailsPath } from '../../constants/constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { Chart } from '../../generated/entity/data/chart';
|
||||
import { Dashboard, TagLabel } from '../../generated/entity/data/dashboard';
|
||||
import {
|
||||
addToRecentViewed,
|
||||
getCurrentUserId,
|
||||
getUserTeams,
|
||||
isEven,
|
||||
@ -147,6 +149,7 @@ const MyDashBoardPage = () => {
|
||||
id,
|
||||
description,
|
||||
followers,
|
||||
fullyQualifiedName,
|
||||
service,
|
||||
tags,
|
||||
owner,
|
||||
@ -182,6 +185,13 @@ const MyDashBoardPage = () => {
|
||||
activeTitle: true,
|
||||
},
|
||||
]);
|
||||
|
||||
addToRecentViewed({
|
||||
entityType: EntityType.DASHBOARD,
|
||||
fqn: fullyQualifiedName,
|
||||
serviceType: serviceRes.data.serviceType,
|
||||
timestamp: 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
fetchCharts(charts).then((charts) => setCharts(charts));
|
||||
|
||||
@ -51,8 +51,10 @@ import {
|
||||
getDatabaseDetailsPath,
|
||||
getServiceDetailsPath,
|
||||
} from '../../constants/constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import useToastContext from '../../hooks/useToastContext';
|
||||
import {
|
||||
addToRecentViewed,
|
||||
getCurrentUserId,
|
||||
getPartialNameFromFQN,
|
||||
getTableFQNFromColumnFQN,
|
||||
@ -308,6 +310,7 @@ const MyDataDetailsPage = () => {
|
||||
owner,
|
||||
usageSummary,
|
||||
followers,
|
||||
fullyQualifiedName,
|
||||
joins,
|
||||
tags,
|
||||
sampleData,
|
||||
@ -347,6 +350,13 @@ const MyDataDetailsPage = () => {
|
||||
activeTitle: true,
|
||||
},
|
||||
]);
|
||||
|
||||
addToRecentViewed({
|
||||
entityType: EntityType.DATASET,
|
||||
fqn: fullyQualifiedName,
|
||||
serviceType: resService.data.serviceType,
|
||||
timestamp: 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@ -25,6 +25,7 @@ import { searchData } from '../../axiosAPIs/miscAPI';
|
||||
import ErrorPlaceHolderES from '../../components/common/error-with-placeholder/ErrorPlaceHolderES';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import MyDataHeader from '../../components/my-data/MyDataHeader';
|
||||
import RecentlyViewed from '../../components/recently-viewed/RecentlyViewed';
|
||||
import SearchedData from '../../components/searched-data/SearchedData';
|
||||
import { ERROR500, PAGE_SIZE } from '../../constants/constants';
|
||||
import { Ownership } from '../../enums/mydata.enum';
|
||||
@ -41,7 +42,7 @@ const MyDataPage: React.FC = (): React.ReactElement => {
|
||||
const [data, setData] = useState<Array<FormatedTableData>>([]);
|
||||
const [currentPage, setCurrentPage] = useState<number>(1);
|
||||
const [totalNumberOfValue, setTotalNumberOfValues] = useState<number>(0);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [currentTab, setCurrentTab] = useState<number>(1);
|
||||
const [error, setError] = useState<string>('');
|
||||
const [filter, setFilter] = useState<string>('');
|
||||
@ -119,7 +120,7 @@ const MyDataPage: React.FC = (): React.ReactElement => {
|
||||
setFilter('');
|
||||
setCurrentPage(1);
|
||||
}}>
|
||||
All
|
||||
Recently Viewed
|
||||
</button>
|
||||
<button
|
||||
className={`tw-pb-2 tw-px-4 tw-gh-tabs ${getActiveTabClass(2)}`}
|
||||
@ -174,6 +175,7 @@ const MyDataPage: React.FC = (): React.ReactElement => {
|
||||
data={data}
|
||||
paginate={paginate}
|
||||
searchText="*"
|
||||
showOnlyChildren={currentTab === 1}
|
||||
showResultCount={filter && data.length > 0 ? true : false}
|
||||
totalValue={totalNumberOfValue}>
|
||||
<>
|
||||
@ -185,6 +187,7 @@ const MyDataPage: React.FC = (): React.ReactElement => {
|
||||
)}
|
||||
/>
|
||||
{getTabs()}
|
||||
{currentTab === 1 ? <RecentlyViewed /> : null}
|
||||
</>
|
||||
</SearchedData>
|
||||
)}
|
||||
|
||||
@ -19,7 +19,12 @@ import Loader from '../../components/Loader/Loader';
|
||||
import ManageTab from '../../components/my-data-details/ManageTab';
|
||||
import SchemaEditor from '../../components/schema-editor/SchemaEditor';
|
||||
import { getServiceDetailsPath } from '../../constants/constants';
|
||||
import { getCurrentUserId, getUserTeams } from '../../utils/CommonUtils';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import {
|
||||
addToRecentViewed,
|
||||
getCurrentUserId,
|
||||
getUserTeams,
|
||||
} from '../../utils/CommonUtils';
|
||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
||||
import {
|
||||
getOwnerFromId,
|
||||
@ -95,6 +100,7 @@ const MyTopicDetailPage = () => {
|
||||
id,
|
||||
description,
|
||||
followers,
|
||||
fullyQualifiedName,
|
||||
name,
|
||||
partitions,
|
||||
schemaType,
|
||||
@ -137,6 +143,13 @@ const MyTopicDetailPage = () => {
|
||||
activeTitle: true,
|
||||
},
|
||||
]);
|
||||
|
||||
addToRecentViewed({
|
||||
entityType: EntityType.TOPIC,
|
||||
fqn: fullyQualifiedName,
|
||||
serviceType: serviceRes.data.serviceType,
|
||||
timestamp: 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
setLoading(false);
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
import { UserTeam } from 'Models';
|
||||
import { RecentlyViewed, RecentlyViewedData, UserTeam } from 'Models';
|
||||
import React from 'react';
|
||||
import { reactLocalStorage } from 'reactjs-localstorage';
|
||||
import AppState from '../AppState';
|
||||
import { LOCALSTORAGE_RECENTLY_VIEWED } from '../constants/constants';
|
||||
import { countBackground } from './styleconstant';
|
||||
|
||||
export const arraySorterByKey = (
|
||||
@ -103,3 +105,43 @@ export const getCountBadge = (count = 0) => {
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const addToRecentViewed = (eData: RecentlyViewedData): void => {
|
||||
const entityData = { ...eData, timestamp: Date.now() };
|
||||
let recentlyViewed: RecentlyViewed = reactLocalStorage.getObject(
|
||||
LOCALSTORAGE_RECENTLY_VIEWED
|
||||
) as RecentlyViewed;
|
||||
if (recentlyViewed?.data) {
|
||||
const arrData = recentlyViewed.data
|
||||
.filter((item) => item.fqn !== entityData.fqn)
|
||||
.sort(
|
||||
arraySorterByKey('timestamp', true) as (
|
||||
a: RecentlyViewedData,
|
||||
b: RecentlyViewedData
|
||||
) => number
|
||||
);
|
||||
arrData.unshift(entityData);
|
||||
|
||||
if (arrData.length > 5) {
|
||||
arrData.pop();
|
||||
}
|
||||
recentlyViewed.data = arrData;
|
||||
} else {
|
||||
recentlyViewed = {
|
||||
data: [entityData],
|
||||
};
|
||||
}
|
||||
reactLocalStorage.setObject(LOCALSTORAGE_RECENTLY_VIEWED, recentlyViewed);
|
||||
};
|
||||
|
||||
export const getRecentlyViewedData = (): Array<RecentlyViewedData> => {
|
||||
const recentlyViewed: RecentlyViewed = reactLocalStorage.getObject(
|
||||
LOCALSTORAGE_RECENTLY_VIEWED
|
||||
) as RecentlyViewed;
|
||||
|
||||
if (recentlyViewed?.data) {
|
||||
return recentlyViewed.data;
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user