1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

new changes

This commit is contained in:
marcoelissa 2022-09-29 15:47:50 +07:00
parent 1c3baad181
commit e639b419d8
26 changed files with 8709 additions and 912 deletions

View File

8805
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,7 @@
"react-modal": "^3.15.1",
"react-paginate": "^8.1.3",
"react-spring": "^9.5.2",
"react-string-replace": "^1.1.0",
"react-tabs": "^5.1.0",
"react-toastify": "^9.0.4",
"remark": "^13.0.0",

View File

@ -8,49 +8,68 @@ import React, {
} from 'react'
import { sleep } from '@utils/index'
import { Orbis } from '@orbisclub/orbis-sdk'
import { useWeb3 } from './Web3'
const OrbisContext = createContext(null)
function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
const { web3Provider } = useWeb3()
const [orbis, setOrbis] = useState<OrbisInterface>()
const [account, setAccount] = useState<AccountInterface>()
const [account, setAccount] = useState<OrbisAccountInterface>()
// Connecting to Orbis
const connectOrbis = async (): Promise<void> => {
const connectOrbis = async (provider: object): Promise<void> => {
if (!orbis) return
const res = await orbis.connect()
const res = await orbis.connect(provider)
if (res.status !== 200) {
await sleep(2000)
connectOrbis()
connectOrbis(provider)
} else {
setAccount(res)
}
}
const checkConnection = async (provider: object): Promise<void> => {
const res = await orbis.isConnected()
if (res.status === 200) {
setAccount(res)
} else {
connectOrbis(web3Provider)
}
}
// Init Orbis
useEffect(() => {
const _orbis = new Orbis()
setOrbis(_orbis)
}, [])
// Check if already has ceramic-session
// Check if already connected to orbis
// useEffect(() => {
// if (!orbis) return
// const isConnected = async (): Promise<void> => {
// const res = await orbis.isConnected()
// if (res.status !== 200) {
// await sleep(2000)
// isConnected()
// } else {
// setAccount(res)
// }
// }
// isConnected()
// }, [orbis])
// Check if wallet connected
useEffect(() => {
if (!orbis) return
const isConnected = async (): Promise<void> => {
const res = await orbis.isConnected()
if (res.status !== 200) {
await sleep(2000)
isConnected()
} else {
setAccount(res)
}
if (!account && orbis && web3Provider) {
checkConnection(web3Provider)
}
isConnected()
}, [orbis])
}, [account, orbis, web3Provider])
return (
<OrbisContext.Provider value={{ orbis, account, connectOrbis }}>

View File

@ -1,7 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="24" height="24">
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 01-.825-.242m9.345-8.334a2.126 2.126 0 00-.476-.095 48.64 48.64 0 00-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0011.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="-1.6 -1.6 19.2 19.2">
<path d="M16 8c0 3.866-3.582 7-8 7a9.06 9.06 0 01-2.347-.306c-.584.296-1.925.864-4.181 1.234-.2.032-.352-.176-.273-.362.354-.836.674-1.95.77-2.966C.744 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7zM5 8a1 1 0 10-2 0 1 1 0 002 0zm4 0a1 1 0 10-2 0 1 1 0 002 0zm3 1a1 1 0 100-2 1 1 0 000 2z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="19.2" height="19.2" viewBox="-1.6 -1.6 19.2 19.2"><path d="M16 8c0 3.866-3.582 7-8 7a9.06 9.06 0 01-2.347-.306c-.584.296-1.925.864-4.181 1.234-.2.032-.352-.176-.273-.362.354-.836.674-1.95.77-2.966C.744 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7zM5 8a1 1 0 10-2 0 1 1 0 002 0zm4 0a1 1 0 10-2 0 1 1 0 002 0zm3 1a1 1 0 100-2 1 1 0 000 2z" /></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 399 B

2
src/@images/comment.svg Normal file
View File

@ -0,0 +1,2 @@
<svg width="19.2" height="19.2" viewBox="-1.6 -1.6 19.2 19.2"><path fill-rule="evenodd" d="M1.5 2.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v5.5a.25.25 0 01-.25.25h-3.5a.75.75 0 00-.53.22L3.5 11.44V9.25a.75.75 0 00-.75-.75h-1a.25.25 0 01-.25-.25v-5.5zM1.75 1A1.75 1.75 0 000 2.75v5.5C0 9.216.784 10 1.75 10H2v1.543a1.457 1.457 0 002.487 1.03L7.061 10h3.189A1.75 1.75 0 0012 8.25v-5.5A1.75 1.75 0 0010.25 1h-8.5zM14.5 4.75a.25.25 0 00-.25-.25h-.5a.75.75 0 110-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0114.25 12H14v1.543a1.457 1.457 0 01-2.487 1.03L9.22 12.28a.75.75 0 111.06-1.06l2.22 2.22v-2.19a.75.75 0 01.75-.75h1a.25.25 0 00.25-.25v-5.5z"></path></svg>

After

Width:  |  Height:  |  Size: 667 B

1
src/@images/laugh.svg Normal file
View File

@ -0,0 +1 @@
<svg width="24" height="24" viewBox="-51.52 -43.52 599.04 599.04"><path d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm141.4 389.4c-37.8 37.8-88 58.6-141.4 58.6s-103.6-20.8-141.4-58.6S48 309.4 48 256s20.8-103.6 58.6-141.4S194.6 56 248 56s103.6 20.8 141.4 58.6S448 202.6 448 256s-20.8 103.6-58.6 141.4zM343.6 196l33.6-40.3c8.6-10.3-3.8-24.8-15.4-18l-80 48c-7.8 4.7-7.8 15.9 0 20.6l80 48c11.5 6.8 24-7.6 15.4-18L343.6 196zm-209.4 58.3l80-48c7.8-4.7 7.8-15.9 0-20.6l-80-48c-11.6-6.9-24 7.7-15.4 18l33.6 40.3-33.6 40.3c-8.7 10.4 3.8 24.8 15.4 18zM362.4 288H133.6c-8.2 0-14.5 7-13.5 15 7.5 59.2 58.9 105 121.1 105h13.6c62.2 0 113.6-45.8 121.1-105 1-8-5.3-15-13.5-15z"></path></svg>

After

Width:  |  Height:  |  Size: 703 B

View File

@ -0,0 +1 @@
<svg width="24" height="24" viewBox="-43.52 -43.52 599.04 599.04"><path d="M466.27 225.31c4.674-22.647.864-44.538-8.99-62.99 2.958-23.868-4.021-48.565-17.34-66.99C438.986 39.423 404.117 0 327 0c-7 0-15 .01-22.22.01C201.195.01 168.997 40 128 40h-10.845c-5.64-4.975-13.042-8-21.155-8H32C14.327 32 0 46.327 0 64v240c0 17.673 14.327 32 32 32h64c11.842 0 22.175-6.438 27.708-16h7.052c19.146 16.953 46.013 60.653 68.76 83.4 13.667 13.667 10.153 108.6 71.76 108.6 57.58 0 95.27-31.936 95.27-104.73 0-18.41-3.93-33.73-8.85-46.54h36.48c48.602 0 85.82-41.565 85.82-85.58 0-19.15-4.96-34.99-13.73-49.84zM64 296c-13.255 0-24-10.745-24-24s10.745-24 24-24 24 10.745 24 24-10.745 24-24 24zm330.18 16.73H290.19c0 37.82 28.36 55.37 28.36 94.54 0 23.75 0 56.73-47.27 56.73-18.91-18.91-9.46-66.18-37.82-94.54C206.9 342.89 167.28 272 138.92 272H128V85.83c53.611 0 100.001-37.82 171.64-37.82h37.82c35.512 0 60.82 17.12 53.12 65.9 15.2 8.16 26.5 36.44 13.94 57.57 21.581 20.384 18.699 51.065 5.21 65.62 9.45 0 22.36 18.91 22.27 37.81-.09 18.91-16.71 37.82-37.82 37.82z"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
src/@images/thumbsup.svg Normal file
View File

@ -0,0 +1 @@
<svg width="24" height="24" viewBox="-43.52 -43.52 599.04 599.04"><path d="M466.27 286.69C475.04 271.84 480 256 480 236.85c0-44.015-37.218-85.58-85.82-85.58H357.7c4.92-12.81 8.85-28.13 8.85-46.54C366.55 31.936 328.86 0 271.28 0c-61.607 0-58.093 94.933-71.76 108.6-22.747 22.747-49.615 66.447-68.76 83.4H32c-17.673 0-32 14.327-32 32v240c0 17.673 14.327 32 32 32h64c14.893 0 27.408-10.174 30.978-23.95 44.509 1.001 75.06 39.94 177.802 39.94 7.22 0 15.22.01 22.22.01 77.117 0 111.986-39.423 112.94-95.33 13.319-18.425 20.299-43.122 17.34-66.99 9.854-18.452 13.664-40.343 8.99-62.99zm-61.75 53.83c12.56 21.13 1.26 49.41-13.94 57.57 7.7 48.78-17.608 65.9-53.12 65.9h-37.82c-71.639 0-118.029-37.82-171.64-37.82V240h10.92c28.36 0 67.98-70.89 94.54-97.46 28.36-28.36 18.91-75.63 37.82-94.54 47.27 0 47.27 32.98 47.27 56.73 0 39.17-28.36 56.72-28.36 94.54h103.99c21.11 0 37.73 18.91 37.82 37.82.09 18.9-12.82 37.81-22.27 37.81 13.489 14.555 16.371 45.236-5.21 65.62zM88 432c0 13.255-10.745 24-24 24s-24-10.745-24-24 10.745-24 24-24 24 10.745 24 24z"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

89
src/@types/Orbis.d.ts vendored
View File

@ -3,7 +3,7 @@
declare module '@orbisclub/orbis-sdk'
interface OrbisInterface {
declare interface OrbisInterface {
connect: function
connectLit: function
connectWithSeed: function
@ -52,9 +52,94 @@ interface OrbisInterface {
updateTileDocument: function
}
interface AccountInterface {
declare interface OrbisAccountInterface {
details: object
did: string
result: string
status: number
}
declare interface OrbisPostCreatorDetailsInterface {
a_r: number
did: string
metadata: {
chain: string
}
}
declare interface OrbisPostMentionsInterface {
did: string
username: string
}
interface OrbisPostContentInterface {
body: string
context: string
master: string
mentions: OrbisPostMentionsInterface[]
reply_to: string
type: string
}
interface OrbisCreatorMetadataInterface {
address?: string
chain?: string
ensName?: string
}
interface OrbisCreatorProfileInterface {
description?: string
pfp?: string
pfpIsNft?: {
chain: string
contract: string
timestamp: string
tokenId: string
}
username?: string
}
declare interface OrbisPostInterface {
content: OrbisPostContentInterface
context: string
context_details?: {
channel_details?: {
description: string
group_id: string
name: string
type: string
}
channel_id?: string
group_details?: {
description: string
name: string
pfp: string
}
group_id?: string
}
count_commits: number
count_downvotes: number
count_haha: number
count_likes: number
count_replies: number
creator: string
creator_details?: {
a_r: number
did: string
metadata: OrbisCreatorMetadataInterface
nonces?: object
profile?: OrbisCreatorProfileInterface
}
group_id?: string | null
master?: string | null
reply_to?: string | null
reply_to_creator_details?: {
did: string
metadata: OrbisCreatorMetadataInterface
profile: OrbisCreatorProfileInterface
}
reply_to_details?: OrbisPostContentInterface
stream_id: string
timestamp: number
type: string
}

85
src/@utils/orbis.tsx Normal file
View File

@ -0,0 +1,85 @@
import React, { ReactNode } from 'react'
import Link from 'next/link'
import reactStringReplace from 'react-string-replace'
/** Regex patterns to use */
const patternMentions = /\B@[a-z0-9_.⍙-]+/gi
export function didToAddress(did: string) {
if (!did) return
const _did = did.split(':')
return _did[4]
}
export function formatMessage(content: OrbisPostContentInterface): ReactNode {
if (!content || !content.body) return null
let { body }: { body: any } = content
/** Replace all <br> generated by the postbox to \n to handle line breaks */
body = reactStringReplace(body, '<br>', function (match, i) {
return <br key={match + i} />
})
body = reactStringReplace(body, '\n', function (match, i) {
return <br key={match + i} />
})
/** Replace URLs */
body = reactStringReplace(
body,
/(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/g,
function (match, i) {
const shortUrl =
match.substring(0, 25) +
'...' +
match.substring(match.length - 15, match.length)
return (
<a
key={match + i}
href={match}
rel="noreferrer"
target="_blank"
title={match}
>
{match.length > 40 ? shortUrl : match}
</a>
)
}
)
/** Identify and replace mentions */
/** Get mentions in post metadata */
const { mentions } = content
/** Retrieve mentions in the body */
const mentionsInBody = content.body.toString().match(patternMentions)
/** Compare both and replace in body */
if (mentionsInBody && mentions && Array.isArray(mentions)) {
mentionsInBody.forEach((_m) => {
/** Find mention with the same name */
const mention = mentions.find((obj) => obj.username === _m)
if (mention !== undefined) {
body = reactStringReplace(body, _m, (match, i) =>
mention.did ? (
<Link
href={`/profile/${didToAddress(mention.did)}`}
key={match + i}
>
{mention.username}
</Link>
) : (
<span className="link" key={i}>
{mention.username}
</span>
)
)
}
})
}
return body
}

View File

@ -0,0 +1,18 @@
.posts {
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 0;
}
.loader {
border-top: 1px solid var(--border-color);
padding: calc(var(--spacer) / 2) var(--spacer);
}
.loadMore {
display: flex;
justify-content: center;
padding: calc(var(--spacer) / 2) var(--spacer);
border-top: 1px solid var(--border-color);
}

View File

@ -0,0 +1,64 @@
import React, { useState, useEffect } from 'react'
import { useOrbis } from '@context/Orbis'
import Loader from '@shared/atoms/Loader'
import Button from '@shared/atoms/Button'
import Post from '../Post'
import styles from './Posts.module.css'
export default function Posts({ id }: { id: string }) {
const { orbis } = useOrbis()
const [posts, setPosts] = useState<OrbisPostInterface[]>([])
const [page, setPage] = useState(0)
const [hasMore, setHasMore] = useState(false)
const [loading, setLoading] = useState(false)
const loadPosts = async () => {
setLoading(true)
const context = process.env.NODE_ENV
? 'kjzl6cwe1jw149vvm1f8p9qlohhtkjuc302f22mipq95q7mevdljgx3tv9swujy'
: id
const { data, error } = await orbis.getPosts({ context }, page)
if (error) {
console.log(error)
}
if (data) {
const newPosts = posts.concat(data)
setPosts(newPosts)
const _hasMore = data.length >= 50
setHasMore(_hasMore)
if (_hasMore) setPage((prev) => prev + 1)
}
setLoading(false)
}
useEffect(() => {
if (id) {
loadPosts()
}
}, [id])
return (
<div className={styles.posts}>
<div>
{posts.length > 0 &&
posts.map((post, index) => <Post key={index} post={post} />)}
</div>
{loading && (
<div className={styles.loader}>
<Loader />
</div>
)}
{!loading && hasMore && (
<div className={styles.loadMore}>
<Button style="text" size="small" onClick={loadPosts}>
Load More
</Button>
</div>
)}
</div>
)
}

View File

@ -0,0 +1,37 @@
.comment {
composes: box from '@shared/atoms/Box.module.css';
padding: 0;
margin-top: var(--spacer);
position: relative;
display: flex;
flex-direction: column;
max-height: 620px;
}
.header {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: calc(0.35 * var(--spacer)) var(--spacer);
border-bottom: 1px solid var(--border-color);
font-size: var(--font-size-h5);
font-weight: var(--font-weight-bold);
}
.icon {
margin: 0 1em 0 0;
width: 1.75em;
fill: var(--font-color-text);
}
.postBox {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: calc(0.35 * var(--spacer)) var(--spacer);
border-bottom: 1px solid var(--border-color);
}
.content {
overflow-y: auto;
}

View File

@ -0,0 +1,22 @@
import React from 'react'
import styles from './index.module.css'
import Posts from './Posts'
import Postbox from '../Postbox'
import CommentIcon from '@images/comment.svg'
export default function Comment({ asset }: { asset: AssetExtended }) {
return (
<div className={styles.comment}>
<div className={styles.header}>
<CommentIcon role="img" aria-label="Comment" className={styles.icon} />
<span>Public Comment</span>
</div>
<div className={styles.postBox}>
<Postbox placeholder="Share your comment here..." />
</div>
<div className={styles.content}>
<Posts id={asset?.id} />
</div>
</div>
)
}

View File

@ -1,54 +0,0 @@
.conversation {
position: fixed;
width: 400px;
display: flex;
justify-content: flex-end;
right: calc(var(--spacer) / 3);
bottom: calc(var(--spacer) / 3);
z-index: 20;
}
.toggler {
width: 3.5rem;
height: 3.5rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid var(--border-color);
border-radius: 50%;
margin-left: calc(var(--spacer) / 3);
background-color: var(--background-content);
white-space: nowrap;
}
.toggler svg {
fill: var(--brand-grey-light);
transition: 0.2s ease-out;
width: 1.75em;
}
.toggler:hover svg {
fill: var(--font-color-text);
}
.box {
height: 600px;
max-height: 100%;
width: 350px;
max-width: 100%;
}
@media screen and (min-width: 42rem) {
.conversation {
right: calc(var(--spacer) / 2);
bottom: calc(var(--spacer) / 2);
}
}
@media screen and (min-width: 55rem) {
.conversation {
right: var(--spacer);
bottom: var(--spacer);
}
}

View File

@ -1,27 +0,0 @@
import React, { useState } from 'react'
import Tooltip from '@shared/atoms/Tooltip'
import ChatBubble from '@images/chatbubble.svg'
import styles from './index.module.css'
export default function FloatingChat() {
const [opened, setOpened] = useState(false)
return (
<div className={`${styles.conversation}`}>
<Tooltip
content={
<div className={styles.box}>
<div className={styles.header}>Header</div>
<div className={styles.body}>Messages</div>
<div className={styles.footer}>Postbox</div>
</div>
}
trigger="click focus"
className={styles.toggler}
placement="top-end"
>
<ChatBubble aria-label="Messages" className={styles.icon} />
</Tooltip>
</div>
)
}

View File

@ -0,0 +1,5 @@
import React from 'react'
export default function DmDetails() {
return <div>DmDetails</div>
}

View File

@ -47,6 +47,7 @@
margin-left: 0;
width: 1.5rem;
margin-right: calc(0.5 * var(--spacer));
fill: var(--font-color-text);
}
.toggle {
@ -59,10 +60,28 @@
height: 32px;
}
.toggle .icon {
fill: transparent;
}
.isFlipped {
transform: scaleY(-1);
}
.dmItem {
padding: calc(var(--spacer) / 2);
border-bottom: 1px solid var(--border-color);
}
.dmDetails {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--background-body);
}
@media screen and (min-width: 42rem) {
.wrapper {
padding: 0 calc(var(--spacer) / 2);

View File

@ -5,13 +5,20 @@ import styles from './index.module.css'
export default function FloatingChat() {
const [opened, setOpened] = useState(false)
const [dmDetails, setDmDetails] = useState(null)
const dummyData = [{ name: 'John' }, { name: 'Jane' }]
const openDetails = (name: string) => {
setDmDetails(name)
}
return (
<div className={`${styles.wrapper} ${!opened && styles.isClosed}`}>
<div className={styles.floating}>
<div className={styles.header}>
<ChatBubble role="img" aria-label="Chat" className={styles.icon} />
<span>Messages</span>
<span>Direct Messages</span>
<button
type="button"
className={styles.toggle}
@ -24,6 +31,21 @@ export default function FloatingChat() {
/>
</button>
</div>
<div style={{ position: 'relative' }}>
{dummyData.map((dm, index) => (
<div
key={index}
className={styles.dmItem}
onClick={() => openDetails(dm.name)}
>
{dm.name}
</div>
))}
{dmDetails && (
<div className={styles.dmDetails}>Details {dmDetails}</div>
)}
</div>
</div>
</div>
)

View File

@ -0,0 +1,72 @@
.post {
display: flex;
width: 100%;
padding: calc(var(--spacer) / 2) var(--spacer);
border-bottom: 1px solid var(--border-color);
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word;
}
.post:last-of-type {
border-bottom: 0;
}
.content {
flex-grow: 1;
flex-shrink: 1;
}
.profile {
display: flex;
align-items: center;
gap: calc(var(--spacer) / 4);
margin-bottom: calc(var(--spacer) / 8);
}
.profile > .name {
font-size: var(--font-size-small);
}
.profile > .metadata {
font-size: var(--font-size-mini);
}
.blockies {
aspect-ratio: 1/1;
flex-shrink: 0;
width: calc(var(--font-size-large) * 1.5) !important;
height: calc(var(--font-size-large) * 1.5) !important;
/* border-radius: 50%; */
margin-left: 0;
margin-right: calc(var(--spacer) / 2);
}
.reactions {
display: flex;
justify-content: flex-start;
align-items: center;
gap: calc(var(--spacer) / 2);
margin-top: calc(var(--spacer) / 2);
}
.reactions button {
display: flex;
align-items: center;
background: transparent;
border: 0;
padding: 0;
cursor: pointer;
color: var(--font-color-text);
}
.reactions button:hover,
.reactions button.reacted {
color: var(--brand-pink);
}
.reactions svg {
fill: currentColor;
width: var(--font-size-large);
margin-right: calc(var(--spacer) / 8);
}

View File

@ -0,0 +1,160 @@
import React, { useState, useEffect, ReactNode } from 'react'
import Link from 'next/link'
import { accountTruncate } from '@utils/web3'
import { didToAddress, formatMessage } from '@utils/orbis'
import get3BoxProfile from '@utils/profile'
import Blockies from '@shared/atoms/Blockies'
import Time from '@shared/atoms/Time'
import { useCancelToken } from '@hooks/useCancelToken'
import { useOrbis } from '@context/Orbis'
import styles from './Post.module.css'
import ThumbsUp from '@images/thumbsup.svg'
import ThumbsDown from '@images/thumbsdown.svg'
import Laugh from '@images/laugh.svg'
export default function Post({
post,
showProfile = true
}: {
post: OrbisPostInterface
showProfile?: boolean
}) {
const { orbis, account } = useOrbis()
const [address, setAddress] = useState('')
const [name, setName] = useState('')
const [profile, setProfile] = useState<Profile>()
const [parsedBody, setParsedBody] = useState<ReactNode>()
const [reacted, setReacted] = useState('')
const newCancelToken = useCancelToken()
const reactions = [
// {
// ctx: 'reply',
// countKey: 'count_replies',
// count: post.count_replies,
// title: 'Reply',
// icon:
// },
{
ctx: 'like',
countKey: 'count_likes',
count: post.count_likes,
title: 'Like',
icon: <ThumbsUp role="img" aria-label="Like" />
},
{
ctx: 'downvote',
countKey: 'count_downvotes',
count: post.count_downvotes,
title: 'Downvote',
icon: <ThumbsDown role="img" aria-label="Downvote" />
},
{
ctx: 'haha',
countKey: 'count_haha',
count: post.count_haha,
title: 'HA HA',
icon: <Laugh role="img" aria-label="Downvote" />
}
]
const getUserReaction = async () => {
if (!account?.did || !post?.stream_id) return
const { data, error } = await orbis.api
.from('orbis_reactions')
.select('type')
.eq('post_id', post.stream_id)
.eq('creator', account.did)
if (error) {
console.log(error)
}
if (data) {
if (data.length > 0) {
console.log(data[0].type, post.content.body)
setReacted(data[0].type)
}
}
}
const handleReaction = (type: string) => {
console.log(type)
}
useEffect(() => {
if (!post) return
const address =
post?.creator_details?.metadata?.address ||
didToAddress(post?.creator_details?.did)
setAddress(address)
if (post?.creator_details?.metadata?.ensName) {
setName(post?.creator_details?.metadata?.ensName)
} else if (post?.creator_details?.profile?.username) {
setName(post?.creator_details?.profile?.username)
} else {
setName(accountTruncate(address))
}
async function getProfileData() {
const profile = await get3BoxProfile(address, newCancelToken())
if (!profile) return
setProfile(profile)
}
getProfileData()
setParsedBody(formatMessage(post.content))
getUserReaction()
}, [post])
useEffect(() => {
if (orbis && account) getUserReaction()
}, [orbis, account])
return (
<div className={styles.post}>
<Blockies
accountId={address}
className={styles.blockies}
image={profile?.image}
/>
<div className={styles.content}>
{showProfile && (
<div className={styles.profile}>
<Link href={`/profile/${address}`}>
<a className={styles.name}>{name}</a>
</Link>
<span>&bull;</span>
<div className={styles.metadata}>{accountTruncate(address)}</div>
<span>&bull;</span>
<div className={styles.metadata}>
<Time date={post.timestamp.toString()} isUnix={true} relative />
</div>
</div>
)}
<div className={styles.message}>{parsedBody}</div>
<div className={styles.reactions}>
{reactions.map(({ ctx, count, icon, title }) => (
<button
key={ctx}
onClick={() => handleReaction(ctx)}
title={title}
className={ctx === reacted && styles.reacted}
>
{icon}
<span>{count}</span>
</button>
))}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,20 @@
.postbox {
width: 100%;
}
.postbox .editable {
padding: calc(var(--spacer) / 4);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
min-height: 80px;
margin-bottom: calc(var(--spacer) / 4);
}
.postbox .editable:empty:before {
content: attr(data-placeholder);
color: var(--font-color-text);
}
.postbox .sendButtonWrap {
text-align: right;
}

View File

@ -0,0 +1,34 @@
import React, { useRef } from 'react'
import Button from '@shared/atoms/Button'
import styles from './Postbox.module.css'
export default function Postbox({
placeholder = 'Share your post here...'
}: {
placeholder: string
}) {
const postBoxArea = useRef(null)
// const handleInput = (e: Event): FormEvent<HTMLDivElement> => {
// e.preventDefault()
// console.log(e)
// }
return (
<>
<div className={styles.postbox}>
<div
id="postbox-area"
ref={postBoxArea}
className={styles.editable}
contentEditable={true}
data-placeholder={placeholder}
></div>
<div className={styles.sendButtonWrap}>
<Button style="primary" type="submit" size="small" disabled={false}>
Send
</Button>
</div>
</div>
</>
)
}

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import Alert from '@shared/atoms/Alert'
import Footer from '../Footer/Footer'
import Header from '../Header'
import Conversation from '@shared/Orbis/Conversation'
import FloatingChat from '@shared/Orbis/FloatingChat'
import { useWeb3 } from '@context/Web3'
import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
import AnnouncementBanner from '@shared/AnnouncementBanner'
@ -39,7 +39,7 @@ export default function App({
<main className={styles.main}>{children}</main>
<Footer />
<Conversation />
<FloatingChat />
{appConfig?.privacyPreferenceCenter === 'true' && (
<PrivacyPreferenceCenter style="small" />

View File

@ -4,7 +4,6 @@ import MetaFull from './MetaFull'
import MetaSecondary from './MetaSecondary'
import AssetActions from '../AssetActions'
import { useUserPreferences } from '@context/UserPreferences'
import { useOrbis } from '@context/Orbis'
import Bookmark from './Bookmark'
import { useAsset } from '@context/Asset'
import Alert from '@shared/atoms/Alert'
@ -16,6 +15,7 @@ import NetworkName from '@shared/NetworkName'
import content from '../../../../content/purgatory.json'
import Web3 from 'web3'
import Button from '@shared/atoms/Button'
import Comment from '../../@shared/Orbis/Comment'
export default function AssetContent({
asset
@ -23,7 +23,6 @@ export default function AssetContent({
asset: AssetExtended
}): ReactElement {
const { isInPurgatory, purgatoryData, isOwner, isAssetNetwork } = useAsset()
const { account, connectOrbis } = useOrbis()
const { debug } = useUserPreferences()
const [receipts, setReceipts] = useState([])
const [nftPublisher, setNftPublisher] = useState<string>()
@ -36,10 +35,6 @@ export default function AssetContent({
)
}, [receipts])
useEffect(() => {
console.log(account)
}, [account])
return (
<>
<div className={styles.networkWrap}>
@ -74,9 +69,7 @@ export default function AssetContent({
{debug === true && <DebugOutput title="DDO" output={asset} />}
</div>
<div>
<button onClick={() => connectOrbis()}>Connect Orbis</button>
</div>
<Comment asset={asset} />
</div>
<div className={styles.actions}>