Encapsulate the global conversation cache collection against accidental
access, avoiding the data-clobbering bug fixed in previous commit.
Also move some one-off program initialization code from panel controller
to background.js
// FREEBIE
The conversation's contactCollection only contains references to the
current membership, and will not provide contact info for people who
have left the group, causing their messages to render without numbers or
avatars.
// FREEBIE
When confirming the creation of a one on one conversation with a new
contact, the first click would do nothing but the second click would
work. Now the first click works.
Fix by only reject new conversation creation if not saved.
// FREEBIE
Fixes auto archive when deleting all messages, and auto unarchive when
sending a new message. Previously, the convo would not reappear in the
inbox after deleting all messages.
// FREEBIE
Only fetch them from a frontend view. If the conversation is not open,
we don't need to load the messages, and if we do load them, they will
render before we've done the initial contact info loading (as
implemented in 74e96ce).
Fixes#344
// FREEBIE
This listener is doing way more work than necessary to update the dom by
removing all the list items and re-creating them. This also causes the
bug where selected state is cleared when new messages arrive, not to
mention binding new event listeners without unbinding the old ones.
Fix by simply promoting an element to the top of the list when it's
active_at value changes, rather than re-rendering the whole list. This
could backfire if the value gets changed to an earlier timestamp but for
now we assume that won't happen.
// FREEBIE
The chrome.notifications api renders iconUrls at full bleed, as opposed
to the Web Notifications api, which adds padding. This was causing our
identicons to look a bit over stretched.
Fixed by rendering them a bit larger and with some padding.
// FREEBIE
Only allow one notification at a time. Use a basic notification for
normal messages, and image notification for image messages, and a list
notification when there are multiple unread messages.
// FREEBIE
Forgot to bind the socket event handler, and the then() handler should
come before the catch() handler or else it will execute every time the
catch handler executes.
// FREEBIE
Always test connectivity with an http request after a websocket closes,
regardless of what code/error it closed with. If that request succeeds,
automatically reconnect the socket.
// FREEBIE
Create a new collection type for the inbox which listens to events on
the main conversation cache. Also don't reload conversation info from
the database as often or when unnecessary.
Fixes#345
// FREEBIE
There's no need to use a custom collection type here since we don't use
any of the ConversationCollection methods. This helps prevent the
introduction of duplicate models for the same chat.
// FREEBIE
The delivery receipt handler should only update messages, so rather than
reloading the conversation and its contacts, only reload the messages.
// FREEBIE
Makes the groupupdate and recipient input fields stick to the top,
restyles the typeahead as a floating dropdown list of suggestions
rather than a full width component, fixes group avatar thumbnail
rendering.
// FREEBIE
Used by member list view. Refactored some templates for shared markup.
Fixes strange behavior where members in the list were hoverable and
selectable.
// FREEBIE
Establishes basic functionality for viewing conversations in two column
mode, including message area and message list resizing, and maintaining
scroll position.
Various subviews need to be retooled but are more or less still
functional, i.e., new message, message detail, key verification, etc...
Start by requesting keys for only the master device, then handle 410 as
needed. Single-device users are the more common case and this strategy
lets us avoid requesting/expending one of our own device keys when
establishing a session with sibling devices.
// FREEBIE
After setting a new identity key as trusted, we retry decryption on all
pending conflicts for that contact. If their identity changed twice in a
row, we can still get a conflict the second time, and should handle it
appropriately.
When resolving conflicts, we should not only discard the old key, but
set the new trusted key to the one the user has verified. Previously, we
would end up trusting the first-seen new key, which may not be the one
the user verified.
// FREEBIE
Related with #278. Redone to include keeping scroll at the bottom when resizing the window, as suggested in #305, and to better fit the current code structure.
This new endpoint should always issue a response to a provisioning
socket so if we don't receive one we should assume the connection has
been lost.
Closes#318
By default, automatically disconnect if no response. This is preferable
because we can sometimes lose connectivity without receiving a close
event from the socket, but it's also possible that the endpoint may not
support responses.
// FREEBIE
saveKeysToDeviceObject is the detector of outgoing identity key errors.
Catch these key errors closer to the source by pulling the
getKeysForNumber into the context of sendMessageToDevices, which lets
it access registerError and the message protobuf.
Previously identity key errors would be uncaught if all existing
sessions with a recipient were closed/deleted, since we would
preemptively fetch the new identity key. The old error handling only
kicked in after a 409/410 response from the server when posting a
message encrypted for a stale session.
// FREEBIE
Previously we would convert a bytebuffer to a string, pass it to
libaxolotl where it would be parsed back into a bytebuffer.
Ideally we would just pass the bytebuffer, but it turns out that
libaxolotl's bytebyffer class is identical but separate from
libtextsecure's bytebuffer class. ¯\_(ツ)_/¯
So instead we pass the underlying array buffer, which is handled
more or less the same way as a bytebuffer, and most importantly,
does not involve any copying.
// FREEBIE
We now disconnect ourselves if we don't get the server's response to a
keepalive request within 30s. This way we will eventually disconnect if
the network goes away but the socket is not closed.*
* See code.google.com/p/chromium/issues/detail?id=197841 and
https://stackoverflow.com/questions/11755605/chrome-websocket-connection-not-closed-when-browser-closed
We will then try to reconnect once a minute (See 8a10c96);
Keepalives belong at this level anyway, since the format is defined by
both the websocket resource protocol and our specific server url
structure.
// FREEBIE
This trigger function uses chrome's runtime message passing api, which
traverses between different windows in our runtime, but we only trigger
the updateInbox event from the backgroud page, so we don't need to use
that api, which requires some extra cpu/memory overhead.
// FREEBIE
Well that didn't work. Luckily this comparison is primarily enforced at
the libaxolotl level.
With this and the corresponding change to libaxolotl, remote identity
keys are always going to be stored as array buffers going forward. This
will cause incompatibility with existing keys stored as strings, so
updating to this point requires you to purge your identity key and
session store.
We're overriding the default with null often enough that we should
just change the default.
Consequently, no more phantom blank conversations with oneself should
appear after receiving a group update. They were being added to the
inbox because they were incorrectly initialized with an active_at value.
Fixes#281
All the old event listeners and in-memory objects have perished with the
old background page. Also reopen the inbox if it was already open.
Fixes#289
// FREEBIE
The following are equivalent, except that the first is longer and
invokes an extra function call.
```
return new Promise(function(resolve, reject) {
reject(new Error("Unknown Group"));
});
return Promise.reject(new Error("Unknown Group"));
```
Refreshing the background page does re-open the socket, but the inbox
and other windows don't reattach correctly. Reload the whole runtime to
force close all windows, reload the background, and re open the inbox.
In lieu of a click event, the change event was being fired when clicking
out of the search input. The input event seems to be what we actually want.
Fixes#273
This bug was caused by a race between indexeddb requests and sending
messages. Order of events to repro was roughly:
1. send async idb request for current message list
2. add new message(s)
3. idb request returns with now incomplete message list
4. message collection gets reset to list from 3, removing messages
added in 2, but not removing their phantom views/dom elements. (bug)
5. send another idb request for current message list
6. idb request returns bearing all messages including those from 2.
7. messages from 2 are added and rendered a second time.
The fix was simply to not remove messages in 4, which means we reuse the
original message model object rather than recreating it in 7.
Fixes#243
// FREEBIE
The unset function, and the series of events/callbacks triggered by its
use, are not as similar to the set/save functions as previously
anticipated, leading to flux in the state of the 'pending' attribute.
Fixes#283
// FREEBIE
Line breaks can now be insterted into message box using Shift+Enter or Alt+Enter. Messages with new lines are properly displayed in the conversation view (but only there, to keep inbox clean). The template was modified to allow HTML, but the message itself is sanitized before new line handling is run.
As discussed in similar issue there: https://github.com/GoogleChrome/chromedeveditor/issues/1023 - it is not possible to add onClosed event listener on an 'abstract' current window property, it needs to be set on the particular window instance instead.
Before that change, the clean up function was never actually called, because the listener was never properly attached. That was probably the reason of existence for "panel isn't actually open ... and so we try again." code that was executed if the previous window wasn't cleaned up properly (so actually every time). This code is no longer needed, I guess, as the windows are now cleaned up properly.
The avatar handler was being added to the list of promises too late,
so we were storing the raw avatar protobuf (Long id, bytes key) rather
than the downloaded/decrypted attachment data.
Fixes#280
Protocol and handling is all analogous to contact sync: Multiple
GroupDetails structs are packed into a single attachment blob and parsed
on our end. We don't display the synced groups in the conversation list
until a new message is sent to one of them.
// FREEBIE
* Fix a css bug preventing bottom bar from sticking to the bottom.
* Resize discussion container as a function of the overall window
height. The previous difference-based method gives the wrong result
when the window height changes but the bottom-bar height stays the
same.
// FREEBIE
Fixes#264
Implement the equivalent of java's String.hashCode on the conversation model.
Change avatar template and attributes. Use css classes for colors.
Initializing a message receiver opens the socket and starts listening
right away rather than requiring a separate call to connect. The only
other publicly accessible method is to query the socket status.
// FREEBIE
Update protobuf definitions and refactor message receive and decrypt
codepath to support new protocol, including various flavors of sync
messages (sent messages, contacts, and groups).
Also cleans up background.js and lets libtextsecure internalize
textsecure.processDecrypted and ensure that it is called before handing
DataMessages off to the application.
The Envelope structure now has a generic content field and a
legacyMessage field for backwards compatibility. We'll send outgoing
messages as legacy messages, and sync messages as "content" while
continuing to support both legacy and non-legacy messages on the receive
side until old clients have a chance to transition.
This behavior was intended to help keep the websocket alive, but keeping
the inbox window around can cause some stale frontend state. Also we now
have a keepalive alarm to check for new messages once a minute.
These collections should always be operating with the same model
instances, so let the inbox reset it self from the same in-memory
cache of conversation models used by the conversation windows.
If all the application windows are closed (and not merely hidden), the
background page will go inactive and there's nothing we can do to stop
it. However, we can ask chrome to trigger an alarm once per minute,
which will spin up the background page and check for new messages.
This will effectively keep us alive as long as chrome has open windows
or is running in the background, subject to chrome settings'
Advanced -> System -> Continue running background apps
As a chrome packaged app, we have to keep at least one window open in
order to maintain our websocket connection in the background page.
This change replaces the system window frame with custom buttons in the
inbox header, such that the 'close' button merely hides the window
rather than unloading it.
Fixes#237
FREEBIE
In a multi device world, it's possible to receive a receipt for a sync
message before the sync message actually arrives. In this case we need
to keep the receipt around and the process it when the message shows up.
My current version of chromium inexplicably exposes a crippled version
of chrome.browserAction even though we are now a packaged app and should
not have that functionality exposed to us anymore. This results in some
errors to the tune of "property 'foo' of undefined".
It also doesn't support the innerBounds property for window creation,
only the older (deprecated) bounds property.
Also make it accessible by providing a mode argument to the install
function. Previously developers could just edit the url but we no longer
have the address bar as an app window, so now they must close the
default installer and run the following from the background page
console: `extension.install('standalone')`.
In the production build, this should result in an error since it is not
supported / the register page is not included there.
Appify tabs, windows, browserAction
Port the extension.windows.focus function to new window api and
generalize its error handling in the case where the requested window
does not exist. An error will be passed to the callback.
Port extension.browserAction and rename it to the more generic
extension.onLaunched.
Use of the id option when opening a window ensures that attempting to
open a duplicate window merely focuses the existing window.
Finally, after registration, close the options window and open the
inbox.
Port extension.remove
Add window.storage to the background page, which loads all data from the
'items' store in indexeddb, caching them in memory for synchronous
access, then override textsecure storage to use that in memory store.