New private conversations have their type set in onMessageReceived. New
group conversations should be handled the same way as normal group
updates. It was pointed out we should never have to handle a group
message without a preceding group update, as those would be rejected by
textsecure.processDecrypted. An exception would be if you delete the
group from indexedDB but not localStorage, but that's not a mode we
should be supporting.
Also in this change I switched to instantiating a new conversation
object on every call to handlePushMessageContent. Originally, I thought
to use the local conversation list as a cache, but it's a bit simpler to
re-read from the database every time for now. Later on we should revisit
and optimize for fewer read/writes per incoming message.
Just display a sensible default in the frontend if it's unset.
For private conversations this should be the phone number, for
groups, the list of numbers.
This was intended to sync the group state of a recently re-installed
client, but is prone to overkill when we have a lot of old stale groups
around. Also this implementation incurs some rate limit errors from the
server.
Uses app-level timestamps for outgoing messages.
Adds timestamp property to the outgoing jsonData.
Triggers a runtime event to notify frontend on delivery receipts.
Renders delivered messages with a 'delivered' class.
This change removes the timestamp field from messages and conversations
in favor of multiple semantically named timestamp fields: sent_at,
received_at on messages; active_at on conversations. This requires/lets
us rethink and improve our indexing scheme thusly:
The inbox index on conversations will order entries by the
conversation.active_at property, which should only appear on
conversations destined for the inbox.
The receipt index will use the message.sent_at property, for effecient
lookup of outgoing messages by timestamp, for use in processing delivery
receipts.
The group index on conversation.members is multi-entry, meaning that
looking up any phone number in this index will efficiently yield all
groups the number belongs to.
The conversation index lets us scan messages in a single conversation,
in the order they were received (or the reverse order). It is a compound
index on [conversationId, received_at].
This ended up turning into a rewrite/refactor of the background page.
For best results, view this diff with `-w` to ignore whitespace. In
order to support retrying message decryption, possibly at a much later
time than the message is received, we now implement the following:
Each message is saved before it is decrypted. This generates a unique
message_id which is later used to update the database entry with the
message contents, or with any errors generated during processing.
When an IncomingIdentityKeyError occurs, we catch it and save it on the
model, then update the front end as usual. When the user clicks to
accept the new key, the error is replayed, which causes the message to
be decrypted and then passed to the background page for normal
processing.
ReplayableErrors make it easy for the frontend to handle identity key
errors by wrapping the necessary steps into one convenient little
replay() callback function.
The frontend remains agnostic to what those steps are. It just calls
replay() once the user has acknowledged the key change.
The protocol layer is responsible for registering the callbacks needed
by the IncomingIdentityKeyError and OutgoingIdentityKeyError.
superfeedr has done a nice job with this backbone -> indexedDB adapter,
but their query interface is somewhat limited. This commit adds an
alternate interface that lets us specify the index and cursor bounds we
want. This interface requires deeper knowledge of indexedDB indices, but
is more powerful overall.
This was used to conditionally render messages in the group style, but
it's actually unnecessary. We can render the same markup in both cases
and change the appearance with css.
This commit provides the javascript complement to
[WebSocket-Resources](https://github.com/WhisperSystems/WebSocket-Resources),
allowing us to use a bi-directional request-response framework over
websockets.
See websocket-resources.js and websocket-resources_test.js
for usage details.
Along the way I also factored the websocket keepalive and reconnect
logic into its own file/wrapper object.
Move base64 encoding of attachments to an AttachmentView. This makes
image rendering an asynchronous task so we fire an update event to
indicate to the parent MessageListView that its content has changed
height and it is time to scroll down.
Register the runtime callback at the top level view rather than having
each conversation view register independently.
Also refactors Layout into InboxView.
After a message is saved asynchronsly, fire an event and pass the
message attributes to frontend listeners via the chrome-runtime API.
This behavior is similar to the 'storage' event fired by localStorage.
Getting up and running with IndexedDB was pretty easy, thanks to
backbone. The tricky part was making reads and writes asynchronous.
In that process I did some refactoring on Whisper.Threads, which
has been renamed Conversations for consistency with the view names.
This change also adds the unlimitedStorage permission.