mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
useLocalStorage to store notifications
This commit is contained in:
parent
100af9d2b0
commit
ac48cb1e39
@ -25,7 +25,7 @@ type IOrbisProvider = {
|
||||
conversationId: string
|
||||
conversations: IOrbisConversation[]
|
||||
conversationTitle: string
|
||||
unreadMessages: IOrbisNotification[]
|
||||
notifications: Record<string, string[]>
|
||||
connectOrbis: (options: {
|
||||
address: string
|
||||
lit?: boolean
|
||||
@ -44,6 +44,8 @@ type IOrbisProvider = {
|
||||
setOpenConversations: (value: boolean) => void
|
||||
setConversationId: (value: string) => void
|
||||
createConversation: (value: string) => Promise<void>
|
||||
clearMessageNotifs: (conversationId: string) => void
|
||||
getConversationTitle: (conversation: IOrbisConversation) => string
|
||||
getDid: (value: string) => Promise<string>
|
||||
}
|
||||
|
||||
@ -56,26 +58,27 @@ const CONVERSATION_CONTEXT = 'ocean_market' // Can be changed to whatever
|
||||
function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
const { web3Provider, accountId } = useWeb3()
|
||||
const prevAccountId = usePrevious(accountId)
|
||||
|
||||
const [ceramicSessions, setCeramicSessions] = useLocalStorage<string[]>(
|
||||
'ocean-ceramic-sessions',
|
||||
[]
|
||||
)
|
||||
const [usersNotifications, setUsersNotifications] = useLocalStorage<
|
||||
Record<string, Record<string, string[]>>
|
||||
>('ocean-convo-notifs', {})
|
||||
|
||||
const [account, setAccount] = useState<IOrbisProfile | null>(null)
|
||||
const [hasLit, setHasLit] = useState(false)
|
||||
const [openConversations, setOpenConversations] = useState(false)
|
||||
const [conversationId, setConversationId] = useState(null)
|
||||
const [conversations, setConversations] = useState<IOrbisConversation[]>([])
|
||||
const [unreadMessages, setUnreadMessages] = useState<IOrbisNotification[]>([])
|
||||
|
||||
// Function to reset states
|
||||
const resetStates = () => {
|
||||
setAccount(null)
|
||||
setConversationId(null)
|
||||
setConversations([])
|
||||
setUnreadMessages([])
|
||||
setHasLit(false)
|
||||
window.localStorage.removeItem('lit-auth-signature')
|
||||
window.localStorage.removeItem('lit-auth-sol-signature')
|
||||
}
|
||||
|
||||
// Remove ceramic session
|
||||
@ -85,6 +88,12 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
setCeramicSessions({ ..._ceramicSessions })
|
||||
}
|
||||
|
||||
// Remove lit signature
|
||||
const removeLitSignature = () => {
|
||||
window.localStorage.removeItem('lit-auth-signature')
|
||||
window.localStorage.removeItem('lit-auth-sol-signature')
|
||||
}
|
||||
|
||||
// Connecting to Orbis
|
||||
const connectOrbis = async ({
|
||||
address,
|
||||
@ -117,7 +126,9 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
const disconnectOrbis = (address: string) => {
|
||||
const res = orbis.logout()
|
||||
if (res.status === 200) {
|
||||
console.log('disconnected')
|
||||
resetStates()
|
||||
removeLitSignature()
|
||||
removeCeramicSession(address)
|
||||
}
|
||||
}
|
||||
@ -143,7 +154,6 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
res.status === 200 &&
|
||||
didToAddress(res.did) === accountId.toLowerCase()
|
||||
) {
|
||||
console.log(res)
|
||||
setHasLit(res.details.hasLit)
|
||||
const { data } = await orbis.getProfile(res.did)
|
||||
setAccount(data)
|
||||
@ -152,7 +162,9 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
const data = await connectOrbis({ address, lit })
|
||||
return data
|
||||
} else {
|
||||
console.log('not connected')
|
||||
resetStates()
|
||||
removeLitSignature()
|
||||
removeCeramicSession(address)
|
||||
return null
|
||||
}
|
||||
@ -264,58 +276,100 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
did = await getDid(accountId)
|
||||
}
|
||||
|
||||
const address = didToAddress(did)
|
||||
|
||||
const { data, error } = await orbis.api.rpc('orbis_f_notifications', {
|
||||
user_did: did || 'none',
|
||||
notif_type: 'messages'
|
||||
})
|
||||
|
||||
if (error) {
|
||||
setUnreadMessages([])
|
||||
} else if (data.length > 0) {
|
||||
// Check if did is mainnet
|
||||
const chainId = parseInt(did.split(':')[3])
|
||||
if (chainId === 0) {
|
||||
setUnreadMessages(data)
|
||||
} else {
|
||||
const _unreads = data.filter((o: IOrbisNotification) => {
|
||||
return o.status === 'new'
|
||||
})
|
||||
setUnreadMessages(_unreads)
|
||||
if (!error && data.length > 0) {
|
||||
const _usersNotifications = { ...usersNotifications }
|
||||
const _unreads = data.filter((o: IOrbisNotification) => {
|
||||
return o.status === 'new'
|
||||
})
|
||||
_unreads.forEach((o: IOrbisNotification) => {
|
||||
const conversationId = o.content.conversation_id
|
||||
const { encryptedString } = o.content.encryptedMessage
|
||||
|
||||
// Add address if not exists
|
||||
if (!_usersNotifications[address]) _usersNotifications[address] = {}
|
||||
|
||||
// Add conversationId if not exists
|
||||
if (!_usersNotifications[address][conversationId])
|
||||
_usersNotifications[address][conversationId] = []
|
||||
|
||||
// Add encryptedString if not exists
|
||||
if (
|
||||
!_usersNotifications[address][conversationId].includes(
|
||||
encryptedString
|
||||
)
|
||||
) {
|
||||
_usersNotifications[address][conversationId].push(encryptedString)
|
||||
}
|
||||
})
|
||||
setUsersNotifications(_usersNotifications)
|
||||
|
||||
if (_unreads.length > 0) {
|
||||
// Get unix timestamp in seconds
|
||||
const timestamp = Math.floor(Date.now() / 1000)
|
||||
// Set read time
|
||||
await orbis.setNotificationsReadTime('messages', timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const clearMessageNotifs = (conversationId: string) => {
|
||||
console.log('clearMessageNotifs', conversationId)
|
||||
const _usersNotifications = { ...usersNotifications }
|
||||
const address = didToAddress(account?.did)
|
||||
if (_usersNotifications[address]) {
|
||||
delete _usersNotifications[address][conversationId]
|
||||
}
|
||||
setUsersNotifications(_usersNotifications)
|
||||
}
|
||||
|
||||
const getConversationTitle = (conversation: IOrbisConversation) => {
|
||||
let title = null
|
||||
|
||||
if (conversation) {
|
||||
const details = conversation.recipients_details.find(
|
||||
(o: IOrbisProfile) => o.did !== account.did
|
||||
)
|
||||
const did = conversation.recipients.find((o: string) => o !== account.did)
|
||||
|
||||
const address = didToAddress(did)
|
||||
|
||||
if (details) {
|
||||
title = details?.metadata?.ensName || accountTruncate(address)
|
||||
} else {
|
||||
title = accountTruncate(address)
|
||||
}
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
const conversationTitle = useMemo(() => {
|
||||
let title = null
|
||||
if (conversationId && conversations.length) {
|
||||
const conversation = conversations.find(
|
||||
(o) => o.stream_id === conversationId
|
||||
)
|
||||
if (conversation) {
|
||||
const details = conversation.recipients_details.find(
|
||||
(o: IOrbisProfile) => o.did !== account.did
|
||||
)
|
||||
const did = conversation.recipients.find(
|
||||
(o: string) => o !== account.did
|
||||
)
|
||||
|
||||
const address = didToAddress(did)
|
||||
|
||||
console.log({ details, address })
|
||||
|
||||
if (details) {
|
||||
title =
|
||||
details?.metadata?.ensName ||
|
||||
details?.profile?.username ||
|
||||
accountTruncate(address)
|
||||
} else {
|
||||
title = accountTruncate(address)
|
||||
}
|
||||
}
|
||||
title = getConversationTitle(conversation)
|
||||
}
|
||||
|
||||
return title
|
||||
}, [conversationId, account, conversations])
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [conversationId, conversations])
|
||||
|
||||
const notifications = useMemo(() => {
|
||||
let _notifications = {}
|
||||
if (accountId && accountId.toLowerCase() in usersNotifications) {
|
||||
_notifications = usersNotifications[accountId.toLowerCase()]
|
||||
}
|
||||
return _notifications
|
||||
}, [accountId, usersNotifications])
|
||||
|
||||
useInterval(async () => {
|
||||
await getMessageNotifs()
|
||||
@ -335,6 +389,7 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
getConversations(account?.did)
|
||||
getMessageNotifs()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [account])
|
||||
@ -349,7 +404,7 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
conversationId,
|
||||
conversations,
|
||||
conversationTitle,
|
||||
unreadMessages,
|
||||
notifications,
|
||||
connectOrbis,
|
||||
disconnectOrbis,
|
||||
checkOrbisConnection,
|
||||
@ -357,6 +412,8 @@ function OrbisProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
setOpenConversations,
|
||||
setConversationId,
|
||||
createConversation,
|
||||
clearMessageNotifs,
|
||||
getConversationTitle,
|
||||
getDid
|
||||
}}
|
||||
>
|
||||
|
@ -10,7 +10,14 @@ import Postbox from './Postbox'
|
||||
import styles from './Conversation.module.css'
|
||||
|
||||
export default function DmConversation() {
|
||||
const { orbis, account, conversationId, hasLit, connectLit } = useOrbis()
|
||||
const {
|
||||
orbis,
|
||||
account,
|
||||
conversationId,
|
||||
hasLit,
|
||||
connectLit,
|
||||
clearMessageNotifs
|
||||
} = useOrbis()
|
||||
const isMounted = useIsMounted()
|
||||
|
||||
const messagesWrapper = useRef(null)
|
||||
@ -47,7 +54,10 @@ export default function DmConversation() {
|
||||
setHasMore(data.length >= 50)
|
||||
const _messages = [...data, ...messages]
|
||||
setMessages(_messages)
|
||||
if (currentPage === 0) scrollToBottom()
|
||||
if (currentPage === 0) {
|
||||
clearMessageNotifs(conversationId)
|
||||
scrollToBottom()
|
||||
}
|
||||
setCurrentPage((prev) => prev + 1)
|
||||
} else {
|
||||
const unique = data.filter(
|
||||
@ -97,6 +107,7 @@ export default function DmConversation() {
|
||||
Math.ceil(el.scrollTop) >= Math.floor(el.scrollHeight - el.offsetHeight)
|
||||
) {
|
||||
setNewMessages(0)
|
||||
clearMessageNotifs(conversationId)
|
||||
}
|
||||
|
||||
// Remove scroll listener
|
||||
|
@ -11,6 +11,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.header > * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.5rem;
|
||||
fill: var(--font-color-text);
|
||||
@ -28,6 +32,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.btnBack:hover {
|
||||
|
@ -10,14 +10,17 @@ export default function Header() {
|
||||
conversationId,
|
||||
openConversations,
|
||||
conversationTitle,
|
||||
unreadMessages,
|
||||
notifications,
|
||||
setOpenConversations,
|
||||
setConversationId
|
||||
} = useOrbis()
|
||||
|
||||
const handleToggle = (e: any) => {
|
||||
const handleToggle = (
|
||||
e: React.MouseEvent<HTMLDivElement | HTMLButtonElement>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
if (e.target.type === 'button') {
|
||||
const target = e.target as HTMLElement
|
||||
if (target.tagName === 'BUTTON') {
|
||||
setConversationId(null)
|
||||
} else {
|
||||
setOpenConversations(!openConversations)
|
||||
@ -32,9 +35,9 @@ export default function Header() {
|
||||
<ChatBubble role="img" aria-label="Chat" className={styles.icon} />
|
||||
</div>
|
||||
<span>Direct Messages</span>
|
||||
{unreadMessages.length > 0 && (
|
||||
{Object.values(notifications).flat().length > 0 && (
|
||||
<span className={styles.notificationCount}>
|
||||
{unreadMessages.length}
|
||||
{Object.values(notifications).flat().length}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
|
@ -5,13 +5,10 @@ import ChatBubble from '@images/chatbubble.svg'
|
||||
import styles from './List.module.css'
|
||||
|
||||
export default function List() {
|
||||
const { conversations, unreadMessages, setConversationId } = useOrbis()
|
||||
const { conversations, notifications, setConversationId } = useOrbis()
|
||||
|
||||
const getConversationUnreads = (conversationId: string) => {
|
||||
const _unreads = unreadMessages.filter(
|
||||
(o) => o.content.conversation_id === conversationId
|
||||
)
|
||||
return _unreads.length
|
||||
return notifications[conversationId]?.length || 0
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { useOrbis } from '@context/Orbis'
|
||||
import { accountTruncate } from '@utils/web3'
|
||||
import { didToAddress } from '@utils/orbis'
|
||||
import Avatar from '@shared/atoms/Avatar'
|
||||
import Time from '@shared/atoms/Time'
|
||||
@ -16,34 +15,19 @@ export default function ConversationItem({
|
||||
unreads: number
|
||||
setConversationId: (value: string) => void
|
||||
}) {
|
||||
const { account } = useOrbis()
|
||||
const { account, getConversationTitle } = useOrbis()
|
||||
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
const [name, setName] = useState(null)
|
||||
const name = getConversationTitle(conversation)
|
||||
const [address, setAddress] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
const getProfile = async () => {
|
||||
const details = conversation.recipients_details.find(
|
||||
(o) => o.did !== account.did
|
||||
)
|
||||
const did = conversation.recipients.find((o) => o !== account.did)
|
||||
|
||||
const _address = didToAddress(did)
|
||||
setAddress(_address)
|
||||
|
||||
if (details) {
|
||||
if (details?.metadata?.ensName) {
|
||||
setName(details?.metadata?.ensName)
|
||||
} else if (details?.profile?.username) {
|
||||
setName(details?.profile?.username)
|
||||
} else {
|
||||
setName(accountTruncate(_address))
|
||||
}
|
||||
} else {
|
||||
setName(accountTruncate(_address))
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation && account) {
|
||||
|
Loading…
Reference in New Issue
Block a user