Skip to content

Commit

Permalink
Update Telegram Notification (#340)
Browse files Browse the repository at this point in the history
* Fix message sharing not working

* Fix disconnect notice showing at wrong time

* Add notification recipient

* Integrate targetAcc

* Add tooltip for notification recipient

* Adjust paddings

* Add event for opening tg message modal

* Add message modal stories
  • Loading branch information
teodorus-nathaniel authored Aug 16, 2023
1 parent ecedb19 commit 5bcf758
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 58 deletions.
31 changes: 18 additions & 13 deletions src/components/ProfilePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export type ProfilePreviewProps = ComponentProps<'div'> & {
address: string
className?: string
avatarClassName?: string
addressesContainerClassName?: string
showMaxOneAddress?: boolean
withGrillAddress?: boolean
withEvmAddress?: boolean
nameClassName?: string
Expand All @@ -23,6 +25,8 @@ const ProfilePreview = ({
className,
avatarClassName,
nameClassName,
addressesContainerClassName,
showMaxOneAddress = false,
withGrillAddress = true,
withEvmAddress = true,
...props
Expand All @@ -42,25 +46,26 @@ const ProfilePreview = ({
address={address}
className={cx('h-20 w-20', avatarClassName)}
/>
<div className='flex flex-col'>
<div className={cx('flex flex-col gap-2', addressesContainerClassName)}>
<Name
address={address}
showEthIcon={false}
className={cx('text-lg leading-none', nameClassName)}
/>
{showingAnyAddress && (
<div className='mt-3 flex flex-col gap-1'>
{withGrillAddress && (
<div className='flex flex-row items-center gap-2'>
<GrillIcon />
<CopyTextInline
text={truncateAddress(address)}
tooltip={`Copy${isMyAddressPart} Grill public address`}
textToCopy={address}
textClassName='font-mono leading-none text-[15px] leading-[14px]'
/>
</div>
)}
<div className='flex flex-col gap-1'>
{withGrillAddress &&
(!isShowingEvmAddress || !showMaxOneAddress) && (
<div className='flex flex-row items-center gap-2'>
<GrillIcon />
<CopyTextInline
text={truncateAddress(address)}
tooltip={`Copy${isMyAddressPart} Grill public address`}
textToCopy={address}
textClassName='font-mono leading-none text-[15px] leading-[14px]'
/>
</div>
)}
{isShowingEvmAddress && (
<div className='flex flex-row items-center gap-2'>
<EthIcon />
Expand Down
14 changes: 9 additions & 5 deletions src/components/auth/LoginModal/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ const CaptchaInvisible = dynamic(
)

export type LoginModalProps = ModalFunctionalityProps & {
initialOpenState?: LoginModalStep
onBackClick?: () => void
afterLogin?: () => void
beforeLogin?: () => void
openModal: () => void
}

type ModalTitle = {
Expand Down Expand Up @@ -69,15 +70,18 @@ const modalHeader: ModalTitle = {
export default function LoginModal({
afterLogin,
beforeLogin,
initialOpenState = 'login',
onBackClick,
...props
}: LoginModalProps) {
const inputRef = useRef<HTMLTextAreaElement>(null)
const [currentStep, setCurrentStep] = useState<LoginModalStep>('login')
const [currentStep, setCurrentStep] =
useState<LoginModalStep>(initialOpenState)

const onBackClick = () => setCurrentStep('login')
const usedOnBackClick = onBackClick || (() => setCurrentStep('login'))

useEffect(() => {
if (props.isOpen) setCurrentStep('login')
if (props.isOpen) setCurrentStep(initialOpenState)
}, [props.isOpen])

const ModalContent = loginModalContents[currentStep]
Expand All @@ -91,7 +95,7 @@ export default function LoginModal({
title={title}
withCloseButton
description={desc}
onBackClick={withBackButton ? onBackClick : undefined}
onBackClick={withBackButton ? usedOnBackClick : undefined}
closeModal={() => {
props.closeModal()
}}
Expand Down
1 change: 0 additions & 1 deletion src/components/auth/LoginModal/LoginModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export type LoginModalStep =
type ContentProps = ModalFunctionalityProps & {
setCurrentStep: Dispatch<SetStateAction<LoginModalStep>>
currentStep: LoginModalStep
openModal: () => void
runCaptcha: () => Promise<string | null>
termsAndService: (className?: string) => JSX.Element
afterLogin?: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function TelegramNotificationContent(props: ContentProps) {
if (!isLoadingAccount && !firstLinkedAccount) {
return (
<>
{!isAfterDisconnect && (
{isAfterDisconnect && (
<Notice className='mb-6' leftIcon='✅'>
You have disconnected your account from Grill&apos;s telegram bot.
</Notice>
Expand Down
1 change: 0 additions & 1 deletion src/components/chats/ChatItem/ChatItemMenus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ export default function ChatItemMenus({
)}
<LoginModal
isOpen={modalState === 'login'}
openModal={() => setModalState('login')}
closeModal={() => setModalState(null)}
beforeLogin={() => (isLoggingInWithKey.current = true)}
afterLogin={() => (isLoggingInWithKey.current = false)}
Expand Down
4 changes: 4 additions & 0 deletions src/components/chats/ChatList/ChatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function ChatListContent({

const [initialNewMessageCount, setInitialNewMessageCount] = useState(0)
const lastReadId = useFocusedLastMessageId(chatId)
const [recipient, setRecipient] = useState('')
const [messageModalMsgId, setMessageModalMsgId] = useState('')
const prevMessageModalMsgId = usePrevious(messageModalMsgId)

Expand Down Expand Up @@ -136,6 +137,7 @@ function ChatListContent({
hasScrolledToMessageRef.current = true

const messageId = getUrlQuery('messageId')
const recipient = getUrlQuery('targetAcc')
const isMessageIdsFetched = rawMessageIds !== undefined

if (!isMessageIdsFetched) return
Expand All @@ -160,6 +162,7 @@ function ChatListContent({
}

setMessageModalMsgId(messageId)
setRecipient(recipient)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rawMessageIds, filteredMessageIdsRef, hasScrolledToMessageRef])

Expand Down Expand Up @@ -298,6 +301,7 @@ function ChatListContent({
closeModal={() => setMessageModalMsgId('')}
messageId={messageModalMsgId}
scrollToMessage={scrollToMessage}
recipient={recipient}
/>
<Component>
<div className='relative'>
Expand Down
50 changes: 50 additions & 0 deletions src/components/modals/MessageModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Meta, StoryObj } from '@storybook/react'
import { useState } from 'react'
import Button from '../Button'
import MessageModal, { MessageModalProps } from './MessageModal'

function MessageModalWrapper(
props: Omit<MessageModalProps, 'isOpen' | 'closeModal'>
) {
const [isOpen, setIsOpen] = useState(false)

return (
<>
<Button onClick={() => setIsOpen(true)}>Open Modal</Button>
<MessageModal
{...props}
isOpen={isOpen}
closeModal={() => setIsOpen(false)}
/>
</>
)
}

const meta = {
title: 'Components/MessageModal',
component: MessageModalWrapper,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
args: {
hubId: '1001',
messageId: '7687',
scrollToMessage: async (messageId) => {
alert('Scroll to message: ' + messageId)
},
},
} satisfies Meta<typeof MessageModalWrapper>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {},
}

export const WithTargetAccount: Story = {
args: {
recipient: '3szaLms2V18XCr2ztvjzcPF16BxvbGeAW4fEG2yeb19XGdGV',
},
}
134 changes: 99 additions & 35 deletions src/components/modals/MessageModal.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
import { getPostQuery } from '@/services/api/query'
import { useSendEvent } from '@/stores/analytics'
import { useMyAccount } from '@/stores/my-account'
import { cx } from '@/utils/class-names'
import { CommentData } from '@subsocial/api/types'
import { useRef, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { HiOutlineInformationCircle } from 'react-icons/hi2'
import LoginModal from '../auth/LoginModal'
import Button from '../Button'
import Card from '../Card'
import ChatItem from '../chats/ChatItem'
import PopOver from '../floating/PopOver'
import ProfilePreview from '../ProfilePreview'
import Modal, { ModalFunctionalityProps } from './Modal'

export type MessageModalProps = ModalFunctionalityProps & {
messageId: string
scrollToMessage?: (messageId: string) => Promise<void>
hubId: string
recipient?: string
}

export default function MessageModal({
messageId,
scrollToMessage,
hubId,
recipient,
...props
}: MessageModalProps) {
const myAddress = useMyAccount((state) => state.address)
const isInitialized = useMyAccount((state) => state.isInitialized)
const isDifferentRecipient = recipient !== myAddress

const [isOpenLoginModal, setIsOpenLoginModal] = useState(false)

const { data: message } = getPostQuery.useQuery(messageId)
const chatId = (message as unknown as CommentData)?.struct.rootPostId
const { data: chat } = getPostQuery.useQuery(chatId)
Expand All @@ -30,6 +45,17 @@ export default function MessageModal({
<span className='ml-2 inline-block h-[1.3em] w-28 animate-pulse rounded-lg bg-background' />
)

const sendEvent = useSendEvent()
useEffect(() => {
if (!isInitialized) return

if (props.isOpen && recipient)
sendEvent('open_tg_notification', {
isMyNotif: (myAddress === recipient) + '',
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.isOpen, isInitialized])

const handleScrollToMessage = async () => {
if (!scrollToMessage) return

Expand All @@ -41,41 +67,79 @@ export default function MessageModal({
}

return (
<Modal
{...props}
initialFocus={buttonRef}
title={
<span className='flex items-center'>Message from {chatTitle}</span>
}
>
<div
className={cx(
'max-h-96 overflow-y-auto rounded-2xl bg-background p-4',
!message && 'h-28 animate-pulse'
)}
<>
<Modal
{...props}
isOpen={props.isOpen && !isOpenLoginModal}
initialFocus={buttonRef}
title={
<span className='flex items-center'>Message from {chatTitle}</span>
}
>
{message && (
<ChatItem
enableChatMenu={false}
isMyMessage={false}
message={message}
chatId={chatId}
hubId={hubId}
/>
)}
</div>
{scrollToMessage && (
<Button
ref={buttonRef}
isLoading={isScrolling}
onClick={handleScrollToMessage}
className={cx('mt-6')}
size='lg'
variant='primary'
<div
className={cx(
'flex max-h-96 flex-col gap-4 overflow-y-auto rounded-2xl bg-background p-4',
!message && 'h-28 animate-pulse'
)}
>
Scroll to message
</Button>
)}
</Modal>
{message && (
<ChatItem
enableChatMenu={false}
isMyMessage={false}
message={message}
chatId={chatId}
hubId={hubId}
/>
)}
{scrollToMessage && (
<Button
ref={buttonRef}
isLoading={isScrolling}
onClick={handleScrollToMessage}
size='lg'
variant='primaryOutline'
>
Scroll to message
</Button>
)}
</div>
{isDifferentRecipient && recipient && (
<Card className='mt-4 bg-background-lighter'>
<div className='flex items-center gap-2 text-text-muted'>
<span className='text-sm'>Notification recipient</span>
<PopOver
trigger={<HiOutlineInformationCircle />}
triggerOnHover
panelSize='sm'
yOffset={6}
placement='top'
>
<p>You are not currently logged in to this account.</p>
</PopOver>
</div>
<ProfilePreview
className='mt-3 gap-3'
address={recipient}
addressesContainerClassName='gap-1'
avatarClassName='h-9 w-9'
showMaxOneAddress
/>
<Button
size='lg'
className='mt-4 w-full'
onClick={() => setIsOpenLoginModal(true)}
>
Log In
</Button>
</Card>
)}
</Modal>
<LoginModal
isOpen={isOpenLoginModal}
closeModal={() => setIsOpenLoginModal(false)}
initialOpenState='enter-secret-key'
onBackClick={() => setIsOpenLoginModal(false)}
/>
</>
)
}
1 change: 0 additions & 1 deletion src/components/navbar/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ export default function Navbar({
</nav>
<LoginModal
isOpen={openLoginModal}
openModal={() => setOpenLoginModal(true)}
closeModal={() => setOpenLoginModal(false)}
beforeLogin={() => (isLoggingInWithKey.current = true)}
afterLogin={() => (isLoggingInWithKey.current = false)}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/chat/ChatPage/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function ChatPage({
if (isNewChat) setIsOpenCreateSuccessModal(true)
}, [])
useEffect(() => {
if (!isOpenCreateSuccessModal) replaceUrl(getCurrentUrlWithoutQuery())
if (!isOpenCreateSuccessModal) replaceUrl(getCurrentUrlWithoutQuery('new'))
}, [isOpenCreateSuccessModal])

const { data: messageIds } = useCommentIdsByPostId(chatId, {
Expand Down
Loading

0 comments on commit 5bcf758

Please sign in to comment.