Left Search, New Chat, Forward Picker: Order chats by last message, check rights to post, add archived
This commit is contained in:
parent
79cde7292b
commit
1984318297
@ -163,8 +163,8 @@ export function fetchFullChat(chat: ApiChat) {
|
|||||||
: getFullChatInfo(input as number);
|
: getFullChatInfo(input as number);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchChats({ query, limit }: { query: string; limit?: number }) {
|
export async function searchChats({ query }: { query: string }) {
|
||||||
const result = await invokeRequest(new GramJs.contacts.Search({ q: query, limit }));
|
const result = await invokeRequest(new GramJs.contacts.Search({ q: query }));
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ type OwnProps = {
|
|||||||
typingStatus?: ApiTypingStatus;
|
typingStatus?: ApiTypingStatus;
|
||||||
avatarSize?: 'small' | 'medium' | 'large' | 'jumbo';
|
avatarSize?: 'small' | 'medium' | 'large' | 'jumbo';
|
||||||
withMediaViewer?: boolean;
|
withMediaViewer?: boolean;
|
||||||
withHandle?: boolean;
|
withUsername?: boolean;
|
||||||
withFullInfo?: boolean;
|
withFullInfo?: boolean;
|
||||||
withUpdatingStatus?: boolean;
|
withUpdatingStatus?: boolean;
|
||||||
withChatType?: boolean;
|
withChatType?: boolean;
|
||||||
@ -45,7 +45,7 @@ const GroupChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
typingStatus,
|
typingStatus,
|
||||||
avatarSize = 'medium',
|
avatarSize = 'medium',
|
||||||
withMediaViewer,
|
withMediaViewer,
|
||||||
withHandle,
|
withUsername,
|
||||||
withFullInfo,
|
withFullInfo,
|
||||||
withUpdatingStatus,
|
withUpdatingStatus,
|
||||||
withChatType,
|
withChatType,
|
||||||
@ -102,7 +102,7 @@ const GroupChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handle = withHandle ? chat.username : undefined;
|
const handle = withUsername ? chat.username : undefined;
|
||||||
const groupStatus = getGroupStatus(chat, lang);
|
const groupStatus = getGroupStatus(chat, lang);
|
||||||
const onlineStatus = onlineCount ? `, ${lang('OnlineCount', onlineCount, 'i')}` : undefined;
|
const onlineStatus = onlineCount ? `, ${lang('OnlineCount', onlineCount, 'i')}` : undefined;
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ type OwnProps = {
|
|||||||
forceShowSelf?: boolean;
|
forceShowSelf?: boolean;
|
||||||
status?: string;
|
status?: string;
|
||||||
withMediaViewer?: boolean;
|
withMediaViewer?: boolean;
|
||||||
withHandle?: boolean;
|
withUsername?: boolean;
|
||||||
withFullInfo?: boolean;
|
withFullInfo?: boolean;
|
||||||
withUpdatingStatus?: boolean;
|
withUpdatingStatus?: boolean;
|
||||||
noStatusOrTyping?: boolean;
|
noStatusOrTyping?: boolean;
|
||||||
@ -44,7 +44,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
avatarSize = 'medium',
|
avatarSize = 'medium',
|
||||||
status,
|
status,
|
||||||
withMediaViewer,
|
withMediaViewer,
|
||||||
withHandle,
|
withUsername,
|
||||||
withFullInfo,
|
withFullInfo,
|
||||||
withUpdatingStatus,
|
withUpdatingStatus,
|
||||||
noStatusOrTyping,
|
noStatusOrTyping,
|
||||||
@ -103,7 +103,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`status ${isUserOnline(user) ? 'online' : ''}`}>
|
<div className={`status ${isUserOnline(user) ? 'online' : ''}`}>
|
||||||
{withHandle && user.username && <span className="handle">{user.username}</span>}
|
{withUsername && user.username && <span className="handle">{user.username}</span>}
|
||||||
<span className="user-status">{getUserStatus(user, lang)}</span>
|
<span className="user-status">{getUserStatus(user, lang)}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -129,7 +129,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
{user && user.isVerified && <VerifiedIcon />}
|
{user && user.isVerified && <VerifiedIcon />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isSavedMessages && !noStatusOrTyping && renderStatusOrTyping()}
|
{(status || (!isSavedMessages && !noStatusOrTyping)) && renderStatusOrTyping()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { IS_MOBILE_SCREEN } from '../../../util/environment';
|
|||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import searchWords from '../../../util/searchWords';
|
import searchWords from '../../../util/searchWords';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { getUserFullName, getSortedUserIds } from '../../../modules/helpers';
|
import { getUserFullName, sortUserIds } from '../../../modules/helpers';
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
@ -63,7 +63,7 @@ const ContactList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return fullName && searchWords(fullName, filter);
|
return fullName && searchWords(fullName, filter);
|
||||||
}) : contactIds;
|
}) : contactIds;
|
||||||
|
|
||||||
return getSortedUserIds(resultIds, usersById);
|
return sortUserIds(resultIds, usersById);
|
||||||
}, [filter, usersById, contactIds]);
|
}, [filter, usersById, contactIds]);
|
||||||
|
|
||||||
const [viewportIds, getMore] = useInfiniteScroll(undefined, listIds, Boolean(filter));
|
const [viewportIds, getMore] = useInfiniteScroll(undefined, listIds, Boolean(filter));
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import React, {
|
|||||||
import { withGlobal } from '../../../lib/teact/teactn';
|
import { withGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
import { ApiUser } from '../../../api/types';
|
import { ApiChat, ApiUser } from '../../../api/types';
|
||||||
|
|
||||||
import { pick, unique } from '../../../util/iteratees';
|
import { pick, unique } from '../../../util/iteratees';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import searchWords from '../../../util/searchWords';
|
import searchWords from '../../../util/searchWords';
|
||||||
import { getSortedUserIds, getUserFullName } from '../../../modules/helpers';
|
import { getUserFullName, sortChatIds } from '../../../modules/helpers';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import Picker from '../../common/Picker';
|
import Picker from '../../common/Picker';
|
||||||
@ -27,11 +27,12 @@ export type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
currentUserId?: number;
|
currentUserId?: number;
|
||||||
usersById: Record<number, ApiUser>;
|
usersById: Record<number, ApiUser>;
|
||||||
|
chatsById: Record<number, ApiChat>;
|
||||||
localContactIds?: number[];
|
localContactIds?: number[];
|
||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
isSearching?: boolean;
|
isSearching?: boolean;
|
||||||
localUsers?: ApiUser[];
|
localUserIds?: number[];
|
||||||
globalUsers?: ApiUser[];
|
globalUserIds?: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'loadContactList' | 'setGlobalSearchQuery'>;
|
type DispatchProps = Pick<GlobalActions, 'loadContactList' | 'setGlobalSearchQuery'>;
|
||||||
@ -46,11 +47,12 @@ const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onReset,
|
onReset,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
usersById,
|
usersById,
|
||||||
|
chatsById,
|
||||||
localContactIds,
|
localContactIds,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
isSearching,
|
isSearching,
|
||||||
localUsers,
|
localUserIds,
|
||||||
globalUsers,
|
globalUserIds,
|
||||||
loadContactList,
|
loadContactList,
|
||||||
setGlobalSearchQuery,
|
setGlobalSearchQuery,
|
||||||
}) => {
|
}) => {
|
||||||
@ -67,13 +69,15 @@ const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}, [setGlobalSearchQuery]);
|
}, [setGlobalSearchQuery]);
|
||||||
|
|
||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
const contactIds = localContactIds ? localContactIds.filter((id) => id !== currentUserId) : [];
|
const contactIds = localContactIds
|
||||||
|
? sortChatIds(localContactIds.filter((id) => id !== currentUserId), chatsById)
|
||||||
|
: [];
|
||||||
|
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
return contactIds;
|
return contactIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundLocalContacts = contactIds.filter((id) => {
|
const foundContactIds = contactIds.filter((id) => {
|
||||||
const user = usersById[id];
|
const user = usersById[id];
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return false;
|
return false;
|
||||||
@ -82,16 +86,19 @@ const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return fullName && searchWords(fullName, searchQuery);
|
return fullName && searchWords(fullName, searchQuery);
|
||||||
});
|
});
|
||||||
|
|
||||||
return getSortedUserIds(
|
return sortChatIds(
|
||||||
unique([
|
unique([
|
||||||
...foundLocalContacts,
|
...foundContactIds,
|
||||||
...(localUsers ? localUsers.map((user) => user.id) : []),
|
...(localUserIds || []),
|
||||||
...(globalUsers ? globalUsers.map((user) => user.id) : []),
|
...(globalUserIds || []),
|
||||||
]) as number[],
|
]),
|
||||||
usersById,
|
chatsById,
|
||||||
|
false,
|
||||||
selectedMemberIds,
|
selectedMemberIds,
|
||||||
);
|
);
|
||||||
}, [localContactIds, searchQuery, localUsers, globalUsers, usersById, selectedMemberIds, currentUserId]);
|
}, [
|
||||||
|
localContactIds, searchQuery, localUserIds, globalUserIds, usersById, chatsById, selectedMemberIds, currentUserId,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleNextStep = useCallback(() => {
|
const handleNextStep = useCallback(() => {
|
||||||
if (selectedMemberIds.length) {
|
if (selectedMemberIds.length) {
|
||||||
@ -144,6 +151,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const { userIds: localContactIds } = global.contactList || {};
|
const { userIds: localContactIds } = global.contactList || {};
|
||||||
const { byId: usersById } = global.users;
|
const { byId: usersById } = global.users;
|
||||||
|
const { byId: chatsById } = global.chats;
|
||||||
const { currentUserId } = global;
|
const { currentUserId } = global;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -152,17 +160,18 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
globalResults,
|
globalResults,
|
||||||
localResults,
|
localResults,
|
||||||
} = global.globalSearch;
|
} = global.globalSearch;
|
||||||
const { users: globalUsers } = globalResults || {};
|
const { userIds: globalUserIds } = globalResults || {};
|
||||||
const { users: localUsers } = localResults || {};
|
const { userIds: localUserIds } = localResults || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentUserId,
|
currentUserId,
|
||||||
usersById,
|
usersById,
|
||||||
|
chatsById,
|
||||||
localContactIds,
|
localContactIds,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
isSearching: fetchingStatus && fetchingStatus.chats,
|
isSearching: fetchingStatus && fetchingStatus.chats,
|
||||||
globalUsers,
|
globalUserIds,
|
||||||
localUsers,
|
localUserIds,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, ['loadContactList', 'setGlobalSearchQuery']),
|
(setGlobal, actions): DispatchProps => pick(actions, ['loadContactList', 'setGlobalSearchQuery']),
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { LoadMoreDirection } from '../../../types';
|
|||||||
import { IS_MOBILE_SCREEN } from '../../../util/environment';
|
import { IS_MOBILE_SCREEN } from '../../../util/environment';
|
||||||
import searchWords from '../../../util/searchWords';
|
import searchWords from '../../../util/searchWords';
|
||||||
import { unique, pick } from '../../../util/iteratees';
|
import { unique, pick } from '../../../util/iteratees';
|
||||||
import { getUserFullName, getMessageSummaryText } from '../../../modules/helpers';
|
import { getUserFullName, getMessageSummaryText, sortChatIds } from '../../../modules/helpers';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
@ -36,10 +36,10 @@ export type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
currentUserId?: number;
|
currentUserId?: number;
|
||||||
localContactIds?: number[];
|
localContactIds?: number[];
|
||||||
localChats?: ApiChat[];
|
localChatIds?: number[];
|
||||||
localUsers?: ApiUser[];
|
localUserIds?: number[];
|
||||||
globalChats?: ApiChat[];
|
globalChatIds?: number[];
|
||||||
globalUsers?: ApiUser[];
|
globalUserIds?: number[];
|
||||||
foundIds?: string[];
|
foundIds?: string[];
|
||||||
globalMessagesByChatId?: Record<number, { byId: Record<number, ApiMessage> }>;
|
globalMessagesByChatId?: Record<number, { byId: Record<number, ApiMessage> }>;
|
||||||
chatsById: Record<number, ApiChat>;
|
chatsById: Record<number, ApiChat>;
|
||||||
@ -55,12 +55,11 @@ type DispatchProps = Pick<GlobalActions, (
|
|||||||
const MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH = 4;
|
const MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH = 4;
|
||||||
const LESS_LIST_ITEMS_AMOUNT = 3;
|
const LESS_LIST_ITEMS_AMOUNT = 3;
|
||||||
|
|
||||||
const sortSearchResults = (a: ApiChat | ApiUser, b: ApiChat | ApiUser) => Number(b.isVerified) - Number(a.isVerified);
|
|
||||||
const runThrottled = throttle((cb) => cb(), 500, true);
|
const runThrottled = throttle((cb) => cb(), 500, true);
|
||||||
|
|
||||||
const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
searchQuery, searchDate, dateSearchQuery, currentUserId,
|
searchQuery, searchDate, dateSearchQuery, currentUserId,
|
||||||
localContactIds, localChats, localUsers, globalChats, globalUsers,
|
localContactIds, localChatIds, localUserIds, globalChatIds, globalUserIds,
|
||||||
foundIds, globalMessagesByChatId, chatsById, usersById, fetchingStatus, lastSyncTime,
|
foundIds, globalMessagesByChatId, chatsById, usersById, fetchingStatus, lastSyncTime,
|
||||||
onReset, onSearchDateSelect, openChat, addRecentlyFoundChatId, searchMessagesGlobal, setGlobalSearchChatId,
|
onReset, onSearchDateSelect, openChat, addRecentlyFoundChatId, searchMessagesGlobal, setGlobalSearchChatId,
|
||||||
}) => {
|
}) => {
|
||||||
@ -102,7 +101,7 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return MEMO_EMPTY_ARRAY;
|
return MEMO_EMPTY_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundLocalContacts = localContactIds
|
const foundContactIds = localContactIds
|
||||||
? localContactIds.filter((id) => {
|
? localContactIds.filter((id) => {
|
||||||
const user = usersById[id];
|
const user = usersById[id];
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@ -111,26 +110,26 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const fullName = getUserFullName(user);
|
const fullName = getUserFullName(user);
|
||||||
return (fullName && searchWords(fullName, searchQuery)) || searchWords(user.username, searchQuery);
|
return (fullName && searchWords(fullName, searchQuery)) || searchWords(user.username, searchQuery);
|
||||||
}).map((id) => usersById[id])
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return unique([
|
return [
|
||||||
...(searchWords(getTranslation('SavedMessages'), searchQuery) ? [currentUserId] : []),
|
...(currentUserId && searchWords(getTranslation('SavedMessages'), searchQuery) ? [currentUserId] : []),
|
||||||
...([
|
...sortChatIds(unique([
|
||||||
...foundLocalContacts,
|
...foundContactIds,
|
||||||
...(localChats || []),
|
...(localChatIds || []),
|
||||||
...(localUsers || []),
|
...(localUserIds || []),
|
||||||
].sort(sortSearchResults).map((chat) => chat.id)),
|
]), chatsById),
|
||||||
]) as number[];
|
];
|
||||||
}, [searchQuery, localContactIds, localChats, localUsers, usersById, currentUserId]);
|
}, [searchQuery, localContactIds, currentUserId, localChatIds, localUserIds, chatsById, usersById]);
|
||||||
|
|
||||||
const globalResults = useMemo(() => {
|
const globalResults = useMemo(() => {
|
||||||
if (!searchQuery || searchQuery.length < MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH || !globalChats || !globalUsers) {
|
if (!searchQuery || searchQuery.length < MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH || !globalChatIds || !globalUserIds) {
|
||||||
return MEMO_EMPTY_ARRAY;
|
return MEMO_EMPTY_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return unique([...globalChats, ...globalUsers].sort(sortSearchResults).map((chat) => chat.id));
|
return sortChatIds(unique([...globalChatIds, ...globalUserIds]), chatsById, true);
|
||||||
}, [globalChats, globalUsers, searchQuery]);
|
}, [chatsById, globalChatIds, globalUserIds, searchQuery]);
|
||||||
|
|
||||||
const foundMessages = useMemo(() => {
|
const foundMessages = useMemo(() => {
|
||||||
if ((!searchQuery && !searchDate) || !foundIds || foundIds.length === 0) {
|
if ((!searchQuery && !searchDate) || !foundIds || foundIds.length === 0) {
|
||||||
@ -248,7 +247,7 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<LeftSearchResultChat
|
<LeftSearchResultChat
|
||||||
chatId={id}
|
chatId={id}
|
||||||
withHandle
|
withUsername
|
||||||
onClick={handleChatClick}
|
onClick={handleChatClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -283,21 +282,18 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const {
|
const {
|
||||||
fetchingStatus, globalResults, localResults, resultsByType,
|
fetchingStatus, globalResults, localResults, resultsByType,
|
||||||
} = global.globalSearch;
|
} = global.globalSearch;
|
||||||
const {
|
const { chatIds: globalChatIds, userIds: globalUserIds } = globalResults || {};
|
||||||
chats: globalChats,
|
const { chatIds: localChatIds, userIds: localUserIds } = localResults || {};
|
||||||
users: globalUsers,
|
|
||||||
} = globalResults || {};
|
|
||||||
const { chats: localChats, users: localUsers } = localResults || {};
|
|
||||||
const { byChatId: globalMessagesByChatId } = messages;
|
const { byChatId: globalMessagesByChatId } = messages;
|
||||||
const { foundIds } = (resultsByType && resultsByType.text) || {};
|
const { foundIds } = (resultsByType && resultsByType.text) || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentUserId,
|
currentUserId,
|
||||||
localContactIds,
|
localContactIds,
|
||||||
localChats,
|
localChatIds,
|
||||||
localUsers,
|
localUserIds,
|
||||||
globalChats,
|
globalChatIds,
|
||||||
globalUsers,
|
globalUserIds,
|
||||||
foundIds,
|
foundIds,
|
||||||
globalMessagesByChatId,
|
globalMessagesByChatId,
|
||||||
chatsById,
|
chatsById,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import ListItem from '../../ui/ListItem';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
withHandle?: boolean;
|
withUsername?: boolean;
|
||||||
onClick: (id: number) => void;
|
onClick: (id: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
|||||||
chat,
|
chat,
|
||||||
privateChatUser,
|
privateChatUser,
|
||||||
isPinned,
|
isPinned,
|
||||||
withHandle,
|
withUsername,
|
||||||
onClick,
|
onClick,
|
||||||
}) => {
|
}) => {
|
||||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
||||||
@ -53,9 +53,9 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
|||||||
contextActions={contextActions}
|
contextActions={contextActions}
|
||||||
>
|
>
|
||||||
{isChatPrivate(chatId) ? (
|
{isChatPrivate(chatId) ? (
|
||||||
<PrivateChatInfo userId={chatId} withHandle={withHandle} avatarSize="large" />
|
<PrivateChatInfo userId={chatId} withUsername={withUsername} avatarSize="large" />
|
||||||
) : (
|
) : (
|
||||||
<GroupChatInfo chatId={chatId} withHandle={withHandle} avatarSize="large" />
|
<GroupChatInfo chatId={chatId} withUsername={withUsername} avatarSize="large" />
|
||||||
)}
|
)}
|
||||||
<DeleteChatModal
|
<DeleteChatModal
|
||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import React, {
|
|||||||
import { withGlobal } from '../../lib/teact/teactn';
|
import { withGlobal } from '../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../global/types';
|
import { GlobalActions } from '../../global/types';
|
||||||
import { ApiChat, ApiUser } from '../../api/types';
|
import { ApiChat, MAIN_THREAD_ID } from '../../api/types';
|
||||||
|
|
||||||
import { IS_MOBILE_SCREEN } from '../../util/environment';
|
import { IS_MOBILE_SCREEN } from '../../util/environment';
|
||||||
import { getChatTitle, prepareChatList, isChatPrivate } from '../../modules/helpers';
|
import {
|
||||||
import { selectUser } from '../../modules/selectors';
|
getCanPostInChat, getChatTitle, isChatPrivate, sortChatIds,
|
||||||
|
} from '../../modules/helpers';
|
||||||
import searchWords from '../../util/searchWords';
|
import searchWords from '../../util/searchWords';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
import useInfiniteScroll from '../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../hooks/useInfiniteScroll';
|
||||||
@ -31,9 +32,10 @@ export type OwnProps = {
|
|||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
chatsById: Record<number, ApiChat>;
|
chatsById: Record<number, ApiChat>;
|
||||||
listIds?: number[];
|
activeListIds?: number[];
|
||||||
|
archivedListIds?: number[];
|
||||||
orderedPinnedIds?: number[];
|
orderedPinnedIds?: number[];
|
||||||
currentUser?: ApiUser;
|
currentUserId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'setForwardChatId' | 'exitForwardMode' | 'loadMoreChats'>;
|
type DispatchProps = Pick<GlobalActions, 'setForwardChatId' | 'exitForwardMode' | 'loadMoreChats'>;
|
||||||
@ -44,9 +46,9 @@ const MODAL_HIDE_DELAY_MS = 300;
|
|||||||
|
|
||||||
const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
|
const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
chatsById,
|
chatsById,
|
||||||
listIds,
|
activeListIds,
|
||||||
orderedPinnedIds,
|
archivedListIds,
|
||||||
currentUser,
|
currentUserId,
|
||||||
isOpen,
|
isOpen,
|
||||||
setForwardChatId,
|
setForwardChatId,
|
||||||
exitForwardMode,
|
exitForwardMode,
|
||||||
@ -78,30 +80,31 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
const chats = useMemo(() => {
|
|
||||||
const chatArrays = listIds ? prepareChatList(chatsById, listIds, orderedPinnedIds) : undefined;
|
|
||||||
if (!chatArrays) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const chatWithSelf = currentUser ? chatsById[currentUser.id] : undefined;
|
|
||||||
|
|
||||||
return [
|
|
||||||
...(chatWithSelf ? [chatWithSelf] : []),
|
|
||||||
...chatArrays.pinnedChats.filter(({ id }) => !chatWithSelf || id !== chatWithSelf.id),
|
|
||||||
...chatArrays.otherChats.filter(({ id }) => !chatWithSelf || id !== chatWithSelf.id),
|
|
||||||
];
|
|
||||||
}, [chatsById, listIds, orderedPinnedIds, currentUser]);
|
|
||||||
|
|
||||||
const chatIds = useMemo(() => {
|
const chatIds = useMemo(() => {
|
||||||
if (!chats) {
|
const listIds = [
|
||||||
return undefined;
|
...activeListIds || [],
|
||||||
}
|
...archivedListIds || [],
|
||||||
|
];
|
||||||
|
|
||||||
return chats
|
return sortChatIds([
|
||||||
.filter((chat) => (!filter || searchWords(getChatTitle(chat, currentUser), filter)))
|
...listIds.filter((id) => {
|
||||||
.map(({ id }) => id);
|
const chat = chatsById[id];
|
||||||
}, [chats, filter, currentUser]);
|
if (!chat) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getCanPostInChat(chat, MAIN_THREAD_ID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchWords(getChatTitle(chatsById[id], undefined, id === currentUserId), filter);
|
||||||
|
}),
|
||||||
|
], chatsById, undefined, currentUserId ? [currentUserId] : undefined);
|
||||||
|
}, [activeListIds, archivedListIds, chatsById, currentUserId, filter]);
|
||||||
|
|
||||||
const [viewportIds, getMore] = useInfiniteScroll(loadMoreChats, chatIds, Boolean(filter));
|
const [viewportIds, getMore] = useInfiniteScroll(loadMoreChats, chatIds, Boolean(filter));
|
||||||
|
|
||||||
@ -152,7 +155,7 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onClick={() => setForwardChatId({ id })}
|
onClick={() => setForwardChatId({ id })}
|
||||||
>
|
>
|
||||||
{isChatPrivate(id) ? (
|
{isChatPrivate(id) ? (
|
||||||
<PrivateChatInfo userId={id} />
|
<PrivateChatInfo status={id === currentUserId ? lang('SavedMessagesInfo') : undefined} userId={id} />
|
||||||
) : (
|
) : (
|
||||||
<GroupChatInfo chatId={id} />
|
<GroupChatInfo chatId={id} />
|
||||||
)}
|
)}
|
||||||
@ -174,16 +177,15 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
chats: {
|
chats: {
|
||||||
byId: chatsById,
|
byId: chatsById,
|
||||||
listIds,
|
listIds,
|
||||||
orderedPinnedIds,
|
|
||||||
},
|
},
|
||||||
currentUserId,
|
currentUserId,
|
||||||
} = global;
|
} = global;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chatsById,
|
chatsById,
|
||||||
listIds: listIds.active,
|
activeListIds: listIds.active,
|
||||||
orderedPinnedIds: orderedPinnedIds.active,
|
archivedListIds: listIds.archived,
|
||||||
currentUser: currentUserId ? selectUser(global, currentUserId) : undefined,
|
currentUserId,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, ['setForwardChatId', 'exitForwardMode', 'loadMoreChats']),
|
(setGlobal, actions): DispatchProps => pick(actions, ['setForwardChatId', 'exitForwardMode', 'loadMoreChats']),
|
||||||
|
|||||||
@ -152,7 +152,7 @@ const MentionMenu: FC<OwnProps> = ({
|
|||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
userId={userId}
|
userId={userId}
|
||||||
avatarSize="small"
|
avatarSize="small"
|
||||||
withHandle
|
withUsername
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { ApiChatMember, ApiMessage, ApiUser } from '../../../api/types';
|
|||||||
import { ProfileTabType, SharedMediaType } from '../../../types';
|
import { ProfileTabType, SharedMediaType } from '../../../types';
|
||||||
|
|
||||||
import { MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
|
import { MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
|
||||||
import { getMessageContentIds, getSortedUserIds } from '../../../modules/helpers';
|
import { getMessageContentIds, sortUserIds } from '../../../modules/helpers';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useOnChange from '../../../hooks/useOnChange';
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export default function useProfileViewportIds(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSortedUserIds(groupChatMembers.map(({ userId }) => userId), usersById);
|
return sortUserIds(groupChatMembers.map(({ userId }) => userId), usersById);
|
||||||
}, [groupChatMembers, usersById]);
|
}, [groupChatMembers, usersById]);
|
||||||
|
|
||||||
const [mediaViewportIds, getMoreMedia, noProfileInfoForMedia] = useInfiniteScrollForSharedMedia(
|
const [mediaViewportIds, getMoreMedia, noProfileInfoForMedia] = useInfiniteScrollForSharedMedia(
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { withGlobal } from '../../../lib/teact/teactn';
|
|||||||
import { ApiChatMember, ApiUser } from '../../../api/types';
|
import { ApiChatMember, ApiUser } from '../../../api/types';
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import { getSortedUserIds, isChatChannel } from '../../../modules/helpers';
|
import { sortUserIds, isChatChannel } from '../../../modules/helpers';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
@ -36,7 +36,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSortedUserIds(members.map(({ userId }) => userId), usersById);
|
return sortUserIds(members.map(({ userId }) => userId), usersById);
|
||||||
}, [members, usersById]);
|
}, [members, usersById]);
|
||||||
|
|
||||||
const handleMemberClick = useCallback((id: number) => {
|
const handleMemberClick = useCallback((id: number) => {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { ApiChatMember, ApiUser } from '../../../api/types';
|
|||||||
import { ManagementScreens } from '../../../types';
|
import { ManagementScreens } from '../../../types';
|
||||||
|
|
||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import { getSortedUserIds, isChatChannel } from '../../../modules/helpers';
|
import { sortUserIds, isChatChannel } from '../../../modules/helpers';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -37,7 +37,7 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSortedUserIds(members.filter((member) => !member.isOwner).map(({ userId }) => userId), usersById);
|
return sortUserIds(members.filter((member) => !member.isOwner).map(({ userId }) => userId), usersById);
|
||||||
}, [members, usersById]);
|
}, [members, usersById]);
|
||||||
|
|
||||||
const handleExceptionMemberClick = useCallback((memberId: number) => {
|
const handleExceptionMemberClick = useCallback((memberId: number) => {
|
||||||
|
|||||||
@ -180,6 +180,7 @@
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
line-height: 1.25rem;
|
line-height: 1.25rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
&.online {
|
&.online {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
|
|||||||
@ -224,12 +224,12 @@ export type GlobalState = {
|
|||||||
messages?: boolean;
|
messages?: boolean;
|
||||||
};
|
};
|
||||||
localResults?: {
|
localResults?: {
|
||||||
chats?: ApiChat[];
|
chatIds?: number[];
|
||||||
users?: ApiUser[];
|
userIds?: number[];
|
||||||
};
|
};
|
||||||
globalResults?: {
|
globalResults?: {
|
||||||
chats?: ApiChat[];
|
chatIds?: number[];
|
||||||
users?: ApiUser[];
|
userIds?: number[];
|
||||||
};
|
};
|
||||||
resultsByType?: Partial<Record<ApiGlobalMessageSearchType, {
|
resultsByType?: Partial<Record<ApiGlobalMessageSearchType, {
|
||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
|
|||||||
@ -65,7 +65,7 @@ addReducer('searchMessagesGlobal', (global, actions, payload) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function searchChats(query: string) {
|
async function searchChats(query: string) {
|
||||||
const result = await callApi('searchChats', { query, limit: GLOBAL_SEARCH_SLICE });
|
const result = await callApi('searchChats', { query });
|
||||||
|
|
||||||
let global = getGlobal();
|
let global = getGlobal();
|
||||||
const currentSearchQuery = selectCurrentGlobalSearchQuery(global);
|
const currentSearchQuery = selectCurrentGlobalSearchQuery(global);
|
||||||
@ -89,13 +89,13 @@ async function searchChats(query: string) {
|
|||||||
global = updateGlobalSearchFetchingStatus(global, { chats: false });
|
global = updateGlobalSearchFetchingStatus(global, { chats: false });
|
||||||
global = updateGlobalSearch(global, {
|
global = updateGlobalSearch(global, {
|
||||||
localResults: {
|
localResults: {
|
||||||
chats: localChats,
|
chatIds: localChats.map(({ id }) => id),
|
||||||
users: localUsers,
|
userIds: localUsers.map(({ id }) => id),
|
||||||
},
|
},
|
||||||
globalResults: {
|
globalResults: {
|
||||||
...global.globalSearch.globalResults,
|
...global.globalSearch.globalResults,
|
||||||
chats: globalChats,
|
chatIds: globalUsers.map(({ id }) => id),
|
||||||
users: globalUsers,
|
userIds: globalChats.map(({ id }) => id),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -58,8 +58,9 @@ export function getPrivateChatUserId(chat: ApiChat) {
|
|||||||
return chat.id;
|
return chat.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChatTitle(chat: ApiChat, user?: ApiUser) {
|
// TODO Get rid of `user`
|
||||||
if (user && chat.id === user.id && user.isSelf) {
|
export function getChatTitle(chat: ApiChat, user?: ApiUser, isSelf = false) {
|
||||||
|
if (isSelf || (user && chat.id === user.id && user.isSelf)) {
|
||||||
return getTranslation('SavedMessages');
|
return getTranslation('SavedMessages');
|
||||||
}
|
}
|
||||||
return chat.title || getTranslation('HiddenName');
|
return chat.title || getTranslation('HiddenName');
|
||||||
@ -432,3 +433,36 @@ export function getMessageSenderName(chatId: number, sender?: ApiUser) {
|
|||||||
|
|
||||||
return getUserFirstOrLastName(sender);
|
return getUserFirstOrLastName(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sortChatIds(
|
||||||
|
chatIds: number[],
|
||||||
|
chatsById: Record<number, ApiChat>,
|
||||||
|
shouldPrioritizeVerified = false,
|
||||||
|
priorityIds?: number[],
|
||||||
|
) {
|
||||||
|
return orderBy(chatIds, (id) => {
|
||||||
|
const chat = chatsById[id];
|
||||||
|
if (!chat) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let priority = 0;
|
||||||
|
|
||||||
|
if (chat.lastMessage) {
|
||||||
|
priority += chat.lastMessage.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldPrioritizeVerified && chat.isVerified) {
|
||||||
|
priority += 3e9; // ~100 years in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorityIds && priorityIds.includes(id)) {
|
||||||
|
// Assuming that last message date can't be less than now,
|
||||||
|
// this should place prioritized on top of the list.
|
||||||
|
// Then we subtract index of `id` in `priorityIds` to preserve selected order
|
||||||
|
priority += Date.now() + (priorityIds.length - priorityIds.indexOf(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return priority;
|
||||||
|
}, 'desc');
|
||||||
|
}
|
||||||
|
|||||||
@ -180,7 +180,7 @@ export function isUserBot(user: ApiUser) {
|
|||||||
return user.type === 'userTypeBot';
|
return user.type === 'userTypeBot';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSortedUserIds(
|
export function sortUserIds(
|
||||||
userIds: number[],
|
userIds: number[],
|
||||||
usersById: Record<number, ApiUser>,
|
usersById: Record<number, ApiUser>,
|
||||||
priorityIds?: number[],
|
priorityIds?: number[],
|
||||||
@ -189,13 +189,10 @@ export function getSortedUserIds(
|
|||||||
const now = Date.now() / 1000;
|
const now = Date.now() / 1000;
|
||||||
|
|
||||||
if (priorityIds && priorityIds.includes(id)) {
|
if (priorityIds && priorityIds.includes(id)) {
|
||||||
/*
|
// Assuming that online status expiration date can't be as far as two days from now,
|
||||||
** Assuming that online status expiration date can't be as far as two days from now,
|
// this should place prioritized on top of the list.
|
||||||
** this should place priorityIds on top of the list.
|
// Then we subtract index of `id` in `priorityIds` to preserve selected order
|
||||||
**
|
return now + (48 * 60 * 60) - (priorityIds.length - priorityIds.indexOf(id));
|
||||||
** We then subtract index of `id` in `priorityIds` to preserve selected order
|
|
||||||
*/
|
|
||||||
return now + (48 * 60 * 60) - priorityIds.indexOf(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = usersById[id];
|
const user = usersById[id];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user