Header props

pull/1102/head
Vincent 5 years ago
parent a5905536fa
commit 5d11ef1457

@ -986,7 +986,7 @@
}
return toastID;
};
};
window.getFriendsFromContacts = contacts => {
// To call from TypeScript, input / output are both

@ -61,42 +61,34 @@
window.getConversationByKey = key => {
// Key is pubkey or public chat name
const conversation = conversations.models.filter(conv => conv.id === key)[0] || null;
const conversation =
conversations.models.filter(conv => conv.id === key)[0] || null;
return conversation;
}
};
window.getMessagesByKey = async (key, loadLive = false) => {
window.getMessagesByKey = async key => {
// loadLive gets messages live, not from the database which can lag behind.
let messages = [];
let messageSet;
// if (loadLive){
messageSet = await window.Signal.Data.getMessagesByConversation(
key,
{ limit: 100, MessageCollection: Whisper.MessageCollection }
);
// } else {
// const conversation = window.getConversationByKey(key);
// // Grab messages and push to conv object.
// await conversation.fetchMessages();
// messageSet = conversation.messageCollection;
// }
const messageSet = await window.Signal.Data.getMessagesByConversation(key, {
limit: 100,
MessageCollection: Whisper.MessageCollection,
});
messages = messageSet.models.map(conv => conv.attributes);
return messages;
}
};
window.getLastMessageByKey = async key => {
const messageSet = await window.Signal.Data.getMessagesByConversation(
key,
{ limit: 1, MessageCollection: Whisper.MessageCollection }
);
const messageSet = await window.Signal.Data.getMessagesByConversation(key, {
limit: 1,
MessageCollection: Whisper.MessageCollection,
});
const message = messageSet.models.map(conv => conv.attributes)[0];
return message;
}
};
window.ConversationController = {
get(id) {

@ -131,7 +131,6 @@
this.messageCollection.on('change:errors', this.handleMessageError, this);
this.messageCollection.on('send-error', this.onMessageError, this);
this.throttledBumpTyping = _.throttle(this.bumpTyping, 300);
const debouncedUpdateLastMessage = _.debounce(
this.updateLastMessage.bind(this),
@ -2274,6 +2273,9 @@
},
async markRead(newestUnreadDate, providedOptions) {
console.log(`[vince][unread] Marking messages as read!!`);
const options = providedOptions || {};
_.defaults(options, { sendReadReceipts: true });
@ -2285,6 +2287,9 @@
);
let unreadMessages = await this.getUnread();
console.log(`[vince][unread] Unread: `, unreadMessages);
const oldUnread = unreadMessages.filter(
message => message.get('received_at') <= newestUnreadDate
);
@ -2310,6 +2315,8 @@
})
);
console.log(`[vince][unread] Read:`, read);
// Some messages we're marking read are local notifications with no sender
read = _.filter(read, m => Boolean(m.sender));
unreadMessages = unreadMessages.filter(m => Boolean(m.isIncoming()));

@ -2,4 +2,7 @@ export function searchMessages(query: string): Promise<Array<any>>;
export function searchConversations(query: string): Promise<Array<any>>;
export function getPrimaryDeviceFor(pubKey: string): Promise<string | null>;
export function getMessagesByConversation(conversationId: string, destructurer: any): Promise<Array<any>>;
export function getMessagesByConversation(
conversationId: string,
destructurer: any
): Promise<Array<any>>;

@ -769,7 +769,7 @@ async function updateConversation(id, data, { Conversation }) {
if (!existing) {
throw new Error(`Conversation ${id} does not exist!`);
}
const merged = _.merge({}, existing.attributes, data);
// Merging is a really bad idea and not what we want here, e.g.

@ -146,7 +146,9 @@ const {
// State
const { createLeftPane } = require('../../ts/state/roots/createLeftPane');
const { createSessionConversation } = require('../../ts/state/roots/createSessionConversation');
const {
createSessionConversation,
} = require('../../ts/state/roots/createSessionConversation');
const { createStore } = require('../../ts/state/createStore');
const conversationsDuck = require('../../ts/state/ducks/conversations');
const userDuck = require('../../ts/state/ducks/user');

@ -63,7 +63,9 @@
// Add sessionConversation to the DOM
$('#main-view .conversation-stack').html('');
$('#main-view .conversation-stack').append(this.sessionConversationView.el);
$('#main-view .conversation-stack').append(
this.sessionConversationView.el
);
},
});
@ -152,14 +154,17 @@
const filledConversations = conversations.map(async conv => {
const messages = await window.getMessagesByKey(conv.id);
return { ...conv, messages};
return { ...conv, messages };
});
const fullFilledConversations = await Promise.all(filledConversations);
const initialState = {
conversations: {
conversationLookup: Signal.Util.makeLookup(fullFilledConversations, 'id'),
conversationLookup: Signal.Util.makeLookup(
fullFilledConversations,
'id'
),
},
user: {
regionCode: window.storage.get('regionCode'),

@ -61,7 +61,7 @@ window.isBeforeVersion = (toCheck, baseVersion) => {
};
window.CONSTANTS = {
SECS_IN_DAY: 60*60*24,
SECS_IN_DAY: 60 * 60 * 24,
MAX_LOGIN_TRIES: 3,
MAX_PASSWORD_LENGTH: 32,
MAX_USERNAME_LENGTH: 20,
@ -74,9 +74,10 @@ window.CONSTANTS = {
DEFAULT_MEDIA_FETCH_COUNT: 50,
DEFAULT_DOCUMENTS_FETCH_COUNT: 150,
DEFAULT_MESSAGE_FETCH_COUNT: 30,
MAX_MESSAGE_FETCH_COUNT: 500,
// Pixels (scroll) from the top of the top of message container
// at which more messages should be loaded
MESSAGE_CONTAINER_BUFFER_OFFSET_PX: 300,
MESSAGE_CONTAINER_BUFFER_OFFSET_PX: 30,
MESSAGE_FETCH_INTERVAL: 1,
};

@ -92,22 +92,19 @@ $composition-container-height: 60px;
}
}
.session-emoji-panel {
position: absolute;
bottom: 68px;
right: 0px;
min-height: 400px;
min-width: 400px;
background-color: $session-shade-4;
border: 1px solid $session-shade-6;
border-top-right-radius: 3px;
border-top-left-radius: 3px;
padding: $session-margin-lg;
bottom: 68px;
right: 0px;
min-height: 400px;
min-width: 400px;
background-color: $session-shade-4;
border: 1px solid $session-shade-6;
border-top-right-radius: 3px;
border-top-left-radius: 3px;
padding: $session-margin-lg;
}
.session-progress {
position: relative;
background-color: rgba(30, 30, 30, 0.5);
@ -120,6 +117,5 @@ $composition-container-height: 60px;
height: 3px;
background-color: $session-color-green;
}
}
}

@ -80,7 +80,6 @@ export class LeftPane extends React.Component<Props, State> {
}
public render(): JSX.Element {
return (
<div className="module-left-pane-session">
<ActionsPanel

@ -677,8 +677,8 @@ export class Message extends React.PureComponent<Props, State> {
const shouldRenderAvatar =
(firstMessageOfSeries ||
! collapseMetadata ||
conversationType === 'group') &&
!collapseMetadata ||
conversationType === 'group') &&
direction === 'incoming';
return (

@ -70,7 +70,7 @@ export class TimerNotification extends React.Component<Props> {
return (
<div className="module-timer-notification">
<div className="module-timer-notification__icon-container">
{ !disabled && (
{!disabled && (
<SessionIcon
iconType={SessionIconType.Stopwatch}
iconSize={SessionIconSize.Large}

@ -58,12 +58,14 @@ export class Timestamp extends React.Component<Props> {
// Use relative time for under 24hrs ago.
const now = Math.floor(Date.now());
const messageAgeInDays = (now - timestamp) / (1000 * window.CONSTANTS.SECS_IN_DAY);
const messageAgeInDays =
(now - timestamp) / (1000 * window.CONSTANTS.SECS_IN_DAY);
const daysBeforeRelativeTiming = 1;
const dateString = messageAgeInDays > daysBeforeRelativeTiming
? formatRelativeTime(timestamp, { i18n, extended })
: moment(timestamp).fromNow();
const dateString =
messageAgeInDays > daysBeforeRelativeTiming
? formatRelativeTime(timestamp, { i18n, extended })
: moment(timestamp).fromNow();
return (
<span

@ -4,15 +4,14 @@ import TextareaAutosize from 'react-autosize-textarea';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { SessionEmojiPanel } from './SessionEmojiPanel';
interface Props {
placeholder?: string;
onSendMessage: any;
}
interface State {
message: string;
showEmojiPanel: boolean;
message: string;
showEmojiPanel: boolean;
}
export class SessionCompositionBox extends React.Component<Props, State> {
@ -22,8 +21,8 @@ export class SessionCompositionBox extends React.Component<Props, State> {
super(props);
this.state = {
message: '',
showEmojiPanel: false,
message: '',
showEmojiPanel: false,
};
this.textarea = React.createRef();
@ -69,10 +68,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
/>
</div>
{ showEmojiPanel &&
( <SessionEmojiPanel/> )
}
{showEmojiPanel && <SessionEmojiPanel />}
</div>
);
}
@ -80,6 +76,6 @@ export class SessionCompositionBox extends React.Component<Props, State> {
public toggleEmojiPanel() {
this.setState({
showEmojiPanel: !this.state.showEmojiPanel,
})
});
}
}

@ -40,8 +40,6 @@ export class SessionConversation extends React.Component<any, State> {
const conversation = this.props.conversations.conversationLookup[conversationKey];
const unreadCount = conversation.unreadCount;
console.log(`[vince][info] Conversation: `, conversation);
this.state = {
sendingProgess: 0,
prevSendingProgess: 0,
@ -76,6 +74,8 @@ export class SessionConversation extends React.Component<any, State> {
doneInitialScroll: true,
});
}, 100);
console.log(`[vince][info] HeaderProps:`, this.getHeaderProps());
}
public componentDidUpdate(){
@ -96,11 +96,17 @@ export class SessionConversation extends React.Component<any, State> {
}
render() {
console.log(`[vince][info] Props`, this.props);
console.log(`[vince][info] Unread: `, this.state.unreadCount);
console.log(`[vince][info] Messages:`, this.state.messages);
// const headerProps = this.props.getHeaderProps;
const { messages, conversationKey, doneInitialScroll } = this.state;
const loading = !doneInitialScroll || messages.length === 0;
const conversation = this.props.conversations.conversationLookup[conversationKey]
const conversation = this.props.conversations.conversationLookup[conversationKey];
const isRss = conversation.isRss;
return (
<div className="conversation-item">
@ -130,9 +136,12 @@ export class SessionConversation extends React.Component<any, State> {
</div>
<SessionCompositionBox
{ !isRss && (
<SessionCompositionBox
onSendMessage={() => null}
/>
/>
)}
</div>
);
}
@ -302,17 +311,30 @@ export class SessionConversation extends React.Component<any, State> {
);
}
public async getMessages(numMessages?: number, fetchInterval = window.CONSTANTS.MESSAGE_FETCH_INTERVAL){
public async getMessages(numMessages?: number, fetchInterval = window.CONSTANTS.MESSAGE_FETCH_INTERVAL, loopback = false){
const { conversationKey, messageFetchTimestamp } = this.state;
const timestamp = this.getTimestamp();
// If we have pulled messages in the last interval, don't bother rescanning
// This avoids getting messages on every re-render.
if (timestamp - messageFetchTimestamp < fetchInterval) {
const timeBuffer = timestamp - messageFetchTimestamp;
if (timeBuffer < fetchInterval) {
// Loopback gets messages after time has elapsed,
// rather than completely cancelling the fetch.
// if (loopback) {
// setTimeout(() => {
// this.getMessages(numMessages, fetchInterval, false);
// }, timeBuffer * 1000);
// }
return { newTopMessage: undefined, previousTopMessage: undefined };
}
const msgCount = numMessages || window.CONSTANTS.DEFAULT_MESSAGE_FETCH_COUNT + this.state.unreadCount;
let msgCount = numMessages || window.CONSTANTS.DEFAULT_MESSAGE_FETCH_COUNT + this.state.unreadCount;
msgCount = msgCount > window.CONSTANTS.MAX_MESSAGE_FETCH_COUNT
? window.CONSTANTS.MAX_MESSAGE_FETCH_COUNT
: msgCount;
const messageSet = await window.Signal.Data.getMessagesByConversation(
conversationKey,
{ limit: msgCount, MessageCollection: window.Whisper.MessageCollection },
@ -336,7 +358,7 @@ export class SessionConversation extends React.Component<any, State> {
const previousTopMessage = this.state.messages[0]?.id;
const newTopMessage = messages[0]?.id;
this.setState({ messages, messageFetchTimestamp });
await this.setState({ messages, messageFetchTimestamp: timestamp });
return { newTopMessage, previousTopMessage };
}
@ -367,8 +389,8 @@ export class SessionConversation extends React.Component<any, State> {
const numMessages = this.state.messages.length + window.CONSTANTS.DEFAULT_MESSAGE_FETCH_COUNT;
// Prevent grabbing messags with scroll more frequently than once per 5s.
const messageFetchInterval = 5;
const previousTopMessage = (await this.getMessages(numMessages, messageFetchInterval))?.previousTopMessage;
const messageFetchInterval = 2;
const previousTopMessage = (await this.getMessages(numMessages, messageFetchInterval, true))?.previousTopMessage;
previousTopMessage && this.scrollToMessage(previousTopMessage);
}
}
@ -394,5 +416,179 @@ export class SessionConversation extends React.Component<any, State> {
const messageContainer = document.getElementsByClassName('messages-container')[0];
messageContainer.scrollTop = messageContainer.scrollHeight - messageContainer.clientHeight;
}
public getHeaderProps() {
const conversationKey = this.props.conversations.selectedConversation;
const conversation = window.getConversationByKey(conversationKey);
console.log(`[vince][info] Key:`, conversationKey);
console.log(`[vince][info] Conversation`, conversation);
console.log(`[vince] Manual: `, );
const expireTimer = conversation.get('expireTimer');
const expirationSettingName = expireTimer
? window.Whisper.ExpirationTimerOptions.getName(expireTimer || 0)
: null;
const members = conversation.get('members') || [];
return {
id: conversation.id,
name: conversation.getName(),
phoneNumber: conversation.getNumber(),
profileName: conversation.getProfileName(),
color: conversation.getColor(),
avatarPath: conversation.getAvatarPath(),
isVerified: conversation.isVerified(),
isFriendRequestPending: conversation.isPendingFriendRequest(),
isFriend: conversation.isFriend(),
isMe: conversation.isMe(),
isClosable: conversation.isClosable(),
isBlocked: conversation.isBlocked(),
isGroup: !conversation.isPrivate(),
isOnline: conversation.isOnline(),
isArchived: conversation.get('isArchived'),
isPublic: conversation.isPublic(),
isRss: conversation.isRss(),
amMod: conversation.isModerator(
window.storage.get('primaryDevicePubKey')
),
members,
subscriberCount: conversation.get('subscriberCount'),
selectedMessages: conversation.selectedMessages,
expirationSettingName,
showBackButton: Boolean(conversation.panels && conversation.panels.length),
timerOptions: window.Whisper.ExpirationTimerOptions.map((item: any) => ({
name: item.getName(),
value: item.get('seconds'),
})),
hasNickname: !!conversation.getNickname(),
onSetDisappearingMessages: (seconds: any) =>
conversation.setDisappearingMessages(seconds),
onDeleteMessages: () => conversation.destroyMessages(),
onDeleteSelectedMessages: () => conversation.deleteSelectedMessages(),
onCloseOverlay: () => conversation.resetMessageSelection(),
onDeleteContact: () => conversation.deleteContact(),
onResetSession: () => conversation.endSession(),
// These are view only and don't update the Conversation model, so they
// need a manual update call.
onShowSafetyNumber: () => {
conversation.showSafetyNumber();
},
onShowAllMedia: async () => {
conversation.updateHeader();
},
onShowGroupMembers: async () => {
await conversation.showMembers();
conversation.updateHeader();
},
onGoBack: () => {
conversation.resetPanel();
conversation.updateHeader();
},
onBlockUser: () => {
conversation.block();
},
onUnblockUser: () => {
conversation.unblock();
},
onChangeNickname: () => {
conversation.changeNickname();
},
onClearNickname: () => {
conversation.setNickname(null);
},
onCopyPublicKey: () => {
conversation.copyPublicKey();
},
onArchive: () => {
conversation.unload('archive');
conversation.setArchived(true);
},
onMoveToInbox: () => {
conversation.setArchived(false);
},
onLeaveGroup: () => {
window.Whisper.events.trigger('leaveGroup', conversation);
},
onInviteFriends: () => {
window.Whisper.events.trigger('inviteFriends', conversation);
},
onAddModerators: () => {
window.Whisper.events.trigger('addModerators', conversation);
},
onRemoveModerators: () => {
window.Whisper.events.trigger('removeModerators', conversation);
},
onAvatarClick: (pubkey: any) => {
if (conversation.isPrivate()) {
window.Whisper.events.trigger('onShowUserDetails', {
userPubKey: pubkey,
});
} else if (!conversation.isRss()) {
conversation.showGroupSettings();
}
},
};
};
public getGroupSettingsProps() {
const {conversationKey} = this.state;
const conversation = window.getConversationByKey[conversationKey];
const ourPK = window.textsecure.storage.user.getNumber();
const members = conversation.get('members') || [];
return {
id: conversation.id,
name: conversation.getName(),
phoneNumber: conversation.getNumber(),
profileName: conversation.getProfileName(),
color: conversation.getColor(),
avatarPath: conversation.getAvatarPath(),
isGroup: !conversation.isPrivate(),
isPublic: conversation.isPublic(),
isAdmin: conversation.get('groupAdmins').includes(ourPK),
isRss: conversation.isRss(),
memberCount: members.length,
timerOptions: window.Whisper.ExpirationTimerOptions.map((item: any) => ({
name: item.getName(),
value: item.get('seconds'),
})),
onSetDisappearingMessages: (seconds: any) =>
conversation.setDisappearingMessages(seconds),
onGoBack: () => {
conversation.hideConversationRight();
},
onUpdateGroupName: () => {
window.Whisper.events.trigger('updateGroupName', conversation);
},
onUpdateGroupMembers: () => {
window.Whisper.events.trigger('updateGroupMembers', conversation);
},
onLeaveGroup: () => {
window.Whisper.events.trigger('leaveGroup', conversation);
},
onInviteFriends: () => {
window.Whisper.events.trigger('inviteFriends', conversation);
},
onShowLightBox: (lightBoxOptions = {}) => {
conversation.showChannelLightbox(lightBoxOptions);
},
};
};
}

@ -1,15 +1,13 @@
import React from 'react';
interface Props {}
interface State {
// FIXME Use Emoji-Mart categories
category: null
category: null;
}
export class SessionEmojiPanel extends React.Component<Props, State> {
constructor(props: any) {
super(props);
@ -19,11 +17,6 @@ export class SessionEmojiPanel extends React.Component<Props, State> {
}
render() {
return (
<div className='session-emoji-panel'>
THIS IS EMOJI STUFF
</div>
);
return <div className="session-emoji-panel">THIS IS EMOJI STUFF</div>;
}
}

@ -1,6 +1,5 @@
import React from 'react';
interface Props {
// Value ranges from 0 to 100
value: number;
@ -53,12 +52,14 @@ export class SessionProgress extends React.PureComponent<Props, State> {
// 1. Width depends on progress.
// 2. Opacity is the inverse of fade.
// 3. Transition duration scales with the
// 3. Transition duration scales with the
// distance it needs to travel
const style = {
width: `${this.state.value}%`,
opacity: `${Number(!startFade)}`,
transition: `width ${shiftDuration.toFixed(2)}s cubic-bezier(0.25, 0.46, 0.45, 0.94)`,
transition: `width ${shiftDuration.toFixed(
2
)}s cubic-bezier(0.25, 0.46, 0.45, 0.94)`,
};
if (value >= 100) {
@ -66,27 +67,23 @@ export class SessionProgress extends React.PureComponent<Props, State> {
}
return (
<div className="session-progress">
<div
className="session-progress__progress"
style={style}
>
&nbsp
</div>
<div className="session-progress">
<div className="session-progress__progress" style={style}>
&nbsp
</div>
</div>
);
}
public onComplete() {
const { fadeOnComplete } = this.props;
// Fade
if ( fadeOnComplete ) {
if (fadeOnComplete) {
this.setState({
startFade: true,
});
}
}
private getShiftDuration(value: number, prevValue?: number) {

@ -14,8 +14,8 @@ export class SessionScrollButton extends React.PureComponent<Props> {
public render() {
return (
<>
{ this.props.display && (
<>
{this.props.display && (
<div className="session-scroll-button">
<SessionIconButton
iconType={SessionIconType.Chevron}
@ -25,7 +25,7 @@ export class SessionScrollButton extends React.PureComponent<Props> {
/>
</div>
)}
</>
</>
);
}
}

2
ts/global.d.ts vendored

@ -16,7 +16,7 @@ interface Window {
getMessagesByKey: any;
getLastMessageByKey: any;
getFriendsFromContacts: any;
mnemonic: any;
clipboard: any;
attemptConnection: any;

@ -1,6 +1,4 @@
export const reducer = (state: any, action: any) => {
console.log(`[vince][redux] Action: `, action);
return state;
}
console.log(`[vince][redux] Action: `, action);
return state;
};

@ -157,7 +157,6 @@ const getMessageProps = (messages: Array<MessageType>) => {
});
};
async function doGetMessages(
query: string,
options: SearchOptions
@ -188,10 +187,6 @@ async function doGetMessages(
};
}
async function queryMessages(query: string) {
try {
const normalized = cleanSearchTerm(query);

@ -7,10 +7,8 @@ import {
} from './ducks/conversations';
import { reducer as user, UserStateType } from './ducks/user';
import { reducer as messages } from './ducks/search';
export type StateType = {
search: SearchStateType;
messages: any;

@ -22,7 +22,8 @@ export const getConversationLookup = createSelector(
}
);
export const getSelectedConversation = createSelector(getConversations,
export const getSelectedConversation = createSelector(
getConversations,
(state: ConversationsStateType): string | undefined => {
return state.selectedConversation;
}
@ -173,16 +174,15 @@ export const _getSessionConversationInfo = (
const sorted = values.sort(comparator);
let conversation;
const max = sorted.length;
const max = sorted.length;
for (let i = 0; i < max; i += 1) {
let conv = sorted[i];
if (conv.id === selectedConversation){
if (conv.id === selectedConversation) {
conversation = conv;
break;
}
}
return {
@ -202,7 +202,7 @@ export const getSessionConversationInfo = createSelector(
getConversationLookup,
getConversationComparator,
getSelectedConversation,
_getSessionConversationInfo,
_getSessionConversationInfo
);
export const getMe = createSelector(

@ -11,8 +11,6 @@ import {
getUserNumber,
} from '../selectors/user';
const mapStateToProps = (state: StateType) => {
//const conversationInfo = getSessionConversationInfo(state);
@ -24,7 +22,7 @@ const mapStateToProps = (state: StateType) => {
return {
conversations: state.conversations,
}
};
};
const smart = connect(mapStateToProps, mapDispatchToProps);

Loading…
Cancel
Save