Profile: GIFs & Main Tab (#6336)
This commit is contained in:
parent
1be16bf765
commit
5e679218e2
@ -5,7 +5,7 @@ module.exports = {
|
|||||||
fontTypes: ['woff2', 'woff'],
|
fontTypes: ['woff2', 'woff'],
|
||||||
assetTypes: ['scss', 'ts'],
|
assetTypes: ['scss', 'ts'],
|
||||||
tag: '',
|
tag: '',
|
||||||
// Use a custom Handlebars template
|
normalize: true,
|
||||||
templates: {
|
templates: {
|
||||||
scss: './dev/icons.scss.hbs',
|
scss: './dev/icons.scss.hbs',
|
||||||
},
|
},
|
||||||
|
|||||||
2
src/@types/global.d.ts
vendored
2
src/@types/global.d.ts
vendored
@ -48,6 +48,8 @@ type AnyToVoidFunction = (...args: any[]) => void;
|
|||||||
type BooleanToVoidFunction = (value: boolean) => void;
|
type BooleanToVoidFunction = (value: boolean) => void;
|
||||||
type NoneToVoidFunction = () => void;
|
type NoneToVoidFunction = () => void;
|
||||||
|
|
||||||
|
type StringAutocomplete<T> = T | (string & {});
|
||||||
|
|
||||||
type Complete<T> = {
|
type Complete<T> = {
|
||||||
[P in keyof Required<T>]: Pick<T, P> extends Required<Pick<T, P>> ? T[P] : (T[P] | undefined);
|
[P in keyof Required<T>]: Pick<T, P> extends Required<Pick<T, P>> ? T[P] : (T[P] | undefined);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,14 +29,17 @@ import { getServerTimeOffset } from '../../../util/serverTime';
|
|||||||
import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers/localDb';
|
import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers/localDb';
|
||||||
import { serializeBytes } from '../helpers/misc';
|
import { serializeBytes } from '../helpers/misc';
|
||||||
import {
|
import {
|
||||||
buildApiBotVerification, buildApiFormattedText, buildApiPhoto, buildApiUsernames, buildAvatarPhotoId,
|
buildApiFormattedText, buildApiPhoto, buildApiUsernames,
|
||||||
} from './common';
|
} from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiPeerNotifySettings, buildApiRestrictionReasons } from './misc';
|
import { buildApiRestrictionReasons } from './misc';
|
||||||
import {
|
import {
|
||||||
|
buildApiBotVerification,
|
||||||
buildApiEmojiStatus,
|
buildApiEmojiStatus,
|
||||||
buildApiPeerColor,
|
buildApiPeerColor,
|
||||||
buildApiPeerId,
|
buildApiPeerId,
|
||||||
|
buildApiPeerNotifySettings,
|
||||||
|
buildAvatarPhotoId,
|
||||||
getApiChatIdFromMtpPeer,
|
getApiChatIdFromMtpPeer,
|
||||||
isMtpPeerChat,
|
isMtpPeerChat,
|
||||||
isMtpPeerUser,
|
isMtpPeerUser,
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import type { Entity } from '../../../lib/gramjs/types';
|
|||||||
import { strippedPhotoToJpg } from '../../../lib/gramjs/Utils';
|
import { strippedPhotoToJpg } from '../../../lib/gramjs/Utils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiBotVerification,
|
|
||||||
ApiFormattedText,
|
ApiFormattedText,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiMessageEntityDefault,
|
ApiMessageEntityDefault,
|
||||||
@ -293,19 +292,3 @@ export function buildApiMessageEntity(entity: GramJs.TypeMessageEntity): ApiMess
|
|||||||
length,
|
length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildAvatarPhotoId(photo: GramJs.TypeUserProfilePhoto | GramJs.TypeChatPhoto) {
|
|
||||||
if ('photoId' in photo) {
|
|
||||||
return photo.photoId.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildApiBotVerification(botVerification: GramJs.BotVerification): ApiBotVerification {
|
|
||||||
return {
|
|
||||||
botId: buildApiPeerId(botVerification.botId, 'user'),
|
|
||||||
iconId: botVerification.icon.toString(),
|
|
||||||
description: botVerification.description,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@ -7,9 +7,6 @@ import type {
|
|||||||
ApiCountry,
|
ApiCountry,
|
||||||
ApiLanguage,
|
ApiLanguage,
|
||||||
ApiOldLangString,
|
ApiOldLangString,
|
||||||
ApiPeerColors,
|
|
||||||
ApiPeerNotifySettings,
|
|
||||||
ApiPeerProfileColorSet,
|
|
||||||
ApiPrivacyKey,
|
ApiPrivacyKey,
|
||||||
ApiRestrictionReason,
|
ApiRestrictionReason,
|
||||||
ApiSession,
|
ApiSession,
|
||||||
@ -20,9 +17,8 @@ import type {
|
|||||||
LangPackStringValue,
|
LangPackStringValue,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { numberToHexColor } from '../../../util/colors';
|
|
||||||
import {
|
import {
|
||||||
buildCollectionByCallback, omit, omitUndefined, pick,
|
omit, omitUndefined, pick,
|
||||||
} from '../../../util/iteratees';
|
} from '../../../util/iteratees';
|
||||||
import { toJSNumber } from '../../../util/numbers';
|
import { toJSNumber } from '../../../util/numbers';
|
||||||
import { addUserToLocalDb } from '../helpers/localDb';
|
import { addUserToLocalDb } from '../helpers/localDb';
|
||||||
@ -111,23 +107,6 @@ export function buildPrivacyKey(key: GramJs.TypePrivacyKey): ApiPrivacyKey | und
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiPeerNotifySettings(
|
|
||||||
notifySettings: GramJs.TypePeerNotifySettings,
|
|
||||||
): ApiPeerNotifySettings {
|
|
||||||
const {
|
|
||||||
silent, muteUntil, showPreviews, otherSound,
|
|
||||||
} = notifySettings;
|
|
||||||
|
|
||||||
const hasSound = !(otherSound instanceof GramJs.NotificationSoundNone);
|
|
||||||
|
|
||||||
return {
|
|
||||||
hasSound,
|
|
||||||
isSilentPosting: silent,
|
|
||||||
mutedUntil: muteUntil,
|
|
||||||
shouldShowPreviews: showPreviews,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildApiCountry(country: GramJs.help.Country, code: GramJs.help.CountryCode) {
|
function buildApiCountry(country: GramJs.help.Country, code: GramJs.help.CountryCode) {
|
||||||
const {
|
const {
|
||||||
hidden, iso2, defaultName, name,
|
hidden, iso2, defaultName, name,
|
||||||
@ -295,46 +274,6 @@ export function buildApiLanguage(lang: GramJs.TypeLangPackLanguage): ApiLanguage
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildApiPeerColorSet(colorSet: GramJs.help.PeerColorSet) {
|
|
||||||
return colorSet.colors.map((color) => numberToHexColor(color));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildApiPeerProfileColorSet(colorSet: GramJs.help.PeerColorProfileSet): ApiPeerProfileColorSet {
|
|
||||||
return {
|
|
||||||
paletteColors: colorSet.paletteColors.map((color) => numberToHexColor(color)),
|
|
||||||
bgColors: colorSet.bgColors.map((color) => numberToHexColor(color)),
|
|
||||||
storyColors: colorSet.storyColors.map((color) => numberToHexColor(color)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildApiPeerColors(wrapper: GramJs.help.TypePeerColors): ApiPeerColors['general'] | undefined {
|
|
||||||
if (!(wrapper instanceof GramJs.help.PeerColors)) return undefined;
|
|
||||||
|
|
||||||
return buildCollectionByCallback(wrapper.colors, (color) => {
|
|
||||||
return [color.colorId, {
|
|
||||||
isHidden: color.hidden,
|
|
||||||
colors: color.colors instanceof GramJs.help.PeerColorSet
|
|
||||||
? buildApiPeerColorSet(color.colors) : undefined,
|
|
||||||
darkColors: color.darkColors instanceof GramJs.help.PeerColorSet
|
|
||||||
? buildApiPeerColorSet(color.darkColors) : undefined,
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildApiPeerProfileColors(wrapper: GramJs.help.TypePeerColors): ApiPeerColors['profile'] | undefined {
|
|
||||||
if (!(wrapper instanceof GramJs.help.PeerColors)) return undefined;
|
|
||||||
|
|
||||||
return buildCollectionByCallback(wrapper.colors, (color) => {
|
|
||||||
return [color.colorId, {
|
|
||||||
isHidden: color.hidden,
|
|
||||||
colors: color.colors instanceof GramJs.help.PeerColorProfileSet
|
|
||||||
? buildApiPeerProfileColorSet(color.colors) : undefined,
|
|
||||||
darkColors: color.darkColors instanceof GramJs.help.PeerColorProfileSet
|
|
||||||
? buildApiPeerProfileColorSet(color.darkColors) : undefined,
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildApiTimezone(timezone: GramJs.TypeTimezone): ApiTimezone {
|
export function buildApiTimezone(timezone: GramJs.TypeTimezone): ApiTimezone {
|
||||||
const { id, name, utcOffset } = timezone;
|
const { id, name, utcOffset } = timezone;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,9 +1,18 @@
|
|||||||
import { Api as GramJs } from '../../../lib/gramjs';
|
import { Api as GramJs } from '../../../lib/gramjs';
|
||||||
|
|
||||||
import type { ApiEmojiStatusType, ApiPeerColor } from '../../types';
|
import type {
|
||||||
|
ApiBotVerification,
|
||||||
|
ApiEmojiStatusType,
|
||||||
|
ApiPeerColor,
|
||||||
|
ApiPeerColors,
|
||||||
|
ApiPeerNotifySettings,
|
||||||
|
ApiPeerProfileColorSet,
|
||||||
|
ApiProfileTab,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
import { CHANNEL_ID_BASE } from '../../../config';
|
import { CHANNEL_ID_BASE } from '../../../config';
|
||||||
import { numberToHexColor } from '../../../util/colors';
|
import { numberToHexColor } from '../../../util/colors';
|
||||||
|
import { buildCollectionByCallback } from '../../../util/iteratees';
|
||||||
|
|
||||||
type TypePeerOrInput = GramJs.TypePeer | GramJs.TypeInputPeer | GramJs.TypeInputUser | GramJs.TypeInputChannel;
|
type TypePeerOrInput = GramJs.TypePeer | GramJs.TypeInputPeer | GramJs.TypeInputUser | GramJs.TypeInputChannel;
|
||||||
|
|
||||||
@ -49,6 +58,46 @@ export function buildApiPeerColor(peerColor: GramJs.TypePeerColor): ApiPeerColor
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildApiPeerColorSet(colorSet: GramJs.help.PeerColorSet) {
|
||||||
|
return colorSet.colors.map((color) => numberToHexColor(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildApiPeerProfileColorSet(colorSet: GramJs.help.PeerColorProfileSet): ApiPeerProfileColorSet {
|
||||||
|
return {
|
||||||
|
paletteColors: colorSet.paletteColors.map((color) => numberToHexColor(color)),
|
||||||
|
bgColors: colorSet.bgColors.map((color) => numberToHexColor(color)),
|
||||||
|
storyColors: colorSet.storyColors.map((color) => numberToHexColor(color)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiPeerColors(wrapper: GramJs.help.TypePeerColors): ApiPeerColors['general'] | undefined {
|
||||||
|
if (!(wrapper instanceof GramJs.help.PeerColors)) return undefined;
|
||||||
|
|
||||||
|
return buildCollectionByCallback(wrapper.colors, (color) => {
|
||||||
|
return [color.colorId, {
|
||||||
|
isHidden: color.hidden,
|
||||||
|
colors: color.colors instanceof GramJs.help.PeerColorSet
|
||||||
|
? buildApiPeerColorSet(color.colors) : undefined,
|
||||||
|
darkColors: color.darkColors instanceof GramJs.help.PeerColorSet
|
||||||
|
? buildApiPeerColorSet(color.darkColors) : undefined,
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiPeerProfileColors(wrapper: GramJs.help.TypePeerColors): ApiPeerColors['profile'] | undefined {
|
||||||
|
if (!(wrapper instanceof GramJs.help.PeerColors)) return undefined;
|
||||||
|
|
||||||
|
return buildCollectionByCallback(wrapper.colors, (color) => {
|
||||||
|
return [color.colorId, {
|
||||||
|
isHidden: color.hidden,
|
||||||
|
colors: color.colors instanceof GramJs.help.PeerColorProfileSet
|
||||||
|
? buildApiPeerProfileColorSet(color.colors) : undefined,
|
||||||
|
darkColors: color.darkColors instanceof GramJs.help.PeerColorProfileSet
|
||||||
|
? buildApiPeerProfileColorSet(color.darkColors) : undefined,
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function buildApiEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus):
|
export function buildApiEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus):
|
||||||
ApiEmojiStatusType | undefined {
|
ApiEmojiStatusType | undefined {
|
||||||
if (mtpEmojiStatus instanceof GramJs.EmojiStatus) {
|
if (mtpEmojiStatus instanceof GramJs.EmojiStatus) {
|
||||||
@ -77,3 +126,61 @@ ApiEmojiStatusType | undefined {
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildAvatarPhotoId(photo: GramJs.TypeUserProfilePhoto | GramJs.TypeChatPhoto) {
|
||||||
|
if ('photoId' in photo) {
|
||||||
|
return photo.photoId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiBotVerification(botVerification: GramJs.BotVerification): ApiBotVerification {
|
||||||
|
return {
|
||||||
|
botId: buildApiPeerId(botVerification.botId, 'user'),
|
||||||
|
iconId: botVerification.icon.toString(),
|
||||||
|
description: botVerification.description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiPeerNotifySettings(
|
||||||
|
notifySettings: GramJs.TypePeerNotifySettings,
|
||||||
|
): ApiPeerNotifySettings {
|
||||||
|
const {
|
||||||
|
silent, muteUntil, showPreviews, otherSound,
|
||||||
|
} = notifySettings;
|
||||||
|
|
||||||
|
const hasSound = !(otherSound instanceof GramJs.NotificationSoundNone);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasSound,
|
||||||
|
isSilentPosting: silent,
|
||||||
|
mutedUntil: muteUntil,
|
||||||
|
shouldShowPreviews: showPreviews,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiProfileTab(profileTab: GramJs.TypeProfileTab): ApiProfileTab {
|
||||||
|
switch (profileTab.className) {
|
||||||
|
case 'ProfileTabPosts':
|
||||||
|
return 'stories';
|
||||||
|
case 'ProfileTabGifts':
|
||||||
|
return 'gifts';
|
||||||
|
case 'ProfileTabMedia':
|
||||||
|
return 'media';
|
||||||
|
case 'ProfileTabFiles':
|
||||||
|
return 'documents';
|
||||||
|
case 'ProfileTabMusic':
|
||||||
|
return 'audio';
|
||||||
|
case 'ProfileTabVoice':
|
||||||
|
return 'voice';
|
||||||
|
case 'ProfileTabLinks':
|
||||||
|
return 'links';
|
||||||
|
case 'ProfileTabGifs':
|
||||||
|
return 'gif';
|
||||||
|
default: {
|
||||||
|
const _exhaustiveCheck: never = profileTab;
|
||||||
|
return _exhaustiveCheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -16,9 +16,9 @@ import type {
|
|||||||
TypeStatisticsGraph,
|
TypeStatisticsGraph,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { buildApiUsernames, buildAvatarPhotoId } from './common';
|
import { buildApiUsernames } from './common';
|
||||||
import { buildApiCurrencyAmount } from './payments';
|
import { buildApiCurrencyAmount } from './payments';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
import { buildApiPeerId, buildAvatarPhotoId, getApiChatIdFromMtpPeer } from './peers';
|
||||||
|
|
||||||
export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiChannelStatistics {
|
export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiChannelStatistics {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -14,11 +14,18 @@ import { toJSNumber } from '../../../util/numbers';
|
|||||||
import { buildApiBotInfo } from './bots';
|
import { buildApiBotInfo } from './bots';
|
||||||
import { buildApiBusinessIntro, buildApiBusinessLocation, buildApiBusinessWorkHours } from './business';
|
import { buildApiBusinessIntro, buildApiBusinessLocation, buildApiBusinessWorkHours } from './business';
|
||||||
import {
|
import {
|
||||||
buildApiBotVerification, buildApiPhoto, buildApiUsernames, buildAvatarPhotoId,
|
buildApiPhoto, buildApiUsernames,
|
||||||
} from './common';
|
} from './common';
|
||||||
import { buildApiDisallowedGiftsSettings } from './gifts';
|
import { buildApiDisallowedGiftsSettings } from './gifts';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiEmojiStatus, buildApiPeerColor, buildApiPeerId } from './peers';
|
import {
|
||||||
|
buildApiBotVerification,
|
||||||
|
buildApiEmojiStatus,
|
||||||
|
buildApiPeerColor,
|
||||||
|
buildApiPeerId,
|
||||||
|
buildApiProfileTab,
|
||||||
|
buildAvatarPhotoId,
|
||||||
|
} from './peers';
|
||||||
|
|
||||||
export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUserFullInfo {
|
export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUserFullInfo {
|
||||||
const {
|
const {
|
||||||
@ -29,7 +36,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
|
|||||||
contactRequirePremium, businessWorkHours, businessLocation, businessIntro,
|
contactRequirePremium, businessWorkHours, businessLocation, businessIntro,
|
||||||
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount, botVerification,
|
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount, botVerification,
|
||||||
botCanManageEmojiStatus, settings, sendPaidMessagesStars, displayGiftsButton, disallowedGifts,
|
botCanManageEmojiStatus, settings, sendPaidMessagesStars, displayGiftsButton, disallowedGifts,
|
||||||
starsRating, starsMyPendingRating, starsMyPendingRatingDate,
|
starsRating, starsMyPendingRating, starsMyPendingRatingDate, mainTab,
|
||||||
},
|
},
|
||||||
users,
|
users,
|
||||||
} = mtpUserFull;
|
} = mtpUserFull;
|
||||||
@ -68,6 +75,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
|
|||||||
hasScheduledMessages: hasScheduled,
|
hasScheduledMessages: hasScheduled,
|
||||||
paidMessagesStars: toJSNumber(sendPaidMessagesStars),
|
paidMessagesStars: toJSNumber(sendPaidMessagesStars),
|
||||||
settings: buildApiPeerSettings(settings),
|
settings: buildApiPeerSettings(settings),
|
||||||
|
mainTab: mainTab && buildApiProfileTab(mainTab),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import type {
|
|||||||
ApiPoll,
|
ApiPoll,
|
||||||
ApiPremiumGiftCodeOption,
|
ApiPremiumGiftCodeOption,
|
||||||
ApiPrivacyKey,
|
ApiPrivacyKey,
|
||||||
|
ApiProfileTab,
|
||||||
ApiReactionWithPaid,
|
ApiReactionWithPaid,
|
||||||
ApiReportReason,
|
ApiReportReason,
|
||||||
ApiRequestInputInvoice,
|
ApiRequestInputInvoice,
|
||||||
@ -980,3 +981,28 @@ export function buildInputSavedStarGift(inputGift: ApiRequestInputSavedStarGift)
|
|||||||
savedId: BigInt(inputGift.savedId),
|
savedId: BigInt(inputGift.savedId),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildInputProfileTab(profileTab: ApiProfileTab) {
|
||||||
|
switch (profileTab) {
|
||||||
|
case 'stories':
|
||||||
|
return new GramJs.ProfileTabPosts();
|
||||||
|
case 'gifts':
|
||||||
|
return new GramJs.ProfileTabGifts();
|
||||||
|
case 'media':
|
||||||
|
return new GramJs.ProfileTabMedia();
|
||||||
|
case 'documents':
|
||||||
|
return new GramJs.ProfileTabFiles();
|
||||||
|
case 'audio':
|
||||||
|
return new GramJs.ProfileTabMusic();
|
||||||
|
case 'voice':
|
||||||
|
return new GramJs.ProfileTabVoice();
|
||||||
|
case 'links':
|
||||||
|
return new GramJs.ProfileTabLinks();
|
||||||
|
case 'gif':
|
||||||
|
return new GramJs.ProfileTabGifs();
|
||||||
|
default: {
|
||||||
|
const _exhaustiveCheck: never = profileTab;
|
||||||
|
return _exhaustiveCheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
import { Api as GramJs } from '../../../lib/gramjs';
|
import { Api as GramJs } from '../../../lib/gramjs';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiPeer, ApiPhoto, ApiReportReason,
|
ApiPeer, ApiPhoto, ApiProfileTab, ApiReportReason,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { buildApiChatLink } from '../apiBuilders/misc';
|
import { buildApiChatLink } from '../apiBuilders/misc';
|
||||||
import { buildInputPeer, buildInputPhoto, buildInputReportReason, DEFAULT_PRIMITIVES } from '../gramjsBuilders';
|
import {
|
||||||
|
buildInputPeer,
|
||||||
|
buildInputPhoto,
|
||||||
|
buildInputProfileTab,
|
||||||
|
buildInputReportReason,
|
||||||
|
DEFAULT_PRIMITIVES,
|
||||||
|
} from '../gramjsBuilders';
|
||||||
import { invokeRequest } from './client';
|
import { invokeRequest } from './client';
|
||||||
|
|
||||||
export async function reportPeer({
|
export async function reportPeer({
|
||||||
@ -124,3 +130,11 @@ export function setAccountTTL({ days }: { days: number }) {
|
|||||||
shouldReturnTrue: true,
|
shouldReturnTrue: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setAccountMainProfileTab({ tab }: { tab: ApiProfileTab }) {
|
||||||
|
return invokeRequest(new GramJs.account.SetMainProfileTab({
|
||||||
|
tab: buildInputProfileTab(tab),
|
||||||
|
}), {
|
||||||
|
shouldReturnTrue: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import type {
|
|||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiPeerNotifySettings,
|
ApiPeerNotifySettings,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
|
ApiProfileTab,
|
||||||
ApiTopic,
|
ApiTopic,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
ApiUserStatus,
|
ApiUserStatus,
|
||||||
@ -51,10 +52,15 @@ import {
|
|||||||
buildChatMembers,
|
buildChatMembers,
|
||||||
getPeerKey,
|
getPeerKey,
|
||||||
} from '../apiBuilders/chats';
|
} from '../apiBuilders/chats';
|
||||||
import { buildApiBotVerification, buildApiPhoto } from '../apiBuilders/common';
|
import { buildApiPhoto } from '../apiBuilders/common';
|
||||||
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
|
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
|
||||||
import { buildApiPeerNotifySettings } from '../apiBuilders/misc';
|
import {
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
buildApiBotVerification,
|
||||||
|
buildApiPeerId,
|
||||||
|
buildApiPeerNotifySettings,
|
||||||
|
buildApiProfileTab,
|
||||||
|
getApiChatIdFromMtpPeer,
|
||||||
|
} from '../apiBuilders/peers';
|
||||||
import { buildStickerSet } from '../apiBuilders/symbols';
|
import { buildStickerSet } from '../apiBuilders/symbols';
|
||||||
import { buildApiPeerSettings, buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
import { buildApiPeerSettings, buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
||||||
import {
|
import {
|
||||||
@ -66,6 +72,7 @@ import {
|
|||||||
buildInputChatReactions,
|
buildInputChatReactions,
|
||||||
buildInputPeer,
|
buildInputPeer,
|
||||||
buildInputPhoto,
|
buildInputPhoto,
|
||||||
|
buildInputProfileTab,
|
||||||
buildInputReplyTo,
|
buildInputReplyTo,
|
||||||
buildInputSuggestedPost,
|
buildInputSuggestedPost,
|
||||||
buildInputUser,
|
buildInputUser,
|
||||||
@ -654,6 +661,7 @@ async function getFullChannelInfo(
|
|||||||
stargiftsCount,
|
stargiftsCount,
|
||||||
stargiftsAvailable,
|
stargiftsAvailable,
|
||||||
paidMessagesAvailable,
|
paidMessagesAvailable,
|
||||||
|
mainTab,
|
||||||
} = result.fullChat;
|
} = result.fullChat;
|
||||||
|
|
||||||
if (chatPhoto) {
|
if (chatPhoto) {
|
||||||
@ -753,6 +761,7 @@ async function getFullChannelInfo(
|
|||||||
starGiftCount: stargiftsCount,
|
starGiftCount: stargiftsCount,
|
||||||
areStarGiftsAvailable: Boolean(stargiftsAvailable),
|
areStarGiftsAvailable: Boolean(stargiftsAvailable),
|
||||||
arePaidMessagesAvailable: paidMessagesAvailable,
|
arePaidMessagesAvailable: paidMessagesAvailable,
|
||||||
|
mainTab: mainTab && buildApiProfileTab(mainTab),
|
||||||
},
|
},
|
||||||
chats,
|
chats,
|
||||||
userStatusesById: statusesById,
|
userStatusesById: statusesById,
|
||||||
@ -2092,3 +2101,12 @@ export function toggleAutoTranslation({
|
|||||||
shouldReturnTrue: true,
|
shouldReturnTrue: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setChannelMainProfileTab({ chat, tab }: { chat: ApiChat; tab: ApiProfileTab }) {
|
||||||
|
return invokeRequest(new GramJs.channels.SetMainProfileTab({
|
||||||
|
channel: buildInputChannel(chat.id, chat.accessHash),
|
||||||
|
tab: buildInputProfileTab(tab),
|
||||||
|
}), {
|
||||||
|
shouldReturnTrue: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -1494,6 +1494,9 @@ export async function searchMessagesInChat({
|
|||||||
case 'profilePhoto':
|
case 'profilePhoto':
|
||||||
filter = new GramJs.InputMessagesFilterChatPhotos();
|
filter = new GramJs.InputMessagesFilterChatPhotos();
|
||||||
break;
|
break;
|
||||||
|
case 'gif':
|
||||||
|
filter = new GramJs.InputMessagesFilterGif();
|
||||||
|
break;
|
||||||
case 'text':
|
case 'text':
|
||||||
default: {
|
default: {
|
||||||
filter = new GramJs.InputMessagesFilterEmpty();
|
filter = new GramJs.InputMessagesFilterEmpty();
|
||||||
|
|||||||
@ -31,9 +31,6 @@ import {
|
|||||||
buildApiConfig,
|
buildApiConfig,
|
||||||
buildApiCountryList,
|
buildApiCountryList,
|
||||||
buildApiLanguage,
|
buildApiLanguage,
|
||||||
buildApiPeerColors,
|
|
||||||
buildApiPeerNotifySettings,
|
|
||||||
buildApiPeerProfileColors,
|
|
||||||
buildApiSession,
|
buildApiSession,
|
||||||
buildApiTimezone,
|
buildApiTimezone,
|
||||||
buildApiWallpaper,
|
buildApiWallpaper,
|
||||||
@ -41,7 +38,12 @@ import {
|
|||||||
buildLangStrings,
|
buildLangStrings,
|
||||||
oldBuildLangPack,
|
oldBuildLangPack,
|
||||||
} from '../apiBuilders/misc';
|
} from '../apiBuilders/misc';
|
||||||
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import {
|
||||||
|
buildApiPeerColors,
|
||||||
|
buildApiPeerNotifySettings,
|
||||||
|
buildApiPeerProfileColors,
|
||||||
|
getApiChatIdFromMtpPeer,
|
||||||
|
} from '../apiBuilders/peers';
|
||||||
import {
|
import {
|
||||||
buildDisallowedGiftsSettings,
|
buildDisallowedGiftsSettings,
|
||||||
buildInputChannel,
|
buildInputChannel,
|
||||||
|
|||||||
@ -49,12 +49,16 @@ import {
|
|||||||
buildMessageDraft,
|
buildMessageDraft,
|
||||||
} from '../apiBuilders/messages';
|
} from '../apiBuilders/messages';
|
||||||
import {
|
import {
|
||||||
buildApiPeerNotifySettings,
|
|
||||||
buildLangStrings,
|
buildLangStrings,
|
||||||
buildPrivacyKey,
|
buildPrivacyKey,
|
||||||
} from '../apiBuilders/misc';
|
} from '../apiBuilders/misc';
|
||||||
import { buildApiCurrencyAmount } from '../apiBuilders/payments';
|
import { buildApiCurrencyAmount } from '../apiBuilders/payments';
|
||||||
import { buildApiEmojiStatus, buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import {
|
||||||
|
buildApiEmojiStatus,
|
||||||
|
buildApiPeerId,
|
||||||
|
buildApiPeerNotifySettings,
|
||||||
|
getApiChatIdFromMtpPeer,
|
||||||
|
} from '../apiBuilders/peers';
|
||||||
import {
|
import {
|
||||||
buildApiPaidReactionPrivacy,
|
buildApiPaidReactionPrivacy,
|
||||||
buildApiReaction,
|
buildApiReaction,
|
||||||
|
|||||||
@ -2,9 +2,17 @@ import type { ApiBotCommand } from './bots';
|
|||||||
import type {
|
import type {
|
||||||
ApiChatReactions, ApiFormattedText, ApiInputMessageReplyInfo, ApiInputSuggestedPostInfo, ApiPhoto, ApiStickerSet,
|
ApiChatReactions, ApiFormattedText, ApiInputMessageReplyInfo, ApiInputSuggestedPostInfo, ApiPhoto, ApiStickerSet,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
import type { ApiBotVerification, ApiChatInviteImporter, ApiPeerNotifySettings, ApiRestrictionReason } from './misc';
|
import type { ApiChatInviteImporter, ApiPeerNotifySettings, ApiRestrictionReason } from './misc';
|
||||||
import type {
|
import type {
|
||||||
ApiEmojiStatusType, ApiFakeType, ApiUser, ApiUsername,
|
ApiBotVerification,
|
||||||
|
ApiEmojiStatusType,
|
||||||
|
ApiFakeType,
|
||||||
|
ApiPeerColor,
|
||||||
|
ApiProfileTab,
|
||||||
|
ApiSendAsPeerId,
|
||||||
|
} from './peers';
|
||||||
|
import type {
|
||||||
|
ApiUser, ApiUsername,
|
||||||
} from './users';
|
} from './users';
|
||||||
|
|
||||||
type ApiChatType = (
|
type ApiChatType = (
|
||||||
@ -156,6 +164,7 @@ export interface ApiChatFullInfo {
|
|||||||
boostsApplied?: number;
|
boostsApplied?: number;
|
||||||
boostsToUnrestrict?: number;
|
boostsToUnrestrict?: number;
|
||||||
botVerification?: ApiBotVerification;
|
botVerification?: ApiBotVerification;
|
||||||
|
mainTab?: ApiProfileTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiChatMember {
|
export interface ApiChatMember {
|
||||||
@ -235,23 +244,6 @@ export interface ApiChatFolder {
|
|||||||
hasMyInvites?: true;
|
hasMyInvites?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiPeerSettings {
|
|
||||||
isAutoArchived?: boolean;
|
|
||||||
canReportSpam?: boolean;
|
|
||||||
canAddContact?: boolean;
|
|
||||||
canBlockContact?: boolean;
|
|
||||||
chargedPaidMessageStars?: number;
|
|
||||||
registrationMonth?: string;
|
|
||||||
phoneCountry?: string;
|
|
||||||
nameChangeDate?: number;
|
|
||||||
photoChangeDate?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiSendAsPeerId {
|
|
||||||
id: string;
|
|
||||||
isPremium?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiTopic {
|
export interface ApiTopic {
|
||||||
id: number;
|
id: number;
|
||||||
isClosed?: boolean;
|
isClosed?: boolean;
|
||||||
@ -296,11 +288,6 @@ export interface ApiChatlistExportedInvite {
|
|||||||
peerIds: string[];
|
peerIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiPeerColor {
|
|
||||||
color?: number;
|
|
||||||
backgroundEmojiId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiMissingInvitedUser {
|
export interface ApiMissingInvitedUser {
|
||||||
id: string;
|
id: string;
|
||||||
isRequiringPremiumToInvite?: boolean;
|
isRequiringPremiumToInvite?: boolean;
|
||||||
|
|||||||
@ -12,3 +12,4 @@ export * from './statistics';
|
|||||||
export * from './stories';
|
export * from './stories';
|
||||||
export * from './business';
|
export * from './business';
|
||||||
export * from './stars';
|
export * from './stars';
|
||||||
|
export * from './peers';
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import type {
|
|||||||
ApiBotInlineResult,
|
ApiBotInlineResult,
|
||||||
ApiWebDocument,
|
ApiWebDocument,
|
||||||
} from './bots';
|
} from './bots';
|
||||||
import type { ApiPeerColor } from './chats';
|
|
||||||
import type { ApiMessageAction } from './messageActions';
|
import type { ApiMessageAction } from './messageActions';
|
||||||
import type { ApiRestrictionReason } from './misc';
|
import type { ApiRestrictionReason } from './misc';
|
||||||
import type {
|
import type {
|
||||||
ApiLabeledPrice,
|
ApiLabeledPrice,
|
||||||
} from './payments';
|
} from './payments';
|
||||||
|
import type { ApiPeerColor } from './peers';
|
||||||
import type { ApiStarGiftUnique, ApiTypeCurrencyAmount } from './stars';
|
import type { ApiStarGiftUnique, ApiTypeCurrencyAmount } from './stars';
|
||||||
import type {
|
import type {
|
||||||
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
||||||
@ -957,7 +957,8 @@ export type ApiTranscription = {
|
|||||||
transcriptionId: string;
|
transcriptionId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiMessageSearchType = 'text' | 'media' | 'documents' | 'links' | 'audio' | 'voice' | 'profilePhoto';
|
export type ApiMessageSearchType = 'text' | 'media' | 'documents' | 'links' | 'audio' | 'voice' | 'gif'
|
||||||
|
| 'profilePhoto';
|
||||||
export type ApiGlobalMessageSearchType = 'text' |
|
export type ApiGlobalMessageSearchType = 'text' |
|
||||||
'channels' | 'media' | 'documents' | 'links' | 'audio' | 'voice' | 'publicPosts';
|
'channels' | 'media' | 'documents' | 'links' | 'audio' | 'voice' | 'publicPosts';
|
||||||
export type ApiMessageSearchContext = 'all' | 'users' | 'groups' | 'channels';
|
export type ApiMessageSearchContext = 'all' | 'users' | 'groups' | 'channels';
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { IconName } from '../../types/icons';
|
|||||||
import type { RegularLangFnParameters } from '../../util/localization';
|
import type { RegularLangFnParameters } from '../../util/localization';
|
||||||
import type { ApiDocument, ApiPhoto, ApiReaction } from './messages';
|
import type { ApiDocument, ApiPhoto, ApiReaction } from './messages';
|
||||||
import type { ApiPremiumSection } from './payments';
|
import type { ApiPremiumSection } from './payments';
|
||||||
|
import type { ApiBotVerification } from './peers';
|
||||||
import type { ApiStarsSubscriptionPricing } from './stars';
|
import type { ApiStarsSubscriptionPricing } from './stars';
|
||||||
import type { ApiUser } from './users';
|
import type { ApiUser } from './users';
|
||||||
|
|
||||||
@ -287,26 +288,6 @@ export interface ApiConfig {
|
|||||||
maxForwardedCount: number;
|
maxForwardedCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiPeerColorSet = string[];
|
|
||||||
export type ApiPeerProfileColorSet = {
|
|
||||||
paletteColors: string[];
|
|
||||||
bgColors: string[];
|
|
||||||
storyColors: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiPeerColorOption<T extends ApiPeerColorSet | ApiPeerProfileColorSet> = {
|
|
||||||
isHidden?: true;
|
|
||||||
colors?: T;
|
|
||||||
darkColors?: T;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ApiPeerColors {
|
|
||||||
general: Record<number, ApiPeerColorOption<ApiPeerColorSet>>;
|
|
||||||
generalHash?: number;
|
|
||||||
profile: Record<number, ApiPeerColorOption<ApiPeerProfileColorSet>>;
|
|
||||||
profileHash?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiTimezone {
|
export interface ApiTimezone {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -352,21 +333,6 @@ export interface ApiCollectibleInfo {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiPeerPhotos {
|
|
||||||
fallbackPhoto?: ApiPhoto;
|
|
||||||
personalPhoto?: ApiPhoto;
|
|
||||||
photos: ApiPhoto[];
|
|
||||||
count: number;
|
|
||||||
nextOffset?: number;
|
|
||||||
isLoading?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiBotVerification {
|
|
||||||
botId: string;
|
|
||||||
iconId: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ApiLimitType =
|
export type ApiLimitType =
|
||||||
| 'uploadMaxFileparts'
|
| 'uploadMaxFileparts'
|
||||||
| 'stickersFaved'
|
| 'stickersFaved'
|
||||||
|
|||||||
84
src/api/types/peers.ts
Normal file
84
src/api/types/peers.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import type { ApiPhoto } from './messages';
|
||||||
|
|
||||||
|
export interface ApiPeerPhotos {
|
||||||
|
fallbackPhoto?: ApiPhoto;
|
||||||
|
personalPhoto?: ApiPhoto;
|
||||||
|
photos: ApiPhoto[];
|
||||||
|
count: number;
|
||||||
|
nextOffset?: number;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiFakeType = 'fake' | 'scam';
|
||||||
|
|
||||||
|
export interface ApiBotVerification {
|
||||||
|
botId: string;
|
||||||
|
iconId: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiEmojiStatusType = ApiEmojiStatus | ApiEmojiStatusCollectible;
|
||||||
|
|
||||||
|
export interface ApiEmojiStatus {
|
||||||
|
type: 'regular';
|
||||||
|
documentId: string;
|
||||||
|
until?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiEmojiStatusCollectible {
|
||||||
|
type: 'collectible';
|
||||||
|
collectibleId: string;
|
||||||
|
documentId: string;
|
||||||
|
title: string;
|
||||||
|
slug: string;
|
||||||
|
patternDocumentId: string;
|
||||||
|
centerColor: string;
|
||||||
|
edgeColor: string;
|
||||||
|
patternColor: string;
|
||||||
|
textColor: string;
|
||||||
|
until?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiPeerSettings {
|
||||||
|
isAutoArchived?: boolean;
|
||||||
|
canReportSpam?: boolean;
|
||||||
|
canAddContact?: boolean;
|
||||||
|
canBlockContact?: boolean;
|
||||||
|
chargedPaidMessageStars?: number;
|
||||||
|
registrationMonth?: string;
|
||||||
|
phoneCountry?: string;
|
||||||
|
nameChangeDate?: number;
|
||||||
|
photoChangeDate?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiSendAsPeerId {
|
||||||
|
id: string;
|
||||||
|
isPremium?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiPeerColor {
|
||||||
|
color?: number;
|
||||||
|
backgroundEmojiId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiPeerColorSet = string[];
|
||||||
|
export type ApiPeerProfileColorSet = {
|
||||||
|
paletteColors: string[];
|
||||||
|
bgColors: string[];
|
||||||
|
storyColors: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiPeerColorOption<T extends ApiPeerColorSet | ApiPeerProfileColorSet> = {
|
||||||
|
isHidden?: true;
|
||||||
|
colors?: T;
|
||||||
|
darkColors?: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ApiPeerColors {
|
||||||
|
general: Record<number, ApiPeerColorOption<ApiPeerColorSet>>;
|
||||||
|
generalHash?: number;
|
||||||
|
profile: Record<number, ApiPeerColorOption<ApiPeerProfileColorSet>>;
|
||||||
|
profileHash?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiProfileTab = 'stories' | 'gifts' | 'media' | 'documents' | 'audio' | 'voice' | 'links' | 'gif';
|
||||||
@ -18,7 +18,6 @@ import type {
|
|||||||
ApiChatFullInfo,
|
ApiChatFullInfo,
|
||||||
ApiChatMember,
|
ApiChatMember,
|
||||||
ApiDraft,
|
ApiDraft,
|
||||||
ApiPeerSettings,
|
|
||||||
ApiTypingStatus,
|
ApiTypingStatus,
|
||||||
} from './chats';
|
} from './chats';
|
||||||
import type {
|
import type {
|
||||||
@ -43,11 +42,12 @@ import type {
|
|||||||
ApiPeerNotifySettings,
|
ApiPeerNotifySettings,
|
||||||
ApiSessionData,
|
ApiSessionData,
|
||||||
} from './misc';
|
} from './misc';
|
||||||
|
import type { ApiEmojiStatusType, ApiPeerSettings } from './peers';
|
||||||
import type { ApiPrivacyKey, LangPackStringValue, PrivacyVisibility } from './settings';
|
import type { ApiPrivacyKey, LangPackStringValue, PrivacyVisibility } from './settings';
|
||||||
import type { ApiTypeCurrencyAmount } from './stars';
|
import type { ApiTypeCurrencyAmount } from './stars';
|
||||||
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
||||||
import type {
|
import type {
|
||||||
ApiEmojiStatusType, ApiUser, ApiUserFullInfo, ApiUserStatus,
|
ApiUser, ApiUserFullInfo, ApiUserStatus,
|
||||||
} from './users';
|
} from './users';
|
||||||
|
|
||||||
export type ApiUpdateReady = {
|
export type ApiUpdateReady = {
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
import type { API_CHAT_TYPES } from '../../config';
|
import type { API_CHAT_TYPES } from '../../config';
|
||||||
import type { ApiBotInfo } from './bots';
|
import type { ApiBotInfo } from './bots';
|
||||||
import type { ApiBusinessIntro, ApiBusinessLocation, ApiBusinessWorkHours } from './business';
|
import type { ApiBusinessIntro, ApiBusinessLocation, ApiBusinessWorkHours } from './business';
|
||||||
import type { ApiPeerColor, ApiPeerSettings } from './chats';
|
|
||||||
import type { ApiDocument, ApiPhoto } from './messages';
|
import type { ApiDocument, ApiPhoto } from './messages';
|
||||||
import type { ApiBotVerification } from './misc';
|
import type {
|
||||||
|
ApiBotVerification,
|
||||||
|
ApiEmojiStatusType,
|
||||||
|
ApiFakeType,
|
||||||
|
ApiPeerColor,
|
||||||
|
ApiPeerSettings,
|
||||||
|
ApiProfileTab,
|
||||||
|
} from './peers';
|
||||||
import type { ApiSavedStarGift, ApiStarsRating } from './stars';
|
import type { ApiSavedStarGift, ApiStarsRating } from './stars';
|
||||||
|
|
||||||
export interface ApiUser {
|
export interface ApiUser {
|
||||||
@ -75,10 +81,9 @@ export interface ApiUserFullInfo {
|
|||||||
botVerification?: ApiBotVerification;
|
botVerification?: ApiBotVerification;
|
||||||
paidMessagesStars?: number;
|
paidMessagesStars?: number;
|
||||||
settings?: ApiPeerSettings;
|
settings?: ApiPeerSettings;
|
||||||
|
mainTab?: ApiProfileTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiFakeType = 'fake' | 'scam';
|
|
||||||
|
|
||||||
export type ApiUserType = 'userTypeBot' | 'userTypeRegular' | 'userTypeDeleted' | 'userTypeUnknown';
|
export type ApiUserType = 'userTypeBot' | 'userTypeRegular' | 'userTypeDeleted' | 'userTypeUnknown';
|
||||||
|
|
||||||
export interface ApiUserStatus {
|
export interface ApiUserStatus {
|
||||||
@ -136,28 +141,6 @@ export interface ApiAttachBotIcon {
|
|||||||
document: ApiDocument;
|
document: ApiDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiEmojiStatusType = ApiEmojiStatus | ApiEmojiStatusCollectible;
|
|
||||||
|
|
||||||
export interface ApiEmojiStatus {
|
|
||||||
type: 'regular';
|
|
||||||
documentId: string;
|
|
||||||
until?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiEmojiStatusCollectible {
|
|
||||||
type: 'collectible';
|
|
||||||
collectibleId: string;
|
|
||||||
documentId: string;
|
|
||||||
title: string;
|
|
||||||
slug: string;
|
|
||||||
patternDocumentId: string;
|
|
||||||
centerColor: string;
|
|
||||||
edgeColor: string;
|
|
||||||
patternColor: string;
|
|
||||||
textColor: string;
|
|
||||||
until?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiBirthday {
|
export interface ApiBirthday {
|
||||||
day: number;
|
day: number;
|
||||||
month: number;
|
month: number;
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="32"><path d="M15.33 15.009a7.105 7.105 0 1 1 .001-14.21 7.105 7.105 0 0 1-.001 14.21zM1.122 27.236c0-1.363.339-2.725 1.306-3.686 1.817-1.804 5.722-4.482 12.901-4.482s11.085 2.678 12.901 4.482c.967.96 1.306 2.322 1.306 3.686a4.011 4.011 0 0 1-3.984 4.01H5.13a4.011 4.011 0 0 1-4.011-4.01zm30.601 4.01a7.346 7.346 0 0 0 1.184-4.01c0-1.839-.45-4.238-2.302-6.076l-.042-.041c.326-.013.66-.02 1.004-.02 6.145 0 9.493 2.226 11.052 3.729.83.8 1.126 1.952 1.126 3.105a3.314 3.314 0 0 1-3.314 3.314h-8.708zm-.156-14.207a6.089 6.089 0 1 1 0-12.178 6.089 6.089 0 0 1 0 12.178z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M11.002 15.286a5 5 0 1 1 0-10 5 5 0 0 1 0 10m-10 8.606c0-.96.239-1.918.92-2.595C3.2 20.027 5.947 18.143 11 18.143c5.052 0 7.802 1.885 9.08 3.154.68.676.919 1.635.919 2.595a2.823 2.823 0 0 1-2.804 2.822H3.823A2.823 2.823 0 0 1 1 23.892zm21.537 2.822a5.17 5.17 0 0 0 .833-2.822c0-1.295-.316-2.983-1.62-4.277l-.03-.028q.345-.015.707-.015c4.325 0 6.681 1.567 7.779 2.625.584.563.792 1.374.792 2.185a2.33 2.33 0 0 1-2.332 2.333zm-.11-9.999a4.285 4.285 0 1 1 0-8.57 4.285 4.285 0 0 1 0 8.57" /></svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 567 B |
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M0 15C0 6.716 6.716 0 15 0c8.284 0 15 6.716 15 15 0 8.284-6.716 15-15 15-8.284 0-15-6.716-15-15Zm13.636-6.818a1.364 1.364 0 1 0 0 2.727h.682c.377 0 .682.305.682.682v8.864a1.364 1.364 0 1 0 2.727 0V11.59a3.41 3.41 0 0 0-3.409-3.41z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M1 16C1 7.716 7.716 1 16 1s15 6.716 15 15-6.716 15-15 15S1 24.284 1 16m13.636-6.818a1.364 1.364 0 1 0 0 2.727h.682c.377 0 .682.305.682.682v8.864a1.364 1.364 0 1 0 2.727 0V12.59a3.41 3.41 0 0 0-3.409-3.41Z"/></svg>
|
||||||
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 309 B |
@ -1434,6 +1434,8 @@
|
|||||||
"ProfileBirthdayValueYear" = "{date} ({age} years old)";
|
"ProfileBirthdayValueYear" = "{date} ({age} years old)";
|
||||||
"ProfileBirthdayTodayValue" = "🎂 {date}";
|
"ProfileBirthdayTodayValue" = "🎂 {date}";
|
||||||
"ProfileBirthdayTodayValueYear" = "🎂 {date} ({age} years old)";
|
"ProfileBirthdayTodayValueYear" = "🎂 {date} ({age} years old)";
|
||||||
|
"ProfileMenuSetMainTab" = "Set as Main Tab";
|
||||||
|
"ProfileItemSubscribers" = "Subscribers";
|
||||||
"MonetizationInfoTONTitle" = "What is 💎 TON?";
|
"MonetizationInfoTONTitle" = "What is 💎 TON?";
|
||||||
"ChannelEarnLearnCoinAbout" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}";
|
"ChannelEarnLearnCoinAbout" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}";
|
||||||
"MonetizationBalanceZeroInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by advertisers to pay for ads. {link}";
|
"MonetizationBalanceZeroInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by advertisers to pay for ads. {link}";
|
||||||
@ -1710,6 +1712,7 @@
|
|||||||
"ProfileTabLinks" = "Links";
|
"ProfileTabLinks" = "Links";
|
||||||
"ProfileTabMusic" = "Music";
|
"ProfileTabMusic" = "Music";
|
||||||
"ProfileTabVoice" = "Voice";
|
"ProfileTabVoice" = "Voice";
|
||||||
|
"ProfileTabGifs" = "GIF";
|
||||||
"ProfileTabSharedGroups" = "Groups";
|
"ProfileTabSharedGroups" = "Groups";
|
||||||
"ProfileTabSimilarChannels" = "Similar Channels";
|
"ProfileTabSimilarChannels" = "Similar Channels";
|
||||||
"ProfileTabSimilarBots" = "Similar Bots";
|
"ProfileTabSimilarBots" = "Similar Bots";
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { memo, useEffect, useRef, useState } from '../../lib/teact/teact';
|
|||||||
import type { ApiSticker } from '../../api/types';
|
import type { ApiSticker } from '../../api/types';
|
||||||
import type { AnimationLevel } from '../../types';
|
import type { AnimationLevel } from '../../types';
|
||||||
|
|
||||||
|
import { ANIMATION_LEVEL_MIN } from '../../config';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
import useHorizontalScroll from '../../hooks/useHorizontalScroll';
|
import useHorizontalScroll from '../../hooks/useHorizontalScroll';
|
||||||
@ -38,7 +39,7 @@ const AnimatedTabList = ({
|
|||||||
const clipPathContainerRef = useRef<HTMLDivElement>();
|
const clipPathContainerRef = useRef<HTMLDivElement>();
|
||||||
const selectedIndex = items.findIndex((item) => item.id === selectedItemId) || 0;
|
const selectedIndex = items.findIndex((item) => item.id === selectedItemId) || 0;
|
||||||
const [clipPath, setClipPath] = useState<string>('');
|
const [clipPath, setClipPath] = useState<string>('');
|
||||||
const shouldAnimate = animationLevel > 0;
|
const shouldAnimate = animationLevel > ANIMATION_LEVEL_MIN;
|
||||||
|
|
||||||
useHorizontalScroll(containerRef, !items.length, true);
|
useHorizontalScroll(containerRef, !items.length, true);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
|
||||||
import { memo, useRef } from '../../lib/teact/teact';
|
import { memo, useRef } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiMessage } from '../../api/types';
|
import type { ApiMessage } from '../../api/types';
|
||||||
@ -8,7 +7,9 @@ import {
|
|||||||
getMessageHtmlId,
|
getMessageHtmlId,
|
||||||
getMessageIsSpoiler,
|
getMessageIsSpoiler,
|
||||||
getMessageVideo,
|
getMessageVideo,
|
||||||
|
getVideoMediaHash,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
|
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { formatMediaDuration } from '../../util/dates/dateFormat';
|
import { formatMediaDuration } from '../../util/dates/dateFormat';
|
||||||
import stopEvent from '../../util/stopEvent';
|
import stopEvent from '../../util/stopEvent';
|
||||||
@ -21,6 +22,7 @@ import useLastCallback from '../../hooks/useLastCallback';
|
|||||||
import useMedia from '../../hooks/useMedia';
|
import useMedia from '../../hooks/useMedia';
|
||||||
import useMediaTransitionDeprecated from '../../hooks/useMediaTransitionDeprecated';
|
import useMediaTransitionDeprecated from '../../hooks/useMediaTransitionDeprecated';
|
||||||
|
|
||||||
|
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||||
import MediaSpoiler from './MediaSpoiler';
|
import MediaSpoiler from './MediaSpoiler';
|
||||||
|
|
||||||
import './Media.scss';
|
import './Media.scss';
|
||||||
@ -29,26 +31,32 @@ type OwnProps = {
|
|||||||
message: ApiMessage;
|
message: ApiMessage;
|
||||||
idPrefix?: string;
|
idPrefix?: string;
|
||||||
isProtected?: boolean;
|
isProtected?: boolean;
|
||||||
|
canAutoPlay?: boolean;
|
||||||
observeIntersection?: ObserveFn;
|
observeIntersection?: ObserveFn;
|
||||||
onClick?: (messageId: number, chatId: string) => void;
|
onClick?: (messageId: number, chatId: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Media: FC<OwnProps> = ({
|
const Media = ({
|
||||||
message,
|
message,
|
||||||
idPrefix = 'shared-media',
|
idPrefix = 'shared-media',
|
||||||
isProtected,
|
isProtected,
|
||||||
|
canAutoPlay,
|
||||||
observeIntersection,
|
observeIntersection,
|
||||||
onClick,
|
onClick,
|
||||||
}) => {
|
}: OwnProps) => {
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||||
|
const [isHovering, markMouseOver, markMouseOut] = useFlag();
|
||||||
|
|
||||||
const thumbDataUri = useThumbnail(message);
|
const thumbDataUri = useThumbnail(message);
|
||||||
const mediaHash = useMessageMediaHash(message, 'pictogram');
|
const mediaHash = useMessageMediaHash(message, 'pictogram');
|
||||||
const mediaBlobUrl = useMedia(mediaHash, !isIntersecting);
|
const mediaBlobUrl = useMedia(mediaHash, !isIntersecting);
|
||||||
const transitionClassNames = useMediaTransitionDeprecated(mediaBlobUrl);
|
const transitionClassNames = useMediaTransitionDeprecated(mediaBlobUrl);
|
||||||
|
|
||||||
const video = getMessageVideo(message);
|
const video = getMessageVideo(message);
|
||||||
|
const fullGiftHash = video?.isGif ? getVideoMediaHash(video, 'full') : undefined;
|
||||||
|
const fullGifBlobUrl = useMedia(fullGiftHash, !isIntersecting);
|
||||||
|
|
||||||
const hasSpoiler = getMessageIsSpoiler(message);
|
const hasSpoiler = getMessageIsSpoiler(message);
|
||||||
const [isSpoilerShown, , hideSpoiler] = useFlag(hasSpoiler);
|
const [isSpoilerShown, , hideSpoiler] = useFlag(hasSpoiler);
|
||||||
@ -64,6 +72,8 @@ const Media: FC<OwnProps> = ({
|
|||||||
id={`${idPrefix}${getMessageHtmlId(message.id)}`}
|
id={`${idPrefix}${getMessageHtmlId(message.id)}`}
|
||||||
className="Media scroll-item"
|
className="Media scroll-item"
|
||||||
onClick={onClick ? handleClick : undefined}
|
onClick={onClick ? handleClick : undefined}
|
||||||
|
onMouseOver={!IS_TOUCH_ENV ? markMouseOver : undefined}
|
||||||
|
onMouseOut={!IS_TOUCH_ENV ? markMouseOut : undefined}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={thumbDataUri}
|
src={thumbDataUri}
|
||||||
@ -73,14 +83,28 @@ const Media: FC<OwnProps> = ({
|
|||||||
decoding="async"
|
decoding="async"
|
||||||
onContextMenu={isProtected ? stopEvent : undefined}
|
onContextMenu={isProtected ? stopEvent : undefined}
|
||||||
/>
|
/>
|
||||||
<img
|
{fullGifBlobUrl ? (
|
||||||
src={mediaBlobUrl}
|
<OptimizedVideo
|
||||||
className={buildClassName('full-media', 'media-miniature', transitionClassNames)}
|
canPlay={isIntersecting && !hasSpoiler && isHovering && Boolean(canAutoPlay)}
|
||||||
alt=""
|
src={fullGifBlobUrl}
|
||||||
draggable={!isProtected}
|
className={buildClassName('full-media', 'media-miniature', transitionClassNames)}
|
||||||
decoding="async"
|
muted
|
||||||
onContextMenu={isProtected ? stopEvent : undefined}
|
loop
|
||||||
/>
|
playsInline
|
||||||
|
draggable={false}
|
||||||
|
disablePictureInPicture
|
||||||
|
onContextMenu={isProtected ? stopEvent : undefined}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={mediaBlobUrl}
|
||||||
|
className={buildClassName('full-media', 'media-miniature', transitionClassNames)}
|
||||||
|
alt=""
|
||||||
|
draggable={false}
|
||||||
|
decoding="async"
|
||||||
|
onContextMenu={isProtected ? stopEvent : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{hasSpoiler && (
|
{hasSpoiler && (
|
||||||
<MediaSpoiler
|
<MediaSpoiler
|
||||||
thumbDataUri={mediaBlobUrl || thumbDataUri}
|
thumbDataUri={mediaBlobUrl || thumbDataUri}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
|
||||||
import {
|
import {
|
||||||
memo, useMemo,
|
memo, useMemo,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
@ -12,8 +11,8 @@ import type {
|
|||||||
ApiUserFullInfo,
|
ApiUserFullInfo,
|
||||||
ApiUsername,
|
ApiUsername,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { BotAppPermissions } from '../../../types';
|
|
||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
import { type BotAppPermissions, ManagementScreens } from '../../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FRAGMENT_PHONE_CODE, FRAGMENT_PHONE_LENGTH, MUTE_INDEFINITE_TIMESTAMP, UNMUTE_TIMESTAMP,
|
FRAGMENT_PHONE_CODE, FRAGMENT_PHONE_LENGTH, MUTE_INDEFINITE_TIMESTAMP, UNMUTE_TIMESTAMP,
|
||||||
@ -22,6 +21,7 @@ import {
|
|||||||
buildStaticMapHash,
|
buildStaticMapHash,
|
||||||
getChatLink,
|
getChatLink,
|
||||||
getHasAdminRight,
|
getHasAdminRight,
|
||||||
|
isChatAdmin,
|
||||||
isChatChannel,
|
isChatChannel,
|
||||||
isUserRightBanned,
|
isUserRightBanned,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
@ -92,6 +92,7 @@ type StateProps = {
|
|||||||
isBotCanManageEmojiStatus?: boolean;
|
isBotCanManageEmojiStatus?: boolean;
|
||||||
botAppPermissions?: BotAppPermissions;
|
botAppPermissions?: BotAppPermissions;
|
||||||
botVerification?: ApiBotVerification;
|
botVerification?: ApiBotVerification;
|
||||||
|
canViewSubscribers?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_MAP_CONFIG = {
|
const DEFAULT_MAP_CONFIG = {
|
||||||
@ -102,7 +103,7 @@ const DEFAULT_MAP_CONFIG = {
|
|||||||
|
|
||||||
const BOT_VERIFICATION_ICON_SIZE = 16;
|
const BOT_VERIFICATION_ICON_SIZE = 16;
|
||||||
|
|
||||||
const ChatExtra: FC<OwnProps & StateProps> = ({
|
const ChatExtra = ({
|
||||||
chatOrUserId,
|
chatOrUserId,
|
||||||
user,
|
user,
|
||||||
chat,
|
chat,
|
||||||
@ -124,7 +125,8 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
|||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
isInSettings,
|
isInSettings,
|
||||||
}) => {
|
canViewSubscribers,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
showNotification,
|
showNotification,
|
||||||
updateChatMutedState,
|
updateChatMutedState,
|
||||||
@ -136,6 +138,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
|||||||
requestMainWebView,
|
requestMainWebView,
|
||||||
toggleUserEmojiStatusPermission,
|
toggleUserEmojiStatusPermission,
|
||||||
toggleUserLocationPermission,
|
toggleUserLocationPermission,
|
||||||
|
requestNextManagementScreen,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -261,6 +264,10 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
|||||||
copy(formatUsername(username.username, isChat), oldLang(isChat ? 'Link' : 'Username'));
|
copy(formatUsername(username.username, isChat), oldLang(isChat ? 'Link' : 'Username'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOpenSubscribers = useLastCallback(() => {
|
||||||
|
requestNextManagementScreen({ screen: ManagementScreens.ChannelSubscribers });
|
||||||
|
});
|
||||||
|
|
||||||
const handleOpenApp = useLastCallback(() => {
|
const handleOpenApp = useLastCallback(() => {
|
||||||
const botId = user?.id;
|
const botId = user?.id;
|
||||||
if (!botId) {
|
if (!botId) {
|
||||||
@ -476,6 +483,12 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
|
{canViewSubscribers && (
|
||||||
|
<ListItem icon="group" narrow multiline ripple onClick={handleOpenSubscribers}>
|
||||||
|
<div className="title">{lang('ProfileItemSubscribers')}</div>
|
||||||
|
<span className="subtitle">{lang.number(chat?.membersCount || 0)}</span>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
{botVerification && (
|
{botVerification && (
|
||||||
<div className={styles.botVerificationSection}>
|
<div className={styles.botVerificationSection}>
|
||||||
<CustomEmoji
|
<CustomEmoji
|
||||||
@ -510,6 +523,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const chatInviteLink = chatFullInfo?.inviteLink;
|
const chatInviteLink = chatFullInfo?.inviteLink;
|
||||||
const description = userFullInfo?.bio || chatFullInfo?.about;
|
const description = userFullInfo?.bio || chatFullInfo?.about;
|
||||||
|
|
||||||
|
const canViewSubscribers = chat && isChatChannel(chat) && isChatAdmin(chat);
|
||||||
const canInviteUsers = chat && !user && (
|
const canInviteUsers = chat && !user && (
|
||||||
(!isChatChannel(chat) && !isUserRightBanned(chat, 'inviteUsers'))
|
(!isChatChannel(chat) && !isUserRightBanned(chat, 'inviteUsers'))
|
||||||
|| getHasAdminRight(chat, 'inviteUsers')
|
|| getHasAdminRight(chat, 'inviteUsers')
|
||||||
@ -542,6 +556,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
hasMainMiniApp,
|
hasMainMiniApp,
|
||||||
isBotCanManageEmojiStatus: userFullInfo?.isBotCanManageEmojiStatus,
|
isBotCanManageEmojiStatus: userFullInfo?.isBotCanManageEmojiStatus,
|
||||||
botVerification,
|
botVerification,
|
||||||
|
canViewSubscribers,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(ChatExtra));
|
)(ChatExtra));
|
||||||
|
|||||||
@ -18,7 +18,8 @@ import { type MediaViewerMedia, MediaViewerOrigin, type ThreadId } from '../../t
|
|||||||
import { ANIMATION_END_DELAY } from '../../config';
|
import { ANIMATION_END_DELAY } from '../../config';
|
||||||
import { requestMutation } from '../../lib/fasterdom/fasterdom';
|
import { requestMutation } from '../../lib/fasterdom/fasterdom';
|
||||||
import {
|
import {
|
||||||
getChatMediaMessageIds, getMessagePaidMedia, isChatAdmin,
|
getMessageContentIds,
|
||||||
|
getMessagePaidMedia, isChatAdmin,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
import {
|
import {
|
||||||
selectChatMessage,
|
selectChatMessage,
|
||||||
@ -156,7 +157,7 @@ const MediaViewer = ({
|
|||||||
bestData,
|
bestData,
|
||||||
dimensions,
|
dimensions,
|
||||||
isGif,
|
isGif,
|
||||||
isFromSharedMedia,
|
contentType,
|
||||||
} = useMediaProps({
|
} = useMediaProps({
|
||||||
media, isAvatar: Boolean(avatarOwner), origin, delay: isGhostAnimation && ANIMATION_DURATION,
|
media, isAvatar: Boolean(avatarOwner), origin, delay: isGhostAnimation && ANIMATION_DURATION,
|
||||||
});
|
});
|
||||||
@ -173,8 +174,8 @@ const MediaViewer = ({
|
|||||||
const messageMediaIds = useMemo(() => {
|
const messageMediaIds = useMemo(() => {
|
||||||
return withDynamicLoading
|
return withDynamicLoading
|
||||||
? collectedMessageIds
|
? collectedMessageIds
|
||||||
: getChatMediaMessageIds(chatMessages || {}, collectedMessageIds || [], isFromSharedMedia);
|
: getMessageContentIds(chatMessages || {}, collectedMessageIds || [], contentType);
|
||||||
}, [chatMessages, collectedMessageIds, isFromSharedMedia, withDynamicLoading]);
|
}, [chatMessages, collectedMessageIds, contentType, withDynamicLoading]);
|
||||||
|
|
||||||
if (isOpen && (!prevSenderId || prevSenderId !== senderId || animationKey.current === undefined)) {
|
if (isOpen && (!prevSenderId || prevSenderId !== senderId || animationKey.current === undefined)) {
|
||||||
animationKey.current = isSingle ? 0 : (messageId || mediaIndex);
|
animationKey.current = isSingle ? 0 : (messageId || mediaIndex);
|
||||||
@ -520,7 +521,7 @@ export default memo(withGlobal(
|
|||||||
const currentItem = getMediaViewerItem({
|
const currentItem = getMediaViewerItem({
|
||||||
avatarOwner, standaloneMedia, profilePhotos, mediaIndex,
|
avatarOwner, standaloneMedia, profilePhotos, mediaIndex,
|
||||||
});
|
});
|
||||||
const viewableMedia = selectViewableMedia(global, currentItem);
|
const viewableMedia = selectViewableMedia(global, origin, currentItem);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
profilePhotos,
|
profilePhotos,
|
||||||
@ -565,6 +566,11 @@ export default memo(withGlobal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentItem = getMediaViewerItem({
|
||||||
|
message, standaloneMedia, mediaIndex, sponsoredMessage,
|
||||||
|
});
|
||||||
|
const viewableMedia = selectViewableMedia(global, origin, currentItem);
|
||||||
|
|
||||||
let chatMessages: Record<number, ApiMessage> | undefined;
|
let chatMessages: Record<number, ApiMessage> | undefined;
|
||||||
|
|
||||||
if (chatId) {
|
if (chatId) {
|
||||||
@ -588,7 +594,8 @@ export default memo(withGlobal(
|
|||||||
collectedMessageIds = foundIds;
|
collectedMessageIds = foundIds;
|
||||||
} else if (origin === MediaViewerOrigin.SharedMedia) {
|
} else if (origin === MediaViewerOrigin.SharedMedia) {
|
||||||
const currentSearch = selectCurrentSharedMediaSearch(global);
|
const currentSearch = selectCurrentSharedMediaSearch(global);
|
||||||
const { foundIds } = (currentSearch && currentSearch.resultsByType && currentSearch.resultsByType.media) || {};
|
const resultsByType = currentSearch?.resultsByType;
|
||||||
|
const { foundIds } = (viewableMedia?.isGif ? resultsByType?.gif : resultsByType?.media) || {};
|
||||||
collectedMessageIds = foundIds;
|
collectedMessageIds = foundIds;
|
||||||
} else if (isOriginInline || isOriginAlbum) {
|
} else if (isOriginInline || isOriginAlbum) {
|
||||||
const outlyingList = selectOutlyingListByMessageId(global, chatId, threadId, messageId);
|
const outlyingList = selectOutlyingListByMessageId(global, chatId, threadId, messageId);
|
||||||
@ -596,11 +603,6 @@ export default memo(withGlobal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentItem = getMediaViewerItem({
|
|
||||||
message, standaloneMedia, mediaIndex, sponsoredMessage,
|
|
||||||
});
|
|
||||||
const viewableMedia = selectViewableMedia(global, currentItem);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chatId,
|
chatId,
|
||||||
threadId,
|
threadId,
|
||||||
|
|||||||
@ -413,7 +413,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const canDelete = canDeleteMessage || canDeleteAvatar;
|
const canDelete = canDeleteMessage || canDeleteAvatar;
|
||||||
const canUpdate = canUpdateMedia && Boolean(avatarPhoto) && !isCurrentAvatar;
|
const canUpdate = canUpdateMedia && Boolean(avatarPhoto) && !isCurrentAvatar;
|
||||||
const messageListType = currentMessageList?.type;
|
const messageListType = currentMessageList?.type;
|
||||||
const viewableMedia = selectViewableMedia(global, item);
|
const viewableMedia = selectViewableMedia(global, origin, item);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeDownloads,
|
activeDownloads,
|
||||||
|
|||||||
@ -257,7 +257,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const message = item.type === 'message' ? item.message : undefined;
|
const message = item.type === 'message' ? item.message : undefined;
|
||||||
const sponsoredMessage = item.type === 'sponsoredMessage' ? item.message : undefined;
|
const sponsoredMessage = item.type === 'sponsoredMessage' ? item.message : undefined;
|
||||||
const textMessage = message || sponsoredMessage;
|
const textMessage = message || sponsoredMessage;
|
||||||
const viewableMedia = selectViewableMedia(global, item);
|
const viewableMedia = selectViewableMedia(global, origin, item);
|
||||||
|
|
||||||
const maxTimestamp = message && selectMessageTimestampableDuration(global, message, true);
|
const maxTimestamp = message && selectMessageTimestampableDuration(global, message, true);
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type {
|
|||||||
ApiMessage, ApiPeer, ApiPeerPhotos, ApiSponsoredMessage,
|
ApiMessage, ApiPeer, ApiPeerPhotos, ApiSponsoredMessage,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { GlobalState } from '../../../global/types';
|
import type { GlobalState } from '../../../global/types';
|
||||||
import type { MediaViewerMedia } from '../../../types';
|
import { type MediaViewerMedia, MediaViewerOrigin } from '../../../types';
|
||||||
|
|
||||||
import { getMessageContent, isDocumentPhoto, isDocumentVideo } from '../../../global/helpers';
|
import { getMessageContent, isDocumentPhoto, isDocumentVideo } from '../../../global/helpers';
|
||||||
import { selectWebPageFromMessage } from '../../../global/selectors';
|
import { selectWebPageFromMessage } from '../../../global/selectors';
|
||||||
@ -28,6 +28,7 @@ export type MediaViewerItem = {
|
|||||||
|
|
||||||
export type ViewableMedia = {
|
export type ViewableMedia = {
|
||||||
media: MediaViewerMedia;
|
media: MediaViewerMedia;
|
||||||
|
isGif?: boolean;
|
||||||
isSingle?: boolean;
|
isSingle?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,12 +78,16 @@ export function getMediaViewerItem({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function selectViewableMedia(global: GlobalState, params?: MediaViewerItem): ViewableMedia | undefined {
|
export default function selectViewableMedia(
|
||||||
|
global: GlobalState, origin?: MediaViewerOrigin, params?: MediaViewerItem,
|
||||||
|
): ViewableMedia | undefined {
|
||||||
if (!params) return undefined;
|
if (!params) return undefined;
|
||||||
|
|
||||||
if (params.type === 'standalone') {
|
if (params.type === 'standalone') {
|
||||||
|
const media = params.media[params.mediaIndex];
|
||||||
return {
|
return {
|
||||||
media: params.media[params.mediaIndex],
|
media,
|
||||||
|
isGif: media.mediaType === 'video' && media.isGif,
|
||||||
isSingle: params.media.length === 1,
|
isSingle: params.media.length === 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -134,6 +139,7 @@ export default function selectViewableMedia(global: GlobalState, params?: MediaV
|
|||||||
const { photo: extendedPhoto, video: extendedVideo } = extendedMedia;
|
const { photo: extendedPhoto, video: extendedVideo } = extendedMedia;
|
||||||
return {
|
return {
|
||||||
media: (extendedPhoto || extendedVideo)!,
|
media: (extendedPhoto || extendedVideo)!,
|
||||||
|
isGif: extendedVideo?.isGif,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +149,8 @@ export default function selectViewableMedia(global: GlobalState, params?: MediaV
|
|||||||
if (media) {
|
if (media) {
|
||||||
return {
|
return {
|
||||||
media,
|
media,
|
||||||
isSingle: video?.isGif,
|
isGif: video?.isGif,
|
||||||
|
isSingle: video?.isGif && origin !== MediaViewerOrigin.SharedMedia,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from '../../../lib/teact/teact';
|
import { useMemo } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { MediaViewerMedia } from '../../../types';
|
import type { MediaViewerMedia } from '../../../types';
|
||||||
import { ApiMediaFormat } from '../../../api/types';
|
import { ApiMediaFormat, type ApiMessageSearchType } from '../../../api/types';
|
||||||
import { MediaViewerOrigin } from '../../../types';
|
import { MediaViewerOrigin } from '../../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -46,6 +46,8 @@ export const useMediaProps = ({
|
|||||||
const isFromSharedMedia = origin === MediaViewerOrigin.SharedMedia;
|
const isFromSharedMedia = origin === MediaViewerOrigin.SharedMedia;
|
||||||
const isFromSearch = origin === MediaViewerOrigin.SearchResult;
|
const isFromSearch = origin === MediaViewerOrigin.SearchResult;
|
||||||
|
|
||||||
|
const contentType: ApiMessageSearchType = isGif ? 'gif' : 'media';
|
||||||
|
|
||||||
const getMediaOrAvatarHash = useMemo(() => (isFull?: boolean) => {
|
const getMediaOrAvatarHash = useMemo(() => (isFull?: boolean) => {
|
||||||
if (!media) return undefined;
|
if (!media) return undefined;
|
||||||
|
|
||||||
@ -133,7 +135,7 @@ export const useMediaProps = ({
|
|||||||
bestImageData,
|
bestImageData,
|
||||||
bestData,
|
bestData,
|
||||||
dimensions,
|
dimensions,
|
||||||
isFromSharedMedia,
|
contentType,
|
||||||
isVideoAvatar,
|
isVideoAvatar,
|
||||||
isLocal,
|
isLocal,
|
||||||
loadProgress,
|
loadProgress,
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
@use '../../styles/mixins';
|
@use '../../styles/mixins';
|
||||||
|
|
||||||
.Profile {
|
.Profile {
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -134,6 +136,7 @@
|
|||||||
&.storiesArchive-list,
|
&.storiesArchive-list,
|
||||||
&.stories-list,
|
&.stories-list,
|
||||||
&.media-list,
|
&.media-list,
|
||||||
|
&.gif-list,
|
||||||
&.previewMedia-list,
|
&.previewMedia-list,
|
||||||
&.gifts-list {
|
&.gifts-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import type {
|
|||||||
ApiChat,
|
ApiChat,
|
||||||
ApiChatMember,
|
ApiChatMember,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
|
ApiProfileTab,
|
||||||
ApiSavedStarGift,
|
ApiSavedStarGift,
|
||||||
ApiStarGiftCollection,
|
ApiStarGiftCollection,
|
||||||
ApiStoryAlbum,
|
ApiStoryAlbum,
|
||||||
@ -29,7 +30,6 @@ import {
|
|||||||
getIsDownloading,
|
getIsDownloading,
|
||||||
getIsSavedDialog,
|
getIsSavedDialog,
|
||||||
getMessageDocument,
|
getMessageDocument,
|
||||||
isChatAdmin,
|
|
||||||
isChatChannel,
|
isChatChannel,
|
||||||
isChatGroup,
|
isChatGroup,
|
||||||
isUserBot,
|
isUserBot,
|
||||||
@ -38,6 +38,7 @@ import {
|
|||||||
import { getSavedGiftKey } from '../../global/helpers/stars';
|
import { getSavedGiftKey } from '../../global/helpers/stars';
|
||||||
import {
|
import {
|
||||||
selectActiveDownloads,
|
selectActiveDownloads,
|
||||||
|
selectCanUpdateMainTab,
|
||||||
selectChat,
|
selectChat,
|
||||||
selectChatFullInfo,
|
selectChatFullInfo,
|
||||||
selectChatMessages,
|
selectChatMessages,
|
||||||
@ -47,6 +48,7 @@ import {
|
|||||||
selectIsRightColumnShown,
|
selectIsRightColumnShown,
|
||||||
selectMonoforumChannel,
|
selectMonoforumChannel,
|
||||||
selectPeerStories,
|
selectPeerStories,
|
||||||
|
selectPerformanceSettingsValue,
|
||||||
selectSimilarBotsIds,
|
selectSimilarBotsIds,
|
||||||
selectSimilarChannelIds,
|
selectSimilarChannelIds,
|
||||||
selectTabState,
|
selectTabState,
|
||||||
@ -86,6 +88,7 @@ import useLang from '../../hooks/useLang';
|
|||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
|
import useSyncEffectWithPrevDeps from '../../hooks/useSyncEffectWithPrevDeps.ts';
|
||||||
import useAsyncRendering from './hooks/useAsyncRendering';
|
import useAsyncRendering from './hooks/useAsyncRendering';
|
||||||
import useProfileState from './hooks/useProfileState';
|
import useProfileState from './hooks/useProfileState';
|
||||||
import useProfileViewportIds from './hooks/useProfileViewportIds';
|
import useProfileViewportIds from './hooks/useProfileViewportIds';
|
||||||
@ -112,7 +115,7 @@ import InfiniteScroll from '../ui/InfiniteScroll';
|
|||||||
import Link from '../ui/Link';
|
import Link from '../ui/Link';
|
||||||
import ListItem, { type MenuItemContextAction } from '../ui/ListItem';
|
import ListItem, { type MenuItemContextAction } from '../ui/ListItem';
|
||||||
import Spinner from '../ui/Spinner';
|
import Spinner from '../ui/Spinner';
|
||||||
import TabList from '../ui/TabList';
|
import TabList, { type TabWithProperties } from '../ui/TabList';
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
import DeleteMemberModal from './DeleteMemberModal';
|
import DeleteMemberModal from './DeleteMemberModal';
|
||||||
import StarGiftCollectionList from './gifts/StarGiftCollectionList';
|
import StarGiftCollectionList from './gifts/StarGiftCollectionList';
|
||||||
@ -179,24 +182,40 @@ type StateProps = {
|
|||||||
isSavedMessages?: boolean;
|
isSavedMessages?: boolean;
|
||||||
isSynced?: boolean;
|
isSynced?: boolean;
|
||||||
hasAvatar?: boolean;
|
hasAvatar?: boolean;
|
||||||
|
mainTab?: ApiProfileTab;
|
||||||
|
canUpdateMainTab?: boolean;
|
||||||
|
canAutoPlayGifs?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TabProps = {
|
type LocalTabProps = {
|
||||||
type: ProfileTabType;
|
type: ProfileTabType;
|
||||||
key: RegularLangKey;
|
key: RegularLangKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TABS: TabProps[] = [
|
type TabWithPropertiesAndType = TabWithProperties & {
|
||||||
|
type: ProfileTabType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TABS: LocalTabProps[] = [
|
||||||
{ type: 'media', key: 'ProfileTabMedia' },
|
{ type: 'media', key: 'ProfileTabMedia' },
|
||||||
{ type: 'documents', key: 'ProfileTabFiles' },
|
{ type: 'documents', key: 'ProfileTabFiles' },
|
||||||
{ type: 'links', key: 'ProfileTabLinks' },
|
{ type: 'links', key: 'ProfileTabLinks' },
|
||||||
{ type: 'audio', key: 'ProfileTabMusic' },
|
{ type: 'audio', key: 'ProfileTabMusic' },
|
||||||
|
{ type: 'gif', key: 'ProfileTabGifs' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const HIDDEN_RENDER_DELAY = 1000;
|
const HIDDEN_RENDER_DELAY = 1000;
|
||||||
const INTERSECTION_THROTTLE = 500;
|
const INTERSECTION_THROTTLE = 500;
|
||||||
|
|
||||||
const SHARED_MEDIA_TYPES = new Set<string>(['media', 'documents', 'links', 'audio', 'voice']);
|
const VALID_CHANNEL_MAIN_TAB_TYPES = new Set<StringAutocomplete<ApiProfileTab>>([
|
||||||
|
'stories', 'gifts', 'media', 'documents', 'audio', 'voice', 'links', 'gif',
|
||||||
|
]);
|
||||||
|
const VALID_USER_MAIN_TAB_TYPES = new Set<StringAutocomplete<ApiProfileTab>>([
|
||||||
|
'stories', 'gifts',
|
||||||
|
]);
|
||||||
|
const SHARED_MEDIA_TYPES = new Set<StringAutocomplete<SharedMediaType>>([
|
||||||
|
'media', 'documents', 'links', 'audio', 'voice', 'gif',
|
||||||
|
]);
|
||||||
|
|
||||||
const Profile = ({
|
const Profile = ({
|
||||||
chatId,
|
chatId,
|
||||||
@ -252,6 +271,9 @@ const Profile = ({
|
|||||||
isSavedMessages,
|
isSavedMessages,
|
||||||
isSynced,
|
isSynced,
|
||||||
hasAvatar,
|
hasAvatar,
|
||||||
|
mainTab,
|
||||||
|
canUpdateMainTab,
|
||||||
|
canAutoPlayGifs,
|
||||||
onProfileStateChange,
|
onProfileStateChange,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
@ -276,11 +298,14 @@ const Profile = ({
|
|||||||
loadStoryAlbums,
|
loadStoryAlbums,
|
||||||
resetSelectedStoryAlbum,
|
resetSelectedStoryAlbum,
|
||||||
changeProfileTab,
|
changeProfileTab,
|
||||||
|
setMainProfileTab,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
const transitionRef = useRef<HTMLDivElement>();
|
const transitionRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
const shouldSkipTransitionRef = useRef(false);
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
@ -296,8 +321,11 @@ const Profile = ({
|
|||||||
|
|
||||||
const [restoreContentHeightKey, setRestoreContentHeightKey] = useState(0);
|
const [restoreContentHeightKey, setRestoreContentHeightKey] = useState(0);
|
||||||
|
|
||||||
|
const isUser = isUserId(chatId);
|
||||||
|
const validMainTabTypes = isUser ? VALID_USER_MAIN_TAB_TYPES : VALID_CHANNEL_MAIN_TAB_TYPES;
|
||||||
|
|
||||||
const tabs = useMemo(() => {
|
const tabs = useMemo(() => {
|
||||||
const arr: TabProps[] = [];
|
const arr: LocalTabProps[] = [];
|
||||||
if (isGeneralSavedMessages) {
|
if (isGeneralSavedMessages) {
|
||||||
arr.push({ type: 'dialogs', key: 'ProfileTabSavedDialogs' });
|
arr.push({ type: 'dialogs', key: 'ProfileTabSavedDialogs' });
|
||||||
}
|
}
|
||||||
@ -306,16 +334,16 @@ const Profile = ({
|
|||||||
arr.push({ type: 'stories', key: 'ProfileTabStories' });
|
arr.push({ type: 'stories', key: 'ProfileTabStories' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasStoriesTab && isOwnProfile) {
|
|
||||||
arr.push({ type: 'storiesArchive', key: 'ProfileTabStoriesArchive' });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasGiftsTab) {
|
if (hasGiftsTab) {
|
||||||
arr.push({ type: 'gifts', key: 'ProfileTabGifts' });
|
arr.push({ type: 'gifts', key: 'ProfileTabGifts' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasStoriesTab && isOwnProfile) {
|
||||||
|
arr.push({ type: 'storiesArchive', key: 'ProfileTabStoriesArchive' });
|
||||||
|
}
|
||||||
|
|
||||||
if (hasMembersTab && !isOwnProfile) {
|
if (hasMembersTab && !isOwnProfile) {
|
||||||
arr.push({ type: 'members', key: isChannel ? 'ProfileTabSubscribers' : 'ProfileTabMembers' });
|
arr.push({ type: 'members', key: 'ProfileTabMembers' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPreviewMediaTab && !isOwnProfile) {
|
if (hasPreviewMediaTab && !isOwnProfile) {
|
||||||
@ -349,13 +377,35 @@ const Profile = ({
|
|||||||
arr.push(TABS[0]);
|
arr.push(TABS[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return arr.map((tab) => ({
|
if (mainTab) {
|
||||||
type: tab.type,
|
const mainTabIndex = arr.findIndex((tab) => tab.type === mainTab);
|
||||||
title: lang(tab.key),
|
if (mainTabIndex !== -1) {
|
||||||
}));
|
const newFirstTab = arr[mainTabIndex];
|
||||||
|
arr.splice(mainTabIndex, 1);
|
||||||
|
arr.unshift(newFirstTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr.map((tab) => {
|
||||||
|
const contextActions: MenuItemContextAction[] | undefined = canUpdateMainTab && mainTab !== tab.type
|
||||||
|
&& validMainTabTypes.has(tab.type) ? [{
|
||||||
|
title: lang('ProfileMenuSetMainTab'),
|
||||||
|
icon: 'replace',
|
||||||
|
handler: () => {
|
||||||
|
setMainProfileTab({ chatId, tab: tab.type as ApiProfileTab });
|
||||||
|
},
|
||||||
|
}] : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: tab.type,
|
||||||
|
title: lang(tab.key),
|
||||||
|
contextActions,
|
||||||
|
} satisfies TabWithPropertiesAndType;
|
||||||
|
});
|
||||||
}, [
|
}, [
|
||||||
isGeneralSavedMessages, hasStoriesTab, hasGiftsTab, hasMembersTab, hasPreviewMediaTab, isTopicInfo,
|
isGeneralSavedMessages, hasStoriesTab, hasGiftsTab, hasMembersTab, hasPreviewMediaTab, isTopicInfo,
|
||||||
hasCommonChatsTab, isChannel, isBot, similarChannels?.length, similarBots?.length, lang, isOwnProfile,
|
hasCommonChatsTab, isChannel, isBot, similarChannels?.length, similarBots?.length, lang, isOwnProfile,
|
||||||
|
mainTab, chatId, canUpdateMainTab, validMainTabTypes,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [allowAutoScrollToTabs, startAutoScrollToTabsIfNeeded, stopAutoScrollToTabs] = useFlag(false);
|
const [allowAutoScrollToTabs, startAutoScrollToTabsIfNeeded, stopAutoScrollToTabs] = useFlag(false);
|
||||||
@ -377,6 +427,11 @@ const Profile = ({
|
|||||||
setActiveTab(tabs[0].type); // Set default tab
|
setActiveTab(tabs[0].type); // Set default tab
|
||||||
}, [isClosed, profileTab, tabs]);
|
}, [isClosed, profileTab, tabs]);
|
||||||
|
|
||||||
|
useEffectWithPrevDeps(([prevMainTab]) => {
|
||||||
|
if (prevMainTab || !mainTab) return;
|
||||||
|
setActiveTab(mainTab); // Only focus when loading full info
|
||||||
|
}, [mainTab]);
|
||||||
|
|
||||||
const handleSwitchTab = useCallback((index: number) => {
|
const handleSwitchTab = useCallback((index: number) => {
|
||||||
startAutoScrollToTabsIfNeeded();
|
startAutoScrollToTabsIfNeeded();
|
||||||
setActiveTab(tabs[index].type);
|
setActiveTab(tabs[index].type);
|
||||||
@ -430,6 +485,17 @@ const Profile = ({
|
|||||||
return index === -1 ? 0 : index;
|
return index === -1 ? 0 : index;
|
||||||
}, [profileTab, tabs]);
|
}, [profileTab, tabs]);
|
||||||
|
|
||||||
|
// Reset skip transition flag from previous render
|
||||||
|
if (shouldSkipTransitionRef.current) {
|
||||||
|
shouldSkipTransitionRef.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
useSyncEffectWithPrevDeps(([prevProfileTab, prevActiveTabIndex]) => {
|
||||||
|
if (prevProfileTab === profileTab && prevActiveTabIndex !== activeTabIndex) {
|
||||||
|
shouldSkipTransitionRef.current = true;
|
||||||
|
}
|
||||||
|
}, [profileTab, activeTabIndex]);
|
||||||
|
|
||||||
const tabType = tabs[activeTabIndex].type;
|
const tabType = tabs[activeTabIndex].type;
|
||||||
const handleLoadCommonChats = useCallback(() => {
|
const handleLoadCommonChats = useCallback(() => {
|
||||||
loadCommonChats({ userId: chatId });
|
loadCommonChats({ userId: chatId });
|
||||||
@ -498,10 +564,7 @@ const Profile = ({
|
|||||||
|
|
||||||
const shouldRenderProfileInfo = !noProfileInfo && !isSavedMessages;
|
const shouldRenderProfileInfo = !noProfileInfo && !isSavedMessages;
|
||||||
|
|
||||||
const isFirstTab = (isGeneralSavedMessages && resultType === 'dialogs')
|
const isFirstTab = tabs[0].type === resultType;
|
||||||
|| (hasStoriesTab && resultType === 'stories')
|
|
||||||
|| resultType === 'members'
|
|
||||||
|| (!hasMembersTab && resultType === 'media');
|
|
||||||
const activeKey = tabs.findIndex(({ type }) => type === resultType);
|
const activeKey = tabs.findIndex(({ type }) => type === resultType);
|
||||||
|
|
||||||
const [isGiftCollectionsShowed, markGiftCollectionsShowed, unmarkGiftCollectionsShowed] = useFlag(false);
|
const [isGiftCollectionsShowed, markGiftCollectionsShowed, unmarkGiftCollectionsShowed] = useFlag(false);
|
||||||
@ -561,7 +624,7 @@ const Profile = ({
|
|||||||
handleStopAutoScrollToTabs,
|
handleStopAutoScrollToTabs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { applyTransitionFix, releaseTransitionFix } = useTransitionFixes(containerRef);
|
useTransitionFixes(containerRef);
|
||||||
|
|
||||||
const [cacheBuster, resetCacheBuster] = useCacheBuster();
|
const [cacheBuster, resetCacheBuster] = useCacheBuster();
|
||||||
|
|
||||||
@ -570,11 +633,6 @@ const Profile = ({
|
|||||||
throttleMs: INTERSECTION_THROTTLE,
|
throttleMs: INTERSECTION_THROTTLE,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleTransitionStop = useLastCallback(() => {
|
|
||||||
releaseTransitionFix();
|
|
||||||
resetCacheBuster();
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleNewMemberDialogOpen = useLastCallback(() => {
|
const handleNewMemberDialogOpen = useLastCallback(() => {
|
||||||
setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.InProgress });
|
setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.InProgress });
|
||||||
});
|
});
|
||||||
@ -613,16 +671,6 @@ const Profile = ({
|
|||||||
setDeletingUserId(undefined);
|
setDeletingUserId(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevHasMemberTabs]) => {
|
|
||||||
if (prevHasMemberTabs === undefined || activeTabIndex === 0 || prevHasMemberTabs === hasMembersTab) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newActiveTab = Math.min(activeTabIndex + (hasMembersTab ? 1 : -1), tabs.length - 1);
|
|
||||||
|
|
||||||
setActiveTab(tabs[newActiveTab].type);
|
|
||||||
}, [hasMembersTab, activeTabIndex, tabs]);
|
|
||||||
|
|
||||||
const handleResetGiftsFilter = useLastCallback(() => {
|
const handleResetGiftsFilter = useLastCallback(() => {
|
||||||
resetGiftProfileFilter({ peerId: chatId });
|
resetGiftProfileFilter({ peerId: chatId });
|
||||||
});
|
});
|
||||||
@ -800,6 +848,9 @@ const Profile = ({
|
|||||||
case 'storiesArchive':
|
case 'storiesArchive':
|
||||||
text = oldLang('StoryList.ArchivedEmptyState.Title');
|
text = oldLang('StoryList.ArchivedEmptyState.Title');
|
||||||
break;
|
break;
|
||||||
|
case 'gif':
|
||||||
|
text = oldLang('lng_media_gif_empty');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
text = oldLang('SharedMedia.EmptyTitle');
|
text = oldLang('SharedMedia.EmptyTitle');
|
||||||
}
|
}
|
||||||
@ -825,15 +876,16 @@ const Profile = ({
|
|||||||
shouldShowContentPanel && 'showContentPanel',
|
shouldShowContentPanel && 'showContentPanel',
|
||||||
noTransition && 'noTransition',
|
noTransition && 'noTransition',
|
||||||
)}
|
)}
|
||||||
dir={oldLang.isRtl && resultType === 'media' ? 'rtl' : undefined}
|
dir={lang.isRtl && (resultType === 'media' || resultType === 'gif') ? 'rtl' : undefined}
|
||||||
teactFastList
|
teactFastList
|
||||||
>
|
>
|
||||||
{resultType === 'media' ? (
|
{resultType === 'media' || resultType === 'gif' ? (
|
||||||
(viewportIds as number[]).map((id) => messagesById[id] && (
|
(viewportIds as number[]).map((id) => messagesById[id] && (
|
||||||
<Media
|
<Media
|
||||||
key={id}
|
key={id}
|
||||||
message={messagesById[id]}
|
message={messagesById[id]}
|
||||||
isProtected={isChatProtected || messagesById[id].isProtected}
|
isProtected={isChatProtected || messagesById[id].isProtected}
|
||||||
|
canAutoPlay={canAutoPlayGifs}
|
||||||
observeIntersection={observeIntersectionForMedia}
|
observeIntersection={observeIntersectionForMedia}
|
||||||
onClick={handleSelectMedia}
|
onClick={handleSelectMedia}
|
||||||
/>
|
/>
|
||||||
@ -988,17 +1040,10 @@ const Profile = ({
|
|||||||
|
|
||||||
onClick={() => openChat({ id: userId })}
|
onClick={() => openChat({ id: userId })}
|
||||||
>
|
>
|
||||||
{isUserId(userId) ? (
|
<PrivateChatInfo
|
||||||
<PrivateChatInfo
|
userId={userId}
|
||||||
userId={userId}
|
avatarSize="medium"
|
||||||
avatarSize="medium"
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<GroupChatInfo
|
|
||||||
chatId={userId}
|
|
||||||
avatarSize="medium"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
{!isCurrentUserPremium && (
|
{!isCurrentUserPremium && (
|
||||||
@ -1081,7 +1126,7 @@ const Profile = ({
|
|||||||
<Transition
|
<Transition
|
||||||
className={`${resultType}-list`}
|
className={`${resultType}-list`}
|
||||||
activeKey={contentTransitionKey}
|
activeKey={contentTransitionKey}
|
||||||
name={resolveTransitionName('slideOptimized', animationLevel, undefined, oldLang.isRtl)}
|
name={resolveTransitionName('slideOptimized', animationLevel, undefined, lang.isRtl)}
|
||||||
shouldCleanup
|
shouldCleanup
|
||||||
shouldRestoreHeight
|
shouldRestoreHeight
|
||||||
restoreHeightKey={restoreContentHeightKey}
|
restoreHeightKey={restoreContentHeightKey}
|
||||||
@ -1149,13 +1194,13 @@ const Profile = ({
|
|||||||
>
|
>
|
||||||
<Transition
|
<Transition
|
||||||
ref={transitionRef}
|
ref={transitionRef}
|
||||||
name={resolveTransitionName('slideOptimized', animationLevel, undefined, oldLang.isRtl)}
|
name={shouldSkipTransitionRef.current ? 'none'
|
||||||
|
: resolveTransitionName('slideOptimized', animationLevel, undefined, lang.isRtl)}
|
||||||
activeKey={activeKey}
|
activeKey={activeKey}
|
||||||
renderCount={tabs.length}
|
renderCount={tabs.length}
|
||||||
shouldRestoreHeight
|
shouldRestoreHeight
|
||||||
className="shared-media-transition"
|
className="shared-media-transition"
|
||||||
onStart={applyTransitionFix}
|
onStop={resetCacheBuster}
|
||||||
onStop={handleTransitionStop}
|
|
||||||
restoreHeightKey={shouldUseTransitionForContent ? restoreContentHeightKey : undefined}
|
restoreHeightKey={shouldUseTransitionForContent ? restoreContentHeightKey : undefined}
|
||||||
contentSelector={shouldUseTransitionForContent
|
contentSelector={shouldUseTransitionForContent
|
||||||
? '.Transition > .Transition_slide-active > .Transition > .Transition_slide-active > .content'
|
? '.Transition > .Transition_slide-active > .Transition > .Transition_slide-active > .content'
|
||||||
@ -1218,8 +1263,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const isGroup = chat && isChatGroup(chat);
|
const isGroup = chat && isChatGroup(chat);
|
||||||
const isChannel = chat && isChatChannel(chat);
|
const isChannel = chat && isChatChannel(chat);
|
||||||
const isBot = user && isUserBot(user);
|
const isBot = user && isUserBot(user);
|
||||||
const hasMembersTab = !isTopicInfo && !isSavedDialog
|
const hasMembersTab = !isTopicInfo && !isSavedDialog && isGroup && !chat?.isMonoforum;
|
||||||
&& (isGroup || (isChannel && isChatAdmin(chat))) && !chat?.isMonoforum;
|
|
||||||
const members = chatFullInfo?.members;
|
const members = chatFullInfo?.members;
|
||||||
const adminMembersById = chatFullInfo?.adminMembersById;
|
const adminMembersById = chatFullInfo?.adminMembersById;
|
||||||
const areMembersHidden = hasMembersTab && chat
|
const areMembersHidden = hasMembersTab && chat
|
||||||
@ -1265,6 +1309,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const isRestricted = chat && selectIsChatRestricted(global, chat.id);
|
const isRestricted = chat && selectIsChatRestricted(global, chat.id);
|
||||||
const hasAvatar = Boolean(peer?.avatarPhotoId);
|
const hasAvatar = Boolean(peer?.avatarPhotoId);
|
||||||
|
|
||||||
|
const canAutoPlayGifs = selectPerformanceSettingsValue(global, 'autoplayGifs');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
theme: selectTheme(global),
|
theme: selectTheme(global),
|
||||||
isChannel,
|
isChannel,
|
||||||
@ -1315,6 +1361,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
commonChatIds: commonChats?.ids,
|
commonChatIds: commonChats?.ids,
|
||||||
monoforumChannel,
|
monoforumChannel,
|
||||||
hasAvatar,
|
hasAvatar,
|
||||||
|
mainTab: peerFullInfo?.mainTab,
|
||||||
|
canUpdateMainTab: selectCanUpdateMainTab(global, chatId),
|
||||||
|
canAutoPlayGifs,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(Profile));
|
)(Profile));
|
||||||
|
|||||||
@ -89,6 +89,10 @@ export default function useProfileViewportIds({
|
|||||||
'media', resultType, searchMessages, chatMessages, foundIds, threadId,
|
'media', resultType, searchMessages, chatMessages, foundIds, threadId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [gifViewportIds, getMoreGifs, noProfileInfoForGifs] = useInfiniteScrollForSharedMedia(
|
||||||
|
'gif', resultType, searchMessages, chatMessages, foundIds, threadId,
|
||||||
|
);
|
||||||
|
|
||||||
const [documentViewportIds, getMoreDocuments, noProfileInfoForDocuments] = useInfiniteScrollForSharedMedia(
|
const [documentViewportIds, getMoreDocuments, noProfileInfoForDocuments] = useInfiniteScrollForSharedMedia(
|
||||||
'documents', resultType, searchMessages, chatMessages, foundIds, threadId,
|
'documents', resultType, searchMessages, chatMessages, foundIds, threadId,
|
||||||
);
|
);
|
||||||
@ -153,6 +157,11 @@ export default function useProfileViewportIds({
|
|||||||
getMore = getMoreMedia;
|
getMore = getMoreMedia;
|
||||||
noProfileInfo = noProfileInfoForMedia;
|
noProfileInfo = noProfileInfoForMedia;
|
||||||
break;
|
break;
|
||||||
|
case 'gif':
|
||||||
|
viewportIds = gifViewportIds;
|
||||||
|
getMore = getMoreGifs;
|
||||||
|
noProfileInfo = noProfileInfoForGifs;
|
||||||
|
break;
|
||||||
case 'documents':
|
case 'documents':
|
||||||
viewportIds = documentViewportIds;
|
viewportIds = documentViewportIds;
|
||||||
getMore = getMoreDocuments;
|
getMore = getMoreDocuments;
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import type { ElementRef } from '../../../lib/teact/teact';
|
import type { ElementRef } from '../../../lib/teact/teact';
|
||||||
import { useEffect } from '../../../lib/teact/teact';
|
import { useEffect } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import { requestMeasure, requestMutation } from '../../../lib/fasterdom/fasterdom';
|
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||||
|
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
|
||||||
|
|
||||||
export default function useTransitionFixes(
|
export default function useTransitionFixes(
|
||||||
containerRef: ElementRef<HTMLDivElement>,
|
containerRef: ElementRef<HTMLDivElement>,
|
||||||
@ -32,28 +30,4 @@ export default function useTransitionFixes(
|
|||||||
window.removeEventListener('resize', setMinHeight, false);
|
window.removeEventListener('resize', setMinHeight, false);
|
||||||
};
|
};
|
||||||
}, [containerRef, transitionElSelector]);
|
}, [containerRef, transitionElSelector]);
|
||||||
|
|
||||||
// Workaround for scrollable content flickering during animation.
|
|
||||||
const applyTransitionFix = useLastCallback(() => {
|
|
||||||
// This callback is called from `Transition.onStart` which is "mutate" phase
|
|
||||||
requestMeasure(() => {
|
|
||||||
const container = containerRef.current!;
|
|
||||||
if (container.style.overflowY === 'hidden') return;
|
|
||||||
|
|
||||||
const scrollBarWidth = container.offsetWidth - container.clientWidth;
|
|
||||||
|
|
||||||
requestMutation(() => {
|
|
||||||
container.style.overflowY = 'hidden';
|
|
||||||
container.style.paddingRight = `${scrollBarWidth}px`;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const releaseTransitionFix = useLastCallback(() => {
|
|
||||||
const container = containerRef.current!;
|
|
||||||
container.style.overflowY = 'scroll';
|
|
||||||
container.style.paddingRight = '0';
|
|
||||||
});
|
|
||||||
|
|
||||||
return { applyTransitionFix, releaseTransitionFix };
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import type { FC, TeactNode } from '../../lib/teact/teact';
|
import type { TeactNode } from '../../lib/teact/teact';
|
||||||
import type React from '../../lib/teact/teact';
|
|
||||||
import { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact';
|
import { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { MenuItemContextAction } from './ListItem';
|
import type { MenuItemContextAction } from './ListItem';
|
||||||
@ -40,7 +39,7 @@ const classNames = {
|
|||||||
badgeActive: 'Tab__badge--active',
|
badgeActive: 'Tab__badge--active',
|
||||||
};
|
};
|
||||||
|
|
||||||
const Tab: FC<OwnProps> = ({
|
const Tab = ({
|
||||||
className,
|
className,
|
||||||
title,
|
title,
|
||||||
isActive,
|
isActive,
|
||||||
@ -48,11 +47,11 @@ const Tab: FC<OwnProps> = ({
|
|||||||
badgeCount,
|
badgeCount,
|
||||||
isBadgeActive,
|
isBadgeActive,
|
||||||
previousActiveTab,
|
previousActiveTab,
|
||||||
onClick,
|
|
||||||
clickArg,
|
|
||||||
contextActions,
|
contextActions,
|
||||||
contextRootElementSelector,
|
contextRootElementSelector,
|
||||||
}) => {
|
clickArg,
|
||||||
|
onClick,
|
||||||
|
}: OwnProps) => {
|
||||||
const tabRef = useRef<HTMLDivElement>();
|
const tabRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { FC, TeactNode } from '../../lib/teact/teact';
|
import type { TeactNode } from '../../lib/teact/teact';
|
||||||
import { memo, useEffect, useRef } from '../../lib/teact/teact';
|
import { memo, useEffect, useRef } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { MenuItemContextAction } from './ListItem';
|
import type { MenuItemContextAction } from './ListItem';
|
||||||
@ -37,10 +37,14 @@ const TAB_SCROLL_THRESHOLD_PX = 16;
|
|||||||
// Should match duration from `--slide-transition` CSS variable
|
// Should match duration from `--slide-transition` CSS variable
|
||||||
const SCROLL_DURATION = IS_IOS ? 450 : IS_ANDROID ? 400 : 300;
|
const SCROLL_DURATION = IS_IOS ? 450 : IS_ANDROID ? 400 : 300;
|
||||||
|
|
||||||
const TabList: FC<OwnProps> = ({
|
const TabList = ({
|
||||||
tabs, activeTab, onSwitchTab,
|
tabs,
|
||||||
contextRootElementSelector, className, tabClassName,
|
activeTab,
|
||||||
}) => {
|
className,
|
||||||
|
tabClassName,
|
||||||
|
contextRootElementSelector,
|
||||||
|
onSwitchTab,
|
||||||
|
}: OwnProps) => {
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
const previousActiveTab = usePreviousDeprecated(activeTab);
|
const previousActiveTab = usePreviousDeprecated(activeTab);
|
||||||
|
|
||||||
|
|||||||
@ -118,6 +118,7 @@ import {
|
|||||||
selectIsCurrentUserFrozen,
|
selectIsCurrentUserFrozen,
|
||||||
selectLastServiceNotification,
|
selectLastServiceNotification,
|
||||||
selectPeer,
|
selectPeer,
|
||||||
|
selectPeerFullInfo,
|
||||||
selectSimilarChannelIds,
|
selectSimilarChannelIds,
|
||||||
selectStickerSet,
|
selectStickerSet,
|
||||||
selectSupportChat,
|
selectSupportChat,
|
||||||
@ -3061,6 +3062,32 @@ addActionHandler('toggleAutoTranslation', async (global, actions, payload): Prom
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('setMainProfileTab', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { chatId, tab } = payload;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
const peerFullInfo = selectPeerFullInfo(global, chatId);
|
||||||
|
const oldMainTab = peerFullInfo?.mainTab;
|
||||||
|
if (oldMainTab === tab) return;
|
||||||
|
|
||||||
|
global = updatePeerFullInfo(global, chatId, { mainTab: tab });
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
let result;
|
||||||
|
if (chatId === global.currentUserId) {
|
||||||
|
result = await callApi('setAccountMainProfileTab', { tab });
|
||||||
|
} else {
|
||||||
|
result = await callApi('setChannelMainProfileTab', { chat, tab });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
global = getGlobal();
|
||||||
|
global = updatePeerFullInfo(global, chatId, { mainTab: oldMainTab });
|
||||||
|
setGlobal(global);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('resolveBusinessChatLink', async (global, actions, payload): Promise<void> => {
|
addActionHandler('resolveBusinessChatLink', async (global, actions, payload): Promise<void> => {
|
||||||
const { slug, tabId = getCurrentTabId() } = payload;
|
const { slug, tabId = getCurrentTabId() } = payload;
|
||||||
const result = await callApi('resolveBusinessChatLink', { slug });
|
const result = await callApi('resolveBusinessChatLink', { slug });
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
|||||||
import { buildCollectionByKey, isInsideSortedArrayRange } from '../../../util/iteratees';
|
import { buildCollectionByKey, isInsideSortedArrayRange } from '../../../util/iteratees';
|
||||||
import { getSearchResultKey } from '../../../util/keys/searchResultKey';
|
import { getSearchResultKey } from '../../../util/keys/searchResultKey';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import { getChatMediaMessageIds, getIsSavedDialog, isSameReaction } from '../../helpers';
|
import { getIsSavedDialog, getMessageContentIds, isSameReaction } from '../../helpers';
|
||||||
import {
|
import {
|
||||||
addActionHandler, getGlobal, setGlobal,
|
addActionHandler, getGlobal, setGlobal,
|
||||||
} from '../../index';
|
} from '../../index';
|
||||||
@ -479,7 +479,7 @@ async function searchChatMedia<T extends GlobalState>(
|
|||||||
|
|
||||||
const loadingState = calcLoadingState(direction, limit, newFoundIds.length, currentSegment);
|
const loadingState = calcLoadingState(direction, limit, newFoundIds.length, currentSegment);
|
||||||
|
|
||||||
const filteredIds = getChatMediaMessageIds(byId, newFoundIds, false);
|
const filteredIds = getMessageContentIds(byId, newFoundIds, 'inlineMedia');
|
||||||
currentSegment = mergeWithChatMediaSearchSegment(
|
currentSegment = mergeWithChatMediaSearchSegment(
|
||||||
filteredIds,
|
filteredIds,
|
||||||
loadingState,
|
loadingState,
|
||||||
|
|||||||
@ -429,12 +429,6 @@ export function hasMediaLocalBlobUrl(media: ApiPhoto | ApiVideo | ApiDocument) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChatMediaMessageIds(
|
|
||||||
messages: Record<number, ApiMessage>, listedIds: number[], isFromSharedMedia = false,
|
|
||||||
) {
|
|
||||||
return getMessageContentIds(messages, listedIds, isFromSharedMedia ? 'media' : 'inlineMedia');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPhotoFullDimensions(photo: Pick<ApiPhoto, 'sizes' | 'thumbnail'>): ApiDimensions | undefined {
|
export function getPhotoFullDimensions(photo: Pick<ApiPhoto, 'sizes' | 'thumbnail'>): ApiDimensions | undefined {
|
||||||
return (
|
return (
|
||||||
photo.sizes.find((size) => size.type === 'w')
|
photo.sizes.find((size) => size.type === 'w')
|
||||||
@ -488,6 +482,13 @@ export function getMessageContentIds(
|
|||||||
validator = getMessageDocument;
|
validator = getMessageDocument;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'gif':
|
||||||
|
validator = (message: ApiMessage) => {
|
||||||
|
const video = getMessageVideo(message);
|
||||||
|
return video?.isGif;
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
case 'links':
|
case 'links':
|
||||||
validator = (message: ApiMessage) => getMessageWebPage(message) || matchLinkInMessageText(message);
|
validator = (message: ApiMessage) => getMessageWebPage(message) || matchLinkInMessageText(message);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type { GlobalState, TabArgs } from '../types';
|
|||||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
||||||
import { isUserId } from '../../util/entities/ids';
|
import { isUserId } from '../../util/entities/ids';
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
import { isChatAdmin, isDeletedUser } from '../helpers';
|
import { getHasAdminRight, isChatAdmin, isChatChannel, isDeletedUser } from '../helpers';
|
||||||
import { selectChat, selectChatFullInfo } from './chats';
|
import { selectChat, selectChatFullInfo } from './chats';
|
||||||
import { type ProfileCollectionKey } from './payments';
|
import { type ProfileCollectionKey } from './payments';
|
||||||
import { selectTabState } from './tabs';
|
import { selectTabState } from './tabs';
|
||||||
@ -74,3 +74,12 @@ export function selectPeerHasProfileBackground<T extends GlobalState>(global: T,
|
|||||||
const peer = selectPeer(global, peerId);
|
const peer = selectPeer(global, peerId);
|
||||||
return Boolean(peer?.profileColor || peer?.emojiStatus?.type === 'collectible');
|
return Boolean(peer?.profileColor || peer?.emojiStatus?.type === 'collectible');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectCanUpdateMainTab<T extends GlobalState>(global: T, peerId: string) {
|
||||||
|
if (global.currentUserId === peerId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chat = selectChat(global, peerId);
|
||||||
|
return Boolean(chat && isChatChannel(chat) && getHasAdminRight(chat, 'postMessages'));
|
||||||
|
}
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import type {
|
|||||||
ApiPreparedInlineMessage,
|
ApiPreparedInlineMessage,
|
||||||
ApiPrivacyKey,
|
ApiPrivacyKey,
|
||||||
ApiPrivacySettings,
|
ApiPrivacySettings,
|
||||||
|
ApiProfileTab,
|
||||||
ApiReaction,
|
ApiReaction,
|
||||||
ApiReactionWithPaid,
|
ApiReactionWithPaid,
|
||||||
ApiReportReason,
|
ApiReportReason,
|
||||||
@ -1142,6 +1143,10 @@ export interface ActionPayloads {
|
|||||||
chatId: string;
|
chatId: string;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
setMainProfileTab: {
|
||||||
|
chatId: string;
|
||||||
|
tab: ApiProfileTab;
|
||||||
|
};
|
||||||
|
|
||||||
updateChat: {
|
updateChat: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type {
|
|||||||
ApiCheckedGiftCode,
|
ApiCheckedGiftCode,
|
||||||
ApiCollectibleInfo,
|
ApiCollectibleInfo,
|
||||||
ApiContact,
|
ApiContact,
|
||||||
|
ApiEmojiStatusCollectible,
|
||||||
ApiError,
|
ApiError,
|
||||||
ApiFormattedText,
|
ApiFormattedText,
|
||||||
ApiGeoPoint,
|
ApiGeoPoint,
|
||||||
@ -54,7 +55,6 @@ import type {
|
|||||||
ApiUser,
|
ApiUser,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { ApiEmojiStatusCollectible } from '../../api/types/users';
|
|
||||||
import type { FoldersActions } from '../../hooks/reducers/useFoldersReducer';
|
import type { FoldersActions } from '../../hooks/reducers/useFoldersReducer';
|
||||||
import type { ReducerAction } from '../../hooks/useReducer';
|
import type { ReducerAction } from '../../hooks/useReducer';
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { getGlobal } from '../../global';
|
|||||||
import type { AnimationLevel } from '../../types';
|
import type { AnimationLevel } from '../../types';
|
||||||
import type { VTTypes } from '../../util/animations/viewTransitionTypes';
|
import type { VTTypes } from '../../util/animations/viewTransitionTypes';
|
||||||
|
|
||||||
import { VT_CLASS_NAME, VT_TYPE_CLASS_PREFIX } from '../../config';
|
import { ANIMATION_LEVEL_MED, VT_CLASS_NAME, VT_TYPE_CLASS_PREFIX } from '../../config';
|
||||||
import { requestMutation, requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
import { requestMutation, requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
||||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||||
import { IS_VIEW_TRANSITION_SUPPORTED } from '../../util/browser/windowEnvironment';
|
import { IS_VIEW_TRANSITION_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||||
@ -98,7 +98,7 @@ export function useViewTransition(): ViewTransitionController {
|
|||||||
function startViewTransition(
|
function startViewTransition(
|
||||||
types: VTTypes,
|
types: VTTypes,
|
||||||
updateCallback?: TransitionFunction,
|
updateCallback?: TransitionFunction,
|
||||||
minimumAnimationLevel: AnimationLevel = 1,
|
minimumAnimationLevel: AnimationLevel = ANIMATION_LEVEL_MED,
|
||||||
): PromiseLike<void> | void {
|
): PromiseLike<void> | void {
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
const { animationLevel } = selectSharedSettings(global);
|
const { animationLevel } = selectSharedSettings(global);
|
||||||
|
|||||||
@ -1537,6 +1537,7 @@ account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
|||||||
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
||||||
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
|
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
|
||||||
account.toggleNoPaidMessagesException#fe2eda76 flags:# refund_charged:flags.0?true require_payment:flags.2?true parent_peer:flags.1?InputPeer user_id:InputUser = Bool;
|
account.toggleNoPaidMessagesException#fe2eda76 flags:# refund_charged:flags.0?true require_payment:flags.2?true parent_peer:flags.1?InputPeer user_id:InputUser = Bool;
|
||||||
|
account.setMainProfileTab#5dee78b0 tab:ProfileTab = Bool;
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||||
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
||||||
@ -1770,6 +1771,7 @@ channels.searchPosts#f2c4f24d flags:# hashtag:flags.0?string query:flags.1?strin
|
|||||||
channels.updatePaidMessagesPrice#4b12327b flags:# broadcast_messages_allowed:flags.0?true channel:InputChannel send_paid_messages_stars:long = Updates;
|
channels.updatePaidMessagesPrice#4b12327b flags:# broadcast_messages_allowed:flags.0?true channel:InputChannel send_paid_messages_stars:long = Updates;
|
||||||
channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
|
channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
|
||||||
channels.checkSearchPostsFlood#22567115 flags:# query:flags.0?string = SearchPostsFlood;
|
channels.checkSearchPostsFlood#22567115 flags:# query:flags.0?string = SearchPostsFlood;
|
||||||
|
channels.setMainProfileTab#3583fcb1 channel:InputChannel tab:ProfileTab = Bool;
|
||||||
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
|
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
|
||||||
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
|
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
|
||||||
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
|
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
|
||||||
|
|||||||
@ -66,6 +66,7 @@
|
|||||||
"account.getPaidMessagesRevenue",
|
"account.getPaidMessagesRevenue",
|
||||||
"account.getAccountTTL",
|
"account.getAccountTTL",
|
||||||
"account.setAccountTTL",
|
"account.setAccountTTL",
|
||||||
|
"account.setMainProfileTab",
|
||||||
"users.getUsers",
|
"users.getUsers",
|
||||||
"users.getFullUser",
|
"users.getFullUser",
|
||||||
"contacts.getContacts",
|
"contacts.getContacts",
|
||||||
@ -288,6 +289,7 @@
|
|||||||
"channels.updatePaidMessagesPrice",
|
"channels.updatePaidMessagesPrice",
|
||||||
"channels.checkSearchPostsFlood",
|
"channels.checkSearchPostsFlood",
|
||||||
"channels.toggleAutotranslation",
|
"channels.toggleAutotranslation",
|
||||||
|
"channels.setMainProfileTab",
|
||||||
"bots.getBotRecommendations",
|
"bots.getBotRecommendations",
|
||||||
"bots.canSendMessage",
|
"bots.canSendMessage",
|
||||||
"bots.allowSendMessage",
|
"bots.allowSendMessage",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -394,13 +394,14 @@ export type ProfileTabType =
|
|||||||
| 'links'
|
| 'links'
|
||||||
| 'audio'
|
| 'audio'
|
||||||
| 'voice'
|
| 'voice'
|
||||||
|
| 'gif'
|
||||||
| 'stories'
|
| 'stories'
|
||||||
| 'storiesArchive'
|
| 'storiesArchive'
|
||||||
| 'similarChannels'
|
| 'similarChannels'
|
||||||
| 'similarBots'
|
| 'similarBots'
|
||||||
| 'dialogs'
|
| 'dialogs'
|
||||||
| 'gifts';
|
| 'gifts';
|
||||||
export type SharedMediaType = 'media' | 'documents' | 'links' | 'audio' | 'voice';
|
export type SharedMediaType = 'media' | 'documents' | 'links' | 'audio' | 'voice' | 'gif';
|
||||||
export type MiddleSearchType = 'chat' | 'myChats' | 'channels';
|
export type MiddleSearchType = 'chat' | 'myChats' | 'channels';
|
||||||
export type MiddleSearchParams = {
|
export type MiddleSearchParams = {
|
||||||
requestedQuery?: string;
|
requestedQuery?: string;
|
||||||
|
|||||||
3
src/types/language.d.ts
vendored
3
src/types/language.d.ts
vendored
@ -1222,6 +1222,8 @@ export interface LangPair {
|
|||||||
'ProfileBotOpenAppInfoLink': undefined;
|
'ProfileBotOpenAppInfoLink': undefined;
|
||||||
'ProfileBirthday': undefined;
|
'ProfileBirthday': undefined;
|
||||||
'ProfileBirthdayToday': undefined;
|
'ProfileBirthdayToday': undefined;
|
||||||
|
'ProfileMenuSetMainTab': undefined;
|
||||||
|
'ProfileItemSubscribers': undefined;
|
||||||
'MonetizationInfoTONTitle': undefined;
|
'MonetizationInfoTONTitle': undefined;
|
||||||
'AriaSearchOlderResult': undefined;
|
'AriaSearchOlderResult': undefined;
|
||||||
'AriaSearchNewerResult': undefined;
|
'AriaSearchNewerResult': undefined;
|
||||||
@ -1395,6 +1397,7 @@ export interface LangPair {
|
|||||||
'ProfileTabLinks': undefined;
|
'ProfileTabLinks': undefined;
|
||||||
'ProfileTabMusic': undefined;
|
'ProfileTabMusic': undefined;
|
||||||
'ProfileTabVoice': undefined;
|
'ProfileTabVoice': undefined;
|
||||||
|
'ProfileTabGifs': undefined;
|
||||||
'ProfileTabSharedGroups': undefined;
|
'ProfileTabSharedGroups': undefined;
|
||||||
'ProfileTabSimilarChannels': undefined;
|
'ProfileTabSimilarChannels': undefined;
|
||||||
'ProfileTabSimilarBots': undefined;
|
'ProfileTabSimilarBots': undefined;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user