Skip to content

Commit

Permalink
Add chat link and unread count
Browse files Browse the repository at this point in the history
  • Loading branch information
samchuk-vlad committed Mar 29, 2024
1 parent cdbc800 commit c976850
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 74 deletions.
32 changes: 6 additions & 26 deletions src/components/activity/AccountActivity.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PostWithSomeDetails, SpaceData } from '@subsocial/api/types'
import { Button, Tabs } from 'antd'
import { Tabs } from 'antd'
import clsx from 'clsx'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
Expand All @@ -12,19 +12,14 @@ import {
// useGetFollowActivities,
useGetPostActivities,
} from 'src/graphql/hooks'
import {
useFetchPosts,
useSelectPost,
useSelectSpace,
useSetChatEntityConfig,
useSetChatOpen,
} from 'src/rtk/app/hooks'
import { useFetchPosts, useSelectPost, useSelectSpace } from 'src/rtk/app/hooks'
import { useIsMyAddress } from '../auth/MyAccountsContext'
import WriteSomething from '../posts/WriteSomething'
// import { PostPreviewsOnSpace } from '../spaces/helpers'
import { Loading } from '../utils'
import { createLoadMorePosts, FeedActivities } from './FeedActivities'
// import { createLoadMoreActivities, NotifActivities } from './Notifications'
import ChatLinkButtonWithCounter from '../chat/ChatLinkButtonWithCounter'
import { OnchainAccountActivity } from './OnchainAccountActivity'
import { SpaceActivities } from './SpaceActivities'
import styles from './style.module.sass'
Expand Down Expand Up @@ -105,6 +100,7 @@ const CommentActivities = (props: BaseActivityProps) => {
const PostActivities = (props: BaseActivityProps) => {
const getPostActivities = useGetPostActivities()
const loadMorePosts = createLoadMorePosts(getPostActivities)

return (
<FeedActivities
{...props}
Expand Down Expand Up @@ -162,15 +158,11 @@ const OffchainAccountActivity = ({
const router = useRouter()
const [activeTab, setActiveTab] = useState<ActivityTab>(initialActiveTab)
const [counts, setCounts] = useState<ActivityCounts>()
const setChatConfig = useSetChatEntityConfig()
const setChatOpen = useSetChatOpen()

const space = useSelectSpace(spaceId)

const chatId = (space?.content?.chats as any[])?.[0]?.id as string | undefined

console.log('ChatId', chatId, space?.content)

useFetchPosts(chatId ? [chatId] : [])

const post = useSelectPost(chatId)
Expand Down Expand Up @@ -213,20 +205,8 @@ const OffchainAccountActivity = ({
renderTabBar={(props, DefaultTabBar) => {
return (
<div className={styles.TabsBlock}>
{chatId && post && (
<Button
onClick={() => {
setChatConfig({
entity: { type: 'post', data: post.post },
withFloatingButton: false,
})
setChatOpen(true)
}}
type='link'
className={styles.ChatButton}
>
Chat
</Button>
{chatId && post && !post.post.struct.hidden && (
<ChatLinkButtonWithCounter post={post.post} />
)}
<DefaultTabBar {...props} />
</div>
Expand Down
14 changes: 2 additions & 12 deletions src/components/activity/style.module.sass
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,8 @@
:global(.ant-tabs-nav)
margin: 0 !important

:global(.ant-tabs-nav)
&::before
border-bottom: none !important

.ChatButton
position: relative
display: inline-flex
align-items: center
margin: 0 32px 0 0
padding: 12px 0
font-size: 14px
background: transparent
border: 0
outline: none
cursor: pointer
:global(.ant-tabs-tab)
margin: 0 19px 0 0 !important
31 changes: 31 additions & 0 deletions src/components/chat/ChatButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { isEmptyArray } from '@subsocial/utils'
import { useSelectPost, useSelectSpace } from 'src/rtk/app/hooks'
import CreateChatModalButton from './CreateChatModal'
import UnhideChatButton from './UnhideChatButton'

type ChatButtonProps = {
spaceId?: string
}

const ChatButton = ({ spaceId }: ChatButtonProps) => {
const space = useSelectSpace(spaceId)

const spaceContent = space?.content

const chats = spaceContent?.chats
const chat = chats?.[0]

const post = useSelectPost(chat.id)

const isPostHidden = !!post?.post.struct.hidden

if (spaceContent && !isEmptyArray(chats) && !isPostHidden) return null

return isPostHidden ? (
<UnhideChatButton post={post?.post?.struct} />
) : (
<CreateChatModalButton spaceId={spaceId} />
)
}

export default ChatButton
2 changes: 1 addition & 1 deletion src/components/chat/ChatFloatingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function ChatFloatingModal() {
}

type Entity = NonNullable<ChatEntity['entity']>
function getUnreadCount(entity: Entity) {
export function getUnreadCount(entity: Entity) {
return parseInt(localStorage.getItem(`unreadCount:${entity.type}:${entity.data.id}`) ?? '') ?? 0
}

Expand Down
37 changes: 36 additions & 1 deletion src/components/chat/ChatIframe.module.sass
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
@import 'src/styles/subsocial-vars.scss'

.ChatIframe
transition: opacity 150ms ease-out
opacity: 1

&.ChatIframeLoading
opacity: 0
opacity: 0

.ButtonWrapper
display: flex
align-items: center
gap: $space_tiny
align-items: center
margin: 0 19px 0 0
padding: 12px 0

.ChatButton
height: auto
position: relative
display: inline-flex
font-size: 14px
background: transparent
align-items: center
padding: 0
border: 0
outline: none
cursor: pointer

.ChatUnreadCount
font-size: 10px
padding: 2px 3px
display: flex
align-items: center
justify-content: center
height: 14px
min-width: 14px
font-weight: 500
background: #EB2F95
border-radius: 32px
color: white
51 changes: 51 additions & 0 deletions src/components/chat/ChatLinkButtonWithCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { PostData } from '@subsocial/api/types'
import { Button } from 'antd'
import clsx from 'clsx'
import { useEffect, useState } from 'react'
import { useSetChatEntityConfig, useSetChatOpen } from 'src/rtk/app/hooks'
import { useAppSelector } from 'src/rtk/app/store'
import { getUnreadCount } from './ChatFloatingModal'
import styles from './ChatIframe.module.sass'

type ChatLinkButtonWithCounterProps = {
post: PostData
}

const ChatLinkButtonWithCounter = ({ post }: ChatLinkButtonWithCounterProps) => {
const setChatConfig = useSetChatEntityConfig()
const setChatOpen = useSetChatOpen()

const entity = useAppSelector(state => state.chat.entity)

const [unreadCount, setUnreadCount] = useState(0)

useEffect(() => {
if (!entity) return

const unreadCountFromStorage = getUnreadCount(entity)
if (unreadCountFromStorage && !isNaN(unreadCountFromStorage)) {
setUnreadCount(unreadCountFromStorage)
}
}, [entity])

return (
<span className={styles.ButtonWrapper}>
<Button
onClick={() => {
setChatConfig({
entity: { type: 'post', data: post },
withFloatingButton: false,
})
setChatOpen(true)
}}
type='link'
className={styles.ChatButton}
>
Chat
</Button>
{!!unreadCount && <div className={clsx(styles.ChatUnreadCount)}>{unreadCount}</div>}
</span>
)
}

export default ChatLinkButtonWithCounter
9 changes: 0 additions & 9 deletions src/components/chat/CreateChatModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Button } from 'antd'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
// import { useSelectSpace } from 'src/rtk/app/hooks'
import { getCurrentUrlOrigin } from 'src/utils/url'

function parseMessage(data: string) {
Expand All @@ -26,12 +25,6 @@ const CreateChatModalButton = ({ size }: CreateChatModalButtonProps) => {
const [openModal, setOpenModal] = useState(false)
const router = useRouter()

// const space = useSelectSpace(spaceId)

// const spaceContent = space?.content

// const chats = spaceContent?.chats

useEffect(() => {
window.onmessage = event => {
const message = parseMessage(event.data + '')
Expand All @@ -53,8 +46,6 @@ const CreateChatModalButton = ({ size }: CreateChatModalButtonProps) => {
}
}, [])

// if (spaceContent && !isEmptyArray(chats)) return null

return (
<span
onClick={() => {
Expand Down
46 changes: 46 additions & 0 deletions src/components/chat/UnhideChatButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { PostUpdate } from '@subsocial/api/substrate/wrappers'
import { PostStruct } from '@subsocial/api/types'
import { useAppDispatch } from 'src/rtk/app/store'
import { fetchPost } from 'src/rtk/features/posts/postsSlice'
import { DataSourceTypes } from 'src/types'
import { useSubsocialApi } from '../substrate'
import HiddenButton from '../utils/HiddenButton'

type UnhideChatButtonProps = {
post?: PostStruct
}

const UnhideChatButton = ({ post }: UnhideChatButtonProps) => {
const dispatch = useAppDispatch()
const { subsocial } = useSubsocialApi()

if (!post) return null

const newTxParams = () => {
const update = PostUpdate({
hidden: false,
})
return [post.id, update]
}

const onTxSuccess = () => {
dispatch(
fetchPost({ api: subsocial, id: post.id, dataSource: DataSourceTypes.CHAIN, reload: true }),
)
}

return (
<HiddenButton
struct={post}
newTxParams={newTxParams}
buttonType='primary'
size={'middle'}
ghost
onTxSuccess={onTxSuccess}
type='post'
label='Unhide chat'
/>
)
}

export default UnhideChatButton
38 changes: 16 additions & 22 deletions src/components/spaces/ViewSpace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EditOutlined } from '@ant-design/icons'
import { isEmptyStr, newLogger, nonEmptyStr } from '@subsocial/utils'
import clsx from 'clsx'
import dynamic from 'next/dynamic'
import React, { MouseEvent, useCallback, useMemo, useState } from 'react'
import React, { MouseEvent, useCallback, useState } from 'react'
import { ButtonLink } from 'src/components/utils/CustomLinks'
import { Segment } from 'src/components/utils/Segment'
import { LARGE_AVATAR_SIZE } from 'src/config/Size.config'
Expand All @@ -17,6 +17,7 @@ import { useSelectProfileSpace } from '../../rtk/features/profiles/profilesHooks
import { useSelectSpace } from '../../rtk/features/spaces/spacesHooks'
import { AccountActivity } from '../activity/AccountActivity'
import { useMyAddress } from '../auth/MyAccountsContext'
import ChatButton from '../chat/ChatButton'
import MobileActiveStakingSection from '../creators/MobileActiveStakingSection'
import WriteSomething from '../posts/WriteSomething'
import MakeAsProfileModal from '../profiles/address-views/utils/MakeAsProfileModal'
Expand Down Expand Up @@ -49,7 +50,6 @@ import { ViewSpaceOptsProps, ViewSpaceProps } from './ViewSpaceProps'
const log = newLogger('ViewSpace')

const FollowSpaceButton = dynamic(() => import('../utils/FollowSpaceButton'), { ssr: false })
const CreateChatModalButton = dynamic(() => import('../chat/CreateChatModal'), { ssr: false })

type Props = ViewSpaceProps

Expand Down Expand Up @@ -136,28 +136,23 @@ export const InnerViewSpace = (props: Props) => {
)
}, [spaceData, imageSize])

// const setChatConfig = useSetChatEntityConfig()
// const setChatOpen = useSetChatOpen()

// const { isCreatorSpace } = useIsCreatorSpace(spaceData?.id)

const isMySpace = useIsMySpace(spaceData?.struct)
const { filteredPostIds, filteredPosts } = useMemo(() => {
if (isMySpace) return { filteredPosts: posts, filteredPostIds: postIds }
// const { filteredPostIds, filteredPosts } = useMemo(() => {
// if (isMySpace) return { filteredPosts: posts, filteredPostIds: postIds }

const hiddenPosts = new Set()
// const hiddenPosts = new Set()

const filteredPosts = posts.filter(post => {
if (isHidden(post.post.struct)) {
hiddenPosts.add(post.post.id)
return false
}
return true
})
// const filteredPosts = posts.filter(post => {
// if (isHidden(post.post.struct)) {
// hiddenPosts.add(post.post.id)
// return false
// }
// return true
// })

const filteredPostIds = postIds.filter(id => !hiddenPosts.has(id))
return { filteredPosts, filteredPostIds }
}, [posts, postIds])
// const filteredPostIds = postIds.filter(id => !hiddenPosts.has(id))
// return { filteredPosts, filteredPostIds }
// }, [posts, postIds])

// We do not return 404 page here, because this component could be used to render a space in list.
if (!spaceData) return null
Expand All @@ -167,7 +162,7 @@ export const InnerViewSpace = (props: Props) => {
const contactInfo = { email, links }
const spaceName = renderSpaceName(spaceData)

const chatButton = <CreateChatModalButton spaceId={spaceId} />
const chatButton = <ChatButton spaceId={spaceId} />

const primaryClass = `ProfileDetails ${isMy && 'MySpace'} d-flex`

Expand Down Expand Up @@ -365,7 +360,6 @@ export const InnerViewSpace = (props: Props) => {
<Section className='DfContentPage mt-4'>
{isProfileSpace ? (
<AccountActivity
withSpacePosts={{ spaceData, postIds: filteredPostIds, posts: filteredPosts }}
withWriteSomethingBlock={false}
address={spaceData.struct.ownerId}
spaceId={spaceId}
Expand Down
Loading

0 comments on commit c976850

Please sign in to comment.