add back bump typing to CompositionBox 7 show bubbles on direct convo

pull/1381/head
Audric Ackermann 5 years ago
parent 08d8b90aaa
commit e8677eef8c

@ -8,4 +8,6 @@ export type ConversationControllerType = {
getOrCreateAndWait: (id: string, type: string) => Promise<ConversationModel>; getOrCreateAndWait: (id: string, type: string) => Promise<ConversationModel>;
getOrCreate: (id: string, type: string) => Promise<ConversationModel>; getOrCreate: (id: string, type: string) => Promise<ConversationModel>;
dangerouslyCreateAndAdd: (any) => any; dangerouslyCreateAndAdd: (any) => any;
getContactProfileNameOrShortenedPubKey: (id: string) => string;
getContactProfileNameOrFullPubKey: (id: string) => string;
}; };

@ -62,6 +62,7 @@ export interface ConversationModel
isClosable: () => boolean; isClosable: () => boolean;
isOnline: () => boolean; isOnline: () => boolean;
isModerator: (id?: string) => boolean; isModerator: (id?: string) => boolean;
throttledBumpTyping: () => void;
lastMessage: string; lastMessage: string;
messageCollection: Backbone.Collection<MessageModel>; messageCollection: Backbone.Collection<MessageModel>;

@ -346,7 +346,7 @@
); );
// send the message to a single recipient if this is a session chat // send the message to a single recipient if this is a session chat
if (this.isPrivate) { if (this.isPrivate()) {
const device = new libsession.Types.PubKey(recipientId); const device = new libsession.Types.PubKey(recipientId);
libsession libsession
.getMessageQueue() .getMessageQueue()

@ -850,19 +850,6 @@
} }
}, },
onKeyUp() {
this.maybeBumpTyping();
},
// Called whenever the user changes the message composition field. But only
// fires if there's content in the message field after the change.
maybeBumpTyping() {
const messageText = this.$messageField.val();
if (messageText.length) {
this.model.throttledBumpTyping();
}
},
handleDeleteOrBackspace(event, isDelete) { handleDeleteOrBackspace(event, isDelete) {
const $input = this.$messageField[0]; const $input = this.$messageField[0];
const text = this.$messageField.val(); const text = this.$messageField.val();

@ -46,14 +46,6 @@
padding-inline-end: 10px; padding-inline-end: 10px;
} }
.session-message {
display: flow-root;
padding-bottom: 4px;
padding-top: 4px;
padding-inline-start: 16px;
padding-inline-end: 16px;
}
.group-invitation-container { .group-invitation-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

@ -3,67 +3,65 @@ import classNames from 'classnames';
import { TypingAnimation } from './TypingAnimation'; import { TypingAnimation } from './TypingAnimation';
import { Avatar } from '../Avatar'; import { Avatar } from '../Avatar';
import styled from 'styled-components';
import { LocalizerType } from '../../types/Util'; interface TypingBubbleProps {
interface Props {
avatarPath?: string; avatarPath?: string;
color: string;
name: string;
phoneNumber: string; phoneNumber: string;
profileName: string; displayedName: string | null;
conversationType: string; conversationType: string;
i18n: LocalizerType; isTyping: boolean;
} }
export class TypingBubble extends React.Component<Props> { const TypingBubbleContainer = styled.div<TypingBubbleProps>`
public renderAvatar() { height: ${props => (props.isTyping ? 'auto' : '0px')};
const { display: flow-root;
avatarPath, padding-bottom: ${props => (props.isTyping ? '4px' : '0px')};
name, padding-top: ${props => (props.isTyping ? '4px' : '0px')};
phoneNumber, transition: ${props => props.theme.common.animations.defaultDuration};
profileName, padding-inline-end: 16px;
conversationType, overflow: hidden;
} = this.props; `;
export const TypingBubble = (props: TypingBubbleProps) => {
const renderAvatar = () => {
const { avatarPath, displayedName, conversationType, phoneNumber } = props;
if (conversationType !== 'group') { if (conversationType !== 'group') {
return; return;
} }
const userName = name || profileName || phoneNumber;
return ( return (
<div className="module-message__author-avatar"> <div className="module-message__author-avatar">
<Avatar <Avatar
avatarPath={avatarPath} avatarPath={avatarPath}
name={userName} name={displayedName || phoneNumber}
size={36} size={36}
pubkey={phoneNumber} pubkey={phoneNumber}
/> />
</div> </div>
); );
} };
public render() { if (props.conversationType === 'group') {
const { i18n, color } = this.props; return <></>;
}
return ( return (
<div className="session-message"> <TypingBubbleContainer {...props}>
<div className={classNames('module-message', 'module-message--incoming')}>
<div <div
className={classNames('module-message', 'module-message--incoming')} className={classNames(
'module-message__container',
'module-message__container--incoming'
)}
> >
<div <div className="module-message__typing-container">
className={classNames( <TypingAnimation i18n={window.i18n} />
'module-message__container',
'module-message__container--incoming'
)}
>
<div className="module-message__typing-container">
<TypingAnimation color={color} i18n={i18n} />
</div>
{this.renderAvatar()}
</div> </div>
{renderAvatar()}
</div> </div>
</div> </div>
); </TypingBubbleContainer>
} );
} };

@ -128,6 +128,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
private linkPreviewAbortController?: AbortController; private linkPreviewAbortController?: AbortController;
private container: any; private container: any;
private readonly mentionsRegex = /@\u{FFD2}05[0-9a-f]{64}:[^\u{FFD2}]+\u{FFD2}/gu; private readonly mentionsRegex = /@\u{FFD2}05[0-9a-f]{64}:[^\u{FFD2}]+\u{FFD2}/gu;
private lastBumpTypingMessageLength: number = 0;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
@ -167,6 +168,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// Events // Events
this.onKeyDown = this.onKeyDown.bind(this); this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.focusCompositionBox = this.focusCompositionBox.bind(this); this.focusCompositionBox = this.focusCompositionBox.bind(this);
@ -190,6 +192,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// reset the state on new conversation key // reset the state on new conversation key
if (prevProps.conversationKey !== this.props.conversationKey) { if (prevProps.conversationKey !== this.props.conversationKey) {
this.setState(getDefaultState(), this.focusCompositionBox); this.setState(getDefaultState(), this.focusCompositionBox);
this.lastBumpTypingMessageLength = 0;
} else if ( } else if (
this.props.stagedAttachments?.length !== this.props.stagedAttachments?.length !==
prevProps.stagedAttachments?.length prevProps.stagedAttachments?.length
@ -360,6 +363,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
value={message} value={message}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
placeholder={messagePlaceHolder} placeholder={messagePlaceHolder}
spellCheck={false} spellCheck={false}
inputRef={this.textarea} inputRef={this.textarea}
@ -723,6 +727,24 @@ export class SessionCompositionBox extends React.Component<Props, State> {
} }
} }
private async onKeyUp(event: any) {
const { message } = this.state;
// Called whenever the user changes the message composition field. But only
// fires if there's content in the message field after the change.
// Also, check for a message length change before firing it up, to avoid
// catching ESC, tab, or whatever which is not typing
if (message.length && message.length !== this.lastBumpTypingMessageLength) {
const conversationModel = window.ConversationController.get(
this.props.conversationKey
);
if (!conversationModel) {
return;
}
conversationModel.throttledBumpTyping();
this.lastBumpTypingMessageLength = message.length;
}
}
private parseEmojis(value: string) { private parseEmojis(value: string) {
const emojisArray = toArray(value); const emojisArray = toArray(value);

@ -16,6 +16,7 @@ import { MessageModel } from '../../../../js/models/messages';
import { SessionLastSeenIndicator } from './SessionLastSeedIndicator'; import { SessionLastSeenIndicator } from './SessionLastSeedIndicator';
import { VerificationNotification } from '../../conversation/VerificationNotification'; import { VerificationNotification } from '../../conversation/VerificationNotification';
import { ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import { TypingBubble } from '../../conversation/TypingBubble';
interface State { interface State {
showScrollButton: boolean; showScrollButton: boolean;
@ -119,15 +120,29 @@ export class SessionMessagesList extends React.Component<Props, State> {
} }
public render() { public render() {
const { messages } = this.props; const { conversationKey, conversation, messages } = this.props;
const { showScrollButton } = this.state; const { showScrollButton } = this.state;
let displayedName = null;
if (conversation.type === 'direct') {
displayedName = window.ConversationController.getContactProfileNameOrShortenedPubKey(
conversationKey
);
}
return ( return (
<div <div
className="messages-container" className="messages-container"
onScroll={this.handleScroll} onScroll={this.handleScroll}
ref={this.messageContainerRef} ref={this.messageContainerRef}
> >
<TypingBubble
phoneNumber={conversationKey}
conversationType={conversation.type}
displayedName={displayedName}
isTyping={conversation.isTyping}
/>
{this.renderMessages(messages)} {this.renderMessages(messages)}
<SessionScrollButton <SessionScrollButton

Loading…
Cancel
Save