Major rework of context menus

pull/710/head
Vincent 5 years ago
parent d0d5012e07
commit 9afb8b4d5e

@ -802,8 +802,10 @@
appView.openConversation(groupId, {});
};
window.generateID = () => Math.random().toString(36).substring(3);
window.generateID = () =>
Math.random()
.toString(36)
.substring(3);
window.toasts = new Map();
window.pushToast = options => {
@ -817,7 +819,7 @@
description: options.description || '',
type: options.type || '',
};
// Give all toasts an ID. User may define.
let currentToast;
const toastID = params.id;

@ -53,7 +53,9 @@ const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog');
const { SessionToast } = require('../../ts/components/session/SessionToast');
const { SessionToggle } = require('../../ts/components/session/SessionToggle');
const { SessionModal } = require('../../ts/components/session/SessionModal');
const { SessionDropdown } = require('../../ts/components/session/SessionDropdown');
const {
SessionDropdown,
} = require('../../ts/components/session/SessionDropdown');
const {
SessionRegistrationView,
} = require('../../ts/components/session/SessionRegistrationView');

@ -1375,11 +1375,11 @@
if (!isAllOurs && !isModerator) {
window.pushToast({
title: i18n('messageDeletionForbidden'),
type: 'error',
id: 'messageDeletionForbidden',
title: i18n('messageDeletionForbidden'),
type: 'error',
id: 'messageDeletionForbidden',
});
return;
}

@ -2,37 +2,31 @@
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.SessionDropdownView = Whisper.View.extend({
initialize(options) {
this.props = {
items: options.items,
};
this.render();
},
render() {
this.dropdownView = new Whisper.ReactWrapperView({
className: 'session-dropdown-wrapper',
Component: window.Signal.Components.SessionDropdown,
props: this.props,
});
this.$el.append(this.dropdownView.el);
},
openDropdown() {
},
closeDropdown() {
},
});
})();
'use strict';
window.Whisper = window.Whisper || {};
Whisper.SessionDropdownView = Whisper.View.extend({
initialize(options) {
this.props = {
items: options.items,
};
this.render();
},
render() {
this.dropdownView = new Whisper.ReactWrapperView({
className: 'session-dropdown-wrapper',
Component: window.Signal.Components.SessionDropdown,
props: this.props,
});
this.$el.append(this.dropdownView.el);
},
openDropdown() {},
closeDropdown() {},
});
})();

@ -60,7 +60,7 @@ $session-opaque-dark-3: rgba(0, 0, 0, 0.5);
$session-color-white: #fff;
$session-color-dark-grey: #353535;
$session-color-black: #000;
$session-color-danger: #FF453A;
$session-color-danger: #ff453a;
$session-color-primary: $session-shade-13;
$session-color-secondary: $session-shade-16;
@ -561,37 +561,36 @@ label {
}
}
.react-contextmenu {
padding: 0px;
margin: 0px;
// .react-contextmenu {
// padding: 0px;
// margin: 0px;
// border: none !important;
// border-radius: 0px;
// }
// .react-contextmenu-item {
// display: flex;
// align-items: center;
border: none !important;
border-radius: 0px;
}
// height: 25px;
// padding: 0px 10px;
.react-contextmenu-item {
display: flex;
align-items: center;
// background-color: #1B1B1B;
height: 25px;
padding: 0px 10px;
// color: $session-color-white;
// font-family: 'Wasa';
// font-size: 12px;
// line-height: $session-icon-size-sm;
// font-weight: 700;
background-color: #1b1b1b;
// &--active, &--selected{
// background-color: $session-shade-7 !important;
// }
// }
color: $session-color-white;
font-family: 'Wasa';
font-size: 12px;
line-height: $session-icon-size-sm;
font-weight: 700;
&--active,
&--selected {
background-color: $session-shade-7 !important;
}
}
.session-dropdown{
.session-dropdown {
position: absolute;
top: 50px;
left: 50px;
@ -608,7 +607,7 @@ label {
height: 25px;
padding-right: 7px;
background-color: #1B1B1B;
background-color: #1b1b1b;
color: $session-color-white;
font-family: 'Wasa';
@ -618,7 +617,7 @@ label {
display: flex;
align-items: center;
.session-icon {
margin-left: 6px;
}
@ -626,7 +625,8 @@ label {
margin-left: 6px;
}
&.active, &:hover{
&.active,
&:hover {
background-color: $session-shade-7;
}
@ -635,4 +635,4 @@ label {
}
}
}
}
}

@ -53,6 +53,4 @@
margin-left: 5px;
}
}
}

@ -1,4 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
@ -12,9 +13,6 @@ import { TypingAnimation } from './conversation/TypingAnimation';
import { Colors, LocalizerType } from '../types/Util';
import { SessionDropdown } from './session/SessionDropdown';
import { SessionDropDownItemType } from './session/SessionDropdownItem';
export type PropsData = {
id: string;
phoneNumber: string;
@ -191,78 +189,36 @@ export class ConversationListItem extends React.PureComponent<Props> {
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockContact : onBlockContact;
const items = [
{
content: blockTitle,
display: true,//!isPublic && !isMe,
onClick: blockHandler
},
{
content: i18n('changeNickname'),
display: true,//!isPublic && !isMe,
onClick: onChangeNickname,
},
{
content: i18n('clearNickname'),
display: true,//!isPublic && !isMe && hasNickname,
onClick: onClearNickname,
},
{
content: i18n('copyPublicKey'),
display: true,//!isPublic,
onClick: onCopyPublicKey,
},
{
content: i18n('deleteMessages'),
onClick: onDeleteMessages,
},
{
content: i18n('deleteContact'),
display: true,//!isMe && isClosable && !isPublic,
onClick: onDeleteContact,
},
{
content: i18n('deletePublicChannel'),
display: true,//!isMe && isClosable && !isPublic,
type: SessionDropDownItemType.Default,
onClick: onDeleteContact,
},
];
return (
<SessionDropdown items={items}/>
<ContextMenu id={triggerId}>
{!isPublic && !isMe ? (
<MenuItem onClick={blockHandler}>{blockTitle}</MenuItem>
) : null}
{!isPublic && !isMe ? (
<MenuItem onClick={onChangeNickname}>
{i18n('changeNickname')}
</MenuItem>
) : null}
{!isPublic && !isMe && hasNickname ? (
<MenuItem onClick={onClearNickname}>{i18n('clearNickname')}</MenuItem>
) : null}
{!isPublic ? (
<MenuItem onClick={onCopyPublicKey}>{i18n('copyPublicKey')}</MenuItem>
) : null}
<MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
{!isMe && isClosable ? (
!isPublic ? (
<MenuItem onClick={onDeleteContact}>
{i18n('deleteContact')}
</MenuItem>
) : (
<MenuItem onClick={onDeleteContact}>
{i18n('deletePublicChannel')}
</MenuItem>
)
) : null}
</ContextMenu>
);
// <ContextMenu id={triggerId}>
// {!isPublic && !isMe ? (
// <MenuItem onClick={blockHandler}>{blockTitle}</MenuItem>
// ) : null}
// {!isPublic && !isMe ? (
// <MenuItem onClick={onChangeNickname}>
// {i18n('changeNickname')}
// </MenuItem>
// ) : null}
// {!isPublic && !isMe && hasNickname ? (
// <MenuItem onClick={onClearNickname}>{i18n('clearNickname')}</MenuItem>
// ) : null}
// {!isPublic ? (
// <MenuItem onClick={onCopyPublicKey}>{i18n('copyPublicKey')}</MenuItem>
// ) : null}
// <MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
// {!isMe && isClosable ? (
// !isPublic ? (
// <MenuItem onClick={onDeleteContact}>
// {i18n('deleteContact')}
// </MenuItem>
// ) : (
// <MenuItem onClick={onDeleteContact}>
// {i18n('deletePublicChannel')}
// </MenuItem>
// )
// ) : null}
// </ContextMenu>
//);
}
public renderMessage() {

@ -34,40 +34,40 @@ export class UserDetailsDialog extends React.Component<Props> {
const cancelText = i18n('cancel');
const startConversation = i18n('startConversation');
const items = [
{
content: "sdgsdfg",
display: true,//!isPublic && !isMe,
content: 'sdgsdfg',
display: true, //!isPublic && !isMe,
},
{
content: i18n('changeNickname'),
display: true,//!isPublic && !isMe,
display: true, //!isPublic && !isMe,
},
{
content: i18n('clearNickname'),
display: true,//!isPublic && !isMe && hasNickname,
display: true, //!isPublic && !isMe && hasNickname,
},
{
content: i18n('copyPublicKey'),
display: false,//!isPublic,
display: false, //!isPublic,
},
{
content: i18n('deleteMessages'),
},
{
content: i18n('deleteContact'),
display: true,//!isMe && isClosable && !isPublic,
display: true, //!isMe && isClosable && !isPublic,
},
{
content: i18n('deletePublicChannel'),
display: true,//!isMe && isClosable && !isPublic,
display: true, //!isMe && isClosable && !isPublic,
},
];
return (
<div className="content">
<SessionDropdown items={items}/>
<SessionDropdown items={items} />
<div className="avatar-center">
<div className="avatar-center-inner">{this.renderAvatar()}</div>

@ -22,6 +22,8 @@ import {
SessionButtonType,
} from '../session/SessionButton';
import { SessionDropdownTrigger } from '../session/SessionDropdownTrigger';
interface TimerOption {
name: string;
value: number;
@ -245,12 +247,12 @@ export class ConversationHeader extends React.Component<Props> {
}
return (
<ContextMenuTrigger id={triggerId} ref={this.menuTriggerRef}>
<SessionDropdownTrigger>
<SessionIconButton
iconType={SessionIconType.Ellipses}
iconSize={SessionIconSize.Large}
/>
</ContextMenuTrigger>
</SessionDropdownTrigger>
);
}

@ -2,52 +2,80 @@ import React from 'react';
import classNames from 'classnames';
import { SessionIconType } from './icon/';
import { SessionDropdownItem, SessionDropDownItemType } from './SessionDropdownItem';
import {
SessionDropdownItem,
SessionDropDownItemType,
} from './SessionDropdownItem';
// THIS IS A FUTURE-PROOFING ELEMENT TO REPLACE ELECTRON CONTEXTMENUS IN PRELOAD.JS
interface State {
x: number;
y: number;
isVisible: boolean;
}
interface Props {
id?: string,
items: Array<{
content: string,
id?: string,
icon?: SessionIconType | null,
type?: SessionDropDownItemType,
active?: boolean,
onClick?: any,
display?: boolean,
}>,
}
id?: string;
onClick?: any;
relativeTo: string | Array<number>;
items: Array<{
content: string;
id?: string;
icon?: SessionIconType | null;
type?: SessionDropDownItemType;
active?: boolean;
onClick?: any;
display?: boolean;
}>;
}
export class SessionDropdown extends React.PureComponent<Props> {
export class SessionDropdown extends React.Component<Props, State> {
constructor(props: any) {
super(props);
this.state = {
x: 0,
y: 0,
isVisible: false,
};
}
public show() {
this.setState({
isVisible: true,
});
}
public hide() {
this.setState({
isVisible: false,
});
}
public render() {
const { items } = this.props;
const { isVisible } = this.state;
return (
<div className={classNames('session-dropdown')}>
<ul>
{items.map((item: any) => {
{isVisible
? items.map((item: any) => {
return item.display ? (
<SessionDropdownItem
id={item.id}
content={item.content}
icon={item.icon}
type={item.type}
active={item.active}
onClick={item.onClick}
/>
) : null
}
)}
<SessionDropdownItem
id={item.id}
content={item.content}
icon={item.icon}
type={item.type}
active={item.active}
onClick={item.onClick}
/>
) : null;
})
: null}
</ul>
</div>
);
}
}

@ -1,68 +1,61 @@
import React from 'react';
import classNames from 'classnames';
import { SessionIcon, SessionIconSize, SessionIconType } from './icon/';
import { generateID } from './tools/ComponentTools';
export enum SessionDropDownItemType {
Default = 'default',
Danger = 'danger',
Default = 'default',
Danger = 'danger',
}
interface Props {
id: string,
content: string,
type: SessionDropDownItemType,
icon: SessionIconType | null,
active: boolean,
onClick: any,
id: string;
content: string;
type: SessionDropDownItemType;
icon: SessionIconType | null;
active: boolean;
onClick: any;
}
export class SessionDropdownItem extends React.PureComponent<Props> {
public static defaultProps = {
id: generateID(),
type: SessionDropDownItemType.Default,
icon: null,
active: false,
onClick: () => null,
};
constructor(props: any) {
super(props);
this.clickHandler = this.clickHandler.bind(this);
}
public render() {
const { id, content, type, icon, active } = this.props;
return (
<li
id={id}
className={classNames(active ? 'active' : '', type || '')}
onClick={this.clickHandler}
>
{ icon ? <SessionIcon
iconType = { icon }
iconSize = { SessionIconSize.Small }
/>
: ''
}
<div className="item-content">
{content}
</div>
</li>
);
}
private clickHandler(e: any){
if (this.props.onClick) {
e.stopPropagation();
this.props.onClick();
}
}
public static defaultProps = {
id: generateID(),
type: SessionDropDownItemType.Default,
icon: null,
active: false,
onClick: () => null,
};
constructor(props: any) {
super(props);
this.clickHandler = this.clickHandler.bind(this);
}
public render() {
const { id, content, type, icon, active } = this.props;
return (
<li
id={id}
className={classNames(active ? 'active' : '', type || '')}
role="button"
onClick={this.clickHandler}
>
{icon ? (
<SessionIcon iconType={icon} iconSize={SessionIconSize.Small} />
) : (
''
)}
<div className="item-content">{content}</div>
</li>
);
}
private clickHandler(e: any) {
if (this.props.onClick) {
e.stopPropagation();
this.props.onClick();
}
}
}

@ -0,0 +1,37 @@
import React from 'react';
interface Props {
//mouseButton: Number;
posX: number;
posY: number;
}
export class SessionDropdownTrigger extends React.Component<Props> {
public static defaultProps = {
//mouseButton: 2, // 0 is left click, 2 is right click
posX: 0,
posY: 0,
};
constructor(props: any) {
super(props);
}
public handleDropdownClick = (event: any) => {
event.preventDefault();
event.stopPropagation();
let x = event.clientX || (event.touches && event.touches[0].pageX);
let y = event.clientY || (event.touches && event.touches[0].pageY);
if (this.props.posX) {
x -= this.props.posX;
}
if (this.props.posY) {
y -= this.props.posY;
}
};
public render() {
return <div role="button" onClick={this.handleDropdownClick} />;
}
}

@ -1,7 +1,8 @@
// This is a collection of tools which can be ued to simplify the esign and rendering process of your components.
export function generateID(){
// Generates a unique ID for your component
return Math.random().toString(36).substring(3);
}
export function generateID() {
// Generates a unique ID for your component
const buffer = new Uint32Array(10);
return window.crypto.getRandomValues(buffer)[0].toString();
}

Loading…
Cancel
Save