1
0
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:
marcoelissa 2022-12-15 05:46:42 +07:00
parent 100af9d2b0
commit ac48cb1e39
6 changed files with 128 additions and 71 deletions

View File

@ -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
}}
>

View File

@ -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

View File

@ -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 {

View File

@ -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>
)}
</>

View File

@ -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 (

View File

@ -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) {