Skip to content

Commit

Permalink
Merge branch 'trunk' into andrewdmontgomery/localize-more-strings
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewdmontgomery committed Sep 13, 2024
2 parents 33f1f42 + 47b6068 commit 6ff9d95
Show file tree
Hide file tree
Showing 21 changed files with 148 additions and 58 deletions.
10 changes: 8 additions & 2 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ steps:
- label: "🛠️ Build SwiftUI Demo"
key: build_swiftui
depends_on: test
command: BUILD_NUMBER=$BUILDKITE_BUILD_NUMBER make build-demo-for-distribution-swiftui
plugins: [$CI_TOOLKIT]
command: |
install_gems
BUILD_NUMBER=$BUILDKITE_BUILD_NUMBER make build-demo-for-distribution-swiftui
artifact_paths:
- ".build/artifacts/*.ipa"
- ".build/artifacts/*.dSYM.zip"
Expand All @@ -74,7 +77,10 @@ steps:
- label: "🛠️ Build UIKit Demo"
key: build_uikit
depends_on: test
command: BUILD_NUMBER=$BUILDKITE_BUILD_NUMBER make build-demo-for-distribution-uikit
plugins: [$CI_TOOLKIT]
command: |
install_gems
BUILD_NUMBER=$BUILDKITE_BUILD_NUMBER make build-demo-for-distribution-uikit
artifact_paths:
- ".build/artifacts/*.ipa"
- ".build/artifacts/*.dSYM.zip"
Expand Down
15 changes: 15 additions & 0 deletions .configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"project_name": "Gravatar-SDK-iOS",
"branch": "trunk",
"pinned_hash": "1761b0a77a5371f9127c5d0575c90f938d864537",
"files_to_copy": [
{
"file": "iOS/GravatarSDK/Secrets.swift",
"destination": "Demo/Demo/Secrets.swift",
"encrypt": true
}
],
"file_dependencies": [

]
}
2 changes: 2 additions & 0 deletions .configure-files/Secrets.swift.enc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
�m�� !u*�;h�i��ftH#L(��~}H+^�*_(,�Tg ����ɥ< �g�f���.����.�l)́���Z~�O�(s9�1�_�fh� ��Վ���RR��ۇx6�]d67��:C�j2��p� �2�/ 5��"g{F}:-�H�%k�,-��&��V����~�h#�� y0=�t�Q�Y煮�h7 �ۤDl�eD!9���o������1{`|6b`M��Z�XQ��U�RT�� B�K0>�^��(l�/��Й��)��U�gI<xP{ii����=a�É$�����J�z�Ҝ�j��v��{H�r`��BX��'X���:������pm
��� ul&�P�w
3 changes: 2 additions & 1 deletion .github/workflows/docc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ jobs:
run: |
xcodebuild docbuild -scheme Gravatar-Package \
-derivedDataPath /tmp/docbuild \
-destination 'generic/platform=iOS';
-destination 'generic/platform=iOS' \
-skipPackagePluginValidation;
- name: Process Gravatar DocC
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct DemoProfileEditorView: View {
isPresented: $isPresentingPicker,
email: email,
scope: .avatarPicker,
contentLayout: AvatarPickerContentLayoutWithPresentation.vertical(),
contentLayout: .horizontal(),
onDismiss: {
updateHasSession(with: email)
}
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ build-demo-swiftui: bundle-install

build-demo-for-distribution: build-demo-for-distribution-swiftui build-demo-for-distribution-uikit

build-demo-for-distribution-swiftui: fetch-code-signing check-build-number
build-demo-for-distribution-swiftui: fetch-code-signing check-build-number setup-secrets
bundle exec fastlane build_demo_for_distribution \
scheme:$(SCHEME_DEMO_SWIFTUI) \
build_number:$(BUILD_NUMBER)

build-demo-for-distribution-uikit: fetch-code-signing check-build-number
build-demo-for-distribution-uikit: fetch-code-signing check-build-number setup-secrets
bundle exec fastlane build_demo_for_distribution \
scheme:$(SCHEME_DEMO_UIKIT) \
build_number:$(BUILD_NUMBER)
Expand All @@ -72,6 +72,9 @@ bundle-install:
fetch-code-signing: bundle-install
bundle exec fastlane configure_code_signing

setup-secrets: bundle-install
bundle exec fastlane run configure_apply

swiftformat: # Automatically find and fixes lint issues
swift package plugin \
--allow-writing-to-package-directory \
Expand Down
4 changes: 2 additions & 2 deletions Sources/Gravatar/Cache/ImageCaching.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public protocol ImageCaching: Sendable {
/// - Parameters:
/// - image: The cache entry to set.
/// - key: The entry's key, used to be found via `.getEntry(key:)`.
func setEntry(_ entry: CacheEntry?, for key: String) async
func setEntry(_ entry: CacheEntry?, for key: String)

/// Gets a `CacheEntry` from cache for the given key, or nil if none is found.
///
Expand All @@ -18,7 +18,7 @@ public protocol ImageCaching: Sendable {
///
/// `.inProgress(task)` is used by the image downloader to check if there's already an ongoing download task for the same image. If yes, the image
/// downloader awaits that ask instead of starting a new one.
func getEntry(with key: String) async -> CacheEntry?
func getEntry(with key: String) -> CacheEntry?
}

/// The default `ImageCaching` used by this SDK.
Expand Down
18 changes: 9 additions & 9 deletions Sources/Gravatar/Network/Services/ImageDownloadService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ public struct ImageDownloadService: ImageDownloader, Sendable {
/// - cache: A type which will perform image caching operations.
public init(client: HTTPClient? = nil, cache: ImageCaching? = nil) {
self.client = client ?? URLSessionHTTPClient()
self.imageCache = cache ?? ImageCache()
self.imageCache = cache ?? ImageCache.shared
}

public init(urlSession: URLSession, cache: ImageCaching? = nil) {
self.client = URLSessionHTTPClient(urlSession: urlSession)
self.imageCache = cache ?? ImageCache()
self.imageCache = cache ?? ImageCache.shared
}

public func fetchImage(with url: URL, forceRefresh: Bool = false, processingMethod: ImageProcessingMethod = .common()) async throws -> ImageDownloadResult {
Expand All @@ -39,7 +39,7 @@ public struct ImageDownloadService: ImageDownloader, Sendable {
}

private func cachedImage(for url: URL) async throws -> UIImage? {
guard let entry = await imageCache.getEntry(with: url.absoluteString) else { return nil }
guard let entry = imageCache.getEntry(with: url.absoluteString) else { return nil }
switch entry {
case .inProgress(let task):
let image = try await task.value
Expand All @@ -50,15 +50,15 @@ public struct ImageDownloadService: ImageDownloader, Sendable {
}

private func awaitAndCacheImage(from task: Task<UIImage, Error>, cacheKey key: String) async throws -> UIImage {
await imageCache.setEntry(.inProgress(task), for: key)
imageCache.setEntry(.inProgress(task), for: key)
let image: UIImage
do {
image = try await task.value
} catch {
await imageCache.setEntry(nil, for: key)
imageCache.setEntry(nil, for: key)
throw error
}
await imageCache.setEntry(.ready(image), for: key)
imageCache.setEntry(.ready(image), for: key)
return image
}

Expand All @@ -78,13 +78,13 @@ public struct ImageDownloadService: ImageDownloader, Sendable {
}
}

public func cancelTask(for url: URL) async {
if let entry = await imageCache.getEntry(with: url.absoluteString) {
public func cancelTask(for url: URL) {
if let entry = imageCache.getEntry(with: url.absoluteString) {
switch entry {
case .inProgress(let task):
if !task.isCancelled {
task.cancel()
await imageCache.setEntry(nil, for: url.absoluteString)
imageCache.setEntry(nil, for: url.absoluteString)
}
default:
break
Expand Down
2 changes: 1 addition & 1 deletion Sources/Gravatar/Network/Services/ImageDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ public protocol ImageDownloader: Sendable {

/// Cancels the download task for the given `URL`.
/// - Parameter url: `URL` of the download task.
func cancelTask(for url: URL) async
func cancelTask(for url: URL)
}
2 changes: 1 addition & 1 deletion Sources/Gravatar/Network/Services/ProfileService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public struct Avatar: Decodable, Sendable {
}

public var url: String {
"https://gravatar.com\(imageUrl)?size=256"
imageUrl
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ extension GravatarWrapper where Component: UIImageView {
}
}

public func cancelImageDownload() async {
public func cancelImageDownload() {
if let sourceURL {
await imageDownloader?.cancelTask(for: sourceURL)
imageDownloader?.cancelTask(for: sourceURL)
}
}

Expand Down
16 changes: 3 additions & 13 deletions Sources/GravatarUI/SwiftUI/AvatarPicker/AvatarPickerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct AvatarPickerView<ImageEditor: ImageEditorView>: View {
public var body: some View {
ZStack {
VStack(spacing: 0) {
email()
EmailText(email: model.email)
.accumulateIntrinsicHeight()
profileView()
.accumulateIntrinsicHeight()
Expand Down Expand Up @@ -61,16 +61,6 @@ struct AvatarPickerView<ImageEditor: ImageEditorView>: View {
}
}

@ViewBuilder
private func email() -> some View {
if let email = model.email?.rawValue, !email.isEmpty {
Text(email)
.padding(.bottom, Constants.emailBottomSpacing / 2)
.font(.footnote)
.foregroundColor(Color(UIColor.secondaryLabel))
}
}

private func header() -> some View {
VStack(alignment: .leading) {
Text(Localized.Header.title)
Expand Down Expand Up @@ -281,7 +271,7 @@ struct AvatarPickerView<ImageEditor: ImageEditorView>: View {
.cornerRadius(8)
.shadow(color: profileShadowColor, radius: profileShadowRadius, y: 3)
})
.padding(.top, Constants.emailBottomSpacing / 2)
.padding(.top, Constants.profileViewTopSpacing / 2)
.padding(.bottom, Constants.vStackVerticalSpacing)
.padding(.horizontal, Constants.horizontalPadding)
}
Expand Down Expand Up @@ -312,7 +302,7 @@ private enum AvatarPicker {
static let lightModeShadowColor = Color(uiColor: UIColor.rgba(25, 30, 35, alpha: 0.2))
static let title: String = "Gravatar" // defined here to avoid translations
static let vStackVerticalSpacing: CGFloat = .DS.Padding.medium
static let emailBottomSpacing: CGFloat = .DS.Padding.double
static let profileViewTopSpacing: CGFloat = .DS.Padding.double
}

enum Localized {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class AvatarPickerViewModel: ObservableObject {
let service = AvatarService()
do {
let avatar = try await service.upload(squareImage, accessToken: accessToken)
await ImageCache.shared.setEntry(.ready(squareImage), for: avatar.url)
ImageCache.shared.setEntry(.ready(squareImage), for: avatar.url)

let newModel = AvatarImageModel(id: avatar.id, source: .remote(url: avatar.url))
grid.replaceModel(withID: localID, with: newModel)
Expand Down
17 changes: 17 additions & 0 deletions Sources/GravatarUI/SwiftUI/AvatarPicker/Views/EmailText.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SwiftUI

struct EmailText: View {
enum Constants {
static let bottomSpacing: CGFloat = .DS.Padding.double
}

let email: Email?
var body: some View {
if let email = email?.rawValue, !email.isEmpty {
Text(email)
.padding(.bottom, Constants.bottomSpacing / 2)
.font(.footnote)
.foregroundColor(Color(UIColor.secondaryLabel))
}
}
}
17 changes: 17 additions & 0 deletions Sources/GravatarUI/SwiftUI/OAuthSession/OAuthSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,30 @@ private struct OAuthURLParams: Encodable {
let blogID: String
let redirectURI: String
let userEmail: String
var scope1: String
var scope2: String
var scope3: String

public enum CodingKeys: String, CodingKey, CaseIterable {
case clientID
case responseType
case blogID
case redirectURI
case userEmail
case scope1 = "scope[1]"
case scope2 = "scope[2]"
case scope3 = "scope[3]"
}

init(email: Email, secrets: Configuration.OAuthSecrets) {
self.clientID = secrets.clientID
self.responseType = "code"
self.blogID = "0"
self.redirectURI = secrets.redirectURI
self.userEmail = email.rawValue
self.scope1 = "gravatar-profile:read"
self.scope2 = "gravatar-profile:manage"
self.scope3 = "auth"
}
}

Expand Down
51 changes: 45 additions & 6 deletions Sources/GravatarUI/SwiftUI/ProfileEditor/QuickEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,27 @@ struct QuickEditor<ImageEditor: ImageEditorView>: View {
func noticeView() -> some View {
VStack {
if !isAuthenticating {
// TODO: Localize Button title
Button("Authenticate (Future error view)") {
Task {
performAuthentication()
}
}
EmailText(email: email)
ContentLoadingErrorView(
title: Constants.Localized.LogInError.title,
subtext: Constants.Localized.LogInError.subtext,
image: nil,
actionButton: {
Button {
performAuthentication()
} label: {
CTAButtonView(Constants.Localized.LogInError.buttonTitle)
}
},
innerPadding: .init(
top: .DS.Padding.double,
leading: .DS.Padding.double,
bottom: .DS.Padding.double,
trailing: .DS.Padding.double
)
)
.padding(.horizontal, .DS.Padding.double)
Spacer()
} else {
ProgressView()
}
Expand Down Expand Up @@ -100,6 +115,30 @@ struct QuickEditor<ImageEditor: ImageEditorView>: View {
}
}

extension QuickEditorConstants {
enum Localized {
enum LogInError {
static let title = SDKLocalizedString(
"AvatarPicker.ContentLoading.Failure.LogInError.title",
value: "Login required",
comment: "Title of a message advising the user that something went wrong while trying to log in."
)

static let buttonTitle = SDKLocalizedString(
"AvatarPicker.ContentLoading.Failure.SessionExpired.LogInError.buttonTitle",
value: "Log in",
comment: "Title of a button that will begin the process of authenticating the user, appearing beneath a message stating that a previous log in attept has failed."
)

static let subtext = SDKLocalizedString(
"AvatarPicker.ContentLoading.Failure.SessionExpired.LogInError.subtext",
value: "To modify your Gravatar profile, you need to log in first.",
comment: "A message describing the error and advising the user to login again to resolve the issue"
)
}
}
}

#Preview {
QuickEditor<NoCustomEditor>(
email: .init(""),
Expand Down
6 changes: 3 additions & 3 deletions Sources/TestHelpers/TestImageCache.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Gravatar
import UIKit

package actor TestImageCache: ImageCaching {
package class TestImageCache: ImageCaching, @unchecked Sendable {
var dict: [String: CacheEntryWrapper] = [:]

package private(set) var getImageCallsCount = 0
Expand All @@ -10,7 +10,7 @@ package actor TestImageCache: ImageCaching {

package init() {}

package func setEntry(_ entry: Gravatar.CacheEntry?, for key: String) async {
package func setEntry(_ entry: Gravatar.CacheEntry?, for key: String) {
guard let entry else {
dict[key] = nil
return
Expand All @@ -24,7 +24,7 @@ package actor TestImageCache: ImageCaching {
dict[key] = CacheEntryWrapper(entry)
}

package func getEntry(with key: String) async -> Gravatar.CacheEntry? {
package func getEntry(with key: String) -> Gravatar.CacheEntry? {
getImageCallsCount += 1
return dict[key]?.cacheEntry
}
Expand Down
Loading

0 comments on commit 6ff9d95

Please sign in to comment.