Skip to content

Commit 1858d63

Browse files
committed
Audio Player, Message Context Menu: Support "Add to Profile" for audio (#7225)
1 parent d7d43d8 commit 1858d63

24 files changed

Lines changed: 1612 additions & 1290 deletions

File tree

‎src/api/gramjs/gramjsBuilders/index.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
22
import { generateRandomBigInt, generateRandomBytes, readBigIntFromBuffer } from '../../../lib/gramjs/Helpers';
33

44
import type {
5+
ApiAudio,
56
ApiBirthday,
67
ApiBotApp,
78
ApiChatAdminRights,
@@ -192,7 +193,7 @@ export function buildInputStickerSetShortName(shortName: string) {
192193
});
193194
}
194195

195-
export function buildInputDocument(media: ApiSticker | ApiVideo | ApiDocument) {
196+
export function buildInputDocument(media: ApiAudio | ApiSticker | ApiVideo | ApiDocument) {
196197
if (!media.id) {
197198
return undefined;
198199
}

‎src/api/gramjs/methods/account.ts‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Api as GramJs } from '../../../lib/gramjs';
22

33
import type {
4-
ApiPeer, ApiPhoto, ApiProfileTab, ApiReportReason,
4+
ApiAudio, ApiPeer, ApiPhoto, ApiProfileTab, ApiReportReason,
55
} from '../../types';
66

77
import { buildApiChatLink } from '../apiBuilders/misc';
88
import {
9+
buildInputDocument,
910
buildInputPeer,
1011
buildInputPhoto,
1112
buildInputProfileTab,
@@ -138,3 +139,26 @@ export function setAccountMainProfileTab({ tab }: { tab: ApiProfileTab }) {
138139
shouldReturnTrue: true,
139140
});
140141
}
142+
143+
export async function fetchSavedMusicIds() {
144+
const result = await invokeRequest(new GramJs.account.GetSavedMusicIds({
145+
hash: DEFAULT_PRIMITIVES.BIGINT,
146+
}));
147+
if (!(result instanceof GramJs.account.SavedMusicIds)) {
148+
return undefined;
149+
}
150+
151+
return result.ids.map(String);
152+
}
153+
154+
export function saveMusic({ audio, shouldRemove }: { audio: ApiAudio; shouldRemove?: boolean }) {
155+
const id = buildInputDocument(audio);
156+
if (!id) return undefined;
157+
158+
return invokeRequest(new GramJs.account.SaveMusic({
159+
id,
160+
unsave: shouldRemove || undefined,
161+
}), {
162+
shouldReturnTrue: true,
163+
});
164+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading

‎src/assets/localization/fallback.strings‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,10 @@
15291529
"AudioPlayerPrevious" = "Previous track";
15301530
"AudioPlayerNext" = "Next track";
15311531
"AudioPlayerClose" = "Close";
1532+
"AudioAddToProfile" = "Add to Profile";
1533+
"AudioRemoveFromProfile" = "Remove from Profile";
1534+
"AudioSaveToMyProfileSaved" = "Audio added to your profile.";
1535+
"AudioSaveToMyProfileUnsaved" = "Audio removed from your profile.";
15321536
"DeleteForMeDescription" = "This will delete them just for you, not for other participants in the chat";
15331537
"DeleteForEveryoneDescription" = "This will delete them for everyone in this chat";
15341538
"AriaSearchInChat" = "Search in chat";

‎src/components/middle/message/ContextMenuContainer.tsx‎

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { FC } from '../../../lib/teact/teact';
21
import {
32
memo, useEffect, useMemo, useState,
43
} from '../../../lib/teact/teact';
@@ -33,6 +32,7 @@ import {
3332
areReactionsEmpty,
3433
getCanPostInChat,
3534
getIsDownloading,
35+
getMessageAudio,
3636
getMessageVideo,
3737
getUserFullName,
3838
hasMessageTtl,
@@ -172,12 +172,14 @@ type StateProps = {
172172
savedDialogId?: string;
173173
noForwardsMyEnabled?: boolean;
174174
noForwardsPeerEnabled?: boolean;
175+
savedMusicById?: Record<string, true>;
176+
isSavedMusicLoading?: boolean;
175177
};
176178

177179
const selection = window.getSelection();
178180
const UNQUOTABLE_OFFSET = -1;
179181

180-
const ContextMenuContainer: FC<OwnProps & StateProps> = ({
182+
const ContextMenuContainer = ({
181183
threadId,
182184
availableReactions,
183185
topReactions,
@@ -247,9 +249,11 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
247249
savedDialogId,
248250
noForwardsMyEnabled,
249251
noForwardsPeerEnabled,
252+
savedMusicById,
253+
isSavedMusicLoading,
250254
onClose,
251255
onCloseAnimationEnd,
252-
}) => {
256+
}: OwnProps & StateProps) => {
253257
const {
254258
openThread,
255259
updateDraftReplyInfo,
@@ -291,6 +295,8 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
291295
showNotification,
292296
setSettingOption,
293297
loadRichMessage,
298+
loadSavedMusicIds,
299+
toggleMusicInProfile,
294300
} = getActions();
295301

296302
const oldLang = useOldLang();
@@ -319,6 +325,17 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
319325
// `undefined` indicates that emoji are present and loading
320326
const hasCustomEmoji = !message.isEphemeral
321327
&& (customEmojiSetsInfo === undefined || Boolean(customEmojiSetsInfo.length));
328+
const audio = getMessageAudio(message);
329+
const isLocalMessage = isMessageLocal(message);
330+
const canManageMusicInProfile = Boolean(audio && !isLocalMessage);
331+
const isMusicProfileStatusLoaded = Boolean(savedMusicById);
332+
const isMusicSaved = Boolean(audio && savedMusicById?.[audio.id]);
333+
334+
useEffect(() => {
335+
if (isOpen && audio && !isLocalMessage && !savedMusicById && !isSavedMusicLoading) {
336+
loadSavedMusicIds();
337+
}
338+
}, [audio, isLocalMessage, isOpen, isSavedMusicLoading, loadSavedMusicIds, savedMusicById]);
322339

323340
useEffect(() => {
324341
if (canShowSeenBy && isOpen) {
@@ -661,6 +678,11 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
661678
closeMenu();
662679
});
663680

681+
const handleToggleMusicInProfile = useLastCallback(() => {
682+
toggleMusicInProfile({ audio: audio! });
683+
closeMenu();
684+
});
685+
664686
const handleToggleReaction = useLastCallback((reaction: ApiReaction) => {
665687
if (isInSavedMessages && !isCurrentUserPremium) {
666688
openPremiumModal({
@@ -791,6 +813,10 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
791813
canSelect={canSelect}
792814
canDownload={canDownload}
793815
canSaveGif={canSaveGif}
816+
canManageMusicInProfile={canManageMusicInProfile}
817+
isMusicProfileStatusLoaded={isMusicProfileStatusLoaded}
818+
isMusicProfileActionLoading={isSavedMusicLoading}
819+
isMusicSaved={isMusicSaved}
794820
canRevote={canRevote}
795821
canClosePoll={canClosePoll}
796822
canShowSeenBy={canShowSeenBy}
@@ -830,6 +856,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
830856
onCopyNumber={handleCopyNumber}
831857
onDownload={handleDownloadClick}
832858
onSaveGif={handleSaveGif}
859+
onToggleMusicInProfile={handleToggleMusicInProfile}
833860
onCancelVote={handleCancelVote}
834861
onClosePoll={openClosePollDialog}
835862
onShowSeenBy={handleOpenSeenByModal}
@@ -1058,6 +1085,8 @@ export default memo(withGlobal<OwnProps>(
10581085
webPage,
10591086
noForwardsMyEnabled: userFullInfo?.noForwardsMyEnabled,
10601087
noForwardsPeerEnabled: userFullInfo?.noForwardsPeerEnabled,
1088+
savedMusicById: global.users.savedMusicById,
1089+
isSavedMusicLoading: global.users.isSavedMusicLoading,
10611090
};
10621091
},
10631092
)(ContextMenuContainer));

‎src/components/middle/message/MessageContextMenu.scss‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,32 @@
7070
padding-inline-start: 1rem;
7171
}
7272

73+
.profile-music-menu-item-transition {
74+
height: 3rem;
75+
}
76+
77+
&.compact .profile-music-menu-item-transition {
78+
height: 2.25rem;
79+
}
80+
81+
.profile-music-menu-icon-placeholder {
82+
flex-shrink: 0;
83+
width: 1.5rem;
84+
height: 1.5rem;
85+
margin-inline-end: 2rem;
86+
}
87+
88+
&.compact .profile-music-menu-icon-placeholder {
89+
width: 1.25rem;
90+
margin-inline: 0.5rem 1.25rem;
91+
}
92+
93+
.profile-music-menu-label-placeholder {
94+
width: 8rem;
95+
height: 0.75rem;
96+
border-radius: 0.375rem;
97+
}
98+
7399
.ReactionSelector {
74100
position: absolute;
75101
top: 0;

‎src/components/middle/message/MessageContextMenu.tsx‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { FC } from '../../../lib/teact/teact';
21
import {
32
memo, useEffect, useMemo, useRef,
43
} from '../../../lib/teact/teact';
@@ -50,6 +49,7 @@ import MenuItem from '../../ui/MenuItem';
5049
import MenuSeparator from '../../ui/MenuSeparator';
5150
import NestedMenuItem from '../../ui/NestedMenuItem';
5251
import Skeleton from '../../ui/placeholder/Skeleton';
52+
import Transition from '../../ui/Transition';
5353
import AutoDeleteTimeMenuItem from './AutoDeleteTimeMenuItem';
5454
import LastEditTimeMenuItem from './LastEditTimeMenuItem';
5555
import ReactionSelector from './reactions/ReactionSelector';
@@ -104,6 +104,10 @@ type OwnProps = {
104104
isCurrentUserPremium?: boolean;
105105
canDownload?: boolean;
106106
canSaveGif?: boolean;
107+
canManageMusicInProfile?: boolean;
108+
isMusicProfileStatusLoaded?: boolean;
109+
isMusicProfileActionLoading?: boolean;
110+
isMusicSaved?: boolean;
107111
canRevote?: boolean;
108112
canClosePoll?: boolean;
109113
isDownloading?: boolean;
@@ -137,6 +141,7 @@ type OwnProps = {
137141
onCopyNumber?: NoneToVoidFunction;
138142
onDownload?: NoneToVoidFunction;
139143
onSaveGif?: NoneToVoidFunction;
144+
onToggleMusicInProfile?: NoneToVoidFunction;
140145
onCancelVote?: NoneToVoidFunction;
141146
onClosePoll?: NoneToVoidFunction;
142147
onShowSeenBy?: NoneToVoidFunction;
@@ -158,7 +163,7 @@ const SCROLLBAR_WIDTH = 10;
158163
const REACTION_SELECTOR_WIDTH_REM = 19.25;
159164
const ANIMATION_DURATION = 200;
160165

161-
const MessageContextMenu: FC<OwnProps> = ({
166+
const MessageContextMenu = ({
162167
isReactionPickerOpen,
163168
availableReactions,
164169
topReactions,
@@ -199,6 +204,10 @@ const MessageContextMenu: FC<OwnProps> = ({
199204
canSelect,
200205
canDownload,
201206
canSaveGif,
207+
canManageMusicInProfile,
208+
isMusicProfileStatusLoaded,
209+
isMusicProfileActionLoading,
210+
isMusicSaved,
202211
canRevote,
203212
canClosePoll,
204213
canTranslate,
@@ -237,6 +246,7 @@ const MessageContextMenu: FC<OwnProps> = ({
237246
onCopyNumber,
238247
onDownload,
239248
onSaveGif,
249+
onToggleMusicInProfile,
240250
onCancelVote,
241251
onClosePoll,
242252
onShowSeenBy,
@@ -253,7 +263,7 @@ const MessageContextMenu: FC<OwnProps> = ({
253263
userFullName,
254264
canGift,
255265
noForwardsNotice,
256-
}) => {
266+
}: OwnProps) => {
257267
const {
258268
showNotification, openStickerSet, openCustomEmojiSets, loadStickers, openGiftModal,
259269
} = getActions();
@@ -550,6 +560,28 @@ const MessageContextMenu: FC<OwnProps> = ({
550560
{canPin && <MenuItem icon="pin" onClick={onPin}>{oldLang('DialogPin')}</MenuItem>}
551561
{canUnpin && <MenuItem icon="unpin" onClick={onUnpin}>{oldLang('DialogUnpin')}</MenuItem>}
552562
{canSaveGif && <MenuItem icon="gifs" onClick={onSaveGif}>{oldLang('lng_context_save_gif')}</MenuItem>}
563+
{canManageMusicInProfile && (
564+
<Transition
565+
activeKey={!isMusicProfileStatusLoaded ? 0 : (isMusicSaved ? 2 : 1)}
566+
name="fade"
567+
className="profile-music-menu-item-transition"
568+
shouldCleanup
569+
>
570+
{!isMusicProfileStatusLoaded ? (
571+
<MenuItem customIcon={<span className="profile-music-menu-icon-placeholder" />} disabled>
572+
<Skeleton className="profile-music-menu-label-placeholder" animation="wave" />
573+
</MenuItem>
574+
) : (
575+
<MenuItem
576+
icon={isMusicSaved ? 'remove-music' : 'add-music'}
577+
disabled={isMusicProfileActionLoading}
578+
onClick={onToggleMusicInProfile}
579+
>
580+
{lang(isMusicSaved ? 'AudioRemoveFromProfile' : 'AudioAddToProfile')}
581+
</MenuItem>
582+
)}
583+
</Transition>
584+
)}
553585
{canRevote && <MenuItem icon="revote" onClick={onCancelVote}>{oldLang('lng_polls_retract')}</MenuItem>}
554586
{canClosePoll && <MenuItem icon="stop" onClick={onClosePoll}>{oldLang('lng_polls_stop')}</MenuItem>}
555587
{canDownload && (

0 commit comments

Comments
 (0)