diff --git a/electron_app/package.json b/electron_app/package.json index 15e2f7dc3..78b3cb35a 100644 --- a/electron_app/package.json +++ b/electron_app/package.json @@ -75,7 +75,7 @@ "electron-packager": "^10.1.2" }, "dependencies": { - "@criptext/email-http-client": "^0.8.1", + "@criptext/email-http-client": "^0.8.2", "knex": "^0.14.2", "sqlite3": "^3.1.13", "websocket": "^1.0.25" diff --git a/electron_app/src/DBManager.js b/electron_app/src/DBManager.js index 89bf1bd80..598fddecf 100644 --- a/electron_app/src/DBManager.js +++ b/electron_app/src/DBManager.js @@ -1,5 +1,6 @@ const { db, cleanDataBase, createTables, Table } = require('./models.js'); const { formContactsRow } = require('./utils/dataTableUtils.js'); +const { noNulls } = require('./utils/ObjectUtils'); /* Account ----------------------------- */ @@ -564,16 +565,19 @@ const deleteEmailLabelAndContactByEmailId = (id, optionalEmailToSave) => { }); }; -const updateEmail = ({ id, key, threadId, date, isMuted, unread }) => { - const params = {}; - if (key) params.key = key; - if (threadId) params.threadId = threadId; - if (date) params.date = date; - if (typeof unread === 'boolean') params.unread = unread; - if (typeof isMuted === 'boolean') params.isMuted = isMuted; +const updateEmail = ({ id, key, threadId, date, isMuted, unread, status }) => { + const params = noNulls({ + key, + threadId, + date, + unread: typeof unread === 'boolean' ? unread : undefined, + isMuted: typeof isMuted === 'boolean' ? isMuted : undefined, + status + }); + const whereParam = id ? { id } : { key }; return db .table(Table.EMAIL) - .where({ id }) + .where(whereParam) .update(params); }; diff --git a/electron_app/src/clientManager.js b/electron_app/src/clientManager.js index fea9881b1..8001e3ac3 100644 --- a/electron_app/src/clientManager.js +++ b/electron_app/src/clientManager.js @@ -64,8 +64,8 @@ class ClientManager { return client.postKeyBundle(params); } - postOpenEvent(params) { - return client.postOpenEvent(params); + postOpenEvent(metadataKeys) { + return client.postOpenEvent(metadataKeys); } postUser(params) { diff --git a/electron_app/yarn.lock b/electron_app/yarn.lock index ef5e957b9..015520446 100644 --- a/electron_app/yarn.lock +++ b/electron_app/yarn.lock @@ -96,7 +96,7 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@criptext/email-http-client@^0.8.1": +"@criptext/email-http-client@^0.8.2": version "0.8.2" resolved "https://registry.yarnpkg.com/@criptext/email-http-client/-/email-http-client-0.8.2.tgz#6e788b0ea3e2e959b7b6bd46829ede7caaa20ec4" dependencies: diff --git a/email_composer/src/containers/Composer.js b/email_composer/src/containers/Composer.js index a1ba72846..85b237d78 100644 --- a/email_composer/src/containers/Composer.js +++ b/email_composer/src/containers/Composer.js @@ -11,7 +11,6 @@ import { myAccount, throwError, updateEmail, - updateEmailLabel, saveDraftChanges, errors, deleteEmailsByIds, @@ -26,6 +25,7 @@ import { } from './../utils/ArrayUtils'; import signal from './../libs/signal'; import { + EmailStatus, formOutgoingEmailFromData, formDataToEditDraft, formDataToReply @@ -280,7 +280,7 @@ class ComposerWrapper extends Component { this.setState({ status: Status.WAITING }); const { data, to, subject, body } = formOutgoingEmailFromData( this.state, - LabelType.draft.id + LabelType.sent.id ); let emailId, key; try { @@ -310,15 +310,14 @@ class ComposerWrapper extends Component { const { metadataKey, date } = res.body; const threadId = this.state.threadId || res.body.threadId; key = metadataKey; - const emailParams = { id: emailId, key, threadId, date }; - await updateEmail(emailParams); - - const emailLabelParams = { - emailId, - oldLabelId: LabelType.draft.id, - newLabelId: LabelType.sent.id + const emailParams = { + id: emailId, + key, + threadId, + date, + status: EmailStatus.SENT }; - await updateEmailLabel(emailLabelParams); + await updateEmail(emailParams); closeComposerWindow(emailId); } catch (e) { diff --git a/email_composer/src/utils/EmailUtils.js b/email_composer/src/utils/EmailUtils.js index d5cad42c7..abc9a66f5 100644 --- a/email_composer/src/utils/EmailUtils.js +++ b/email_composer/src/utils/EmailUtils.js @@ -33,6 +33,16 @@ const getCriptextRecipients = (recipients, type) => { })); }; +export const EmailStatus = { + FAIL: 1, + UNSENT: 2, + NONE: 3, + SENDING: 4, + SENT: 5, + DELIVERED: 6, + READ: 7 +}; + export const formOutgoingEmailFromData = (composerData, labelId) => { const recipients = { to: composerData.toEmails, @@ -52,7 +62,7 @@ export const formOutgoingEmailFromData = (composerData, labelId) => { content: body, preview: removeHTMLTags(body).slice(0, 21), date: Date.now(), - status: 1, + status: EmailStatus.SENDING, unread: false, secure: true, isMuted: false diff --git a/email_composer/src/utils/__tests__/__snapshots__/EmailUtils.js.snap b/email_composer/src/utils/__tests__/__snapshots__/EmailUtils.js.snap index 0ab10c352..08be7ea4c 100644 --- a/email_composer/src/utils/__tests__/__snapshots__/EmailUtils.js.snap +++ b/email_composer/src/utils/__tests__/__snapshots__/EmailUtils.js.snap @@ -30,7 +30,7 @@ Object { "preview": " ", "secure": true, - "status": 1, + "status": 4, "subject": "Subject", "unread": false, }, diff --git a/email_mailbox/src/actions/index.js b/email_mailbox/src/actions/index.js index 9cdfcc751..caa67f1ed 100644 --- a/email_mailbox/src/actions/index.js +++ b/email_mailbox/src/actions/index.js @@ -20,7 +20,8 @@ import { selectThreads, removeThreads, removeThreadsLabel, - sendOpenEvent + sendOpenEvent, + updateStatusThread } from './threads'; import { addEmails, @@ -90,6 +91,7 @@ export { updateFeedItemSuccess, updateLabel, updateLabelSuccess, + updateStatusThread, updateUnreadEmails, updateUnreadThread, updateUnreadThreads diff --git a/email_mailbox/src/actions/threads.js b/email_mailbox/src/actions/threads.js index c4d91c1bc..7392bec8a 100644 --- a/email_mailbox/src/actions/threads.js +++ b/email_mailbox/src/actions/threads.js @@ -106,6 +106,12 @@ export const moveThreads = (threadIds, labelId) => ({ labelId }); +export const updateStatusThread = (threadId, status) => ({ + type: Thread.UPDATE_STATUS, + threadId, + status +}); + export const updateUnreadThread = thread => { return { type: Thread.UPDATE_UNREAD_THREAD, @@ -308,8 +314,7 @@ export const sendOpenEvent = threadId => { const unreadEmails = await getUnreadEmailsByThreadId(threadId); if (unreadEmails.length > 0) { const metadataKeys = unreadEmails.map(item => Number(item.key)); - const params = { metadataKeys }; - await postOpenEvent(params); + await postOpenEvent(metadataKeys); } } catch (e) { // TO DO diff --git a/email_mailbox/src/actions/types.js b/email_mailbox/src/actions/types.js index aa8b975c4..b20b67e9f 100644 --- a/email_mailbox/src/actions/types.js +++ b/email_mailbox/src/actions/types.js @@ -18,7 +18,8 @@ export const Thread = { MOVE_THREADS: 'MOVE_THREADS', UPDATE_UNREAD_THREADS: 'UPDATE_UNREAD_THREADS', UPDATE_UNREAD_THREAD: 'UPDATE_UNREAD_THREAD', - ADD_EMAIL: 'UPDATE_THREAD_EMAIL_IDS' + ADD_EMAIL: 'UPDATE_THREAD_EMAIL_IDS', + UPDATE_STATUS: 'UPDATE_STATUS' }; export const Contact = { diff --git a/email_mailbox/src/components/PanelWrapper.js b/email_mailbox/src/components/PanelWrapper.js index 57f9f0169..6c06e76c3 100644 --- a/email_mailbox/src/components/PanelWrapper.js +++ b/email_mailbox/src/components/PanelWrapper.js @@ -68,9 +68,13 @@ class PanelWrapper extends Component { } }); + addEvent(Event.EMAIL_TRACKING_UPDATE, threadId => { + props.onMarkThreadAsOpen(threadId); + }); + addEvent(Event.UPDATE_THREAD_EMAILS, eventParams => { - const newThreadId = eventParams.threadId; - props.onLoadEmails(newThreadId); + const { threadId } = eventParams; + props.onLoadEmails(threadId); props.onAddEmailToThread({ threadId: this.state.sectionSelected.params.threadIdSelected, emailId: eventParams.emailId @@ -178,6 +182,7 @@ class PanelWrapper extends Component { PanelWrapper.propTypes = { onAddEmailToThread: PropTypes.func, onLoadEmails: PropTypes.func, + onMarkThreadAsOpen: PropTypes.func, onLoadThreads: PropTypes.func, onUpdateOpenedAccount: PropTypes.func, onUpdateTimestamp: PropTypes.func, diff --git a/email_mailbox/src/components/ThreadItem.js b/email_mailbox/src/components/ThreadItem.js index f32fa6a3f..2497a82e8 100644 --- a/email_mailbox/src/components/ThreadItem.js +++ b/email_mailbox/src/components/ThreadItem.js @@ -88,7 +88,7 @@ class ThreadItem extends Component { return ; case EmailStatus.DELIVERED: return ; - case EmailStatus.OPENED: + case EmailStatus.READ: return ; default: return null; diff --git a/email_mailbox/src/containers/Panel.js b/email_mailbox/src/containers/Panel.js index de39be041..c238ca787 100644 --- a/email_mailbox/src/containers/Panel.js +++ b/email_mailbox/src/containers/Panel.js @@ -4,7 +4,8 @@ import { loadThreads, updateLabelSuccess, updateAllFeedItemsAsOlder, - loadEmails + loadEmails, + updateStatusThread } from '../actions'; import PanelWrapper from '../components/PanelWrapper'; import { @@ -13,6 +14,7 @@ import { updateAccount } from '../utils/electronInterface'; import { storeSeenTimestamp } from '../utils/storage'; +import { EmailStatus } from '../utils/const'; const mapStateToProps = state => { const threadsCount = state.get('threads').size; @@ -46,6 +48,10 @@ const mapDispatchToProps = dispatch => { const rejectedLabelIds = defineRejectedLabels(labelId); dispatch(loadThreads({ ...params, ...rejectedLabelIds })); }, + onMarkThreadAsOpen: threadId => { + const readStatus = EmailStatus.READ; + dispatch(updateStatusThread(threadId, readStatus)); + }, onUpdateOpenedAccount: async () => { const opened = true; const recipientId = myAccount.recipientId; diff --git a/email_mailbox/src/reducers/__tests__/threads.js b/email_mailbox/src/reducers/__tests__/threads.js index 568a978c6..a96b20183 100644 --- a/email_mailbox/src/reducers/__tests__/threads.js +++ b/email_mailbox/src/reducers/__tests__/threads.js @@ -33,4 +33,26 @@ describe('Set thread state by actions', () => { const unread = emailUpdated.get('unread'); expect(unread).toBe(false); }); + + it('should set thread param: status, action[UPDATE_STATUS]', () => { + const state = initState(threads); + const threadId = 1; + const newStatus = 2; + const action = actions.updateStatusThread(threadId, newStatus); + const newState = threadsReducer(state, action); + const emailUpdated = newState.get('0'); + const status = emailUpdated.get('status'); + expect(status).toBe(newStatus); + }); + + it('should not set thread param status because is undefined, action[UPDATE_STATUS]', () => { + const state = initState(threads); + const threadId = 1; + const badStatus = undefined; + const action = actions.updateStatusThread(threadId, badStatus); + const newState = threadsReducer(state, action); + const emailUpdated = newState.get('0'); + const status = emailUpdated.get('status'); + expect(status).not.toBe(badStatus); + }); }); diff --git a/email_mailbox/src/reducers/threads.js b/email_mailbox/src/reducers/threads.js index d11813a8e..a6fb123f8 100644 --- a/email_mailbox/src/reducers/threads.js +++ b/email_mailbox/src/reducers/threads.js @@ -134,7 +134,19 @@ const threads = (state = List([]), action) => { if (threadItem.get('id') === threadId) { return thread(threadItem, action); } - return thread; + return threadItem; + }); + } + case Thread.UPDATE_STATUS: { + const { status, threadId } = action; + if (!threadId || !status) { + return state; + } + return state.map(threadItem => { + if (threadItem.get('id') === threadId) { + return thread(threadItem, action); + } + return threadItem; }); } default: @@ -150,6 +162,9 @@ const thread = (state, action) => { case Thread.ADD_EMAIL: { return state.set('emailIds', state.get('emailIds').push(action.emailId)); } + case Thread.UPDATE_STATUS: { + return state.set('status', action.status); + } default: return state; } diff --git a/email_mailbox/src/utils/EmailUtils.js b/email_mailbox/src/utils/EmailUtils.js index eb8766c3f..7c008e3c9 100644 --- a/email_mailbox/src/utils/EmailUtils.js +++ b/email_mailbox/src/utils/EmailUtils.js @@ -1,5 +1,6 @@ import { removeAppDomain, removeHTMLTags } from './StringUtils'; import signal from './../libs/signal'; +import { EmailStatus } from './const'; const getContentMessage = async ({ bodyKey, @@ -49,7 +50,7 @@ export const formIncomingEmailFromData = async data => { preview, subject: data.subject, date: data.date, - status: 1, + status: EmailStatus.NONE, unread: true, secure: true, isMuted: false diff --git a/email_mailbox/src/utils/const.js b/email_mailbox/src/utils/const.js index 3a1722363..61007ca88 100644 --- a/email_mailbox/src/utils/const.js +++ b/email_mailbox/src/utils/const.js @@ -48,11 +48,13 @@ export const FeedItemType = { }; export const EmailStatus = { - UNSENT: -1, - NONE: 0, - SENT: 1, - DELIVERED: 2, - OPENED: 3 + FAIL: 1, + UNSENT: 2, + NONE: 3, + SENDING: 4, + SENT: 5, + DELIVERED: 6, + READ: 7 }; export const SocketCommand = { diff --git a/email_mailbox/src/utils/electronEventInterface.js b/email_mailbox/src/utils/electronEventInterface.js index f0f922074..96bb27a95 100644 --- a/email_mailbox/src/utils/electronEventInterface.js +++ b/email_mailbox/src/utils/electronEventInterface.js @@ -7,7 +7,8 @@ import { LabelType, getContactByEmails, createFeedItem, - myAccount + myAccount, + updateOpenedEmailByKey } from './electronInterface'; import { formEmailLabel, @@ -15,7 +16,7 @@ import { formIncomingEmailFromData, getRecipientsFromData } from './EmailUtils'; -import { SocketCommand, appDomain, FeedItemType } from './const'; +import { SocketCommand, appDomain, EmailStatus } from './const'; const EventEmitter = window.require('events'); const electron = window.require('electron'); @@ -92,18 +93,24 @@ export const handleNewMessageEvent = async ({ rowid, params }) => { export const handleEmailTrackingUpdate = async ({ rowid, params }) => { const [metadataKey, recipientId] = [params.metadataKey, params.from]; if (recipientId !== myAccount.recipientId) { - const contactEmail = `${recipientId}@${appDomain}`; - const [contact] = await getContactByEmails([contactEmail]); const [email] = await getEmailByKey(metadataKey); - const feedItemParams = { - date: params.date, - type: FeedItemType.OPENED.value, - emailId: email.id, - contactId: contact.id - }; - await createFeedItem([feedItemParams]); - await acknowledgeEvents([rowid]); - emitter.emit(Event.EMAIL_TRACKING_UPDATE); + if (email) { + await updateOpenedEmailByKey({ + key: metadataKey, + status: EmailStatus.READ + }); + const contactEmail = `${recipientId}@${appDomain}`; + const [contact] = await getContactByEmails([contactEmail]); + const feedItemParams = { + date: params.date, + type: params.type, + emailId: email.id, + contactId: contact.id + }; + await createFeedItem([feedItemParams]); + await acknowledgeEvents([rowid]); + emitter.emit(Event.EMAIL_TRACKING_UPDATE, email.id); + } } // To do: Sync this event with my other devices }; diff --git a/email_mailbox/src/utils/electronInterface.js b/email_mailbox/src/utils/electronInterface.js index 1bf45640d..ce286ad41 100644 --- a/email_mailbox/src/utils/electronInterface.js +++ b/email_mailbox/src/utils/electronInterface.js @@ -237,6 +237,10 @@ export const updateLabel = params => { return dbManager.updateLabel(params); }; +export const updateOpenedEmailByKey = ({ key, status }) => { + return dbManager.updateEmail({ key, status }); +}; + export const updateUnreadEmailByThreadId = (threadId, value) => { return dbManager.updateEmailByThreadId({ threadId, unread: value }); };