feat: moved convo model interaction props into the lastMessage logic

this makes it easier to interact with since if an interaction fails we save it to the db as a message
pull/2789/head
William Grant 2 years ago
parent 027b412fb2
commit 24776c0d5c

@ -14,7 +14,7 @@ import {
ConversationInteractionType,
updateConversationInteractionState,
} from '../../interactions/conversationInteractions';
import { useConversationInteractionPropsById } from '../../hooks/useParamSelector';
import { useLastMessage } from '../../hooks/useParamSelector';
import styled from 'styled-components';
const StyledMessageText = styled(SessionHtmlRenderer)`
@ -84,7 +84,7 @@ export const SessionConfirm = (props: SessionConfirmDialogProps) => {
conversationId,
} = props;
const interactionProps = useConversationInteractionPropsById(conversationId);
const lastMessage = useLastMessage(conversationId);
const [isLoading, setIsLoading] = useState(false);
@ -130,15 +130,15 @@ export const SessionConfirm = (props: SessionConfirmDialogProps) => {
useEffect(() => {
if (isLoading) {
if (conversationId && interactionProps?.interactionType) {
if (conversationId && lastMessage?.interactionType) {
void updateConversationInteractionState({
conversationId,
type: interactionProps?.interactionType,
type: lastMessage?.interactionType,
status: ConversationInteractionStatus.Loading,
});
}
}
}, [isLoading, conversationId, interactionProps?.interactionType]);
}, [isLoading, conversationId, lastMessage?.interactionType]);
return (
<SessionWrapperModal

@ -5,10 +5,8 @@ import { useIsPrivate, useIsPublic } from '../../../hooks/useParamSelector';
import { MessageBody } from '../../conversation/message/message-content/MessageBody';
import { assertUnreachable } from '../../../types/sqlSharedTypes';
import {
ConversationInteractionProps,
ConversationInteractionStatus,
ConversationInteractionType,
clearConversationInteractionState,
} from '../../../interactions/conversationInteractions';
import styled from 'styled-components';
import { getConversationController } from '../../../session/conversations';
@ -18,40 +16,42 @@ const StyledInteractionItemText = styled.div<{ isError: boolean }>`
${props => props.isError && 'color: var(--danger-color) !important;'}
`;
type InteractionItemProps = ConversationInteractionProps & {
lastMessage?: LastMessageType | null;
type InteractionItemProps = {
conversationId: string;
lastMessage: LastMessageType | null;
};
export const InteractionItem = (props: InteractionItemProps) => {
const { conversationId, interactionStatus, interactionType, lastMessage } = props;
const { conversationId, lastMessage } = props;
const isGroup = !useIsPrivate(conversationId);
const isCommunity = useIsPublic(conversationId);
const [storedLastMessageId, setStoredLastMessageId] = useState(lastMessage?.id);
if (!lastMessage) {
return null;
}
const { interactionType, interactionStatus } = lastMessage;
if (!interactionType || !interactionStatus) {
return null;
}
const [storedLastMessageText, setStoredLastMessageText] = useState(lastMessage?.text);
const [storedLastMessageInteractionStatus, setStoredLastMessageInteractionStatus] = useState(
lastMessage?.interactionStatus
);
// NOTE we want to reset the interaction state when the last message changes
useEffect(() => {
if (conversationId) {
const convo = getConversationController().get(conversationId);
window.log.debug(
`WIP: storedLastMessageId "${storedLastMessageId}" convo.get('lastMessageId') "${convo.get(
'lastMessageId'
)}' storedLastMessageText ${storedLastMessageText} text ${text}`
);
if (storedLastMessageId !== convo.get('lastMessageId')) {
setStoredLastMessageId(convo.get('lastMessageId'));
if (storedLastMessageInteractionStatus !== convo.get('lastMessageInteractionStatus')) {
setStoredLastMessageInteractionStatus(convo.get('lastMessageInteractionStatus'));
setStoredLastMessageText(convo.get('lastMessage'));
void clearConversationInteractionState({ conversationId });
}
}
}, [conversationId, interactionStatus, lastMessage?.id]);
if (isEmpty(conversationId) || isEmpty(interactionType) || isEmpty(interactionStatus)) {
return null;
}
}, [conversationId, interactionStatus, lastMessage?.interactionStatus]);
let text = storedLastMessageText || '';
let failText = '';

@ -3,7 +3,6 @@ import { isEmpty } from 'lodash';
import React from 'react';
import { useSelector } from 'react-redux';
import {
useConversationInteractionPropsById,
useHasUnread,
useIsPrivate,
useIsTyping,
@ -28,10 +27,8 @@ export const MessageItem = () => {
const isSearchingMode = useSelector(isSearching);
const interactionProps = useConversationInteractionPropsById(conversationId);
if (!isConvoTyping && interactionProps) {
return <InteractionItem lastMessage={lastMessage} {...interactionProps} />;
if (!isConvoTyping && lastMessage?.interactionType && lastMessage?.interactionStatus) {
return <InteractionItem conversationId={conversationId} lastMessage={lastMessage} />;
}
if (!lastMessage && !isConvoTyping) {

@ -10,7 +10,6 @@ import { StateType } from '../state/reducer';
import { getMessageReactsProps } from '../state/selectors/conversations';
import { isPrivateAndFriend } from '../state/selectors/selectedConversation';
import { CONVERSATION } from '../session/constants';
import { ConversationInteractionProps } from '../interactions/conversationInteractions';
export function useAvatarPath(convoId: string | undefined) {
const convoProps = useConversationPropsById(convoId);
@ -269,29 +268,14 @@ export function useIsTyping(conversationId?: string): boolean {
return useConversationPropsById(conversationId)?.isTyping || false;
}
export function useLastMessage(convoId: string) {
const convoProps = useConversationPropsById(convoId);
if (!convoProps) {
return null;
}
return convoProps.lastMessage;
}
export function useConversationInteractionPropsById(
conversationId?: string
): ConversationInteractionProps | null {
if (!conversationId) {
export function useLastMessage(convoId?: string) {
if (!convoId) {
return null;
}
const convoProps = useConversationPropsById(conversationId);
if (!convoProps || !convoProps.interactionType || !convoProps.interactionStatus) {
const convoProps = useConversationPropsById(convoId);
if (!convoProps) {
return null;
}
const interactionType = convoProps.interactionType;
const interactionStatus = convoProps.interactionStatus;
return { conversationId, interactionType, interactionStatus };
return convoProps.lastMessage;
}

@ -43,7 +43,6 @@ import { encryptProfile } from '../util/crypto/profileEncrypter';
import { ReleasedFeatures } from '../util/releaseFeature';
import { Storage, setLastProfileUpdateTimestamp } from '../util/storage';
import { UserGroupsWrapperActions } from '../webworker/workers/browser/libsession_worker_interface';
import { ConversationModel } from '../models/conversation';
export enum ConversationInteractionStatus {
Start = 'start',
@ -57,12 +56,6 @@ export enum ConversationInteractionType {
Leave = 'leave',
}
export type ConversationInteractionProps = {
conversationId: string;
interactionType: ConversationInteractionType;
interactionStatus: ConversationInteractionStatus;
};
export async function copyPublicKeyByConvoId(convoId: string) {
if (OpenGroupUtils.isOpenGroupV2(convoId)) {
const fromWrapper = await UserGroupsWrapperActions.getCommunityByFullUrl(convoId);
@ -460,7 +453,8 @@ export async function deleteAllMessagesByConvoIdNoConfirmation(conversationId: s
// conversation still appears on the conversation list but is empty
conversation.set({
lastMessage: null,
lastMessageId: null,
lastMessageInteractionType: null,
lastMessageInteractionStatus: null,
});
await conversation.commit();
@ -708,8 +702,8 @@ export async function updateConversationInteractionState({
}) {
const convo = getConversationController().get(conversationId);
if (convo) {
convo.set('interactionType', type);
convo.set('interactionStatus', status);
convo.set('lastMessageInteractionType', type);
convo.set('lastMessageInteractionStatus', status);
await convo.commit();
window.log.debug(
@ -729,8 +723,8 @@ export async function clearConversationInteractionState({
}) {
const convo = getConversationController().get(conversationId);
if (convo) {
convo.set('interactionType', undefined);
convo.set('interactionStatus', undefined);
convo.set('lastMessageInteractionType', undefined);
convo.set('lastMessageInteractionStatus', undefined);
await convo.commit();
window.log.debug(`WIP: clearConversationInteractionState() for ${conversationId}`);

@ -368,13 +368,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
}
}
if (this.get('interactionType')) {
toRet.interactionType = this.get('interactionType');
}
if (this.get('interactionStatus')) {
toRet.interactionStatus = this.get('interactionStatus');
}
// -- Handle the field stored only in memory for all types of conversation--
const inMemoryConvoInfo = inMemoryConvoInfos.get(this.id);
if (inMemoryConvoInfo) {
@ -389,13 +382,15 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// -- Handle the last message status, if present --
const lastMessageText = this.get('lastMessage');
if (lastMessageText && lastMessageText.length) {
const lastMessageId = this.get('lastMessageId');
const lastMessageStatus = this.get('lastMessageStatus');
const lastMessageInteractionType = this.get('lastMessageInteractionType');
const lastMessageInteractionStatus = this.get('lastMessageInteractionStatus');
toRet.lastMessage = {
id: lastMessageId,
status: lastMessageStatus,
text: lastMessageText,
interactionType: lastMessageInteractionType,
interactionStatus: lastMessageInteractionStatus,
};
}
return toRet;
@ -755,10 +750,18 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
this.set({
lastMessage: messageModel.getNotificationText(),
lastMessageId: messageModel.get('id'),
lastMessageStatus: 'sending',
active_at: networkTimestamp,
});
if (messageModel.get('interactionNotification')) {
this.set({
lastMessageInteractionType: messageModel.get('interactionNotification')?.interactionType,
lastMessageInteractionStatus: messageModel.get('interactionNotification')
?.interactionStatus,
});
}
await this.commit();
void this.queueJob(async () => {
@ -1856,7 +1859,10 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
return;
}
const lastMessageModel = messages.at(0);
const lastMessageId = lastMessageModel.get('id');
const lastMessageInteractionType = lastMessageModel.get('interactionNotification')
?.interactionType;
const lastMessageInteractionStatus = lastMessageModel.get('interactionNotification')
?.interactionStatus;
const lastMessageStatus = lastMessageModel.getMessagePropStatus() || undefined;
const lastMessageNotificationText = lastMessageModel.getNotificationText() || undefined;
// we just want to set the `status` to `undefined` if there are no `lastMessageNotificationText`
@ -1864,22 +1870,31 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
!!lastMessageNotificationText && !isEmpty(lastMessageNotificationText)
? {
lastMessage: lastMessageNotificationText || '',
lastMessageId,
lastMessageStatus,
lastMessageInteractionType,
lastMessageInteractionStatus,
}
: { lastMessage: '', lastMessageId: '', lastMessageStatus: undefined };
: {
lastMessage: '',
lastMessageStatus: undefined,
lastMessageInteractionType: undefined,
lastMessageInteractionStatus: undefined,
};
const existingLastMessageAttribute = this.get('lastMessage');
const existingLastMessageId = this.get('lastMessageId');
const existingLastMessageStatus = this.get('lastMessageStatus');
const existingLastMessageInteractionType = this.get('lastMessageInteractionType');
const existingLastMessageInteractionStatus = this.get('lastMessageInteractionStatus');
if (
lastMessageUpdate.lastMessage !== existingLastMessageAttribute ||
lastMessageUpdate.lastMessageStatus !== existingLastMessageStatus ||
lastMessageUpdate.lastMessageId !== existingLastMessageId
lastMessageUpdate.lastMessageInteractionType !== existingLastMessageInteractionType ||
lastMessageUpdate.lastMessageInteractionStatus !== existingLastMessageInteractionStatus
) {
if (
lastMessageUpdate.lastMessageId === existingLastMessageId &&
lastMessageUpdate.lastMessageStatus === existingLastMessageStatus &&
lastMessageUpdate.lastMessageInteractionType === existingLastMessageInteractionType &&
lastMessageUpdate.lastMessageInteractionStatus === existingLastMessageInteractionStatus &&
lastMessageUpdate.lastMessage &&
lastMessageUpdate.lastMessage.length > 40 &&
existingLastMessageAttribute &&

@ -66,8 +66,9 @@ export interface ConversationAttributes {
* The shortening is made in sql.ts directly.
*/
lastMessage: string | null;
lastMessageId: string | null;
lastMessageStatus: LastMessageStatusType;
lastMessageInteractionType: ConversationInteractionType | null;
lastMessageInteractionStatus: ConversationInteractionStatus | null;
avatarImageId?: number; // avatar imageID is currently used only for sogs. It's the fileID of the image uploaded and set as the sogs avatar (not only sogs I think, but our profile too?)
@ -108,8 +109,6 @@ export interface ConversationAttributes {
markedAsUnread: boolean; // Force the conversation as unread even if all the messages are read. Used to highlight a conversation the user wants to check again later, synced.
// the last interaction we had with this conversation e.g. failed to leave a group
interactionType?: ConversationInteractionType; // e.g. Leave
interactionStatus?: ConversationInteractionStatus; // e.g. Error
}
/**
@ -131,8 +130,9 @@ export const fillConvoAttributesWithDefaults = (
active_at: 0,
lastMessage: null,
lastMessageId: null,
lastMessageStatus: undefined,
lastMessageInteractionType: null,
lastMessageInteractionStatus: null,
triggerNotificationsFor: 'all', // if the settings is not set in the db, this is the default

@ -58,8 +58,9 @@ const allowedKeysFormatRowOfConversation = [
'isKickedFromGroup',
'left',
'lastMessage',
'lastMessageId',
'lastMessageStatus',
'lastMessageInteractionType',
'lastMessageInteractionStatus',
'triggerNotificationsFor',
'unreadCount',
'lastJoinedTimestamp',
@ -76,8 +77,6 @@ const allowedKeysFormatRowOfConversation = [
'conversationIdOrigin',
'markedAsUnread',
'priority',
'interactionType',
'interactionStatus',
];
// tslint:disable: cyclomatic-complexity
export function formatRowOfConversation(
@ -139,14 +138,18 @@ export function formatRowOfConversation(
convo.lastMessage = null;
}
if (!convo.lastMessageId) {
convo.lastMessageId = null;
}
if (!convo.lastMessageStatus) {
convo.lastMessageStatus = undefined;
}
if (!convo.lastMessageInteractionType) {
convo.lastMessageInteractionType = null;
}
if (!convo.lastMessageInteractionStatus) {
convo.lastMessageInteractionStatus = null;
}
if (!convo.triggerNotificationsFor) {
convo.triggerNotificationsFor = 'all';
}
@ -183,8 +186,9 @@ const allowedKeysOfConversationAttributes = [
'isKickedFromGroup',
'left',
'lastMessage',
'lastMessageId',
'lastMessageStatus',
'lastMessageInteractionType',
'lastMessageInteractionStatus',
'triggerNotificationsFor',
'lastJoinedTimestamp',
'expireTimer',
@ -200,8 +204,6 @@ const allowedKeysOfConversationAttributes = [
'conversationIdOrigin',
'markedAsUnread',
'priority',
'interactionType',
'interactionStatus',
];
/**

@ -1842,11 +1842,13 @@ function updateToSessionSchemaVersion32(currentVersion: number, db: BetterSqlite
console.log(`updateToSessionSchemaVersion${targetVersion}: starting...`);
db.transaction(() => {
db.prepare(`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN interactionType TEXT;`).run();
db.prepare(`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN interactionStatus TEXT;`).run();
db.prepare(
`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN lastMessageInteractionType TEXT;`
).run();
db.prepare(`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN lastMessageId TEXT;`).run();
db.prepare(
`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN lastMessageInteractionStatus TEXT;`
).run();
writeSessionSchemaVersion(targetVersion, db);
})();

@ -428,8 +428,9 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
left,
expireTimer,
lastMessage,
lastMessageId,
lastMessageStatus,
lastMessageInteractionType,
lastMessageInteractionStatus,
lastJoinedTimestamp,
groupAdmins,
isKickedFromGroup,
@ -444,8 +445,6 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
conversationIdOrigin,
priority,
markedAsUnread,
interactionType,
interactionStatus,
} = formatted;
const omited = omit(formatted);
@ -479,8 +478,9 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
left: toSqliteBoolean(left),
expireTimer,
lastMessage: shortenedLastMessage,
lastMessageId,
lastMessageStatus,
lastMessageInteractionType,
lastMessageInteractionStatus,
lastJoinedTimestamp,
groupAdmins: groupAdmins && groupAdmins.length ? arrayStrToJson(groupAdmins) : '[]',
@ -496,8 +496,6 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
displayNameInProfile,
conversationIdOrigin,
markedAsUnread: toSqliteBoolean(markedAsUnread),
interactionType,
interactionStatus,
});
return fetchConvoMemoryDetails(id);

@ -276,7 +276,8 @@ async function handleRegularMessage(
conversation.set({
active_at: message.get('sent_at'),
lastMessage: message.getNotificationText(),
lastMessageId: message.get('id'),
lastMessageInteractionType: message.get('interactionNotification')?.interactionType,
lastMessageInteractionStatus: message.get('interactionNotification')?.interactionStatus,
});
// a new message was received for that conversation. If it was not it should not be hidden anymore
await conversation.unhideIfNeeded(false);

@ -226,11 +226,13 @@ export type PropsForMessageWithConvoProps = PropsForMessageWithoutConvoProps & {
};
export type LastMessageType = {
id: string | null;
status: LastMessageStatusType;
text: string | null;
interactionType: ConversationInteractionType | null;
interactionStatus: ConversationInteractionStatus | null;
};
// NOTE This is used for failed interactions that are saved as messages to the db and rendered in a conversation
export type InteractionNotificationType = {
interactionType: ConversationInteractionType;
interactionStatus: ConversationInteractionStatus;
@ -282,9 +284,6 @@ export interface ReduxConversationType {
didApproveMe?: boolean;
isMarkedUnread?: boolean;
interactionType?: ConversationInteractionType;
interactionStatus?: ConversationInteractionStatus;
}
export interface NotificationForConvoOption {

Loading…
Cancel
Save