Commit Graph

114 Commits

Author SHA1 Message Date
george-signal
45186034d6
Bring order to index paths in All Media 2023-03-06 14:55:17 -08:00
george-signal
ce18ac8d29
[All media] Add filtering to prerelease builds. 2023-03-03 15:55:13 -08:00
george-signal
242d949439
Race condition fixes in MediaGallerySections 2023-01-23 15:45:24 -08:00
Evan Hahn
f387afb745
Clean up badge expiration sheet tests
This test-only change should have no user impact.

I wrote these tests last year (in
c5bdf6c094). They test every single
string, which proves annoying to update and offers little value. This
updates the tests in an effort to make them more useful.
2023-01-20 18:30:17 -08:00
Sasha Weiss
bcbf52101e
PayPal recurring donations
Co-Authored-By: Evan Hahn <evanhahn@signal.org>
2023-01-18 14:48:50 -08:00
Evan Hahn
46b4cb7fae Update gift badge copy on badge expiration sheet 2023-01-11 09:44:59 -06:00
Evan Hahn
f3b8ded89f
Show "choose payment method" sheet for gift badges
We plan to support PayPal and credit/debit cards for gift badges. This
change, invisible to external users, is a step towards that.

Before this change, Apple Pay was always shown as the only payment
method. Now, all supported payment methods are shown.

PayPal and card don't do anything right now—they show an error UI if
tapped.
2023-01-10 13:35:05 -06:00
george-signal
e47a9cba34
Adopt MediaGalleryCollectionViewUpdater, redux. 2023-01-04 14:26:44 -08:00
Harry
120c7ebd8e
Revert "Adopt MediaGalleryCollectionViewUpdater"
This reverts commit e8f21942d5.
2022-12-23 14:00:28 -08:00
george-signal
e8f21942d5
Adopt MediaGalleryCollectionViewUpdater 2022-12-20 17:26:30 -08:00
Evan Hahn
75cdf810cd Donation error string metadata should mention Apple Pay
This change should have no user impact.

`DONATION_PAYMENT_ERROR_*` is now `APPLE_PAY_DONATION_ERROR_*`, and
Apple Pay is now mentioned in the string comments. None of the
user-visible strings actually change.

We plan to show different errors for cards and PayPal and this will help
us get there, but I think this is a useful change on its own.
2022-12-13 15:04:06 -06:00
Sasha Weiss
6f30bd58ca Fix badge expiration test across language localizations 2022-12-01 14:39:03 -08:00
Sasha Weiss
ee4799b506 One-time donations via PayPal 2022-12-01 14:39:03 -08:00
Sasha Weiss
40e765fa4f Integrate the /v1/subscription/configuration API 2022-12-01 14:39:03 -08:00
Sasha Weiss
b143e585d9 Only allow PayPal for one-time and gift donations 2022-12-01 14:39:03 -08:00
Evan Hahn
53642df893 Format credit/debit card donation inputs
This formats the text users enter on the card donation screen. For
example, "4242424242424242" becomes "4242 4242 4242 4242".

It uses [the `textField(_:shouldChangeCharactersIn:replacementString:)`
method of `UITextFieldDelegate`][0] to accomplish this, and implements a
helper that can handle all the cases (inserting, deleting, replacing,
pasting, and so on).

Tested this in an iPhone 14 simulator and on a physical iOS 12 device.

[0]: https://developer.apple.com/documentation/uikit/uitextfielddelegate/1619599-textfield
2022-11-23 18:49:21 -06:00
george-signal
c74f30b3b6
Add MediaGalleryCollectionViewUpdater
This is the keystone commit of turning MediaTileView asynchronous.

Because updates to the collection view's datasource now happen asynchronously and out-of-order, the batch update must be computed on the main queue based on what has changed.

MediaGalleryCollectionViewUpdater combines a log of operations from JournalingOrderedDictionary with the previous state of the collection view to compute insertions/deletions/reloads of items and sections.
2022-11-23 15:20:48 -08:00
Evan Hahn
21f2a36125
Add skeleton credit/debit card donation support
This adds very basic support for donations via credit/debit card. It's
missing important features which is why it's behind an internal-only
feature flag.

At a high level, this adds a new screen with a card form. This card data
is submitted to Stripe and then uses the same "rails" as our existing
Apple Pay donations.

This change is missing a few important features, intended to be added
soon:

- [3D Secure][3DS] support
- Validation error messages
- Input formatting (e.g., "1234" becomes "12/34")
- Gift badge support (currently only supports Apple Pay)
- Various smaller UI changes

[3DS]: https://stripe.com/docs/payments/3d-secure
2022-11-22 09:07:36 -06:00
Evan Hahn
6bf554ca17
Update subscriptions without re-prompting for Apple Pay
We shouldn't ask for Apple Pay when users update their subscriptions.
The update should just happen, unless there was previously a charge
failure.

This also fixes two other bugs:

- Some race conditions around observing subscription jobs
- One-time donations used the wrong progress spinner

This makes an upcoming change easier but is also useful on its own.
2022-11-20 08:08:15 -06:00
george-signal
7a49a0fa82
Adopt JournalingOrderedDictionary. (#5405)
This change also moves mutation to the ordered
dictionary into a separate queue. There is no
change to behavior yet because the queue is only
dispatched to synchronously so far. Doing this
forces changes to how database transactions are
acquired since they must be used on the same queue
where they orignated.
2022-11-16 17:31:23 -08:00
george-signal
214229f0cb
Add JournalingOrderedDictionary
This is a wrappar around OrderedDictionary that
maintains a change log to facilitate
UICollectionView batch updates in a system with
concurrent updates to the dictionary.
2022-11-10 17:29:04 -08:00
Evan Hahn
b12d1c3d28
Update badge expiration sheet to match new designs
This updates the copy and the "Donate" button.
2022-11-10 17:08:35 -06:00
Evan Hahn
ec78cc3a0a
Hide Apple Pay UI when the server disallows your region
_I recommend reviewing this with whitespace changes disabled._

The server's `global.donations.apayDisabledRegions` is a list of regions
(phone number prefixes) where Apple Pay is disallowed. This respects
that.
2022-11-10 16:19:47 -06:00
Evan Hahn
1ec693e2b9
Remove localized string values from source
This change should have no user impact.

We decided to stop doing this, so I removed them all. (I _think_ I got
them all, anyway.)
2022-11-09 13:50:00 -08:00
Evan Hahn
3f5f08e892
Use consistent OK button in badge expiration sheet
The badge expiration sheet sometimes shows "Okay" and sometimes shows
"Ok". This makes it consistent.

I think this is a useful change on its own but it'll make an upcoming
change a little smaller.
2022-11-08 16:31:33 -08:00
Evan Hahn
198fc1784a
Consolidate one-time & monthly donation screens
Previously, one-time donations were on one screen and monthly donations
were on another. Now, they're on a single screen with a picker.

Most of the interesting changes are in `DonateViewController`.

Other things of note:

- There are some new TODOs here for existing bugs I didn't fix. For
  example, one-time donations don't do so well if there are any problems
  at all.

- Even though we only support Apple Pay, there's code that alludes to
  additional payment methods. For example,
  `DonateChoosePaymentMethodSheet`. We'll expand on this in the future.

- Users should only be able to select currencies that the server
  supports. For example, you shouldn't be able to select EUR if the
  server doesn't support euros. This wasn't working correctly before,
  but is fixed here.

  We already fixed one part, where users could change to an unsupported
  currency (see dab02f30ae). However, if
  your _default_ currency is unsupported and you didn't change it,
  that's no good. This is unlikely for most users but could happen.

  I fixed this by changing it from (effectively)
  `Locale.current.currencyCode ?? "USD"`, which might not be supported,
  to a preference list, choosing the first one the server likes.

- I skip animations if the Reduce Motion setting is enabled.

- On the donation screen, the logic for the preview badge has changed
  slightly. If you already have a badge, we'll always use that.
2022-11-08 16:27:50 -06:00
george-signal
9956f148de
Move impl into State (#5345)
In order to get the most out of struct method
mutability annotations, move the code that
modifies MediaGallerySections.State into State.
The remaining methods in MediaGallerySections are
wafer-thin wrappers. Eventually
MediaGallerySections will justify its existence by
managing two instances of state.
2022-11-04 15:48:27 -07:00
george-signal
b9e8235d30
Store rowid for each item in MediaGallerySections
* Encapsulate mutable state of MediaGallerySections.

This is the first baby step toward imbuing
MediaGallerySections with multi-version
concurrency.

* Store rowid for each item in MediaGallerySections

This is necessary for forthcoming asynchronicity
as well as to correct existing problems where an
IndexPath is used as an identifier for an item.
IndexPath is a bad identifier because a database
change can invalidate it at any time. This will be
needed, for example, by an async ensureItemLoaded
method.
2022-11-04 15:23:14 -07:00
Evan Hahn
4675434c6c Add FiatMoney type
This change should have no user impact.

We commonly pair a currency, like USD, and an amount, like 1.23. This
adds the `FiatMoney` struct. It's a simple struct with two fields.

After adding it, I tried to use it everywhere. (It's possible I missed a
spot.)

I think this is a useful change on its own, but it'll be nice for an
upcoming change, too.

See also: [Android's equivalent class][0].

[0]: cb65347bb3/core-util/src/main/java/org/signal/core/util/money/FiatMoney.java (L1)
2022-10-26 17:04:51 -05:00
Evan Hahn
370ff654e7
Change license to AGPL
Change license to AGPL

This commit:

- Updates the `LICENSE` file

- Start every file with something like:

      // Copyright YEAR_FIRST_PUBLISHED Signal Messenger, LLC
      // SPDX-License-Identifier: AGPL-3.0-only

---

First, I removed existing license headers with this Ruby 3.1.2 script:

    require 'set'

    EXTENSIONS_TO_CHECK = Set['.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift']

    same = 0
    different = 0

    all_files = `git ls-files`.lines.map { |line| line.strip }
    all_files.each do |relative_path|
      if relative_path == 'Pods'
        next
      end

      unless EXTENSIONS_TO_CHECK.include? File.extname(relative_path)
        next
      end

      path = File.expand_path(relative_path)

      contents = File.read(path)
      new_contents = contents.sub(/\/\/\n\/\/  Copyright .*\n\/\/\n\n/, '')

      if contents == new_contents
        same += 1
      else
        different += 1
      end

      File.write(path, new_contents)
    end

    puts "updated #{different} file(s), left #{same} untouched"

I'm sure this script could be improved, but it worked well enough.

Then, I created `Scripts/lint/lint-license-headers` and ran it to auto-
fix a lot of files. This changed the mode of some files, but I think
that's actually desirable. For example,
`SignalServiceKit/src/Util/AppContext.m` previously had a mode of
`0755/-rwxr-xr-x`, and it's now `0644/-rw-r--r--`.

Then I fixed some stragglers and updated the precommit script.

See [a similar change in the Desktop app][0].

[0]: 8bfaf598af
2022-10-13 08:25:37 -05:00
Evan Hahn
a066511a62 Remove useless test setup/teardown
We had a lot of functions like this, which are useless:

    override func setUp() {
        super.setUp()
    }

This removes them.
2022-10-04 14:06:34 -07:00
Sasha Weiss
07a5e95d6a
Show the MemberActionSheet for tapped names in group system messages, reapplied 2022-09-06 14:40:01 -07:00
Max Radermacher
6955b6f53a Revert tapping names in group system messages
- Revert "More flexible selection styling in CVTextLabel"
  (commit e1317d814c)

- Revert "Show the MemberActionSheet for tapped names in group system messages"
  (commit 48bc3c07d0)
2022-08-26 11:43:58 -07:00
Sasha Weiss
e1317d814c
More flexible selection styling in CVTextLabel 2022-08-25 15:33:28 -07:00
Jordan Rose
bb7d5892d9
MediaGallery: Separate the loading logic into MediaGallerySections (take two)
Re-applies 4e37af097b and ecd0740c7d after further testing and cleanup.
2022-08-24 10:46:29 -07:00
Sasha Weiss
fb11013859
Rename CVBodyTextLabel to CVTextLabel 2022-08-23 22:00:47 -07:00
Max Radermacher
def8129ac8
Handle expired gift badges
- Show an error when trying to redeem them
- Show an error if your last remaining gift badge expires
2022-07-27 13:15:58 -07:00
Max Radermacher
67a96cfc55
Fix a few issues with badge expiration body text
The Boost case used `String(format:...)` even though there aren’t any
string replacements in the body text. We should avoid formatting strings
that don’t have format specifiers.

There was also a typo in the localized string key.

This also separates the monthly donation call to action string so that
it can be reused for gift badges.
2022-07-25 08:55:32 -07:00
Max Radermacher
18de6bf70a
Fix behavior when redeeming a second gift badge 2022-07-19 11:51:08 -07:00
Ehren Kret
9b6526545e Remove unused variables 2022-05-27 00:13:17 -05:00
Evan Hahn
d2836235af Show additional details for subscription charge failures
In c5bdf6c094, we started to show errors
when a subscription failed to renew because of a charge failure.

Now, we show additional text depending on what the charge failure was.
For example, if you had an invalid card number, we show a special string
for that.
2022-05-25 18:17:53 +00:00
Evan Hahn
baddc85564 Add first gift badge screen
This adds the first screen for badge gifting. It lets you see the gift
badge, pick the currency, and advance to the next screen.

It also adds a skeleton for the next screen, so there's somewhere to
advance to, but that screen is unfinished.

All of this is behind disabled flags, so this should have no user
impact.
2022-05-19 14:07:23 +00:00
Evan Hahn
4c0e511793 Fix typos in localization string comments
I noticed a few typos in localization string comments while working on
c5bdf6c094. This fixes those.

This should have no (direct) user impact.
2022-05-16 12:26:31 -05:00
Evan Hahn
c5bdf6c094 Improve subscription charge error UI
Currently, when your subscription expires due a charge failure, we
incorrectly tell you that it's due to inactivity. This fixes that, by
telling you about the charge failure.

This is a bit difficult to test on its own, so I:

- Faked out the smallest pieces I could in an effort to test states
  manually
- Created `BadgeErrorSheetState` which is pretty thoroughly tested
2022-05-12 18:28:14 -05:00
Jordan Rose
182e97f02c Revert "MediaGallery: Separate the lazy loading logic into a testable MediaGallerySections"
This reverts commit 4e37af097b.
2022-05-10 10:34:57 -07:00
Jordan Rose
4e37af097b
MediaGallery: Separate the lazy loading logic into a testable MediaGallerySections
MediaGallery models a list of GalleryDate-based sections (in practice, months), each of which has a certain number of items. Sections are loaded on demand (that is, there may be newer and older sections that are not in the model), and always know their number of items. Items are also loaded on demand, potentially non-contiguously. This model is designed around the needs of UICollectionView (a thread's All Media view), but it also supports flat views of media (swiping between items in the media detail view, which can cross album boundaries).

This model is a result of refactoring I did last year to improve the performance and UX of the All Media view, but the implementation in MediaGallery.swift is entangled with the actual attachments and messages in the database, making it hard to test. This commit pulls the state management part out into its own struct, MediaGallerySections, which wraps the OrderedDictionary of items-by-section. Our MediaGalleryItem model references TSAttachmentStream directly and our queries go straight to the database, so to avoid having to set up real attachments in testing, MediaGallerySections is generic over a loader (delegate? data source?) of items with "gallery dates" and unique IDs. Now all the basic section- and item-loading APIs can be unit-tested.
2022-05-09 18:04:12 -07:00
Jordan Rose
1689aec8c6 Don't linkify a truncated URL until the user expands the message body
This is implemented by seeing if a data-detected item is at the very
end of a truncated string, in which case it isn't trusted to be
complete. This applies not only to URLs, but to other data-detected
items as well like emails and addresses. It's also specific to
truncated text since a user could very well type an ellipsis in their
actual message, and *that* shouldn't prevent linkification.

It's possible that an item at the very end of a truncated string *is*
complete (i.e. the truncation point was just *after* the URL), but we
can't be sure without comparing to the full text or storing additional
information, and that's trickier for a number of reasons. The user can
still expand the message and get the linkification in this case.
2022-05-06 10:56:45 -07:00
Matthew Chen
9176ce6309 Add SignalUI framework target. 2021-10-19 09:39:54 -03:00
Michelle Linington
744c527233 Fix tests 2021-08-26 22:47:07 -07:00
Michelle Linington
6417494bc3 Fix tests 2021-08-19 16:54:29 -07:00