import { addReducer, getGlobal, setGlobal } from '../../../lib/teact/teactn'; import { GlobalState } from '../../../global/types'; import { ApiPrivacyKey, PrivacyVisibility, ProfileEditProgress, InputPrivacyRules, InputPrivacyContact, UPLOADING_WALLPAPER_SLUG, } from '../../../types'; import { COUNTRIES_WITH_12H_TIME_FORMAT } from '../../../config'; import { callApi } from '../../../api/gramjs'; import { buildCollectionByKey } from '../../../util/iteratees'; import { subscribe, unsubscribe } from '../../../util/notifications'; import { setTimeFormat } from '../../../util/langProvider'; import { selectUser } from '../../selectors'; import { addUsers, addBlockedContact, updateChats, updateUser, removeBlockedContact, replaceSettings, updateNotifySettings, addNotifyExceptions, } from '../../reducers'; import { isUserId } from '../../helpers'; addReducer('updateProfile', (global, actions, payload) => { const { photo, firstName, lastName, bio: about, username, } = payload!; (async () => { const { currentUserId } = global; if (!currentUserId) { return; } setGlobal({ ...getGlobal(), profileEdit: { progress: ProfileEditProgress.InProgress, }, }); if (photo) { await callApi('updateProfilePhoto', photo); } if (firstName || lastName || about) { const result = await callApi('updateProfile', { firstName, lastName, about }); if (result) { global = getGlobal(); const currentUser = currentUserId && selectUser(global, currentUserId); if (currentUser) { setGlobal(updateUser( global, currentUser.id, { firstName, lastName, fullInfo: { ...currentUser.fullInfo, bio: about, }, }, )); } } } if (username) { const result = await callApi('updateUsername', username); if (result && currentUserId) { setGlobal(updateUser(getGlobal(), currentUserId, { username })); } } setGlobal({ ...getGlobal(), profileEdit: { progress: ProfileEditProgress.Complete, }, }); })(); }); addReducer('checkUsername', (global, actions, payload) => { const { username } = payload!; (async () => { // No need to check the username if profile update is already in progress if (global.profileEdit && global.profileEdit.progress === ProfileEditProgress.InProgress) { return; } setGlobal({ ...global, profileEdit: { progress: global.profileEdit ? global.profileEdit.progress : ProfileEditProgress.Idle, isUsernameAvailable: undefined, }, }); const isUsernameAvailable = await callApi('checkUsername', username); global = getGlobal(); setGlobal({ ...global, profileEdit: { ...global.profileEdit!, isUsernameAvailable, }, }); })(); }); addReducer('loadWallpapers', () => { (async () => { const result = await callApi('fetchWallpapers'); if (!result) { return; } const global = getGlobal(); setGlobal({ ...global, settings: { ...global.settings, loadedWallpapers: result.wallpapers, }, }); })(); }); addReducer('uploadWallpaper', (global, actions, payload) => { const file = payload; const previewBlobUrl = URL.createObjectURL(file); setGlobal({ ...global, settings: { ...global.settings, loadedWallpapers: [ { slug: UPLOADING_WALLPAPER_SLUG, document: { fileName: '', size: file.size, mimeType: file.type, previewBlobUrl, }, }, ...(global.settings.loadedWallpapers || []), ], }, }); (async () => { const result = await callApi('uploadWallpaper', file); if (!result) { return; } const { wallpaper } = result; global = getGlobal(); if (!global.settings.loadedWallpapers) { return; } const firstWallpaper = global.settings.loadedWallpapers[0]; if (!firstWallpaper || firstWallpaper.slug !== UPLOADING_WALLPAPER_SLUG) { return; } const withLocalMedia = { ...wallpaper, document: { ...wallpaper.document, previewBlobUrl, }, }; setGlobal({ ...global, settings: { ...global.settings, loadedWallpapers: [ withLocalMedia, ...global.settings.loadedWallpapers.slice(1), ], }, }); })(); }); addReducer('loadBlockedContacts', () => { (async () => { const result = await callApi('fetchBlockedContacts'); if (!result) { return; } let newGlobal = getGlobal(); if (result.users?.length) { newGlobal = addUsers(newGlobal, buildCollectionByKey(result.users, 'id')); } if (result.chats?.length) { newGlobal = updateChats(newGlobal, buildCollectionByKey(result.chats, 'id')); } newGlobal = { ...newGlobal, blocked: { ...newGlobal.blocked, ids: [...(newGlobal.blocked.ids || []), ...result.blockedIds], totalCount: result.totalCount, }, }; setGlobal(newGlobal); })(); }); addReducer('blockContact', (global, actions, payload) => { const { contactId, accessHash } = payload!; (async () => { const result = await callApi('blockContact', contactId, accessHash); if (!result) { return; } const newGlobal = getGlobal(); setGlobal(addBlockedContact(newGlobal, contactId)); })(); }); addReducer('unblockContact', (global, actions, payload) => { const { contactId } = payload!; let accessHash: string | undefined; const isPrivate = isUserId(contactId); if (isPrivate) { const user = selectUser(global, contactId); if (!user) { return; } accessHash = user.accessHash; } (async () => { const result = await callApi('unblockContact', contactId, accessHash); if (!result) { return; } const newGlobal = getGlobal(); setGlobal(removeBlockedContact(newGlobal, contactId)); })(); }); addReducer('loadAuthorizations', () => { (async () => { const result = await callApi('fetchAuthorizations'); if (!result) { return; } setGlobal({ ...getGlobal(), activeSessions: result, }); })(); }); addReducer('terminateAuthorization', (global, actions, payload) => { const { hash } = payload!; (async () => { const result = await callApi('terminateAuthorization', hash); if (!result) { return; } const newGlobal = getGlobal(); setGlobal({ ...newGlobal, activeSessions: newGlobal.activeSessions.filter((session) => session.hash !== hash), }); })(); }); addReducer('terminateAllAuthorizations', () => { (async () => { const result = await callApi('terminateAllAuthorizations'); if (!result) { return; } const global = getGlobal(); setGlobal({ ...global, activeSessions: global.activeSessions.filter((session) => session.isCurrent), }); })(); }); addReducer('loadNotificationExceptions', (global) => { const { serverTimeOffset } = global; (async () => { const result = await callApi('fetchNotificationExceptions', { serverTimeOffset }); if (!result) { return; } setGlobal(addNotifyExceptions(getGlobal(), result)); })(); }); addReducer('loadNotificationSettings', (global) => { const { serverTimeOffset } = global; (async () => { const result = await callApi('fetchNotificationSettings', { serverTimeOffset, }); if (!result) { return; } setGlobal(replaceSettings(getGlobal(), result)); })(); }); addReducer('updateNotificationSettings', (global, actions, payload) => { const { peerType, isSilent, shouldShowPreviews } = payload!; (async () => { const result = await callApi('updateNotificationSettings', peerType, { isSilent, shouldShowPreviews }); if (!result) { return; } setGlobal(updateNotifySettings(getGlobal(), peerType, isSilent, shouldShowPreviews)); })(); }); addReducer('updateWebNotificationSettings', (global, actions, payload) => { (async () => { setGlobal(replaceSettings(getGlobal(), payload)); const newGlobal = getGlobal(); const { hasPushNotifications, hasWebNotifications } = newGlobal.settings.byKey; if (hasWebNotifications && hasPushNotifications) { await subscribe(); } else { await unsubscribe(); } })(); }); addReducer('updateContactSignUpNotification', (global, actions, payload) => { const { isSilent } = payload!; (async () => { const result = await callApi('updateContactSignUpNotification', isSilent); if (!result) { return; } setGlobal(replaceSettings(getGlobal(), { hasContactJoinedNotifications: !isSilent })); })(); }); addReducer('loadLanguages', () => { (async () => { const result = await callApi('fetchLanguages'); if (!result) { return; } setGlobal(replaceSettings(getGlobal(), { languages: result })); })(); }); addReducer('loadPrivacySettings', () => { (async () => { const [ phoneNumberSettings, lastSeenSettings, profilePhotoSettings, forwardsSettings, chatInviteSettings, ] = await Promise.all([ callApi('fetchPrivacySettings', 'phoneNumber'), callApi('fetchPrivacySettings', 'lastSeen'), callApi('fetchPrivacySettings', 'profilePhoto'), callApi('fetchPrivacySettings', 'forwards'), callApi('fetchPrivacySettings', 'chatInvite'), ]); if ( !phoneNumberSettings || !lastSeenSettings || !profilePhotoSettings || !forwardsSettings || !chatInviteSettings ) { return; } const global = getGlobal(); global.settings.privacy.phoneNumber = phoneNumberSettings; global.settings.privacy.lastSeen = lastSeenSettings; global.settings.privacy.profilePhoto = profilePhotoSettings; global.settings.privacy.forwards = forwardsSettings; global.settings.privacy.chatInvite = chatInviteSettings; setGlobal(global); })(); }); addReducer('setPrivacyVisibility', (global, actions, payload) => { const { privacyKey, visibility } = payload!; const { privacy: { [privacyKey as ApiPrivacyKey]: settings }, } = global.settings; if (!settings) { return; } const rules = buildInputPrivacyRules(global, { visibility, allowedIds: [...settings.allowUserIds, ...settings.allowChatIds], deniedIds: [...settings.blockUserIds, ...settings.blockChatIds], }); (async () => { const result = await callApi('setPrivacySettings', privacyKey, rules); if (result) { const newGlobal = getGlobal(); newGlobal.settings.privacy[privacyKey as ApiPrivacyKey] = result; setGlobal(newGlobal); } })(); }); addReducer('setPrivacySettings', (global, actions, payload) => { const { privacyKey, isAllowList, contactsIds } = payload!; const { privacy: { [privacyKey as ApiPrivacyKey]: settings }, } = global.settings; if (!settings) { return; } const rules = buildInputPrivacyRules(global, { visibility: settings.visibility, allowedIds: isAllowList ? contactsIds : [...settings.allowUserIds, ...settings.allowChatIds], deniedIds: !isAllowList ? contactsIds : [...settings.blockUserIds, ...settings.blockChatIds], }); (async () => { const result = await callApi('setPrivacySettings', privacyKey, rules); if (result) { const newGlobal = getGlobal(); newGlobal.settings.privacy[privacyKey as ApiPrivacyKey] = result; setGlobal(newGlobal); } })(); }); function buildInputPrivacyRules(global: GlobalState, { visibility, allowedIds, deniedIds, }: { visibility: PrivacyVisibility; allowedIds: string[]; deniedIds: string[]; }): InputPrivacyRules { const { users: { byId: usersById }, chats: { byId: chatsById }, } = global; const rules: InputPrivacyRules = { visibility, }; let users: InputPrivacyContact[]; let chats: InputPrivacyContact[]; const collectUsers = (userId: string) => { if (!isUserId(userId)) { return undefined; } const { id, accessHash } = usersById[userId] || {}; if (!id) { return undefined; } return { id, accessHash }; }; const collectChats = (userId: string) => { if (isUserId(userId)) { return undefined; } const chat = chatsById[userId]; return chat ? { id: chat.id } : undefined; }; if (visibility === 'contacts' || visibility === 'nobody') { users = allowedIds.map(collectUsers).filter(Boolean) as InputPrivacyContact[]; chats = allowedIds.map(collectChats).filter(Boolean) as InputPrivacyContact[]; if (users.length > 0) { rules.allowedUsers = users; } if (chats.length > 0) { rules.allowedChats = chats; } } if (visibility === 'everybody' || visibility === 'contacts') { users = deniedIds.map(collectUsers).filter(Boolean) as InputPrivacyContact[]; chats = deniedIds.map(collectChats).filter(Boolean) as InputPrivacyContact[]; if (users.length > 0) { rules.blockedUsers = users; } if (chats.length > 0) { rules.blockedChats = chats; } } return rules; } addReducer('updateIsOnline', (global, actions, payload) => { callApi('updateIsOnline', payload); }); addReducer('loadContentSettings', () => { (async () => { const result = await callApi('fetchContentSettings'); if (!result) return; setGlobal(replaceSettings(getGlobal(), result)); })(); }); addReducer('updateContentSettings', (global, actions, payload) => { (async () => { setGlobal(replaceSettings(getGlobal(), { isSensitiveEnabled: payload })); const result = await callApi('updateContentSettings', payload); if (!result) { setGlobal(replaceSettings(getGlobal(), { isSensitiveEnabled: !payload })); } })(); }); addReducer('loadCountryList', (global, actions, payload = {}) => { let { langCode } = payload; if (!langCode) langCode = global.settings.byKey.language; (async () => { const countryList = await callApi('fetchCountryList', { langCode }); if (!countryList) return; setGlobal({ ...getGlobal(), countryList, }); })(); }); addReducer('ensureTimeFormat', (global, actions) => { if (global.authNearestCountry) { const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(global.authNearestCountry.toUpperCase()) ? '12h' : '24h'; actions.setSettingOption({ timeFormat }); setTimeFormat(timeFormat); } (async () => { if (getGlobal().settings.byKey.wasTimeFormatSetManually) { return; } const nearestCountryCode = await callApi('fetchNearestCountry'); if (nearestCountryCode) { const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(nearestCountryCode.toUpperCase()) ? '12h' : '24h'; actions.setSettingOption({ timeFormat }); setTimeFormat(timeFormat); } })(); });