Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show users who have voted for non anonymous polls (mobile app) #830

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { SaveMessageAction } from './SaveMessageAction';
import { useGetUser } from '@/hooks/useGetUser';
import { ShareAction } from './ShareAction';
import { EmojiAction } from './EmojiAction';
import { RetractVoteAction } from './RetractVoteAction';
import MessagePreview from './MessagePreview';
import { PollActions } from './PollActions';

interface MessageActionModalProps {
selectedMessage?: Message,
Expand Down Expand Up @@ -79,8 +79,8 @@ export const MessageActionModal = ({ selectedMessage, onDismiss }: MessageAction
<IonLabel className='font-semibold'>Reply</IonLabel>
</IonItem> */}

{selectedMessage.message_type === 'Poll' &&
<RetractVoteAction message={selectedMessage} onSuccess={onDismiss} />}
{selectedMessage.message_type === 'Poll' && <PollActions message={selectedMessage} onSuccess={onDismiss} />}

{selectedMessage.message_type !== 'Poll' &&
<ShareAction message={selectedMessage} onSuccess={onDismiss} />}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useFrappeGetCall } from 'frappe-react-sdk'
import { ActionIcon, ActionItem, ActionLabel, ActionProps } from './common'
import { Poll } from '../chat-view/MessageBlock'
import { useState } from 'react'
import { peopleOutline } from 'ionicons/icons'
import { RetractVoteAction } from './RetractVoteAction'
import { ViewPollVotes } from '../../polls/ViewPollVotes'

export const PollActions = ({ message, onSuccess }: ActionProps) => {

// fetch poll data using message_id
const { data } = useFrappeGetCall<{ message: Poll }>('raven.api.raven_poll.get_poll', {
'message_id': message?.name,
}, `poll_data_${message?.poll_id}`, {
revalidateOnFocus: false,
revalidateIfStale: false,
revalidateOnReconnect: false
})

if (data && data.message) {
return <>
{data.message.current_user_votes.length > 0 && <RetractVoteAction message={message} onSuccess={onSuccess} />}
{data.message.poll.is_anonymous !== 1 && <ViewPollVotesAction poll={data.message} />}
</>
}

return null
}

const ViewPollVotesAction = ({ poll }: { poll: Poll }) => {

const [isOpen, setIsOpen] = useState(false)

return (
<>
<ActionItem onClick={() => setIsOpen(true)}>
<ActionIcon icon={peopleOutline} />
<ActionLabel label='View poll votes' />
</ActionItem>
<ViewPollVotes
isOpen={isOpen}
onDismiss={() => setIsOpen(false)}
poll={poll}
/>
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import { useFrappeGetCall, useFrappePostCall } from 'frappe-react-sdk'
import { useFrappePostCall } from 'frappe-react-sdk'
import { ActionIcon, ActionItem, ActionLabel, ActionProps } from './common'
import { arrowUndoOutline } from 'ionicons/icons'
import { useIonToast } from '@ionic/react'
import { Poll } from '../chat-view/MessageBlock'

export const RetractVoteAction = ({ message, onSuccess }: ActionProps) => {

const [present] = useIonToast()

// fetch poll data using message_id
const { data } = useFrappeGetCall<{ message: Poll }>('raven.api.raven_poll.get_poll', {
'message_id': message?.name,
}, `poll_data_${message?.poll_id}`, {
revalidateOnFocus: false,
revalidateIfStale: false,
revalidateOnReconnect: false
})

const { call } = useFrappePostCall('raven.api.raven_poll.retract_vote')
const onRetractVote = () => {
return call({
Expand All @@ -38,13 +28,10 @@ export const RetractVoteAction = ({ message, onSuccess }: ActionProps) => {
})
}

if (data && data.message?.current_user_votes.length > 0)
return (
<ActionItem onClick={onRetractVote}>
<ActionIcon icon={arrowUndoOutline} />
<ActionLabel label='Retract vote' />
</ActionItem>
)

return null
return (
<ActionItem onClick={onRetractVote}>
<ActionIcon icon={arrowUndoOutline} />
<ActionLabel label='Retract vote' />
</ActionItem>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import { useLongPress } from "@uidotdev/usehooks";
import MessageReactions from './components/MessageReactions'
import parse from 'html-react-parser';
import clsx from 'clsx'
import { Avatar, Badge, Box, Button, Checkbox, Flex, RadioGroup, Text, Theme } from '@radix-ui/themes'
import { Avatar, Badge, Box, Button, Checkbox, Flex, RadioGroup, Separator, Text, Theme } from '@radix-ui/themes'
import { useGetUser } from '@/hooks/useGetUser'
import { generateAvatarColor, getInitials } from '@/components/common/UserAvatar'
import { RiRobot2Fill } from 'react-icons/ri'
import { useFrappeDocumentEventListener, useFrappeGetCall, useFrappePostCall, useSWRConfig } from 'frappe-react-sdk'
import { RavenPoll } from '@/types/RavenMessaging/RavenPoll'
import { RavenPollOption } from '@/types/RavenMessaging/RavenPollOption'
import { MdOutlineBarChart } from 'react-icons/md'
import { ViewPollVotes } from '../../polls/ViewPollVotes'

type Props = {
message: Message,
Expand Down Expand Up @@ -80,8 +81,10 @@ export const NonContinuationMessageBlock = ({ message, onMessageSelect, isScroll

const { user, isActive } = useGetUserDetails(message.is_bot_message && message.bot ? message.bot : message.owner)

const [disableLongPress, setDisableLongPress] = useState(false)
const longPressEvent = useLongPress((e) => {
if (isScrolling) return
if (disableLongPress) return
Haptics.impact({
style: ImpactStyle.Medium
})
Expand All @@ -100,7 +103,11 @@ export const NonContinuationMessageBlock = ({ message, onMessageSelect, isScroll
{isBot && <Badge className='ml-2' color='gray'>Bot</Badge>}
<Text as='span' size='1' className='pl-1.5 text-gray-10'>{DateObjectToTimeString(message.creation)}</Text>
</div>
<MessageContent message={message} onReplyMessageClick={onReplyMessageClick} />
<MessageContent
message={message}
onLongPressDisabled={() => setDisableLongPress(true)}
onLongPressEnabled={() => setDisableLongPress(false)}
onReplyMessageClick={onReplyMessageClick} />
{message.is_edited === 1 && <Text size='1' color='gray'>(edited)</Text>}
</div>
</div>
Expand Down Expand Up @@ -149,8 +156,10 @@ interface ContinuationMessageBlockProps {
}
const ContinuationMessageBlock = ({ message, onMessageSelect, isScrolling, isHighlighted, onReplyMessageClick }: ContinuationMessageBlockProps) => {

const [disableLongPress, setDisableLongPress] = useState(false)
const longPressEvent = useLongPress((e) => {
if (isScrolling) return
if (disableLongPress) return
Haptics.impact({
style: ImpactStyle.Medium
})
Expand All @@ -163,7 +172,11 @@ const ContinuationMessageBlock = ({ message, onMessageSelect, isScrolling, isHig
<div className='w-11'>
</div>
<div>
<MessageContent message={message} onReplyMessageClick={onReplyMessageClick} />
<MessageContent
message={message}
onLongPressDisabled={() => setDisableLongPress(true)}
onLongPressEnabled={() => setDisableLongPress(false)}
onReplyMessageClick={onReplyMessageClick} />
{message.is_edited === 1 && <IonText className='text-xs' color={'medium'}>(edited)</IonText>}
</div>

Expand All @@ -175,7 +188,13 @@ const ContinuationMessageBlock = ({ message, onMessageSelect, isScrolling, isHig
</div>
}

const MessageContent = ({ message, onReplyMessageClick }: { message: Message, onReplyMessageClick: (messageID: string) => void }) => {
interface MessageContentProps {
message: Message,
onReplyMessageClick: (messageID: string) => void
onLongPressDisabled: () => void
onLongPressEnabled: () => void
}
const MessageContent = ({ message, onReplyMessageClick, onLongPressDisabled, onLongPressEnabled }: MessageContentProps) => {
const scrollToMessage = () => {
if (message.linked_message) {
Haptics.impact({
Expand All @@ -193,7 +212,10 @@ const MessageContent = ({ message, onReplyMessageClick }: { message: Message, on
{message.text && <div><TextMessageBlock message={message as TextMessage} /></div>}
{message.message_type === 'Image' && <ImageMessageBlock message={message} />}
{message.message_type === 'File' && <FileMessageBlock message={message} />}
{message.message_type === 'Poll' && <PollMessageBlock message={message} />}
{message.message_type === 'Poll' && <PollMessageBlock
message={message}
onModalClose={onLongPressEnabled}
onModalOpen={onLongPressDisabled} />}
</div>
}

Expand Down Expand Up @@ -306,31 +328,50 @@ export interface Poll {
'current_user_votes': { 'option': string }[]
}

const PollMessageBlock = ({ message }: { message: PollMessage }) => {
const PollMessageBlock = ({ message, onModalClose, onModalOpen }: { message: PollMessage, onModalOpen: VoidFunction, onModalClose: VoidFunction }) => {

const { mutate: globalMutate } = useSWRConfig()
// fetch poll data using message_id
const { data, error, mutate } = useFrappeGetCall<{ message: Poll }>('raven.api.raven_poll.get_poll', {
'message_id': message.name,
}, `poll_data_${message.poll_id}`, {
revalidateOnFocus: false,
revalidateIfStale: false,
revalidateOnReconnect: false
revalidateOnReconnect: false,
revalidateOnMount: true
})

useFrappeDocumentEventListener('Raven Poll', message.poll_id, () => {
mutate()
globalMutate(`poll_votes_${message.poll_id}`)
})

return (
<div className='py-1.5 rounded-lg'>
{data && <PollMessageBox data={data.message} messageID={message.name} />}
{data && <PollMessageBox
data={data.message}
messageID={message.name}
onModalOpen={onModalOpen}
onModalClose={onModalClose}
/>}
</div>
)
}

const PollMessageBox = ({ data, messageID }: { data: Poll, messageID: string }) => {
const PollMessageBox = ({ data, messageID, onModalClose, onModalOpen }: { data: Poll, messageID: string, onModalOpen: VoidFunction, onModalClose: VoidFunction }) => {

const [isOpen, setOpen] = useState<boolean>(false)
const onViewClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
setOpen(true)
onModalOpen()
}

const closeModal = () => {
setOpen(false)
onModalClose()
}
return (
<Flex align='center' gap='4' p='2' className="bg-gray-2
<Flex align='center' direction='column' className="bg-gray-2
shadow-sm
dark:bg-gray-3
group-hover:bg-accent-a2
Expand All @@ -340,9 +381,11 @@ const PollMessageBox = ({ data, messageID }: { data: Poll, messageID: string })
min-w-64
w-full
rounded-md">
<Flex direction='column' gap='2' p='2' className="w-full">
<Flex justify='between' align='center' gap='2'>
<Text size='2' weight={'medium'}>{data.poll.question}</Text>
<Flex direction='column' gap='3' p='4' className="w-full">
<Flex direction='column' gap='2'>
<Text size='2' weight={'medium'}>
{data.poll.question}
</Text>
{data.poll.is_anonymous ? <Badge color='blue' className={'w-fit'}>Anonymous</Badge> : null}
</Flex>
{data.current_user_votes.length > 0 ?
Expand All @@ -356,6 +399,17 @@ const PollMessageBox = ({ data, messageID }: { data: Poll, messageID: string })
}
{data.poll.is_disabled ? <Badge color="gray" className={'w-fit'}>Poll is now closed</Badge> : null}
</Flex>
{data.poll.is_anonymous ? null :
<>
<Separator size='4' />
<Flex pt='3' pb='3'>
<Button variant='ghost'
onClick={onViewClick}
className='hover:bg-transparent hover:text-accent-10 w-full'>View Votes</Button>
</Flex>
<ViewPollVotes isOpen={isOpen} onDismiss={closeModal} poll={data} />
</>
}
</Flex>
)
}
Expand Down
Loading
Loading