Profile Management: Add Contact (#1519)
This commit is contained in:
parent
d82cdce9ef
commit
394b91533b
@ -28,7 +28,7 @@ export {
|
||||
export {
|
||||
fetchFullUser, fetchNearestCountry, fetchCountryList,
|
||||
fetchTopUsers, fetchContactList, fetchUsers,
|
||||
updateContact, deleteUser, fetchProfilePhotos,
|
||||
addContact, updateContact, deleteUser, fetchProfilePhotos,
|
||||
} from './users';
|
||||
|
||||
export {
|
||||
|
||||
@ -149,6 +149,27 @@ export function updateContact({
|
||||
}));
|
||||
}
|
||||
|
||||
export function addContact({
|
||||
id,
|
||||
accessHash,
|
||||
phoneNumber = '',
|
||||
firstName = '',
|
||||
lastName = '',
|
||||
}: {
|
||||
id: number;
|
||||
accessHash?: string;
|
||||
phoneNumber?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
}) {
|
||||
return invokeRequest(new GramJs.contacts.AddContact({
|
||||
id: buildInputEntity(id, accessHash) as GramJs.InputUser,
|
||||
firstName,
|
||||
lastName,
|
||||
phone: phoneNumber,
|
||||
}), true);
|
||||
}
|
||||
|
||||
export async function deleteUser({
|
||||
id,
|
||||
accessHash,
|
||||
|
||||
@ -9,9 +9,13 @@ import { IAnchorPosition } from '../../types';
|
||||
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
|
||||
import { disableScrolling, enableScrolling } from '../../util/scrollLock';
|
||||
import { selectChat, selectNotifySettings, selectNotifyExceptions } from '../../modules/selectors';
|
||||
import {
|
||||
selectChat, selectNotifySettings, selectNotifyExceptions, selectUser,
|
||||
} from '../../modules/selectors';
|
||||
import { pick } from '../../util/iteratees';
|
||||
import { isChatPrivate, getCanDeleteChat, selectIsChatMuted } from '../../modules/helpers';
|
||||
import {
|
||||
isChatPrivate, getCanDeleteChat, selectIsChatMuted, getCanAddContact,
|
||||
} from '../../modules/helpers';
|
||||
import useShowTransition from '../../hooks/useShowTransition';
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
@ -23,7 +27,7 @@ import DeleteChatModal from '../common/DeleteChatModal';
|
||||
import './HeaderMenuContainer.scss';
|
||||
|
||||
type DispatchProps = Pick<GlobalActions, (
|
||||
'updateChatMutedState' | 'enterMessageSelectMode' | 'sendBotCommand' | 'restartBot' | 'openLinkedChat'
|
||||
'updateChatMutedState' | 'enterMessageSelectMode' | 'sendBotCommand' | 'restartBot' | 'openLinkedChat' | 'addContact'
|
||||
)>;
|
||||
|
||||
export type OwnProps = {
|
||||
@ -48,6 +52,7 @@ type StateProps = {
|
||||
chat?: ApiChat;
|
||||
isPrivate?: boolean;
|
||||
isMuted?: boolean;
|
||||
canAddContact?: boolean;
|
||||
canDeleteChat?: boolean;
|
||||
hasLinkedChat?: boolean;
|
||||
};
|
||||
@ -68,6 +73,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
isMuted,
|
||||
canDeleteChat,
|
||||
hasLinkedChat,
|
||||
canAddContact,
|
||||
onSubscribeChannel,
|
||||
onSearchClick,
|
||||
onClose,
|
||||
@ -77,6 +83,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
sendBotCommand,
|
||||
restartBot,
|
||||
openLinkedChat,
|
||||
addContact,
|
||||
}) => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(true);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
@ -117,6 +124,11 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
closeMenu();
|
||||
}, [chatId, closeMenu, openLinkedChat]);
|
||||
|
||||
const handleAddContactClick = useCallback(() => {
|
||||
addContact({ userId: chatId });
|
||||
closeMenu();
|
||||
}, [addContact, chatId, closeMenu]);
|
||||
|
||||
const handleSubscribe = useCallback(() => {
|
||||
onSubscribeChannel();
|
||||
closeMenu();
|
||||
@ -173,6 +185,14 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
{lang(isChannel ? 'Subscribe' : 'Join Group')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{canAddContact && (
|
||||
<MenuItem
|
||||
icon="add-user"
|
||||
onClick={handleAddContactClick}
|
||||
>
|
||||
{lang('AddContact')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{IS_SINGLE_COLUMN_LAYOUT && canSearch && (
|
||||
<MenuItem
|
||||
icon="search"
|
||||
@ -233,11 +253,15 @@ export default memo(withGlobal<OwnProps>(
|
||||
if (!chat || chat.isRestricted) {
|
||||
return {};
|
||||
}
|
||||
const isPrivate = isChatPrivate(chat.id);
|
||||
const user = isPrivate ? selectUser(global, chatId) : undefined;
|
||||
const canAddContact = user && getCanAddContact(user);
|
||||
|
||||
return {
|
||||
chat,
|
||||
isMuted: selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)),
|
||||
isPrivate: isChatPrivate(chat.id),
|
||||
isPrivate,
|
||||
canAddContact,
|
||||
canDeleteChat: getCanDeleteChat(chat),
|
||||
hasLinkedChat: Boolean(chat?.fullInfo?.linkedChatId),
|
||||
};
|
||||
@ -248,5 +272,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
'sendBotCommand',
|
||||
'restartBot',
|
||||
'openLinkedChat',
|
||||
'addContact',
|
||||
]),
|
||||
)(HeaderMenuContainer));
|
||||
|
||||
@ -16,8 +16,14 @@ import {
|
||||
selectCurrentStickerSearch,
|
||||
selectCurrentTextSearch,
|
||||
selectIsChatWithSelf,
|
||||
selectUser,
|
||||
} from '../../modules/selectors';
|
||||
import { isChatAdmin, isChatChannel, isChatPrivate } from '../../modules/helpers';
|
||||
import {
|
||||
getCanAddContact,
|
||||
isChatAdmin,
|
||||
isChatChannel,
|
||||
isChatPrivate,
|
||||
} from '../../modules/helpers';
|
||||
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
@ -44,8 +50,10 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
canAddContact?: boolean;
|
||||
canManage?: boolean;
|
||||
isChannel?: boolean;
|
||||
userId?: number;
|
||||
messageSearchQuery?: string;
|
||||
stickerSearchQuery?: string;
|
||||
gifSearchQuery?: string;
|
||||
@ -53,7 +61,7 @@ type StateProps = {
|
||||
|
||||
type DispatchProps = Pick<GlobalActions, (
|
||||
'setLocalTextSearchQuery' | 'setStickerSearchQuery' | 'setGifSearchQuery' |
|
||||
'searchTextMessagesLocal' | 'toggleManagement' | 'openHistoryCalendar'
|
||||
'searchTextMessagesLocal' | 'toggleManagement' | 'openHistoryCalendar' | 'addContact'
|
||||
)>;
|
||||
|
||||
const COLUMN_CLOSE_DELAY_MS = 300;
|
||||
@ -94,6 +102,8 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
isAddingChatMembers,
|
||||
profileState,
|
||||
managementScreen,
|
||||
canAddContact,
|
||||
userId,
|
||||
canManage,
|
||||
isChannel,
|
||||
onClose,
|
||||
@ -107,6 +117,7 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
toggleManagement,
|
||||
openHistoryCalendar,
|
||||
shouldSkipAnimation,
|
||||
addContact,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const backButtonRef = useRef<HTMLDivElement>(null);
|
||||
@ -127,6 +138,10 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
setGifSearchQuery({ query });
|
||||
}, [setGifSearchQuery]);
|
||||
|
||||
const handleAddContact = useCallback(() => {
|
||||
addContact({ userId });
|
||||
}, [addContact, userId]);
|
||||
|
||||
const [shouldSkipTransition, setShouldSkipTransition] = useState(!isColumnOpen);
|
||||
|
||||
useEffect(() => {
|
||||
@ -263,6 +278,17 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
<>
|
||||
<h3>Profile</h3>
|
||||
<section className="tools">
|
||||
{canAddContact && (
|
||||
<Button
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('AddContact')}
|
||||
onClick={handleAddContact}
|
||||
>
|
||||
<i className="icon-add-user" />
|
||||
</Button>
|
||||
)}
|
||||
{canManage && (
|
||||
<Button
|
||||
round
|
||||
@ -323,10 +349,13 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { query: gifSearchQuery } = selectCurrentGifSearch(global) || {};
|
||||
const chat = chatId ? selectChat(global, chatId) : undefined;
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
const user = isProfile && chatId && isChatPrivate(chatId) ? selectUser(global, chatId) : undefined;
|
||||
|
||||
const canAddContact = user && getCanAddContact(user);
|
||||
const canManage = Boolean(
|
||||
!isManagement
|
||||
&& isProfile
|
||||
&& !canAddContact
|
||||
&& chat
|
||||
&& !selectIsChatWithSelf(global, chat.id)
|
||||
// chat.isCreator is for Basic Groups
|
||||
@ -335,7 +364,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
canManage,
|
||||
canAddContact,
|
||||
isChannel,
|
||||
userId: user?.id,
|
||||
messageSearchQuery,
|
||||
stickerSearchQuery,
|
||||
gifSearchQuery,
|
||||
@ -348,5 +379,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
'searchTextMessagesLocal',
|
||||
'toggleManagement',
|
||||
'openHistoryCalendar',
|
||||
'addContact',
|
||||
]),
|
||||
)(RightHeader));
|
||||
|
||||
@ -504,7 +504,7 @@ export type ActionTypes = (
|
||||
'acceptInviteConfirmation' |
|
||||
// users
|
||||
'loadFullUser' | 'openUserInfo' | 'loadNearestCountry' | 'loadCountryList' | 'loadTopUsers' | 'loadContactList' |
|
||||
'loadCurrentUser' | 'updateProfile' | 'checkUsername' | 'updateContact' |
|
||||
'loadCurrentUser' | 'updateProfile' | 'checkUsername' | 'addContact' | 'updateContact' |
|
||||
'deleteUser' | 'loadUser' | 'setUserSearchQuery' |
|
||||
// Channel / groups creation
|
||||
'createChannel' | 'createGroupChat' | 'resetChatCreation' |
|
||||
|
||||
@ -36,5 +36,5 @@ if (DEBUG) {
|
||||
|
||||
document.addEventListener('dblclick', () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('GLOBAL STATE', getGlobal());
|
||||
console.warn('GLOBAL STATE', getGlobal());
|
||||
});
|
||||
|
||||
@ -969,6 +969,7 @@ contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
|
||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
|
||||
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
|
||||
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
|
||||
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
||||
messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
|
||||
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
|
||||
@ -970,6 +970,7 @@ contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
|
||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
|
||||
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
|
||||
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
|
||||
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
||||
messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
|
||||
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
|
||||
@ -6,7 +6,7 @@ import { ApiUser } from '../../../api/types';
|
||||
import { ManagementProgress } from '../../../types';
|
||||
|
||||
import { debounce, throttle } from '../../../util/schedulers';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { buildCollectionByKey, pick } from '../../../util/iteratees';
|
||||
import { isChatPrivate } from '../../helpers';
|
||||
import { callApi } from '../../../api/gramjs';
|
||||
import { selectChat, selectUser } from '../../selectors';
|
||||
@ -150,7 +150,19 @@ async function updateContact(
|
||||
|
||||
setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.InProgress));
|
||||
|
||||
const result = await callApi('updateContact', { phone: user.phoneNumber, firstName, lastName });
|
||||
let result;
|
||||
if (user.phoneNumber) {
|
||||
result = await callApi('updateContact', { phone: user.phoneNumber, firstName, lastName });
|
||||
} else {
|
||||
const { id, accessHash } = user;
|
||||
result = await callApi('addContact', {
|
||||
id,
|
||||
accessHash,
|
||||
phoneNumber: '',
|
||||
firstName,
|
||||
lastName,
|
||||
});
|
||||
}
|
||||
|
||||
if (result) {
|
||||
setGlobal(updateUser(
|
||||
@ -217,6 +229,16 @@ addReducer('setUserSearchQuery', (global, actions, payload) => {
|
||||
});
|
||||
});
|
||||
|
||||
addReducer('addContact', (global, actions, payload) => {
|
||||
const { userId } = payload!;
|
||||
const user = selectUser(global, userId);
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
void callApi('addContact', pick(user, ['id', 'accessHash', 'firstName', 'lastName', 'phoneNumber']));
|
||||
});
|
||||
|
||||
async function searchUsers(query: string) {
|
||||
const result = await callApi('searchChats', { query });
|
||||
|
||||
|
||||
@ -183,6 +183,10 @@ export function isUserBot(user: ApiUser) {
|
||||
return user.type === 'userTypeBot';
|
||||
}
|
||||
|
||||
export function getCanAddContact(user: ApiUser) {
|
||||
return !user.isContact && !isUserBot(user);
|
||||
}
|
||||
|
||||
export function sortUserIds(
|
||||
userIds: number[],
|
||||
usersById: Record<number, ApiUser>,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user