Giveaway: Creating giveaway in groups (#4442)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
97d3d31f10
commit
db1d7ceea0
@ -42,5 +42,5 @@
|
||||
}
|
||||
|
||||
.picker {
|
||||
height: 70vh;
|
||||
height: 75vh;
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo,
|
||||
useMemo,
|
||||
useState,
|
||||
memo, useMemo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
@ -10,7 +8,7 @@ import type { ApiChat, ApiChatMember, ApiUserStatus } from '../../api/types';
|
||||
|
||||
import {
|
||||
filterChatsByName,
|
||||
filterUsersByName, isChatChannel, isChatPublic, isUserBot, sortUserIds,
|
||||
filterUsersByName, isChatChannel, isChatPublic, isChatSuperGroup, isUserBot, sortUserIds,
|
||||
} from '../../global/helpers';
|
||||
import { selectChat, selectChatFullInfo } from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -45,6 +43,7 @@ interface StateProps {
|
||||
userStatusesById: Record<string, ApiUserStatus>;
|
||||
channelList?: (ApiChat | undefined)[] | undefined;
|
||||
isChannel?: boolean;
|
||||
isSuperGroup?: boolean;
|
||||
currentUserId?: string | undefined;
|
||||
}
|
||||
|
||||
@ -57,6 +56,7 @@ const AppendEntityPickerModal: FC<OwnProps & StateProps> = ({
|
||||
userStatusesById,
|
||||
entityType,
|
||||
isChannel,
|
||||
isSuperGroup,
|
||||
onSubmit,
|
||||
currentUserId,
|
||||
selectionLimit,
|
||||
@ -76,7 +76,8 @@ const AppendEntityPickerModal: FC<OwnProps & StateProps> = ({
|
||||
const activeChatIds = getGlobal().chats.listIds.active;
|
||||
|
||||
return activeChatIds!.map((id) => chatsById[id])
|
||||
.filter((chat) => chat && isChatChannel(chat) && chat.id !== chatId)
|
||||
.filter((chat) => chat && (isChatChannel(chat)
|
||||
|| isChatSuperGroup(chat)) && chat.id !== chatId)
|
||||
.map((chat) => chat!.id);
|
||||
}, [chatId]);
|
||||
|
||||
@ -123,11 +124,11 @@ const AppendEntityPickerModal: FC<OwnProps & StateProps> = ({
|
||||
return true;
|
||||
}
|
||||
|
||||
return isChannel;
|
||||
return isChannel || isSuperGroup;
|
||||
}),
|
||||
false,
|
||||
selectedChannelIds);
|
||||
}, [channelsIds, lang, searchQuery, selectedChannelIds, isChannel]);
|
||||
}, [channelsIds, lang, searchQuery, selectedChannelIds, isSuperGroup, isChannel]);
|
||||
|
||||
const handleCloseButtonClick = useLastCallback(() => {
|
||||
onSubmit([]);
|
||||
@ -248,6 +249,7 @@ const AppendEntityPickerModal: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>((global, { chatId, entityType }): StateProps => {
|
||||
const { statusesById: userStatusesById } = global.users;
|
||||
let isChannel;
|
||||
let isSuperGroup;
|
||||
let members: ApiChatMember[] | undefined;
|
||||
let adminMembersById: Record<string, ApiChatMember> | undefined;
|
||||
let currentUserId: string | undefined;
|
||||
@ -263,6 +265,7 @@ export default memo(withGlobal<OwnProps>((global, { chatId, entityType }): State
|
||||
const chat = chatId ? selectChat(global, chatId) : undefined;
|
||||
if (chat) {
|
||||
isChannel = isChatChannel(chat);
|
||||
isSuperGroup = isChatSuperGroup(chat);
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,6 +275,7 @@ export default memo(withGlobal<OwnProps>((global, { chatId, entityType }): State
|
||||
adminMembersById,
|
||||
userStatusesById,
|
||||
isChannel,
|
||||
isSuperGroup,
|
||||
currentUserId,
|
||||
};
|
||||
})(AppendEntityPickerModal));
|
||||
|
||||
@ -15,8 +15,9 @@ import {
|
||||
GIVEAWAY_MAX_ADDITIONAL_COUNTRIES,
|
||||
GIVEAWAY_MAX_ADDITIONAL_USERS,
|
||||
} from '../../../config';
|
||||
import { getUserFullName } from '../../../global/helpers';
|
||||
import { getUserFullName, isChatChannel } from '../../../global/helpers';
|
||||
import {
|
||||
selectChat,
|
||||
selectTabState,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -69,6 +70,7 @@ type StateProps = {
|
||||
countryList: ApiCountry[];
|
||||
prepaidGiveaway?: ApiPrepaidGiveaway;
|
||||
countrySelectionLimit: number | undefined;
|
||||
isChannel?: boolean;
|
||||
};
|
||||
|
||||
type GiveawayAction = 'createRandomlyUsers' | 'createSpecificUsers';
|
||||
@ -99,6 +101,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
chatId,
|
||||
gifts,
|
||||
isOpen,
|
||||
isChannel,
|
||||
selectedMemberList,
|
||||
selectedChannelList,
|
||||
giveawayBoostPerPremiumLimit = GIVEAWAY_BOOST_PER_PREMIUM,
|
||||
@ -160,19 +163,19 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
const SUBSCRIBER_OPTIONS = useMemo(() => [
|
||||
{
|
||||
value: 'all',
|
||||
label: lang('BoostingAllSubscribers'),
|
||||
label: lang(isChannel ? 'BoostingAllSubscribers' : 'BoostingAllMembers'),
|
||||
subLabel: selectedCountriesIds && selectedCountriesIds.length > 0
|
||||
? lang('Giveaway.ReceiverType.Countries', selectedCountriesIds.length)
|
||||
: lang('BoostingFromAllCountries'),
|
||||
},
|
||||
{
|
||||
value: 'new',
|
||||
label: lang('BoostingNewSubscribers'),
|
||||
label: lang(isChannel ? 'BoostingNewSubscribers' : 'BoostingNewMembers'),
|
||||
subLabel: selectedCountriesIds && selectedCountriesIds.length > 0
|
||||
? lang('Giveaway.ReceiverType.Countries', selectedCountriesIds.length)
|
||||
: lang('BoostingFromAllCountries'),
|
||||
},
|
||||
], [lang, selectedCountriesIds]);
|
||||
], [isChannel, lang, selectedCountriesIds]);
|
||||
|
||||
const monthQuantity = lang('Months', selectedMonthOption);
|
||||
|
||||
@ -448,7 +451,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
{renderText(lang('BoostingBoostsViaGifts'))}
|
||||
</h2>
|
||||
<div className={styles.description}>
|
||||
{renderText(lang('BoostingGetMoreBoost'))}
|
||||
{renderText(lang(isChannel ? 'BoostingGetMoreBoost' : 'BoostingGetMoreBoostsGroup'))}
|
||||
</div>
|
||||
<div className={buildClassName(styles.header, isHeaderHidden && styles.hiddenHeader)}>
|
||||
<h2 className={styles.premiumHeaderText}>
|
||||
@ -522,7 +525,8 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<GroupChatInfo
|
||||
chatId={chatId!}
|
||||
status={lang('BoostingChannelWillReceiveBoost', boostQuantity, 'i')}
|
||||
status={lang(isChannel ? 'BoostingChannelWillReceiveBoost'
|
||||
: 'BoostingGroupWillReceiveBoost', boostQuantity, 'i')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@ -551,7 +555,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
className={styles.addButton}
|
||||
iconClassName={styles.addChannel}
|
||||
>
|
||||
{lang('BoostingAddChannel')}
|
||||
{lang('BoostingAddChannelOrGroup')}
|
||||
</ListItem>
|
||||
)}
|
||||
</div>
|
||||
@ -565,7 +569,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
|
||||
<div className={styles.subscription}>
|
||||
{renderText(lang('BoostGift.LimitSubscribersInfo'))}
|
||||
{renderText(lang(isChannel ? 'BoostGift.LimitSubscribersInfo' : 'lng_giveaway_users_about_group'))}
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
@ -726,9 +730,12 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
const {
|
||||
giveawayModal,
|
||||
} = selectTabState(global);
|
||||
const chatId = giveawayModal?.chatId;
|
||||
const chat = chatId ? selectChat(global, chatId) : undefined;
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
|
||||
return {
|
||||
chatId: giveawayModal?.chatId,
|
||||
chatId,
|
||||
gifts: giveawayModal?.gifts,
|
||||
selectedMemberList: giveawayModal?.selectedMemberIds,
|
||||
selectedChannelList: giveawayModal?.selectedChannelIds,
|
||||
@ -737,5 +744,6 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
countrySelectionLimit: global.appConfig?.giveawayCountriesMax,
|
||||
countryList: global.countryList.general,
|
||||
prepaidGiveaway: giveawayModal?.prepaidGiveaway,
|
||||
isChannel,
|
||||
};
|
||||
})(GiveawayModal));
|
||||
|
||||
@ -500,7 +500,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const canCreateVoiceChat = ARE_CALLS_SUPPORTED && isMainThread && !chat.isCallActive
|
||||
&& (chat.adminRights?.manageCall || (chat.isCreator && isChatBasicGroup(chat)));
|
||||
const canViewStatistics = isMainThread && chatFullInfo?.canViewStatistics;
|
||||
const canViewBoosts = isMainThread && isChannel && (canViewStatistics || getHasAdminRight(chat, 'postStories'));
|
||||
const canViewBoosts = isMainThread
|
||||
&& (isSuperGroup || isChannel) && (canViewStatistics || getHasAdminRight(chat, 'postStories'));
|
||||
const canShowBoostModal = !canViewBoosts && (isSuperGroup || isChannel);
|
||||
const pendingJoinRequests = isMainThread ? chatFullInfo?.requestsPending : undefined;
|
||||
const shouldJoinToSend = Boolean(chat?.isNotJoined && chat.isJoinToSend);
|
||||
|
||||
@ -5,7 +5,8 @@ import type { ApiBoostStatistics, ApiPrepaidGiveaway } from '../../../api/types'
|
||||
import type { TabState } from '../../../global/types';
|
||||
|
||||
import { GIVEAWAY_BOOST_PER_PREMIUM } from '../../../config';
|
||||
import { selectIsGiveawayGiftsPurchaseAvailable, selectTabState } from '../../../global/selectors';
|
||||
import { isChatChannel } from '../../../global/helpers';
|
||||
import { selectChat, selectIsGiveawayGiftsPurchaseAvailable, selectTabState } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatDateAtTime } from '../../../util/date/dateFormat';
|
||||
import { getBoostProgressInfo } from '../../common/helpers/boostInfo';
|
||||
@ -33,6 +34,7 @@ type StateProps = {
|
||||
isGiveawayAvailable?: boolean;
|
||||
chatId: string;
|
||||
giveawayBoostsPerPremium?: number;
|
||||
isChannel?: boolean;
|
||||
};
|
||||
|
||||
const GIVEAWAY_IMG_LIST: { [key: number]: string } = {
|
||||
@ -46,6 +48,7 @@ const BoostStatistics = ({
|
||||
isGiveawayAvailable,
|
||||
chatId,
|
||||
giveawayBoostsPerPremium,
|
||||
isChannel,
|
||||
}: StateProps) => {
|
||||
const {
|
||||
openChat, loadMoreBoosters, closeBoostStatistics, openGiveawayModal,
|
||||
@ -162,51 +165,56 @@ const BoostStatistics = ({
|
||||
<p className="text-muted hint" key="links-hint">{lang('BoostingSelectPaidGiveaway')}</p>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.section}>
|
||||
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang('Boosters')}
|
||||
</h4>
|
||||
{!boostStatistics.boosterIds?.length && (
|
||||
<div className={styles.noResults}>{lang('NoBoostersHint')}</div>
|
||||
)}
|
||||
{boostStatistics.boosterIds?.map((userId) => (
|
||||
<ListItem
|
||||
key={userId}
|
||||
className="chat-item-clickable"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleBoosterClick(userId)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
className={styles.user}
|
||||
forceShowSelf
|
||||
userId={userId}
|
||||
status={lang('BoostExpireOn', formatDateAtTime(lang, boostStatistics.boosters![userId] * 1000))}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
{Boolean(boostersToLoadCount) && (
|
||||
<ListItem
|
||||
key="load-more"
|
||||
className={styles.showMore}
|
||||
disabled={boostStatistics?.isLoadingBoosters}
|
||||
onClick={handleLoadMore}
|
||||
>
|
||||
{boostStatistics?.isLoadingBoosters ? (
|
||||
<Spinner className={styles.loadMoreSpinner} />
|
||||
) : (
|
||||
<Icon name="down" className={styles.down} />
|
||||
)}
|
||||
{lang('ShowVotes', boostersToLoadCount)}
|
||||
</ListItem>
|
||||
)}
|
||||
</div>
|
||||
{isChannel && (
|
||||
<div className={styles.section}>
|
||||
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang('Boosters')}
|
||||
</h4>
|
||||
{!boostStatistics.boosterIds?.length && (
|
||||
<div className={styles.noResults}>{lang('NoBoostersHint')}</div>
|
||||
)}
|
||||
{boostStatistics.boosterIds?.map((userId) => (
|
||||
<ListItem
|
||||
key={userId}
|
||||
className="chat-item-clickable"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleBoosterClick(userId)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
className={styles.user}
|
||||
forceShowSelf
|
||||
userId={userId}
|
||||
status={lang('BoostExpireOn', formatDateAtTime(lang, boostStatistics.boosters![userId] * 1000))}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
{Boolean(boostersToLoadCount) && (
|
||||
<ListItem
|
||||
key="load-more"
|
||||
className={styles.showMore}
|
||||
disabled={boostStatistics?.isLoadingBoosters}
|
||||
onClick={handleLoadMore}
|
||||
>
|
||||
{boostStatistics?.isLoadingBoosters ? (
|
||||
<Spinner className={styles.loadMoreSpinner} />
|
||||
) : (
|
||||
<Icon name="down" className={styles.down} />
|
||||
)}
|
||||
{lang('ShowVotes', boostersToLoadCount)}
|
||||
</ListItem>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<LinkField className={styles.section} link={status!.boostUrl} withShare title={lang('LinkForBoosting')} />
|
||||
{isGiveawayAvailable && (
|
||||
<div className={styles.section}>
|
||||
<ListItem icon="gift" ripple onClick={handleGiveawayClick}>
|
||||
{lang('BoostingGetBoostsViaGifts')}
|
||||
</ListItem>
|
||||
<p className="text-muted hint" key="links-hint">{lang('BoostingGetMoreBoosts')}</p>
|
||||
<p className="text-muted hint" key="links-hint">{lang(
|
||||
isChannel ? 'BoostingGetMoreBoosts' : 'BoostingGetMoreBoostsGroup',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@ -221,6 +229,8 @@ export default memo(withGlobal(
|
||||
const boostStatistics = tabState.boostStatistics;
|
||||
const isGiveawayAvailable = selectIsGiveawayGiftsPurchaseAvailable(global);
|
||||
const chatId = boostStatistics && boostStatistics.chatId;
|
||||
const chat = chatId ? selectChat(global, chatId) : undefined;
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
const giveawayBoostsPerPremium = global.appConfig?.giveawayBoostsPerPremium;
|
||||
|
||||
return {
|
||||
@ -228,6 +238,7 @@ export default memo(withGlobal(
|
||||
isGiveawayAvailable,
|
||||
chatId: chatId!,
|
||||
giveawayBoostsPerPremium,
|
||||
isChannel,
|
||||
};
|
||||
},
|
||||
)(BoostStatistics));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user