Moved edit profile saving into edit profile component file.

pull/1665/head
Warrick Corfe-Tan 4 years ago
parent 72d31bf158
commit ec39f7ae9b

@ -393,239 +393,6 @@
appView.showResetSessionIdDialog();
};
window.showEditProfileDialog = async () => {
const ourNumber = window.storage.get('primaryDevicePubKey');
const conversation = await window
.getConversationController()
.getOrCreateAndWait(ourNumber, 'private');
const readFile = attachment =>
new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = e => {
const data = e.target.result;
resolve({
...attachment,
data,
size: data.byteLength,
});
};
fileReader.onerror = reject;
fileReader.onabort = reject;
fileReader.readAsArrayBuffer(attachment.file);
});
const avatarPath = conversation.getAvatarPath();
const profile = conversation.getLokiProfile();
const displayName = profile && profile.displayName;
if (appView) {
appView.showEditProfileDialog({
profileName: displayName,
pubkey: ourNumber,
avatarPath,
onOk: async (newName, avatar) => {
let newAvatarPath = '';
let url = null;
let profileKey = null;
if (avatar) {
const data = await readFile({ file: avatar });
// Ensure that this file is either small enough or is resized to meet our
// requirements for attachments
try {
const withBlob = await winvadow.Signal.Util.AttachmentUtil.autoScale(
{
contentType: avatar.type,
file: new Blob([data.data], {
type: avatar.contentType,
}),
},
{
maxSide: 640,
maxSize: 1000 * 1024,
}
);
const dataResized = await window.Signal.Types.Attachment.arrayBufferFromFile(
withBlob.file
);
// For simplicity we use the same attachment pointer that would send to
// others, which means we need to wait for the database response.
// To avoid the wait, we create a temporary url for the local image
// and use it until we the the response from the server
const tempUrl = window.URL.createObjectURL(avatar);
conversation.setLokiProfile({ displayName: newName });
conversation.set('avatar', tempUrl);
// Encrypt with a new key every time
profileKey = libsignal.crypto.getRandomBytes(32);
const encryptedData = await textsecure.crypto.encryptProfile(
dataResized,
profileKey
);
const avatarPointer = await libsession.Utils.AttachmentUtils.uploadAvatarV1({
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
});
({ url } = avatarPointer);
storage.put('profileKey', profileKey);
conversation.set('avatarPointer', url);
const upgraded = await Signal.Migrations.processNewAttachment({
isRaw: true,
data: data.data,
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,
});
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
} catch (error) {
window.log.error(
'showEditProfileDialog Error ensuring that image is properly sized:',
error && error.stack ? error.stack : error
);
}
} else {
// do not update the avatar if it did not change
conversation.setLokiProfile({
displayName: newName,
});
// might be good to not trigger a sync if the name did not change
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}
// 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);
if (avatar) {
window
.getConversationController()
.getConversations()
.filter(convo => convo.isPublic())
.forEach(convo => convo.trigger('ourAvatarChanged', { url, profileKey }));
}
},
});
}
};
window.onProfileEditOk = async (newName, avatar) => {
let newAvatarPath = '';
let url = null;
let profileKey = null;
if (avatar) {
const data = await readFile({ file: avatar });
// Ensure that this file is either small enough or is resized to meet our
// requirements for attachments
try {
const withBlob = await winvadow.Signal.Util.AttachmentUtil.autoScale(
{
contentType: avatar.type,
file: new Blob([data.data], {
type: avatar.contentType,
}),
},
{
maxSide: 640,
maxSize: 1000 * 1024,
}
);
const dataResized = await window.Signal.Types.Attachment.arrayBufferFromFile(
withBlob.file
);
// For simplicity we use the same attachment pointer that would send to
// others, which means we need to wait for the database response.
// To avoid the wait, we create a temporary url for the local image
// and use it until we the the response from the server
const tempUrl = window.URL.createObjectURL(avatar);
conversation.setLokiProfile({ displayName: newName });
conversation.set('avatar', tempUrl);
// Encrypt with a new key every time
profileKey = libsignal.crypto.getRandomBytes(32);
const encryptedData = await textsecure.crypto.encryptProfile(
dataResized,
profileKey
);
const avatarPointer = await libsession.Utils.AttachmentUtils.uploadAvatarV1({
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
});
({ url } = avatarPointer);
storage.put('profileKey', profileKey);
conversation.set('avatarPointer', url);
const upgraded = await Signal.Migrations.processNewAttachment({
isRaw: true,
data: data.data,
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,
});
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
} catch (error) {
window.log.error(
'showEditProfileDialog Error ensuring that image is properly sized:',
error && error.stack ? error.stack : error
);
}
} else {
// do not update the avatar if it did not change
conversation.setLokiProfile({
displayName: newName,
});
// might be good to not trigger a sync if the name did not change
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}
// 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);
if (avatar) {
window
.getConversationController()
.getConversations()
.filter(convo => convo.isPublic())
.forEach(convo => convo.trigger('ourAvatarChanged', { url, profileKey }));
}
}
window.showOnionStatusDialog = () => {
appView.showOnionStatusDialog();

@ -11,6 +11,7 @@ export interface LokiPublicChatFactoryInterface {
): Promise<LokiPublicChannelAPI | null>;
getListOfMembers(): Promise<Array<{ authorPhoneNumber: string; authorProfileName?: string }>>;
setListOfMembers(members: Array<{ authorPhoneNumber: string; authorProfileName?: string }>);
setProfileName(name: string);
}
declare class LokiPublicChatFactoryAPI implements LokiPublicChatFactoryInterface {

@ -1,45 +1,45 @@
/* global i18n, Whisper */
// /* global i18n, Whisper */
// eslint-disable-next-line func-names
(function() {
'use strict';
// // eslint-disable-next-line func-names
// (function() {
// 'use strict';
window.Whisper = window.Whisper || {};
// window.Whisper = window.Whisper || {};
Whisper.EditProfileDialogView = Whisper.View.extend({
className: 'loki-dialog modal',
initialize({ profileName, avatarPath, pubkey, onOk, theme }) {
this.close = this.close.bind(this);
// Whisper.EditProfileDialogView = Whisper.View.extend({
// className: 'loki-dialog modal',
// initialize({ profileName, avatarPath, pubkey, onOk, theme }) {
// this.close = this.close.bind(this);
this.profileName = profileName;
this.pubkey = pubkey;
this.avatarPath = avatarPath;
this.onOk = onOk;
this.theme = theme;
// this.profileName = profileName;
// this.pubkey = pubkey;
// this.avatarPath = avatarPath;
// this.onOk = onOk;
// this.theme = theme;
this.$el.focus();
this.render();
},
render() {
this.dialogView = new Whisper.ReactWrapperView({
className: 'edit-profile-dialog',
Component: window.Signal.Components.EditProfileDialog,
props: {
onOk: this.onOk,
onClose: this.close,
profileName: this.profileName,
pubkey: this.pubkey,
avatarPath: this.avatarPath,
i18n,
theme: this.theme,
},
});
// this.$el.focus();
// this.render();
// },
// render() {
// this.dialogView = new Whisper.ReactWrapperView({
// className: 'edit-profile-dialog',
// Component: window.Signal.Components.EditProfileDialog,
// props: {
// onOk: this.onOk,
// onClose: this.close,
// profileName: this.profileName,
// pubkey: this.pubkey,
// avatarPath: this.avatarPath,
// i18n,
// theme: this.theme,
// },
// });
this.$el.append(this.dialogView.el);
return this;
},
close() {
this.remove();
},
});
})();
// this.$el.append(this.dialogView.el);
// return this;
// },
// close() {
// this.remove();
// },
// });
// })();

@ -13,4 +13,5 @@ export interface LibTextsecureCryptoInterface {
theirDigest: ArrayBuffer
): Promise<ArrayBuffer>;
decryptProfile(data: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer>;
encryptProfile(data: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer>;
}

@ -1,5 +1,5 @@
.modal {
position: absolute;
position: fixed;
top: 0;
left: 0;
height: 100%;
@ -311,42 +311,17 @@
}
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10000;
padding: 2rem;
background-color: rgba(0, 0, 0, 0.55);
.modal-content {
background-color: white;
max-height: 80%;
height: 80%;
.modal-title {
padding: 0 2.25rem;
height: 5rem;
max-height: 5rem;
text-transform: uppercase;
font-size: 1.8rem;
}
.modal-body {
height: calc(100% - 16rem);
overflow-y: scroll;
padding: 0 2rem;
}
.modal-footer {
height: 5rem;
max-height: 5rem;
padding: 0 2.25rem;
}
}
}
// .modal {
// position: fixed;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// z-index: 10000;
// padding: 2rem;
// background-color: rgba(0, 0, 0, 0.55);
// }
.onion-status-dialog {
.session-modal__header__title {

@ -1220,27 +1220,9 @@ input {
.session-icon-button {
margin: $session-margin-sm !important;
}
// svg {
// animation: glow 1s ease-in-out infinite alternate;
// }
}
}
// @keyframes glow {
// from {
// -webkit-filter: drop-shadow( 0px 0px 3px rgba(0,0,0,0));
// filter: drop-shadow( 0px 0px 3px rgba(0,0,0,0));
// }
// to {
// -webkit-filter: drop-shadow( 0px 0px 5px rgba(46, 247, 28, 0.7));
// filter: drop-shadow( 0px 0px 5px rgba(68, 236, 2, 0.7));
// color: red;
// }
// }
// }
.session-nickname-wrapper {
position: absolute;
height: 100%;

@ -9,7 +9,7 @@ import { SessionButton, SessionButtonColor, SessionButtonType } from './session/
import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon';
import { SessionModal } from './session/SessionModal';
import { PillDivider } from './session/PillDivider';
import { ToastUtils, UserUtils } from '../session/utils';
import { AttachmentUtils, SyncUtils, ToastUtils, UserUtils } from '../session/utils';
import { DefaultTheme, useTheme } from 'styled-components';
import { MAX_USERNAME_LENGTH } from './session/registration/RegistrationTabs';
import { SessionSpinner } from './session/SessionSpinner';
@ -20,6 +20,7 @@ import { useSelector } from 'react-redux';
import { getOurNumber } from '../state/selectors/user';
import { SessionWrapperModal } from './session/SessionWrapperModal';
import { times } from 'underscore';
import { AttachmentUtil, } from '../util';
interface Props {
i18n?: any;
@ -42,6 +43,7 @@ interface State {
export class EditProfileDialog extends React.Component<Props, State> {
private readonly inputEl: any;
constructor(props: any) {
super(props);
@ -134,11 +136,11 @@ export class EditProfileDialog extends React.Component<Props, State> {
<div className="edit-profile-dialog">
<SessionWrapperModal
title={i18n('editProfileModalTitle')}
onClose={this.closeDialog}
headerIconButtons={backButton}
showExitIcon={true}
theme={this.props.theme}
title={i18n('editProfileModalTitle')}
onClose={this.closeDialog}
headerIconButtons={backButton}
showExitIcon={true}
theme={this.props.theme}
>
<div className="spacer-md" />
@ -182,6 +184,8 @@ export class EditProfileDialog extends React.Component<Props, State> {
);
}
private renderProfileHeader() {
return (
<>
@ -310,6 +314,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
switch (event.key) {
case 'Enter':
if (this.state.mode === 'edit') {
// this.onClickOK();
this.onClickOK();
}
break;
@ -326,6 +331,10 @@ export class EditProfileDialog extends React.Component<Props, State> {
ToastUtils.pushCopiedToClipBoard();
}
/**
* Tidy the profile name input text and save the new profile name and avatar
* @returns
*/
private onClickOK() {
const newName = this.state.profileName.trim();
@ -346,8 +355,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
loading: true,
},
async () => {
// await this.props.onOk(newName, avatar);
await window.commitProfileEdits(newName, avatar);
await this.commitProfileEdits(newName, avatar);
this.setState({
loading: false,
@ -363,4 +371,107 @@ export class EditProfileDialog extends React.Component<Props, State> {
this.props.onClose();
}
private async commitProfileEdits(newName: string, avatar: any) {
let ourNumber = window.storage.get('primaryDevicePubKey');
const conversation = await window.getConversationController().getOrCreateAndWait(ourNumber, ConversationTypeEnum.PRIVATE);
let newAvatarPath = '';
let url: any = null;
let profileKey: any = null;
if (avatar) {
const data = await AttachmentUtil.readFile({ file: avatar });
// Ensure that this file is either small enough or is resized to meet our
// requirements for attachments
try {
const withBlob = await AttachmentUtil.autoScale(
{
contentType: avatar.type,
file: new Blob([data.data], {
type: avatar.contentType,
}),
},
{
maxSide: 640,
maxSize: 1000 * 1024,
}
);
const dataResized = await window.Signal.Types.Attachment.arrayBufferFromFile(
withBlob.file
);
// For simplicity we use the same attachment pointer that would send to
// others, which means we need to wait for the database response.
// To avoid the wait, we create a temporary url for the local image
// and use it until we the the response from the server
const tempUrl = window.URL.createObjectURL(avatar);
conversation.setLokiProfile({ displayName: newName });
conversation.set('avatar', tempUrl);
// Encrypt with a new key every time
profileKey = window.libsignal.crypto.getRandomBytes(32);
const encryptedData = await window.textsecure.crypto.encryptProfile(
dataResized,
profileKey
);
const avatarPointer = await AttachmentUtils.uploadAvatarV1({
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
});
url = avatarPointer ? avatarPointer.url : null;
window.storage.put('profileKey', profileKey);
conversation.set('avatarPointer', url);
const upgraded = await window.Signal.Migrations.processNewAttachment({
isRaw: true,
data: data.data,
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,
});
await conversation.commit();
UserUtils.setLastProfileUpdateTimestamp(Date.now());
await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
} catch (error) {
window.log.error(
'showEditProfileDialog Error ensuring that image is properly sized:',
error && error.stack ? error.stack : error
);
}
} else {
// do not update the avatar if it did not change
conversation.setLokiProfile({
displayName: newName,
});
// might be good to not trigger a sync if the name did not change
await conversation.commit();
UserUtils.setLastProfileUpdateTimestamp(Date.now());
await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}
// 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);
if (avatar) {
window
.getConversationController()
.getConversations()
.filter(convo => convo.isPublic())
.forEach(convo => convo.trigger('ourAvatarChanged', { url, profileKey }));
}
}
}

@ -2,21 +2,25 @@ import React, { useState } from 'react';
import { ConversationController } from '../../session/conversations/ConversationController';
import { SessionModal } from './SessionModal';
import { SessionButton } from './SessionButton';
import { DefaultTheme, withTheme } from 'styled-components';
import { DefaultTheme, withTheme, useTheme } from 'styled-components';
import _ from 'lodash';
import { SessionWrapperModal } from './SessionWrapperModal';
type Props = {
onClickOk: any;
onClickClose: any;
theme: DefaultTheme;
convoId: string;
onClickOk?: any;
onClickClose?: any;
theme?: DefaultTheme;
conversationId?: string;
};
const SessionNicknameInner = (props: Props) => {
const { onClickOk, onClickClose, convoId, theme } = props;
const { onClickOk, onClickClose, conversationId } = props;
let { theme } = props;
const [nickname, setNickname] = useState('');
theme = theme ? theme : useTheme();
/**
* Changes the state of nickname variable. If enter is pressed, saves the current
* entered nickname value as the nickname.
@ -34,41 +38,58 @@ const SessionNicknameInner = (props: Props) => {
* Saves the currently entered nickname.
*/
const saveNickname = async () => {
if (!convoId) {
return;
if (!conversationId) {
throw "Cant save withou conversation id"
// return;
}
const conversation = ConversationController.getInstance().get(conversationId);
if (onClickOk) {
onClickOk(nickname);
}
const convo = ConversationController.getInstance().get(convoId);
onClickOk(nickname);
await convo.setNickname(nickname);
await conversation.setNickname(nickname);
onClickClose();
};
return (
<SessionModal
title={window.i18n('changeNickname')}
onClose={onClickClose}
showExitIcon={false}
showHeader={true}
theme={theme}
>
<div className="session-modal__centered">
<span className="subtle">{window.i18n('changeNicknameMessage')}</span>
<div className="spacer-lg" />
</div>
// <SessionModal
// title={window.i18n('changeNickname')}
// onClose={onClickClose}
// showExitIcon={false}
// showHeader={true}
// theme={theme}
// >
// TODO: Implement showHeader option for modal
<SessionWrapperModal
title={window.i18n('changeNickname')}
onClose={onClickClose}
showExitIcon={false}
// showHeader={true}
theme={theme}
>
<div className="session-modal__centered">
<span className="subtle">{window.i18n('changeNicknameMessage')}</span>
<div className="spacer-lg" />
</div>
<input
autoFocus
type="nickname"
id="nickname-modal-input"
placeholder={window.i18n('nicknamePlaceholder')}
onKeyUp={e => {
void onNicknameInput(_.cloneDeep(e));
}}
/>
<div className="session-modal__button-group">
<SessionButton text={window.i18n('ok')} onClick={saveNickname} />
<SessionButton text={window.i18n('cancel')} onClick={onClickClose} />
</div>
</SessionWrapperModal>
<input
type="nickname"
id="nickname-modal-input"
placeholder={window.i18n('nicknamePlaceholder')}
onKeyUp={e => {
void onNicknameInput(_.cloneDeep(e));
}}
/>
<div className="session-modal__button-group">
<SessionButton text={window.i18n('ok')} onClick={saveNickname} />
<SessionButton text={window.i18n('cancel')} onClick={onClickClose} />
</div>
</SessionModal>
);
};

@ -74,7 +74,8 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
};
return (
<div className="loki-dialog session-confirm-wrapper modal">
<div className="loki-dialog modal">
{/* <div className="loki-dialog session-confirm-wrapper modal"> */}
<div className="session-confirm-wrapper">
<div className="session-modal">
<div className={classNames('session-modal__header', headerReverse && 'reverse')}>

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { animation, Menu } from 'react-contexify';
import {
getAddModeratorsMenuItem,
@ -18,6 +18,7 @@ import {
import { TimerOption } from '../../conversation/ConversationHeader';
export type PropsConversationHeaderMenu = {
id: string;
triggerId: string;
isMe: boolean;
isPublic?: boolean;
@ -49,6 +50,7 @@ export type PropsConversationHeaderMenu = {
export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
const {
id,
triggerId,
isMe,
isPublic,
@ -77,39 +79,48 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
onSetDisappearingMessages,
} = props;
const [modal, setModal] = useState<any>(null);
return (
<Menu id={triggerId} animation={animation.fade}>
{getDisappearingMenuItem(
isPublic,
isKickedFromGroup,
left,
isBlocked,
timerOptions,
onSetDisappearingMessages,
window.i18n
)}
{getBlockMenuItem(isMe, isPrivate, isBlocked, onBlockUser, onUnblockUser, window.i18n)}
<>
{modal ? modal : null}
<Menu id={triggerId} animation={animation.fade}>
{getDisappearingMenuItem(
isPublic,
isKickedFromGroup,
left,
isBlocked,
timerOptions,
onSetDisappearingMessages,
window.i18n
)}
{getBlockMenuItem(isMe, isPrivate, isBlocked, onBlockUser, onUnblockUser, window.i18n)}
{getCopyMenuItem(isPublic, isGroup, onCopyPublicKey, window.i18n)}
{getMarkAllReadMenuItem(onMarkAllRead, window.i18n)}
{getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n)}
{getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)}
{getAddModeratorsMenuItem(isAdmin, isKickedFromGroup, onAddModerators, window.i18n)}
{getRemoveModeratorsMenuItem(isAdmin, isKickedFromGroup, onRemoveModerators, window.i18n)}
{getUpdateGroupNameMenuItem(isAdmin, isKickedFromGroup, left, onUpdateGroupName, window.i18n)}
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n)}
{/* TODO: add delete group */}
{getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)}
{getDeleteContactMenuItem(
isMe,
isGroup,
isPublic,
left,
isKickedFromGroup,
onDeleteContact,
window.i18n
)}
</Menu>
{getCopyMenuItem(isPublic, isGroup, onCopyPublicKey, window.i18n)}
{getMarkAllReadMenuItem(onMarkAllRead, window.i18n)}
{getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n, id, setModal)}
{getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)}
{getAddModeratorsMenuItem(isAdmin, isKickedFromGroup, onAddModerators, window.i18n)}
{getRemoveModeratorsMenuItem(isAdmin, isKickedFromGroup, onRemoveModerators, window.i18n)}
{getUpdateGroupNameMenuItem(isAdmin, isKickedFromGroup, left, onUpdateGroupName, window.i18n)}
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n)}
{/* TODO: add delete group */}
{getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)}
{getDeleteContactMenuItem(
isMe,
isGroup,
isPublic,
left,
isKickedFromGroup,
onDeleteContact,
window.i18n
)}
</Menu>
</>
);
};

@ -1,7 +1,8 @@
import React from 'react';
import React, { Dispatch } from 'react';
import { LocalizerType } from '../../../types/Util';
import { TimerOption } from '../../conversation/ConversationHeader';
import { Item, Submenu } from 'react-contexify';
import { SessionNicknameDialog } from '../SessionNicknameDialog';
function showTimerOptions(
isPublic: boolean,
@ -205,7 +206,7 @@ export function getDisappearingMenuItem(
// Remove the && false to make context menu work with RTL support
<Submenu
label={i18n('disappearingMessages') as any}
// rtl={isRtlMode && false}
// rtl={isRtlMode && false}
>
{(timerOptions || []).map(item => (
<Item
@ -272,10 +273,31 @@ export function getChangeNicknameMenuItem(
isMe: boolean | undefined,
action: any,
isGroup: boolean | undefined,
i18n: LocalizerType
i18n: LocalizerType,
conversationId?: string,
setModal?: any
): JSX.Element | null {
if (showChangeNickname(Boolean(isMe), Boolean(isGroup))) {
return <Item onClick={action}>{i18n('changeNickname')}</Item>;
const clearModal = () => {
setModal(null);
}
// const onClickOk = () => {
// console.log("@@ onclickok clicked");
// }
const onClickCustom = () => {
setModal(<SessionNicknameDialog onClickClose={clearModal} conversationId={conversationId}></SessionNicknameDialog>);
// setModal(null);
}
return (
<>
<Item onClick={onClickCustom}>{i18n('changeNickname')}</Item>
{/* <Item onClick={action}>{i18n('changeNickname')}</Item> */}
</>
);
}
return null;
}

@ -112,7 +112,15 @@ export async function getFile(attachment: StagedAttachmentType, maxMeasurements?
};
}
export async function readFile(attachment: any): Promise<object> {
export type AttachmentFileType = {
attachment: any;
data: ArrayBuffer;
size: number;
}
// export async function readFile(attachment: any): Promise<object> {
export async function readFile(attachment: any): Promise<AttachmentFileType> {
return new Promise((resolve, reject) => {
const FR = new FileReader();
FR.onload = e => {

Loading…
Cancel
Save