diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 37eec6bf8..a871d300f 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -772,7 +772,7 @@ "description": "Shown if the user attempts to send an audio message without audio permssions turned on" }, "audioPermissionNeeded": { - "message": "Session needs microphone access to send audio messages.", + "message": "You can enable microphone access under: Settings (Gear icon) => Privacy", "description": "Shown if the user attempts to send an audio message without audio permssions turned on", "androidKey": "ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone" }, diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index f12bb793d..211b2b7df 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -630,7 +630,7 @@ "description": "In Android theme, shown in quote if you or someone else replies to you" }, "audioPermissionNeeded": { - "message": "Pour envoyer des messages audio, autorisez Session à accéder à votre microphone.", + "message": "Vous pouvez autoriser l'accès au microphone via: Paramètres (icon roue dentée) => Confidentialité.", "description": "Shown if the user attempts to send an audio message without audio permssions turned on" }, "allowAccess": { diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 7c91a879b..c0f62dc6f 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -33,14 +33,15 @@ interface Props { unreadMessageCount: number; searchResults?: SearchResultsProps; searchTerm: string; + focusedSection: SectionType; - focusSection: (section: SectionType) => void; + focusedSettingsSection?: SessionSettingCategory; + showLeftPaneSection: (section: SectionType) => void; + showSettingsSection: (section: SessionSettingCategory) => void; + isExpired: boolean; openConversationExternal: (id: string, messageId?: string) => void; - showSessionSettingsCategory: (category: SessionSettingCategory) => void; - showSessionViewConversation: () => void; - settingsCategory?: SessionSettingCategory; updateSearchTerm: (searchTerm: string) => void; search: (query: string, options: SearchOptions) => void; clearSearch: () => void; @@ -56,12 +57,7 @@ export class LeftPane extends React.Component { public handleSectionSelected(section: SectionType) { this.props.clearSearch(); - this.props.focusSection(section); - if (section === SectionType.Settings) { - this.props.showSessionSettingsCategory(SessionSettingCategory.Appearance); - } else { - this.props.showSessionViewConversation(); - } + this.props.showLeftPaneSection(section); } public render(): JSX.Element { @@ -146,13 +142,14 @@ export class LeftPane extends React.Component { } private renderSettingSection() { - const { settingsCategory } = this.props; - - const category = settingsCategory || SessionSettingCategory.Appearance; - + const settingsCategory = + this.props.focusedSettingsSection || SessionSettingCategory.Appearance; return ( <> - + ); } diff --git a/ts/components/SessionMainPanel.tsx b/ts/components/SessionMainPanel.tsx new file mode 100644 index 000000000..c55a4945e --- /dev/null +++ b/ts/components/SessionMainPanel.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +import { DefaultTheme } from 'styled-components'; +import { SmartSessionConversation } from '../state/smart/SessionConversation'; +import { + SessionSettingCategory, + SmartSettingsView, +} from './session/settings/SessionSettings'; + +const FilteredSettingsView = SmartSettingsView as any; + +interface Props { + focusedSettingsSection?: SessionSettingCategory; +} + +export class SessionMainPanel extends React.Component { + public constructor(props: Props) { + super(props); + } + + public render() { + const isSettingsView = this.props.focusedSettingsSection !== undefined; + + return isSettingsView + ? this.renderSettings() + : this.renderSessionConversation(); + } + + private renderSettings() { + const category = this.props.focusedSettingsSection; + + return ; + } + + private renderSessionConversation() { + return ( +
+ +
+ ); + } +} diff --git a/ts/components/session/LeftPaneSettingSection.tsx b/ts/components/session/LeftPaneSettingSection.tsx index fdb781ba1..dbef6afa1 100644 --- a/ts/components/session/LeftPaneSettingSection.tsx +++ b/ts/components/session/LeftPaneSettingSection.tsx @@ -18,7 +18,7 @@ import { deleteAccount } from '../../util/accountManager'; interface Props { settingsCategory: SessionSettingCategory; - showSessionSettingsCategory: (category: SessionSettingCategory) => void; + showSettingsSection: (category: SessionSettingCategory) => void; theme: DefaultTheme; } @@ -34,7 +34,6 @@ export class LeftPaneSettingSection extends React.Component { searchQuery: '', }; - this.setCategory = this.setCategory.bind(this); this.onDeleteAccount = this.onDeleteAccount.bind(this); } @@ -67,7 +66,7 @@ export class LeftPaneSettingSection extends React.Component { )} role="link" onClick={() => { - this.setCategory(item.id); + this.props.showSettingsSection(item.id); }} >
@@ -214,8 +213,4 @@ export class LeftPaneSettingSection extends React.Component { }, ]; } - - public setCategory(category: SessionSettingCategory) { - this.props.showSessionSettingsCategory(category); - } } diff --git a/ts/components/session/SessionInboxView.tsx b/ts/components/session/SessionInboxView.tsx index 3992f4aaa..043425f36 100644 --- a/ts/components/session/SessionInboxView.tsx +++ b/ts/components/session/SessionInboxView.tsx @@ -1,59 +1,34 @@ import React from 'react'; import { Provider } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { getMessageById } from '../../data/data'; import { ConversationModel } from '../../models/conversation'; -import { MessageModel } from '../../models/message'; -import { getMessageQueue } from '../../session'; import { ConversationController } from '../../session/conversations'; -import { MessageController } from '../../session/messages'; -import { OpenGroupMessage } from '../../session/messages/outgoing'; -import { RawMessage } from '../../session/types'; import { UserUtils } from '../../session/utils'; import { createStore } from '../../state/createStore'; import { actions as conversationActions } from '../../state/ducks/conversations'; -import { actions as userActions } from '../../state/ducks/user'; import { SmartLeftPane } from '../../state/smart/LeftPane'; -import { SmartSessionConversation } from '../../state/smart/SessionConversation'; +import { SmartSessionMainPanel } from '../../state/smart/SessionMainPanel'; import { makeLookup } from '../../util'; -import { - SessionSettingCategory, - SmartSettingsView, -} from './settings/SessionSettings'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 const FilteredLeftPane = SmartLeftPane as any; -const FilteredSettingsView = SmartSettingsView as any; - -type Props = { - focusedSection: number; -}; type State = { isInitialLoadComplete: boolean; - settingsCategory?: SessionSettingCategory; isExpired: boolean; }; -export class SessionInboxView extends React.Component { +export class SessionInboxView extends React.Component { private store: any; constructor(props: any) { super(props); this.state = { isInitialLoadComplete: false, - settingsCategory: undefined, isExpired: false, }; - this.showSessionSettingsCategory = this.showSessionSettingsCategory.bind( - this - ); - this.showSessionViewConversation = this.showSessionViewConversation.bind( - this - ); - void this.setupLeftPane(); // not reactified yet. this is a callback called once we were able to check for expiration of this Session version @@ -71,46 +46,19 @@ export class SessionInboxView extends React.Component { return <>; } - const { settingsCategory } = this.state; - - const isSettingsView = settingsCategory !== undefined; return (
{this.renderLeftPane()}
- {isSettingsView - ? this.renderSettings() - : this.renderSessionConversation()} + ); } private renderLeftPane() { - return ( - - ); - } - - private renderSettings() { - const category = - this.state.settingsCategory || SessionSettingCategory.Appearance; - - return ; - } - - private renderSessionConversation() { - return ( -
- -
- ); + return ; } private async setupLeftPane() { @@ -155,12 +103,4 @@ export class SessionInboxView extends React.Component { this.setState({ isInitialLoadComplete: true }); } - - private showSessionSettingsCategory(category: SessionSettingCategory) { - this.setState({ settingsCategory: category }); - } - - private showSessionViewConversation() { - this.setState({ settingsCategory: undefined }); - } } diff --git a/ts/components/session/SessionToast.tsx b/ts/components/session/SessionToast.tsx index 65752effa..e3621a08d 100644 --- a/ts/components/session/SessionToast.tsx +++ b/ts/components/session/SessionToast.tsx @@ -3,6 +3,7 @@ import React, { useContext } from 'react'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon/'; import { Flex } from './Flex'; import styled, { ThemeContext } from 'styled-components'; +import { noop } from 'lodash'; export enum SessionToastType { Info = 'info', @@ -18,6 +19,7 @@ type Props = { icon?: SessionIconType; description?: string; closeToast?: any; + onToastClick?: () => void; }; const TitleDiv = styled.div` @@ -74,7 +76,12 @@ export const SessionToast = (props: Props) => { } return ( - + // tslint:disable-next-line: use-simple-attributes + any; removeAttachment: (toRemove: AttachmentType) => void; onChoseAttachments: (newAttachments: Array) => void; + showLeftPaneSection: (section: SectionType) => void; + showSettingsSection: (category: SessionSettingCategory) => void; theme: DefaultTheme; } @@ -931,7 +935,10 @@ export class SessionCompositionBox extends React.Component { return; } - ToastUtils.pushAudioPermissionNeeded(); + ToastUtils.pushAudioPermissionNeeded(() => { + this.props.showLeftPaneSection(SectionType.Settings); + this.props.showSettingsSection(SessionSettingCategory.Privacy); + }); } private onExitVoiceNoteView() { diff --git a/ts/components/session/conversation/SessionConversation.tsx b/ts/components/session/conversation/SessionConversation.tsx index 55fc17355..6becc6e27 100644 --- a/ts/components/session/conversation/SessionConversation.tsx +++ b/ts/components/session/conversation/SessionConversation.tsx @@ -29,8 +29,6 @@ import { MessageView } from '../../MainViewController'; import { pushUnblockToSend } from '../../../session/utils/Toast'; import { MessageDetail } from '../../conversation/MessageDetail'; import { ConversationController } from '../../../session/conversations'; -import { PubKey } from '../../../session/types'; -import { MessageModel } from '../../../models/message'; import { getMessageById, getPubkeysInPublicConversation, @@ -210,6 +208,7 @@ export class SessionConversation extends React.Component { selectedConversation, selectedConversationKey, messages, + actions, } = this.props; if (!selectedConversation || !messages) { @@ -308,6 +307,8 @@ export class SessionConversation extends React.Component { onMessageFailure={this.onMessageFailure} onLoadVoiceNoteView={this.onLoadVoiceNoteView} onExitVoiceNoteView={this.onExitVoiceNoteView} + showLeftPaneSection={actions.showLeftPaneSection} + showSettingsSection={actions.showSettingsSection} quotedMessageProps={quotedMessageProps} removeQuotedMessage={() => { void this.replyToMessage(undefined); diff --git a/ts/session/utils/Toast.tsx b/ts/session/utils/Toast.tsx index 50570251d..2a93e0970 100644 --- a/ts/session/utils/Toast.tsx +++ b/ts/session/utils/Toast.tsx @@ -35,12 +35,18 @@ export function pushToastWarning( ); } -export function pushToastInfo(id: string, title: string, description?: string) { +export function pushToastInfo( + id: string, + title: string, + description?: string, + onToastClick?: () => void +) { toast.info( ); } @@ -151,11 +157,12 @@ export function pushMessageDeleteForbidden() { ); } -export function pushAudioPermissionNeeded() { +export function pushAudioPermissionNeeded(onClicked: () => void) { pushToastInfo( 'audioPermissionNeeded', window.i18n('audioPermissionNeededTitle'), - window.i18n('audioPermissionNeeded') + window.i18n('audioPermissionNeeded'), + onClicked ); } diff --git a/ts/state/ducks/section.tsx b/ts/state/ducks/section.tsx index 28c458493..ed5d81ac9 100644 --- a/ts/state/ducks/section.tsx +++ b/ts/state/ducks/section.tsx @@ -1,26 +1,52 @@ import { SectionType } from '../../components/session/ActionsPanel'; +import { SessionSettingCategory } from '../../components/session/settings/SessionSettings'; export const FOCUS_SECTION = 'FOCUS_SECTION'; +export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION'; type FocusSectionActionType = { type: 'FOCUS_SECTION'; payload: SectionType; }; -function focusSection(section: SectionType): FocusSectionActionType { +type FocusSettingsSectionActionType = { + type: 'FOCUS_SETTINGS_SECTION'; + payload: SessionSettingCategory; +}; + +function showLeftPaneSection(section: SectionType): FocusSectionActionType { return { type: FOCUS_SECTION, payload: section, }; } +type SectionActionTypes = + | FocusSectionActionType + | FocusSettingsSectionActionType; + +function showSettingsSection( + category: SessionSettingCategory +): FocusSettingsSectionActionType { + return { + type: FOCUS_SETTINGS_SECTION, + payload: category, + }; +} + export const actions = { - focusSection, + showLeftPaneSection, + showSettingsSection, +}; + +const initialState = { + focusedSection: SectionType.Message, + focusedSettingsSection: undefined, }; -const initialState = { focusedSection: SectionType.Message }; export type SectionStateType = { focusedSection: SectionType; + focusedSettingsSection?: SessionSettingCategory; }; export const reducer = ( @@ -30,12 +56,32 @@ export const reducer = ( payload, }: { type: string; - payload: SectionType; + payload: SectionActionTypes; } ): SectionStateType => { switch (type) { case FOCUS_SECTION: - return { focusedSection: payload }; + // if we change to something else than settings, reset the focused settings section + const castedPayload = (payload as unknown) as SectionType; + + if (castedPayload !== SectionType.Settings) { + return { + focusedSection: castedPayload, + focusedSettingsSection: undefined, + }; + } + + // on click on the gear icon: show the appearance tab by default + return { + ...state, + focusedSection: payload, + focusedSettingsSection: SessionSettingCategory.Appearance, + }; + case FOCUS_SETTINGS_SECTION: + return { + ...state, + focusedSettingsSection: payload, + }; default: return state; } diff --git a/ts/state/selectors/section.ts b/ts/state/selectors/section.ts index 33a9bc66e..31f580a08 100644 --- a/ts/state/selectors/section.ts +++ b/ts/state/selectors/section.ts @@ -3,6 +3,7 @@ import { createSelector } from 'reselect'; import { StateType } from '../reducer'; import { SectionStateType } from '../ducks/section'; import { SectionType } from '../../components/session/ActionsPanel'; +import { SessionSettingCategory } from '../../components/session/settings/SessionSettings'; export const getSection = (state: StateType): SectionStateType => state.section; @@ -10,3 +11,9 @@ export const getFocusedSection = createSelector( getSection, (state: SectionStateType): SectionType => state.focusedSection ); + +export const getFocusedSettingsSection = createSelector( + getSection, + (state: SectionStateType): SessionSettingCategory | undefined => + state.focusedSettingsSection +); diff --git a/ts/state/smart/LeftPane.tsx b/ts/state/smart/LeftPane.tsx index 0768233a3..6feac3cca 100644 --- a/ts/state/smart/LeftPane.tsx +++ b/ts/state/smart/LeftPane.tsx @@ -9,7 +9,10 @@ import { getOurPrimaryConversation, } from '../selectors/conversations'; import { mapDispatchToProps } from '../actions'; -import { getFocusedSection } from '../selectors/section'; +import { + getFocusedSection, + getFocusedSettingsSection, +} from '../selectors/section'; import { getTheme } from '../selectors/theme'; // Workaround: A react component's required properties are filtering up through connect() @@ -33,6 +36,7 @@ const mapStateToProps = (state: StateType) => { unreadMessageCount: leftPaneList.unreadCount, theme: getTheme(state), focusedSection: getFocusedSection(state), + focusedSettingsSection: getFocusedSettingsSection(state), }; }; diff --git a/ts/state/smart/SessionMainPanel.tsx b/ts/state/smart/SessionMainPanel.tsx new file mode 100644 index 000000000..16a919437 --- /dev/null +++ b/ts/state/smart/SessionMainPanel.tsx @@ -0,0 +1,18 @@ +import { connect } from 'react-redux'; +import { StateType } from '../reducer'; + +import { mapDispatchToProps } from '../actions'; +import { getFocusedSettingsSection } from '../selectors/section'; +import { getTheme } from '../selectors/theme'; +import { SessionMainPanel } from '../../components/SessionMainPanel'; + +const mapStateToProps = (state: StateType) => { + return { + theme: getTheme(state), + focusedSettingsSection: getFocusedSettingsSection(state), + }; +}; + +const smart = connect(mapStateToProps, mapDispatchToProps); + +export const SmartSessionMainPanel = smart(SessionMainPanel);