Previously, sending a gift badge was not a durable operation, which
meant that crashes/failures could cause users to have their payment
methods charged without actually sending the badge.
Now, the flow is split up into two steps: non-durable parts before the
charge is attempted, and durable parts afterward.
The high-level flow is:
1. Prepare the payment, which involves a couple of repeatable network
requests.
2. Enqueue a job with the prepared payment, that:
1. Charges the payment method (idempotently)
2. Requests a receipt credential (idempotently)
3. Enqueues a gift message, and optionally a text message
3. When the job completes, open the conversation in the UI.
The database writes are ultimately serial, so there’s little benefit to
scheduling each of these on its own queue. If another slow-ish write is
already happening, there’s a chance that we may see thread explosion,
with many threads waiting to write to the database.
We frequently send outgoing messages with some parameters, like "link
preview" or "quoted reply", empty.
This change lets those parameters be omitted (instead of explicitly
empty) for brevity.
This is a stylistic change and should have no user impact.
Tested this by sending a regular message, a message with a link preview,
and two messages with photos.
If you've got the `giftBadgeSending` flag enabled, you can now send gift
badges to anyone who has the capability.
This commit doesn't complete the feature, though. It is missing:
- Proper durability and error handling (to be addressed separately)
- Saving of receipts
- A few other cleanups
This makes a few small changes to `OWSRequestFactory.boostCreatePaymentIntent()`:
- Adds tests
- Rewrites the function in Swift
I made sure I could do a boost (in staging) in the simulator.
anyUpdate runs its closure twice with different
messages but the batching context applied updates
always to the same copy of the message.
Also apply lint fixes.
* Little fix for context menu
* Add 'My Stories' section to stories tab
* Add new story thread types
* Show stories in conversation picker
* Support for sending stories
* Update story list when sending stories
* Add basic 'My Stories' view controller
* Initial stories settings screens
* Consolidate TSPrivateStoryThread and TSMyStoryThread into one class
* Require an explicit read transaction to initialize an outgoing message
* Fix linting
* Allow enabling group story from internal settings
* Fix tests
* PR Feedback
This respects the `giftBadges` capability when trying to send gift
badges. In other words, it prevents you from sending gift badges to
someone who lacks the capability.
The bulk of this change involves fetching and saving of this new
capability. The rest of the code involves showing it on the "choose
recipient" screen (and some debug screens).
A migration (introduced in b2c0e975b6)
tries to save legacy avatars to disk. However, if that legacy avatar
data is invalid, we'd crash on launch.
A user encountered this.
Our possible solutions:
1. If legacy avatar data is invalid, just drop it.
2. If legacy avatar data is invalid, try to repair it. (For example, if
it's too large, try to resize it.) This might not fix the user's
issue—unfortunately, we don't know why the avatar was invalid.
3. Loosen our definition of a valid avatar. (For example, increase the
maximum size.) This also might not fix things—we don't know how the
avatar was invalid.
I went with the first solution.
This was difficult to test. While developing, I made a temporary button
that (1) updated a group, giving it an empty legacy avatar buffer (2)
tried to migrate it.
(Thanks to c5bd634ff1 for making this easy
to diagnose!)
The purpose of this change is to alleviate the
main source of CPU-grinding after sending a
message to a large group: about 20% of the total
time is spent decoding and re-encoding messages as
they are fetched from the DB and then updated with
info about which recipients have acknowledged
delivery.
Prior to this commit, we decrypted the payload and
then processed the message in a single rather deep
call tree that began in `processNextBatch`. There
are two completely different paths through this
tree that lead to handling a delivery receipt
because server-generated delivery receipts have
the necessary info in plaintext in the envelope
and client-generated delivery receipts have it in
the ciphertext.
The idea here is to break incoming envelope
handling into two parts.
In the first part, each envelope is decrypted and
other common activities (like handling sender key
distribution messages) are performed. A processing
request is created for each. Next, processing
requests are handled. It's easy to examine a
processing request to determine if it is a
delivery receipt, and if so, for what outgoing
message. Doing so allows the new
DeliveryReceiptContext class to cache message
fetches and combine updates to the same message.
Processing requests are grouped together so that
sequential delivery receipt requests enjoy caching
of messages and coalescing of updates into a
single fetch/decode/encode/commit. Other kinds of
envelopes are handled immediately to avoid
increasing memory pressure that caching multiple
messages could cause.
The gist is:
```diff
-foo = foo + 1
+foo += 1
```
Most of the violations were in generated files, so I changed and re-ran the generator.
A few of these violations required implementing some new methods, which I added tests for.
See [the docs for this rule][0].
[0]: https://realm.github.io/SwiftLint/shorthand_operator.html
_I recommend reviewing this with whitespace changes disabled._
This fixes violations of [SwiftLint's `implicit_getter` rule][0] by
removing explicit getters when an implicit one would do.
[0]: https://realm.github.io/SwiftLint/implicit_getter.html
And fix the presented content type and filename for the attachments in
a quote (it should be the type and filename of the original
attachment, not the thumbnail).
We sometimes attempt to do N operations at once where up to N of them
end up getting caught by the rate limiter. This should *not* turn into
N push challenges, or else we'll get rate limited for that too.
This removes DatabaseChanges.deletedAttachmentIds and replaces it with
a notification that carries more information: the attachment's thread
and original message received-at timestamp in addition to the unique
ID. With this information, we can correctly refresh the parts of
MediaGallery that may be affected by the deletion. (The previous
implementation was correct for the original version of MediaGallery,
but not the rewritten version I implemented last year. My apologies to
all users seeing crashes here!)
In the new model, deletions from outside of MediaGallery's control
result in reloads of the sections containing the items in question.
This may result in refetching other media that was previously loaded,
but avoids the issue in the previous implementation where existing
loaded items would now be at the wrong offsets.
Each GRDB migration runs in a separate transaction, so it’s possible
only some will be completed. In normal cases, this is fine. However, the
initial migration creates the latest version of the schema. As a result,
we must ensure all incremental migrations are atomically marked as
complete so that we don’t try to run them again after an app crash.