Skip to content

Commit

Permalink
fix: fix import
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinCupela committed Nov 1, 2024
1 parent 8174682 commit 0cf5482
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 8 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docusaurus/docs/React/assets/end-poll-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docusaurus/docs/React/assets/poll-results.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
194 changes: 194 additions & 0 deletions docusaurus/docs/React/components/message-components/poll.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
---
id: poll
title: Poll
---

Messages can contain polls. Polls are by default created using `PollCreationDialog` that is invoked from [`AttachmentSelector`](../../message-input-components/attachment-selector). Messages that render polls are not editable. Polls can be only closed by the poll creator. The top-level component to render the message poll data is `Poll` and it renders a header followed by option list and poll actions section.

![](../../assets/message-with-poll.png)

## Poll UI customization

The following part of the poll UI can be customized:

- `QuotedPoll` - UI rendered if the poll is rendered in a quoted message
- `PollContent` - component that renders the whole non-quoted poll UI
- `PollHeader` - customizes the topmost part of the poll UI
- `PollOptionSelector` - customizes the individual clickable option selectors
- `PollActions` - customizes the bottom part of the poll UI that consists of buttons that invoke action dialogs

### Poll header customization

```tsx
import { ReactNode } from 'react';
import { Channel } from 'stream-chat-react';

const PollHeader = () => <div>Custom Header</div>;

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
<Channel PollHeader={PollHeader}>{children}</Channel>
);
```

### Poll option selector customization

If we wanted to customize only the option selector we can do it with custom `PollOptionSelector` component.

```tsx
import { ReactNode } from 'react';
import { Channel } from 'stream-chat-react';

const PollOptionSelector = () => <div>Custom Option Selector</div>;

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
<Channel PollOptionSelector={PollOptionSelector}>{children}</Channel>
);
```

### Poll actions customization

The component `PollActions` controls the display of dialogs or modals that allow user to further interact with the poll data. There are the following poll actions supported by the component that invoke corresponding dialogs resp. modals:

| Action button | Visible condition | Invokes |
| ------------------------- | ----------------------------------------------------------------------------------------- | ----------------------- |
| See all options | option count > 10 | `PollOptionsFullList` |
| Suggest an option | poll is not closed and `poll.allow_user_suggested_options === true` | `SuggestPollOptionForm` |
| Add or update own comment | poll is not closed and `poll.allow_answers === true` | `AddCommentForm` |
| View comments | `channel.own_capabilities` array contains `'query-poll-votes'` & `poll.answers_count > 0` | `PollAnswerList` |
| View results | always visible | `PollResults` |
| End vote | owner of the poll | `EndPollDialog` |

**Default PollOptionsFullList**

![](../../assets/poll-option-full-list.png)

**Default SuggestPollOptionForm**

![](../../assets/suggest-poll-option-form.png)

**Default AddCommentForm**

![](../../assets/add-poll-comment-form.png)

**Default PollAnswerList**

![](../../assets/poll-comment-list.png)

**Default PollResults**

![](../../assets/poll-results.png)

**Default EndPollDialog**

![](../../assets/end-poll-dialog.png)

Individual dialogs and thus the whole `PollActions` component can be overridden via `PollActions` component props as follows:

```tsx
import { ReactNode } from 'react';
import { Channel, PollActions } from 'stream-chat-react';
import {
CustomAddCommentForm,
CustomEndPollDialog,
CustomPollAnswerList,
CustomPollOptionsFullList,
CustomPollResults,
CustomSuggestPollOptionForm,
} from './PollActions';

const CustomPollActions = () => (
<PollActions
AddCommentForm={CustomAddCommentForm}
EndPollDialog={CustomEndPollDialog}
PollAnswerList={CustomPollAnswerList}
PollOptionsFullList={CustomPollOptionsFullList}
PollResults={CustomPollResults}
SuggestPollOptionForm={CustomSuggestPollOptionForm}
/>
);

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
<Channel PollActions={CustomPollActions}>{children}</Channel>
);
```

### Poll contents layout customization

This approach is useful when we want to change the organization of the poll UI. For the purpose we can provide custom `PollContent` component to `Channel`.

```tsx
import { ReactNode } from 'react';
import { Channel } from 'stream-chat-react';
import { CustomPollHeader, CustomPollOptionList } from './Poll';

const PollContents = () => (
<div>
<CustomPollHeader />
<CustomPollOptionList />
</div>
);

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
<Channel PollContents={PollContents}>{children}</Channel>
);
```

## Poll API and state

In order to be fully capable to customize the poll UI, we need to learn how to utilize Poll API and later access the reactive poll state.

First of all, the Poll API is exposed via a `Poll` instance. This instance is provided via React context to all the children of the `Poll` component that is rendered internally by `Message` component. The context can be consumed using `usePollContext` hook:

```tsx
import { usePollContext } from 'stream-chat-react';

const Component = () => {
const { poll } = usePollContext();
};
```

The `Poll` instance exposes the following methods:

- `query` - queries the data for a given poll (permission to query polls is required)
- `update` - overwrites the poll data
- `partialUpdate` - overwrites only the given poll data
- `close` - marks the poll as closed (useful for custom `EndPollDialog`)
- `delete` - deletes the poll
- `createOption` - creates a new option for given poll (useful for custom `SuggestPollOptionForm`)
- `updateOption` - updates an option
- `deleteOption` - removes the option from a poll
- `castVote` - casts a vote to a given option (useful for custom `PollOptionSelector`)
- `removeVote` - removes a vote from a given option (useful for custom `PollOptionSelector`)
- `addAnswer` - adds an answer (comment)
- `removeAnswer` - removes an answer (comment)
- `queryAnswers` - queries and paginates answers (useful for custom `PollAnswerList`)
- `queryOptionVotes` - queries and paginates votes for a given option (useful for option detail)

The poll state can be accessed inside the custom React components using the following pattern

```tsx
import { usePollContext, useStateStore } from 'stream-chat-react';

import type { PollState, PollVote } from 'stream-chat';
import type { StreamChatGenerics } from './types';

type PollStateSelectorReturnValue = {
latest_votes_by_option: Record<string, PollVote<StreamChatGenerics>[]>;
};

// 1. Define the selector function that receives the fresh value every time the observed property changes
const pollStateSelector = (
nextValue: PollState<StreamChatGenerics>,
): PollStateSelectorReturnValue => ({ latest_votes_by_option: nextValue.latest_votes_by_option });

const CustomComponent = () => {
// 2. Retrieve the poll instance from the context
const { poll } = usePollContext<StreamChatGenerics>();
// 3. Use the useStateStore hook to subscribe to updates in the poll state with selector picking out only properties we are interested in
const { latest_votes_by_option } = useStateStore(poll.state, pollStateSelector);
};
```

:::warning
Do not try to access the `poll` data via `message` object available from `MessageContext`. This data is not updated and serve only as a seed, for the `poll` state.
:::
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
id: attachment-selector
title: Attachment Selector
---

Messages can be enriched with attachments or polls by default. The `AttachmentSelector` component is a component that allows to select what information is to be attached to a message. The attachment objects are included in `message.attachments` property and represent various file uploads. The poll representation is available via `message.poll` property.

## Enabling the default attachment selector features

The configuration is possible via Stream dashboard. File uploads and poll creation can be controlled via

1. role permissions

![](../../assets/dashboard-roles-permissions-ui.png)

2. channel type configuration

![](../../assets/dashboard-channel-type-feature-configuration-ui.png)

## File uploads

Uploads are possible only if both Upload Attachment permission is granted to the user role and channel type Uploads configuration is enabled.

## Poll creation

:::note
Polls feature is available in the React SDK as of version 12.5.0
:::

Poll creation is enabled only if both Create Poll permission is granted to the user role and channel type Polls configuration is enabled. Poll creation is not possible withing threads.

### Poll creation UI

The component in charge of rendering [the poll creation UI is `PollCreationDialog`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Poll/PollCreationDialog/PollCreationDialog.tsx). The component is rendered in a modal and therefore accepts a prop `close`.

![](../../assets/poll-creation-dialog.png)

Custom `PollCreationDialog` can be provided via `Channel` prop `PollCreationDialog`:

```tsx
import { ReactNode } from 'react';
import { Channel } from 'stream-chat-react';
import type { PollCreationDialogProps } from 'stream-chat-react';

const CustomPollCreationDialog = ({ close }: PollCreationDialogProps) => (
<div onClick={close}>Custom Poll Creation Dialog</div>
);

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
<Channel PollCreationDialog={CustomPollCreationDialog}>{children}</Channel>
);
```

Created poll is then rendered within a message list by [`Poll` component](../../message-components/poll).

## Attachment selector customization

### Custom attachment selector actions

Items in the `AttachementSelector` menu can be customized via its `attachmentSelectorActionSet` prop:

```tsx
import { ReactNode } from 'react';
import { AttachmentSelector, Channel, defaultAttachmentSelectorActionSet } from 'stream-chat-react';
import type {
AttachmentSelectorAction,
AttachmentSelectorActionProps,
AttachmentSelectorModalContentProps,
} from 'stream-chat-react';

// Define the menu button
const AddLocationAttachmentAction = ({
closeMenu,
openModalForAction,
}: AttachmentSelectorActionProps) => (
<button
onClick={() => {
openModalForAction('addLocation');
closeMenu();
}}
>
Location
</button>
);

// Define the modal contents to be rendered if AddLocationAttachmentAction button is clicked
const AddLocationModalContent = ({ close }: AttachmentSelectorModalContentProps) => {
return <div onClick={close}>abc</div>;
};

// the custom action will be at the top of the menu
const attachmentSelectorActionSet: AttachmentSelectorAction[] = [
{
ActionButton: AddLocationAttachmentAction,
ModalContent: AddLocationModalContent,
type: 'addLocation',
},
...defaultAttachmentSelectorActionSet,
];

const CustomAttachmentSelector = () => (
<AttachmentSelector attachmentSelectorActionSet={attachmentSelectorActionSet} />
);

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
<Channel AttachmentSelector={CustomAttachmentSelector}>{children}</Channel>
);
```

### Custom modal portal destination

By default, the modals invoked by clicking on AttachmentSelector menu buttons are anchored to the channel container `div` element. The destination element can be changed by providing `getModalPortalDestination` prop to `AttachmentSelector`. This would be function that would return a reference to the target element that would serve as a parent for the modal.

```tsx
const getModalPortalDestination = () => document.querySelector<HTMLDivElement>('#my-element-id');

const CustomAttachmentSelector = () => (
<AttachmentSelector getModalPortalDestination={getModalPortalDestination} />
);
```
2 changes: 2 additions & 0 deletions docusaurus/sidebars-react.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"components/message-components/ui-components",
"components/utility-components/avatar",
"components/utility-components/base-image",
"components/message-components/poll",
{
"Attachment": [
"components/message-components/attachment",
Expand All @@ -78,6 +79,7 @@
"components/message-input-components/ui_components",
"components/message-input-components/emoji-picker",
"components/message-input-components/audio_recorder",
"components/message-input-components/attachment-selector",
"components/contexts/typing_context"
]
},
Expand Down
16 changes: 8 additions & 8 deletions src/components/Poll/PollContent.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { DefaultStreamChatGenerics } from '../../types';
import type { PollState } from '../../../../stream-chat-js';
import { PollHeader as DefaulPollHeader } from './PollHeader';
import { PollActions as DefaultPollActions } from './PollActions';
import { useComponentContext, usePollContext } from '../../context';
import { useStateStore } from '../../store';
import clsx from 'clsx';
import React from 'react';
import { PollHeader as DefaultPollHeader } from './PollHeader';
import { PollActions as DefaultPollActions } from './PollActions';
import { PollOptionList } from './PollOptionList';
import { MAX_OPTIONS_DISPLAYED } from './constants';
import React from 'react';
import { useComponentContext, usePollContext } from '../../context';
import { useStateStore } from '../../store';
import type { PollState } from 'stream-chat';
import type { DefaultStreamChatGenerics } from '../../types';

type PollStateSelectorPollContentReturnValue = { is_closed: boolean | undefined };
const pollStateSelectorPollContent = <
Expand All @@ -19,7 +19,7 @@ export const PollContent = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>() => {
const {
PollHeader = DefaulPollHeader,
PollHeader = DefaultPollHeader,
PollActions = DefaultPollActions,
} = useComponentContext<StreamChatGenerics>();
const { poll } = usePollContext<StreamChatGenerics>();
Expand Down

0 comments on commit 0cf5482

Please sign in to comment.