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(); 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 = () => { window.showOnionStatusDialog = () => {
appView.showOnionStatusDialog(); appView.showOnionStatusDialog();

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

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

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

@ -1,5 +1,5 @@
.modal { .modal {
position: absolute; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
height: 100%; height: 100%;
@ -311,42 +311,17 @@
} }
} }
.modal { // .modal {
position: fixed; // position: fixed;
top: 0; // top: 0;
left: 0; // left: 0;
width: 100%; // width: 100%;
height: 100%; // height: 100%;
z-index: 10000; // z-index: 10000;
padding: 2rem; // padding: 2rem;
background-color: rgba(0, 0, 0, 0.55); // 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;
}
}
}
.onion-status-dialog { .onion-status-dialog {
.session-modal__header__title { .session-modal__header__title {

@ -1220,27 +1220,9 @@ input {
.session-icon-button { .session-icon-button {
margin: $session-margin-sm !important; 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 { .session-nickname-wrapper {
position: absolute; position: absolute;
height: 100%; height: 100%;

@ -9,7 +9,7 @@ import { SessionButton, SessionButtonColor, SessionButtonType } from './session/
import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon'; import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon';
import { SessionModal } from './session/SessionModal'; import { SessionModal } from './session/SessionModal';
import { PillDivider } from './session/PillDivider'; 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 { DefaultTheme, useTheme } from 'styled-components';
import { MAX_USERNAME_LENGTH } from './session/registration/RegistrationTabs'; import { MAX_USERNAME_LENGTH } from './session/registration/RegistrationTabs';
import { SessionSpinner } from './session/SessionSpinner'; import { SessionSpinner } from './session/SessionSpinner';
@ -20,6 +20,7 @@ import { useSelector } from 'react-redux';
import { getOurNumber } from '../state/selectors/user'; import { getOurNumber } from '../state/selectors/user';
import { SessionWrapperModal } from './session/SessionWrapperModal'; import { SessionWrapperModal } from './session/SessionWrapperModal';
import { times } from 'underscore'; import { times } from 'underscore';
import { AttachmentUtil, } from '../util';
interface Props { interface Props {
i18n?: any; i18n?: any;
@ -42,6 +43,7 @@ interface State {
export class EditProfileDialog extends React.Component<Props, State> { export class EditProfileDialog extends React.Component<Props, State> {
private readonly inputEl: any; private readonly inputEl: any;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
@ -134,11 +136,11 @@ export class EditProfileDialog extends React.Component<Props, State> {
<div className="edit-profile-dialog"> <div className="edit-profile-dialog">
<SessionWrapperModal <SessionWrapperModal
title={i18n('editProfileModalTitle')} title={i18n('editProfileModalTitle')}
onClose={this.closeDialog} onClose={this.closeDialog}
headerIconButtons={backButton} headerIconButtons={backButton}
showExitIcon={true} showExitIcon={true}
theme={this.props.theme} theme={this.props.theme}
> >
<div className="spacer-md" /> <div className="spacer-md" />
@ -182,6 +184,8 @@ export class EditProfileDialog extends React.Component<Props, State> {
); );
} }
private renderProfileHeader() { private renderProfileHeader() {
return ( return (
<> <>
@ -310,6 +314,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
switch (event.key) { switch (event.key) {
case 'Enter': case 'Enter':
if (this.state.mode === 'edit') { if (this.state.mode === 'edit') {
// this.onClickOK();
this.onClickOK(); this.onClickOK();
} }
break; break;
@ -326,6 +331,10 @@ export class EditProfileDialog extends React.Component<Props, State> {
ToastUtils.pushCopiedToClipBoard(); ToastUtils.pushCopiedToClipBoard();
} }
/**
* Tidy the profile name input text and save the new profile name and avatar
* @returns
*/
private onClickOK() { private onClickOK() {
const newName = this.state.profileName.trim(); const newName = this.state.profileName.trim();
@ -346,8 +355,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
loading: true, loading: true,
}, },
async () => { async () => {
// await this.props.onOk(newName, avatar); await this.commitProfileEdits(newName, avatar);
await window.commitProfileEdits(newName, avatar);
this.setState({ this.setState({
loading: false, loading: false,
@ -363,4 +371,107 @@ export class EditProfileDialog extends React.Component<Props, State> {
this.props.onClose(); 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 { ConversationController } from '../../session/conversations/ConversationController';
import { SessionModal } from './SessionModal'; import { SessionModal } from './SessionModal';
import { SessionButton } from './SessionButton'; import { SessionButton } from './SessionButton';
import { DefaultTheme, withTheme } from 'styled-components'; import { DefaultTheme, withTheme, useTheme } from 'styled-components';
import _ from 'lodash'; import _ from 'lodash';
import { SessionWrapperModal } from './SessionWrapperModal';
type Props = { type Props = {
onClickOk: any; onClickOk?: any;
onClickClose: any; onClickClose?: any;
theme: DefaultTheme; theme?: DefaultTheme;
convoId: string; conversationId?: string;
}; };
const SessionNicknameInner = (props: Props) => { const SessionNicknameInner = (props: Props) => {
const { onClickOk, onClickClose, convoId, theme } = props; const { onClickOk, onClickClose, conversationId } = props;
let { theme } = props;
const [nickname, setNickname] = useState(''); const [nickname, setNickname] = useState('');
theme = theme ? theme : useTheme();
/** /**
* Changes the state of nickname variable. If enter is pressed, saves the current * Changes the state of nickname variable. If enter is pressed, saves the current
* entered nickname value as the nickname. * entered nickname value as the nickname.
@ -34,41 +38,58 @@ const SessionNicknameInner = (props: Props) => {
* Saves the currently entered nickname. * Saves the currently entered nickname.
*/ */
const saveNickname = async () => { const saveNickname = async () => {
if (!convoId) { if (!conversationId) {
return; throw "Cant save withou conversation id"
// return;
}
const conversation = ConversationController.getInstance().get(conversationId);
if (onClickOk) {
onClickOk(nickname);
} }
const convo = ConversationController.getInstance().get(convoId); await conversation.setNickname(nickname);
onClickOk(nickname); onClickClose();
await convo.setNickname(nickname);
}; };
return ( return (
<SessionModal // <SessionModal
title={window.i18n('changeNickname')} // title={window.i18n('changeNickname')}
onClose={onClickClose} // onClose={onClickClose}
showExitIcon={false} // showExitIcon={false}
showHeader={true} // showHeader={true}
theme={theme} // theme={theme}
> // >
<div className="session-modal__centered">
<span className="subtle">{window.i18n('changeNicknameMessage')}</span> // TODO: Implement showHeader option for modal
<div className="spacer-lg" /> <SessionWrapperModal
</div> 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 ( 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-confirm-wrapper">
<div className="session-modal"> <div className="session-modal">
<div className={classNames('session-modal__header', headerReverse && 'reverse')}> <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 { animation, Menu } from 'react-contexify';
import { import {
getAddModeratorsMenuItem, getAddModeratorsMenuItem,
@ -18,6 +18,7 @@ import {
import { TimerOption } from '../../conversation/ConversationHeader'; import { TimerOption } from '../../conversation/ConversationHeader';
export type PropsConversationHeaderMenu = { export type PropsConversationHeaderMenu = {
id: string;
triggerId: string; triggerId: string;
isMe: boolean; isMe: boolean;
isPublic?: boolean; isPublic?: boolean;
@ -49,6 +50,7 @@ export type PropsConversationHeaderMenu = {
export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => { export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
const { const {
id,
triggerId, triggerId,
isMe, isMe,
isPublic, isPublic,
@ -77,39 +79,48 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
onSetDisappearingMessages, onSetDisappearingMessages,
} = props; } = props;
const [modal, setModal] = useState<any>(null);
return ( return (
<Menu id={triggerId} animation={animation.fade}> <>
{getDisappearingMenuItem( {modal ? modal : null}
isPublic,
isKickedFromGroup, <Menu id={triggerId} animation={animation.fade}>
left,
isBlocked,
timerOptions, {getDisappearingMenuItem(
onSetDisappearingMessages, isPublic,
window.i18n isKickedFromGroup,
)} left,
{getBlockMenuItem(isMe, isPrivate, isBlocked, onBlockUser, onUnblockUser, window.i18n)} isBlocked,
timerOptions,
onSetDisappearingMessages,
window.i18n
)}
{getBlockMenuItem(isMe, isPrivate, isBlocked, onBlockUser, onUnblockUser, window.i18n)}
{getCopyMenuItem(isPublic, isGroup, onCopyPublicKey, window.i18n)} {getCopyMenuItem(isPublic, isGroup, onCopyPublicKey, window.i18n)}
{getMarkAllReadMenuItem(onMarkAllRead, window.i18n)} {getMarkAllReadMenuItem(onMarkAllRead, window.i18n)}
{getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n)} {getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n, id, setModal)}
{getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)} {getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)} {getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)}
{getAddModeratorsMenuItem(isAdmin, isKickedFromGroup, onAddModerators, window.i18n)} {getAddModeratorsMenuItem(isAdmin, isKickedFromGroup, onAddModerators, window.i18n)}
{getRemoveModeratorsMenuItem(isAdmin, isKickedFromGroup, onRemoveModerators, window.i18n)} {getRemoveModeratorsMenuItem(isAdmin, isKickedFromGroup, onRemoveModerators, window.i18n)}
{getUpdateGroupNameMenuItem(isAdmin, isKickedFromGroup, left, onUpdateGroupName, window.i18n)} {getUpdateGroupNameMenuItem(isAdmin, isKickedFromGroup, left, onUpdateGroupName, window.i18n)}
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n)} {getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n)}
{/* TODO: add delete group */} {/* TODO: add delete group */}
{getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)} {getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)}
{getDeleteContactMenuItem( {getDeleteContactMenuItem(
isMe, isMe,
isGroup, isGroup,
isPublic, isPublic,
left, left,
isKickedFromGroup, isKickedFromGroup,
onDeleteContact, onDeleteContact,
window.i18n window.i18n
)} )}
</Menu> </Menu>
</>
); );
}; };

@ -1,7 +1,8 @@
import React from 'react'; import React, { Dispatch } from 'react';
import { LocalizerType } from '../../../types/Util'; import { LocalizerType } from '../../../types/Util';
import { TimerOption } from '../../conversation/ConversationHeader'; import { TimerOption } from '../../conversation/ConversationHeader';
import { Item, Submenu } from 'react-contexify'; import { Item, Submenu } from 'react-contexify';
import { SessionNicknameDialog } from '../SessionNicknameDialog';
function showTimerOptions( function showTimerOptions(
isPublic: boolean, isPublic: boolean,
@ -205,7 +206,7 @@ export function getDisappearingMenuItem(
// Remove the && false to make context menu work with RTL support // Remove the && false to make context menu work with RTL support
<Submenu <Submenu
label={i18n('disappearingMessages') as any} label={i18n('disappearingMessages') as any}
// rtl={isRtlMode && false} // rtl={isRtlMode && false}
> >
{(timerOptions || []).map(item => ( {(timerOptions || []).map(item => (
<Item <Item
@ -272,10 +273,31 @@ export function getChangeNicknameMenuItem(
isMe: boolean | undefined, isMe: boolean | undefined,
action: any, action: any,
isGroup: boolean | undefined, isGroup: boolean | undefined,
i18n: LocalizerType i18n: LocalizerType,
conversationId?: string,
setModal?: any
): JSX.Element | null { ): JSX.Element | null {
if (showChangeNickname(Boolean(isMe), Boolean(isGroup))) { 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; 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) => { return new Promise((resolve, reject) => {
const FR = new FileReader(); const FR = new FileReader();
FR.onload = e => { FR.onload = e => {

Loading…
Cancel
Save