mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Bookmarks (#159)
* handle pinned items in UserPreferencesProvider * prototype add/remove pins * output pins * pins → bookmarks * styling * output datatoken info
This commit is contained in:
parent
a80c6ff5e8
commit
0972944e03
@ -14,7 +14,8 @@ function Empty(): ReactElement {
|
|||||||
export default function Table({
|
export default function Table({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
isLoading
|
isLoading,
|
||||||
|
...props
|
||||||
}: TableProps): ReactElement {
|
}: TableProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
@ -27,6 +28,7 @@ export default function Table({
|
|||||||
noDataComponent={<Empty />}
|
noDataComponent={<Empty />}
|
||||||
progressPending={isLoading}
|
progressPending={isLoading}
|
||||||
progressComponent={<Loader />}
|
progressComponent={<Loader />}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -53,5 +53,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.symbol {
|
.symbol {
|
||||||
margin-bottom: 0;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import Price from '../atoms/Price'
|
|||||||
import styles from './AssetTeaser.module.css'
|
import styles from './AssetTeaser.module.css'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import removeMarkdown from 'remove-markdown'
|
import removeMarkdown from 'remove-markdown'
|
||||||
|
import Tooltip from '../atoms/Tooltip'
|
||||||
|
|
||||||
declare type AssetTeaserProps = {
|
declare type AssetTeaserProps = {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
@ -23,7 +24,13 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
|||||||
return (
|
return (
|
||||||
<article className={styles.teaser}>
|
<article className={styles.teaser}>
|
||||||
<Link to={`/asset/${ddo.id}`} className={styles.link}>
|
<Link to={`/asset/${ddo.id}`} className={styles.link}>
|
||||||
<p className={styles.symbol}>{dataTokenInfo?.symbol}</p>
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
content={dataTokenInfo?.name}
|
||||||
|
className={styles.symbol}
|
||||||
|
>
|
||||||
|
{dataTokenInfo?.symbol}
|
||||||
|
</Tooltip>
|
||||||
<h1 className={styles.title}>{name}</h1>
|
<h1 className={styles.title}>{name}</h1>
|
||||||
|
|
||||||
{isCompute && <div className={styles.accessLabel}>Compute</div>}
|
{isCompute && <div className={styles.accessLabel}>Compute</div>}
|
||||||
|
16
src/components/molecules/Bookmarks.module.css
Normal file
16
src/components/molecules/Bookmarks.module.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.title {
|
||||||
|
line-height: var(--line-height);
|
||||||
|
margin: 0;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title a {
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
color: var(--brand-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
87
src/components/molecules/Bookmarks.tsx
Normal file
87
src/components/molecules/Bookmarks.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { useUserPreferences } from '../../providers/UserPreferences'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import Table from '../atoms/Table'
|
||||||
|
import { DDO, Logger, MetadataCache } from '@oceanprotocol/lib'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
import { Link } from 'gatsby'
|
||||||
|
import styles from './Bookmarks.module.css'
|
||||||
|
import Price from '../atoms/Price'
|
||||||
|
import Tooltip from '../atoms/Tooltip'
|
||||||
|
|
||||||
|
async function getAssetsBookmarked(pins: string[], metadataCacheUri: string) {
|
||||||
|
try {
|
||||||
|
const metadataCache = new MetadataCache(metadataCacheUri, Logger)
|
||||||
|
const result: DDO[] = []
|
||||||
|
|
||||||
|
for (const pin of pins) {
|
||||||
|
result.push(await metadataCache.retrieveDDO(pin))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Data Set',
|
||||||
|
selector: function getAssetRow(row: DDO) {
|
||||||
|
const { attributes } = row.findServiceByType('metadata')
|
||||||
|
return (
|
||||||
|
<h3 className={styles.title}>
|
||||||
|
<Link to={`/asset/${row.id}`}>{attributes.main.name}</Link>
|
||||||
|
</h3>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
grow: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Datatoken Symbol',
|
||||||
|
selector: function getAssetRow(row: DDO) {
|
||||||
|
return (
|
||||||
|
<Tooltip content={row.dataTokenInfo.name}>
|
||||||
|
{row.dataTokenInfo.symbol}
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Price',
|
||||||
|
selector: function getAssetRow(row: DDO) {
|
||||||
|
return <Price ddo={row} small conversion />
|
||||||
|
},
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Bookmarks(): ReactElement {
|
||||||
|
const { config } = useOcean()
|
||||||
|
const { bookmarks } = useUserPreferences()
|
||||||
|
|
||||||
|
const [pinned, setPinned] = useState<DDO[]>()
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>()
|
||||||
|
|
||||||
|
const noBookmarks = !bookmarks || !bookmarks.length
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (noBookmarks) return
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
setIsLoading(true)
|
||||||
|
const resultPinned = await getAssetsBookmarked(
|
||||||
|
bookmarks,
|
||||||
|
config.metadataCacheUri
|
||||||
|
)
|
||||||
|
setPinned(resultPinned)
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
}, [bookmarks, config.metadataCacheUri, noBookmarks])
|
||||||
|
|
||||||
|
return noBookmarks ? (
|
||||||
|
<div className={styles.empty}>Your bookmarks will appear here.</div>
|
||||||
|
) : (
|
||||||
|
<Table columns={columns} data={pinned} isLoading={isLoading} noTableHead />
|
||||||
|
)
|
||||||
|
}
|
28
src/components/organisms/AssetContent/Bookmark.module.css
Normal file
28
src/components/organisms/AssetContent/Bookmark.module.css
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.bookmark {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
right: calc(var(--spacer) / 4);
|
||||||
|
appearance: none;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark svg {
|
||||||
|
fill: var(--brand-grey-light);
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark:hover,
|
||||||
|
.bookmark:focus {
|
||||||
|
transform: translate3d(0, 6px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark.active svg {
|
||||||
|
fill: var(--brand-violet);
|
||||||
|
}
|
23
src/components/organisms/AssetContent/Bookmark.tsx
Normal file
23
src/components/organisms/AssetContent/Bookmark.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './Bookmark.module.css'
|
||||||
|
import { ReactComponent as BookmarkIcon } from '../../../images/bookmark.svg'
|
||||||
|
|
||||||
|
export default function Bookmark({ did }: { did: string }): ReactElement {
|
||||||
|
const { bookmarks, addBookmark, removeBookmark } = useUserPreferences()
|
||||||
|
const isBookmarked = bookmarks?.includes(did)
|
||||||
|
|
||||||
|
function handleBookmark() {
|
||||||
|
isBookmarked ? removeBookmark(did) : addBookmark(did)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleBookmark}
|
||||||
|
className={`${styles.bookmark} ${isBookmarked ? styles.active : ''} `}
|
||||||
|
title={isBookmarked ? 'Remove Bookmark' : 'Add Bookmark'}
|
||||||
|
>
|
||||||
|
<BookmarkIcon />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
@ -12,6 +12,7 @@
|
|||||||
.content {
|
.content {
|
||||||
composes: box from '../../atoms/Box.module.css';
|
composes: box from '../../atoms/Box.module.css';
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 60rem) {
|
@media (min-width: 60rem) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { MetadataMarket } from '../../../@types/MetaData'
|
import { MetadataMarket } from '../../../@types/MetaData'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Time from '../../atoms/Time'
|
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import Markdown from '../../atoms/Markdown'
|
import Markdown from '../../atoms/Markdown'
|
||||||
import MetaFull from './MetaFull'
|
import MetaFull from './MetaFull'
|
||||||
@ -12,7 +11,7 @@ import { useUserPreferences } from '../../../providers/UserPreferences'
|
|||||||
import Pricing from './Pricing'
|
import Pricing from './Pricing'
|
||||||
import { useOcean, usePricing } from '@oceanprotocol/react'
|
import { useOcean, usePricing } from '@oceanprotocol/react'
|
||||||
import EtherscanLink from '../../atoms/EtherscanLink'
|
import EtherscanLink from '../../atoms/EtherscanLink'
|
||||||
import MetaItem from './MetaItem'
|
import Bookmark from './Bookmark'
|
||||||
|
|
||||||
export interface AssetContentProps {
|
export interface AssetContentProps {
|
||||||
metadata: MetadataMarket
|
metadata: MetadataMarket
|
||||||
@ -89,6 +88,8 @@ export default function AssetContent({
|
|||||||
<code>{JSON.stringify(ddo, null, 2)}</code>
|
<code>{JSON.stringify(ddo, null, 2)}</code>
|
||||||
</pre>
|
</pre>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Bookmark did={ddo.id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import Container from '../atoms/Container'
|
|||||||
import Loader from '../atoms/Loader'
|
import Loader from '../atoms/Loader'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import Button from '../atoms/Button'
|
import Button from '../atoms/Button'
|
||||||
|
import Bookmarks from '../molecules/Bookmarks'
|
||||||
|
|
||||||
const queryHighest = {
|
const queryHighest = {
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -46,6 +47,7 @@ async function getAssets(query: SearchQuery, metadataCacheUri: string) {
|
|||||||
|
|
||||||
export default function HomePage(): ReactElement {
|
export default function HomePage(): ReactElement {
|
||||||
const { config } = useOcean()
|
const { config } = useOcean()
|
||||||
|
|
||||||
const [queryResultLatest, setQueryResultLatest] = useState<QueryResult>()
|
const [queryResultLatest, setQueryResultLatest] = useState<QueryResult>()
|
||||||
const [queryResultPoolsLatest, setQueryResultPoolsLatest] = useState<
|
const [queryResultPoolsLatest, setQueryResultPoolsLatest] = useState<
|
||||||
QueryResult
|
QueryResult
|
||||||
@ -83,6 +85,11 @@ export default function HomePage(): ReactElement {
|
|||||||
<SearchBar />
|
<SearchBar />
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
<section className={styles.latest}>
|
||||||
|
<h3>Bookmarks</h3>
|
||||||
|
<Bookmarks />
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className={styles.latest}>
|
<section className={styles.latest}>
|
||||||
<h3>Highest Liquidity Pools</h3>
|
<h3>Highest Liquidity Pools</h3>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
3
src/images/bookmark.svg
Normal file
3
src/images/bookmark.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="8" height="24" viewBox="0 0 8 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8 1.00001V24L4 20L0 24V1.00001C0 0.447009 0.585 -0.0199908 1 9.18139e-06H7C7.689 -0.0199908 8 0.447009 8 1.00001Z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 217 B |
@ -13,8 +13,11 @@ interface UserPreferencesValue {
|
|||||||
debug: boolean
|
debug: boolean
|
||||||
currency: string
|
currency: string
|
||||||
locale: string
|
locale: string
|
||||||
|
bookmarks: string[]
|
||||||
setDebug: (value: boolean) => void
|
setDebug: (value: boolean) => void
|
||||||
setCurrency: (value: string) => void
|
setCurrency: (value: string) => void
|
||||||
|
addBookmark: (did: string) => void
|
||||||
|
removeBookmark: (did: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserPreferencesContext = createContext(null)
|
const UserPreferencesContext = createContext(null)
|
||||||
@ -48,13 +51,14 @@ function UserPreferencesProvider({
|
|||||||
localStorage?.currency || 'EUR'
|
localStorage?.currency || 'EUR'
|
||||||
)
|
)
|
||||||
const [locale, setLocale] = useState<string>()
|
const [locale, setLocale] = useState<string>()
|
||||||
|
const [bookmarks, setBookmarks] = useState(localStorage?.bookmarks || [])
|
||||||
|
|
||||||
// Write values to localStorage on change
|
// Write values to localStorage on change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalStorage({ debug, currency })
|
setLocalStorage({ debug, currency, bookmarks })
|
||||||
}, [debug, currency])
|
}, [debug, currency, bookmarks])
|
||||||
|
|
||||||
// Set ocean-lib-js log levels, default: Error
|
// Set ocean.js log levels, default: Error
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
debug === true
|
debug === true
|
||||||
? Logger.setLevel(LogLevel.Verbose)
|
? Logger.setLevel(LogLevel.Verbose)
|
||||||
@ -67,6 +71,16 @@ function UserPreferencesProvider({
|
|||||||
setLocale(window.navigator.language)
|
setLocale(window.navigator.language)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
function addBookmark(didToAdd: string): void {
|
||||||
|
const newPinned = bookmarks.concat(didToAdd)
|
||||||
|
setBookmarks(newPinned)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeBookmark(didToAdd: string): void {
|
||||||
|
const newPinned = bookmarks.filter((did: string) => did !== didToAdd)
|
||||||
|
setBookmarks(newPinned)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserPreferencesContext.Provider
|
<UserPreferencesContext.Provider
|
||||||
value={
|
value={
|
||||||
@ -74,8 +88,11 @@ function UserPreferencesProvider({
|
|||||||
debug,
|
debug,
|
||||||
currency,
|
currency,
|
||||||
locale,
|
locale,
|
||||||
|
bookmarks,
|
||||||
setDebug,
|
setDebug,
|
||||||
setCurrency
|
setCurrency,
|
||||||
|
addBookmark,
|
||||||
|
removeBookmark
|
||||||
} as UserPreferencesValue
|
} as UserPreferencesValue
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user