Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion app/containers/MessageActions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { connect } from 'react-redux';
import dayjs from '../../lib/dayjs';
import database from '../../lib/database';
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
import { getMessageById } from '../../lib/database/services/Message';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
import { registerOptimisticUpdate } from '../../lib/methods/helpers/optimisticUpdates';
import I18n from '../../i18n';
import log, { logEvent } from '../../lib/methods/helpers/log';
import Navigation from '../../lib/navigation/appNavigation';
Expand Down Expand Up @@ -318,10 +321,45 @@ const MessageActions = React.memo(
};

const handlePin = async (message: TAnyMessageModel) => {
const currentPinned = Boolean(message.pinned);
const willPin = !currentPinned;
logEvent(events.ROOM_MSG_ACTION_PIN);

try {
await togglePinMessage(message.id, message.pinned as boolean); // TODO: reevaluate `message.pinned` type on IMessage
const db = database.active;
const messageRecord = await getMessageById(message.id);
if (messageRecord) {
await db.write(async () => {
await messageRecord.update(
protectedFunction((m: any) => {
m.pinned = willPin;
})
);
});
registerOptimisticUpdate(message.id, { pinned: willPin });
}
} catch (optimisticError) {
// Do nothing
}

try {
await togglePinMessage(message.id, currentPinned); // TODO: reevaluate `message.pinned` type on IMessage
} catch (e) {
try {
const db = database.active;
const messageRecord = await getMessageById(message.id);
if (messageRecord) {
await db.write(async () => {
await messageRecord.update(
protectedFunction((m: any) => {
m.pinned = currentPinned;
})
);
});
}
} catch (revertError) {
// Do nothing
}
logEvent(events.ROOM_MSG_ACTION_PIN_F);
log(e);
}
Expand Down
4 changes: 3 additions & 1 deletion app/containers/message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ const Message = React.memo((props: IMessageTouchable & IMessage) => {
: `${user} ${hour} ${translated} ${label}. ${encryptedMessageLabel} ${readReceipt}`;
};

const showRightIcons = !props.isHeader;

if (props.isThreadReply || props.isThreadSequential || props.isInfo || props.isIgnored) {
const thread = props.isThreadReply ? <RepliedThread {...props} /> : null;
// Prevent misalignment of info when the font size is increased.
Expand Down Expand Up @@ -195,7 +197,7 @@ const Message = React.memo((props: IMessageTouchable & IMessage) => {
<View style={styles.messageContent}>
<MessageInner {...props} />
</View>
{!props.isHeader ? (
{showRightIcons ? (
<RightIcons
type={props.type}
msg={props.msg}
Expand Down
42 changes: 42 additions & 0 deletions app/lib/methods/helpers/optimisticUpdates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
interface IOptimisticUpdate {
pinned?: boolean;
timestamp: number;
}

const optimisticUpdates: Map<string, IOptimisticUpdate> = new Map();
const CLEANUP_THRESHOLD = 10000;

const cleanupStaleUpdates = (maxAge: number = CLEANUP_THRESHOLD) => {
const now = Date.now();
for (const [messageId, update] of optimisticUpdates.entries()) {
const age = now - update.timestamp;
if (age >= maxAge) {
optimisticUpdates.delete(messageId);
}
}
};

export const registerOptimisticUpdate = (messageId: string, update: { pinned?: boolean }) => {
cleanupStaleUpdates();
optimisticUpdates.set(messageId, {
...update,
timestamp: Date.now()
});
};

export const getOptimisticUpdate = (messageId: string): IOptimisticUpdate | undefined => {
cleanupStaleUpdates();
return optimisticUpdates.get(messageId);
};

export const clearOptimisticUpdate = (messageId: string) => {
optimisticUpdates.delete(messageId);
};

export const isRecentOptimisticUpdate = (messageId: string, maxAge: number = 2000): boolean => {
cleanupStaleUpdates();
const update = optimisticUpdates.get(messageId);
if (!update) return false;
const age = Date.now() - update.timestamp;
return age < maxAge;
};
19 changes: 18 additions & 1 deletion app/lib/methods/subscriptions/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Q } from '@nozbe/watermelondb';
import log from '../helpers/log';
import protectedFunction from '../helpers/protectedFunction';
import buildMessage from '../helpers/buildMessage';
import { getOptimisticUpdate, isRecentOptimisticUpdate, clearOptimisticUpdate } from '../helpers/optimisticUpdates';
import database from '../../database';
import { getMessageById } from '../../database/services/Message';
import { getThreadById } from '../../database/services/Thread';
Expand Down Expand Up @@ -281,7 +282,23 @@ export default class RoomSubscription {
batch.push(
messageRecord.prepareUpdate(
protectedFunction((m: TMessageModel) => {
Object.assign(m, message);
const optimisticUpdate = getOptimisticUpdate(message._id);
const isRecentOptimistic = isRecentOptimisticUpdate(message._id, 2000);

const { pinned: _pinned, ...restMessage } = message;
Object.assign(m, restMessage);

if (message.pinned !== undefined) {
if (isRecentOptimistic && optimisticUpdate?.pinned !== undefined) {
m.pinned = optimisticUpdate.pinned;
} else {
m.pinned = message.pinned;
}
}

if (isRecentOptimistic) {
clearOptimisticUpdate(message._id);
}
})
)
);
Expand Down