From 123e68c167d8d3246588513966fbd28e4f20971a Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Fri, 15 Oct 2021 10:19:45 +1100 Subject: [PATCH 01/34] WIP: Adding message requests using existing convo list item. --- .../session/LeftPaneMessageSection.tsx | 51 +++++++-- .../session/SessionClosableOverlay.tsx | 106 +++++++++++++++++- ts/models/conversation.ts | 19 ++++ ts/state/ducks/conversations.ts | 1 + ts/test/test-utils/utils/message.ts | 1 + 5 files changed, 164 insertions(+), 14 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 0aabe6e5d..23a3112eb 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -51,7 +51,7 @@ export type SessionGroupType = SessionComposeToType; interface State { loading: boolean; - overlay: false | SessionComposeToType; + overlay: false | SessionClosableOverlayType; valuePasted: string; } @@ -144,7 +144,7 @@ export class LeftPaneMessageSection extends React.Component { return (
{this.renderHeader()} - {overlay ? this.renderClosableOverlay(overlay) : this.renderConversations()} + {overlay ? this.renderClosableOverlay() : this.renderConversations()}
); } @@ -157,12 +157,18 @@ export class LeftPaneMessageSection extends React.Component { onChange={this.updateSearch} placeholder={window.i18n('searchFor...')} /> +
message requests
{this.renderList()} {this.renderBottomButtons()} ); } + private handleMessageRequestsClick() { + console.warn('handle msg req clicked'); + this.handleToggleOverlay(SessionClosableOverlayType.MessageRequests); + } + public updateSearch(searchTerm: string) { if (!searchTerm) { window.inboxStore?.dispatch(clearSearch()); @@ -201,9 +207,9 @@ export class LeftPaneMessageSection extends React.Component { ); } - private renderClosableOverlay(overlay: SessionComposeToType) { + private renderClosableOverlay() { const { searchTerm, searchResults } = this.props; - const { loading } = this.state; + const { loading, overlay } = this.state; const openGroupElement = ( { /> ); + const messageRequestsElement = ( + { + this.handleToggleOverlay(undefined); + }} + onButtonClick={this.handleMessageButtonClick} + searchTerm={searchTerm} + searchResults={searchResults} + showSpinner={loading} + updateSearch={this.updateSearch} + /> + ); + let overlayElement; switch (overlay) { - case SessionComposeToType.OpenGroup: + case SessionClosableOverlayType.OpenGroup: overlayElement = openGroupElement; break; - case SessionComposeToType.ClosedGroup: + case SessionClosableOverlayType.ClosedGroup: overlayElement = closedGroupElement; break; - default: + case SessionClosableOverlayType.Message: overlayElement = messageElement; + break; + case SessionClosableOverlayType.MessageRequests: + overlayElement = messageRequestsElement; + break; + default: + overlayElement = false; } return overlayElement; @@ -277,7 +304,7 @@ export class LeftPaneMessageSection extends React.Component { buttonType={SessionButtonType.SquareOutline} buttonColor={SessionButtonColor.Green} onClick={() => { - this.handleToggleOverlay(SessionComposeToType.OpenGroup); + this.handleToggleOverlay(SessionClosableOverlayType.OpenGroup); }} /> { buttonType={SessionButtonType.SquareOutline} buttonColor={SessionButtonColor.White} onClick={() => { - this.handleToggleOverlay(SessionComposeToType.ClosedGroup); + this.handleToggleOverlay(SessionClosableOverlayType.ClosedGroup); }} /> ); } - private handleToggleOverlay(conversationType?: SessionComposeToType) { - const overlayState = conversationType || false; + private handleToggleOverlay(overlayType?: SessionClosableOverlayType) { + const overlayState = overlayType || false; this.setState({ overlay: overlayState }); @@ -403,6 +430,6 @@ export class LeftPaneMessageSection extends React.Component { } private handleNewSessionButtonClick() { - this.handleToggleOverlay(SessionComposeToType.Message); + this.handleToggleOverlay(SessionClosableOverlayType.Message); } } diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index cae8021dd..c643762be 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -9,11 +9,18 @@ import { SessionSpinner } from './SessionSpinner'; import { ConversationTypeEnum } from '../../models/conversation'; import { SessionJoinableRooms } from './SessionJoinableDefaultRooms'; import { SpacerLG, SpacerMD } from '../basic/Text'; +import { useSelector } from 'react-redux'; +import { getLeftPaneLists } from '../../state/selectors/conversations'; +import { + ConversationListItemProps, + MemoConversationListItemWithDetails, +} from '../ConversationListItem'; export enum SessionClosableOverlayType { Message = 'message', OpenGroup = 'open-group', ClosedGroup = 'closed-group', + MessageRequests = 'message-requests', } interface Props { @@ -106,6 +113,7 @@ export class SessionClosableOverlay extends React.Component { const isMessageView = overlayMode === SessionClosableOverlayType.Message; const isOpenGroupView = overlayMode === SessionClosableOverlayType.OpenGroup; const isClosedGroupView = overlayMode === SessionClosableOverlayType.ClosedGroup; + const isMessageRequestView = overlayMode === SessionClosableOverlayType.MessageRequests; let title; let buttonText; @@ -133,6 +141,12 @@ export class SessionClosableOverlay extends React.Component { subtitle = window.i18n('createClosedGroupNamePrompt'); placeholder = window.i18n('createClosedGroupPlaceholder'); break; + case SessionClosableOverlayType.MessageRequests: + title = 'Message Requests'; + buttonText = 'requests done'; + subtitle = 'Pending Requests'; + placeholder = 'placeholder'; + break; default: } @@ -172,14 +186,25 @@ export class SessionClosableOverlay extends React.Component { onPressEnter={() => onButtonClick(groupName, selectedMembers)} /> - ) : ( + ) : null} + + {isMessageView ? ( - )} + ) : null} + + {isMessageRequestView ? ( + <> + +
+ + + + ) : null} @@ -266,3 +291,80 @@ export class SessionClosableOverlay extends React.Component { } } } + +const MessageRequestList = () => { + // get all conversations with (accepted / known) + // const convos = useSelector(getConversationLookup); + + const lists = useSelector(getLeftPaneLists); + const conversationx = lists?.conversations as Array; + console.warn({ conversationx }); + + // console.warn({ convos }); + // const allConversations = getConversationController().getConversations(); + // const messageRequests = allConversations.filter(convo => convo.get('isApproved') !== true); + + return ( + <> + {/* {messageRequests.map(convoOfMessage => { */} + {conversationx.map(convoOfMessage => { + return ; + })} + + ); +}; + +// const MessageRequestListItem = (props: { conversation: ConversationModel }) => { +const MessageRequestListItem = (props: { conversation: ConversationListItemProps }) => { + const { conversation } = props; + // const { id: conversationId } = conversation; + + // TODO: add hovering + // TODO: add styling + + /** + * open the conversation selected + */ + // const openConvo = useCallback( + // async (e: React.MouseEvent) => { + // // mousedown is invoked sooner than onClick, but for both right and left click + // if (e.button === 0) { + // await openConversationWithMessages({ conversationKey: conversationId }); + // } + // }, + // [conversationId] + // ); + + // /** + // * show basic highlight animation + // */ + // const handleMouseOver = () => { + // console.warn('hovered'); + // }; + + return ( + //
{ + // e.stopPropagation(); + // e.preventDefault(); + // }} + // // className="message-request__item" + + // // className={classNames( + // // 'module-conversation-list-item', + // // unreadCount && unreadCount > 0 ? 'module-conversation-list-item--has-unread' : null, + // // unreadCount && unreadCount > 0 && mentionedUs + // // ? 'module-conversation-list-item--mentioned-us' + // // : null, + // // isSelected ? 'module-conversation-list-item--is-selected' : null, + // // isBlocked ? 'module-conversation-list-item--is-blocked' : null + // // )} + // > + // {conversation.getName()} + //
+ + + ); +}; diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index dfc68c9df..3704170a2 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -104,6 +104,7 @@ export interface ConversationAttributes { triggerNotificationsFor: ConversationNotificationSettingType; isTrustedForAttachmentDownload: boolean; isPinned: boolean; + isApproved: boolean; } export interface ConversationAttributesOptionals { @@ -144,6 +145,7 @@ export interface ConversationAttributesOptionals { triggerNotificationsFor?: ConversationNotificationSettingType; isTrustedForAttachmentDownload?: boolean; isPinned: boolean; + isApproved: boolean; } /** @@ -433,6 +435,7 @@ export class ConversationModel extends Backbone.Model { const isBlocked = this.isBlocked(); const subscriberCount = this.get('subscriberCount'); const isPinned = this.isPinned(); + const isApproved = this.isApproved(); const hasNickname = !!this.getNickname(); const isKickedFromGroup = !!this.get('isKickedFromGroup'); const left = !!this.get('left'); @@ -508,6 +511,9 @@ export class ConversationModel extends Backbone.Model { if (isPinned) { toRet.isPinned = isPinned; } + if (isApproved) { + toRet.isApproved = isApproved; + } if (subscriberCount) { toRet.subscriberCount = subscriberCount; } @@ -1375,6 +1381,15 @@ export class ConversationModel extends Backbone.Model { } } + public async setIsApproved(value: boolean) { + if (value !== this.get('isApproved')) { + this.set({ + isApproved: true, + }); + await this.commit(); + } + } + public async setGroupName(name: string) { const profileName = this.get('name'); if (profileName !== name) { @@ -1482,6 +1497,10 @@ export class ConversationModel extends Backbone.Model { return this.get('isPinned'); } + public isApproved() { + return this.get('isApproved'); + } + public getTitle() { if (this.isPrivate()) { const profileName = this.getProfileName(); diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 789d5974e..0de58628c 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -243,6 +243,7 @@ export interface ReduxConversationType { currentNotificationSetting?: ConversationNotificationSettingType; isPinned?: boolean; + isApproved?: boolean; } export interface NotificationForConvoOption { diff --git a/ts/test/test-utils/utils/message.ts b/ts/test/test-utils/utils/message.ts index b45ac39ef..de5d4c7a6 100644 --- a/ts/test/test-utils/utils/message.ts +++ b/ts/test/test-utils/utils/message.ts @@ -95,6 +95,7 @@ export class MockConversation { triggerNotificationsFor: 'all', isTrustedForAttachmentDownload: false, isPinned: false, + isApproved: false, }; } From c3f20aceb2dba8ca8e7eacec943c57902db10ccf Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Wed, 20 Oct 2021 17:49:14 +1100 Subject: [PATCH 02/34] WIP message requesting. Banner styling finished. --- stylesheets/_session_left_pane.scss | 8 ++ ts/components/ConversationListItem.tsx | 41 +++++++- .../session/LeftPaneMessageSection.tsx | 14 ++- .../session/MessageRequestsBanner.tsx | 98 +++++++++++++++++++ .../session/SessionClosableOverlay.tsx | 75 +++----------- .../session/settings/SessionSettings.tsx | 27 ++++- ts/models/conversation.ts | 9 +- .../conversations/ConversationController.ts | 1 + ts/state/ducks/userConfig.tsx | 15 ++- 9 files changed, 217 insertions(+), 71 deletions(-) create mode 100644 ts/components/session/MessageRequestsBanner.tsx diff --git a/stylesheets/_session_left_pane.scss b/stylesheets/_session_left_pane.scss index ade13d4d4..982d7a4d7 100644 --- a/stylesheets/_session_left_pane.scss +++ b/stylesheets/_session_left_pane.scss @@ -249,6 +249,14 @@ $session-compose-margin: 20px; margin-bottom: 3rem; flex-shrink: 0; } + + .message-request-list__container { + width: 100%; + + .session-button { + margin: $session-margin-xs 0; + } + } } } .module-search-results { diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index cdb1921f3..99d1a1d07 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -27,6 +27,10 @@ import { useSelector } from 'react-redux'; import { SectionType } from '../state/ducks/section'; import { getFocusedSection } from '../state/selectors/section'; import { ConversationNotificationSettingType } from '../models/conversation'; +import { Flex } from './basic/Flex'; +import { SessionButton } from './session/SessionButton'; +import { getConversationById } from '../data/data'; +import { getConversationController } from '../session/conversations'; // tslint:disable-next-line: no-empty-interface export interface ConversationListItemProps extends ReduxConversationType {} @@ -42,6 +46,7 @@ export const StyledConversationListItemIconWrapper = styled.div` type PropsHousekeeping = { style?: Object; + isMessageRequest?: boolean; }; // tslint:disable: use-simple-attributes @@ -261,6 +266,7 @@ const ConversationListItem = (props: Props) => { avatarPath, isPrivate, currentNotificationSetting, + isMessageRequest, } = props; const triggerId = `conversation-item-${conversationId}-ctxmenu`; const key = `conversation-item-${conversationId}`; @@ -277,15 +283,32 @@ const ConversationListItem = (props: Props) => { [conversationId] ); + /** + * deletes the conversation + */ + const handleConversationDecline = async () => { + await getConversationController().deleteContact(conversationId); + }; + + /** + * marks the conversation as approved. + */ + const handleConversationAccept = async () => { + const convo = await getConversationById(conversationId); + convo?.setIsApproved(true); + console.warn('convo marked as approved'); + console.warn({ convo }); + }; + return (
{ - e.stopPropagation(); - e.preventDefault(); - }} + // onMouseUp={e => { + // e.stopPropagation(); + // e.preventDefault(); + // }} onContextMenu={(e: any) => { contextMenu.show({ id: triggerId, @@ -327,6 +350,16 @@ const ConversationListItem = (props: Props) => { unreadCount={unreadCount || 0} lastMessage={lastMessage} /> + {isMessageRequest ? ( + + Decline + Accept + + ) : null}
diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 23a3112eb..fc6871017 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -28,6 +28,7 @@ import { onsNameRegex } from '../../session/snode_api/SNodeAPI'; import { SNodeAPI } from '../../session/snode_api'; import { clearSearch, search, updateSearchTerm } from '../../state/ducks/search'; import _ from 'lodash'; +import { MessageRequestsBanner } from './MessageRequestsBanner'; export interface Props { searchTerm: string; @@ -95,6 +96,15 @@ export class LeftPaneMessageSection extends React.Component { throw new Error('render: must provided conversations if no search results are provided'); } + // TODO: make selectors for this instead. + // TODO: only filter conversations if setting for requests is applied + const approvedConversations = conversations.filter(c => c.isApproved === true); + console.warn({ approvedConversations }); + const messageRequestsEnabled = + window.inboxStore?.getState().userConfig.messageRequests === true; + + const conversationsForList = messageRequestsEnabled ? approvedConversations : conversations; + const length = conversations.length; const listKey = 0; // Note: conversations is not a known prop for List, but it is required to ensure that @@ -106,7 +116,7 @@ export class LeftPaneMessageSection extends React.Component { {({ height, width }) => ( { onChange={this.updateSearch} placeholder={window.i18n('searchFor...')} /> -
message requests
+ {this.renderList()} {this.renderBottomButtons()} diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx new file mode 100644 index 000000000..8e5fcb2ce --- /dev/null +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import styled from 'styled-components'; +import { getLeftPaneLists } from '../../state/selectors/conversations'; +import { SessionIcon, SessionIconSize, SessionIconType } from './icon'; + +const StyledMessageRequestBanner = styled.div` + border-left: var(--border-unread); + height: 64px; + width: 100%; + max-width: 300px; + display: flex; + flex-direction: row; + padding: 8px 16px; + align-items: center; + cursor: pointer; + + transition: var(--session-transition-duration); + + &:hover { + background: var(--color-clickable-hovered); + } +`; + +const StyledMessageRequestBannerHeader = styled.span` + font-weight: bold; + font-size: 15px; + color: var(--color-text-subtle); + padding-left: var(--margin-xs); + margin-inline-start: 12px; + margin-top: var(--margin-sm); + line-height: 18px; + overflow-x: hidden; + overflow-y: hidden; + white-space: nowrap; + text-overflow: ellipsis; +`; + +const StyledCircleIcon = styled.div` + padding-left: var(--margin-xs); +`; + +const StyledUnreadCounter = styled.div` + font-weight: bold; + border-radius: 50%; + background-color: var(--color-clickable-hovered); + margin-left: 10px; + width: 20px; + height: 20px; + line-height: 25px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +`; + +const StyledGridContainer = styled.div` + border: solid 1px black; + display: flex; + width: 36px; + height: 36px; + align-items: center; + border-radius: 50%; + justify-content: center; + background-color: var(--color-conversation-item-has-unread); +`; + +export const CirclularIcon = (props: { iconType: SessionIconType; iconSize: SessionIconSize }) => { + const { iconSize, iconType } = props; + + return ( + + + + + + ); +}; + +export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { + const { handleOnClick } = props; + const convos = useSelector(getLeftPaneLists).conversations; + const pendingRequests = convos.filter(c => c.isApproved !== true) || []; + + return ( + + + Message Requests + +
{pendingRequests.length}
+
+
+ ); +}; diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index c643762be..a396d62ca 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -292,79 +292,30 @@ export class SessionClosableOverlay extends React.Component { } } + + const MessageRequestList = () => { // get all conversations with (accepted / known) - // const convos = useSelector(getConversationLookup); - const lists = useSelector(getLeftPaneLists); - const conversationx = lists?.conversations as Array; - console.warn({ conversationx }); - - // console.warn({ convos }); - // const allConversations = getConversationController().getConversations(); - // const messageRequests = allConversations.filter(convo => convo.get('isApproved') !== true); - + const unapprovedConversations = lists?.conversations.filter(c => { + return !c.isApproved; + }) as Array; return ( - <> - {/* {messageRequests.map(convoOfMessage => { */} - {conversationx.map(convoOfMessage => { - return ; +
+ {unapprovedConversations.map(conversation => { + return ; })} - +
); }; // const MessageRequestListItem = (props: { conversation: ConversationModel }) => { const MessageRequestListItem = (props: { conversation: ConversationListItemProps }) => { const { conversation } = props; - // const { id: conversationId } = conversation; - - // TODO: add hovering - // TODO: add styling - - /** - * open the conversation selected - */ - // const openConvo = useCallback( - // async (e: React.MouseEvent) => { - // // mousedown is invoked sooner than onClick, but for both right and left click - // if (e.button === 0) { - // await openConversationWithMessages({ conversationKey: conversationId }); - // } - // }, - // [conversationId] - // ); - - // /** - // * show basic highlight animation - // */ - // const handleMouseOver = () => { - // console.warn('hovered'); - // }; - return ( - //
{ - // e.stopPropagation(); - // e.preventDefault(); - // }} - // // className="message-request__item" - - // // className={classNames( - // // 'module-conversation-list-item', - // // unreadCount && unreadCount > 0 ? 'module-conversation-list-item--has-unread' : null, - // // unreadCount && unreadCount > 0 && mentionedUs - // // ? 'module-conversation-list-item--mentioned-us' - // // : null, - // // isSelected ? 'module-conversation-list-item--is-selected' : null, - // // isBlocked ? 'module-conversation-list-item--is-blocked' : null - // // )} - // > - // {conversation.getName()} - //
- - + ); }; diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index 05db39c86..5c6228ee0 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -17,7 +17,11 @@ import { import { shell } from 'electron'; import { mapDispatchToProps } from '../../../state/actions'; import { unblockConvoById } from '../../../interactions/conversationInteractions'; -import { toggleAudioAutoplay } from '../../../state/ducks/userConfig'; +import { + disableMessageRequests, + enableMessageRequests, + toggleAudioAutoplay, +} from '../../../state/ducks/userConfig'; import { sessionPassword, updateConfirmModal } from '../../../state/ducks/modalDialog'; import { PasswordAction } from '../../dialog/SessionPasswordDialog'; import { SessionIconButton } from '../icon'; @@ -406,7 +410,28 @@ class SettingsViewInner extends React.Component { comparisonValue: undefined, onClick: undefined, }, + { + id: 'message-request-setting', + title: 'Message Requests', // TODO: translation + description: 'Enable Message Request Inbox', + hidden: false, + type: SessionSettingType.Toggle, + category: SessionSettingCategory.Appearance, + setFn: () => { + window.inboxStore?.dispatch(toggleAudioAutoplay()); + if (window.inboxStore?.getState().userConfig.messageRequests) { + window.inboxStore?.dispatch(disableMessageRequests()); + } else { + window.inboxStore?.dispatch(enableMessageRequests()); + } + }, + content: { + defaultValue: window.inboxStore?.getState().userConfig.audioAutoplay, + }, + comparisonValue: undefined, + onClick: undefined, + }, { id: 'notification-setting', title: window.i18n('notificationSettingsDialog'), diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 3704170a2..8919c9afe 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -714,6 +714,12 @@ export class ConversationModel extends Backbone.Model { const sentAt = message.get('sent_at'); + // TODO: for debuggong + if (message.get('body')?.includes('unapprove')) { + console.warn('setting to unapprove'); + await this.setIsApproved(false); + } + if (!sentAt) { throw new Error('sendMessageJob() sent_at must be set.'); } @@ -771,6 +777,7 @@ export class ConversationModel extends Backbone.Model { const chatMessagePrivate = new VisibleMessage(chatMessageParams); await getMessageQueue().sendToPubKey(destinationPubkey, chatMessagePrivate); + // this.setIsApproved(true); // consider the conversation approved even if the message fails to send return; } @@ -1384,7 +1391,7 @@ export class ConversationModel extends Backbone.Model { public async setIsApproved(value: boolean) { if (value !== this.get('isApproved')) { this.set({ - isApproved: true, + isApproved: value, }); await this.commit(); } diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 08f3e39a0..3e0988f87 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -99,6 +99,7 @@ export class ConversationController { const create = async () => { try { + debugger; await saveConversation(conversation.attributes); } catch (error) { window?.log?.error( diff --git a/ts/state/ducks/userConfig.tsx b/ts/state/ducks/userConfig.tsx index af64b2cb4..ea34b9cfd 100644 --- a/ts/state/ducks/userConfig.tsx +++ b/ts/state/ducks/userConfig.tsx @@ -7,11 +7,13 @@ import { createSlice } from '@reduxjs/toolkit'; export interface UserConfigState { audioAutoplay: boolean; showRecoveryPhrasePrompt: boolean; + messageRequests: boolean; } export const initialUserConfigState = { audioAutoplay: false, showRecoveryPhrasePrompt: true, + messageRequests: true, }; const userConfigSlice = createSlice({ @@ -24,9 +26,20 @@ const userConfigSlice = createSlice({ disableRecoveryPhrasePrompt: state => { state.showRecoveryPhrasePrompt = false; }, + disableMessageRequests: state => { + state.messageRequests = false; + }, + enableMessageRequests: state => { + state.messageRequests = true; + }, }, }); const { actions, reducer } = userConfigSlice; -export const { toggleAudioAutoplay, disableRecoveryPhrasePrompt } = actions; +export const { + toggleAudioAutoplay, + disableRecoveryPhrasePrompt, + disableMessageRequests, + enableMessageRequests, +} = actions; export const userConfigReducer = reducer; From dcfa286d771b21b1a03e7ff1b4d3bbdaae103add Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Fri, 22 Oct 2021 12:12:59 +1100 Subject: [PATCH 03/34] WIP: fixing missing spaces for list items that have been removed. --- ts/components/ConversationListItem.tsx | 30 ++++++++++---- ts/components/LeftPane.tsx | 2 + .../session/LeftPaneMessageSection.tsx | 40 +++++++++++++++++-- .../session/MessageRequestsBanner.tsx | 8 +++- .../session/SessionClosableOverlay.tsx | 3 -- ts/models/conversation.ts | 12 +++++- ts/state/selectors/conversations.ts | 16 ++++++++ 7 files changed, 93 insertions(+), 18 deletions(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 99d1a1d07..47457e4da 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -28,7 +28,7 @@ import { SectionType } from '../state/ducks/section'; import { getFocusedSection } from '../state/selectors/section'; import { ConversationNotificationSettingType } from '../models/conversation'; import { Flex } from './basic/Flex'; -import { SessionButton } from './session/SessionButton'; +import { SessionButton, SessionButtonColor } from './session/SessionButton'; import { getConversationById } from '../data/data'; import { getConversationController } from '../session/conversations'; @@ -294,10 +294,10 @@ const ConversationListItem = (props: Props) => { * marks the conversation as approved. */ const handleConversationAccept = async () => { - const convo = await getConversationById(conversationId); - convo?.setIsApproved(true); + const conversationToApprove = await getConversationById(conversationId); + conversationToApprove?.setIsApproved(true); console.warn('convo marked as approved'); - console.warn({ convo }); + console.warn({ convo: conversationToApprove }); }; return ( @@ -351,14 +351,24 @@ const ConversationListItem = (props: Props) => { lastMessage={lastMessage} /> {isMessageRequest ? ( - - Decline - Accept - + + Decline + + + Accept + + ) : null} @@ -381,4 +391,8 @@ const ConversationListItem = (props: Props) => { ); }; +const StyledButtonContainer = styled(Flex)` + justify-content: space-evenly; +`; + export const MemoConversationListItemWithDetails = React.memo(ConversationListItem, _.isEqual); diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 3d9bf780d..56bfe79bc 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -31,6 +31,8 @@ const InnerLeftPaneMessageSection = () => { const lists = showSearch ? undefined : useSelector(getLeftPaneLists); // tslint:disable: use-simple-attributes + // const + return ( { @@ -62,25 +64,42 @@ export class LeftPaneMessageSection extends React.Component { public constructor(props: Props) { super(props); + const approvedConversations = props.conversations?.filter(convo => Boolean(convo.isApproved)); + const unapprovedConversations = props.conversations?.filter( + convo => !Boolean(convo.isApproved) + ); + this.state = { loading: false, overlay: false, valuePasted: '', + approvedConversations: approvedConversations || [], + unapprovedConversations: unapprovedConversations || [], }; autoBind(this); this.debouncedSearch = _.debounce(this.search.bind(this), 20); } - public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element => { + public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element | null => { const { conversations } = this.props; + // const { approvedConversations: conversations } = this.state; + console.warn({ conversations }); if (!conversations) { throw new Error('renderRow: Tried to render without conversations'); } - const conversation = conversations[index]; + console.warn(`${index}`); + console.warn({ conversation }); + + // TODO: need to confirm whats best here. + // true by default then false on newly received or + // false by default and true when approved but then how to handle pre-existing convos? + if (conversation.isApproved === undefined || conversation.isApproved === false) { + return null; + } return ; }; @@ -99,13 +118,21 @@ export class LeftPaneMessageSection extends React.Component { // TODO: make selectors for this instead. // TODO: only filter conversations if setting for requests is applied const approvedConversations = conversations.filter(c => c.isApproved === true); - console.warn({ approvedConversations }); const messageRequestsEnabled = window.inboxStore?.getState().userConfig.messageRequests === true; const conversationsForList = messageRequestsEnabled ? approvedConversations : conversations; + if (!this.state.approvedConversations.length) { + this.setState({ + approvedConversations: conversationsForList, + }); + } + + console.warn({ conversationsForList }); + + // const length = conversations.length; + const length = conversationsForList.length; - const length = conversations.length; const listKey = 0; // Note: conversations is not a known prop for List, but it is required to ensure that // it re-renders when our conversation data changes. Otherwise it would just render @@ -120,6 +147,7 @@ export class LeftPaneMessageSection extends React.Component { height={height} rowCount={length} rowHeight={64} + // rowHeight={this.getRowHeight} rowRenderer={this.renderRow} width={width} autoHeight={false} @@ -132,6 +160,10 @@ export class LeftPaneMessageSection extends React.Component { return [list]; } + // private getRowHeight({index: any}: any) { + // if (this.) + // } + public closeOverlay({ pubKey }: { pubKey: string }) { if (this.state.valuePasted === pubKey) { this.setState({ overlay: false, valuePasted: '' }); diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index 8e5fcb2ce..e0e87bcfa 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -84,14 +84,18 @@ export const CirclularIcon = (props: { iconType: SessionIconType; iconSize: Sess export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { const { handleOnClick } = props; const convos = useSelector(getLeftPaneLists).conversations; - const pendingRequests = convos.filter(c => c.isApproved !== true) || []; + const pendingRequestsCount = (convos.filter(c => c.isApproved !== true) || []).length; + + if (!pendingRequestsCount) { + return null; + } return ( Message Requests -
{pendingRequests.length}
+
{pendingRequestsCount}
); diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index a396d62ca..371b0a00a 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -131,7 +131,6 @@ export class SessionClosableOverlay extends React.Component { case 'open-group': title = window.i18n('joinOpenGroup'); buttonText = window.i18n('next'); - // descriptionLong = ''; subtitle = window.i18n('openGroupURL'); placeholder = window.i18n('enterAnOpenGroupURL'); break; @@ -292,8 +291,6 @@ export class SessionClosableOverlay extends React.Component { } } - - const MessageRequestList = () => { // get all conversations with (accepted / known) const lists = useSelector(getLeftPaneLists); diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 8919c9afe..78e785bde 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -714,7 +714,7 @@ export class ConversationModel extends Backbone.Model { const sentAt = message.get('sent_at'); - // TODO: for debuggong + // TODO: for debugging if (message.get('body')?.includes('unapprove')) { console.warn('setting to unapprove'); await this.setIsApproved(false); @@ -1389,11 +1389,21 @@ export class ConversationModel extends Backbone.Model { } public async setIsApproved(value: boolean) { + console.warn(`Setting ${this.attributes.nickname} isApproved to:: ${value}`); if (value !== this.get('isApproved')) { this.set({ isApproved: value, }); await this.commit(); + + if (window?.inboxStore) { + window.inboxStore?.dispatch( + conversationChanged({ + id: this.id, + data: this.getConversationModelProps(), + }) + ); + } } } diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index aecbdf0ec..7a5e4a580 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -343,6 +343,22 @@ export const getLeftPaneLists = createSelector( _getLeftPaneLists ); +export const getApprovedConversations = createSelector( + getConversationLookup, + (lookup: ConversationLookupType): Array => { + return Object.values(lookup).filter(convo => convo.isApproved === true); + } +); + +export const getUnapprovedConversations = createSelector( + getConversationLookup, + (lookup: ConversationLookupType): Array => { + return Object.values(lookup).filter( + convo => convo.isApproved === false || convo.isApproved === undefined + ); + } +); + export const getMe = createSelector( [getConversationLookup, getOurNumber], (lookup: ConversationLookupType, ourNumber: string): ReduxConversationType => { From e405b5ffd974fd7fa69067b3dfe99294e6f4d5da Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Mon, 25 Oct 2021 17:33:37 +1100 Subject: [PATCH 04/34] git stash --- ts/components/ConversationListItem.tsx | 1 + ts/components/session/LeftPaneMessageSection.tsx | 8 +++++++- ts/components/session/MessageRequestsBanner.tsx | 2 +- ts/components/session/SessionClosableOverlay.tsx | 2 +- ts/components/session/icon/Icons.tsx | 7 +++++++ ts/session/utils/syncUtils.ts | 4 +++- 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 47457e4da..e071f6408 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -298,6 +298,7 @@ const ConversationListItem = (props: Props) => { conversationToApprove?.setIsApproved(true); console.warn('convo marked as approved'); console.warn({ convo: conversationToApprove }); + conversationToApprove.sendS }; return ( diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index e4d1863bf..50fc1b4e8 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -306,7 +306,13 @@ export class LeftPaneMessageSection extends React.Component { onCloseClick={() => { this.handleToggleOverlay(undefined); }} - onButtonClick={this.handleMessageButtonClick} + onButtonClick={async () => { + // decline all convos + // close modal + // this.state.approvedConversations.map(async(convo) => { + console.warn('Test'); + // } ) + }} searchTerm={searchTerm} searchResults={searchResults} showSpinner={loading} diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index e0e87bcfa..c375ea926 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -92,7 +92,7 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { return ( - + Message Requests
{pendingRequestsCount}
diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 371b0a00a..66b059808 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -142,7 +142,7 @@ export class SessionClosableOverlay extends React.Component { break; case SessionClosableOverlayType.MessageRequests: title = 'Message Requests'; - buttonText = 'requests done'; + buttonText = 'Decline All'; subtitle = 'Pending Requests'; placeholder = 'placeholder'; break; diff --git a/ts/components/session/icon/Icons.tsx b/ts/components/session/icon/Icons.tsx index de70a859c..45ab607fe 100644 --- a/ts/components/session/icon/Icons.tsx +++ b/ts/components/session/icon/Icons.tsx @@ -25,6 +25,7 @@ export type SessionIconType = | 'info' | 'link' | 'lock' + | 'messageRequest' | 'microphone' | 'moon' | 'mute' @@ -223,6 +224,12 @@ export const icons = { viewBox: '0 0 512 512', ratio: 1, }, + messageRequest: { + path: + 'M68.987 7.718H27.143c-2.73 0-5.25.473-7.508 1.417-2.257.945-4.357 2.363-6.248 4.253-1.89 1.89-3.308 3.99-4.253 6.248-.945 2.257-1.417 4.778-1.417 7.508V67.99c0 2.73.472 5.25 1.417 7.508.945 2.258 2.363 4.357 4.253 6.248 1.942 1.891 4.043 3.359 6.3 4.252 2.258.945 4.726 1.418 7.456 1.418h17.956c2.101 0 3.833 1.732 3.833 3.832 0 .473-.105.893-.21 1.313-.683 2.521-1.418 5.041-2.258 7.455-.893 2.574-1.837 4.988-2.888 7.352-.525 1.207-1.155 2.361-1.837 3.57 3.675-1.629 7.14-3.518 10.343-5.619 3.36-2.205 6.51-4.672 9.397-7.35 2.94-2.73 5.565-5.723 7.98-8.926.735-.996 1.89-1.521 3.045-1.521H87.94c2.73 0 5.198-.473 7.455-1.418 2.258-.945 4.358-2.363 6.301-4.252 1.89-1.891 3.308-3.99 4.253-6.248.944-2.258 1.417-4.779 1.417-7.508V27.249c0-2.73-.473-5.25-1.417-7.508-.945-2.258-2.363-4.357-4.253-6.248s-3.99-3.308-6.248-4.252c-2.258-.945-4.777-1.418-7.508-1.418H68.987v-.105zm-7.282 47.97h-9.976V54.61c0-1.833.188-3.327.574-4.471.386-1.155.958-2.193 1.721-3.143.762-.951 2.474-2.619 5.136-5.005 1.416-1.251 2.124-2.396 2.124-3.435 0-1.047-.287-1.852-.851-2.434-.574-.573-1.435-.864-2.59-.864-1.247 0-2.269.446-3.083 1.338-.816.883-1.335 2.444-1.561 4.657l-10.191-1.368c.349-4.054 1.711-7.314 4.078-9.787 2.376-2.473 6.015-3.706 10.917-3.706 3.818 0 6.893.863 9.24 2.58 3.184 2.338 4.778 5.441 4.778 9.321 0 1.61-.412 3.172-1.237 4.666-.815 1.493-2.501 3.327-5.037 5.48-1.766 1.523-2.887 2.735-3.353 3.657-.456.914-.689 2.116-.689 3.592zm-10.325 2.87h10.693v8.532H51.38v-8.532zM46.097.053H87.94c3.675 0 7.141.683 10.396 1.995 3.202 1.312 6.143 3.308 8.768 5.933 2.626 2.625 4.621 5.565 5.934 8.768 1.312 3.203 1.994 6.667 1.994 10.396V67.99c0 3.729-.683 7.193-1.994 10.396-1.313 3.201-3.308 6.141-5.934 8.768-2.625 2.625-5.565 4.566-8.768 5.932-3.202 1.313-6.668 1.996-10.396 1.996H74.395c-2.362 2.992-4.935 5.826-7.665 8.4-3.255 3.045-6.72 5.773-10.448 8.189-3.728 2.467-7.718 4.621-11.971 6.457-4.2 1.838-8.715 3.361-13.44 4.621-1.365.367-2.835-.053-3.833-1.156-1.417-1.574-1.26-3.988.315-5.406 2.205-1.943 4.095-3.938 5.618-5.934 1.47-1.941 2.678-3.938 3.57-5.984v-.053c.998-2.205 1.89-4.463 2.678-6.721.263-.787.525-1.627.788-2.467H27.091c-3.675 0-7.14-.684-10.396-1.996-3.203-1.313-6.143-3.307-8.768-5.932-2.625-2.625-4.62-5.566-5.933-8.768C.682 75.078 0 71.613 0 67.938V27.091c0-3.676.682-7.141 1.995-10.396 1.313-3.203 3.308-6.143 5.933-8.768 2.625-2.625 5.565-4.62 8.768-5.933S23.363 0 27.091 0h18.953l.053.053z', + viewBox: '0 0 115.031 122.88', + ratio: 1, + }, microphone: { path: 'M43.362728,18.444286 C46.0752408,18.444286 48.2861946,16.2442453 48.2861946,13.5451212 L48.2861946,6.8991648 C48.2861946,4.20004074 46.0752408,2 43.362728,2 C40.6502153,2 38.4392615,4.20004074 38.4392615,6.8991648 L38.4392615,13.5451212 C38.4392615,16.249338 40.6502153,18.444286 43.362728,18.444286 Z M51.0908304,12.9238134 C51.4388509,12.9238134 51.7203381,13.2039112 51.7203381,13.5502139 C51.7203381,17.9248319 48.3066664,21.5202689 43.9871178,21.8411082 L43.9871178,21.8411082 L43.9871178,25.747199 L47.2574869,25.747199 C47.6055074,25.747199 47.8869946,26.0272968 47.8869946,26.3735995 C47.8869946,26.7199022 47.6055074,27 47.2574869,27 L47.2574869,27 L39.4628512,27 C39.1148307,27 38.8333435,26.7199022 38.8333435,26.3735995 C38.8333435,26.0272968 39.1148307,25.747199 39.4628512,25.747199 L39.4628512,25.747199 L42.7332204,25.747199 L42.7332204,21.8411082 C38.4136717,21.5253616 35,17.9248319 35,13.5502139 C35,13.2039112 35.2814872,12.9238134 35.6295077,12.9238134 C35.9775282,12.9238134 36.2538974,13.2039112 36.2436615,13.5502139 C36.2436615,17.4512121 39.4321435,20.623956 43.3524921,20.623956 C47.2728408,20.623956 50.4613228,17.4512121 50.4613228,13.5502139 C50.4613228,13.2039112 50.7428099,12.9238134 51.0908304,12.9238134 Z M43.362728,3.24770829 C45.3843177,3.24770829 47.0322972,4.88755347 47.0322972,6.8991648 L47.0322972,13.5451212 C47.0322972,15.5567325 45.3843177,17.1965777 43.362728,17.1965777 C41.3411383,17.1965777 39.6931589,15.5567325 39.6931589,13.5451212 L39.6931589,6.8991648 C39.6931589,4.88755347 41.3411383,3.24770829 43.362728,3.24770829', diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index c4cac9648..11eeaaad6 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -200,7 +200,9 @@ const getValidContacts = (convos: Array) => { return _.compact(contacts); }; -export const getCurrentConfigurationMessage = async (convos: Array) => { +export const getCurrentConfigurationMessage = async ( + convos: Array +): Promise => { const ourPubKey = UserUtils.getOurPubKeyStrFromCache(); const ourConvo = convos.find(convo => convo.id === ourPubKey); From b6c1578262ea2a59ba63c47dbde492fc09348d49 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Tue, 26 Oct 2021 09:21:37 +1100 Subject: [PATCH 05/34] WIP message request adding todo note. --- ts/components/ConversationListItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index e071f6408..b8de0c397 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -298,7 +298,7 @@ const ConversationListItem = (props: Props) => { conversationToApprove?.setIsApproved(true); console.warn('convo marked as approved'); console.warn({ convo: conversationToApprove }); - conversationToApprove.sendS + // TODO: Send sync message to other devices. Using config message }; return ( From 84e12ff42fe9969bc5dda14de91f2ae505556439 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Tue, 26 Oct 2021 10:57:51 +1100 Subject: [PATCH 06/34] Alter request button item positioning. --- stylesheets/_session_left_pane.scss | 2 +- ts/components/ConversationListItem.tsx | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/stylesheets/_session_left_pane.scss b/stylesheets/_session_left_pane.scss index 982d7a4d7..f33fba1df 100644 --- a/stylesheets/_session_left_pane.scss +++ b/stylesheets/_session_left_pane.scss @@ -254,7 +254,7 @@ $session-compose-margin: 20px; width: 100%; .session-button { - margin: $session-margin-xs 0; + margin: $session-margin-xs $session-margin-xs $session-margin-xs 0; } } } diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index b8de0c397..61a2fb67b 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -352,10 +352,11 @@ const ConversationListItem = (props: Props) => { lastMessage={lastMessage} /> {isMessageRequest ? ( - { > Accept - + ) : null} @@ -392,8 +393,4 @@ const ConversationListItem = (props: Props) => { ); }; -const StyledButtonContainer = styled(Flex)` - justify-content: space-evenly; -`; - export const MemoConversationListItemWithDetails = React.memo(ConversationListItem, _.isEqual); From 116cb25b275e6afb2a2fc2effc2649139aea3960 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Tue, 26 Oct 2021 11:10:54 +1100 Subject: [PATCH 07/34] fix icon position --- ts/components/session/MessageRequestsBanner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index c375ea926..8f319fb8d 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -11,7 +11,7 @@ const StyledMessageRequestBanner = styled.div` max-width: 300px; display: flex; flex-direction: row; - padding: 8px 16px; + padding: 8px 12px; // adjusting for unread border always being active align-items: center; cursor: pointer; From d57300688ec745a939d0bb41996dc5d33c2a23a6 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Tue, 26 Oct 2021 22:56:22 +1100 Subject: [PATCH 08/34] no longer showing empty space for conversations moved from list. --- ts/components/ConversationListItem.tsx | 2 ++ .../session/LeftPaneMessageSection.tsx | 33 +++++++++---------- .../session/SessionClosableOverlay.tsx | 1 + 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 61a2fb67b..f466f7170 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -299,6 +299,8 @@ const ConversationListItem = (props: Props) => { console.warn('convo marked as approved'); console.warn({ convo: conversationToApprove }); // TODO: Send sync message to other devices. Using config message + + }; return ( diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 50fc1b4e8..e1d57a2c1 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -69,6 +69,8 @@ export class LeftPaneMessageSection extends React.Component { convo => !Boolean(convo.isApproved) ); + console.warn('convos updated'); + this.state = { loading: false, overlay: false, @@ -83,21 +85,22 @@ export class LeftPaneMessageSection extends React.Component { public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element | null => { const { conversations } = this.props; - // const { approvedConversations: conversations } = this.state; - console.warn({ conversations }); - - if (!conversations) { + const approvedConversations = conversations?.filter(c => c.isApproved === true); + if (!conversations || !approvedConversations) { throw new Error('renderRow: Tried to render without conversations'); } - const conversation = conversations[index]; - - console.warn(`${index}`); - console.warn({ conversation }); + // const conversation = conversations[index]; + let conversation; + if (approvedConversations?.length) { + conversation = approvedConversations[index]; + } // TODO: need to confirm whats best here. - // true by default then false on newly received or - // false by default and true when approved but then how to handle pre-existing convos? - if (conversation.isApproved === undefined || conversation.isApproved === false) { + if ( + !conversation || + conversation?.isApproved === undefined || + conversation.isApproved === false + ) { return null; } return ; @@ -143,11 +146,11 @@ export class LeftPaneMessageSection extends React.Component { {({ height, width }) => ( { return [list]; } - // private getRowHeight({index: any}: any) { - // if (this.) - // } - public closeOverlay({ pubKey }: { pubKey: string }) { if (this.state.valuePasted === pubKey) { this.setState({ overlay: false, valuePasted: '' }); diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 66b059808..9536fead5 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -297,6 +297,7 @@ const MessageRequestList = () => { const unapprovedConversations = lists?.conversations.filter(c => { return !c.isApproved; }) as Array; + console.warn({ unapprovedConversationsListConstructor: unapprovedConversations }); return (
{unapprovedConversations.map(conversation => { From 9e0f128fc67860a7516bfd80a73d215fad2520fd Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Wed, 27 Oct 2021 10:36:22 +1100 Subject: [PATCH 09/34] Adding isApproved field to protobuf. --- protos/SignalService.proto | 1 + .../messages/outgoing/controlMessage/ConfigurationMessage.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/protos/SignalService.proto b/protos/SignalService.proto index 439b5b91f..0d3658963 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -170,6 +170,7 @@ message ConfigurationMessage { required string name = 2; optional string profilePicture = 3; optional bytes profileKey = 4; + optional bool isApproved = 5; } repeated ClosedGroup closedGroups = 1; diff --git a/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts b/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts index 82c6845db..89f8b710d 100644 --- a/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts +++ b/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts @@ -93,22 +93,26 @@ export class ConfigurationMessageContact { public displayName: string; public profilePictureURL?: string; public profileKey?: Uint8Array; + public isApproved?: boolean; public constructor({ publicKey, displayName, profilePictureURL, profileKey, + isApproved, }: { publicKey: string; displayName: string; profilePictureURL?: string; profileKey?: Uint8Array; + isApproved?: boolean; }) { this.publicKey = publicKey; this.displayName = displayName; this.profilePictureURL = profilePictureURL; this.profileKey = profileKey; + this.isApproved = isApproved; // will throw if public key is invalid PubKey.cast(publicKey); @@ -131,6 +135,7 @@ export class ConfigurationMessageContact { name: this.displayName, profilePicture: this.profilePictureURL, profileKey: this.profileKey, + isApproved: this.isApproved, }); } } From 4ad14e4c5b76ebe5ad54c7b5d4f75effcec1fc05 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Fri, 29 Oct 2021 10:58:40 +1100 Subject: [PATCH 10/34] Added syncing accepting of contact between running instances. --- app/sql.js | 7 ++++ ts/components/ConversationListItem.tsx | 15 ++++--- .../session/LeftPaneMessageSection.tsx | 40 ++++++++----------- ts/data/data.ts | 4 +- ts/models/conversation.ts | 13 +----- ts/receiver/configMessage.ts | 21 +++++----- ts/receiver/contentMessage.ts | 1 + .../conversations/ConversationController.ts | 1 - ts/session/utils/syncUtils.ts | 16 ++++++-- 9 files changed, 62 insertions(+), 56 deletions(-) diff --git a/app/sql.js b/app/sql.js index 217f7ba56..159d760cf 100644 --- a/app/sql.js +++ b/app/sql.js @@ -1600,9 +1600,14 @@ function updateConversation(data) { type, members, name, + isApproved, profileName, } = data; + console.log({ usrData: data }); + console.log({ usrDataTrace: console.trace() }); + console.log('usrData@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'); + globalInstance .prepare( `UPDATE ${CONVERSATIONS_TABLE} SET @@ -1612,6 +1617,7 @@ function updateConversation(data) { type = $type, members = $members, name = $name, + isApproved = $isApproved, profileName = $profileName WHERE id = $id;` ) @@ -1623,6 +1629,7 @@ function updateConversation(data) { type, members: members ? members.join(' ') : null, name, + isApproved, profileName, }); } diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index f466f7170..6991e5ff1 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -30,7 +30,7 @@ import { ConversationNotificationSettingType } from '../models/conversation'; import { Flex } from './basic/Flex'; import { SessionButton, SessionButtonColor } from './session/SessionButton'; import { getConversationById } from '../data/data'; -import { getConversationController } from '../session/conversations'; +import { syncConfigurationIfNeeded } from '../session/utils/syncUtils'; // tslint:disable-next-line: no-empty-interface export interface ConversationListItemProps extends ReduxConversationType {} @@ -287,7 +287,10 @@ const ConversationListItem = (props: Props) => { * deletes the conversation */ const handleConversationDecline = async () => { - await getConversationController().deleteContact(conversationId); + // const convoToDecline = await getConversationById(conversationId); + // convoToDecline?.setIsApproved(false); + // await getConversationController().deleteContact(conversationId); // TODO: might be unnecessary + console.warn('decline'); }; /** @@ -295,12 +298,12 @@ const ConversationListItem = (props: Props) => { */ const handleConversationAccept = async () => { const conversationToApprove = await getConversationById(conversationId); - conversationToApprove?.setIsApproved(true); - console.warn('convo marked as approved'); - console.warn({ convo: conversationToApprove }); + await conversationToApprove?.setIsApproved(true); + console.warn({ convoAfterSetIsApproved: conversationToApprove }); // TODO: Send sync message to other devices. Using config message - + + await syncConfigurationIfNeeded(true); }; return ( diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index e1d57a2c1..19578b2ce 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -85,22 +85,26 @@ export class LeftPaneMessageSection extends React.Component { public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element | null => { const { conversations } = this.props; - const approvedConversations = conversations?.filter(c => c.isApproved === true); + const approvedConversations = conversations?.filter(c => Boolean(c.isApproved) === true); if (!conversations || !approvedConversations) { throw new Error('renderRow: Tried to render without conversations'); } - // const conversation = conversations[index]; + + // TODO: make this only filtered when the setting is enabled + const messageRequestsEnabled = + window.inboxStore?.getState().userConfig.messageRequests === true; + let conversation; if (approvedConversations?.length) { conversation = approvedConversations[index]; } - // TODO: need to confirm whats best here. - if ( - !conversation || - conversation?.isApproved === undefined || - conversation.isApproved === false - ) { + if (!conversation) { + return null; + } + + // TODO: need to confirm what default setting is best here. + if (messageRequestsEnabled && !Boolean(conversation.isApproved)) { return null; } return ; @@ -120,21 +124,9 @@ export class LeftPaneMessageSection extends React.Component { // TODO: make selectors for this instead. // TODO: only filter conversations if setting for requests is applied - const approvedConversations = conversations.filter(c => c.isApproved === true); - const messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true; - const conversationsForList = messageRequestsEnabled ? approvedConversations : conversations; - if (!this.state.approvedConversations.length) { - this.setState({ - approvedConversations: conversationsForList, - }); - } - - console.warn({ conversationsForList }); - - // const length = conversations.length; - const length = conversationsForList.length; + // TODO: readjust to be approved length as only approved convos will show here. + const length = this.props.conversations ? this.props.conversations.length : 0; const listKey = 0; // Note: conversations is not a known prop for List, but it is required to ensure that @@ -146,8 +138,8 @@ export class LeftPaneMessageSection extends React.Component { {({ height, width }) => ( { - await channels.updateConversation(data); + const cleanedData = _cleanData(data); + await channels.updateConversation(cleanedData); } export async function removeConversation(id: string): Promise { @@ -601,7 +602,6 @@ export async function cleanLastHashes(): Promise { await channels.cleanLastHashes(); } -// TODO: Strictly type the following export async function saveSeenMessageHashes( data: Array<{ expiresAt: number; diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 78e785bde..d6ae0acc6 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -176,6 +176,7 @@ export const fillConvoAttributesWithDefaults = ( triggerNotificationsFor: 'all', // if the settings is not set in the db, this is the default isTrustedForAttachmentDownload: false, // we don't trust a contact until we say so isPinned: false, + isApproved: false, }); }; @@ -777,7 +778,6 @@ export class ConversationModel extends Backbone.Model { const chatMessagePrivate = new VisibleMessage(chatMessageParams); await getMessageQueue().sendToPubKey(destinationPubkey, chatMessagePrivate); - // this.setIsApproved(true); // consider the conversation approved even if the message fails to send return; } @@ -1389,21 +1389,12 @@ export class ConversationModel extends Backbone.Model { } public async setIsApproved(value: boolean) { - console.warn(`Setting ${this.attributes.nickname} isApproved to:: ${value}`); if (value !== this.get('isApproved')) { + console.warn(`Setting ${this.attributes.profileName} isApproved to:: ${value}`); this.set({ isApproved: value, }); await this.commit(); - - if (window?.inboxStore) { - window.inboxStore?.dispatch( - conversationChanged({ - id: this.id, - data: this.getConversationModelProps(), - }) - ); - } } } diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 63590efcc..cca1743b1 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { createOrUpdateItem, getItemById, hasSyncedInitialConfigurationItem } from '../data/data'; +import { createOrUpdateItem } from '../data/data'; import { ConversationTypeEnum } from '../models/conversation'; import { joinOpenGroupV2WithUIEvents, @@ -53,14 +53,16 @@ async function handleGroupsAndContactsFromConfigMessage( envelope: EnvelopePlus, configMessage: SignalService.ConfigurationMessage ) { - const didWeHandleAConfigurationMessageAlready = - (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; - if (didWeHandleAConfigurationMessageAlready) { - window?.log?.info( - 'Dropping configuration contacts/groups change as we already handled one... ' - ); - return; - } + // const didWeHandleAConfigurationMessageAlready = + // (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; + + // TODO: debug + // if (didWeHandleAConfigurationMessageAlready) { + // window?.log?.info( + // 'Dropping configuration contacts/groups change as we already handled one... ' + // ); + // return; + // } await createOrUpdateItem({ id: 'hasSyncedInitialConfigurationItem', value: true, @@ -125,6 +127,7 @@ async function handleGroupsAndContactsFromConfigMessage( }; // updateProfile will do a commit for us contactConvo.set('active_at', _.toNumber(envelope.timestamp)); + contactConvo.setIsApproved(Boolean(c.isApproved)); await updateProfileOneAtATime(contactConvo, profile, c.profileKey); } catch (e) { diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index f5422bc33..aec1ebba4 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -377,6 +377,7 @@ export async function innerHandleContentMessage( } if (content.configurationMessage) { // this one can be quite long (downloads profilePictures and everything, is do not block) + console.warn('@@config message received. contentmessage.ts'); void handleConfigurationMessage( envelope, content.configurationMessage as SignalService.ConfigurationMessage diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 3e0988f87..08f3e39a0 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -99,7 +99,6 @@ export class ConversationController { const create = async () => { try { - debugger; await saveConversation(conversation.attributes); } catch (error) { window?.log?.error( diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index 11eeaaad6..07f191477 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -1,5 +1,6 @@ import { createOrUpdateItem, + getAllConversations, getItemById, getLatestClosedGroupEncryptionKeyPair, } from '../../../ts/data/data'; @@ -36,16 +37,24 @@ const getLastSyncTimestampFromDb = async (): Promise => const writeLastSyncTimestampToDb = async (timestamp: number) => createOrUpdateItem({ id: ITEM_ID_LAST_SYNC_TIMESTAMP, value: timestamp }); -export const syncConfigurationIfNeeded = async () => { +/** + * Syncs usre configuration with other devices linked to this user. + * @param force Bypass duration time limit for sending sync messages + * @returns + */ +export const syncConfigurationIfNeeded = async (force: boolean = false) => { const lastSyncedTimestamp = (await getLastSyncTimestampFromDb()) || 0; const now = Date.now(); // if the last sync was less than 2 days before, return early. - if (Math.abs(now - lastSyncedTimestamp) < DURATION.DAYS * 7) { + if (!force && Math.abs(now - lastSyncedTimestamp) < DURATION.DAYS * 7) { return; } - const allConvos = getConversationController().getConversations(); + const allConvos = await (await getAllConversations()).models; + + console.warn({ test: allConvos[0].attributes.isApproved }); + // const configMessage = await getCurrentConfigurationMessage(allConvos); const configMessage = await getCurrentConfigurationMessage(allConvos); try { // window?.log?.info('syncConfigurationIfNeeded with', configMessage); @@ -191,6 +200,7 @@ const getValidContacts = (convos: Array) => { displayName: c.getLokiProfile()?.displayName, profilePictureURL: c.get('avatarPointer'), profileKey: !profileKeyForContact?.length ? undefined : profileKeyForContact, + isApproved: c.isApproved(), }); } catch (e) { window?.log.warn('getValidContacts', e); From c3924f85a9acf52718b2efed3a9ecdb90a1cd33c Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Thu, 4 Nov 2021 14:47:47 +1100 Subject: [PATCH 11/34] Adding blocking of individual requests and syncing of block to devices. Added approval by replying to a message. --- _locales/en/messages.json | 6 +- app/sql.js | 22 ++++++- protos/SignalService.proto | 3 +- ts/components/ConversationListItem.tsx | 21 ++++--- .../session/LeftPaneMessageSection.tsx | 19 +++--- .../session/MessageRequestsBanner.tsx | 10 ++-- .../session/SessionClosableOverlay.tsx | 22 +++---- ts/models/conversation.ts | 16 +++-- ts/receiver/configMessage.ts | 58 +++++++++++-------- .../conversations/ConversationController.ts | 6 ++ .../controlMessage/ConfigurationMessage.ts | 5 ++ ts/session/utils/syncUtils.ts | 3 +- ts/state/selectors/conversations.ts | 11 +++- 13 files changed, 137 insertions(+), 65 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 8b649412b..ae849d3d5 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -443,5 +443,9 @@ "messageDeletedPlaceholder": "This message has been deleted", "messageDeleted": "Message deleted", "surveyTitle": "Take our Session Survey", - "goToOurSurvey": "Go to our survey" + "goToOurSurvey": "Go to our survey", + "blockAll": "Block All", + "messageRequests": "Message Requests", + "requestsSubtitle": "Pending Requests", + "requestsPlaceHolder": "No requests" } diff --git a/app/sql.js b/app/sql.js index 159d760cf..819cc6a4c 100644 --- a/app/sql.js +++ b/app/sql.js @@ -835,6 +835,7 @@ const LOKI_SCHEMA_VERSIONS = [ updateToLokiSchemaVersion14, updateToLokiSchemaVersion15, updateToLokiSchemaVersion16, + updateToLokiSchemaVersion17, ]; function updateToLokiSchemaVersion1(currentVersion, db) { @@ -1228,6 +1229,23 @@ function updateToLokiSchemaVersion16(currentVersion, db) { console.log(`updateToLokiSchemaVersion${targetVersion}: success!`); } +function updateToLokiSchemaVersion17(currentVersion, db) { + const targetVersion = 17; + if (currentVersion >= targetVersion) { + return; + } + console.log(`updateToLokiSchemaVersion${targetVersion}: starting...`); + + db.transaction(() => { + db.exec(` + ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN isApproved BOOLEAN; + `); + + writeLokiSchemaVersion(targetVersion, db); + })(); + console.log(`updateToLokiSchemaVersion${targetVersion}: success!`); +} + function writeLokiSchemaVersion(newVersion, db) { db.prepare( `INSERT INTO loki_schema( @@ -1604,8 +1622,8 @@ function updateConversation(data) { profileName, } = data; + // TODO: msgreq - remove console.log({ usrData: data }); - console.log({ usrDataTrace: console.trace() }); console.log('usrData@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'); globalInstance @@ -1619,7 +1637,7 @@ function updateConversation(data) { name = $name, isApproved = $isApproved, profileName = $profileName - WHERE id = $id;` + WHERE id = $id;` ) .run({ id, diff --git a/protos/SignalService.proto b/protos/SignalService.proto index 0d3658963..989c2fd32 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -170,7 +170,8 @@ message ConfigurationMessage { required string name = 2; optional string profilePicture = 3; optional bytes profileKey = 4; - optional bool isApproved = 5; + optional bool isApproved = 5; + optional bool isBlocked = 6; } repeated ClosedGroup closedGroups = 1; diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 6991e5ff1..6af908935 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -31,6 +31,7 @@ import { Flex } from './basic/Flex'; import { SessionButton, SessionButtonColor } from './session/SessionButton'; import { getConversationById } from '../data/data'; import { syncConfigurationIfNeeded } from '../session/utils/syncUtils'; +import { BlockedNumberController } from '../util'; // tslint:disable-next-line: no-empty-interface export interface ConversationListItemProps extends ReduxConversationType {} @@ -284,13 +285,16 @@ const ConversationListItem = (props: Props) => { ); /** - * deletes the conversation + * Removes conversation from requests list, + * adds ID to block list, syncs the block with linked devices. */ - const handleConversationDecline = async () => { - // const convoToDecline = await getConversationById(conversationId); - // convoToDecline?.setIsApproved(false); - // await getConversationController().deleteContact(conversationId); // TODO: might be unnecessary - console.warn('decline'); + const handleConversationBlock = async () => { + const convoToBlock = await getConversationById(conversationId); + if (!convoToBlock) { + window?.log?.error('Unable to find conversation to be blocked.'); + } + await BlockedNumberController.block(convoToBlock?.id); + await syncConfigurationIfNeeded(true); }; /** @@ -302,7 +306,6 @@ const ConversationListItem = (props: Props) => { console.warn({ convoAfterSetIsApproved: conversationToApprove }); // TODO: Send sync message to other devices. Using config message - await syncConfigurationIfNeeded(true); }; @@ -364,10 +367,10 @@ const ConversationListItem = (props: Props) => { justifyContent="flex-end" > - Decline + Block { public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element | null => { const { conversations } = this.props; - const approvedConversations = conversations?.filter(c => Boolean(c.isApproved) === true); - if (!conversations || !approvedConversations) { + const conversationsToShow = conversations?.filter(async c => { + return ( + Boolean(c.isApproved) === true && + (await BlockedNumberController.isBlockedAsync(c.id)) === false + ); + }); + if (!conversations || !conversationsToShow) { throw new Error('renderRow: Tried to render without conversations'); } @@ -95,8 +101,8 @@ export class LeftPaneMessageSection extends React.Component { window.inboxStore?.getState().userConfig.messageRequests === true; let conversation; - if (approvedConversations?.length) { - conversation = approvedConversations[index]; + if (conversationsToShow?.length) { + conversation = conversationsToShow[index]; } if (!conversation) { @@ -298,11 +304,8 @@ export class LeftPaneMessageSection extends React.Component { this.handleToggleOverlay(undefined); }} onButtonClick={async () => { - // decline all convos - // close modal - // this.state.approvedConversations.map(async(convo) => { + // TODO: msgrequest iterate all convos and block console.warn('Test'); - // } ) }} searchTerm={searchTerm} searchResults={searchResults} diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index 8f319fb8d..8766e9d09 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -83,10 +83,12 @@ export const CirclularIcon = (props: { iconType: SessionIconType; iconSize: Sess export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { const { handleOnClick } = props; - const convos = useSelector(getLeftPaneLists).conversations; - const pendingRequestsCount = (convos.filter(c => c.isApproved !== true) || []).length; + const convos = useSelector(getLeftPaneLists).conversationRequests; + // const pendingRequestsCount = ( + // convos.filter(c => Boolean(c.isApproved) === false && !Boolean(c.isBlocked)) || [] + // ).length; - if (!pendingRequestsCount) { + if (!convos.length) { return null; } @@ -95,7 +97,7 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { Message Requests -
{pendingRequestsCount}
+
{convos.length || 0}
); diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 9536fead5..54414e585 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -141,10 +141,10 @@ export class SessionClosableOverlay extends React.Component { placeholder = window.i18n('createClosedGroupPlaceholder'); break; case SessionClosableOverlayType.MessageRequests: - title = 'Message Requests'; - buttonText = 'Decline All'; - subtitle = 'Pending Requests'; - placeholder = 'placeholder'; + title = window.i18n('messageRequests'); + buttonText = window.i18n('blockAll'); + subtitle = window.i18n('requestsSubtitle'); + placeholder = window.i18n('requestsPlaceholder'); break; default: } @@ -291,16 +291,18 @@ export class SessionClosableOverlay extends React.Component { } } +/** + * A request needs to be be unapproved and not blocked to be valid. + * @returns List of message request items + */ const MessageRequestList = () => { - // get all conversations with (accepted / known) const lists = useSelector(getLeftPaneLists); - const unapprovedConversations = lists?.conversations.filter(c => { - return !c.isApproved; - }) as Array; - console.warn({ unapprovedConversationsListConstructor: unapprovedConversations }); + // const validConversationRequests = lists?.conversations.filter(c => { + const validConversationRequests = lists?.conversationRequests; + console.warn({ unapprovedConversationsListConstructor: validConversationRequests }); return (
- {unapprovedConversations.map(conversation => { + {validConversationRequests.map(conversation => { return ; })}
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index d6ae0acc6..6cce5e326 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -50,6 +50,7 @@ import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsMana import { IMAGE_JPEG } from '../types/MIME'; import { UnsendMessage } from '../session/messages/outgoing/controlMessage/UnsendMessage'; import { getLatestTimestampOffset, networkDeleteMessages } from '../session/snode_api/SNodeAPI'; +import { syncConfigurationIfNeeded } from '../session/utils/syncUtils'; export enum ConversationTypeEnum { GROUP = 'group', @@ -715,9 +716,9 @@ export class ConversationModel extends Backbone.Model { const sentAt = message.get('sent_at'); - // TODO: for debugging - if (message.get('body')?.includes('unapprove')) { - console.warn('setting to unapprove'); + // TODO: msgreq for debugging + const unapprove = message.get('body')?.includes('unapprove'); + if (unapprove) { await this.setIsApproved(false); } @@ -740,6 +741,13 @@ export class ConversationModel extends Backbone.Model { lokiProfile: UserUtils.getOurProfile(), }; + const updateApprovalNeeded = + !this.isApproved() && (this.isPrivate() || this.isMediumGroup() || this.isClosedGroup()); + if (updateApprovalNeeded && !unapprove) { + this.setIsApproved(true); + await syncConfigurationIfNeeded(true); + } + if (this.isOpenGroupV2()) { const chatMessageOpenGroupV2 = new OpenGroupVisibleMessage(chatMessageParams); const roomInfos = this.toOpenGroupV2(); @@ -1506,7 +1514,7 @@ export class ConversationModel extends Backbone.Model { } public isApproved() { - return this.get('isApproved'); + return Boolean(this.get('isApproved')); } public getTitle() { diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index cca1743b1..e0b0fbb3a 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -11,6 +11,7 @@ import { getConversationController } from '../session/conversations'; import { UserUtils } from '../session/utils'; import { toHex } from '../session/utils/String'; import { configurationMessageReceived, trigger } from '../shims/events'; +import { BlockedNumberController } from '../util'; import { removeFromCache } from './cache'; import { handleNewClosedGroup } from './closedGroups'; import { updateProfileOneAtATime } from './dataMessage'; @@ -111,33 +112,42 @@ async function handleGroupsAndContactsFromConfigMessage( } } if (configMessage.contacts?.length) { - await Promise.all( - configMessage.contacts.map(async c => { - try { - if (!c.publicKey) { - return; - } - const contactConvo = await getConversationController().getOrCreateAndWait( - toHex(c.publicKey), - ConversationTypeEnum.PRIVATE - ); - const profile = { - displayName: c.name, - profilePictre: c.profilePicture, - }; - // updateProfile will do a commit for us - contactConvo.set('active_at', _.toNumber(envelope.timestamp)); - contactConvo.setIsApproved(Boolean(c.isApproved)); - - await updateProfileOneAtATime(contactConvo, profile, c.profileKey); - } catch (e) { - window?.log?.warn('failed to handle a new closed group from configuration message'); - } - }) - ); + await Promise.all(configMessage.contacts.map(async c => handleContactReceived(c, envelope))); } } +const handleContactReceived = async ( + contactReceived: SignalService.ConfigurationMessage.IContact, + envelope: EnvelopePlus +) => { + try { + if (!contactReceived.publicKey) { + return; + } + const contactConvo = await getConversationController().getOrCreateAndWait( + toHex(contactReceived.publicKey), + ConversationTypeEnum.PRIVATE + ); + const profile = { + displayName: contactReceived.name, + profilePictre: contactReceived.profilePicture, + }; + // updateProfile will do a commit for us + contactConvo.set('active_at', _.toNumber(envelope.timestamp)); + contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); + + if (contactReceived.isBlocked === true) { + await BlockedNumberController.block(contactConvo.id); + } else { + await BlockedNumberController.unblock(contactConvo.id); + } + + await updateProfileOneAtATime(contactConvo, profile, contactReceived.profileKey); + } catch (e) { + window?.log?.warn('failed to handle a new closed group from configuration message'); + } +}; + export async function handleConfigurationMessage( envelope: EnvelopePlus, configurationMessage: SignalService.ConfigurationMessage diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 08f3e39a0..ae840959b 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -262,6 +262,12 @@ export class ConversationController { return Array.from(this.conversations.models); } + public getConversationRequests(): Array { + return Array.from(this.conversations.models).filter( + conversation => conversation.isApproved() && !conversation.isBlocked + ); + } + public unsafeDelete(convo: ConversationModel) { this.conversations.remove(convo); } diff --git a/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts b/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts index 89f8b710d..d6e5ee656 100644 --- a/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts +++ b/ts/session/messages/outgoing/controlMessage/ConfigurationMessage.ts @@ -94,6 +94,7 @@ export class ConfigurationMessageContact { public profilePictureURL?: string; public profileKey?: Uint8Array; public isApproved?: boolean; + public isBlocked?: boolean; public constructor({ publicKey, @@ -101,18 +102,21 @@ export class ConfigurationMessageContact { profilePictureURL, profileKey, isApproved, + isBlocked, }: { publicKey: string; displayName: string; profilePictureURL?: string; profileKey?: Uint8Array; isApproved?: boolean; + isBlocked?: boolean; }) { this.publicKey = publicKey; this.displayName = displayName; this.profilePictureURL = profilePictureURL; this.profileKey = profileKey; this.isApproved = isApproved; + this.isBlocked = isBlocked; // will throw if public key is invalid PubKey.cast(publicKey); @@ -136,6 +140,7 @@ export class ConfigurationMessageContact { profilePicture: this.profilePictureURL, profileKey: this.profileKey, isApproved: this.isApproved, + isBlocked: this.isBlocked, }); } } diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index 07f191477..43db448c5 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -164,7 +164,7 @@ const getValidClosedGroups = async (convos: Array) => { const getValidContacts = (convos: Array) => { // Filter contacts const contactsModels = convos.filter( - c => !!c.get('active_at') && c.getLokiProfile()?.displayName && c.isPrivate() && !c.isBlocked() + c => !!c.get('active_at') && c.getLokiProfile()?.displayName && c.isPrivate() ); const contacts = contactsModels.map(c => { @@ -201,6 +201,7 @@ const getValidContacts = (convos: Array) => { profilePictureURL: c.get('avatarPointer'), profileKey: !profileKeyForContact?.length ? undefined : profileKeyForContact, isApproved: c.isApproved(), + isBlocked: c.isBlocked(), }); } catch (e) { window?.log.warn('getValidContacts', e); diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 7a5e4a580..fd2ed734e 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -275,6 +275,7 @@ export const _getLeftPaneLists = ( ): { conversations: Array; contacts: Array; + conversationRequests: Array; unreadCount: number; } => { const values = Object.values(lookup); @@ -282,6 +283,7 @@ export const _getLeftPaneLists = ( const conversations: Array = []; const directConversations: Array = []; + const conversationRequests: Array = []; let unreadCount = 0; for (let conversation of sorted) { @@ -317,6 +319,10 @@ export const _getLeftPaneLists = ( directConversations.push(conversation); } + if (!conversation.isApproved && !conversation.isBlocked) { + conversationRequests.push(conversation); + } + if ( unreadCount < 9 && conversation.unreadCount && @@ -326,12 +332,15 @@ export const _getLeftPaneLists = ( unreadCount += conversation.unreadCount; } - conversations.push(conversation); + if (conversation.isApproved) { + conversations.push(conversation); + } } return { conversations, contacts: directConversations, + conversationRequests, unreadCount, }; }; From 87235641cb14f4f34a3da9dfb59d8dc4656f56d1 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Thu, 4 Nov 2021 16:07:27 +1100 Subject: [PATCH 12/34] fixed typos for translations and method name. --- _locales/en/messages.json | 2 +- ts/receiver/queuedJob.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index ae849d3d5..dba5ec8dc 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -447,5 +447,5 @@ "blockAll": "Block All", "messageRequests": "Message Requests", "requestsSubtitle": "Pending Requests", - "requestsPlaceHolder": "No requests" + "requestsPlaceholder": "No requests" } diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index a1847bd8e..c0a35b5d4 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -463,7 +463,7 @@ export async function handleMessageJob( conversationKey: conversation.id, messageModelProps: message.getMessageModelProps(), }); - trotthledAllMessagesAddedDispatch(); + throttledAllMessagesAddedDispatch(); if (message.get('unread')) { await conversation.throttledNotify(message); } @@ -479,7 +479,7 @@ export async function handleMessageJob( } } -const trotthledAllMessagesAddedDispatch = _.throttle(() => { +const throttledAllMessagesAddedDispatch = _.throttle(() => { if (updatesToDispatch.size === 0) { return; } From 6a62437c3e33c5eb7f90c7fb27b6c27745e1cd0e Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Fri, 12 Nov 2021 13:29:35 +1100 Subject: [PATCH 13/34] Blocking, accepting on click and accepting on msg send working across clients. --- app/sql.js | 4 ---- ts/components/ConversationListItem.tsx | 7 ++++--- ts/components/session/LeftPaneMessageSection.tsx | 16 +++++++++++++++- ts/models/conversation.ts | 5 +++++ ts/receiver/configMessage.ts | 1 + ts/receiver/contentMessage.ts | 1 - ts/receiver/dataMessage.ts | 9 +++++++-- ts/receiver/queuedJob.ts | 3 +++ ts/receiver/receiver.ts | 9 ++++----- ts/session/utils/syncUtils.ts | 6 ++++-- 10 files changed, 43 insertions(+), 18 deletions(-) diff --git a/app/sql.js b/app/sql.js index 819cc6a4c..76ae257a7 100644 --- a/app/sql.js +++ b/app/sql.js @@ -1622,10 +1622,6 @@ function updateConversation(data) { profileName, } = data; - // TODO: msgreq - remove - console.log({ usrData: data }); - console.log('usrData@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'); - globalInstance .prepare( `UPDATE ${CONVERSATIONS_TABLE} SET diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 6af908935..9decf0223 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -30,7 +30,7 @@ import { ConversationNotificationSettingType } from '../models/conversation'; import { Flex } from './basic/Flex'; import { SessionButton, SessionButtonColor } from './session/SessionButton'; import { getConversationById } from '../data/data'; -import { syncConfigurationIfNeeded } from '../session/utils/syncUtils'; +import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils'; import { BlockedNumberController } from '../util'; // tslint:disable-next-line: no-empty-interface @@ -294,7 +294,7 @@ const ConversationListItem = (props: Props) => { window?.log?.error('Unable to find conversation to be blocked.'); } await BlockedNumberController.block(convoToBlock?.id); - await syncConfigurationIfNeeded(true); + await forceSyncConfigurationNowIfNeeded(); }; /** @@ -306,7 +306,8 @@ const ConversationListItem = (props: Props) => { console.warn({ convoAfterSetIsApproved: conversationToApprove }); // TODO: Send sync message to other devices. Using config message - await syncConfigurationIfNeeded(true); + // await syncConfigurationIfNeeded(true); + await forceSyncConfigurationNowIfNeeded(); }; return ( diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 9fa401bdc..68a3b818d 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -30,6 +30,7 @@ import { clearSearch, search, updateSearchTerm } from '../../state/ducks/search' import _ from 'lodash'; import { MessageRequestsBanner } from './MessageRequestsBanner'; import { BlockedNumberController } from '../../util'; +import { forceSyncConfigurationNowIfNeeded } from '../../session/utils/syncUtils'; export interface Props { searchTerm: string; @@ -305,7 +306,20 @@ export class LeftPaneMessageSection extends React.Component { }} onButtonClick={async () => { // TODO: msgrequest iterate all convos and block - console.warn('Test'); + // iterate all conversations and set all to approve then + const allConversations = getConversationController().getConversations(); + let syncRequired = false; + + _.forEach(allConversations, convo => { + if (convo.isApproved() !== true) { + convo.setIsApproved(true); + syncRequired = true; + } + }); + if (syncRequired) { + // syncConfigurationIfNeeded(true); + await forceSyncConfigurationNowIfNeeded(); + } }} searchTerm={searchTerm} searchResults={searchResults} diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 6cce5e326..b747c2bd2 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -1161,6 +1161,11 @@ export class ConversationModel extends Backbone.Model { public async addSingleMessage(messageAttributes: MessageAttributesOptionals, setToExpire = true) { const model = new MessageModel(messageAttributes); + const isMe = messageAttributes.source === UserUtils.getOurPubKeyStrFromCache(); + if (isMe) { + await this.setIsApproved(true); + } + // no need to trigger a UI update now, we trigger a messageAdded just below const messageId = await model.commit(false); model.set({ id: messageId }); diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index e0b0fbb3a..a8d7f0ec6 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -152,6 +152,7 @@ export async function handleConfigurationMessage( envelope: EnvelopePlus, configurationMessage: SignalService.ConfigurationMessage ): Promise { + window?.log?.info('Handling configuration message'); const ourPubkey = UserUtils.getOurPubKeyStrFromCache(); if (!ourPubkey) { return; diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index aec1ebba4..f5422bc33 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -377,7 +377,6 @@ export async function innerHandleContentMessage( } if (content.configurationMessage) { // this one can be quite long (downloads profilePictures and everything, is do not block) - console.warn('@@config message received. contentmessage.ts'); void handleConfigurationMessage( envelope, content.configurationMessage as SignalService.ConfigurationMessage diff --git a/ts/receiver/dataMessage.ts b/ts/receiver/dataMessage.ts index aff80bf61..751ffe2db 100644 --- a/ts/receiver/dataMessage.ts +++ b/ts/receiver/dataMessage.ts @@ -32,11 +32,14 @@ export async function updateProfileOneAtATime( } const oneAtaTimeStr = `updateProfileOneAtATime:${conversation.id}`; return allowOnlyOneAtATime(oneAtaTimeStr, async () => { - return updateProfile(conversation, profile, profileKey); + return createOrUpdateProfile(conversation, profile, profileKey); }); } -async function updateProfile( +/** + * Creates a new profile from the profile provided. Creates the profile if it doesn't exist. + */ +async function createOrUpdateProfile( conversation: ConversationModel, profile: SignalService.DataMessage.ILokiProfile, profileKey?: Uint8Array | null // was any @@ -400,6 +403,7 @@ export async function isMessageDuplicate({ return false; } const filteredResult = [result].filter((m: any) => m.attributes.body === message.body); + console.warn({ filteredResult }); return filteredResult.some(m => isDuplicate(m, message, source)); } catch (error) { window?.log?.error('isMessageDuplicate error:', Errors.toLogFormat(error)); @@ -420,6 +424,7 @@ export const isDuplicate = ( Math.abs(m.attributes.sent_at - testedMessage.timestamp) <= PUBLICCHAT_MIN_TIME_BETWEEN_DUPLICATE_MESSAGES; + debugger; return sameUsername && sameText && timestampsSimilar; }; diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index c0a35b5d4..6c185b5d8 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -313,6 +313,9 @@ async function handleRegularMessage( if (type === 'outgoing') { handleSyncedReceipts(message, conversation); + + // TODO: Can we assume sync receipts are always from linked device outgoings? + if (dataMessage.body !== 'unapprove') conversation.setIsApproved(true); } const conversationActiveAt = conversation.get('active_at'); diff --git a/ts/receiver/receiver.ts b/ts/receiver/receiver.ts index 601a4f1fd..6a2a342fd 100644 --- a/ts/receiver/receiver.ts +++ b/ts/receiver/receiver.ts @@ -161,12 +161,11 @@ export function handleRequest(body: any, options: ReqOptions, messageHash: strin incomingMessagePromises.push(promise); } + // tslint:enable:cyclomatic-complexity max-func-body-length */ - -// *********************************************************************** -// *********************************************************************** -// *********************************************************************** - +/** + * Used in background.js + */ export async function queueAllCached() { const items = await getAllFromCache(); items.forEach(async item => { diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index 43db448c5..52ca3e15a 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -70,8 +70,9 @@ export const syncConfigurationIfNeeded = async (force: boolean = false) => { }; export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = false) => - new Promise(resolve => { - const allConvos = getConversationController().getConversations(); + new Promise(async resolve => { + // const allConvos = getConversationController().getConversations(); + const allConvos = (await getAllConversations()).models; // if we hang for more than 10sec, force resolve this promise. setTimeout(() => { @@ -88,6 +89,7 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal resolve(true); } : undefined; + console.warn({configMessage}); void getMessageQueue().sendSyncMessage(configMessage, callback as any); // either we resolve from the callback if we need to wait for it, // or we don't want to wait, we resolve it here. From 690abb9d52b1b6a42927de3743c1b809d5a1c8fa Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Fri, 12 Nov 2021 13:49:19 +1100 Subject: [PATCH 14/34] adding simple PR changes requested. --- ts/components/session/MessageRequestsBanner.tsx | 3 --- ts/state/ducks/userConfig.tsx | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index 8766e9d09..f281bface 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -84,9 +84,6 @@ export const CirclularIcon = (props: { iconType: SessionIconType; iconSize: Sess export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { const { handleOnClick } = props; const convos = useSelector(getLeftPaneLists).conversationRequests; - // const pendingRequestsCount = ( - // convos.filter(c => Boolean(c.isApproved) === false && !Boolean(c.isBlocked)) || [] - // ).length; if (!convos.length) { return null; diff --git a/ts/state/ducks/userConfig.tsx b/ts/state/ducks/userConfig.tsx index ea34b9cfd..8628dcd20 100644 --- a/ts/state/ducks/userConfig.tsx +++ b/ts/state/ducks/userConfig.tsx @@ -30,7 +30,7 @@ const userConfigSlice = createSlice({ state.messageRequests = false; }, enableMessageRequests: state => { - state.messageRequests = true; + state.messageRequests = false; }, }, }); From cb5551c1e9cdba8548ae5d0808d55c0dcd85cc48 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Sun, 14 Nov 2021 22:38:07 +1100 Subject: [PATCH 15/34] PR changes --- app/sql.js | 21 ------------------- .../session/LeftPaneMessageSection.tsx | 3 +-- ts/models/conversation.ts | 2 +- ts/receiver/dataMessage.ts | 1 - ts/session/utils/syncUtils.ts | 1 - ts/state/selectors/conversations.ts | 16 -------------- 6 files changed, 2 insertions(+), 42 deletions(-) diff --git a/app/sql.js b/app/sql.js index 76ae257a7..f81655201 100644 --- a/app/sql.js +++ b/app/sql.js @@ -835,7 +835,6 @@ const LOKI_SCHEMA_VERSIONS = [ updateToLokiSchemaVersion14, updateToLokiSchemaVersion15, updateToLokiSchemaVersion16, - updateToLokiSchemaVersion17, ]; function updateToLokiSchemaVersion1(currentVersion, db) { @@ -1229,23 +1228,6 @@ function updateToLokiSchemaVersion16(currentVersion, db) { console.log(`updateToLokiSchemaVersion${targetVersion}: success!`); } -function updateToLokiSchemaVersion17(currentVersion, db) { - const targetVersion = 17; - if (currentVersion >= targetVersion) { - return; - } - console.log(`updateToLokiSchemaVersion${targetVersion}: starting...`); - - db.transaction(() => { - db.exec(` - ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN isApproved BOOLEAN; - `); - - writeLokiSchemaVersion(targetVersion, db); - })(); - console.log(`updateToLokiSchemaVersion${targetVersion}: success!`); -} - function writeLokiSchemaVersion(newVersion, db) { db.prepare( `INSERT INTO loki_schema( @@ -1618,7 +1600,6 @@ function updateConversation(data) { type, members, name, - isApproved, profileName, } = data; @@ -1631,7 +1612,6 @@ function updateConversation(data) { type = $type, members = $members, name = $name, - isApproved = $isApproved, profileName = $profileName WHERE id = $id;` ) @@ -1643,7 +1623,6 @@ function updateConversation(data) { type, members: members ? members.join(' ') : null, name, - isApproved, profileName, }); } diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 68a3b818d..6ee88f3d2 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -312,12 +312,11 @@ export class LeftPaneMessageSection extends React.Component { _.forEach(allConversations, convo => { if (convo.isApproved() !== true) { - convo.setIsApproved(true); + BlockedNumberController.block(convo.id); syncRequired = true; } }); if (syncRequired) { - // syncConfigurationIfNeeded(true); await forceSyncConfigurationNowIfNeeded(); } }} diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index b747c2bd2..b7f107557 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -146,7 +146,7 @@ export interface ConversationAttributesOptionals { triggerNotificationsFor?: ConversationNotificationSettingType; isTrustedForAttachmentDownload?: boolean; isPinned: boolean; - isApproved: boolean; + isApproved?: boolean; } /** diff --git a/ts/receiver/dataMessage.ts b/ts/receiver/dataMessage.ts index 751ffe2db..47a8d3d68 100644 --- a/ts/receiver/dataMessage.ts +++ b/ts/receiver/dataMessage.ts @@ -424,7 +424,6 @@ export const isDuplicate = ( Math.abs(m.attributes.sent_at - testedMessage.timestamp) <= PUBLICCHAT_MIN_TIME_BETWEEN_DUPLICATE_MESSAGES; - debugger; return sameUsername && sameText && timestampsSimilar; }; diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index 52ca3e15a..9c6017bd5 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -89,7 +89,6 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal resolve(true); } : undefined; - console.warn({configMessage}); void getMessageQueue().sendSyncMessage(configMessage, callback as any); // either we resolve from the callback if we need to wait for it, // or we don't want to wait, we resolve it here. diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index fd2ed734e..15ec06bba 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -352,22 +352,6 @@ export const getLeftPaneLists = createSelector( _getLeftPaneLists ); -export const getApprovedConversations = createSelector( - getConversationLookup, - (lookup: ConversationLookupType): Array => { - return Object.values(lookup).filter(convo => convo.isApproved === true); - } -); - -export const getUnapprovedConversations = createSelector( - getConversationLookup, - (lookup: ConversationLookupType): Array => { - return Object.values(lookup).filter( - convo => convo.isApproved === false || convo.isApproved === undefined - ); - } -); - export const getMe = createSelector( [getConversationLookup, getOurNumber], (lookup: ConversationLookupType, ourNumber: string): ReduxConversationType => { From e5a203a48e837a741dde1e002d6cd82a8cf16b64 Mon Sep 17 00:00:00 2001 From: warrickct Date: Tue, 16 Nov 2021 13:09:44 +1100 Subject: [PATCH 16/34] adding setting of active_at to hide unapproved messages. --- ts/models/conversation.ts | 4 ++++ ts/receiver/queuedJob.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index b7f107557..173ad05f2 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -1407,6 +1407,10 @@ export class ConversationModel extends Backbone.Model { this.set({ isApproved: value, }); + + // to exclude the conversation from left pane messages list and message requests + if (value === false) this.set({ active_at: undefined }); + await this.commit(); } } diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 6c185b5d8..00ce7d73c 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -314,8 +314,8 @@ async function handleRegularMessage( if (type === 'outgoing') { handleSyncedReceipts(message, conversation); - // TODO: Can we assume sync receipts are always from linked device outgoings? - if (dataMessage.body !== 'unapprove') conversation.setIsApproved(true); + // assumes sync receipts are always from linked device outgoings? + conversation.setIsApproved(true); } const conversationActiveAt = conversation.get('active_at'); From 2eab74246bd8d5e0119aff3c17c588ba0c8b690f Mon Sep 17 00:00:00 2001 From: warrickct Date: Tue, 16 Nov 2021 13:22:12 +1100 Subject: [PATCH 17/34] PR changes. Disabling message requests behind feature flags. --- preload.js | 1 + ts/components/ConversationListItem.tsx | 8 ----- ts/components/LeftPane.tsx | 3 -- .../session/LeftPaneMessageSection.tsx | 36 ++++++------------- .../session/SessionClosableOverlay.tsx | 3 -- ts/models/conversation.ts | 8 +---- ts/receiver/configMessage.ts | 10 ------ ts/receiver/dataMessage.ts | 1 - ts/state/selectors/conversations.ts | 4 +++ ts/window.d.ts | 1 + 10 files changed, 17 insertions(+), 58 deletions(-) diff --git a/preload.js b/preload.js index 281b08ee2..9cf51698a 100644 --- a/preload.js +++ b/preload.js @@ -51,6 +51,7 @@ window.lokiFeatureFlags = { padOutgoingAttachments: true, enablePinConversations: true, useUnsendRequests: false, + useMessageRequests: false, }; window.isBeforeVersion = (toCheck, baseVersion) => { diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 9decf0223..c412b4db3 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -303,10 +303,6 @@ const ConversationListItem = (props: Props) => { const handleConversationAccept = async () => { const conversationToApprove = await getConversationById(conversationId); await conversationToApprove?.setIsApproved(true); - console.warn({ convoAfterSetIsApproved: conversationToApprove }); - // TODO: Send sync message to other devices. Using config message - - // await syncConfigurationIfNeeded(true); await forceSyncConfigurationNowIfNeeded(); }; @@ -315,10 +311,6 @@ const ConversationListItem = (props: Props) => {
{ - // e.stopPropagation(); - // e.preventDefault(); - // }} onContextMenu={(e: any) => { contextMenu.show({ id: triggerId, diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 56bfe79bc..271485155 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -30,9 +30,6 @@ const InnerLeftPaneMessageSection = () => { const lists = showSearch ? undefined : useSelector(getLeftPaneLists); // tslint:disable: use-simple-attributes - - // const - return ( { @@ -66,19 +64,12 @@ export class LeftPaneMessageSection extends React.Component { public constructor(props: Props) { super(props); - const approvedConversations = props.conversations?.filter(convo => Boolean(convo.isApproved)); - const unapprovedConversations = props.conversations?.filter( - convo => !Boolean(convo.isApproved) - ); - console.warn('convos updated'); this.state = { loading: false, overlay: false, valuePasted: '', - approvedConversations: approvedConversations || [], - unapprovedConversations: unapprovedConversations || [], }; autoBind(this); @@ -87,23 +78,19 @@ export class LeftPaneMessageSection extends React.Component { public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element | null => { const { conversations } = this.props; - const conversationsToShow = conversations?.filter(async c => { - return ( - Boolean(c.isApproved) === true && - (await BlockedNumberController.isBlockedAsync(c.id)) === false - ); - }); - if (!conversations || !conversationsToShow) { + + //assume conversations that have been marked unapproved should be filtered out by selector. + if (!conversations) { throw new Error('renderRow: Tried to render without conversations'); } - // TODO: make this only filtered when the setting is enabled const messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true; + window.inboxStore?.getState().userConfig.messageRequests === true && + window?.lokiFeatureFlags?.useMessageRequests; let conversation; - if (conversationsToShow?.length) { - conversation = conversationsToShow[index]; + if (conversations?.length) { + conversation = conversations[index]; } if (!conversation) { @@ -129,10 +116,6 @@ export class LeftPaneMessageSection extends React.Component { throw new Error('render: must provided conversations if no search results are provided'); } - // TODO: make selectors for this instead. - // TODO: only filter conversations if setting for requests is applied - - // TODO: readjust to be approved length as only approved convos will show here. const length = this.props.conversations ? this.props.conversations.length : 0; const listKey = 0; @@ -145,7 +128,6 @@ export class LeftPaneMessageSection extends React.Component { {({ height, width }) => ( { onChange={this.updateSearch} placeholder={window.i18n('searchFor...')} /> - + {window.lokiFeatureFlags.useMessageRequests ? ( + + ) : null} {this.renderList()} {this.renderBottomButtons()}
diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 54414e585..c5b532aa1 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -297,9 +297,7 @@ export class SessionClosableOverlay extends React.Component { */ const MessageRequestList = () => { const lists = useSelector(getLeftPaneLists); - // const validConversationRequests = lists?.conversations.filter(c => { const validConversationRequests = lists?.conversationRequests; - console.warn({ unapprovedConversationsListConstructor: validConversationRequests }); return (
{validConversationRequests.map(conversation => { @@ -309,7 +307,6 @@ const MessageRequestList = () => { ); }; -// const MessageRequestListItem = (props: { conversation: ConversationModel }) => { const MessageRequestListItem = (props: { conversation: ConversationListItemProps }) => { const { conversation } = props; return ( diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 173ad05f2..c05077662 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -716,12 +716,6 @@ export class ConversationModel extends Backbone.Model { const sentAt = message.get('sent_at'); - // TODO: msgreq for debugging - const unapprove = message.get('body')?.includes('unapprove'); - if (unapprove) { - await this.setIsApproved(false); - } - if (!sentAt) { throw new Error('sendMessageJob() sent_at must be set.'); } @@ -743,7 +737,7 @@ export class ConversationModel extends Backbone.Model { const updateApprovalNeeded = !this.isApproved() && (this.isPrivate() || this.isMediumGroup() || this.isClosedGroup()); - if (updateApprovalNeeded && !unapprove) { + if (updateApprovalNeeded) { this.setIsApproved(true); await syncConfigurationIfNeeded(true); } diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index a8d7f0ec6..4df55f14d 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -54,16 +54,6 @@ async function handleGroupsAndContactsFromConfigMessage( envelope: EnvelopePlus, configMessage: SignalService.ConfigurationMessage ) { - // const didWeHandleAConfigurationMessageAlready = - // (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; - - // TODO: debug - // if (didWeHandleAConfigurationMessageAlready) { - // window?.log?.info( - // 'Dropping configuration contacts/groups change as we already handled one... ' - // ); - // return; - // } await createOrUpdateItem({ id: 'hasSyncedInitialConfigurationItem', value: true, diff --git a/ts/receiver/dataMessage.ts b/ts/receiver/dataMessage.ts index 47a8d3d68..e6720c81b 100644 --- a/ts/receiver/dataMessage.ts +++ b/ts/receiver/dataMessage.ts @@ -403,7 +403,6 @@ export async function isMessageDuplicate({ return false; } const filteredResult = [result].filter((m: any) => m.attributes.body === message.body); - console.warn({ filteredResult }); return filteredResult.some(m => isDuplicate(m, message, source)); } catch (error) { window?.log?.error('isMessageDuplicate error:', Errors.toLogFormat(error)); diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 15ec06bba..8cbed2136 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -304,6 +304,10 @@ export const _getLeftPaneLists = ( }; } + if (!Boolean(conversation.isApproved) === true && window.lokiFeatureFlags.useMessageRequests) { + continue; + } + // Add Open Group to list as soon as the name has been set if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { continue; diff --git a/ts/window.d.ts b/ts/window.d.ts index 61d1a538f..488b79281 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -48,6 +48,7 @@ declare global { padOutgoingAttachments: boolean; enablePinConversations: boolean; useUnsendRequests: boolean; + useMessageRequests: boolean; }; lokiSnodeAPI: LokiSnodeAPI; onLogin: any; From 40396224dc870a100fc9f5ef5f1581e9cdfab4ca Mon Sep 17 00:00:00 2001 From: warrickct Date: Tue, 16 Nov 2021 14:40:52 +1100 Subject: [PATCH 18/34] adding feature flag for config message receiving --- ts/receiver/configMessage.ts | 13 ++++++++----- ts/receiver/queuedJob.ts | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 4df55f14d..37f0e2637 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -124,12 +124,15 @@ const handleContactReceived = async ( }; // updateProfile will do a commit for us contactConvo.set('active_at', _.toNumber(envelope.timestamp)); - contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); - if (contactReceived.isBlocked === true) { - await BlockedNumberController.block(contactConvo.id); - } else { - await BlockedNumberController.unblock(contactConvo.id); + if (window.lokiFeatureFlags.useMessageRequests === true) { + contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); + + if (contactReceived.isBlocked === true) { + await BlockedNumberController.block(contactConvo.id); + } else { + await BlockedNumberController.unblock(contactConvo.id); + } } await updateProfileOneAtATime(contactConvo, profile, contactReceived.profileKey); diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 00ce7d73c..9be31a3a8 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -311,7 +311,7 @@ async function handleRegularMessage( updateReadStatus(message, conversation); } - if (type === 'outgoing') { + if (type === 'outgoing' && window.lokiFeatureFlags.useMessageRequests) { handleSyncedReceipts(message, conversation); // assumes sync receipts are always from linked device outgoings? From 2e2941ba9b7b977041a07a322d230860ec3cb5ae Mon Sep 17 00:00:00 2001 From: warrickct Date: Mon, 22 Nov 2021 12:08:48 +1100 Subject: [PATCH 19/34] message request refactoring. --- preload.js | 2 +- .../session/LeftPaneMessageSection.tsx | 8 -- .../conversations/ConversationController.ts | 6 +- ts/state/selectors/conversations.ts | 132 +++++++++++++++--- 4 files changed, 115 insertions(+), 33 deletions(-) diff --git a/preload.js b/preload.js index 9cf51698a..aab289d73 100644 --- a/preload.js +++ b/preload.js @@ -51,7 +51,7 @@ window.lokiFeatureFlags = { padOutgoingAttachments: true, enablePinConversations: true, useUnsendRequests: false, - useMessageRequests: false, + useMessageRequests: true, }; window.isBeforeVersion = (toCheck, baseVersion) => { diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index c90ab435b..b78208892 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -84,10 +84,6 @@ export class LeftPaneMessageSection extends React.Component { throw new Error('renderRow: Tried to render without conversations'); } - const messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true && - window?.lokiFeatureFlags?.useMessageRequests; - let conversation; if (conversations?.length) { conversation = conversations[index]; @@ -97,10 +93,6 @@ export class LeftPaneMessageSection extends React.Component { return null; } - // TODO: need to confirm what default setting is best here. - if (messageRequestsEnabled && !Boolean(conversation.isApproved)) { - return null; - } return ; }; diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index ae840959b..61016796b 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -236,7 +236,11 @@ export class ConversationController { if (conversation.isPrivate()) { window.log.info(`deleteContact isPrivate, marking as inactive: ${id}`); - conversation.set('active_at', undefined); + // conversation.set('active_at', undefined); + conversation.set({ + active_at: undefined, + isApproved: false, + }); await conversation.commit(); } else { window.log.info(`deleteContact !isPrivate, removing convo from DB: ${id}`); diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 8cbed2136..bf9f20e52 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -304,41 +304,49 @@ export const _getLeftPaneLists = ( }; } - if (!Boolean(conversation.isApproved) === true && window.lokiFeatureFlags.useMessageRequests) { - continue; - } + const messageRequestsEnabled = + window.inboxStore?.getState().userConfig.messageRequests === true && + window?.lokiFeatureFlags?.useMessageRequests === true; + + // if (!Boolean(conversation.isApproved) === true && window.lokiFeatureFlags.useMessageRequests) { + // continue; + // } // Add Open Group to list as soon as the name has been set - if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { - continue; + // if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + // continue; + // } + + // if (!conversation.isApproved && !conversation.isBlocked) { + // conversationRequests.push(conversation); + // } + + if (shouldShowInRequestList(conversation, messageRequestsEnabled)) { + conversationRequests.push(conversation); } // Remove all invalid conversations and conversatons of devices associated // with cancelled attempted links - if (!conversation.isPublic && !conversation.activeAt) { - continue; - } + // if (!conversation.isPublic && !conversation.activeAt) { + // continue; + // } - if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { + // if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { + // directConversations.push(conversation); + // } + + if (shouldShowInContacts(conversation)) { directConversations.push(conversation); } - if (!conversation.isApproved && !conversation.isBlocked) { - conversationRequests.push(conversation); - } - - if ( - unreadCount < 9 && - conversation.unreadCount && - conversation.unreadCount > 0 && - conversation.currentNotificationSetting !== 'disabled' - ) { - unreadCount += conversation.unreadCount; - } - - if (conversation.isApproved) { + if (shouldShowInConversationList(conversation, messageRequestsEnabled)) { conversations.push(conversation); + unreadCount = calculateNewUnreadTotal(unreadCount, conversation); } + + // if (conversation.isApproved) { + // conversations.push(conversation); + // } } return { @@ -349,6 +357,84 @@ export const _getLeftPaneLists = ( }; }; +const calculateNewUnreadTotal = (unreadCount: number, conversation: ReduxConversationType) => { + if ( + unreadCount < 9 && + conversation.unreadCount && + conversation.unreadCount > 0 && + conversation.currentNotificationSetting !== 'disabled' + ) { + unreadCount += conversation.unreadCount; + } + return unreadCount; +}; + +const shouldShowInRequestList = ( + conversation: ReduxConversationType, + messageRequestsEnabled: boolean +) => { + if (conversation.isPublic || conversation.isBlocked || Boolean(conversation.activeAt) === false) { + return false; + } + + if (messageRequestsEnabled) { + if (Boolean(conversation.isApproved || !conversation.isBlocked)) { + return false; + } + } + return true; +}; + +const shouldShowInContacts = (conversation: ReduxConversationType) => { + // Add Open Group to list as soon as the name has been set + if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + return false; + } + + // if (shouldShowInRequestList(conversation)) { + // conversationRequests.push(conversation); + // } + + // Remove all invalid conversations and conversatons of devices associated + // with cancelled attempted links + if (!conversation.isPublic && !conversation.activeAt) { + return false; + } + + if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { + // directConversations.push(conversation); + return true; + } else { + return false; + } +}; + +const shouldShowInConversationList = ( + conversation: ReduxConversationType, + messageRequestsEnabled: boolean +) => { + // // Add Open Group to list as soon as the name has been set + if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + return false; + } + + // Remove all invalid conversations and conversatons of devices associated + // with cancelled attempted links + if (!conversation.isPublic && !conversation.activeAt) { + return false; + } + + // if (!conversation.activeAt) { + // return false; + // } + + if (messageRequestsEnabled && !conversation.isApproved) { + return false; + } + + return true; +}; + export const getLeftPaneLists = createSelector( getConversationLookup, getConversationComparator, From 9c9a43ee97f4982088c9fe0456c62ac6d040f761 Mon Sep 17 00:00:00 2001 From: warrickct Date: Mon, 22 Nov 2021 15:48:12 +1100 Subject: [PATCH 20/34] Adding improvements to message request handling. --- _locales/en/messages.json | 3 +- ts/components/LeftPane.tsx | 1 + .../session/LeftPaneMessageSection.tsx | 13 +- .../session/settings/SessionSettings.tsx | 20 +-- ts/receiver/configMessage.ts | 2 +- ts/state/ducks/userConfig.tsx | 14 +- ts/state/selectors/conversations.ts | 131 ++++-------------- 7 files changed, 46 insertions(+), 138 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index dba5ec8dc..1dc5dc183 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -447,5 +447,6 @@ "blockAll": "Block All", "messageRequests": "Message Requests", "requestsSubtitle": "Pending Requests", - "requestsPlaceholder": "No requests" + "requestsPlaceholder": "No requests", + "messageRequestsDescription": "Enable Message Request Inbox" } diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 271485155..30623f91f 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -34,6 +34,7 @@ const InnerLeftPaneMessageSection = () => { diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index b78208892..d47566c98 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -37,6 +37,7 @@ export interface Props { contacts: Array; conversations?: Array; + conversationRequests?: Array; searchResults?: SearchResultsProps; } @@ -281,13 +282,19 @@ export class LeftPaneMessageSection extends React.Component { this.handleToggleOverlay(undefined); }} onButtonClick={async () => { + window?.log?.info('Blocking all conversations'); // TODO: msgrequest iterate all convos and block // iterate all conversations and set all to approve then - const allConversations = getConversationController().getConversations(); + const { conversationRequests } = this.props; let syncRequired = false; - _.forEach(allConversations, convo => { - if (convo.isApproved() !== true) { + if (!conversationRequests) { + window?.log?.info('No conversation requests to block.'); + return; + } + + _.forEach(conversationRequests, convo => { + if (convo.isApproved !== true) { BlockedNumberController.block(convo.id); syncRequired = true; } diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index 5c6228ee0..8e4df3739 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -17,11 +17,7 @@ import { import { shell } from 'electron'; import { mapDispatchToProps } from '../../../state/actions'; import { unblockConvoById } from '../../../interactions/conversationInteractions'; -import { - disableMessageRequests, - enableMessageRequests, - toggleAudioAutoplay, -} from '../../../state/ducks/userConfig'; +import { toggleAudioAutoplay, toggleMessageRequests } from '../../../state/ducks/userConfig'; import { sessionPassword, updateConfirmModal } from '../../../state/ducks/modalDialog'; import { PasswordAction } from '../../dialog/SessionPasswordDialog'; import { SessionIconButton } from '../icon'; @@ -412,22 +408,16 @@ class SettingsViewInner extends React.Component { }, { id: 'message-request-setting', - title: 'Message Requests', // TODO: translation - description: 'Enable Message Request Inbox', + title: window.i18n('messageRequests'), + description: window.i18n('messageRequestsDescription'), hidden: false, type: SessionSettingType.Toggle, category: SessionSettingCategory.Appearance, setFn: () => { - window.inboxStore?.dispatch(toggleAudioAutoplay()); - - if (window.inboxStore?.getState().userConfig.messageRequests) { - window.inboxStore?.dispatch(disableMessageRequests()); - } else { - window.inboxStore?.dispatch(enableMessageRequests()); - } + window.inboxStore?.dispatch(toggleMessageRequests()); }, content: { - defaultValue: window.inboxStore?.getState().userConfig.audioAutoplay, + defaultValue: window.inboxStore?.getState().userConfig.messageRequests, }, comparisonValue: undefined, onClick: undefined, diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 37f0e2637..eb7ce798b 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -125,7 +125,7 @@ const handleContactReceived = async ( // updateProfile will do a commit for us contactConvo.set('active_at', _.toNumber(envelope.timestamp)); - if (window.lokiFeatureFlags.useMessageRequests === true) { + if (window.lokiFeatureFlags.useMessageRequests === true && window.inboxStore?.getState().userConfig.messageRequests) { contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); if (contactReceived.isBlocked === true) { diff --git a/ts/state/ducks/userConfig.tsx b/ts/state/ducks/userConfig.tsx index 8628dcd20..57af32fdc 100644 --- a/ts/state/ducks/userConfig.tsx +++ b/ts/state/ducks/userConfig.tsx @@ -26,20 +26,12 @@ const userConfigSlice = createSlice({ disableRecoveryPhrasePrompt: state => { state.showRecoveryPhrasePrompt = false; }, - disableMessageRequests: state => { - state.messageRequests = false; - }, - enableMessageRequests: state => { - state.messageRequests = false; + toggleMessageRequests: state => { + state.messageRequests = !state.messageRequests; }, }, }); const { actions, reducer } = userConfigSlice; -export const { - toggleAudioAutoplay, - disableRecoveryPhrasePrompt, - disableMessageRequests, - enableMessageRequests, -} = actions; +export const { toggleAudioAutoplay, disableRecoveryPhrasePrompt, toggleMessageRequests } = actions; export const userConfigReducer = reducer; diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index bf9f20e52..7c76033b9 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -304,49 +304,44 @@ export const _getLeftPaneLists = ( }; } + // TODO: if message requests toggle on and msg requesnt enable const messageRequestsEnabled = window.inboxStore?.getState().userConfig.messageRequests === true && - window?.lokiFeatureFlags?.useMessageRequests === true; - - // if (!Boolean(conversation.isApproved) === true && window.lokiFeatureFlags.useMessageRequests) { - // continue; - // } + window.lokiFeatureFlags?.useMessageRequests === true; // Add Open Group to list as soon as the name has been set - // if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { - // continue; - // } - - // if (!conversation.isApproved && !conversation.isBlocked) { - // conversationRequests.push(conversation); - // } - - if (shouldShowInRequestList(conversation, messageRequestsEnabled)) { - conversationRequests.push(conversation); + if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + continue; } // Remove all invalid conversations and conversatons of devices associated // with cancelled attempted links - // if (!conversation.isPublic && !conversation.activeAt) { - // continue; - // } + if (!conversation.isPublic && !conversation.activeAt) { + continue; + } - // if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { - // directConversations.push(conversation); - // } - - if (shouldShowInContacts(conversation)) { + if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { directConversations.push(conversation); } - if (shouldShowInConversationList(conversation, messageRequestsEnabled)) { - conversations.push(conversation); - unreadCount = calculateNewUnreadTotal(unreadCount, conversation); + if (messageRequestsEnabled) { + if (!conversation.isApproved && !conversation.isBlocked) { + // dont increase unread counter, don't push to convo list. + conversationRequests.push(conversation); + continue; + } } - // if (conversation.isApproved) { - // conversations.push(conversation); - // } + if ( + unreadCount < 9 && + conversation.unreadCount && + conversation.unreadCount > 0 && + conversation.currentNotificationSetting !== 'disabled' + ) { + unreadCount += conversation.unreadCount; + } + + conversations.push(conversation); } return { @@ -357,84 +352,6 @@ export const _getLeftPaneLists = ( }; }; -const calculateNewUnreadTotal = (unreadCount: number, conversation: ReduxConversationType) => { - if ( - unreadCount < 9 && - conversation.unreadCount && - conversation.unreadCount > 0 && - conversation.currentNotificationSetting !== 'disabled' - ) { - unreadCount += conversation.unreadCount; - } - return unreadCount; -}; - -const shouldShowInRequestList = ( - conversation: ReduxConversationType, - messageRequestsEnabled: boolean -) => { - if (conversation.isPublic || conversation.isBlocked || Boolean(conversation.activeAt) === false) { - return false; - } - - if (messageRequestsEnabled) { - if (Boolean(conversation.isApproved || !conversation.isBlocked)) { - return false; - } - } - return true; -}; - -const shouldShowInContacts = (conversation: ReduxConversationType) => { - // Add Open Group to list as soon as the name has been set - if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { - return false; - } - - // if (shouldShowInRequestList(conversation)) { - // conversationRequests.push(conversation); - // } - - // Remove all invalid conversations and conversatons of devices associated - // with cancelled attempted links - if (!conversation.isPublic && !conversation.activeAt) { - return false; - } - - if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { - // directConversations.push(conversation); - return true; - } else { - return false; - } -}; - -const shouldShowInConversationList = ( - conversation: ReduxConversationType, - messageRequestsEnabled: boolean -) => { - // // Add Open Group to list as soon as the name has been set - if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { - return false; - } - - // Remove all invalid conversations and conversatons of devices associated - // with cancelled attempted links - if (!conversation.isPublic && !conversation.activeAt) { - return false; - } - - // if (!conversation.activeAt) { - // return false; - // } - - if (messageRequestsEnabled && !conversation.isApproved) { - return false; - } - - return true; -}; - export const getLeftPaneLists = createSelector( getConversationLookup, getConversationComparator, From 23ca19b125bf42319050448f52b26386ec09b18d Mon Sep 17 00:00:00 2001 From: warrickct Date: Mon, 22 Nov 2021 16:18:19 +1100 Subject: [PATCH 21/34] Only updating approval when it is a true value as we consider a block a decline. --- ts/models/conversation.ts | 2 +- ts/receiver/configMessage.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index c05077662..e30ff986e 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -1517,7 +1517,7 @@ export class ConversationModel extends Backbone.Model { } public isApproved() { - return Boolean(this.get('isApproved')); + return this.get('isApproved'); } public getTitle() { diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index eb7ce798b..297a8ea79 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -125,7 +125,11 @@ const handleContactReceived = async ( // updateProfile will do a commit for us contactConvo.set('active_at', _.toNumber(envelope.timestamp)); - if (window.lokiFeatureFlags.useMessageRequests === true && window.inboxStore?.getState().userConfig.messageRequests) { + if ( + window.lokiFeatureFlags.useMessageRequests === true && + window.inboxStore?.getState().userConfig.messageRequests && + contactReceived.isApproved === true + ) { contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); if (contactReceived.isBlocked === true) { From 2144a3980fad308ed781c6894bfa1ed3a32d9728 Mon Sep 17 00:00:00 2001 From: warrickct Date: Mon, 22 Nov 2021 16:48:30 +1100 Subject: [PATCH 22/34] Linting and formatting. --- ts/components/session/LeftPaneMessageSection.tsx | 16 +++++++--------- ts/components/session/SessionClosableOverlay.tsx | 10 ++-------- ts/models/conversation.ts | 12 +++++++----- ts/receiver/configMessage.ts | 2 +- ts/receiver/queuedJob.ts | 4 ++-- ts/session/utils/syncUtils.ts | 14 +++++--------- 6 files changed, 24 insertions(+), 34 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index d47566c98..c5c356c43 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -181,11 +181,6 @@ export class LeftPaneMessageSection extends React.Component { ); } - private handleMessageRequestsClick() { - console.warn('handle msg req clicked'); - this.handleToggleOverlay(SessionClosableOverlayType.MessageRequests); - } - public updateSearch(searchTerm: string) { if (!searchTerm) { window.inboxStore?.dispatch(clearSearch()); @@ -224,6 +219,10 @@ export class LeftPaneMessageSection extends React.Component { ); } + private handleMessageRequestsClick() { + this.handleToggleOverlay(SessionClosableOverlayType.MessageRequests); + } + private renderClosableOverlay() { const { searchTerm, searchResults } = this.props; const { loading, overlay } = this.state; @@ -282,9 +281,8 @@ export class LeftPaneMessageSection extends React.Component { this.handleToggleOverlay(undefined); }} onButtonClick={async () => { + // block all convo requests. Force sync if there were changes. window?.log?.info('Blocking all conversations'); - // TODO: msgrequest iterate all convos and block - // iterate all conversations and set all to approve then const { conversationRequests } = this.props; let syncRequired = false; @@ -293,9 +291,9 @@ export class LeftPaneMessageSection extends React.Component { return; } - _.forEach(conversationRequests, convo => { + _.forEach(conversationRequests, async convo => { if (convo.isApproved !== true) { - BlockedNumberController.block(convo.id); + await BlockedNumberController.block(convo.id); syncRequired = true; } }); diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index c5b532aa1..ad4edeb0e 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -199,7 +199,6 @@ export class SessionClosableOverlay extends React.Component { {isMessageRequestView ? ( <> -
@@ -301,7 +300,7 @@ const MessageRequestList = () => { return (
{validConversationRequests.map(conversation => { - return ; + return ; })}
); @@ -309,10 +308,5 @@ const MessageRequestList = () => { const MessageRequestListItem = (props: { conversation: ConversationListItemProps }) => { const { conversation } = props; - return ( - - ); + return ; }; diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index e30ff986e..7fc08d049 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -50,7 +50,7 @@ import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsMana import { IMAGE_JPEG } from '../types/MIME'; import { UnsendMessage } from '../session/messages/outgoing/controlMessage/UnsendMessage'; import { getLatestTimestampOffset, networkDeleteMessages } from '../session/snode_api/SNodeAPI'; -import { syncConfigurationIfNeeded } from '../session/utils/syncUtils'; +import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils'; export enum ConversationTypeEnum { GROUP = 'group', @@ -738,8 +738,8 @@ export class ConversationModel extends Backbone.Model { const updateApprovalNeeded = !this.isApproved() && (this.isPrivate() || this.isMediumGroup() || this.isClosedGroup()); if (updateApprovalNeeded) { - this.setIsApproved(true); - await syncConfigurationIfNeeded(true); + await this.setIsApproved(true); + await forceSyncConfigurationNowIfNeeded(); } if (this.isOpenGroupV2()) { @@ -1397,13 +1397,15 @@ export class ConversationModel extends Backbone.Model { public async setIsApproved(value: boolean) { if (value !== this.get('isApproved')) { - console.warn(`Setting ${this.attributes.profileName} isApproved to:: ${value}`); + window?.log?.info(`Setting ${this.attributes.profileName} isApproved to:: ${value}`); this.set({ isApproved: value, }); // to exclude the conversation from left pane messages list and message requests - if (value === false) this.set({ active_at: undefined }); + if (value === false) { + this.set({ active_at: undefined }); + } await this.commit(); } diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 297a8ea79..4a2fb4d67 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -130,7 +130,7 @@ const handleContactReceived = async ( window.inboxStore?.getState().userConfig.messageRequests && contactReceived.isApproved === true ) { - contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); + await contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); if (contactReceived.isBlocked === true) { await BlockedNumberController.block(contactConvo.id); diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 9be31a3a8..ca57e91cd 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -314,8 +314,8 @@ async function handleRegularMessage( if (type === 'outgoing' && window.lokiFeatureFlags.useMessageRequests) { handleSyncedReceipts(message, conversation); - // assumes sync receipts are always from linked device outgoings? - conversation.setIsApproved(true); + // assumes sync receipts are always from linked device outgoings + await conversation.setIsApproved(true); } const conversationActiveAt = conversation.get('active_at'); diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index 9c6017bd5..854be417c 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -38,23 +38,20 @@ const writeLastSyncTimestampToDb = async (timestamp: number) => createOrUpdateItem({ id: ITEM_ID_LAST_SYNC_TIMESTAMP, value: timestamp }); /** - * Syncs usre configuration with other devices linked to this user. - * @param force Bypass duration time limit for sending sync messages - * @returns + * Conditionally Syncs user configuration with other devices linked. */ -export const syncConfigurationIfNeeded = async (force: boolean = false) => { +export const syncConfigurationIfNeeded = async () => { const lastSyncedTimestamp = (await getLastSyncTimestampFromDb()) || 0; const now = Date.now(); // if the last sync was less than 2 days before, return early. - if (!force && Math.abs(now - lastSyncedTimestamp) < DURATION.DAYS * 7) { + if (Math.abs(now - lastSyncedTimestamp) < DURATION.DAYS * 7) { return; } - const allConvos = await (await getAllConversations()).models; + const allConvoCollection = await getAllConversations(); + const allConvos = allConvoCollection.models; - console.warn({ test: allConvos[0].attributes.isApproved }); - // const configMessage = await getCurrentConfigurationMessage(allConvos); const configMessage = await getCurrentConfigurationMessage(allConvos); try { // window?.log?.info('syncConfigurationIfNeeded with', configMessage); @@ -71,7 +68,6 @@ export const syncConfigurationIfNeeded = async (force: boolean = false) => { export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = false) => new Promise(async resolve => { - // const allConvos = getConversationController().getConversations(); const allConvos = (await getAllConversations()).models; // if we hang for more than 10sec, force resolve this promise. From f0161ec338f123521512187eb2299fee035ef09f Mon Sep 17 00:00:00 2001 From: warrickct Date: Mon, 22 Nov 2021 16:59:43 +1100 Subject: [PATCH 23/34] More formatting and linting --- ts/state/selectors/conversations.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 7c76033b9..c4fd02ba6 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -268,6 +268,7 @@ export const _getConversationComparator = (testingi18n?: LocalizerType) => { export const getConversationComparator = createSelector(getIntl, _getConversationComparator); // export only because we use it in some of our tests +// tslint:disable-next-line: cyclomatic-complexity export const _getLeftPaneLists = ( lookup: ConversationLookupType, comparator: (left: ReduxConversationType, right: ReduxConversationType) => number, @@ -304,10 +305,13 @@ export const _getLeftPaneLists = ( }; } + let messageRequestsEnabled = false; // TODO: if message requests toggle on and msg requesnt enable - const messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true && - window.lokiFeatureFlags?.useMessageRequests === true; + if (window?.inboxStore?.getState()) { + messageRequestsEnabled = + window.inboxStore?.getState().userConfig.messageRequests === true && + window.lokiFeatureFlags?.useMessageRequests === true; + } // Add Open Group to list as soon as the name has been set if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { From 043c2fa99f266d39fb109c3d2f7714d796da3eea Mon Sep 17 00:00:00 2001 From: warrickct Date: Mon, 22 Nov 2021 17:25:28 +1100 Subject: [PATCH 24/34] fixing merge conflicts --- .../session/settings/section/CategoryPrivacy.tsx | 12 ++++++++++++ ts/window.d.ts | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/ts/components/session/settings/section/CategoryPrivacy.tsx b/ts/components/session/settings/section/CategoryPrivacy.tsx index a0565fc83..da4e7c02f 100644 --- a/ts/components/session/settings/section/CategoryPrivacy.tsx +++ b/ts/components/session/settings/section/CategoryPrivacy.tsx @@ -2,6 +2,7 @@ import React from 'react'; // tslint:disable-next-line: no-submodule-imports import useUpdate from 'react-use/lib/useUpdate'; import { sessionPassword, updateConfirmModal } from '../../../../state/ducks/modalDialog'; +import { toggleMessageRequests } from '../../../../state/ducks/userConfig'; import { PasswordAction } from '../../../dialog/SessionPasswordDialog'; import { SessionButtonColor } from '../../SessionButton'; import { SessionSettingButtonItem, SessionToggleWithDescription } from '../SessionSettingListItem'; @@ -107,6 +108,17 @@ export const SettingsCategoryPrivacy = (props: { description={window.i18n('autoUpdateSettingDescription')} active={Boolean(window.getSettingValue(settingsAutoUpdate))} /> + { + // const old = Boolean(window.getSettingValue(settingsAutoUpdate)); + // window.setSettingValue(settingsAutoUpdate, !old); + window.inboxStore?.dispatch(toggleMessageRequests()); + forceUpdate(); + }} + title={window.i18n('messageRequests')} + description={window.i18n('messageRequestsDescription')} + active={Boolean(window.getSettingValue(settingsAutoUpdate))} + /> {!props.hasPassword && ( Date: Mon, 22 Nov 2021 17:36:23 +1100 Subject: [PATCH 25/34] linting and formatting changes --- ts/components/ConversationListItem.tsx | 1 + ts/receiver/queuedJob.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 8031916b2..cba97902f 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -257,6 +257,7 @@ const AvatarItem = (props: { ); }; +// tslint:disable: max-func-body-length const ConversationListItem = (props: Props) => { const { activeAt, diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 17edc225b..cdf3cbd61 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -318,7 +318,7 @@ async function handleRegularMessage( } if (type === 'outgoing' && window.lokiFeatureFlags.useMessageRequests) { - handleSyncedReceipts(message, conversation); + await handleSyncedReceipts(message, conversation); // assumes sync receipts are always from linked device outgoings await conversation.setIsApproved(true); From 3602b51986165756a4c4f72cc94d587d978f64bb Mon Sep 17 00:00:00 2001 From: warrickct Date: Tue, 23 Nov 2021 11:00:11 +1100 Subject: [PATCH 26/34] Fixing up block all logic. --- ts/components/session/LeftPaneMessageSection.tsx | 9 +++++---- ts/receiver/configMessage.ts | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index c5c356c43..89a17b140 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -291,12 +291,13 @@ export class LeftPaneMessageSection extends React.Component { return; } - _.forEach(conversationRequests, async convo => { - if (convo.isApproved !== true) { + await Promise.all( + conversationRequests.map(async convo => { await BlockedNumberController.block(convo.id); syncRequired = true; - } - }); + }) + ); + if (syncRequired) { await forceSyncConfigurationNowIfNeeded(); } diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 4a2fb4d67..86a0de634 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -127,10 +127,11 @@ const handleContactReceived = async ( if ( window.lokiFeatureFlags.useMessageRequests === true && - window.inboxStore?.getState().userConfig.messageRequests && - contactReceived.isApproved === true + window.inboxStore?.getState().userConfig.messageRequests ) { - await contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); + if (contactReceived.isApproved === true) { + await contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); + } if (contactReceived.isBlocked === true) { await BlockedNumberController.block(contactConvo.id); From 2d664a2df759cb3b1b0c3b84cbf87dbdce3ac893 Mon Sep 17 00:00:00 2001 From: warrickct Date: Tue, 23 Nov 2021 16:03:24 +1100 Subject: [PATCH 27/34] Applying PR changes. --- ts/components/ConversationListItem.tsx | 6 +- ts/components/LeftPane.tsx | 6 +- .../session/LeftPaneMessageSection.tsx | 2 - .../session/MessageRequestsBanner.tsx | 8 +- .../session/SessionClosableOverlay.tsx | 7 +- ts/receiver/queuedJob.ts | 8 +- ts/state/selectors/conversations.ts | 79 ++++++++++++++++--- ts/window.d.ts | 5 -- 8 files changed, 88 insertions(+), 33 deletions(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index cba97902f..4cd52f7e8 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -377,15 +377,15 @@ const ConversationListItem = (props: Props) => { Block - Accept - + text={window.i18n('accept')} + > ) : null}
diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 30623f91f..81beaef98 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -8,7 +8,7 @@ import { LeftPaneSettingSection } from './session/LeftPaneSettingSection'; import { SessionTheme } from '../state/ducks/SessionTheme'; import { getFocusedSection } from '../state/selectors/section'; import { useSelector } from 'react-redux'; -import { getLeftPaneLists } from '../state/selectors/conversations'; +import { getConversationRequests, getLeftPaneLists } from '../state/selectors/conversations'; import { getQuery, getSearchResults, isSearching } from '../state/selectors/search'; import { SectionType } from '../state/ducks/section'; @@ -29,12 +29,14 @@ const InnerLeftPaneMessageSection = () => { const searchResults = showSearch ? useSelector(getSearchResults) : undefined; const lists = showSearch ? undefined : useSelector(getLeftPaneLists); + const conversationRequests = useSelector(getConversationRequests); + // tslint:disable: use-simple-attributes return ( diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 89a17b140..c12ea5843 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -65,8 +65,6 @@ export class LeftPaneMessageSection extends React.Component { public constructor(props: Props) { super(props); - console.warn('convos updated'); - this.state = { loading: false, overlay: false, diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index f281bface..7e2f5408e 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { getLeftPaneLists } from '../../state/selectors/conversations'; +import { getConversationRequests } from '../../state/selectors/conversations'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon'; const StyledMessageRequestBanner = styled.div` @@ -83,9 +83,9 @@ export const CirclularIcon = (props: { iconType: SessionIconType; iconSize: Sess export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { const { handleOnClick } = props; - const convos = useSelector(getLeftPaneLists).conversationRequests; + const conversationRequests = useSelector(getConversationRequests); - if (!convos.length) { + if (!conversationRequests.length) { return null; } @@ -94,7 +94,7 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { Message Requests -
{convos.length || 0}
+
{conversationRequests.length || 0}
); diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index ad4edeb0e..a533d09a2 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -10,7 +10,7 @@ import { ConversationTypeEnum } from '../../models/conversation'; import { SessionJoinableRooms } from './SessionJoinableDefaultRooms'; import { SpacerLG, SpacerMD } from '../basic/Text'; import { useSelector } from 'react-redux'; -import { getLeftPaneLists } from '../../state/selectors/conversations'; +import { getConversationRequests } from '../../state/selectors/conversations'; import { ConversationListItemProps, MemoConversationListItemWithDetails, @@ -295,11 +295,10 @@ export class SessionClosableOverlay extends React.Component { * @returns List of message request items */ const MessageRequestList = () => { - const lists = useSelector(getLeftPaneLists); - const validConversationRequests = lists?.conversationRequests; + const conversationRequests = useSelector(getConversationRequests); return (
- {validConversationRequests.map(conversation => { + {conversationRequests.map(conversation => { return ; })}
diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index cdf3cbd61..274770287 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -317,11 +317,13 @@ async function handleRegularMessage( updateReadStatus(message, conversation); } - if (type === 'outgoing' && window.lokiFeatureFlags.useMessageRequests) { + if (type === 'outgoing') { await handleSyncedReceipts(message, conversation); - // assumes sync receipts are always from linked device outgoings - await conversation.setIsApproved(true); + if (window.lokiFeatureFlags.useMessageRequests) { + // assumes sync receipts are always from linked device outgoings + await conversation.setIsApproved(true); + } } const conversationActiveAt = conversation.get('active_at'); diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index bf1f80567..ed0022ab4 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -429,7 +429,6 @@ export const _getLeftPaneLists = ( ): { conversations: Array; contacts: Array; - conversationRequests: Array; unreadCount: number; } => { const values = Object.values(lookup); @@ -437,7 +436,6 @@ export const _getLeftPaneLists = ( const conversations: Array = []; const directConversations: Array = []; - const conversationRequests: Array = []; let unreadCount = 0; for (let conversation of sorted) { @@ -459,7 +457,7 @@ export const _getLeftPaneLists = ( } let messageRequestsEnabled = false; - // TODO: if message requests toggle on and msg requesnt enable + if (window?.inboxStore?.getState()) { messageRequestsEnabled = window.inboxStore?.getState().userConfig.messageRequests === true && @@ -481,12 +479,9 @@ export const _getLeftPaneLists = ( directConversations.push(conversation); } - if (messageRequestsEnabled) { - if (!conversation.isApproved && !conversation.isBlocked) { - // dont increase unread counter, don't push to convo list. - conversationRequests.push(conversation); - continue; - } + if (messageRequestsEnabled && !conversation.isApproved && !conversation.isBlocked) { + // dont increase unread counter, don't push to convo list. + continue; } if ( @@ -504,11 +499,75 @@ export const _getLeftPaneLists = ( return { conversations, contacts: directConversations, - conversationRequests, unreadCount, }; }; +export const _getConversationRequests = ( + lookup: ConversationLookupType, + comparator: (left: ReduxConversationType, right: ReduxConversationType) => number, + selectedConversation?: string +): Array => { + const values = Object.values(lookup); + const sorted = values.sort(comparator); + + const conversationRequests: Array = []; + + for (let conversation of sorted) { + if (selectedConversation === conversation.id) { + conversation = { + ...conversation, + isSelected: true, + }; + } + + const isBlocked = + BlockedNumberController.isBlocked(conversation.id) || + BlockedNumberController.isGroupBlocked(conversation.id); + + if (isBlocked) { + conversation = { + ...conversation, + isBlocked: true, + }; + } + + let messageRequestsEnabled = false; + + if (window?.inboxStore?.getState()) { + messageRequestsEnabled = + window.inboxStore?.getState().userConfig.messageRequests === true && + window.lokiFeatureFlags?.useMessageRequests === true; + } + + // Add Open Group to list as soon as the name has been set + if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + continue; + } + + // Remove all invalid conversations and conversatons of devices associated + // with cancelled attempted links + if (!conversation.isPublic && !conversation.activeAt) { + continue; + } + + if (messageRequestsEnabled && !conversation.isApproved && !conversation.isBlocked) { + // dont increase unread counter, don't push to convo list. + conversationRequests.push(conversation); + continue; + } + } + + return conversationRequests; +}; + +export const getConversationRequests = createSelector( + getConversationLookup, + getConversationComparator, + getSelectedConversationKey, + _getConversationRequests +); + export const getLeftPaneLists = createSelector( getConversationLookup, getConversationComparator, diff --git a/ts/window.d.ts b/ts/window.d.ts index 8ef04b728..389e29876 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -43,11 +43,6 @@ declare global { log: any; lokiFeatureFlags: { useOnionRequests: boolean; - useFileOnionRequests: boolean; - useFileOnionRequestsV2: boolean; - padOutgoingAttachments: boolean; - enablePinConversations: boolean; - useUnsendRequests: boolean; useMessageRequests: boolean; useCallMessage: boolean; }; From 726418887cb0b0537f49b2cdc1a42a6d1531be0d Mon Sep 17 00:00:00 2001 From: warrickct Date: Wed, 24 Nov 2021 09:32:07 +1100 Subject: [PATCH 28/34] Addressing PR comments --- ts/components/ConversationListItem.tsx | 8 ++- .../session/LeftPaneMessageSection.tsx | 50 +++++++++++-------- ts/receiver/configMessage.ts | 14 +++++- .../conversations/ConversationController.ts | 6 --- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 4cd52f7e8..9725cc20b 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -377,15 +377,13 @@ const ConversationListItem = (props: Props) => { - Block - + text={window.i18n('blockUser')} + /> + /> ) : null}
diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index c12ea5843..2632409fd 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -221,6 +221,33 @@ export class LeftPaneMessageSection extends React.Component { this.handleToggleOverlay(SessionClosableOverlayType.MessageRequests); } + /** + * Blocks all message request conversations and synchronizes across linked devices + * @returns void + */ + private async handleBlockAllRequestsClick() { + // block all convo requests. Force sync if there were changes. + window?.log?.info('Blocking all conversations'); + const { conversationRequests } = this.props; + let syncRequired = false; + + if (!conversationRequests) { + window?.log?.info('No conversation requests to block.'); + return; + } + + await Promise.all( + conversationRequests.map(async convo => { + await BlockedNumberController.block(convo.id); + syncRequired = true; + }) + ); + + if (syncRequired) { + await forceSyncConfigurationNowIfNeeded(); + } + } + private renderClosableOverlay() { const { searchTerm, searchResults } = this.props; const { loading, overlay } = this.state; @@ -278,28 +305,7 @@ export class LeftPaneMessageSection extends React.Component { onCloseClick={() => { this.handleToggleOverlay(undefined); }} - onButtonClick={async () => { - // block all convo requests. Force sync if there were changes. - window?.log?.info('Blocking all conversations'); - const { conversationRequests } = this.props; - let syncRequired = false; - - if (!conversationRequests) { - window?.log?.info('No conversation requests to block.'); - return; - } - - await Promise.all( - conversationRequests.map(async convo => { - await BlockedNumberController.block(convo.id); - syncRequired = true; - }) - ); - - if (syncRequired) { - await forceSyncConfigurationNowIfNeeded(); - } - }} + onButtonClick={this.handleBlockAllRequestsClick} searchTerm={searchTerm} searchResults={searchResults} showSpinner={loading} diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 86a0de634..362355306 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { createOrUpdateItem } from '../data/data'; +import { createOrUpdateItem, getItemById, hasSyncedInitialConfigurationItem } from '../data/data'; import { ConversationTypeEnum } from '../models/conversation'; import { joinOpenGroupV2WithUIEvents, @@ -59,6 +59,18 @@ async function handleGroupsAndContactsFromConfigMessage( value: true, }); + const didWeHandleAConfigurationMessageAlready = + (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; + if (didWeHandleAConfigurationMessageAlready) { + window?.log?.info( + 'Dropping configuration groups change as we already handled one... Only handling contacts ' + ); + if (configMessage.contacts?.length) { + await Promise.all(configMessage.contacts.map(async c => handleContactReceived(c, envelope))); + } + return; + } + const numberClosedGroup = configMessage.closedGroups?.length || 0; window?.log?.info( diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 629851540..0bb29e58c 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -266,12 +266,6 @@ export class ConversationController { return Array.from(this.conversations.models); } - public getConversationRequests(): Array { - return Array.from(this.conversations.models).filter( - conversation => conversation.isApproved() && !conversation.isBlocked - ); - } - public unsafeDelete(convo: ConversationModel) { this.conversations.remove(convo); } From 9823a700e278aa30d85b5c01662063fef0e0b179 Mon Sep 17 00:00:00 2001 From: warrickct Date: Wed, 24 Nov 2021 11:14:24 +1100 Subject: [PATCH 29/34] Addressing PR fixes --- preload.js | 5 ---- ts/components/ConversationListItem.tsx | 26 +++++++------------ .../session/LeftPaneMessageSection.tsx | 9 ++----- .../session/MessageRequestsBanner.tsx | 2 +- .../session/SessionClosableOverlay.tsx | 2 +- .../settings/section/CategoryPrivacy.tsx | 10 +++---- ts/interactions/conversationInteractions.ts | 11 ++++++++ ts/state/selectors/userConfig.ts | 5 ++++ 8 files changed, 34 insertions(+), 36 deletions(-) diff --git a/preload.js b/preload.js index cd1679c43..ebe50c750 100644 --- a/preload.js +++ b/preload.js @@ -39,11 +39,6 @@ window.isBehindProxy = () => Boolean(config.proxyUrl); window.lokiFeatureFlags = { useOnionRequests: true, - useFileOnionRequests: true, - useFileOnionRequestsV2: true, // more compact encoding of files in response - padOutgoingAttachments: true, - enablePinConversations: true, - useUnsendRequests: false, useMessageRequests: true, useCallMessage: true, }; diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 9725cc20b..4d0ea641a 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -29,10 +29,9 @@ import { getFocusedSection } from '../state/selectors/section'; import { ConversationNotificationSettingType } from '../models/conversation'; import { Flex } from './basic/Flex'; import { SessionButton, SessionButtonColor } from './session/SessionButton'; -import { getConversationById } from '../data/data'; import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils'; -import { BlockedNumberController } from '../util'; import { updateUserDetailsModal } from '../state/ducks/modalDialog'; +import { approveConversation, blockConvoById } from '../interactions/conversationInteractions'; // tslint:disable-next-line: no-empty-interface export interface ConversationListItemProps extends ReduxConversationType {} @@ -303,20 +302,7 @@ const ConversationListItem = (props: Props) => { * adds ID to block list, syncs the block with linked devices. */ const handleConversationBlock = async () => { - const convoToBlock = await getConversationById(conversationId); - if (!convoToBlock) { - window?.log?.error('Unable to find conversation to be blocked.'); - } - await BlockedNumberController.block(convoToBlock?.id); - await forceSyncConfigurationNowIfNeeded(); - }; - - /** - * marks the conversation as approved. - */ - const handleConversationAccept = async () => { - const conversationToApprove = await getConversationById(conversationId); - await conversationToApprove?.setIsApproved(true); + blockConvoById(conversationId); await forceSyncConfigurationNowIfNeeded(); }; @@ -325,6 +311,10 @@ const ConversationListItem = (props: Props) => {
{ + e.stopPropagation(); + e.preventDefault(); + }} onContextMenu={(e: any) => { contextMenu.show({ id: triggerId, @@ -381,7 +371,9 @@ const ConversationListItem = (props: Props) => { /> { + approveConversation(conversationId); + }} text={window.i18n('accept')} /> diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 2632409fd..694e46612 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -107,7 +107,7 @@ export class LeftPaneMessageSection extends React.Component { throw new Error('render: must provided conversations if no search results are provided'); } - const length = this.props.conversations ? this.props.conversations.length : 0; + const length = conversations.length; const listKey = 0; // Note: conversations is not a known prop for List, but it is required to ensure that @@ -119,7 +119,7 @@ export class LeftPaneMessageSection extends React.Component { {({ height, width }) => ( { const messageRequestsElement = ( { this.handleToggleOverlay(undefined); }} onButtonClick={this.handleBlockAllRequestsClick} - searchTerm={searchTerm} - searchResults={searchResults} - showSpinner={loading} - updateSearch={this.updateSearch} /> ); diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index 7e2f5408e..ffd5a970b 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -92,7 +92,7 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { return ( - Message Requests + {window.i18n('messageRequests')}
{conversationRequests.length || 0}
diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index a533d09a2..14d6efd72 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -25,7 +25,7 @@ export enum SessionClosableOverlayType { interface Props { overlayMode: SessionClosableOverlayType; - onChangeSessionID: any; + onChangeSessionID?: any; onCloseClick: any; onButtonClick: any; contacts?: Array; diff --git a/ts/components/session/settings/section/CategoryPrivacy.tsx b/ts/components/session/settings/section/CategoryPrivacy.tsx index da4e7c02f..e876bbc2b 100644 --- a/ts/components/session/settings/section/CategoryPrivacy.tsx +++ b/ts/components/session/settings/section/CategoryPrivacy.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; // tslint:disable-next-line: no-submodule-imports import useUpdate from 'react-use/lib/useUpdate'; import { sessionPassword, updateConfirmModal } from '../../../../state/ducks/modalDialog'; import { toggleMessageRequests } from '../../../../state/ducks/userConfig'; +import { getIsMessageRequestsEnabled } from '../../../../state/selectors/userConfig'; import { PasswordAction } from '../../../dialog/SessionPasswordDialog'; import { SessionButtonColor } from '../../SessionButton'; import { SessionSettingButtonItem, SessionToggleWithDescription } from '../SessionSettingListItem'; @@ -53,6 +55,7 @@ export const SettingsCategoryPrivacy = (props: { onPasswordUpdated: (action: string) => void; }) => { const forceUpdate = useUpdate(); + const dispatch = useDispatch(); if (props.hasPassword !== null) { return ( @@ -110,14 +113,11 @@ export const SettingsCategoryPrivacy = (props: { /> { - // const old = Boolean(window.getSettingValue(settingsAutoUpdate)); - // window.setSettingValue(settingsAutoUpdate, !old); - window.inboxStore?.dispatch(toggleMessageRequests()); - forceUpdate(); + dispatch(toggleMessageRequests()); }} title={window.i18n('messageRequests')} description={window.i18n('messageRequestsDescription')} - active={Boolean(window.getSettingValue(settingsAutoUpdate))} + active={useSelector(getIsMessageRequestsEnabled)} /> {!props.hasPassword && ( { if (convoId.match(openGroupV2ConversationIdRegex)) { @@ -115,6 +117,15 @@ export async function unblockConvoById(conversationId: string) { await conversation.commit(); } +/** + * marks the conversation as approved. + */ +export const approveConversation = async (conversationId: string) => { + const conversationToApprove = await getConversationById(conversationId); + await conversationToApprove?.setIsApproved(true); + await forceSyncConfigurationNowIfNeeded(); +}; + export async function showUpdateGroupNameByConvoId(conversationId: string) { const conversation = getConversationController().get(conversationId); if (conversation.isMediumGroup()) { diff --git a/ts/state/selectors/userConfig.ts b/ts/state/selectors/userConfig.ts index 9c8641cb2..39dd45eba 100644 --- a/ts/state/selectors/userConfig.ts +++ b/ts/state/selectors/userConfig.ts @@ -13,3 +13,8 @@ export const getShowRecoveryPhrasePrompt = createSelector( getUserConfig, (state: UserConfigState): boolean => state.showRecoveryPhrasePrompt ); + +export const getIsMessageRequestsEnabled = createSelector( + getUserConfig, + (state: UserConfigState): boolean => state.messageRequests +); From b5df47c2b892950d716973feda5e32edc5e89fd3 Mon Sep 17 00:00:00 2001 From: warrickct Date: Wed, 24 Nov 2021 13:26:04 +1100 Subject: [PATCH 30/34] Addressing PR comments --- ts/components/ConversationListItem.tsx | 6 +-- ts/components/LeftPane.tsx | 4 +- .../session/LeftPaneMessageSection.tsx | 41 ++++++++++++++++++- .../session/MessageRequestsBanner.tsx | 4 +- ts/models/conversation.ts | 12 +++--- ts/receiver/configMessage.ts | 6 +-- .../conversations/ConversationController.ts | 1 - 7 files changed, 55 insertions(+), 19 deletions(-) diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 4d0ea641a..463a90a88 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -302,7 +302,7 @@ const ConversationListItem = (props: Props) => { * adds ID to block list, syncs the block with linked devices. */ const handleConversationBlock = async () => { - blockConvoById(conversationId); + await blockConvoById(conversationId); await forceSyncConfigurationNowIfNeeded(); }; @@ -371,8 +371,8 @@ const ConversationListItem = (props: Props) => { /> { - approveConversation(conversationId); + onClick={async () => { + await approveConversation(conversationId); }} text={window.i18n('accept')} /> diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 81beaef98..19ca70545 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -8,7 +8,7 @@ import { LeftPaneSettingSection } from './session/LeftPaneSettingSection'; import { SessionTheme } from '../state/ducks/SessionTheme'; import { getFocusedSection } from '../state/selectors/section'; import { useSelector } from 'react-redux'; -import { getConversationRequests, getLeftPaneLists } from '../state/selectors/conversations'; +import { getLeftPaneLists } from '../state/selectors/conversations'; import { getQuery, getSearchResults, isSearching } from '../state/selectors/search'; import { SectionType } from '../state/ducks/section'; @@ -29,14 +29,12 @@ const InnerLeftPaneMessageSection = () => { const searchResults = showSearch ? useSelector(getSearchResults) : undefined; const lists = showSearch ? undefined : useSelector(getLeftPaneLists); - const conversationRequests = useSelector(getConversationRequests); // tslint:disable: use-simple-attributes return ( diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 694e46612..848651b30 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -37,7 +37,6 @@ export interface Props { contacts: Array; conversations?: Array; - conversationRequests?: Array; searchResults?: SearchResultsProps; } @@ -226,9 +225,47 @@ export class LeftPaneMessageSection extends React.Component { * @returns void */ private async handleBlockAllRequestsClick() { + let messageRequestsEnabled = false; + if (window?.inboxStore?.getState()) { + messageRequestsEnabled = + window.inboxStore?.getState().userConfig.messageRequests === true && + window.lokiFeatureFlags?.useMessageRequests === true; + } + if (!messageRequestsEnabled) { + return; + } + // block all convo requests. Force sync if there were changes. window?.log?.info('Blocking all conversations'); - const { conversationRequests } = this.props; + const conversations = getConversationController().getConversations(); + + if (!conversations) { + window?.log?.info('No message requests to block.'); + return; + } + + const conversationRequests = conversations.filter(conversation => { + // Add Open Group to list as soon as the name has been set + if ( + conversation.isPublic() && + (!conversation.get('name') || conversation.get('name') === 'Unknown group') + ) { + return false; + } + + // Remove all invalid conversations and conversatons of devices associated + // with cancelled attempted links + if (!conversation.isPublic && !conversation.get('active_at')) { + return false; + } + + if (conversation.attributes.isApproved || !conversation.get('active_at')) { + return false; + } + + return true; + }); + let syncRequired = false; if (!conversationRequests) { diff --git a/ts/components/session/MessageRequestsBanner.tsx b/ts/components/session/MessageRequestsBanner.tsx index ffd5a970b..e19642439 100644 --- a/ts/components/session/MessageRequestsBanner.tsx +++ b/ts/components/session/MessageRequestsBanner.tsx @@ -92,7 +92,9 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { return ( - {window.i18n('messageRequests')} + + {window.i18n('messageRequests')} +
{conversationRequests.length || 0}
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 3e6fe5e7b..7c1edb278 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -1042,7 +1042,12 @@ export class ConversationModel extends Backbone.Model { const model = new MessageModel(messageAttributes); const isMe = messageAttributes.source === UserUtils.getOurPubKeyStrFromCache(); - if (isMe) { + + if ( + isMe && + window.lokiFeatureFlags.useMessageRequests && + window.inboxStore?.getState().userConfig.messageRequests + ) { await this.setIsApproved(true); } @@ -1288,11 +1293,6 @@ export class ConversationModel extends Backbone.Model { isApproved: value, }); - // to exclude the conversation from left pane messages list and message requests - if (value === false) { - this.set({ active_at: undefined }); - } - await this.commit(); } } diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 362355306..b091343b3 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -138,14 +138,14 @@ const handleContactReceived = async ( contactConvo.set('active_at', _.toNumber(envelope.timestamp)); if ( - window.lokiFeatureFlags.useMessageRequests === true && + window.lokiFeatureFlags.useMessageRequests && window.inboxStore?.getState().userConfig.messageRequests ) { - if (contactReceived.isApproved === true) { + if (contactReceived.isApproved) { await contactConvo.setIsApproved(Boolean(contactReceived.isApproved)); } - if (contactReceived.isBlocked === true) { + if (contactReceived.isBlocked) { await BlockedNumberController.block(contactConvo.id); } else { await BlockedNumberController.unblock(contactConvo.id); diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 0bb29e58c..1de41ac0f 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -236,7 +236,6 @@ export class ConversationController { if (conversation.isPrivate()) { window.log.info(`deleteContact isPrivate, marking as inactive: ${id}`); - // conversation.set('active_at', undefined); conversation.set({ active_at: undefined, isApproved: false, From f91e2c4edd96ac28e3e7bfe2caaddf37d4c40cd3 Mon Sep 17 00:00:00 2001 From: warrickct Date: Wed, 24 Nov 2021 13:55:16 +1100 Subject: [PATCH 31/34] Minor PR fixes --- ts/components/session/LeftPaneMessageSection.tsx | 7 ++----- ts/receiver/callMessage.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 848651b30..4c2c56b00 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -82,12 +82,9 @@ export class LeftPaneMessageSection extends React.Component { throw new Error('renderRow: Tried to render without conversations'); } - let conversation; - if (conversations?.length) { - conversation = conversations[index]; - } - + const conversation = conversations[index]; if (!conversation) { + window?.log?.info('No conversation found at index'); return null; } diff --git a/ts/receiver/callMessage.ts b/ts/receiver/callMessage.ts index bec8f2f6b..3670670fb 100644 --- a/ts/receiver/callMessage.ts +++ b/ts/receiver/callMessage.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; import { SignalService } from '../protobuf'; import { TTL_DEFAULT } from '../session/constants'; import { SNodeAPI } from '../session/snode_api'; -import { CallManager, UserUtils } from '../session/utils'; +import { CallManager } from '../session/utils'; import { removeFromCache } from './cache'; import { EnvelopePlus } from './types'; From c3e58f725e0478b7190e32644a83f666da8778ca Mon Sep 17 00:00:00 2001 From: warrickct Date: Wed, 24 Nov 2021 15:37:22 +1100 Subject: [PATCH 32/34] Adding trigger logic for conversation filtering of requests. --- ts/state/selectors/conversations.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index ed0022ab4..44ebe7620 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -36,6 +36,7 @@ import { MessageAttachmentSelectorProps } from '../../components/conversation/me import { MessageContentSelectorProps } from '../../components/conversation/message/MessageContent'; import { MessageContentWithStatusSelectorProps } from '../../components/conversation/message/MessageContentWithStatus'; import { GenericReadableMessageSelectorProps } from '../../components/conversation/message/GenericReadableMessage'; +import { getIsMessageRequestsEnabled } from './userConfig'; export const getConversations = (state: StateType): ConversationsStateType => state.conversations; @@ -425,6 +426,7 @@ export const getConversationComparator = createSelector(getIntl, _getConversatio export const _getLeftPaneLists = ( lookup: ConversationLookupType, comparator: (left: ReduxConversationType, right: ReduxConversationType) => number, + isMessageRequestEnabled?: boolean, selectedConversation?: string ): { conversations: Array; @@ -456,13 +458,8 @@ export const _getLeftPaneLists = ( }; } - let messageRequestsEnabled = false; - - if (window?.inboxStore?.getState()) { - messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true && - window.lokiFeatureFlags?.useMessageRequests === true; - } + const excludeUnapproved = + isMessageRequestEnabled && window.lokiFeatureFlags?.useMessageRequests; // Add Open Group to list as soon as the name has been set if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { @@ -479,7 +476,7 @@ export const _getLeftPaneLists = ( directConversations.push(conversation); } - if (messageRequestsEnabled && !conversation.isApproved && !conversation.isBlocked) { + if (excludeUnapproved && !conversation.isApproved && !conversation.isBlocked) { // dont increase unread counter, don't push to convo list. continue; } @@ -571,6 +568,7 @@ export const getConversationRequests = createSelector( export const getLeftPaneLists = createSelector( getConversationLookup, getConversationComparator, + getIsMessageRequestsEnabled, getSelectedConversationKey, _getLeftPaneLists ); From e32f20d8bc04cec3abe52f1d4db84f163bbd8010 Mon Sep 17 00:00:00 2001 From: warrickct Date: Fri, 26 Nov 2021 13:20:03 +1100 Subject: [PATCH 33/34] PR changes --- ts/components/LeftPane.tsx | 4 +- .../session/LeftPaneMessageSection.tsx | 13 ++- .../session/SessionClosableOverlay.tsx | 18 ++-- ts/interactions/conversationInteractions.ts | 11 ++- ts/models/conversation.ts | 2 +- ts/receiver/callMessage.ts | 2 +- ts/receiver/configMessage.ts | 12 +-- ts/state/selectors/conversations.ts | 82 ++++++------------- .../unit/selectors/conversations_test.ts | 10 +-- 9 files changed, 66 insertions(+), 88 deletions(-) diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 19ca70545..194f00a29 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -11,6 +11,7 @@ import { useSelector } from 'react-redux'; import { getLeftPaneLists } from '../state/selectors/conversations'; import { getQuery, getSearchResults, isSearching } from '../state/selectors/search'; import { SectionType } from '../state/ducks/section'; +import { getIsMessageRequestsEnabled } from '../state/selectors/userConfig'; // from https://github.com/bvaughn/react-virtualized/blob/fb3484ed5dcc41bffae8eab029126c0fb8f7abc0/source/List/types.js#L5 export type RowRendererParamsType = { @@ -29,14 +30,15 @@ const InnerLeftPaneMessageSection = () => { const searchResults = showSearch ? useSelector(getSearchResults) : undefined; const lists = showSearch ? undefined : useSelector(getLeftPaneLists); + const messageRequestsEnabled = useSelector(getIsMessageRequestsEnabled); - // tslint:disable: use-simple-attributes return ( ); }; diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 4c2c56b00..6c3746d4b 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -38,6 +38,8 @@ export interface Props { contacts: Array; conversations?: Array; searchResults?: SearchResultsProps; + + messageRequestsEnabled?: boolean; } export enum SessionComposeToType { @@ -84,7 +86,7 @@ export class LeftPaneMessageSection extends React.Component { const conversation = conversations[index]; if (!conversation) { - window?.log?.info('No conversation found at index'); + throw new Error('renderRow: conversations selector returned element containing falsy value.'); return null; } @@ -222,12 +224,9 @@ export class LeftPaneMessageSection extends React.Component { * @returns void */ private async handleBlockAllRequestsClick() { - let messageRequestsEnabled = false; - if (window?.inboxStore?.getState()) { - messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true && - window.lokiFeatureFlags?.useMessageRequests === true; - } + const messageRequestsEnabled = + this.props.messageRequestsEnabled && window?.lokiFeatureFlags?.useMessageRequests; + if (!messageRequestsEnabled) { return; } diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 14d6efd72..967dc1b77 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -11,10 +11,7 @@ import { SessionJoinableRooms } from './SessionJoinableDefaultRooms'; import { SpacerLG, SpacerMD } from '../basic/Text'; import { useSelector } from 'react-redux'; import { getConversationRequests } from '../../state/selectors/conversations'; -import { - ConversationListItemProps, - MemoConversationListItemWithDetails, -} from '../ConversationListItem'; +import { MemoConversationListItemWithDetails } from '../ConversationListItem'; export enum SessionClosableOverlayType { Message = 'message', @@ -299,13 +296,14 @@ const MessageRequestList = () => { return (
{conversationRequests.map(conversation => { - return ; + return ( + + ); })}
); }; - -const MessageRequestListItem = (props: { conversation: ConversationListItemProps }) => { - const { conversation } = props; - return ; -}; diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 247cb3537..fad5af236 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -122,8 +122,17 @@ export async function unblockConvoById(conversationId: string) { */ export const approveConversation = async (conversationId: string) => { const conversationToApprove = await getConversationById(conversationId); + + if (!conversationToApprove || conversationToApprove.isApproved()) { + window?.log?.info('Conversation is already approved.'); + return; + } + await conversationToApprove?.setIsApproved(true); - await forceSyncConfigurationNowIfNeeded(); + + if (conversationToApprove?.isApproved() === true) { + await forceSyncConfigurationNowIfNeeded(); + } }; export async function showUpdateGroupNameByConvoId(conversationId: string) { diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 507b0f1d5..7a95a9979 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -738,7 +738,7 @@ export class ConversationModel extends Backbone.Model { !this.isApproved() && (this.isPrivate() || this.isMediumGroup() || this.isClosedGroup()); if (updateApprovalNeeded) { await this.setIsApproved(true); - await forceSyncConfigurationNowIfNeeded(); + void forceSyncConfigurationNowIfNeeded(); } if (this.isOpenGroupV2()) { diff --git a/ts/receiver/callMessage.ts b/ts/receiver/callMessage.ts index b1f387262..cbf0d4891 100644 --- a/ts/receiver/callMessage.ts +++ b/ts/receiver/callMessage.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; import { SignalService } from '../protobuf'; import { TTL_DEFAULT } from '../session/constants'; import { SNodeAPI } from '../session/snode_api'; -import { CallManager } from '../session/utils'; +import { CallManager, UserUtils } from '../session/utils'; import { removeFromCache } from './cache'; import { EnvelopePlus } from './types'; diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index b091343b3..e396d76a1 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -54,11 +54,6 @@ async function handleGroupsAndContactsFromConfigMessage( envelope: EnvelopePlus, configMessage: SignalService.ConfigurationMessage ) { - await createOrUpdateItem({ - id: 'hasSyncedInitialConfigurationItem', - value: true, - }); - const didWeHandleAConfigurationMessageAlready = (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; if (didWeHandleAConfigurationMessageAlready) { @@ -71,6 +66,11 @@ async function handleGroupsAndContactsFromConfigMessage( return; } + await createOrUpdateItem({ + id: 'hasSyncedInitialConfigurationItem', + value: true, + }); + const numberClosedGroup = configMessage.closedGroups?.length || 0; window?.log?.info( @@ -152,7 +152,7 @@ const handleContactReceived = async ( } } - await updateProfileOneAtATime(contactConvo, profile, contactReceived.profileKey); + void updateProfileOneAtATime(contactConvo, profile, contactReceived.profileKey); } catch (e) { window?.log?.warn('failed to handle a new closed group from configuration message'); } diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index d508f1fbf..07927f2fd 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -331,54 +331,21 @@ export const getConversationComparator = createSelector(getIntl, _getConversatio // export only because we use it in some of our tests // tslint:disable-next-line: cyclomatic-complexity export const _getLeftPaneLists = ( - lookup: ConversationLookupType, - comparator: (left: ReduxConversationType, right: ReduxConversationType) => number, - isMessageRequestEnabled?: boolean, - selectedConversation?: string + sortedConversations: Array, + isMessageRequestEnabled?: boolean ): { conversations: Array; contacts: Array; unreadCount: number; } => { - const values = Object.values(lookup); - const sorted = values.sort(comparator); - const conversations: Array = []; const directConversations: Array = []; let unreadCount = 0; - for (let conversation of sorted) { - if (selectedConversation === conversation.id) { - conversation = { - ...conversation, - isSelected: true, - }; - } - const isBlocked = - BlockedNumberController.isBlocked(conversation.id) || - BlockedNumberController.isGroupBlocked(conversation.id); - - if (isBlocked) { - conversation = { - ...conversation, - isBlocked: true, - }; - } - + for (const conversation of sortedConversations) { const excludeUnapproved = isMessageRequestEnabled && window.lokiFeatureFlags?.useMessageRequests; - // Add Open Group to list as soon as the name has been set - if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { - continue; - } - - // Remove all invalid conversations and conversatons of devices associated - // with cancelled attempted links - if (!conversation.isPublic && !conversation.activeAt) { - continue; - } - if (conversation.activeAt !== undefined && conversation.type === ConversationTypeEnum.PRIVATE) { directConversations.push(conversation); } @@ -407,7 +374,7 @@ export const _getLeftPaneLists = ( }; }; -export const _getConversationRequests = ( +export const _getSortedConversations = ( lookup: ConversationLookupType, comparator: (left: ReduxConversationType, right: ReduxConversationType) => number, selectedConversation?: string @@ -415,7 +382,7 @@ export const _getConversationRequests = ( const values = Object.values(lookup); const sorted = values.sort(comparator); - const conversationRequests: Array = []; + const sortedConversations: Array = []; for (let conversation of sorted) { if (selectedConversation === conversation.id) { @@ -436,14 +403,6 @@ export const _getConversationRequests = ( }; } - let messageRequestsEnabled = false; - - if (window?.inboxStore?.getState()) { - messageRequestsEnabled = - window.inboxStore?.getState().userConfig.messageRequests === true && - window.lokiFeatureFlags?.useMessageRequests === true; - } - // Add Open Group to list as soon as the name has been set if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { continue; @@ -455,28 +414,39 @@ export const _getConversationRequests = ( continue; } - if (messageRequestsEnabled && !conversation.isApproved && !conversation.isBlocked) { - // dont increase unread counter, don't push to convo list. - conversationRequests.push(conversation); - continue; - } + sortedConversations.push(conversation); } - return conversationRequests; + return sortedConversations; }; -export const getConversationRequests = createSelector( +export const getSortedConversations = createSelector( getConversationLookup, getConversationComparator, getSelectedConversationKey, + _getSortedConversations +); + +export const _getConversationRequests = ( + sortedConversations: Array, + isMessageRequestEnabled?: boolean +): Array => { + const pushToMessageRequests = + isMessageRequestEnabled && window.lokiFeatureFlags?.useMessageRequests; + return _.filter(sortedConversations, conversation => { + return pushToMessageRequests && !conversation.isApproved && !conversation.isBlocked; + }); +}; + +export const getConversationRequests = createSelector( + getSortedConversations, + getIsMessageRequestsEnabled, _getConversationRequests ); export const getLeftPaneLists = createSelector( - getConversationLookup, - getConversationComparator, + getSortedConversations, getIsMessageRequestsEnabled, - getSelectedConversationKey, _getLeftPaneLists ); diff --git a/ts/test/session/unit/selectors/conversations_test.ts b/ts/test/session/unit/selectors/conversations_test.ts index 30e5bfb89..d3b8c9677 100644 --- a/ts/test/session/unit/selectors/conversations_test.ts +++ b/ts/test/session/unit/selectors/conversations_test.ts @@ -4,11 +4,11 @@ import { ConversationTypeEnum } from '../../../../models/conversation'; import { ConversationLookupType } from '../../../../state/ducks/conversations'; import { _getConversationComparator, - _getLeftPaneLists, + _getSortedConversations, } from '../../../../state/selectors/conversations'; describe('state/selectors/conversations', () => { - describe('#getLeftPaneList', () => { + describe('#getSortedConversationsList', () => { // tslint:disable-next-line: max-func-body-length it('sorts conversations based on timestamp then by intl-friendly title', () => { const i18n = (key: string) => key; @@ -160,7 +160,7 @@ describe('state/selectors/conversations', () => { }, }; const comparator = _getConversationComparator(i18n); - const { conversations } = _getLeftPaneLists(data, comparator); + const conversations = _getSortedConversations(data, comparator); assert.strictEqual(conversations[0].name, 'First!'); assert.strictEqual(conversations[1].name, 'Á'); @@ -169,7 +169,7 @@ describe('state/selectors/conversations', () => { }); }); - describe('#getLeftPaneListWithPinned', () => { + describe('#getSortedConversationsWithPinned', () => { // tslint:disable-next-line: max-func-body-length it('sorts conversations based on pin, timestamp then by intl-friendly title', () => { const i18n = (key: string) => key; @@ -325,7 +325,7 @@ describe('state/selectors/conversations', () => { }, }; const comparator = _getConversationComparator(i18n); - const { conversations } = _getLeftPaneLists(data, comparator); + const conversations = _getSortedConversations(data, comparator); assert.strictEqual(conversations[0].name, 'Á'); assert.strictEqual(conversations[1].name, 'C'); From f17b923addb7ef7c644080adb52bd9276686acc1 Mon Sep 17 00:00:00 2001 From: warrickct Date: Fri, 26 Nov 2021 15:29:57 +1100 Subject: [PATCH 34/34] Fixing rimraf transpile bug. Adding PR fixes - icon buttons. --- package.json | 2 +- stylesheets/_modules.scss | 4 ++++ ts/components/ConversationListItem.tsx | 23 +++++++++++------- .../session/LeftPaneMessageSection.tsx | 24 +++---------------- ts/state/selectors/conversations.ts | 2 +- 5 files changed, 24 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 7a4f72254..98441bd91 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "transpile": "tsc --incremental", "transpile:watch": "tsc -w", "integration-test": "mocha --recursive --exit --timeout 30000 \"./ts/test-integration/**/*.test.js\" \"./ts/test/*.test.js\"", - "clean-transpile": "rimraf 'ts/**/*.js ts/*.js' 'ts/*.js.map' 'ts/**/*.js.map' && rimraf tsconfig.tsbuildinfo;", + "clean-transpile": "rimraf 'ts/**/*.js' 'ts/*.js' 'ts/*.js.map' 'ts/**/*.js.map' && rimraf tsconfig.tsbuildinfo;", "ready": "yarn clean-transpile; yarn grunt && yarn lint-full && yarn test", "build:webpack:sql-worker": "cross-env NODE_ENV=production webpack -c webpack-sql-worker.config.ts", "sedtoAppImage": "sed -i 's/\"target\": \\[\"deb\", \"rpm\", \"freebsd\"\\]/\"target\": \"AppImage\"/g' package.json", diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index cda5a79b5..badeba855 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -888,6 +888,10 @@ flex-direction: column; align-items: stretch; overflow: hidden; + + .session-icon-button:first-child { + margin-right: $session-margin-sm; + } } .module-conversation-list-item__header { diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 463a90a88..5131c8b49 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -22,13 +22,12 @@ import { } from '../state/ducks/conversations'; import _ from 'underscore'; import { useMembersAvatars } from '../hooks/useMembersAvatar'; -import { SessionIcon } from './session/icon'; +import { SessionIcon, SessionIconButton } from './session/icon'; import { useDispatch, useSelector } from 'react-redux'; import { SectionType } from '../state/ducks/section'; import { getFocusedSection } from '../state/selectors/section'; import { ConversationNotificationSettingType } from '../models/conversation'; import { Flex } from './basic/Flex'; -import { SessionButton, SessionButtonColor } from './session/SessionButton'; import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils'; import { updateUserDetailsModal } from '../state/ducks/modalDialog'; import { approveConversation, blockConvoById } from '../interactions/conversationInteractions'; @@ -364,17 +363,25 @@ const ConversationListItem = (props: Props) => { flexDirection="row" justifyContent="flex-end" > - - { await approveConversation(conversationId); }} - text={window.i18n('accept')} + backgroundColor="var(--color-accent)" + iconColor="var(--color-foreground-primary)" + iconPadding="var(--margins-xs)" + borderRadius="2px" /> ) : null} diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 6c3746d4b..79d7874b3 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -240,27 +240,9 @@ export class LeftPaneMessageSection extends React.Component { return; } - const conversationRequests = conversations.filter(conversation => { - // Add Open Group to list as soon as the name has been set - if ( - conversation.isPublic() && - (!conversation.get('name') || conversation.get('name') === 'Unknown group') - ) { - return false; - } - - // Remove all invalid conversations and conversatons of devices associated - // with cancelled attempted links - if (!conversation.isPublic && !conversation.get('active_at')) { - return false; - } - - if (conversation.attributes.isApproved || !conversation.get('active_at')) { - return false; - } - - return true; - }); + const conversationRequests = conversations.filter( + c => c.isPrivate() && c.get('active_at') && c.get('isApproved') + ); let syncRequired = false; diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 07927f2fd..9b0364900 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -432,7 +432,7 @@ export const _getConversationRequests = ( isMessageRequestEnabled?: boolean ): Array => { const pushToMessageRequests = - isMessageRequestEnabled && window.lokiFeatureFlags?.useMessageRequests; + isMessageRequestEnabled && window?.lokiFeatureFlags?.useMessageRequests; return _.filter(sortedConversations, conversation => { return pushToMessageRequests && !conversation.isApproved && !conversation.isBlocked; });