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

This commit is contained in:
Audric Ackermann 2021-03-16 15:19:56 +11:00
parent f4fb56d9ed
commit eb30c7823c
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
14 changed files with 172 additions and 101 deletions

View File

@ -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"
},

View File

@ -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": {

View File

@ -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<Props> {
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<Props> {
}
private renderSettingSection() {
const { settingsCategory } = this.props;
const category = settingsCategory || SessionSettingCategory.Appearance;
const settingsCategory =
this.props.focusedSettingsSection || SessionSettingCategory.Appearance;
return (
<>
<LeftPaneSettingSection {...this.props} settingsCategory={category} />
<LeftPaneSettingSection
{...this.props}
settingsCategory={settingsCategory}
/>
</>
);
}

View File

@ -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>
);
}
}

View File

@ -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<Props, State> {
searchQuery: '',
};
this.setCategory = this.setCategory.bind(this);
this.onDeleteAccount = this.onDeleteAccount.bind(this);
}
@ -67,7 +66,7 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
)}
role="link"
onClick={() => {
this.setCategory(item.id);
this.props.showSettingsSection(item.id);
}}
>
<div>
@ -214,8 +213,4 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
},
];
}
public setCategory(category: SessionSettingCategory) {
this.props.showSessionSettingsCategory(category);
}
}

View File

@ -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<Props, State> {
export class SessionInboxView extends React.Component<any, State> {
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<Props, State> {
return <></>;
}
const { settingsCategory } = this.state;
const isSettingsView = settingsCategory !== undefined;
return (
<Provider store={this.store}>
<div className="gutter">
<div className="network-status-container" />
{this.renderLeftPane()}
</div>
{isSettingsView
? this.renderSettings()
: this.renderSessionConversation()}
<SmartSessionMainPanel />
</Provider>
);
}
private renderLeftPane() {
return (
<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>
);
return <FilteredLeftPane isExpired={this.state.isExpired} />;
}
private async setupLeftPane() {
@ -155,12 +103,4 @@ export class SessionInboxView extends React.Component<Props, State> {
this.setState({ isInitialLoadComplete: true });
}
private showSessionSettingsCategory(category: SessionSettingCategory) {
this.setState({ settingsCategory: category });
}
private showSessionViewConversation() {
this.setState({ settingsCategory: undefined });
}
}

View File

@ -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 (
<Flex container={true} alignItems="center">
// tslint:disable-next-line: use-simple-attributes
<Flex
container={true}
alignItems="center"
onClick={props?.onToastClick || noop}
>
<IconDiv>
<SessionIcon
iconType={toastIcon}

View File

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

View File

@ -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<Props, State> {
selectedConversation,
selectedConversationKey,
messages,
actions,
} = this.props;
if (!selectedConversation || !messages) {
@ -308,6 +307,8 @@ export class SessionConversation extends React.Component<Props, State> {
onMessageFailure={this.onMessageFailure}
onLoadVoiceNoteView={this.onLoadVoiceNoteView}
onExitVoiceNoteView={this.onExitVoiceNoteView}
showLeftPaneSection={actions.showLeftPaneSection}
showSettingsSection={actions.showSettingsSection}
quotedMessageProps={quotedMessageProps}
removeQuotedMessage={() => {
void this.replyToMessage(undefined);

View File

@ -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(
<SessionToast
title={title}
description={description}
type={SessionToastType.Info}
onToastClick={onToastClick}
/>
);
}
@ -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
);
}

View File

@ -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;
}

View File

@ -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
);

View File

@ -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),
};
};

View File

@ -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);