Skip to content

Commit bb7f9fb

Browse files
authored
Refactor NewUserLocation and LocationSheet to use async/await (#421)
* refactor NewUserLocation and LocationSheet to use async await instead of closures * rename NewUserLocation -> UserLocation * fix some issues with continuations and report all errors with location * fix merge * some lingering updates from code coverage * add full UserLocation test coverage * fix warning * add LocationSheetTests
1 parent b9c3bd6 commit bb7f9fb

23 files changed

+250
-264
lines changed

.gitignore

+18
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,21 @@ fastlane/test_output
7272
vendor/
7373

7474
.DS_Store
75+
Gemfile.lock
76+
fastlane/xcov_report/index.html
77+
fastlane/xcov_report/xccovreport-0.xccovreport
78+
fastlane/xcov_report/resources/application.css
79+
fastlane/xcov_report/resources/application.js
80+
fastlane/xcov_report/resources/bootstrap.min.css
81+
fastlane/xcov_report/resources/bootstrap.min.js
82+
fastlane/xcov_report/resources/file_cpp.png
83+
fastlane/xcov_report/resources/file_objc.png
84+
fastlane/xcov_report/resources/file_swift.png
85+
fastlane/xcov_report/resources/jquery.min.js
86+
fastlane/xcov_report/resources/main.css
87+
fastlane/xcov_report/resources/main.js
88+
fastlane/xcov_report/resources/opensans.css
89+
fastlane/xcov_report/resources/xcov_logo.png
90+
fastlane/xcov_report/xccovarchive-0.xccovarchive/Coverage
91+
fastlane/xcov_report/xccovarchive-0.xccovarchive/Index
92+
fastlane/xcov_report/xccovarchive-0.xccovarchive/Metadata.plist

FiveCalls/FiveCalls.xcodeproj/project.pbxproj

+12-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
4F11A59E2AAB956C00AF30B9 /* ScheduleReminders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F11A59D2AAB956C00AF30B9 /* ScheduleReminders.swift */; };
1717
4F2178752B488CD70032D592 /* StoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2178742B488CD70032D592 /* StoreTests.swift */; };
1818
4F2C1CA22AD5C1AC000AB39E /* YourImpact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2C1CA12AD5C1AC000AB39E /* YourImpact.swift */; };
19+
4F3A572B2B597C8E009D4616 /* UserLocationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F3A572A2B597C8E009D4616 /* UserLocationTests.swift */; };
20+
4F3A572E2B598203009D4616 /* LocationSheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F3A572D2B598203009D4616 /* LocationSheetTests.swift */; };
1921
4F4868B92AC222B300E1733A /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4868B82AC222B300E1733A /* WebView.swift */; };
2022
4F4868BB2AC3A3B400E1733A /* AboutListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4868BA2AC3A3B400E1733A /* AboutListItem.swift */; };
2123
4F4868BD2AC3A3E900E1733A /* EmailComposerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4868BC2AC3A3E900E1733A /* EmailComposerView.swift */; };
@@ -61,7 +63,6 @@
6163
B5E7626B1E40D6DD00D63D62 /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E7626A1E40D6DD00D63D62 /* Contact.swift */; };
6264
B5E762711E40D8DD00D63D62 /* JSONSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E762701E40D8DD00D63D62 /* JSONSerializable.swift */; };
6365
B5E762731E40DC5800D63D62 /* FetchIssuesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E762721E40DC5800D63D62 /* FetchIssuesOperation.swift */; };
64-
B5E806FF1E463FE300EBE553 /* UserLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E806FE1E463FE300EBE553 /* UserLocation.swift */; };
6566
B5E98E351E529E66005221B7 /* functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E98E341E529E66005221B7 /* functions.swift */; };
6667
B5E98E3B1E52A566005221B7 /* GET-v1-report.json in Resources */ = {isa = PBXBuildFile; fileRef = B5E98E3A1E52A566005221B7 /* GET-v1-report.json */; };
6768
B5E98E411E52A8AB005221B7 /* POST-report.json in Resources */ = {isa = PBXBuildFile; fileRef = B5E98E401E52A8AB005221B7 /* POST-report.json */; };
@@ -75,7 +76,7 @@
7576
D0176E932B3346FD0090E6B7 /* GET-v1-report.json in Resources */ = {isa = PBXBuildFile; fileRef = B5E98E3A1E52A566005221B7 /* GET-v1-report.json */; };
7677
D029C6D01F6F42EA00E56CC7 /* Outcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = D029C6CF1F6F42EA00E56CC7 /* Outcome.swift */; };
7778
D02FFCE2245F5733003ADA2B /* ScriptCustomizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02FFCE1245F5733003ADA2B /* ScriptCustomizationTests.swift */; };
78-
D0312DD82A84A27F00F58F6B /* NewUserLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0312DD72A84A27F00F58F6B /* NewUserLocation.swift */; };
79+
D0312DD82A84A27F00F58F6B /* UserLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0312DD72A84A27F00F58F6B /* UserLocation.swift */; };
7980
D0312DDC2A868D9A00F58F6B /* IssueDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0312DDB2A868D9A00F58F6B /* IssueDetail.swift */; };
8081
D03A20512AC53B9B008BBA6E /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03A20502AC53B9B008BBA6E /* Actions.swift */; };
8182
D03A20562AC53C54008BBA6E /* Middleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03A20552AC53C54008BBA6E /* Middleware.swift */; };
@@ -167,6 +168,8 @@
167168
4F11A59D2AAB956C00AF30B9 /* ScheduleReminders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleReminders.swift; sourceTree = "<group>"; };
168169
4F2178742B488CD70032D592 /* StoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreTests.swift; sourceTree = "<group>"; };
169170
4F2C1CA12AD5C1AC000AB39E /* YourImpact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YourImpact.swift; sourceTree = "<group>"; };
171+
4F3A572A2B597C8E009D4616 /* UserLocationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLocationTests.swift; sourceTree = "<group>"; };
172+
4F3A572D2B598203009D4616 /* LocationSheetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSheetTests.swift; sourceTree = "<group>"; };
170173
4F4868B82AC222B300E1733A /* WebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
171174
4F4868BA2AC3A3B400E1733A /* AboutListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutListItem.swift; sourceTree = "<group>"; };
172175
4F4868BC2AC3A3E900E1733A /* EmailComposerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailComposerView.swift; sourceTree = "<group>"; };
@@ -221,7 +224,6 @@
221224
B5E7626A1E40D6DD00D63D62 /* Contact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = "<group>"; };
222225
B5E762701E40D8DD00D63D62 /* JSONSerializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONSerializable.swift; sourceTree = "<group>"; };
223226
B5E762721E40DC5800D63D62 /* FetchIssuesOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchIssuesOperation.swift; sourceTree = "<group>"; };
224-
B5E806FE1E463FE300EBE553 /* UserLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserLocation.swift; sourceTree = "<group>"; };
225227
B5E98E341E529E66005221B7 /* functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = functions.swift; sourceTree = "<group>"; };
226228
B5E98E3A1E52A566005221B7 /* GET-v1-report.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "GET-v1-report.json"; sourceTree = "<group>"; };
227229
B5E98E401E52A8AB005221B7 /* POST-report.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "POST-report.json"; sourceTree = "<group>"; };
@@ -234,7 +236,7 @@
234236
D0176E912B33457D0090E6B7 /* ReportParsingTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportParsingTest.swift; sourceTree = "<group>"; };
235237
D029C6CF1F6F42EA00E56CC7 /* Outcome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Outcome.swift; sourceTree = "<group>"; };
236238
D02FFCE1245F5733003ADA2B /* ScriptCustomizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptCustomizationTests.swift; sourceTree = "<group>"; };
237-
D0312DD72A84A27F00F58F6B /* NewUserLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewUserLocation.swift; sourceTree = "<group>"; };
239+
D0312DD72A84A27F00F58F6B /* UserLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLocation.swift; sourceTree = "<group>"; };
238240
D0312DDB2A868D9A00F58F6B /* IssueDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetail.swift; sourceTree = "<group>"; };
239241
D03A20502AC53B9B008BBA6E /* Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
240242
D03A20552AC53C54008BBA6E /* Middleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Middleware.swift; sourceTree = "<group>"; };
@@ -346,10 +348,12 @@
346348
D0176E8A2B3343150090E6B7 /* ContactParsingTest.swift */,
347349
D05EDAF52B3282F500FD94BD /* IssueParsingTest.swift */,
348350
D0E042652B47D06700D22647 /* IssueTests.swift */,
351+
4F3A572D2B598203009D4616 /* LocationSheetTests.swift */,
349352
D0176E912B33457D0090E6B7 /* ReportParsingTest.swift */,
350353
D02FFCE1245F5733003ADA2B /* ScriptCustomizationTests.swift */,
351354
4F2178742B488CD70032D592 /* StoreTests.swift */,
352355
AACC2AEC1E735144004B2F2B /* StreakCounterTests.swift */,
356+
4F3A572A2B597C8E009D4616 /* UserLocationTests.swift */,
353357
);
354358
path = FiveCallsTests;
355359
sourceTree = "<group>";
@@ -446,8 +450,7 @@
446450
D029C6CF1F6F42EA00E56CC7 /* Outcome.swift */,
447451
B5E7626A1E40D6DD00D63D62 /* Contact.swift */,
448452
B5D5C9591E441FEC00C80D5F /* IssuesManager.swift */,
449-
B5E806FE1E463FE300EBE553 /* UserLocation.swift */,
450-
D0312DD72A84A27F00F58F6B /* NewUserLocation.swift */,
453+
D0312DD72A84A27F00F58F6B /* UserLocation.swift */,
451454
B50280531E46A91F00749ED7 /* AreaOffice.swift */,
452455
B50280551E47875500749ED7 /* ContactLog.swift */,
453456
AACC2AEA1E732775004B2F2B /* StreakCounter.swift */,
@@ -822,10 +825,12 @@
822825
files = (
823826
D0176E922B33457D0090E6B7 /* ReportParsingTest.swift in Sources */,
824827
D02FFCE2245F5733003ADA2B /* ScriptCustomizationTests.swift in Sources */,
828+
4F3A572E2B598203009D4616 /* LocationSheetTests.swift in Sources */,
825829
B50280681E478BF500749ED7 /* ContactLogsTests.swift in Sources */,
826830
D05EDAF62B3282F500FD94BD /* IssueParsingTest.swift in Sources */,
827831
AACC2AED1E735144004B2F2B /* StreakCounterTests.swift in Sources */,
828832
D0176E8B2B3343150090E6B7 /* ContactParsingTest.swift in Sources */,
833+
4F3A572B2B597C8E009D4616 /* UserLocationTests.swift in Sources */,
829834
D0E042662B47D06700D22647 /* IssueTests.swift in Sources */,
830835
4F2178752B488CD70032D592 /* StoreTests.swift in Sources */,
831836
);
@@ -855,7 +860,6 @@
855860
B5E98E351E529E66005221B7 /* functions.swift in Sources */,
856861
D0480B1E2A4D493B00502818 /* IssueListItem.swift in Sources */,
857862
D0480AE32A4D45A700502818 /* App.swift in Sources */,
858-
B5E806FF1E463FE300EBE553 /* UserLocation.swift in Sources */,
859863
D0DD4570229F11FF00AA94C1 /* AnalyticsManager.swift in Sources */,
860864
B5E762591E40374600D63D62 /* FetchStatsOperation.swift in Sources */,
861865
D0C465D32AD30BEE00CE3E65 /* IssueRouter.swift in Sources */,
@@ -869,7 +873,7 @@
869873
B5E762711E40D8DD00D63D62 /* JSONSerializable.swift in Sources */,
870874
A0D474B4202A22B90012BC94 /* FetchUserStatsOperation.swift in Sources */,
871875
D07D7C242B3F48A1004721B3 /* ProtocolMock.swift in Sources */,
872-
D0312DD82A84A27F00F58F6B /* NewUserLocation.swift in Sources */,
876+
D0312DD82A84A27F00F58F6B /* UserLocation.swift in Sources */,
873877
B5E7626B1E40D6DD00D63D62 /* Contact.swift in Sources */,
874878
B5E762651E40480300D63D62 /* UserDefaultsKey.swift in Sources */,
875879
D029C6D01F6F42EA00E56CC7 /* Outcome.swift in Sources */,

FiveCalls/FiveCalls/Actions.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ enum Action {
1616
case SetDonateOn(Bool)
1717
case FetchIssues
1818
case SetIssues([Issue])
19-
case FetchContacts(NewUserLocation)
19+
case FetchContacts(UserLocation)
2020
case SetContacts([Contact])
21-
case SetLocation(NewUserLocation)
21+
case SetLocation(UserLocation)
2222
case ReportOutcome(Issue, ContactLog, Outcome)
2323
case SetFetchingContacts(Bool)
2424
case SetLoadingStatsError(Error)

FiveCalls/FiveCalls/AppState.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class AppState: ObservableObject, ReduxState {
2727
@Published var donateOn = false
2828
@Published var issues: [Issue] = []
2929
@Published var contacts: [Contact] = []
30-
@Published var location: NewUserLocation? {
30+
@Published var location: UserLocation? {
3131
didSet {
3232
guard let location = self.location else { return }
3333
let defaults = UserDefaults.standard
@@ -56,13 +56,13 @@ class AppState: ObservableObject, ReduxState {
5656

5757
switch locationType {
5858
case "address", "zipCode":
59-
self.location = NewUserLocation(address: locationValue, display: locationDisplay)
59+
self.location = UserLocation(address: locationValue, display: locationDisplay)
6060
case "coordinates":
6161
let locValues = locationValue.split(separator: ",")
6262
if locValues.count != 2 { return }
6363
guard let lat = Double(locValues[0]), let lng = Double(locValues[1]) else { return }
6464

65-
self.location = NewUserLocation(location: CLLocation(latitude: lat, longitude: lng), display: locationDisplay)
65+
self.location = UserLocation(location: CLLocation(latitude: lat, longitude: lng), display: locationDisplay)
6666
default:
6767
Logger().warning("unknown stored location type data: \(locationType)")
6868
}

FiveCalls/FiveCalls/FetchContactsOperation.swift

+5-13
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,16 @@ import Foundation
1010
import OneSignal
1111

1212
class FetchContactsOperation : BaseOperation {
13-
13+
1414
var location: UserLocation
15-
15+
1616
var httpResponse: HTTPURLResponse?
1717
var error: Error?
1818
var contacts: [Contact]?
19-
20-
init(location: UserLocation) {
19+
20+
init(location: UserLocation, config: URLSessionConfiguration? = nil) {
2121
self.location = location
22-
}
2322

24-
init(location: NewUserLocation, config: URLSessionConfiguration? = nil) {
25-
let loc = UserLocation()
26-
loc.locationType = UserLocation.LocationType(rawValue: location.locationType.rawValue)
27-
loc.locationValue = location.locationValue
28-
loc.locationDisplay = location.locationDisplay
29-
self.location = loc
30-
3123
super.init()
3224
if let config {
3325
self.session = URLSession(configuration: config)
@@ -36,7 +28,7 @@ class FetchContactsOperation : BaseOperation {
3628

3729
var url: URL {
3830
var components = URLComponents(string: "https://api.5calls.org/v1/reps")
39-
let locationQueryParam = URLQueryItem(name: "location", value: location.locationValue ?? "")
31+
let locationQueryParam = URLQueryItem(name: "location", value: location.locationValue)
4032
components?.queryItems = [locationQueryParam]
4133
return components!.url!
4234
}

FiveCalls/FiveCalls/Issue.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct Issue: Identifiable, Decodable {
7070
}
7171
}
7272

73-
func markdownIssueScript(contact: Contact, location: NewUserLocation?) -> AttributedString {
73+
func markdownIssueScript(contact: Contact, location: UserLocation?) -> AttributedString {
7474
do {
7575
return try AttributedString(markdown: ScriptReplacements.replacing(script: self.script, contact: contact, location: location), options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace))
7676
} catch {

FiveCalls/FiveCalls/IssueContactDetail.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ struct IssueContactDetail: View {
164164
#Preview {
165165
let previewState = {
166166
let state = AppState()
167-
state.location = NewUserLocation(address: "3400 24th St, San Francisco, CA 94114", display: "San Francisco")
167+
state.location = UserLocation(address: "3400 24th St, San Francisco, CA 94114", display: "San Francisco")
168168
return state
169169
}()
170170

FiveCalls/FiveCalls/IssueListItem.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ struct IssueListItem: View {
5656
#Preview {
5757
let previewState = {
5858
let state = AppState()
59-
state.location = NewUserLocation(address: "3400 24th St, San Francisco, CA 94114", display: "San Francisco")
59+
state.location = UserLocation(address: "3400 24th St, San Francisco, CA 94114", display: "San Francisco")
6060
return state
6161
}()
6262

FiveCalls/FiveCalls/Localizable.strings

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
"location-instructions" = "Use an address, zip code or zip + 4";
114114
"location-error-off" = "Location permission is off";
115115
"location-error-default" = "An error occured trying to find your location";
116+
"location-or" = "Or";
116117

117118
// outcomes
118119
"outcomes.contact" = "Made Contact";

FiveCalls/FiveCalls/LocationCoordinator.swift

+9-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ enum LocationCoordinatorError: Error {
1414
case NoLocationsReturned
1515
case Unauthorized
1616
case LocationManagerError(Error)
17+
case Unknown
1718
}
1819

1920
class LocationCoordinator: NSObject, CLLocationManagerDelegate {
@@ -32,6 +33,7 @@ class LocationCoordinator: NSObject, CLLocationManagerDelegate {
3233
switch self?.manager.authorizationStatus {
3334
case .denied, .restricted:
3435
self?.locationContinuation?.resume(throwing: LocationCoordinatorError.Unauthorized)
36+
self?.locationContinuation = nil
3537
case .authorizedAlways, .authorizedWhenInUse:
3638
self?.manager.requestLocation()
3739
default:
@@ -43,17 +45,20 @@ class LocationCoordinator: NSObject, CLLocationManagerDelegate {
4345

4446
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
4547
// print("new location: \(locations)")
46-
if let location = locations.first {
48+
if let location = locations.last {
4749
locationContinuation?.resume(with: .success(location))
50+
locationContinuation = nil
4851
} else {
4952
locationContinuation?.resume(with: .failure(LocationCoordinatorError.NoLocationsReturned))
53+
locationContinuation = nil
5054
}
5155
}
5256

5357
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
5458
// print("loc manager failed: \(error)")
55-
locationContinuation?.resume(with: .failure(error))
56-
59+
locationContinuation?.resume(with: .failure(LocationCoordinatorError.Unknown))
60+
locationContinuation = nil
61+
5762
}
5863

5964
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
@@ -64,6 +69,7 @@ class LocationCoordinator: NSObject, CLLocationManagerDelegate {
6469
}
6570
} else if manager.authorizationStatus == .denied {
6671
locationContinuation?.resume(with: .failure(LocationCoordinatorError.Unauthorized))
72+
locationContinuation = nil
6773
}
6874
}
6975
}

FiveCalls/FiveCalls/LocationHeader.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import SwiftUI
1010

1111
struct LocationHeader: View {
12-
let location: NewUserLocation?
12+
let location: UserLocation?
1313
let fetchingContacts: Bool
1414

1515
var body: some View {
@@ -84,7 +84,7 @@ struct LocationHeader_Previews: PreviewProvider {
8484
static var previews: some View {
8585
VStack {
8686
LocationHeader(location: nil, fetchingContacts: true)
87-
LocationHeader(location: NewUserLocation(address: "19444"), fetchingContacts: false)
87+
LocationHeader(location: UserLocation(address: "19444"), fetchingContacts: false)
8888
Spacer()
8989
}
9090
}

0 commit comments

Comments
 (0)