diff --git a/README.md b/README.md index daf006745..6e7f3e99e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![NPM](https://img.shields.io/npm/v/stream-chat-react-native.svg)](https://www.npmjs.com/package/stream-chat-react-native) [![Build Status](https://github.com/GetStream/stream-chat-react-native/actions/workflows/release.yml/badge.svg)](https://github.com/GetStream/stream-chat-react-native/actions) [![Component Reference](https://img.shields.io/badge/docs-component%20reference-blue.svg)](https://getstream.io/chat/docs/sdk/reactnative) -![JS Bundle Size](https://img.shields.io/badge/js_bundle_size-447%20KB-blue) +![JS Bundle Size](https://img.shields.io/badge/js_bundle_size-451%20KB-blue) diff --git a/docusaurus/docs/reactnative/hooks/poll/use-poll-state-store.mdx b/docusaurus/docs/reactnative/hooks/poll/use-poll-state-store.mdx index 58e756c5a..8c99907d1 100644 --- a/docusaurus/docs/reactnative/hooks/poll/use-poll-state-store.mdx +++ b/docusaurus/docs/reactnative/hooks/poll/use-poll-state-store.mdx @@ -16,16 +16,16 @@ import { PollVote, PollState } from 'stream-chat'; import { usePollStateStore } from 'stream-chat-react-native'; type PollOptionSelectorReturnValue = { - latest_votes_by_option: Record; + latestVotesByOption: Record; maxVotedOptionIds: string[]; }; const selector = ( nextValue: PollState, ): PollOptionSelectorReturnValue => ({ - latest_votes_by_option: nextValue.latest_votes_by_option, + latestVotesByOption: nextValue.latestVotesByOption, maxVotedOptionIds: nextValue.maxVotedOptionIds, }); -const { latest_votes_by_option, maxVotedOptionIds } = usePollStateStore(selector); +const { latestVotesByOption, maxVotedOptionIds } = usePollStateStore(selector); ``` diff --git a/docusaurus/docs/reactnative/hooks/poll/use-poll-state.mdx b/docusaurus/docs/reactnative/hooks/poll/use-poll-state.mdx index e35a75d4b..ae8fbc710 100644 --- a/docusaurus/docs/reactnative/hooks/poll/use-poll-state.mdx +++ b/docusaurus/docs/reactnative/hooks/poll/use-poll-state.mdx @@ -15,7 +15,7 @@ It is highly recommended that the user familiarizes themselves with the [`Poll` The `usePollState` hook returns an object containing values from the `PollState`. -### `allow_answers` +### `allowAnswers` A property depicting whether answers (comments) are allowed to the `poll`. @@ -23,7 +23,7 @@ A property depicting whether answers (comments) are allowed to the `poll`. | --------- | ----------- | | `boolean` | `undefined` | -### `allow_user_suggested_options` +### `allowUserSuggestedOptions` A property depicting whether user option suggestions are allowed to the `poll`. @@ -31,7 +31,7 @@ A property depicting whether user option suggestions are allowed to the `poll`. | --------- | ----------- | | `boolean` | `undefined` | -### `answers_count` +### `answersCount` A property containing the number of answers (comments) to the `poll`. @@ -49,7 +49,7 @@ This property will be `null` for anonymous polls. | -------- | ------ | | `object` | `null` | -### `enforce_unique_vote` +### `enforceUniqueVote` A property depicting whether each user should have only one and unique vote or they would be able to vote multiple times during the lifespan of a `poll`. @@ -65,7 +65,7 @@ A property depicting whether the `poll` is still open for voting or not. | --------- | | `boolean` | -### `latest_votes_by_option` +### `latestVotesByOption` A property containing the latest votes for each option stored in an object by option ID. Only the last 10 votes are maintained here. If all votes are needed for an option, the [`usePollOptionVotesPagination`](./use-poll-option-votes-pagination.mdx) hook should be used instead. @@ -123,7 +123,7 @@ A property containing the current user's own votes in a given `poll`, stored in | -------------------------- | | `Record` | -### `vote_counts_by_option` +### `voteCountsByOption` A property containing the vote counts in a given `poll`, stored in an object by option ID. @@ -131,7 +131,7 @@ A property containing the vote counts in a given `poll`, stored in an object by | ------------------------ | | `Record` | -### `voting_visibility` +### `votingVisibility` A property depicting the visibility of votes in a given `poll`. It conforms to the `VotingVisibility` enum found [here](https://github.com/GetStream/stream-chat-js/blob/b447512922b19bc7e3668bd9df81debcb673dd81/src/types.ts). diff --git a/package/src/components/ChannelPreview/hooks/useLatestMessagePreview.ts b/package/src/components/ChannelPreview/hooks/useLatestMessagePreview.ts index aec0fafa8..da5899942 100644 --- a/package/src/components/ChannelPreview/hooks/useLatestMessagePreview.ts +++ b/package/src/components/ChannelPreview/hooks/useLatestMessagePreview.ts @@ -38,15 +38,15 @@ export type LatestMessagePreview< }; export type LatestMessagePreviewSelectorReturnType = { - created_by?: UserResponse | null; - latest_votes_by_option?: Record; + createdBy?: UserResponse | null; + latestVotesByOption?: Record; }; const selector = ( nextValue: PollState, ): LatestMessagePreviewSelectorReturnType => ({ - created_by: nextValue.created_by, - latest_votes_by_option: nextValue.latest_votes_by_option, + createdBy: nextValue.created_by, + latestVotesByOption: nextValue.latest_votes_by_option, }); const getMessageSenderName = < @@ -146,15 +146,15 @@ const getLatestMessageDisplayText = < ]; } if (message.poll && pollState) { - const { created_by, latest_votes_by_option } = pollState; + const { createdBy, latestVotesByOption } = pollState; let latestVotes; - if (latest_votes_by_option) { - latestVotes = Object.values(latest_votes_by_option) + if (latestVotesByOption) { + latestVotes = Object.values(latestVotesByOption) .map((votes) => votes?.[0]) .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); } let previewAction = 'created'; - let previewUser = created_by; + let previewUser = createdBy; if (latestVotes && latestVotes.length && latestVotes[0]?.user) { previewAction = 'voted'; previewUser = latestVotes[0]?.user; @@ -310,7 +310,7 @@ export const useLatestMessagePreview = < const poll = client.polls.fromState(pollId); const pollState: LatestMessagePreviewSelectorReturnType = useStateStore(poll?.state, selector) ?? {}; - const { created_by, latest_votes_by_option } = pollState; + const { createdBy, latestVotesByOption } = pollState; useEffect( () => @@ -325,14 +325,7 @@ export const useLatestMessagePreview = < }), ), // eslint-disable-next-line react-hooks/exhaustive-deps - [ - channelLastMessageString, - forceUpdate, - readEvents, - readStatus, - latest_votes_by_option, - created_by, - ], + [channelLastMessageString, forceUpdate, readEvents, readStatus, latestVotesByOption, createdBy], ); return latestMessagePreview; diff --git a/package/src/components/Poll/Poll.tsx b/package/src/components/Poll/Poll.tsx index 8d3966d90..0c5cce96f 100644 --- a/package/src/components/Poll/Poll.tsx +++ b/package/src/components/Poll/Poll.tsx @@ -47,13 +47,13 @@ export const PollButtons = () => ( export const PollHeader = () => { const { t } = useTranslationContext(); - const { enforce_unique_vote, is_closed, max_votes_allowed, name } = usePollState(); + const { enforceUniqueVote, isClosed, maxVotesAllowed, name } = usePollState(); const subtitle = useMemo(() => { - if (is_closed) return t('Vote ended'); - if (enforce_unique_vote) return t('Select one'); - if (max_votes_allowed) return t('Select up to {{count}}', { count: max_votes_allowed }); + if (isClosed) return t('Vote ended'); + if (enforceUniqueVote) return t('Select one'); + if (maxVotesAllowed) return t('Select up to {{count}}', { count: maxVotesAllowed }); return t('Select one or more'); - }, [is_closed, t, enforce_unique_vote, max_votes_allowed]); + }, [isClosed, t, enforceUniqueVote, maxVotesAllowed]); const { theme: { diff --git a/package/src/components/Poll/components/Button.tsx b/package/src/components/Poll/components/Button.tsx index 527416dc1..59d4abdc3 100644 --- a/package/src/components/Poll/components/Button.tsx +++ b/package/src/components/Poll/components/Button.tsx @@ -115,10 +115,10 @@ export const ViewResultsButton = (props: PollButtonProps) => { export const EndVoteButton = () => { const { t } = useTranslationContext(); - const { created_by, endVote, is_closed } = usePollState(); + const { createdBy, endVote, isClosed } = usePollState(); const { client } = useChatContext(); - return !is_closed && created_by?.id === client.userID ? ( + return !isClosed && createdBy?.id === client.userID ? ( ('End Vote')} /> ) : null; }; @@ -126,7 +126,7 @@ export const EndVoteButton = () => { export const AddCommentButton = (props: PollButtonProps) => { const { t } = useTranslationContext(); const { message, poll } = usePollContext(); - const { addComment, allow_answers, is_closed, ownAnswer } = usePollState(); + const { addComment, allowAnswers, isClosed, ownAnswer } = usePollState(); const [showAddCommentDialog, setShowAddCommentDialog] = useState(false); const { onPress } = props; @@ -141,7 +141,7 @@ export const AddCommentButton = (props: PollButtonProps) => { return ( <> - {!is_closed && allow_answers ? ( + {!isClosed && allowAnswers ? ( ('Add a comment')} /> ) : null} {showAddCommentDialog ? ( @@ -160,7 +160,7 @@ export const AddCommentButton = (props: PollButtonProps) => { export const ShowAllCommentsButton = (props: PollButtonProps) => { const { t } = useTranslationContext(); const { message, poll } = usePollContext(); - const { answers_count } = usePollState(); + const { answersCount } = usePollState(); const [showAnswers, setShowAnswers] = useState(false); const { onPress } = props; @@ -181,10 +181,10 @@ export const ShowAllCommentsButton = (props: PollButtonProps) => { return ( <> - {answers_count && answers_count > 0 ? ( + {answersCount && answersCount > 0 ? ( ('View {{count}} comments', { count: answers_count })} + title={t('View {{count}} comments', { count: answersCount })} /> ) : null} {showAnswers ? ( @@ -262,7 +262,7 @@ export const AnswerListAddCommentButton = (props: PollButtonProps) => { export const SuggestOptionButton = (props: PollButtonProps) => { const { t } = useTranslationContext(); const { message, poll } = usePollContext(); - const { addOption, allow_user_suggested_options, is_closed } = usePollState(); + const { addOption, allowUserSuggestedOptions, isClosed } = usePollState(); const [showAddOptionDialog, setShowAddOptionDialog] = useState(false); const { onPress } = props; @@ -277,7 +277,7 @@ export const SuggestOptionButton = (props: PollButtonProps) => { return ( <> - {!is_closed && allow_user_suggested_options ? ( + {!isClosed && allowUserSuggestedOptions ? ( ('Suggest an option')} /> ) : null} {showAddOptionDialog ? ( @@ -343,7 +343,7 @@ export const ShowAllOptionsButton = (props: PollButtonProps) => { export const VoteButton = ({ onPress, option }: PollVoteButtonProps) => { const { message, poll } = usePollContext(); - const { is_closed, ownVotesByOptionId } = usePollState(); + const { isClosed, ownVotesByOptionId } = usePollState(); const ownCapabilities = useOwnCapabilitiesContext(); const { @@ -374,7 +374,7 @@ export const VoteButton = ({ onPress, option }: PollVoteButtonProps) => { toggleVote(); }, [message, onPress, poll, toggleVote]); - return ownCapabilities.castPollVote && !is_closed ? ( + return ownCapabilities.castPollVote && !isClosed ? ( { export const ShowAllVotesButton = (props: ShowAllVotesButtonProps) => { const { t } = useTranslationContext(); const { message, poll } = usePollContext(); - const { vote_counts_by_option } = usePollState(); + const { voteCountsByOption } = usePollState(); const ownCapabilities = useOwnCapabilitiesContext(); const [showAllVotes, setShowAllVotes] = useState(false); const { onPress, option } = props; @@ -421,8 +421,8 @@ export const ShowAllVotesButton = (props: ShowAllVotesButtonProps) => { return ( <> {ownCapabilities.queryPollVotes && - vote_counts_by_option && - vote_counts_by_option?.[option.id] > 5 ? ( + voteCountsByOption && + voteCountsByOption?.[option.id] > 5 ? ( ('Show All')} /> ) : null} {showAllVotes ? ( diff --git a/package/src/components/Poll/components/PollAnswersList.tsx b/package/src/components/Poll/components/PollAnswersList.tsx index 2afa7581f..655ceb2f6 100644 --- a/package/src/components/Poll/components/PollAnswersList.tsx +++ b/package/src/components/Poll/components/PollAnswersList.tsx @@ -26,7 +26,7 @@ export type PollAnswersListProps< export const PollAnswerListItem = ({ answer }: { answer: PollAnswer }) => { const { t, tDateTimeParser } = useTranslationContext(); - const { voting_visibility } = usePollState(); + const { votingVisibility } = usePollState(); const { theme: { @@ -49,8 +49,8 @@ export const PollAnswerListItem = ({ answer }: { answer: PollAnswer }) => { ); const isAnonymous = useMemo( - () => voting_visibility === VotingVisibility.anonymous, - [voting_visibility], + () => votingVisibility === VotingVisibility.anonymous, + [votingVisibility], ); return ( diff --git a/package/src/components/Poll/components/PollOption.tsx b/package/src/components/Poll/components/PollOption.tsx index 69a22c2b8..70732977a 100644 --- a/package/src/components/Poll/components/PollOption.tsx +++ b/package/src/components/Poll/components/PollOption.tsx @@ -73,21 +73,18 @@ export const PollAllOptions = ({ ); export const PollOption = ({ option, showProgressBar = true }: PollOptionProps) => { - const { is_closed, latest_votes_by_option, maxVotedOptionIds, vote_counts_by_option } = - usePollState(); + const { isClosed, latestVotesByOption, maxVotedOptionIds, voteCountsByOption } = usePollState(); const relevantVotes = useMemo( - () => latest_votes_by_option?.[option.id]?.slice(0, 2) || [], - [latest_votes_by_option, option.id], + () => latestVotesByOption?.[option.id]?.slice(0, 2) || [], + [latestVotesByOption, option.id], ); const maxVotes = useMemo( () => - maxVotedOptionIds?.[0] && vote_counts_by_option - ? vote_counts_by_option[maxVotedOptionIds[0]] - : 0, - [maxVotedOptionIds, vote_counts_by_option], + maxVotedOptionIds?.[0] && voteCountsByOption ? voteCountsByOption[maxVotedOptionIds[0]] : 0, + [maxVotedOptionIds, voteCountsByOption], ); - const votes = vote_counts_by_option[option.id] || 0; + const votes = voteCountsByOption[option.id] || 0; const { theme: { @@ -118,9 +115,7 @@ export const PollOption = ({ option, showProgressBar = true }: PollOptionProps) {relevantVotes.map((vote: PollVote) => ( ))} - - {vote_counts_by_option[option.id] || 0} - + {voteCountsByOption[option.id] || 0} {showProgressBar ? ( @@ -128,7 +123,7 @@ export const PollOption = ({ option, showProgressBar = true }: PollOptionProps) 0 ? votes / maxVotes : 0, diff --git a/package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx b/package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx index 84bf74507..997076a2f 100644 --- a/package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx +++ b/package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx @@ -24,7 +24,7 @@ export type PollOptionFullResultsProps< }; export const PollOptionFullResultsItem = ({ item }: { item: PollVoteClass }) => ( - + ); export const PollOptionFullResultsContent = ({ @@ -33,7 +33,7 @@ export const PollOptionFullResultsContent = ({ }: Pick) => { const { t } = useTranslationContext(); const { hasNextPage, loadMore, votes } = usePollOptionVotesPagination({ option }); - const { vote_counts_by_option } = usePollState(); + const { voteCountsByOption } = usePollState(); const { theme: { @@ -48,11 +48,11 @@ export const PollOptionFullResultsContent = ({ () => ( - {t('{{count}} votes', { count: vote_counts_by_option[option.id] ?? 0 })} + {t('{{count}} votes', { count: voteCountsByOption[option.id] ?? 0 })} ), - [black, headerContainer, headerText, option.id, t, vote_counts_by_option], + [black, headerContainer, headerText, option.id, t, voteCountsByOption], ); return ( diff --git a/package/src/components/Poll/components/PollResults/PollResultItem.tsx b/package/src/components/Poll/components/PollResults/PollResultItem.tsx index f3d4ad9e3..3c525bf43 100644 --- a/package/src/components/Poll/components/PollResults/PollResultItem.tsx +++ b/package/src/components/Poll/components/PollResults/PollResultItem.tsx @@ -16,9 +16,9 @@ export type PollResultItemProps< option: PollOption; }; -export const PollVote = (vote: PollVoteClass) => { +export const PollVote = ({ vote }: { vote: PollVoteClass }) => { const { t, tDateTimeParser } = useTranslationContext(); - const { voting_visibility } = usePollState(); + const { votingVisibility } = usePollState(); const { theme: { colors: { black, text_low_emphasis }, @@ -42,12 +42,12 @@ export const PollVote = (vote: PollVoteClass) => { ); const isAnonymous = useMemo( - () => voting_visibility === VotingVisibility.anonymous, - [voting_visibility], + () => votingVisibility === VotingVisibility.anonymous, + [votingVisibility], ); return ( - + {!isAnonymous && vote.user?.image ? ( @@ -61,9 +61,13 @@ export const PollVote = (vote: PollVoteClass) => { ); }; +const PollResultsVoteItem = (vote: PollVoteClass) => ( + +); + export const PollResultsItem = ({ option }: PollResultItemProps) => { const { t } = useTranslationContext(); - const { latest_votes_by_option, vote_counts_by_option } = usePollState(); + const { latestVotesByOption, voteCountsByOption } = usePollState(); const { theme: { @@ -81,12 +85,12 @@ export const PollResultsItem = ({ option }: PollResultItemProps) => { {option.text} - {t('{{count}} votes', { count: vote_counts_by_option[option.id] ?? 0 })} + {t('{{count}} votes', { count: voteCountsByOption[option.id] ?? 0 })} - {latest_votes_by_option?.[option.id]?.length > 0 ? ( + {latestVotesByOption?.[option.id]?.length > 0 ? ( - {(latest_votes_by_option?.[option.id] ?? []).slice(0, 5).map(PollVote)} + {(latestVotesByOption?.[option.id] ?? []).slice(0, 5).map(PollResultsVoteItem)} ) : null} diff --git a/package/src/components/Poll/components/PollResults/PollResults.tsx b/package/src/components/Poll/components/PollResults/PollResults.tsx index ee107f5f0..277812a7a 100644 --- a/package/src/components/Poll/components/PollResults/PollResults.tsx +++ b/package/src/components/Poll/components/PollResults/PollResults.tsx @@ -16,14 +16,14 @@ export type PollResultsProps = PollContextValue & { export const PollResultsContent = ({ additionalScrollViewProps, }: Pick) => { - const { name, options, vote_counts_by_option } = usePollState(); + const { name, options, voteCountsByOption } = usePollState(); const sortedOptions = useMemo( () => [...options].sort( - (a, b) => (vote_counts_by_option[b.id] ?? 0) - (vote_counts_by_option[a.id] ?? 0), + (a, b) => (voteCountsByOption[b.id] ?? 0) - (voteCountsByOption[a.id] ?? 0), ), - [vote_counts_by_option, options], + [voteCountsByOption, options], ); const { diff --git a/package/src/components/Poll/hooks/usePollOptionVotesPagination.ts b/package/src/components/Poll/hooks/usePollOptionVotesPagination.ts index c5cd557f5..4ef7b1226 100644 --- a/package/src/components/Poll/hooks/usePollOptionVotesPagination.ts +++ b/package/src/components/Poll/hooks/usePollOptionVotesPagination.ts @@ -22,7 +22,7 @@ export type UsePollVotesReturnType = { /** * A hook that queries votes for a given Poll and returns them in a paginated fashion. - * Should be used instead of the latest_votes_by_option property within the reactive state in the + * Should be used instead of the latestVotesByOption property within the reactive state in the * event that we need more than the top 10 votes for an option. The returned property votes will * automatically be updated and trigger a state change when paginating further. Querying for votes * can only be done on an option by option basis. diff --git a/package/src/components/Poll/hooks/usePollState.ts b/package/src/components/Poll/hooks/usePollState.ts index 162a59fe4..0c64c67c3 100644 --- a/package/src/components/Poll/hooks/usePollState.ts +++ b/package/src/components/Poll/hooks/usePollState.ts @@ -19,21 +19,21 @@ import { usePollContext } from '../../../contexts'; import { DefaultStreamChatGenerics } from '../../../types/types'; export type UsePollStateSelectorReturnType = { - allow_answers: boolean | undefined; - allow_user_suggested_options: boolean | undefined; - answers_count: number; - created_by: UserResponse | null; - enforce_unique_vote: boolean; - is_closed: boolean | undefined; - latest_votes_by_option: Record; - max_votes_allowed: number; + allowAnswers: boolean | undefined; + allowUserSuggestedOptions: boolean | undefined; + answersCount: number; + createdBy: UserResponse | null; + enforceUniqueVote: boolean; + isClosed: boolean | undefined; + latestVotesByOption: Record; maxVotedOptionIds: string[]; + maxVotesAllowed: number; name: string; options: PollOption[]; ownAnswer: PollAnswer | undefined; ownVotesByOptionId: Record; - vote_counts_by_option: Record; - voting_visibility: VotingVisibility | undefined; + voteCountsByOption: Record; + votingVisibility: VotingVisibility | undefined; }; export type UsePollStateReturnType< @@ -49,41 +49,41 @@ export type UsePollStateReturnType< const selector = ( nextValue: PollState, ): UsePollStateSelectorReturnType => ({ - allow_answers: nextValue.allow_answers, - allow_user_suggested_options: nextValue.allow_user_suggested_options, - answers_count: nextValue.answers_count, - created_by: nextValue.created_by, - enforce_unique_vote: nextValue.enforce_unique_vote, - is_closed: nextValue.is_closed, - latest_votes_by_option: nextValue.latest_votes_by_option, - max_votes_allowed: nextValue.max_votes_allowed, + allowAnswers: nextValue.allow_answers, + allowUserSuggestedOptions: nextValue.allow_user_suggested_options, + answersCount: nextValue.answers_count, + createdBy: nextValue.created_by, + enforceUniqueVote: nextValue.enforce_unique_vote, + isClosed: nextValue.is_closed, + latestVotesByOption: nextValue.latest_votes_by_option, maxVotedOptionIds: nextValue.maxVotedOptionIds, + maxVotesAllowed: nextValue.max_votes_allowed, name: nextValue.name, options: nextValue.options, ownAnswer: nextValue.ownAnswer, ownVotesByOptionId: nextValue.ownVotesByOptionId, - vote_counts_by_option: nextValue.vote_counts_by_option, - voting_visibility: nextValue.voting_visibility, + voteCountsByOption: nextValue.vote_counts_by_option, + votingVisibility: nextValue.voting_visibility, }); export const usePollState = (): UsePollStateReturnType => { const { message, poll } = usePollContext(); const { - allow_answers, - allow_user_suggested_options, - answers_count, - created_by, - enforce_unique_vote, - is_closed, - latest_votes_by_option, - max_votes_allowed, + allowAnswers, + allowUserSuggestedOptions, + answersCount, + createdBy, + enforceUniqueVote, + isClosed, + latestVotesByOption, maxVotedOptionIds, + maxVotesAllowed, name, options, ownAnswer, ownVotesByOptionId, - vote_counts_by_option, - voting_visibility, + voteCountsByOption, + votingVisibility, } = usePollStateStore(selector); const addOption = useCallback( @@ -102,21 +102,21 @@ export const usePollState = (): UsePollStateReturnType => { return { addComment, addOption, - allow_answers, - allow_user_suggested_options, - answers_count, - created_by, + allowAnswers, + allowUserSuggestedOptions, + answersCount, + createdBy, endVote, - enforce_unique_vote, - is_closed, - latest_votes_by_option, - max_votes_allowed, + enforceUniqueVote, + isClosed, + latestVotesByOption, maxVotedOptionIds, + maxVotesAllowed, name, options, ownAnswer, ownVotesByOptionId, - vote_counts_by_option, - voting_visibility, + voteCountsByOption, + votingVisibility, }; };