[FIX] Watermelon batches (#1277)

This commit is contained in:
Diego Mello 2019-10-08 09:36:15 -03:00 committed by GitHub
parent 08dac6ff86
commit 145e5c6b55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 286 additions and 131 deletions

View File

@ -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;
});
}
});
})

View File

@ -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({

View File

@ -52,7 +52,7 @@ MessageInner.displayName = 'MessageInner';
const Message = React.memo((props) => {
if (props.isThreadReply || props.isThreadSequential || props.isInfo) {
const thread = props.isThreadReply ? <RepliedThread isTemp={props.isTemp} {...props} /> : null;
const thread = props.isThreadReply ? <RepliedThread {...props} /> : null;
return (
<View style={[styles.container, props.style]}>
{thread}

View File

@ -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';

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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() => {

View File

@ -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) {

View File

@ -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);
}
},

View File

@ -775,6 +775,7 @@ class RoomView extends React.Component {
}
{showErrorActions ? (
<MessageErrorActions
tmid={this.tmid}
message={selectedMessage}
actionsHide={this.onErrorActionsHide}
/>

View File

@ -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({