add ourPrimary convo to redux and update the actionPanel with it

pull/1381/head
Audric Ackermann 4 years ago
parent c203303c71
commit 1503d83f3a
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -613,7 +613,7 @@
confirmDialog.render();
};
window.showEditProfileDialog = async callback => {
window.showEditProfileDialog = async () => {
const ourNumber = window.storage.get('primaryDevicePubKey');
const conversation = await ConversationController.getOrCreateAndWait(
ourNumber,
@ -642,7 +642,6 @@
if (appView) {
appView.showEditProfileDialog({
callback,
profileName: displayName,
pubkey: ourNumber,
avatarPath,
@ -706,26 +705,35 @@
url,
});
newAvatarPath = upgraded.path;
// Replace our temporary image with the attachment pointer from the server:
conversation.set('avatar', null);
conversation.setLokiProfile({
displayName: newName,
avatar: newAvatarPath,
});
} else {
// do not update the avatar if it did not change
conversation.setLokiProfile({
displayName: newName,
});
}
// Replace our temporary image with the attachment pointer from the server:
conversation.set('avatar', null);
conversation.setLokiProfile({
displayName: newName,
avatar: newAvatarPath,
});
conversation.commit();
// inform all your registered public servers
// could put load on all the servers
// if they just keep changing their names without sending messages
// so we could disable this here
// or least it enable for the quickest response
window.lokiPublicChatAPI.setProfileName(newName);
window
.getConversations()
.filter(convo => convo.isPublic() && !convo.isRss())
.forEach(convo =>
convo.trigger('ourAvatarChanged', { url, profileKey })
);
if (avatar) {
window
.getConversations()
.filter(convo => convo.isPublic() && !convo.isRss())
.forEach(convo =>
convo.trigger('ourAvatarChanged', { url, profileKey })
);
}
},
});
}
@ -938,13 +946,6 @@
});
Whisper.events.on('onShowUserDetails', async ({ userPubKey }) => {
const isMe = userPubKey === textsecure.storage.user.getNumber();
if (isMe) {
Whisper.events.trigger('onEditProfile');
return;
}
const conversation = await ConversationController.getOrCreateAndWait(
userPubKey,
'private'

@ -1895,8 +1895,11 @@
await this.commit();
}
// if set to null, it will show a placeholder with color and first letter
await this.setProfileAvatar({ path: newProfile.avatar });
// a user cannot remove an avatar. Only change it
// if you change this behavior, double check all setLokiProfile calls (especially the one in EditProfileDialog)
if (newProfile.avatar) {
await this.setProfileAvatar({ path: newProfile.avatar });
}
await this.updateProfileName();
},
@ -2386,7 +2389,6 @@
getAvatarPath() {
const avatar = this.get('avatar') || this.get('profileAvatar');
if (typeof avatar === 'string') {
return avatar;
}

@ -8,10 +8,9 @@
Whisper.EditProfileDialogView = Whisper.View.extend({
className: 'loki-dialog modal',
initialize({ profileName, avatarPath, pubkey, onOk, callback }) {
initialize({ profileName, avatarPath, pubkey, onOk }) {
this.close = this.close.bind(this);
this.callback = callback;
this.profileName = profileName;
this.pubkey = pubkey;
this.avatarPath = avatarPath;
@ -25,7 +24,6 @@
className: 'edit-profile-dialog',
Component: window.Signal.Components.EditProfileDialog,
props: {
callback: this.callback,
onOk: this.onOk,
onClose: this.close,
profileName: this.profileName,

@ -26,7 +26,6 @@ declare global {
}
interface Props {
callback: any;
i18n: any;
profileName: string;
avatarPath: string;
@ -318,17 +317,10 @@ export class EditProfileDialog extends React.Component<Props, State> {
this.props.onOk(newName, avatar);
this.setState(
{
mode: 'default',
setProfileName: this.state.profileName,
},
() => {
// Update settings in dialog complete;
// now callback to reloadactions panel avatar
this.props.callback(this.state.avatar);
}
);
this.setState({
mode: 'default',
setProfileName: this.state.profileName,
});
}
private closeDialog() {

@ -29,6 +29,7 @@ export type RowRendererParamsType = {
};
interface Props {
ourPrimaryConversation: ConversationType;
conversations: Array<ConversationListItemPropsType>;
contacts: Array<ConversationType>;
@ -90,14 +91,15 @@ export class LeftPane extends React.Component<Props> {
}
public render(): JSX.Element {
const ourPrimaryConversation = this.props.ourPrimaryConversation;
return (
<SessionTheme theme={this.props.theme}>
<div className="module-left-pane-session">
<ActionsPanel
selectedSection={this.props.focusedSection}
onSectionSelected={this.handleSectionSelected}
conversations={this.props.conversations}
unreadMessageCount={this.props.unreadMessageCount}
ourPrimaryConversation={ourPrimaryConversation}
/>
<div className="module-left-pane">{this.renderSection()}</div>
</div>

@ -1,12 +1,13 @@
import React from 'react';
import { connect, useDispatch } from 'react-redux';
import { connect } from 'react-redux';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { Avatar } from '../Avatar';
import { PropsData as ConversationListItemPropsType } from '../ConversationListItem';
import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { APPLY_THEME } from '../../state/ducks/theme';
import { removeItemById } from '../../../js/modules/data';
import { darkTheme, lightTheme } from '../../state/ducks/SessionTheme';
import { SessionToastContainer } from './SessionToastContainer';
import { mapDispatchToProps } from '../../state/actions';
import { ConversationType } from '../../state/ducks/conversations';
import { noop } from 'lodash';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
export enum SectionType {
@ -18,104 +19,22 @@ export enum SectionType {
Moon,
}
interface State {
avatarPath: string;
}
interface Props {
onSectionSelected: any;
selectedSection: SectionType;
conversations: Array<ConversationListItemPropsType> | undefined;
unreadMessageCount: number;
dispatch?: any;
ourPrimaryConversation: ConversationType;
applyTheme?: any;
}
class ActionsPanelPrivate extends React.Component<Props, State> {
private ourConversation: any;
class ActionsPanelPrivate extends React.Component<Props> {
constructor(props: Props) {
super(props);
this.state = {
avatarPath: '',
};
this.editProfileHandle = this.editProfileHandle.bind(this);
this.refreshAvatarCallback = this.refreshAvatarCallback.bind(this);
}
public componentDidMount() {
// tslint:disable-next-line: no-backbone-get-set-outside-model
const ourNumber = window.storage.get('primaryDevicePubKey');
void window.ConversationController.getOrCreateAndWait(
ourNumber,
'private'
).then((conversation: any) => {
this.setState({
avatarPath: conversation.getAvatarPath(),
});
// When our primary device updates its avatar, we will need for a message sync to know about that.
// Once we get the avatar update, we need to refresh this react component.
// So we listen to changes on our profile avatar and use the updated avatarPath (done on message received).
this.ourConversation = conversation;
this.ourConversation.on(
'change',
() => {
this.refreshAvatarCallback(this.ourConversation);
},
'refreshAvatarCallback'
);
void this.showLightThemeDialogIfNeeded();
});
}
public async showLightThemeDialogIfNeeded() {
const currentTheme = window.Events.getThemeSetting(); // defaults to light on new registration
if (currentTheme !== 'light') {
const message = 'Light Mode';
const messageSub =
'Whoops, who left the lights on?</br></br>\
Thats right, Session has a spiffy new light mode! Take the fresh new color palette for a spin its now the default mode.</br></br>\
Want to go back to the dark side? Just tap the moon symbol in the lower left corner of the app to switch modes.';
const hasSeenLightMode = await getItemById('hasSeenLightModeDialog');
if (hasSeenLightMode?.value === true) {
// if hasSeen is set and true, we have nothing to do
return;
}
// force light them right now, then ask for permission
await window.Events.setThemeSetting('light');
window.confirmationDialog({
message,
messageSub,
resolve: async () => {
const data = {
id: 'hasSeenLightModeDialog',
value: true,
};
void createOrUpdateItem(data);
},
okTheme: 'default primary',
hideCancel: true,
sessionIcon: SessionIconType.Sun,
iconSize: SessionIconSize.Max,
});
}
}
public refreshAvatarCallback(conversation: any) {
if (conversation.changed?.profileAvatar) {
this.setState({
avatarPath: conversation.getAvatarPath(),
});
}
}
public componentWillUnmount() {
if (this.ourConversation) {
this.ourConversation.off('change', null, 'refreshAvatarCallback');
}
// we consider people had the time to upgrade, so remove this id from the db
// it was used to display a dialog when we added the light mode auto-enabled
void removeItemById('hasSeenLightModeDialog');
}
public Section = ({
@ -143,10 +62,7 @@ class ActionsPanelPrivate extends React.Component<Props, State> {
const newThemeObject =
updatedTheme === 'dark' ? darkTheme : lightTheme;
this.props.dispatch({
type: APPLY_THEME,
payload: newThemeObject,
});
this.props.applyTheme(newThemeObject);
} else {
onSelect(type);
}
@ -204,11 +120,7 @@ class ActionsPanelPrivate extends React.Component<Props, State> {
};
public editProfileHandle() {
window.showEditProfileDialog((avatar: any) => {
this.setState({
avatarPath: avatar,
});
});
window.showEditProfileDialog(noop);
}
public render(): JSX.Element {
@ -224,7 +136,7 @@ class ActionsPanelPrivate extends React.Component<Props, State> {
<div className="module-left-pane__sections-container">
<this.Section
type={SectionType.Profile}
avatarPath={this.state.avatarPath}
avatarPath={this.props.ourPrimaryConversation.avatarPath}
isSelected={isProfilePageSelected}
onSelect={this.handleSectionSelect}
/>
@ -260,4 +172,6 @@ class ActionsPanelPrivate extends React.Component<Props, State> {
};
}
export const ActionsPanel = connect()(ActionsPanelPrivate);
const smart = connect(null, mapDispatchToProps);
export const ActionsPanel = smart(ActionsPanelPrivate);

@ -15,7 +15,6 @@ import {
// 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 FilteredSessionConversation = SmartSessionConversation as any;
type Props = {
focusedSection: number;
@ -27,8 +26,6 @@ type State = {
isExpired: boolean;
};
// tslint:disable: react-a11y-img-has-alt
export class SessionInboxView extends React.Component<Props, State> {
private store: any;
@ -111,7 +108,7 @@ export class SessionInboxView extends React.Component<Props, State> {
private renderSessionConversation() {
return (
<div className="session-conversation">
<FilteredSessionConversation />
<SmartSessionConversation />
</div>
);
}

@ -43,10 +43,7 @@ interface Props {
onDeleteSelectedMessages: () => Promise<void>;
}
export class SessionMessagesList extends React.Component<
Props,
State
> {
export class SessionMessagesList extends React.Component<Props, State> {
private readonly messagesEndRef: React.RefObject<HTMLDivElement>;
private readonly messageContainerRef: React.RefObject<any>;

@ -4,15 +4,19 @@ import { actions as search } from './ducks/search';
import { actions as conversations } from './ducks/conversations';
import { actions as user } from './ducks/user';
import { actions as sections } from './ducks/section';
const actions = {
...search,
...conversations,
...user,
// ...messages,
...sections,
};
import { actions as theme } from './ducks/theme';
export function mapDispatchToProps(dispatch: Dispatch): Object {
return { ...bindActionCreators(actions, dispatch) };
return {
...bindActionCreators(
{
...search,
...conversations,
...user,
...theme,
...sections,
},
dispatch
),
};
}

@ -75,6 +75,7 @@ export type ConversationType = {
isBlocked: boolean;
isKickedFromGroup: boolean;
leftGroup: boolean;
avatarPath?: string; // absolute filepath to the avatar
};
export type ConversationLookupType = {
[key: string]: ConversationType;

@ -2,7 +2,6 @@ import { omit, reject } from 'lodash';
import { normalize } from '../../types/PhoneNumber';
import { AdvancedSearchOptions, SearchOptions } from '../../types/Search';
import { trigger } from '../../shims/events';
import { getMessageModel } from '../../shims/Whisper';
import { cleanSearchTerm } from '../../util/cleanSearchTerm';
import { searchConversations, searchMessages } from '../../../js/modules/data';

@ -2,12 +2,17 @@ import { SectionType } from '../../components/session/ActionsPanel';
export const FOCUS_SECTION = 'FOCUS_SECTION';
const focusSection = (section: SectionType) => {
type FocusSectionActionType = {
type: 'FOCUS_SECTION';
payload: SectionType;
};
function focusSection(section: SectionType): FocusSectionActionType {
return {
type: FOCUS_SECTION,
payload: section,
};
};
}
export const actions = {
focusSection,

@ -29,3 +29,7 @@ export const reducer = (
return state;
}
};
export const actions = {
applyTheme,
};

@ -30,6 +30,12 @@ export const getSelectedConversation = createSelector(
}
);
export const getOurPrimaryConversation = createSelector(
getConversations,
(state: ConversationsStateType): ConversationType =>
state.conversationLookup[window.storage.get('primaryDevicePubKey')]
);
function getConversationTitle(
conversation: ConversationType,
options: { i18n: LocalizerType; ourRegionCode: string }

@ -1,5 +1,4 @@
import { connect } from 'react-redux';
import { mapDispatchToProps } from '../actions';
import { LeftPane } from '../../components/LeftPane';
import { StateType } from '../reducer';
@ -10,7 +9,11 @@ import {
getRegionCode,
getUserNumber,
} from '../selectors/user';
import { getLeftPaneLists } from '../selectors/conversations';
import {
getLeftPaneLists,
getOurPrimaryConversation,
} from '../selectors/conversations';
import { mapDispatchToProps } from '../actions';
// Workaround: A react component's required properties are filtering up through connect()
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
@ -23,6 +26,7 @@ const mapStateToProps = (state: StateType) => {
const searchResults = showSearch ? getSearchResults(state) : undefined;
return {
...lists,
ourPrimaryConversation: getOurPrimaryConversation(state), // used in actionPanel
searchTerm: getQuery(state),
regionCode: getRegionCode(state),
ourNumber: getUserNumber(state),

Loading…
Cancel
Save