diff --git a/app/containers/EmojiPicker/index.js b/app/containers/EmojiPicker/index.js
index 1b0a7c172..05ec129c1 100644
--- a/app/containers/EmojiPicker/index.js
+++ b/app/containers/EmojiPicker/index.js
@@ -91,22 +91,24 @@ class EmojiPicker extends Component {
_addFrequentlyUsed = protectedFunction(async(emoji) => {
const db = database.active;
const freqEmojiCollection = db.collections.get('frequently_used_emojis');
+ let freqEmojiRecord;
+ try {
+ freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
+ } catch (error) {
+ // Do nothing
+ }
+
await db.action(async() => {
- try {
- const freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
+ if (freqEmojiRecord) {
await freqEmojiRecord.update((f) => {
f.count += 1;
});
- } catch (error) {
- try {
- await freqEmojiCollection.create((f) => {
- f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
- Object.assign(f, emoji);
- f.count = 1;
- });
- } catch (e) {
- // Do nothing
- }
+ } else {
+ await freqEmojiCollection.create((f) => {
+ f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
+ Object.assign(f, emoji);
+ f.count = 1;
+ });
}
});
})
diff --git a/app/containers/MessageErrorActions.js b/app/containers/MessageErrorActions.js
index d42cd0330..d106da635 100644
--- a/app/containers/MessageErrorActions.js
+++ b/app/containers/MessageErrorActions.js
@@ -6,11 +6,13 @@ import RocketChat from '../lib/rocketchat';
import database from '../lib/database';
import protectedFunction from '../lib/methods/helpers/protectedFunction';
import I18n from '../i18n';
+import log from '../utils/log';
class MessageErrorActions extends React.Component {
static propTypes = {
actionsHide: PropTypes.func.isRequired,
- message: PropTypes.object
+ message: PropTypes.object,
+ tmid: PropTypes.string
};
// eslint-disable-next-line react/sort-comp
@@ -27,17 +29,66 @@ class MessageErrorActions extends React.Component {
}
handleResend = protectedFunction(async() => {
- const { message } = this.props;
- await RocketChat.resendMessage(message);
+ const { message, tmid } = this.props;
+ await RocketChat.resendMessage(message, tmid);
});
- handleDelete = protectedFunction(async() => {
- const { message } = this.props;
- const db = database.active;
- await db.action(async() => {
- await message.destroyPermanently();
- });
- })
+ handleDelete = async() => {
+ try {
+ const { message, tmid } = this.props;
+ const db = database.active;
+ const deleteBatch = [];
+ const msgCollection = db.collections.get('messages');
+ const threadCollection = db.collections.get('threads');
+
+ // Delete the object (it can be Message or ThreadMessage instance)
+ deleteBatch.push(message.prepareDestroyPermanently());
+
+ // If it's a thread, we find and delete the whole tree, if necessary
+ if (tmid) {
+ try {
+ const msg = await msgCollection.find(message.id);
+ deleteBatch.push(msg.prepareDestroyPermanently());
+ } catch (error) {
+ // Do nothing: message not found
+ }
+
+ try {
+ // Find the thread header and update it
+ const msg = await msgCollection.find(tmid);
+ if (msg.tcount <= 1) {
+ deleteBatch.push(
+ msg.prepareUpdate((m) => {
+ m.tcount = null;
+ m.tlm = null;
+ })
+ );
+
+ try {
+ // If the whole thread was removed, delete the thread
+ const thread = await threadCollection.find(tmid);
+ deleteBatch.push(thread.prepareDestroyPermanently());
+ } catch (error) {
+ // Do nothing: thread not found
+ }
+ } else {
+ deleteBatch.push(
+ msg.prepareUpdate((m) => {
+ m.tcount -= 1;
+ })
+ );
+ }
+ } catch (error) {
+ // Do nothing: message not found
+ }
+ }
+ await db.action(async() => {
+ await db.batch(...deleteBatch);
+ });
+ } catch (e) {
+ log(e);
+ }
+ }
showActionSheet = () => {
ActionSheet.showActionSheetWithOptions({
diff --git a/app/containers/message/Message.js b/app/containers/message/Message.js
index e26040d5a..8e49b3b3a 100644
--- a/app/containers/message/Message.js
+++ b/app/containers/message/Message.js
@@ -52,7 +52,7 @@ MessageInner.displayName = 'MessageInner';
const Message = React.memo((props) => {
if (props.isThreadReply || props.isThreadSequential || props.isInfo) {
- const thread = props.isThreadReply ? : null;
+ const thread = props.isThreadReply ? : null;
return (
{thread}
diff --git a/app/containers/message/RepliedThread.js b/app/containers/message/RepliedThread.js
index 7a6f17b7f..7e43dbd11 100644
--- a/app/containers/message/RepliedThread.js
+++ b/app/containers/message/RepliedThread.js
@@ -9,9 +9,9 @@ import DisclosureIndicator from '../DisclosureIndicator';
import styles from './styles';
const RepliedThread = React.memo(({
- tmid, tmsg, isHeader, isTemp, fetchThreadName, id
+ tmid, tmsg, isHeader, fetchThreadName, id
}) => {
- if (!tmid || !isHeader || isTemp) {
+ if (!tmid || !isHeader) {
return null;
}
@@ -40,9 +40,6 @@ const RepliedThread = React.memo(({
if (prevProps.isHeader !== nextProps.isHeader) {
return false;
}
- if (prevProps.isTemp !== nextProps.isTemp) {
- return false;
- }
return true;
});
@@ -51,7 +48,6 @@ RepliedThread.propTypes = {
tmsg: PropTypes.string,
id: PropTypes.string,
isHeader: PropTypes.bool,
- isTemp: PropTypes.bool,
fetchThreadName: PropTypes.func
};
RepliedThread.displayName = 'MessageRepliedThread';
diff --git a/app/lib/methods/readMessages.js b/app/lib/methods/readMessages.js
index 9e61cfc44..552cd1e0e 100644
--- a/app/lib/methods/readMessages.js
+++ b/app/lib/methods/readMessages.js
@@ -3,12 +3,14 @@ import log from '../../utils/log';
export default async function readMessages(rid, lastOpen) {
try {
- // RC 0.61.0
- const data = await this.sdk.post('subscriptions.read', { rid });
const db = database.active;
+ const subscription = await db.collections.get('subscriptions').find(rid);
+
+ // RC 0.61.0
+ await this.sdk.post('subscriptions.read', { rid });
+
await db.action(async() => {
try {
- const subscription = await db.collections.get('subscriptions').find(rid);
await subscription.update((s) => {
s.open = true;
s.alert = false;
@@ -22,7 +24,6 @@ export default async function readMessages(rid, lastOpen) {
// Do nothing
}
});
- return data;
} catch (e) {
log(e);
}
diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js
index 5b7f051ca..bd383bd5d 100644
--- a/app/lib/methods/sendMessage.js
+++ b/app/lib/methods/sendMessage.js
@@ -5,81 +5,155 @@ import database from '../database';
import log from '../../utils/log';
import random from '../../utils/random';
-export const getMessage = async(rid, msg = '', tmid, user) => {
- const _id = random(17);
- const { id, username } = user;
- try {
- const db = database.active;
- const msgCollection = db.collections.get('messages');
- let message;
- await db.action(async() => {
- message = await msgCollection.create((m) => {
- m._raw = sanitizedRaw({ id: _id }, msgCollection.schema);
- m.subscription.id = rid;
- m.msg = msg;
- m.tmid = tmid;
- m.ts = new Date();
- m._updatedAt = new Date();
- m.status = messagesStatus.TEMP;
- m.u = {
- _id: id || '1',
- username
- };
- });
- });
- return message;
- } catch (error) {
- console.warn('getMessage', error);
- }
-};
-
export async function sendMessageCall(message) {
const {
id: _id, subscription: { id: rid }, msg, tmid
} = message;
- // RC 0.60.0
- const data = await this.sdk.post('chat.sendMessage', {
- message: {
- _id, rid, msg, tmid
+ try {
+ // RC 0.60.0
+ await this.sdk.post('chat.sendMessage', {
+ message: {
+ _id, rid, msg, tmid
+ }
+ });
+ } catch (e) {
+ const db = database.active;
+ const msgCollection = db.collections.get('messages');
+ const threadMessagesCollection = db.collections.get('thread_messages');
+ const errorBatch = [];
+ const messageRecord = await msgCollection.find(_id);
+ errorBatch.push(
+ messageRecord.prepareUpdate((m) => {
+ m.status = messagesStatus.ERROR;
+ })
+ );
+
+ if (tmid) {
+ const threadMessageRecord = await threadMessagesCollection.find(_id);
+ errorBatch.push(
+ threadMessageRecord.prepareUpdate((tm) => {
+ tm.status = messagesStatus.ERROR;
+ })
+ );
}
- });
- return data;
+
+ await db.action(async() => {
+ await db.batch(...errorBatch);
+ });
+ }
}
export default async function(rid, msg, tmid, user) {
try {
const db = database.active;
- const subsCollections = db.collections.get('subscriptions');
- const message = await getMessage(rid, msg, tmid, user);
- if (!message) {
- return;
+ const subsCollection = db.collections.get('subscriptions');
+ const msgCollection = db.collections.get('messages');
+ const threadCollection = db.collections.get('threads');
+ const threadMessagesCollection = db.collections.get('thread_messages');
+ const messageId = random(17);
+ const batch = [];
+ const message = {
+ id: messageId, subscription: { id: rid }, msg, tmid
+ };
+ const messageDate = new Date();
+ let tMessageRecord;
+
+ // If it's replying to a thread
+ if (tmid) {
+ try {
+ // Find thread message header in Messages collection
+ tMessageRecord = await msgCollection.find(tmid);
+ batch.push(
+ tMessageRecord.prepareUpdate((m) => {
+ m.tlm = messageDate;
+ m.tcount += 1;
+ })
+ );
+
+ try {
+ // Find thread message header in Threads collection
+ await threadCollection.find(tmid);
+ } catch (error) {
+ // If there's no record, create one
+ batch.push(
+ threadCollection.prepareCreate((tm) => {
+ tm._raw = sanitizedRaw({ id: tmid }, threadCollection.schema);
+ tm.subscription.id = rid;
+ tm.tmid = tmid;
+ tm.msg = tMessageRecord.msg;
+ tm.ts = tMessageRecord.ts;
+ tm._updatedAt = messageDate;
+ tm.status = messagesStatus.SENT; // Original message was sent already
+ tm.u = tMessageRecord.u;
+ })
+ );
+ }
+
+ // Create the message sent in ThreadMessages collection
+ batch.push(
+ threadMessagesCollection.prepareCreate((tm) => {
+ tm._raw = sanitizedRaw({ id: messageId }, threadMessagesCollection.schema);
+ tm.subscription.id = rid;
+ tm.rid = tmid;
+ tm.msg = msg;
+ tm.ts = messageDate;
+ tm._updatedAt = messageDate;
+ tm.status = messagesStatus.TEMP;
+ tm.u = {
+ _id: user.id || '1',
+ username: user.username
+ };
+ })
+ );
+ } catch (e) {
+ log(e);
+ }
}
+ // Create the message sent in Messages collection
+ batch.push(
+ msgCollection.prepareCreate((m) => {
+ m._raw = sanitizedRaw({ id: messageId }, msgCollection.schema);
+ m.subscription.id = rid;
+ m.msg = msg;
+ m.ts = messageDate;
+ m._updatedAt = messageDate;
+ m.status = messagesStatus.TEMP;
+ m.u = {
+ _id: user.id || '1',
+ username: user.username
+ };
+ if (tmid) {
+ m.tmid = tmid;
+ m.tlm = messageDate;
+ m.tmsg = tMessageRecord.msg;
+ }
+ })
+ );
+
try {
- const room = await subsCollections.find(rid);
- await db.action(async() => {
- await room.update((r) => {
- r.draftMessage = null;
- });
- });
+ const room = await subsCollection.find(rid);
+ if (room.draftMessage) {
+ batch.push(
+ room.prepareUpdate((r) => {
+ r.draftMessage = null;
+ })
+ );
+ }
} catch (e) {
// Do nothing
}
try {
- await sendMessageCall.call(this, message);
await db.action(async() => {
- await message.update((m) => {
- m.status = messagesStatus.SENT;
- });
+ await db.batch(...batch);
});
} catch (e) {
- await db.action(async() => {
- await message.update((m) => {
- m.status = messagesStatus.ERROR;
- });
- });
+ log(e);
+ return;
}
+
+ await sendMessageCall.call(this, message);
} catch (e) {
log(e);
}
diff --git a/app/lib/methods/subscriptions/room.js b/app/lib/methods/subscriptions/room.js
index 1a3de160b..2dab116d5 100644
--- a/app/lib/methods/subscriptions/room.js
+++ b/app/lib/methods/subscriptions/room.js
@@ -8,6 +8,7 @@ import buildMessage from '../helpers/buildMessage';
import database from '../../database';
import reduxStore from '../../createStore';
import { addUserTyping, removeUserTyping, clearUserTyping } from '../../../actions/usersTyping';
+import debounce from '../../../utils/debounce';
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
const removeListener = listener => listener.stop();
@@ -85,6 +86,10 @@ export default function subscribeRoom({ rid }) {
}
});
+ const read = debounce((lastOpen) => {
+ this.readMessages(rid, lastOpen);
+ }, 300);
+
const handleMessageReceived = protectedFunction((ddpMessage) => {
const message = buildMessage(EJSON.fromJSONValue(ddpMessage.fields.args[0]));
const lastOpen = new Date();
@@ -94,20 +99,26 @@ export default function subscribeRoom({ rid }) {
InteractionManager.runAfterInteractions(async() => {
const db = database.active;
const batch = [];
- const subCollection = db.collections.get('subscriptions');
const msgCollection = db.collections.get('messages');
const threadsCollection = db.collections.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages');
+ let messageRecord;
+ let threadRecord;
+ let threadMessageRecord;
// Create or update message
try {
- const messageRecord = await msgCollection.find(message._id);
- batch.push(
- messageRecord.prepareUpdate((m) => {
- Object.assign(m, message);
- })
- );
+ messageRecord = await msgCollection.find(message._id);
} catch (error) {
+ // Do nothing
+ }
+ if (messageRecord) {
+ batch.push(
+ messageRecord.prepareUpdate(protectedFunction((m) => {
+ Object.assign(m, message);
+ }))
+ );
+ } else {
batch.push(
msgCollection.prepareCreate(protectedFunction((m) => {
m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema);
@@ -120,13 +131,18 @@ export default function subscribeRoom({ rid }) {
// Create or update thread
if (message.tlm) {
try {
- const threadRecord = await threadsCollection.find(message._id);
- batch.push(
- threadRecord.prepareUpdate((t) => {
- Object.assign(t, message);
- })
- );
+ threadRecord = await threadsCollection.find(message._id);
} catch (error) {
+ // Do nothing
+ }
+
+ if (threadRecord) {
+ batch.push(
+ threadRecord.prepareUpdate(protectedFunction((t) => {
+ Object.assign(t, message);
+ }))
+ );
+ } else {
batch.push(
threadsCollection.prepareCreate(protectedFunction((t) => {
t._raw = sanitizedRaw({ id: message._id }, threadsCollection.schema);
@@ -140,15 +156,20 @@ export default function subscribeRoom({ rid }) {
// Create or update thread message
if (message.tmid) {
try {
- const threadMessageRecord = await threadMessagesCollection.find(message._id);
+ threadMessageRecord = await threadMessagesCollection.find(message._id);
+ } catch (error) {
+ // Do nothing
+ }
+
+ if (threadMessageRecord) {
batch.push(
- threadMessageRecord.prepareUpdate((tm) => {
+ threadMessageRecord.prepareUpdate(protectedFunction((tm) => {
Object.assign(tm, message);
tm.rid = message.tmid;
delete tm.tmid;
- })
+ }))
);
- } catch (error) {
+ } else {
batch.push(
threadMessagesCollection.prepareCreate(protectedFunction((tm) => {
tm._raw = sanitizedRaw({ id: message._id }, threadMessagesCollection.schema);
@@ -161,12 +182,7 @@ export default function subscribeRoom({ rid }) {
}
}
- try {
- await subCollection.find(rid);
- this.readMessages(rid, lastOpen);
- } catch (e) {
- console.log('Subscription not found. We probably subscribed to a not joined channel. No need to mark as read.');
- }
+ read(lastOpen);
try {
await db.action(async() => {
diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js
index c6b940c9e..f279f8b30 100644
--- a/app/lib/methods/subscriptions/rooms.js
+++ b/app/lib/methods/subscriptions/rooms.js
@@ -66,10 +66,10 @@ const createOrUpdateSubscription = async(subscription, room) => {
} catch (error) {
try {
await db.action(async() => {
- await roomsCollection.create((r) => {
+ await roomsCollection.create(protectedFunction((r) => {
r._raw = sanitizedRaw({ id: room._id }, roomsCollection.schema);
Object.assign(r, room);
- });
+ }));
});
} catch (e) {
// Do nothing
@@ -96,19 +96,25 @@ const createOrUpdateSubscription = async(subscription, room) => {
const tmp = merge(subscription, room);
await db.action(async() => {
+ let sub;
try {
- const sub = await subCollection.find(tmp.rid);
- await sub.update((s) => {
- Object.assign(s, tmp);
- });
+ sub = await subCollection.find(tmp.rid);
} catch (error) {
- await subCollection.create((s) => {
+ // Do nothing
+ }
+
+ if (sub) {
+ await sub.update(protectedFunction((s) => {
+ Object.assign(s, tmp);
+ }));
+ } else {
+ await subCollection.create(protectedFunction((s) => {
s._raw = sanitizedRaw({ id: tmp.rid }, subCollection.schema);
Object.assign(s, tmp);
if (s.roomUpdatedAt) {
s.roomUpdatedAt = new Date();
}
- });
+ }));
}
});
} catch (e) {
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index e62548457..a9007b71f 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -39,7 +39,7 @@ import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages';
import loadThreadMessages from './methods/loadThreadMessages';
-import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage';
+import sendMessage, { sendMessageCall } from './methods/sendMessage';
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import callJitsi from './methods/callJitsi';
@@ -427,11 +427,10 @@ const RocketChat = {
loadMissedMessages,
loadMessagesForRoom,
loadThreadMessages,
- getMessage,
sendMessage,
getRooms,
readMessages,
- async resendMessage(message) {
+ async resendMessage(message, tmid) {
const db = database.active;
try {
await db.action(async() => {
@@ -439,17 +438,20 @@ const RocketChat = {
m.status = messagesStatus.TEMP;
});
});
- await sendMessageCall.call(this, message);
- } catch (error) {
- try {
- await db.action(async() => {
- await message.update((m) => {
- m.status = messagesStatus.ERROR;
- });
- });
- } catch (e) {
- log(e);
+ let m = {
+ id: message.id,
+ msg: message.msg,
+ subscription: { id: message.subscription.id }
+ };
+ if (tmid) {
+ m = {
+ ...m,
+ tmid
+ };
}
+ await sendMessageCall.call(this, m);
+ } catch (e) {
+ log(e);
}
},
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 31e9f40ac..6975599d1 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -775,6 +775,7 @@ class RoomView extends React.Component {
}
{showErrorActions ? (
diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js
index f419ffc60..5914d8048 100644
--- a/app/views/ShareListView/index.js
+++ b/app/views/ShareListView/index.js
@@ -198,7 +198,13 @@ class ShareListView extends React.Component {
const serversCollection = serversDB.collections.get('servers');
this.servers = await serversCollection.query().fetch();
this.chats = this.data.slice(0, LIMIT);
- const serverInfo = await serversCollection.find(server);
+ let serverInfo = {};
+ try {
+ serverInfo = await serversCollection.find(server);
+ } catch (error) {
+ // Do nothing
+ }
+
const canUploadFileResult = canUploadFile(fileInfo || fileData, serverInfo);
this.internalSetState({