diff --git a/app/containers/MessageActions/index.tsx b/app/containers/MessageActions/index.tsx
index 466a70ce51..b5e80fbcc9 100644
--- a/app/containers/MessageActions/index.tsx
+++ b/app/containers/MessageActions/index.tsx
@@ -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';
@@ -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);
}
diff --git a/app/containers/message/Message.tsx b/app/containers/message/Message.tsx
index ce32c546e6..8f811872ca 100644
--- a/app/containers/message/Message.tsx
+++ b/app/containers/message/Message.tsx
@@ -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 ? : null;
// Prevent misalignment of info when the font size is increased.
@@ -195,7 +197,7 @@ const Message = React.memo((props: IMessageTouchable & IMessage) => {
- {!props.isHeader ? (
+ {showRightIcons ? (
= 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;
+};
diff --git a/app/lib/methods/subscriptions/room.ts b/app/lib/methods/subscriptions/room.ts
index a0ce1310b2..93083284c5 100644
--- a/app/lib/methods/subscriptions/room.ts
+++ b/app/lib/methods/subscriptions/room.ts
@@ -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';
@@ -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);
+ }
})
)
);