Merge pull request #165 from Mikunj/online-indicator

Online indicator
pull/167/head
Beaudan Campbell-Brown 6 years ago committed by GitHub
commit 5f49c5aafd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,4 @@
/* global _, Whisper, Backbone, storage */
/* global _, Whisper, Backbone, storage, lokiP2pAPI */
/* eslint-disable more/no-then */
@ -248,6 +248,14 @@
async load() {
window.log.info('ConversationController: starting initial fetch');
// We setup online and offline listeners here because we want
// to minimize the amount of listeners we have to avoid memory leaks
if (!this.p2pListenersSet) {
lokiP2pAPI.on('online', this._handleOnline.bind(this));
lokiP2pAPI.on('offline', this._handleOffline.bind(this));
this.p2pListenersSet = true;
}
if (conversations.length) {
throw new Error('ConversationController: Already loaded!');
}
@ -268,7 +276,6 @@
conversation.updateProfile(),
conversation.updateProfileAvatar(),
conversation.resetPendingSend(),
conversation.updateProfile(),
]);
});
await Promise.all(promises);
@ -292,5 +299,17 @@
return this._initialPromise;
},
_handleOnline(pubKey) {
try {
const conversation = this.get(pubKey);
conversation.set({ isOnline: true });
} catch (e) {} // eslint-disable-line
},
_handleOffline(pubKey) {
try {
const conversation = this.get(pubKey);
conversation.set({ isOnline: false });
} catch (e) {} // eslint-disable-line
},
};
})();

@ -7,6 +7,7 @@
/* global storage: false */
/* global textsecure: false */
/* global Whisper: false */
/* global lokiP2pAPI: false */
/* eslint-disable more/no-then */
@ -77,6 +78,7 @@
unlockTimestamp: null, // Timestamp used for expiring friend requests.
sessionResetStatus: SessionResetEnum.none,
swarmNodes: new Set([]),
isOnline: false,
};
},
@ -155,6 +157,9 @@
this.setFriendRequestExpiryTimeout();
this.typingRefreshTimer = null;
this.typingPauseTimer = null;
// Online status handling
this.set({ isOnline: lokiP2pAPI.isOnline(this.id) });
},
isMe() {
@ -386,6 +391,7 @@
status: this.lastMessageStatus,
text: this.lastMessage,
},
isOnline: this.get('isOnline'),
onClick: () => this.trigger('select', this),
};

@ -11,6 +11,7 @@ const {
map,
merge,
set,
omit,
} = require('lodash');
const { base64ToArrayBuffer, arrayBufferToBase64 } = require('./crypto');
@ -688,11 +689,13 @@ async function getConversationCount() {
}
async function saveConversation(data) {
await channels.saveConversation(data);
const cleaned = omit(data, 'isOnline');
await channels.saveConversation(cleaned);
}
async function saveConversations(data) {
await channels.saveConversations(data);
const cleaned = data.map(d => omit(d, 'isOnline'));
await channels.saveConversations(cleaned);
}
async function getConversationById(id, { Conversation }) {
@ -712,7 +715,10 @@ async function updateConversation(id, data, { Conversation }) {
if (merged.swarmNodes instanceof Set) {
merged.swarmNodes = Array.from(merged.swarmNodes);
}
await channels.updateConversation(merged);
// Don't save the online status of the object
const cleaned = omit(merged, 'isOnline');
await channels.updateConversation(cleaned);
}
async function removeConversation(id, { Conversation }) {

@ -16,7 +16,10 @@ class LokiP2pAPI extends EventEmitter {
? 60 * 1000 // 1 minute
: 2 * 60 * 1000; // 2 minutes
if (this.contactP2pDetails[pubKey] && this.contactP2pDetails[pubKey].pingTimer) {
if (
this.contactP2pDetails[pubKey] &&
this.contactP2pDetails[pubKey].pingTimer
) {
clearTimeout(this.contactP2pDetails[pubKey].pingTimer);
}
this.contactP2pDetails[pubKey] = {
@ -66,6 +69,12 @@ class LokiP2pAPI extends EventEmitter {
);
}
isOnline(pubKey) {
return !!(
this.contactP2pDetails[pubKey] && this.contactP2pDetails[pubKey].isOnline
);
}
pingContact(pubKey) {
if (!this.contactP2pDetails[pubKey]) {
return;

@ -60,6 +60,7 @@
const props = this.model.getPropsForListItem();
delete props.lastMessage;
delete props.lastUpdated;
delete props.isSelected;
return props;
},

@ -1767,6 +1767,10 @@
.module-avatar {
background-color: $color-dark-85;
}
.module-contact-name {
margin-right: 0px;
}
}
.module-conversation-list-item--has-unread {
@ -1822,12 +1826,10 @@
.module-conversation-list-item__content {
flex-grow: 1;
margin-left: 12px;
// parent - 48px (for avatar) - 16px (our right margin)
max-width: calc(100% - 64px);
display: flex;
flex-direction: column;
align-items: stretch;
overflow: hidden;
}
.module-conversation-list-item__header {

@ -555,6 +555,7 @@ describe('Backup', () => {
'friendRequestStatus',
'unlockTimestamp',
'sessionResetStatus',
'isOnline',
];
const conversationFromDB = conversationCollection.at(0).attributes;
console.log({ conversationFromDB, conversation });

@ -13,6 +13,7 @@ interface Props {
phoneNumber?: string;
profileName?: string;
size: number;
borderColor?: string;
}
interface State {
@ -41,7 +42,14 @@ export class Avatar extends React.Component<Props, State> {
}
public renderImage() {
const { avatarPath, i18n, name, phoneNumber, profileName } = this.props;
const {
avatarPath,
i18n,
name,
phoneNumber,
profileName,
borderColor,
} = this.props;
const { imageBroken } = this.state;
const hasImage = avatarPath && !imageBroken;
@ -53,8 +61,16 @@ export class Avatar extends React.Component<Props, State> {
!name && profileName ? ` ~${profileName}` : ''
}`;
const borderStyle = borderColor
? {
borderColor: borderColor,
borderStyle: 'solid',
}
: undefined;
return (
<img
style={borderStyle}
onError={this.handleImageErrorBound}
alt={i18n('contactAvatarAlt', [title])}
src={avatarPath}
@ -63,11 +79,18 @@ export class Avatar extends React.Component<Props, State> {
}
public renderNoImage() {
const { conversationType, name, size } = this.props;
const { conversationType, name, size, borderColor } = this.props;
const initials = getInitials(name);
const isGroup = conversationType === 'group';
const borderStyle = borderColor
? {
borderColor: borderColor,
borderStyle: 'solid',
}
: undefined;
if (!isGroup && initials) {
return (
<div
@ -75,6 +98,7 @@ export class Avatar extends React.Component<Props, State> {
'module-avatar__label',
`module-avatar__label--${size}`
)}
style={borderStyle}
>
{initials}
</div>
@ -88,6 +112,7 @@ export class Avatar extends React.Component<Props, State> {
`module-avatar__icon--${conversationType}`,
`module-avatar__icon--${size}`
)}
style={borderStyle}
/>
);
}

@ -28,6 +28,7 @@ interface Props {
};
showFriendRequestIndicator?: boolean;
isBlocked: boolean;
isOnline: boolean;
i18n: Localizer;
onClick?: () => void;
@ -43,6 +44,7 @@ export class ConversationListItem extends React.Component<Props> {
name,
phoneNumber,
profileName,
isOnline,
} = this.props;
return (
@ -56,6 +58,7 @@ export class ConversationListItem extends React.Component<Props> {
phoneNumber={phoneNumber}
profileName={profileName}
size={48}
borderColor={isOnline ? '#1c8260' : '#3d3e44'}
/>
{this.renderUnread()}
</div>

Loading…
Cancel
Save