Poll: Support date in vote (#6760)

This commit is contained in:
Alexander Zinchuk 2026-03-31 11:28:46 +02:00
parent aa44302ed4
commit ac06cb6e80
5 changed files with 50 additions and 28 deletions

View File

@ -12,7 +12,10 @@
display: flex;
align-items: center;
padding: 1rem 0.75rem 0.5rem 1rem;
padding-top: 1rem;
padding-bottom: 0.5rem;
padding-inline-start: 1rem;
padding-inline-end: 0.75rem;
font-size: 0.9375rem;
font-weight: var(--font-weight-medium);
@ -38,7 +41,6 @@
.poll-voters {
position: relative;
min-height: 3rem;
padding: 0 0.75rem;
.Spinner {
--spinner-size: 1.25rem;
@ -50,6 +52,11 @@
}
.chat-item-clickable {
.ListItem-button {
padding-inline-start: 1rem;
padding-inline-end: 0.75rem;
}
.ChatInfo .Avatar.size-tiny {
margin-right: 1.75rem;
}
@ -60,6 +67,11 @@
margin-left: 1.75rem;
}
}
.vote-date {
font-size: 0.875rem;
color: var(--color-text-secondary);
}
}
.ShowMoreButton {

View File

@ -11,8 +11,10 @@ import type {
ApiPollAnswer,
ApiPollResult,
} from '../../api/types';
import type { PollVote } from '../../global/types/tabState';
import { selectTabState } from '../../global/selectors';
import { formatMediaDateTime } from '../../util/dates/dateFormat';
import { isUserId } from '../../util/entities/ids';
import { renderTextWithEntities } from '../common/helpers/renderTextWithEntities';
@ -36,7 +38,7 @@ type OwnProps = {
};
type StateProps = {
voters?: string[];
votes?: PollVote[];
offset: string;
};
@ -49,7 +51,7 @@ const PollAnswerResults: FC<OwnProps & StateProps> = ({
answer,
answerVote,
totalVoters,
voters,
votes,
offset,
}) => {
const {
@ -60,7 +62,7 @@ const PollAnswerResults: FC<OwnProps & StateProps> = ({
const prevVotersCount = usePreviousDeprecated<number>(answerVote.votersCount);
const [isLoading, setIsLoading] = useState<boolean>(true);
const areVotersLoaded = Boolean(voters);
const areVotersLoaded = Boolean(votes);
const { option, text } = answer;
const lang = useOldLang();
@ -83,7 +85,7 @@ const PollAnswerResults: FC<OwnProps & StateProps> = ({
useEffect(() => {
setIsLoading(false);
}, [voters]);
}, [votes]);
const handleMemberClick = useCallback((id: string) => {
openChat({ id });
@ -91,7 +93,7 @@ const PollAnswerResults: FC<OwnProps & StateProps> = ({
}, [closePollResults, openChat]);
function renderViewMoreButton() {
const leftVotersCount = answerVote.votersCount - voters!.length;
const leftVotersCount = answerVote.votersCount - votes!.length;
return answerVote.votersCount > INITIAL_LIMIT && leftVotersCount > 0 && (
<ShowMoreButton
@ -106,32 +108,34 @@ const PollAnswerResults: FC<OwnProps & StateProps> = ({
return (
<div className="PollAnswerResults">
<div className="poll-voters">
{voters
? voters.map((id) => (
{votes
? votes.map(({ peerId, date }) => (
<ListItem
key={id}
key={peerId}
className="chat-item-clickable"
onClick={() => handleMemberClick(id)}
onClick={() => handleMemberClick(peerId)}
>
{isUserId(id) ? (
{isUserId(peerId) ? (
<PrivateChatInfo
avatarSize="tiny"
userId={id}
userId={peerId}
forceShowSelf
noStatusOrTyping
/>
) : (
<GroupChatInfo
avatarSize="tiny"
chatId={id}
chatId={peerId}
noStatusOrTyping
/>
)}
<span className="vote-date">
{formatMediaDateTime(lang, date * 1000, true)}
</span>
</ListItem>
))
: <Loading />}
{voters && renderViewMoreButton()}
{votes && renderViewMoreButton()}
</div>
<div className="answer-head" dir={lang.isRtl ? 'rtl' : undefined}>
<span className="answer-title" dir="auto">
@ -155,10 +159,10 @@ function getPercentage(value: number, total: number) {
export default memo(withGlobal<OwnProps>(
(global, { answer }: OwnProps): Complete<StateProps> => {
const { voters, offsets } = selectTabState(global).pollResults;
const { votesByOption, offsets } = selectTabState(global).pollResults;
return {
voters: voters?.[answer.option],
votes: votesByOption?.[answer.option],
offset: (offsets?.[answer.option]) || '',
};
},

View File

@ -52,6 +52,7 @@ import {
partition,
split,
unique,
uniqueByField,
} from '../../../util/iteratees';
import { getMessageKey, isLocalMessageId } from '../../../util/keys/messageKey';
import { getTranslationFn, type RegularLangFnParameters } from '../../../util/localization';
@ -1477,17 +1478,17 @@ addActionHandler('loadPollOptionResults', async (global, actions, payload): Prom
const tabState = selectTabState(global, tabId);
const { pollResults } = tabState;
const { voters } = tabState.pollResults;
const { votesByOption } = pollResults;
const existingVotes = !shouldResetVoters && votesByOption?.[option] ? votesByOption[option] : [];
const newVotes = uniqueByField([...existingVotes, ...result.votes], 'peerId');
global = updateTabState(global, {
pollResults: {
...pollResults,
voters: {
...voters,
[option]: unique([
...(!shouldResetVoters && voters?.[option] ? voters[option] : []),
...result.votes.map((vote) => vote.peerId),
]),
votesByOption: {
...votesByOption,
[option]: newVotes,
},
offsets: {
...(pollResults.offsets ? pollResults.offsets : {}),

View File

@ -297,7 +297,7 @@ addActionHandler('openPollResults', (global, actions, payload): ActionReturnType
pollResults: {
chatId,
messageId,
voters: {},
votesByOption: {},
},
}, tabId);
setGlobal(global);
@ -307,7 +307,7 @@ addActionHandler('openPollResults', (global, actions, payload): ActionReturnType
pollResults: {
chatId,
messageId,
voters: {},
votesByOption: {},
},
}, tabId);
}

View File

@ -102,6 +102,11 @@ import type { RegularLangFnParameters } from '../../util/localization';
import type { ProfileCollectionKey } from '../selectors/payments';
import type { CallbackAction } from './actions';
export type PollVote = {
peerId: string;
date: number;
};
export type TabState = {
id: number;
isBlurred?: boolean;
@ -418,7 +423,7 @@ export type TabState = {
pollResults: {
chatId?: string;
messageId?: number;
voters?: Record<string, string[]>; // TODO Rename to `voterIds`
votesByOption?: Record<string, PollVote[]>;
offsets?: Record<string, string>;
};