add a way to click the toast to turn on microphone to show the settings

pull/1540/head
Audric Ackermann 4 years ago
parent f4fb56d9ed
commit eb30c7823c
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -772,7 +772,7 @@
"description": "Shown if the user attempts to send an audio message without audio permssions turned on" "description": "Shown if the user attempts to send an audio message without audio permssions turned on"
}, },
"audioPermissionNeeded": { "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", "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" "androidKey": "ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone"
}, },

@ -630,7 +630,7 @@
"description": "In Android theme, shown in quote if you or someone else replies to you" "description": "In Android theme, shown in quote if you or someone else replies to you"
}, },
"audioPermissionNeeded": { "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" "description": "Shown if the user attempts to send an audio message without audio permssions turned on"
}, },
"allowAccess": { "allowAccess": {

@ -33,14 +33,15 @@ interface Props {
unreadMessageCount: number; unreadMessageCount: number;
searchResults?: SearchResultsProps; searchResults?: SearchResultsProps;
searchTerm: string; searchTerm: string;
focusedSection: SectionType; focusedSection: SectionType;
focusSection: (section: SectionType) => void; focusedSettingsSection?: SessionSettingCategory;
showLeftPaneSection: (section: SectionType) => void;
showSettingsSection: (section: SessionSettingCategory) => void;
isExpired: boolean; isExpired: boolean;
openConversationExternal: (id: string, messageId?: string) => void; openConversationExternal: (id: string, messageId?: string) => void;
showSessionSettingsCategory: (category: SessionSettingCategory) => void;
showSessionViewConversation: () => void;
settingsCategory?: SessionSettingCategory;
updateSearchTerm: (searchTerm: string) => void; updateSearchTerm: (searchTerm: string) => void;
search: (query: string, options: SearchOptions) => void; search: (query: string, options: SearchOptions) => void;
clearSearch: () => void; clearSearch: () => void;
@ -56,12 +57,7 @@ export class LeftPane extends React.Component<Props> {
public handleSectionSelected(section: SectionType) { public handleSectionSelected(section: SectionType) {
this.props.clearSearch(); this.props.clearSearch();
this.props.focusSection(section); this.props.showLeftPaneSection(section);
if (section === SectionType.Settings) {
this.props.showSessionSettingsCategory(SessionSettingCategory.Appearance);
} else {
this.props.showSessionViewConversation();
}
} }
public render(): JSX.Element { public render(): JSX.Element {
@ -146,13 +142,14 @@ export class LeftPane extends React.Component<Props> {
} }
private renderSettingSection() { private renderSettingSection() {
const { settingsCategory } = this.props; const settingsCategory =
this.props.focusedSettingsSection || SessionSettingCategory.Appearance;
const category = settingsCategory || SessionSettingCategory.Appearance;
return ( return (
<> <>
<LeftPaneSettingSection {...this.props} settingsCategory={category} /> <LeftPaneSettingSection
{...this.props}
settingsCategory={settingsCategory}
/>
</> </>
); );
} }

@ -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<Props> {
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 <FilteredSettingsView category={category} />;
}
private renderSessionConversation() {
return (
<div className="session-conversation">
<SmartSessionConversation />
</div>
);
}
}

@ -18,7 +18,7 @@ import { deleteAccount } from '../../util/accountManager';
interface Props { interface Props {
settingsCategory: SessionSettingCategory; settingsCategory: SessionSettingCategory;
showSessionSettingsCategory: (category: SessionSettingCategory) => void; showSettingsSection: (category: SessionSettingCategory) => void;
theme: DefaultTheme; theme: DefaultTheme;
} }
@ -34,7 +34,6 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
searchQuery: '', searchQuery: '',
}; };
this.setCategory = this.setCategory.bind(this);
this.onDeleteAccount = this.onDeleteAccount.bind(this); this.onDeleteAccount = this.onDeleteAccount.bind(this);
} }
@ -67,7 +66,7 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
)} )}
role="link" role="link"
onClick={() => { onClick={() => {
this.setCategory(item.id); this.props.showSettingsSection(item.id);
}} }}
> >
<div> <div>
@ -214,8 +213,4 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
}, },
]; ];
} }
public setCategory(category: SessionSettingCategory) {
this.props.showSessionSettingsCategory(category);
}
} }

@ -1,59 +1,34 @@
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { getMessageById } from '../../data/data';
import { ConversationModel } from '../../models/conversation'; import { ConversationModel } from '../../models/conversation';
import { MessageModel } from '../../models/message';
import { getMessageQueue } from '../../session';
import { ConversationController } from '../../session/conversations'; 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 { UserUtils } from '../../session/utils';
import { createStore } from '../../state/createStore'; import { createStore } from '../../state/createStore';
import { actions as conversationActions } from '../../state/ducks/conversations'; import { actions as conversationActions } from '../../state/ducks/conversations';
import { actions as userActions } from '../../state/ducks/user';
import { SmartLeftPane } from '../../state/smart/LeftPane'; import { SmartLeftPane } from '../../state/smart/LeftPane';
import { SmartSessionConversation } from '../../state/smart/SessionConversation'; import { SmartSessionMainPanel } from '../../state/smart/SessionMainPanel';
import { makeLookup } from '../../util'; import { makeLookup } from '../../util';
import {
SessionSettingCategory,
SmartSettingsView,
} from './settings/SessionSettings';
// Workaround: A react component's required properties are filtering up through connect() // Workaround: A react component's required properties are filtering up through connect()
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
const FilteredLeftPane = SmartLeftPane as any; const FilteredLeftPane = SmartLeftPane as any;
const FilteredSettingsView = SmartSettingsView as any;
type Props = {
focusedSection: number;
};
type State = { type State = {
isInitialLoadComplete: boolean; isInitialLoadComplete: boolean;
settingsCategory?: SessionSettingCategory;
isExpired: boolean; isExpired: boolean;
}; };
export class SessionInboxView extends React.Component<Props, State> { export class SessionInboxView extends React.Component<any, State> {
private store: any; private store: any;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
isInitialLoadComplete: false, isInitialLoadComplete: false,
settingsCategory: undefined,
isExpired: false, isExpired: false,
}; };
this.showSessionSettingsCategory = this.showSessionSettingsCategory.bind(
this
);
this.showSessionViewConversation = this.showSessionViewConversation.bind(
this
);
void this.setupLeftPane(); void this.setupLeftPane();
// not reactified yet. this is a callback called once we were able to check for expiration of this Session version // 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<Props, State> {
return <></>; return <></>;
} }
const { settingsCategory } = this.state;
const isSettingsView = settingsCategory !== undefined;
return ( return (
<Provider store={this.store}> <Provider store={this.store}>
<div className="gutter"> <div className="gutter">
<div className="network-status-container" /> <div className="network-status-container" />
{this.renderLeftPane()} {this.renderLeftPane()}
</div> </div>
{isSettingsView <SmartSessionMainPanel />
? this.renderSettings()
: this.renderSessionConversation()}
</Provider> </Provider>
); );
} }
private renderLeftPane() { private renderLeftPane() {
return ( return <FilteredLeftPane isExpired={this.state.isExpired} />;
<FilteredLeftPane
showSessionSettingsCategory={this.showSessionSettingsCategory}
showSessionViewConversation={this.showSessionViewConversation}
settingsCategory={this.state.settingsCategory}
isExpired={this.state.isExpired}
/>
);
}
private renderSettings() {
const category =
this.state.settingsCategory || SessionSettingCategory.Appearance;
return <FilteredSettingsView category={category} />;
}
private renderSessionConversation() {
return (
<div className="session-conversation">
<SmartSessionConversation />
</div>
);
} }
private async setupLeftPane() { private async setupLeftPane() {
@ -155,12 +103,4 @@ export class SessionInboxView extends React.Component<Props, State> {
this.setState({ isInitialLoadComplete: true }); this.setState({ isInitialLoadComplete: true });
} }
private showSessionSettingsCategory(category: SessionSettingCategory) {
this.setState({ settingsCategory: category });
}
private showSessionViewConversation() {
this.setState({ settingsCategory: undefined });
}
} }

@ -3,6 +3,7 @@ import React, { useContext } from 'react';
import { SessionIcon, SessionIconSize, SessionIconType } from './icon/'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon/';
import { Flex } from './Flex'; import { Flex } from './Flex';
import styled, { ThemeContext } from 'styled-components'; import styled, { ThemeContext } from 'styled-components';
import { noop } from 'lodash';
export enum SessionToastType { export enum SessionToastType {
Info = 'info', Info = 'info',
@ -18,6 +19,7 @@ type Props = {
icon?: SessionIconType; icon?: SessionIconType;
description?: string; description?: string;
closeToast?: any; closeToast?: any;
onToastClick?: () => void;
}; };
const TitleDiv = styled.div` const TitleDiv = styled.div`
@ -74,7 +76,12 @@ export const SessionToast = (props: Props) => {
} }
return ( return (
<Flex container={true} alignItems="center"> // tslint:disable-next-line: use-simple-attributes
<Flex
container={true}
alignItems="center"
onClick={props?.onToastClick || noop}
>
<IconDiv> <IconDiv>
<SessionIcon <SessionIcon
iconType={toastIcon} iconType={toastIcon}

@ -31,6 +31,8 @@ import { ConversationController } from '../../../session/conversations';
import { ConversationType } from '../../../state/ducks/conversations'; import { ConversationType } from '../../../state/ducks/conversations';
import { SessionMemberListItem } from '../SessionMemberListItem'; import { SessionMemberListItem } from '../SessionMemberListItem';
import autoBind from 'auto-bind'; import autoBind from 'auto-bind';
import { SectionType } from '../ActionsPanel';
import { SessionSettingCategory } from '../settings/SessionSettings';
export interface ReplyingToMessageProps { export interface ReplyingToMessageProps {
convoId: string; convoId: string;
@ -78,6 +80,8 @@ interface Props {
clearAttachments: () => any; clearAttachments: () => any;
removeAttachment: (toRemove: AttachmentType) => void; removeAttachment: (toRemove: AttachmentType) => void;
onChoseAttachments: (newAttachments: Array<File>) => void; onChoseAttachments: (newAttachments: Array<File>) => void;
showLeftPaneSection: (section: SectionType) => void;
showSettingsSection: (category: SessionSettingCategory) => void;
theme: DefaultTheme; theme: DefaultTheme;
} }
@ -931,7 +935,10 @@ export class SessionCompositionBox extends React.Component<Props, State> {
return; return;
} }
ToastUtils.pushAudioPermissionNeeded(); ToastUtils.pushAudioPermissionNeeded(() => {
this.props.showLeftPaneSection(SectionType.Settings);
this.props.showSettingsSection(SessionSettingCategory.Privacy);
});
} }
private onExitVoiceNoteView() { private onExitVoiceNoteView() {

@ -29,8 +29,6 @@ import { MessageView } from '../../MainViewController';
import { pushUnblockToSend } from '../../../session/utils/Toast'; import { pushUnblockToSend } from '../../../session/utils/Toast';
import { MessageDetail } from '../../conversation/MessageDetail'; import { MessageDetail } from '../../conversation/MessageDetail';
import { ConversationController } from '../../../session/conversations'; import { ConversationController } from '../../../session/conversations';
import { PubKey } from '../../../session/types';
import { MessageModel } from '../../../models/message';
import { import {
getMessageById, getMessageById,
getPubkeysInPublicConversation, getPubkeysInPublicConversation,
@ -210,6 +208,7 @@ export class SessionConversation extends React.Component<Props, State> {
selectedConversation, selectedConversation,
selectedConversationKey, selectedConversationKey,
messages, messages,
actions,
} = this.props; } = this.props;
if (!selectedConversation || !messages) { if (!selectedConversation || !messages) {
@ -308,6 +307,8 @@ export class SessionConversation extends React.Component<Props, State> {
onMessageFailure={this.onMessageFailure} onMessageFailure={this.onMessageFailure}
onLoadVoiceNoteView={this.onLoadVoiceNoteView} onLoadVoiceNoteView={this.onLoadVoiceNoteView}
onExitVoiceNoteView={this.onExitVoiceNoteView} onExitVoiceNoteView={this.onExitVoiceNoteView}
showLeftPaneSection={actions.showLeftPaneSection}
showSettingsSection={actions.showSettingsSection}
quotedMessageProps={quotedMessageProps} quotedMessageProps={quotedMessageProps}
removeQuotedMessage={() => { removeQuotedMessage={() => {
void this.replyToMessage(undefined); void this.replyToMessage(undefined);

@ -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( toast.info(
<SessionToast <SessionToast
title={title} title={title}
description={description} description={description}
type={SessionToastType.Info} type={SessionToastType.Info}
onToastClick={onToastClick}
/> />
); );
} }
@ -151,11 +157,12 @@ export function pushMessageDeleteForbidden() {
); );
} }
export function pushAudioPermissionNeeded() { export function pushAudioPermissionNeeded(onClicked: () => void) {
pushToastInfo( pushToastInfo(
'audioPermissionNeeded', 'audioPermissionNeeded',
window.i18n('audioPermissionNeededTitle'), window.i18n('audioPermissionNeededTitle'),
window.i18n('audioPermissionNeeded') window.i18n('audioPermissionNeeded'),
onClicked
); );
} }

@ -1,26 +1,52 @@
import { SectionType } from '../../components/session/ActionsPanel'; import { SectionType } from '../../components/session/ActionsPanel';
import { SessionSettingCategory } from '../../components/session/settings/SessionSettings';
export const FOCUS_SECTION = 'FOCUS_SECTION'; export const FOCUS_SECTION = 'FOCUS_SECTION';
export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION';
type FocusSectionActionType = { type FocusSectionActionType = {
type: 'FOCUS_SECTION'; type: 'FOCUS_SECTION';
payload: SectionType; payload: SectionType;
}; };
function focusSection(section: SectionType): FocusSectionActionType { type FocusSettingsSectionActionType = {
type: 'FOCUS_SETTINGS_SECTION';
payload: SessionSettingCategory;
};
function showLeftPaneSection(section: SectionType): FocusSectionActionType {
return { return {
type: FOCUS_SECTION, type: FOCUS_SECTION,
payload: section, payload: section,
}; };
} }
type SectionActionTypes =
| FocusSectionActionType
| FocusSettingsSectionActionType;
function showSettingsSection(
category: SessionSettingCategory
): FocusSettingsSectionActionType {
return {
type: FOCUS_SETTINGS_SECTION,
payload: category,
};
}
export const actions = { export const actions = {
focusSection, showLeftPaneSection,
showSettingsSection,
};
const initialState = {
focusedSection: SectionType.Message,
focusedSettingsSection: undefined,
}; };
const initialState = { focusedSection: SectionType.Message };
export type SectionStateType = { export type SectionStateType = {
focusedSection: SectionType; focusedSection: SectionType;
focusedSettingsSection?: SessionSettingCategory;
}; };
export const reducer = ( export const reducer = (
@ -30,12 +56,32 @@ export const reducer = (
payload, payload,
}: { }: {
type: string; type: string;
payload: SectionType; payload: SectionActionTypes;
} }
): SectionStateType => { ): SectionStateType => {
switch (type) { switch (type) {
case FOCUS_SECTION: 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: default:
return state; return state;
} }

@ -3,6 +3,7 @@ import { createSelector } from 'reselect';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { SectionStateType } from '../ducks/section'; import { SectionStateType } from '../ducks/section';
import { SectionType } from '../../components/session/ActionsPanel'; import { SectionType } from '../../components/session/ActionsPanel';
import { SessionSettingCategory } from '../../components/session/settings/SessionSettings';
export const getSection = (state: StateType): SectionStateType => state.section; export const getSection = (state: StateType): SectionStateType => state.section;
@ -10,3 +11,9 @@ export const getFocusedSection = createSelector(
getSection, getSection,
(state: SectionStateType): SectionType => state.focusedSection (state: SectionStateType): SectionType => state.focusedSection
); );
export const getFocusedSettingsSection = createSelector(
getSection,
(state: SectionStateType): SessionSettingCategory | undefined =>
state.focusedSettingsSection
);

@ -9,7 +9,10 @@ import {
getOurPrimaryConversation, getOurPrimaryConversation,
} from '../selectors/conversations'; } from '../selectors/conversations';
import { mapDispatchToProps } from '../actions'; import { mapDispatchToProps } from '../actions';
import { getFocusedSection } from '../selectors/section'; import {
getFocusedSection,
getFocusedSettingsSection,
} from '../selectors/section';
import { getTheme } from '../selectors/theme'; import { getTheme } from '../selectors/theme';
// Workaround: A react component's required properties are filtering up through connect() // Workaround: A react component's required properties are filtering up through connect()
@ -33,6 +36,7 @@ const mapStateToProps = (state: StateType) => {
unreadMessageCount: leftPaneList.unreadCount, unreadMessageCount: leftPaneList.unreadCount,
theme: getTheme(state), theme: getTheme(state),
focusedSection: getFocusedSection(state), focusedSection: getFocusedSection(state),
focusedSettingsSection: getFocusedSettingsSection(state),
}; };
}; };

@ -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);
Loading…
Cancel
Save