+
);
diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts
index 9d61b5472..2e3ea3217 100644
--- a/src/global/actions/api/chats.ts
+++ b/src/global/actions/api/chats.ts
@@ -42,6 +42,7 @@ import {
isChatSummaryOnly,
isChatSuperGroup,
isUserBot,
+ toChannelId,
} from '../../helpers';
import {
addActionHandler, getGlobal, setGlobal,
@@ -973,6 +974,7 @@ addActionHandler('openTelegramLink', (global, actions, payload): ActionReturnTyp
checkChatlistInvite,
openChatByUsername: openChatByUsernameAction,
openStoryViewerByUsername,
+ processBoostParameters,
} = actions;
if (url.match(RE_TG_LINK)) {
@@ -1002,6 +1004,7 @@ addActionHandler('openTelegramLink', (global, actions, payload): ActionReturnTyp
const hasStartApp = params.hasOwnProperty('startapp');
const choose = parseChooseParameter(params.choose);
const storyId = part2 === 's' && (Number(part3) || undefined);
+ const hasBoost = params.hasOwnProperty('boost');
if (part1.match(/^\+([0-9]+)(\?|$)/)) {
openChatByPhoneNumber({
@@ -1064,19 +1067,39 @@ addActionHandler('openTelegramLink', (global, actions, payload): ActionReturnTyp
inviteHash: params.voicechat || params.livestream,
tabId,
});
+ } else if (part1 === 'boost') {
+ const username = part2;
+ const id = params.c;
+
+ const isPrivate = !username && Boolean(id);
+
+ processBoostParameters({
+ usernameOrId: username || id,
+ isPrivate,
+ tabId,
+ });
+ } else if (hasBoost) {
+ const isPrivate = part1 === 'c' && Boolean(chatOrChannelPostId);
+ processBoostParameters({
+ usernameOrId: chatOrChannelPostId || part1,
+ isPrivate,
+ tabId,
+ });
} else if (part1 === 'c' && chatOrChannelPostId && messageId) {
- const chatId = `-100${chatOrChannelPostId}`;
+ const chatId = toChannelId(chatOrChannelPostId);
const chat = selectChat(global, chatId);
if (!chat) {
showNotification({ message: 'Chat does not exist', tabId });
return;
}
- focusMessage({
- chatId: chat.id,
- messageId,
- tabId,
- });
+ if (messageId) {
+ focusMessage({
+ chatId: chat.id,
+ messageId,
+ tabId,
+ });
+ }
} else if (part1.startsWith('$')) {
openInvoice({
slug: part1.substring(1),
@@ -1110,6 +1133,37 @@ addActionHandler('openTelegramLink', (global, actions, payload): ActionReturnTyp
}
});
+addActionHandler('processBoostParameters', async (global, actions, payload): Promise
=> {
+ const { usernameOrId, isPrivate, tabId = getCurrentTabId() } = payload;
+
+ let chat: ApiChat | undefined;
+
+ if (isPrivate) {
+ const chatId = toChannelId(usernameOrId);
+ chat = selectChat(global, chatId);
+ if (!chat) {
+ actions.showNotification({ message: 'Chat does not exist', tabId });
+ return;
+ }
+ } else {
+ chat = await fetchChatByUsername(global, usernameOrId);
+ if (!chat) {
+ actions.showNotification({ message: 'User does not exist', tabId });
+ return;
+ }
+ }
+
+ if (!isChatChannel(chat)) {
+ actions.openChat({ id: chat.id, tabId });
+ return;
+ }
+
+ actions.openBoostModal({
+ chatId: chat.id,
+ tabId,
+ });
+});
+
addActionHandler('acceptInviteConfirmation', async (global, actions, payload): Promise => {
const { hash, tabId = getCurrentTabId() } = payload!;
const result = await callApi('importChatInvite', { hash });
@@ -1139,7 +1193,9 @@ addActionHandler('openChatByUsername', async (global, actions, payload): Promise
return;
}
if (!isWebApp) {
- await openChatByUsername(global, actions, username, threadId, messageId, startParam, startAttach, attach, tabId);
+ await openChatByUsername(
+ global, actions, username, threadId, messageId, startParam, startAttach, attach, tabId,
+ );
return;
}
}
diff --git a/src/global/actions/api/stories.ts b/src/global/actions/api/stories.ts
index bcb31a0db..d1d514ed8 100644
--- a/src/global/actions/api/stories.ts
+++ b/src/global/actions/api/stories.ts
@@ -6,7 +6,7 @@ import { buildCollectionByKey } from '../../../util/iteratees';
import { translate } from '../../../util/langProvider';
import { getServerTime } from '../../../util/serverTime';
import { callApi } from '../../../api/gramjs';
-import { buildApiInputPrivacyRules } from '../../helpers';
+import { buildApiInputPrivacyRules, isChatChannel } from '../../helpers';
import { addActionHandler, getGlobal, setGlobal } from '../../index';
import {
addChats,
@@ -26,8 +26,10 @@ import {
updateStoryViews,
updateStoryViewsLoading,
} from '../../reducers';
+import { updateTabState } from '../../reducers/tabs';
import {
- selectPeer, selectPeerStories, selectPeerStory,
+ selectChat,
+ selectPeer, selectPeerStories, selectPeerStory, selectTabState,
} from '../../selectors';
const INFINITE_LOOP_MARKER = 100;
@@ -502,3 +504,89 @@ addActionHandler('activateStealthMode', (global, actions, payload): ActionReturn
callApi('activateStealthMode', { isForPast: isForPast || true, isForFuture: isForFuture || true });
});
+
+addActionHandler('openBoostModal', async (global, actions, payload): Promise => {
+ const { chatId, tabId = getCurrentTabId() } = payload;
+ const chat = selectChat(global, chatId);
+ if (!chat || !isChatChannel(chat)) return;
+
+ global = updateTabState(global, {
+ boostModal: {
+ chatId,
+ },
+ }, tabId);
+ setGlobal(global);
+
+ const result = await callApi('fetchBoostsStatus', {
+ chat,
+ });
+
+ if (!result) {
+ actions.closeBoostModal({ tabId });
+ return;
+ }
+
+ global = getGlobal();
+ global = updateTabState(global, {
+ boostModal: {
+ chatId,
+ boostStatus: result,
+ },
+ }, tabId);
+ setGlobal(global);
+
+ const applyInfoResult = await callApi('fetchCanApplyBoost', {
+ chat,
+ });
+
+ if (!applyInfoResult?.info) return;
+
+ const applyInfo = applyInfoResult.info;
+
+ global = getGlobal();
+ const tabState = selectTabState(global, tabId);
+ if (!tabState.boostModal) return;
+
+ global = addChats(global, buildCollectionByKey(applyInfoResult.chats, 'id'));
+ global = updateTabState(global, {
+ boostModal: {
+ ...tabState.boostModal,
+ applyInfo,
+ },
+ }, tabId);
+ setGlobal(global);
+});
+
+addActionHandler('applyBoost', async (global, actions, payload): Promise => {
+ const { chatId, tabId = getCurrentTabId() } = payload;
+
+ const chat = selectChat(global, chatId);
+ if (!chat) return;
+
+ const result = await callApi('applyBoost', {
+ chat,
+ });
+
+ if (!result) {
+ return;
+ }
+
+ const newStatusResult = await callApi('fetchBoostsStatus', {
+ chat,
+ });
+
+ if (!newStatusResult) {
+ return;
+ }
+
+ global = getGlobal();
+ const tabState = selectTabState(global, tabId);
+ if (!tabState.boostModal?.boostStatus) return;
+ global = updateTabState(global, {
+ boostModal: {
+ ...tabState.boostModal,
+ boostStatus: newStatusResult,
+ },
+ }, tabId);
+ setGlobal(global);
+});
diff --git a/src/global/actions/ui/stories.ts b/src/global/actions/ui/stories.ts
index fe2f06cbc..9024b7cd9 100644
--- a/src/global/actions/ui/stories.ts
+++ b/src/global/actions/ui/stories.ts
@@ -411,3 +411,11 @@ addActionHandler('updateStoryView', (global, actions, payload): ActionReturnType
},
}, tabId);
});
+
+addActionHandler('closeBoostModal', (global, actions, payload): ActionReturnType => {
+ const { tabId = getCurrentTabId() } = payload || {};
+
+ return updateTabState(global, {
+ boostModal: undefined,
+ }, tabId);
+});
diff --git a/src/global/helpers/chats.ts b/src/global/helpers/chats.ts
index e4676c132..74edd5e4d 100644
--- a/src/global/helpers/chats.ts
+++ b/src/global/helpers/chats.ts
@@ -35,6 +35,10 @@ export function isChannelId(entityId: string) {
return entityId.length === CHANNEL_ID_LENGTH && entityId.startsWith('-100');
}
+export function toChannelId(mtpId: string) {
+ return `-100${mtpId}`;
+}
+
export function isChatGroup(chat: ApiChat) {
return isChatBasicGroup(chat) || isChatSuperGroup(chat);
}
diff --git a/src/global/types.ts b/src/global/types.ts
index 27be0ee0e..32ebaf39e 100644
--- a/src/global/types.ts
+++ b/src/global/types.ts
@@ -1,8 +1,10 @@
import type {
ApiAppConfig,
+ ApiApplyBoostInfo,
ApiAttachBot,
ApiAttachment,
ApiAvailableReaction,
+ ApiBoostsStatus,
ApiChannelStatistics,
ApiChat,
ApiChatAdminRights,
@@ -617,6 +619,12 @@ export type TabState = {
suggestedPeerIds?: string[];
};
};
+
+ boostModal?: {
+ chatId: string;
+ boostStatus?: ApiBoostsStatus;
+ applyInfo?: ApiApplyBoostInfo;
+ };
};
export type GlobalState = {
@@ -1358,6 +1366,10 @@ export interface ActionPayloads {
startApp?: string;
originalParts?: string[];
} & WithTabId;
+ processBoostParameters: {
+ usernameOrId: string;
+ isPrivate?: boolean;
+ } & WithTabId;
requestThreadInfoUpdate: {
chatId: string;
threadId: number;
@@ -2068,6 +2080,14 @@ export interface ActionPayloads {
isForFuture?: boolean;
} | undefined;
+ openBoostModal: {
+ chatId: string;
+ } & WithTabId;
+ closeBoostModal: WithTabId | undefined;
+ applyBoost: {
+ chatId: string;
+ } & WithTabId;
+
// Media Viewer & Audio Player
openMediaViewer: {
chatId?: string;
diff --git a/src/lib/gramjs/tl/api.d.ts b/src/lib/gramjs/tl/api.d.ts
index b78211038..3b087cc2a 100644
--- a/src/lib/gramjs/tl/api.d.ts
+++ b/src/lib/gramjs/tl/api.d.ts
@@ -348,7 +348,7 @@ namespace Api {
export type TypeAccessPointRule = AccessPointRule;
export type TypeTlsClientHello = TlsClientHello;
export type TypeTlsBlock = TlsBlockString | TlsBlockRandom | TlsBlockZero | TlsBlockDomain | TlsBlockGrease | TlsBlockScope;
-
+
export namespace storage {
export type TypeFileType = storage.FileUnknown | storage.FilePartial | storage.FileJpeg | storage.FileGif | storage.FilePng | storage.FilePdf | storage.FileMp3 | storage.FileMov | storage.FileMp4 | storage.FileWebp;
@@ -423,6 +423,7 @@ namespace Api {
export type TypeEmojiGroups = messages.EmojiGroupsNotModified | messages.EmojiGroups;
export type TypeTranslatedText = messages.TranslateResult;
export type TypeBotApp = messages.BotApp;
+ export type TypeWebPage = messages.WebPage;
}
export namespace updates {
@@ -8915,7 +8916,7 @@ namespace Api {
}> {
entries: Api.TypeTlsBlock[];
};
-
+
export namespace storage {
export class FileUnknown extends VirtualClass {};
@@ -9753,6 +9754,15 @@ namespace Api {
hasSettings?: true;
app: Api.TypeBotApp;
};
+ export class WebPage extends VirtualClass<{
+ webpage: Api.TypeWebPage;
+ chats: Api.TypeChat[];
+ users: Api.TypeUser[];
+ }> {
+ webpage: Api.TypeWebPage;
+ chats: Api.TypeChat[];
+ users: Api.TypeUser[];
+ };
}
export namespace updates {
@@ -10807,6 +10817,7 @@ namespace Api {
boosts: int;
nextLevelBoosts?: int;
premiumAudience?: Api.TypeStatsPercentValue;
+ boostUrl: string;
}> {
// flags: undefined;
myBoost?: true;
@@ -10815,6 +10826,7 @@ namespace Api {
boosts: int;
nextLevelBoosts?: int;
premiumAudience?: Api.TypeStatsPercentValue;
+ boostUrl: string;
};
export class CanApplyBoostOk extends VirtualClass {};
export class CanApplyBoostReplace extends VirtualClass<{
@@ -10971,7 +10983,7 @@ namespace Api {
}>, Api.TypeDestroySessionRes> {
sessionId: long;
};
-
+
export namespace auth {
export class SendCode extends Request, Api.TypeWebPage> {
+ }>, messages.TypeWebPage> {
url: string;
hash: int;
};
diff --git a/src/lib/gramjs/tl/apiTl.js b/src/lib/gramjs/tl/apiTl.js
index 2121fac43..26a7e1948 100644
--- a/src/lib/gramjs/tl/apiTl.js
+++ b/src/lib/gramjs/tl/apiTl.js
@@ -1148,11 +1148,12 @@ mediaAreaGeoPoint#df8b3b22 coordinates:MediaAreaCoordinates geo:GeoPoint = Media
mediaAreaSuggestedReaction#14455871 flags:# dark:flags.0?true flipped:flags.1?true coordinates:MediaAreaCoordinates reaction:Reaction = MediaArea;
peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector = PeerStories;
stories.peerStories#cae68768 stories:PeerStories chats:Vector users:Vector = stories.PeerStories;
-stories.boostsStatus#66ea1fef flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue = stories.BoostsStatus;
+stories.boostsStatus#e5c1aa5c flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string = stories.BoostsStatus;
stories.canApplyBoostOk#c3173587 = stories.CanApplyBoostResult;
stories.canApplyBoostReplace#712c4655 current_boost:Peer chats:Vector = stories.CanApplyBoostResult;
booster#e9e6380 user_id:long expires:int = Booster;
stories.boostersList#f3dd3d1d flags:# count:int boosters:Vector next_offset:flags.0?string users:Vector = stories.BoostersList;
+messages.webPage#fd5e12bd webpage:WebPage chats:Vector users:Vector = messages.WebPage;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
@@ -1279,7 +1280,7 @@ messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = me
messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats;
-messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
+messages.getWebPage#8d9692a3 url:string hash:int = messages.WebPage;
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;
messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
@@ -1468,4 +1469,8 @@ stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?tr
stories.sendReaction#7fd736b2 flags:# add_to_recent:flags.0?true peer:InputPeer story_id:int reaction:Reaction = Updates;
stories.getPeerStories#2c4ada50 peer:InputPeer = stories.PeerStories;
stories.getPeerMaxIDs#535983c3 id:Vector = Vector;
-stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;`;
\ No newline at end of file
+stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
+stories.getBoostsStatus#4c449472 peer:InputPeer = stories.BoostsStatus;
+stories.getBoostersList#337ef980 peer:InputPeer offset:string limit:int = stories.BoostersList;
+stories.canApplyBoost#db05c1bd peer:InputPeer = stories.CanApplyBoostResult;
+stories.applyBoost#f29d7c2b peer:InputPeer = Bool;`;
diff --git a/src/lib/gramjs/tl/static/api.json b/src/lib/gramjs/tl/static/api.json
index f528dde15..3ff857756 100644
--- a/src/lib/gramjs/tl/static/api.json
+++ b/src/lib/gramjs/tl/static/api.json
@@ -314,5 +314,9 @@
"stories.sendReaction",
"stories.getPeerMaxIDs",
"stories.togglePeerStoriesHidden",
- "stories.getPeerStories"
+ "stories.getPeerStories",
+ "stories.getBoostsStatus",
+ "stories.getBoostersList",
+ "stories.canApplyBoost",
+ "stories.applyBoost"
]
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index 1fa995831..e5c4a04e8 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -288,6 +288,8 @@ $color-message-story-mention-to: #74bcff;
--drag-target-border: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%23DDDFE0' stroke-width='4' stroke-dasharray='9.1%2c 10.5' stroke-dashoffset='3' stroke-linecap='round'/%3e%3c/svg%3e");
--drag-target-border-hovered: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%2363A2E3' stroke-width='4' stroke-dasharray='9.1%2c 10.5' stroke-dashoffset='3' stroke-linecap='round'/%3e%3c/svg%3e");
+ --premium-gradient: linear-gradient(84.4deg, #6C93FF -4.85%, #976FFF 51.72%, #DF69D1 110.7%);
+
--layer-blackout-opacity: 0.3;
--layer-transition: 300ms cubic-bezier(0.33, 1, 0.68, 1);
diff --git a/src/styles/icons.scss b/src/styles/icons.scss
index 0386ae6eb..456b0e0f9 100644
--- a/src/styles/icons.scss
+++ b/src/styles/icons.scss
@@ -3,8 +3,8 @@ $icons-font: "icons";
@font-face {
font-family: $icons-font;
- src: url("./icons.woff2?2e8e2fec4b27141c4d298083615a0665") format("woff2"),
-url("./icons.woff?2e8e2fec4b27141c4d298083615a0665") format("woff");
+ src: url("./icons.woff2?aa9c231863df4bab22759fc9f141c077") format("woff2"),
+url("./icons.woff?aa9c231863df4bab22759fc9f141c077") format("woff");
font-weight: normal;
font-style: normal;
font-display: block;
@@ -58,200 +58,202 @@ $icons-map: (
"avatar-deleted-account": "\f115",
"avatar-saved-messages": "\f116",
"bold": "\f117",
- "bot-command": "\f118",
- "bot-commands-filled": "\f119",
- "bots": "\f11a",
- "bug": "\f11b",
- "calendar-filter": "\f11c",
- "calendar": "\f11d",
- "camera-add": "\f11e",
- "camera": "\f11f",
- "car": "\f120",
- "card": "\f121",
- "channel-filled": "\f122",
- "channel": "\f123",
- "channelviews": "\f124",
- "chat-badge": "\f125",
- "chats-badge": "\f126",
- "check": "\f127",
- "close-circle": "\f128",
- "close-topic": "\f129",
- "close": "\f12a",
- "cloud-download": "\f12b",
- "collapse": "\f12c",
- "colorize": "\f12d",
- "comments-sticker": "\f12e",
- "comments": "\f12f",
- "copy-media": "\f130",
- "copy": "\f131",
- "darkmode": "\f132",
- "data": "\f133",
- "delete-filled": "\f134",
- "delete-left": "\f135",
- "delete-user": "\f136",
- "delete": "\f137",
- "document": "\f138",
- "double-badge": "\f139",
- "down": "\f13a",
- "download": "\f13b",
- "eats": "\f13c",
- "edit": "\f13d",
- "email": "\f13e",
- "enter": "\f13f",
- "expand": "\f140",
- "eye-closed-outline": "\f141",
- "eye-closed": "\f142",
- "eye-outline": "\f143",
- "eye": "\f144",
- "favorite-filled": "\f145",
- "favorite": "\f146",
- "file-badge": "\f147",
- "flag": "\f148",
- "folder-badge": "\f149",
- "folder": "\f14a",
- "fontsize": "\f14b",
- "forums": "\f14c",
- "forward": "\f14d",
- "fullscreen": "\f14e",
- "gifs": "\f14f",
- "gift": "\f150",
- "group-filled": "\f151",
- "group": "\f152",
- "grouped-disable": "\f153",
- "grouped": "\f154",
- "hand-stop": "\f155",
- "hashtag": "\f156",
- "heart-outline": "\f157",
- "heart": "\f158",
- "help": "\f159",
- "info-filled": "\f15a",
- "info": "\f15b",
- "install": "\f15c",
- "italic": "\f15d",
- "key": "\f15e",
- "keyboard": "\f15f",
- "lamp": "\f160",
- "language": "\f161",
- "large-pause": "\f162",
- "large-play": "\f163",
- "link-badge": "\f164",
- "link-broken": "\f165",
- "link": "\f166",
- "location": "\f167",
- "lock-badge": "\f168",
- "lock": "\f169",
- "logout": "\f16a",
- "loop": "\f16b",
- "mention": "\f16c",
- "message-failed": "\f16d",
- "message-pending": "\f16e",
- "message-read": "\f16f",
- "message-succeeded": "\f170",
- "message": "\f171",
- "microphone-alt": "\f172",
- "microphone": "\f173",
- "monospace": "\f174",
- "more-circle": "\f175",
- "more": "\f176",
- "mute": "\f177",
- "muted": "\f178",
- "new-chat-filled": "\f179",
- "next": "\f17a",
- "noise-suppression": "\f17b",
- "non-contacts": "\f17c",
- "open-in-new-tab": "\f17d",
- "password-off": "\f17e",
- "pause": "\f17f",
- "permissions": "\f180",
- "phone-discard-outline": "\f181",
- "phone-discard": "\f182",
- "phone": "\f183",
- "photo": "\f184",
- "pin-badge": "\f185",
- "pin-list": "\f186",
- "pin": "\f187",
- "pinned-chat": "\f188",
- "pinned-message": "\f189",
- "pip": "\f18a",
- "play-story": "\f18b",
- "play": "\f18c",
- "poll": "\f18d",
- "premium": "\f18e",
- "previous": "\f18f",
- "privacy-policy": "\f190",
- "readchats": "\f191",
- "recent": "\f192",
- "reload": "\f193",
- "remove": "\f194",
- "reopen-topic": "\f195",
- "replace": "\f196",
- "replies": "\f197",
- "reply-filled": "\f198",
- "reply": "\f199",
- "revote": "\f19a",
- "save-story": "\f19b",
- "saved-messages": "\f19c",
- "schedule": "\f19d",
- "search": "\f19e",
- "select": "\f19f",
- "send-outline": "\f1a0",
- "send": "\f1a1",
- "settings-filled": "\f1a2",
- "settings": "\f1a3",
- "share-filled": "\f1a4",
- "share-screen-outlined": "\f1a5",
- "share-screen-stop": "\f1a6",
- "share-screen": "\f1a7",
- "sidebar": "\f1a8",
- "skip-next": "\f1a9",
- "skip-previous": "\f1aa",
- "smallscreen": "\f1ab",
- "smile": "\f1ac",
- "sort": "\f1ad",
- "speaker-muted-story": "\f1ae",
- "speaker-outline": "\f1af",
- "speaker-story": "\f1b0",
- "speaker": "\f1b1",
- "spoiler-disable": "\f1b2",
- "spoiler": "\f1b3",
- "sport": "\f1b4",
- "stats": "\f1b5",
- "stealth-future": "\f1b6",
- "stealth-past": "\f1b7",
- "stickers": "\f1b8",
- "stop-raising-hand": "\f1b9",
- "stop": "\f1ba",
- "story-caption": "\f1bb",
- "story-expired": "\f1bc",
- "story-priority": "\f1bd",
- "story-reply": "\f1be",
- "strikethrough": "\f1bf",
- "timer": "\f1c0",
- "transcribe": "\f1c1",
- "truck": "\f1c2",
- "unarchive": "\f1c3",
- "underlined": "\f1c4",
- "unlock-badge": "\f1c5",
- "unlock": "\f1c6",
- "unmute": "\f1c7",
- "unpin": "\f1c8",
- "unread": "\f1c9",
- "up": "\f1ca",
- "user-filled": "\f1cb",
- "user-online": "\f1cc",
- "user": "\f1cd",
- "video-outlined": "\f1ce",
- "video-stop": "\f1cf",
- "video": "\f1d0",
- "voice-chat": "\f1d1",
- "volume-1": "\f1d2",
- "volume-2": "\f1d3",
- "volume-3": "\f1d4",
- "web": "\f1d5",
- "webapp": "\f1d6",
- "word-wrap": "\f1d7",
- "zoom-in": "\f1d8",
- "zoom-out": "\f1d9",
+ "boost": "\f118",
+ "boostcircle": "\f119",
+ "bot-command": "\f11a",
+ "bot-commands-filled": "\f11b",
+ "bots": "\f11c",
+ "bug": "\f11d",
+ "calendar-filter": "\f11e",
+ "calendar": "\f11f",
+ "camera-add": "\f120",
+ "camera": "\f121",
+ "car": "\f122",
+ "card": "\f123",
+ "channel-filled": "\f124",
+ "channel": "\f125",
+ "channelviews": "\f126",
+ "chat-badge": "\f127",
+ "chats-badge": "\f128",
+ "check": "\f129",
+ "close-circle": "\f12a",
+ "close-topic": "\f12b",
+ "close": "\f12c",
+ "cloud-download": "\f12d",
+ "collapse": "\f12e",
+ "colorize": "\f12f",
+ "comments-sticker": "\f130",
+ "comments": "\f131",
+ "copy-media": "\f132",
+ "copy": "\f133",
+ "darkmode": "\f134",
+ "data": "\f135",
+ "delete-filled": "\f136",
+ "delete-left": "\f137",
+ "delete-user": "\f138",
+ "delete": "\f139",
+ "document": "\f13a",
+ "double-badge": "\f13b",
+ "down": "\f13c",
+ "download": "\f13d",
+ "eats": "\f13e",
+ "edit": "\f13f",
+ "email": "\f140",
+ "enter": "\f141",
+ "expand": "\f142",
+ "eye-closed-outline": "\f143",
+ "eye-closed": "\f144",
+ "eye-outline": "\f145",
+ "eye": "\f146",
+ "favorite-filled": "\f147",
+ "favorite": "\f148",
+ "file-badge": "\f149",
+ "flag": "\f14a",
+ "folder-badge": "\f14b",
+ "folder": "\f14c",
+ "fontsize": "\f14d",
+ "forums": "\f14e",
+ "forward": "\f14f",
+ "fullscreen": "\f150",
+ "gifs": "\f151",
+ "gift": "\f152",
+ "group-filled": "\f153",
+ "group": "\f154",
+ "grouped-disable": "\f155",
+ "grouped": "\f156",
+ "hand-stop": "\f157",
+ "hashtag": "\f158",
+ "heart-outline": "\f159",
+ "heart": "\f15a",
+ "help": "\f15b",
+ "info-filled": "\f15c",
+ "info": "\f15d",
+ "install": "\f15e",
+ "italic": "\f15f",
+ "key": "\f160",
+ "keyboard": "\f161",
+ "lamp": "\f162",
+ "language": "\f163",
+ "large-pause": "\f164",
+ "large-play": "\f165",
+ "link-badge": "\f166",
+ "link-broken": "\f167",
+ "link": "\f168",
+ "location": "\f169",
+ "lock-badge": "\f16a",
+ "lock": "\f16b",
+ "logout": "\f16c",
+ "loop": "\f16d",
+ "mention": "\f16e",
+ "message-failed": "\f16f",
+ "message-pending": "\f170",
+ "message-read": "\f171",
+ "message-succeeded": "\f172",
+ "message": "\f173",
+ "microphone-alt": "\f174",
+ "microphone": "\f175",
+ "monospace": "\f176",
+ "more-circle": "\f177",
+ "more": "\f178",
+ "mute": "\f179",
+ "muted": "\f17a",
+ "new-chat-filled": "\f17b",
+ "next": "\f17c",
+ "noise-suppression": "\f17d",
+ "non-contacts": "\f17e",
+ "open-in-new-tab": "\f17f",
+ "password-off": "\f180",
+ "pause": "\f181",
+ "permissions": "\f182",
+ "phone-discard-outline": "\f183",
+ "phone-discard": "\f184",
+ "phone": "\f185",
+ "photo": "\f186",
+ "pin-badge": "\f187",
+ "pin-list": "\f188",
+ "pin": "\f189",
+ "pinned-chat": "\f18a",
+ "pinned-message": "\f18b",
+ "pip": "\f18c",
+ "play-story": "\f18d",
+ "play": "\f18e",
+ "poll": "\f18f",
+ "premium": "\f190",
+ "previous": "\f191",
+ "privacy-policy": "\f192",
+ "readchats": "\f193",
+ "recent": "\f194",
+ "reload": "\f195",
+ "remove": "\f196",
+ "reopen-topic": "\f197",
+ "replace": "\f198",
+ "replies": "\f199",
+ "reply-filled": "\f19a",
+ "reply": "\f19b",
+ "revote": "\f19c",
+ "save-story": "\f19d",
+ "saved-messages": "\f19e",
+ "schedule": "\f19f",
+ "search": "\f1a0",
+ "select": "\f1a1",
+ "send-outline": "\f1a2",
+ "send": "\f1a3",
+ "settings-filled": "\f1a4",
+ "settings": "\f1a5",
+ "share-filled": "\f1a6",
+ "share-screen-outlined": "\f1a7",
+ "share-screen-stop": "\f1a8",
+ "share-screen": "\f1a9",
+ "sidebar": "\f1aa",
+ "skip-next": "\f1ab",
+ "skip-previous": "\f1ac",
+ "smallscreen": "\f1ad",
+ "smile": "\f1ae",
+ "sort": "\f1af",
+ "speaker-muted-story": "\f1b0",
+ "speaker-outline": "\f1b1",
+ "speaker-story": "\f1b2",
+ "speaker": "\f1b3",
+ "spoiler-disable": "\f1b4",
+ "spoiler": "\f1b5",
+ "sport": "\f1b6",
+ "stats": "\f1b7",
+ "stealth-future": "\f1b8",
+ "stealth-past": "\f1b9",
+ "stickers": "\f1ba",
+ "stop-raising-hand": "\f1bb",
+ "stop": "\f1bc",
+ "story-caption": "\f1bd",
+ "story-expired": "\f1be",
+ "story-priority": "\f1bf",
+ "story-reply": "\f1c0",
+ "strikethrough": "\f1c1",
+ "timer": "\f1c2",
+ "transcribe": "\f1c3",
+ "truck": "\f1c4",
+ "unarchive": "\f1c5",
+ "underlined": "\f1c6",
+ "unlock-badge": "\f1c7",
+ "unlock": "\f1c8",
+ "unmute": "\f1c9",
+ "unpin": "\f1ca",
+ "unread": "\f1cb",
+ "up": "\f1cc",
+ "user-filled": "\f1cd",
+ "user-online": "\f1ce",
+ "user": "\f1cf",
+ "video-outlined": "\f1d0",
+ "video-stop": "\f1d1",
+ "video": "\f1d2",
+ "voice-chat": "\f1d3",
+ "volume-1": "\f1d4",
+ "volume-2": "\f1d5",
+ "volume-3": "\f1d6",
+ "web": "\f1d7",
+ "webapp": "\f1d8",
+ "word-wrap": "\f1d9",
+ "zoom-in": "\f1da",
+ "zoom-out": "\f1db",
);
.icon-active-sessions::before {
@@ -323,6 +325,12 @@ $icons-map: (
.icon-bold::before {
content: map.get($icons-map, "bold");
}
+.icon-boost::before {
+ content: map.get($icons-map, "boost");
+}
+.icon-boostcircle::before {
+ content: map.get($icons-map, "boostcircle");
+}
.icon-bot-command::before {
content: map.get($icons-map, "bot-command");
}
diff --git a/src/styles/icons.woff b/src/styles/icons.woff
index ee3396447..c0d7eefc6 100644
Binary files a/src/styles/icons.woff and b/src/styles/icons.woff differ
diff --git a/src/styles/icons.woff2 b/src/styles/icons.woff2
index 76cbad0bf..93587410a 100644
Binary files a/src/styles/icons.woff2 and b/src/styles/icons.woff2 differ
diff --git a/src/types/icons/font.ts b/src/types/icons/font.ts
index 55b6f4ba1..affc52798 100644
--- a/src/types/icons/font.ts
+++ b/src/types/icons/font.ts
@@ -22,6 +22,8 @@ export type FontIconName =
| 'avatar-deleted-account'
| 'avatar-saved-messages'
| 'bold'
+ | 'boost'
+ | 'boostcircle'
| 'bot-command'
| 'bot-commands-filled'
| 'bots'
diff --git a/src/util/dateFormat.ts b/src/util/dateFormat.ts
index a6749856b..2ad7f67bd 100644
--- a/src/util/dateFormat.ts
+++ b/src/util/dateFormat.ts
@@ -363,6 +363,31 @@ export function formatDateAtTime(
return lang('formatDateAtTime', [formattedDate, time]);
}
+export function formatDateInFuture(
+ lang: LangFn,
+ currentTime: number,
+ datetime: number,
+) {
+ const diff = Math.ceil(datetime - currentTime);
+ if (diff < 0) {
+ return lang('RightNow');
+ }
+
+ if (diff < 60) {
+ return lang('Seconds', diff);
+ }
+
+ if (diff < 60 * 60) {
+ return lang('Minutes', Math.ceil(diff / 60));
+ }
+
+ if (diff < 60 * 60 * 24) {
+ return lang('Hours', Math.ceil(diff / (60 * 60)));
+ }
+
+ return lang('Days', Math.ceil(diff / (60 * 60 * 24)));
+}
+
function isValidDate(day: number, month: number, year = 2021): boolean {
if (month > (MAX_MONTH_IN_YEAR - 1) || day > MAX_DAY_IN_MONTH) {
return false;
diff --git a/src/util/deeplink.ts b/src/util/deeplink.ts
index bbf37a3dd..9b8e8a433 100644
--- a/src/util/deeplink.ts
+++ b/src/util/deeplink.ts
@@ -7,7 +7,7 @@ import { IS_SAFARI } from './windowEnvironment';
type DeepLinkMethod = 'resolve' | 'login' | 'passport' | 'settings' | 'join' | 'addstickers' | 'addemoji' |
'setlanguage' | 'addtheme' | 'confirmphone' | 'socks' | 'proxy' | 'privatepost' | 'bg' | 'share' | 'msg' | 'msg_url' |
-'invoice' | 'addlist';
+'invoice' | 'addlist' | 'boost';
export const processDeepLink = (url: string) => {
const {
@@ -28,6 +28,7 @@ export const processDeepLink = (url: string) => {
openChatWithDraft,
checkChatlistInvite,
openStoryViewerByUsername,
+ processBoostParameters,
} = getActions();
// Safari thinks the path in tg://path links is hostname for some reason
@@ -43,6 +44,7 @@ export const processDeepLink = (url: string) => {
const hasStartAttach = params.hasOwnProperty('startattach');
const hasStartApp = params.hasOwnProperty('startapp');
+ const hasBoost = params.hasOwnProperty('boost');
const choose = parseChooseParameter(params.choose);
const threadId = Number(thread) || Number(topic) || undefined;
@@ -64,6 +66,8 @@ export const processDeepLink = (url: string) => {
username: domain,
inviteHash: voicechat || livestream,
});
+ } else if (hasBoost) {
+ processBoostParameters({ usernameOrId: domain });
} else if (phone) {
openChatByPhoneNumber({ phoneNumber: phone, startAttach: startattach, attach });
} else if (story) {
@@ -87,6 +91,13 @@ export const processDeepLink = (url: string) => {
post, channel,
} = params;
+ const hasBoost = params.hasOwnProperty('boost');
+
+ if (hasBoost) {
+ processBoostParameters({ usernameOrId: channel, isPrivate: true });
+ return;
+ }
+
focusMessage({
chatId: `-${channel}`,
messageId: Number(post),
@@ -138,6 +149,14 @@ export const processDeepLink = (url: string) => {
openInvoice({ slug });
break;
}
+
+ case 'boost': {
+ const { channel, domain } = params;
+ const isPrivate = Boolean(channel);
+
+ processBoostParameters({ usernameOrId: channel || domain, isPrivate });
+ break;
+ }
default:
// Unsupported deeplink