├── .buildkite ├── commands │ ├── publish-pod.sh │ ├── upload-demo-to-firebase.sh │ └── validate-pods.sh ├── pipeline.yml └── shared-pipeline-vars ├── .bundle └── config ├── .configure ├── .configure-files └── Secrets.swift.enc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── enhancement.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── docc.yml │ └── run-danger.yml ├── .gitignore ├── .rubocop.yml ├── .ruby-version ├── .spi.yml ├── .swiftformat ├── .swiftlint.yml ├── .travis.yml ├── CODE-OF-CONDUCT.md ├── CODINGSTYLE.md ├── CONTRIBUTING.md ├── Dangerfile ├── Demo ├── Demo │ ├── Base.xcconfig │ ├── Enterprise.xcconfig │ ├── Gravatar-Demo │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── 1024x10244.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── placeholder.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── placeholder.png │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Common │ │ │ ├── BaseFormViewController.swift │ │ │ ├── FormFields │ │ │ │ ├── ButtonField.swift │ │ │ │ ├── ButtonLabelField.swift │ │ │ │ ├── ImageFormField.swift │ │ │ │ ├── LabelField.swift │ │ │ │ ├── SegmentedControlField.swift │ │ │ │ ├── SwitchField.swift │ │ │ │ └── TextFormField.swift │ │ │ └── SwitchWithLabel.swift │ │ ├── DemoAvatarDownloadViewController.swift │ │ ├── DemoBaseProfileViewController.swift │ │ ├── DemoFetchProfileViewController.swift │ │ ├── DemoImageCropperViewController.swift │ │ ├── DemoProfileConfigurationViewController.swift │ │ ├── DemoProfilePresentationStylesViewController.swift │ │ ├── DemoProfileViewsViewController.swift │ │ ├── DemoQuickEditorViewController.swift │ │ ├── DemoRemoteSVGViewController.swift │ │ ├── DemoUIImageViewExtensionViewController.swift │ │ ├── DemoUploadImageViewController.swift │ │ ├── Gravatar-Demo.Base.xcconfig │ │ ├── Gravatar-Demo.Release.xcconfig │ │ ├── Info.plist │ │ ├── MainTableViewController.swift │ │ ├── SceneDelegate.swift │ │ ├── SwiftUI │ │ │ ├── ContentView.swift │ │ │ ├── Controls │ │ │ │ ├── AboutInfoChecklistView.swift │ │ │ │ ├── AvatarPickerLayoutOptions.swift │ │ │ │ ├── QEColorSchemePickerRow.swift │ │ │ │ ├── QEContentLayoutPickerRow.swift │ │ │ │ ├── QEInitialPagePickerRow.swift │ │ │ │ ├── QEScopesPickerRow.swift │ │ │ │ └── QEVerticalStylePickerRow.swift │ │ │ ├── DemoAvatarView.swift │ │ │ ├── DemoProfileEditorView.swift │ │ │ ├── DemoProfileView.swift │ │ │ ├── ProfileViewTypePickerRow.swift │ │ │ ├── SafariView.swift │ │ │ ├── TestImageCropper.swift │ │ │ └── View+Demo.swift │ │ ├── ar.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── de.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── es.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── fr.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── he.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── id.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── it.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── ja.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── ko.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── nl.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── pt-BR.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── ru.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── sv.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── tr.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ ├── zh-Hans.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ │ └── zh-Hant.lproj │ │ │ ├── LaunchScreen.strings │ │ │ └── Main.strings │ ├── Localizations │ │ ├── ar.lproj │ │ │ └── Localizable.strings │ │ ├── de.lproj │ │ │ └── Localizable.strings │ │ ├── en.lproj │ │ │ └── Localizable.strings │ │ ├── es.lproj │ │ │ └── Localizable.strings │ │ ├── fr.lproj │ │ │ └── Localizable.strings │ │ ├── he.lproj │ │ │ └── Localizable.strings │ │ ├── id.lproj │ │ │ └── Localizable.strings │ │ ├── it.lproj │ │ │ └── Localizable.strings │ │ ├── ja.lproj │ │ │ └── Localizable.strings │ │ ├── ko.lproj │ │ │ └── Localizable.strings │ │ ├── nl.lproj │ │ │ └── Localizable.strings │ │ ├── pt-BR.lproj │ │ │ └── Localizable.strings │ │ ├── ru.lproj │ │ │ └── Localizable.strings │ │ ├── sv.lproj │ │ │ └── Localizable.strings │ │ ├── tr.lproj │ │ │ └── Localizable.strings │ │ ├── zh-Hans.lproj │ │ │ └── Localizable.strings │ │ └── zh-Hant.lproj │ │ │ └── Localizable.strings │ └── Secrets.tpl ├── Gravatar Demo.entitlements └── Gravatar-Demo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved │ └── xcshareddata │ └── xcschemes │ └── Gravatar Demo.xcscheme ├── Documentation └── ruby.md ├── Gemfile ├── Gemfile.lock ├── Gravatar.podspec ├── GravatarUI.podspec ├── LICENSE.md ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── SECURITY.md ├── Sources ├── Gravatar │ ├── AvatarURL.swift │ ├── Bundle+ResourceBundle.swift │ ├── BundleInfo.swift │ ├── Cache │ │ └── ImageCaching.swift │ ├── Configuration.swift │ ├── Extensions │ │ ├── String.swift │ │ ├── UIImage+Additions.swift │ │ ├── UIImage+Square.swift │ │ └── URL.swift │ ├── Gravatar.docc │ │ ├── 1. GettingStarted.md │ │ ├── 2. DownloadingAvatar.md │ │ ├── 3. UploadingAvatar.md │ │ ├── 4. FetchingProfile.md │ │ ├── Gravatar.md │ │ └── Resources │ │ │ └── gravatar-sdk@2x.png │ ├── Identifiers │ │ ├── AvatarIdentifier.swift │ │ ├── Email.swift │ │ ├── HashID.swift │ │ └── ProfileIdentifier.swift │ ├── Network │ │ ├── APIErrorPayload.swift │ │ ├── AvatarType.swift │ │ ├── Data+Extension.swift │ │ ├── Error+Extension.swift │ │ ├── Errors.swift │ │ ├── HTTPStatus.swift │ │ ├── HTTPURLResponse+Additions.swift │ │ ├── ImageDownloadResult.swift │ │ ├── Services │ │ │ ├── AvatarService.swift │ │ │ ├── CheckTokenAuthorizationService.swift │ │ │ ├── HTTPClient.swift │ │ │ ├── ImageDownloadService.swift │ │ │ ├── ImageDownloader.swift │ │ │ ├── ImageUploadService.swift │ │ │ ├── ImageUploader.swift │ │ │ ├── Model │ │ │ │ ├── Avatar+AvatarDetails.swift │ │ │ │ ├── AvatarDetails.swift │ │ │ │ └── AvatarRating+Additions.swift │ │ │ ├── ProfileFetching.swift │ │ │ ├── ProfileService.swift │ │ │ ├── ServiceConfig.swift │ │ │ └── URLSessionHTTPClient.swift │ │ └── URLSessionProtocol.swift │ ├── OpenApi │ │ └── Generated │ │ │ ├── AssociatedResponse.swift │ │ │ ├── Avatar.swift │ │ │ ├── AvatarRating.swift │ │ │ ├── CryptoWalletAddress.swift │ │ │ ├── GalleryImage.swift │ │ │ ├── Interest.swift │ │ │ ├── Language.swift │ │ │ ├── Link.swift │ │ │ ├── ModelError.swift │ │ │ ├── Profile.swift │ │ │ ├── ProfileContactInfo.swift │ │ │ ├── ProfilePayments.swift │ │ │ ├── SetEmailAvatarRequest.swift │ │ │ ├── UpdateAvatarRequest.swift │ │ │ ├── UpdateProfileRequest.swift │ │ │ └── VerifiedAccount.swift │ ├── Options │ │ ├── AvatarQueryOptions.swift │ │ ├── AvatarSelection.swift │ │ ├── DefaultAvatarOption.swift │ │ ├── ImageDownloadOptions.swift │ │ ├── ImageProcessorOption.swift │ │ ├── ImageSize.swift │ │ ├── ImageTransition.swift │ │ └── Rating.swift │ ├── ProfileURL.swift │ ├── Resources │ │ ├── .gitkeep │ │ └── SDKInfo.plist │ ├── UIImage │ │ └── Processor │ │ │ ├── DefaultImageProcessor.swift │ │ │ └── ImageProcessor.swift │ └── URLComponents+Additions.swift ├── GravatarUI │ ├── Base │ │ ├── Array+Additions.swift │ │ ├── AssociatedObject.swift │ │ ├── Box.swift │ │ ├── IdentifiableURL.swift │ │ ├── Result+Gravatar.swift │ │ ├── SimpleCounter.swift │ │ ├── UIApplication+Additions.swift │ │ └── UIView+Additions.swift │ ├── Bundle+ResourceBundle.swift │ ├── Configurator │ │ └── Configurators.swift │ ├── DesignSystem │ │ ├── CGFloat+DesignSystem.swift │ │ ├── Palette.swift │ │ ├── UIColor+DesignSystem.swift │ │ └── UIFont+DesignSystem.swift │ ├── Error+Extension.swift │ ├── Exports.swift │ ├── GravatarCompatibleUI │ │ ├── GravatarCompatible.swift │ │ └── UIImageView+Gravatar.swift │ ├── GravatarUI.docc │ │ ├── AvatarViewArticle.md │ │ ├── GettingStarted.md │ │ ├── GravatarOAuth.md │ │ ├── GravatarUI.md │ │ ├── ProfileViews.md │ │ ├── QuickEditorArticle.md │ │ ├── Resources │ │ │ ├── ProfileExamples │ │ │ │ ├── largeProfileSummaryView.view@2x.png │ │ │ │ ├── largeProfileSummaryView.view~dark@2x.png │ │ │ │ ├── largeProfileView.view@2x.png │ │ │ │ ├── largeProfileView.view~dark@2x.png │ │ │ │ ├── profileSummaryView.view@2x.png │ │ │ │ ├── profileSummaryView.view~dark@2x.png │ │ │ │ ├── profileView.view@2x.png │ │ │ │ └── profileView.view~dark@2x.png │ │ │ ├── QEExamples │ │ │ │ ├── about-editor-intrinsic@2x.png │ │ │ │ ├── about-editor-intrinsic~dark@2x.png │ │ │ │ ├── about-editor-medium@2x.png │ │ │ │ ├── about-editor-medium~dark@2x.png │ │ │ │ ├── about-editor@2x.png │ │ │ │ ├── about-editor~dark@2x.png │ │ │ │ ├── avatar-and-about@2x.gif │ │ │ │ ├── avatar-and-about~dark@2x.gif │ │ │ │ ├── horizontal-intrinsic-height.png │ │ │ │ ├── vertical-large.png │ │ │ │ └── vertical-medium-expandable.png │ │ │ ├── QuickEditor_Tutorial │ │ │ │ ├── QuickEditor_Tutorial_01_03@2x.png │ │ │ │ ├── QuickEditor_Tutorial_01_04@2x.png │ │ │ │ ├── QuickEditor_Tutorial_01_05@2x.png │ │ │ │ ├── QuickEditor_Tutorial_Header@2x.png │ │ │ │ └── QuickEditor_Tutorial_Preview@2x.png │ │ │ ├── gravatar-sdk@2x.png │ │ │ ├── tutorial_01_01@2x.png │ │ │ ├── tutorial_01_02@2x.png │ │ │ └── tutorial_01_header@2x.png │ │ ├── Tutorials │ │ │ ├── ContactsList │ │ │ │ ├── ContactsList.tutorial │ │ │ │ ├── Tut_01.swift │ │ │ │ ├── Tut_02.swift │ │ │ │ ├── Tut_03.swift │ │ │ │ ├── Tut_04.swift │ │ │ │ ├── Tut_05.swift │ │ │ │ ├── Tut_06.swift │ │ │ │ └── Tut_06_comparison.swift │ │ │ ├── QuickEditor │ │ │ │ ├── QE_Tut_01.swift │ │ │ │ ├── QE_Tut_02.swift │ │ │ │ ├── QE_Tut_03.swift │ │ │ │ ├── QE_Tut_04.swift │ │ │ │ ├── QE_Tut_05.swift │ │ │ │ └── QuickEditor.tutorial │ │ │ └── Tutorials.tutorial │ │ └── UIImageViewExtension.md │ ├── ImageCropper │ │ ├── CropFrameOverlayView.swift │ │ └── ImageCropperViewController.swift │ ├── Options │ │ ├── ActivityIndicatorType.swift │ │ └── ImageSettingOption.swift │ ├── ProfileFields │ │ ├── AboutMeBuilder.swift │ │ ├── AccountButtonBuilder.swift │ │ ├── DisplayNameBuilder.swift │ │ ├── Model │ │ │ ├── AboutMeModel.swift │ │ │ ├── AccountModel.swift │ │ │ ├── AvatarIdentifierProvider.swift │ │ │ ├── ClaimProfileModel.swift │ │ │ ├── DisplayNameModel.swift │ │ │ ├── PersonalInfoModel.swift │ │ │ ├── ProfileMetadataModel.swift │ │ │ └── ProfileModel.swift │ │ ├── PersonalInfoField.swift │ │ └── ProfileButtonBuilder.swift │ ├── ProfileView │ │ ├── AccountIconWebView │ │ │ └── RemoteSVGView.swift │ │ ├── ActivityIndicator │ │ │ ├── ActivityIndicator.swift │ │ │ └── ProfileActivityIndicator.swift │ │ ├── Avatar │ │ │ ├── AvatarType.swift │ │ │ └── DefaultAvatarProvider.swift │ │ ├── BaseProfileView.swift │ │ ├── DashedView │ │ │ └── DashedLabel.swift │ │ ├── LargeProfileSummaryView.swift │ │ ├── LargeProfileView.swift │ │ ├── Placeholder │ │ │ ├── PlaceholderDisplayers │ │ │ │ ├── AccountButtonsPlaceholderDisplayer.swift │ │ │ │ ├── BackgroundColorPlaceholderDisplayer.swift │ │ │ │ ├── LabelPlaceholderDisplayer.swift │ │ │ │ ├── ProfileButtonPlaceholderDisplayer.swift │ │ │ │ └── RectangularPlaceholderDisplayer.swift │ │ │ ├── PlaceholderDisplaying.swift │ │ │ ├── ProfileViewPlaceholderDisplaying.swift │ │ │ └── UIView+Placeholder.swift │ │ ├── ProfileSummaryView.swift │ │ ├── ProfileView.swift │ │ └── ProfileViewConfiguration.swift │ ├── ProfileViewController │ │ ├── ProfileViewController.swift │ │ └── ProfileViewModel.swift │ ├── QuickEditor │ │ ├── AboutInfoField.swift │ │ ├── QuickEditorConfiguration.swift │ │ ├── QuickEditorViewController.swift │ │ ├── SheetPresentationStyle.swift │ │ └── UIKitSheetPresentationOnSwiftUI.swift │ ├── Resources │ │ ├── AccountIcons.xcassets │ │ │ ├── Contents.json │ │ │ ├── calendly.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── calendly.pdf │ │ │ ├── fediverse.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── fediverse.pdf │ │ │ ├── foursquare.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── foursquare.pdf │ │ │ ├── github.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── github.pdf │ │ │ ├── gravatar.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── gravatar.pdf │ │ │ ├── instagram.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── instagram.pdf │ │ │ ├── mastodongeneric.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── mastodongeneric.pdf │ │ │ ├── stackoverflow.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── stackoverflow.pdf │ │ │ ├── tiktok.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── tiktok.pdf │ │ │ ├── tripit.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── tripit.pdf │ │ │ ├── tumblr.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── tumblr.pdf │ │ │ ├── twitch.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── twitch.pdf │ │ │ ├── twitter-alt.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── twitter-alt.pdf │ │ │ ├── vimeo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── vimeo.pdf │ │ │ ├── wordpress.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── wordpress.pdf │ │ │ └── wp-link.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── LinkIcon.svg │ │ ├── Media.xcassets │ │ │ ├── Contents.json │ │ │ ├── empty-profile-avatar.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Regular-S.svg │ │ │ ├── more-horizontal.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── more-horizontal.svg │ │ │ ├── pencil.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pencil.pdf │ │ │ ├── qe-intro-empty-profile-avatar.imageset │ │ │ │ ├── Avatar 1.svg │ │ │ │ ├── Avatar.svg │ │ │ │ └── Contents.json │ │ │ └── setup-avatar-emoji.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── emoji-smile.svg │ │ ├── ar.lproj │ │ │ └── Localizable.strings │ │ ├── de.lproj │ │ │ └── Localizable.strings │ │ ├── en.lproj │ │ │ └── Localizable.strings │ │ ├── es.lproj │ │ │ └── Localizable.strings │ │ ├── fr.lproj │ │ │ └── Localizable.strings │ │ ├── he.lproj │ │ │ └── Localizable.strings │ │ ├── id.lproj │ │ │ └── Localizable.strings │ │ ├── it.lproj │ │ │ └── Localizable.strings │ │ ├── ja.lproj │ │ │ └── Localizable.strings │ │ ├── ko.lproj │ │ │ └── Localizable.strings │ │ ├── nl.lproj │ │ │ └── Localizable.strings │ │ ├── pt-BR.lproj │ │ │ └── Localizable.strings │ │ ├── ru.lproj │ │ │ └── Localizable.strings │ │ ├── sv.lproj │ │ │ └── Localizable.strings │ │ ├── tr.lproj │ │ │ └── Localizable.strings │ │ ├── zh-Hans.lproj │ │ │ └── Localizable.strings │ │ └── zh-Hant.lproj │ │ │ └── Localizable.strings │ ├── SwiftUI │ │ ├── AboutEditor │ │ │ ├── AboutEditorView.swift │ │ │ └── AboutInfoModel.swift │ │ ├── AvatarPicker │ │ │ ├── AvatarAction.swift │ │ │ ├── AvatarGridModel.swift │ │ │ ├── AvatarImageModel.swift │ │ │ ├── AvatarPickerContentLayout.swift │ │ │ ├── AvatarPickerProfileView.swift │ │ │ ├── AvatarPickerProfileViewWrapper.swift │ │ │ ├── AvatarPickerView.swift │ │ │ ├── AvatarPickerViewModel.swift │ │ │ ├── AvatarShareItem.swift │ │ │ ├── ImageEditorView.swift │ │ │ ├── ProfileViewPlaceholderColorManager.swift │ │ │ ├── SystemImagePicker │ │ │ │ ├── CameraImagePicker.swift │ │ │ │ ├── CustomizableImageEditor.swift │ │ │ │ ├── ImagePlaygroundModifier.swift │ │ │ │ ├── PhotosImagePicker.swift │ │ │ │ └── SystemImagePickerView.swift │ │ │ └── Views │ │ │ │ ├── AltTextEditorView.swift │ │ │ │ ├── AvatarGrid.swift │ │ │ │ ├── AvatarPickerAvatarView.swift │ │ │ │ ├── CTAButtonView.swift │ │ │ │ ├── ContentLoadingErrorView.swift │ │ │ │ ├── DimmingActivityIndicator.swift │ │ │ │ ├── DimmingButton.swift │ │ │ │ ├── EmailText.swift │ │ │ │ ├── HorizontalAvatarGrid.swift │ │ │ │ ├── PlusButtonView.swift │ │ │ │ └── ShareSheet.swift │ │ ├── AvatarView.swift │ │ ├── CachedAsyncImage.swift │ │ ├── DismissDetectingModifier.swift │ │ ├── GravatarNavigationModifier.swift │ │ ├── ImageCropper.swift │ │ ├── IntrinsicHeightView.swift │ │ ├── LoadingIndicatorView.swift │ │ ├── ModalPresentationModifier.swift │ │ ├── OAuthSession │ │ │ ├── AccessToken.swift │ │ │ ├── Keychain.swift │ │ │ ├── KeychainToken.swift │ │ │ ├── OAuthSession.swift │ │ │ ├── OldAuthenticationSession+Environment.swift │ │ │ ├── OldAuthenticationSession.swift │ │ │ └── ScopeLoadingErrorView.swift │ │ ├── PreferenceKeys.swift │ │ ├── ProfileEditor │ │ │ ├── QuickEditor.swift │ │ │ ├── QuickEditorNoticeView.swift │ │ │ ├── QuickEditorScopeOption.swift │ │ │ └── QuickEditorUpdate.swift │ │ ├── ProfileView │ │ │ └── ProfileViewRepresentable.swift │ │ ├── QEDetent.swift │ │ ├── QuickEditorModalPresentationModifier.swift │ │ ├── SafariView.swift │ │ ├── Toast │ │ │ ├── Toast.swift │ │ │ ├── ToastContainerView.swift │ │ │ └── ToastManager.swift │ │ └── View+Additions.swift │ └── Utility │ │ └── SDKLocalizedString.swift └── TestHelpers │ ├── Bundle+ResourceBundle.swift │ ├── HTTPURLResponse+Extension.swift │ ├── ImageDownloadService+Extension.swift │ ├── ImageHelper.swift │ ├── Resources │ ├── Profiles │ │ └── fullProfile.json │ ├── avatarSelected.json │ ├── avatarSetAltTextResponse.json │ ├── avatarSetRatingResponse.json │ ├── avatarUploadResponse.json │ ├── avatarsResponse.json │ ├── example_avatar.png │ ├── placeholder.png │ └── test.png │ ├── TestImageCache.swift │ ├── TestImageProcessor.swift │ ├── TestURLSession.swift │ ├── TestURLSessionError.swift │ └── URLSessionMock.swift ├── Tests ├── GravatarTests │ ├── AvatarIdentifierTests.swift │ ├── AvatarServiceTests.swift │ ├── AvatarURLTests.swift │ ├── EmailTests.swift │ ├── GravatarImageCacheTests.swift │ ├── HashIDTests.swift │ ├── ImageDownloadServiceTests.swift │ ├── ImageSizeTests.swift │ ├── ImageSquaringTests.swift │ ├── ProfileIdentifierTests.swift │ ├── ProfileServiceTests.swift │ ├── ProfileURLTests.swift │ ├── RenameMeTests.swift │ ├── Resources │ │ ├── placeholder.png │ │ └── test.png │ ├── UIImage+AdditionsTests.swift │ ├── URLComponentsTests.swift │ └── URLSessionHTTPClientTests.swift └── GravatarUITests │ ├── AboutEditorViewTests.swift │ ├── AboutInfoFieldTests.swift │ ├── AboutInfoModelTests.swift │ ├── AboutMeBuilderTests.swift │ ├── AccessTokenTests.swift │ ├── AvatarGridModelTests.swift │ ├── AvatarImageModelTests.swift │ ├── AvatarPickerProfileViewTests.swift │ ├── AvatarPickerViewModelTests.swift │ ├── Bundle+ResourceBundle.swift │ ├── CropFrameOverlayViewTests.swift │ ├── DisplayNameBuilderTests.swift │ ├── GravatarOptionsTests.swift │ ├── GravatarWrapper+UIImageViewTests.swift │ ├── KeychainTokenTests.swift │ ├── LargeProfileSummaryViewTests.swift │ ├── LargeProfileViewTests.swift │ ├── OAuthSessionTests.swift │ ├── PersonalInfoBuilderTests.swift │ ├── ProfileButtonTests.swift │ ├── ProfileButtonsActionsTests.swift │ ├── ProfileConfigurationTests.swift │ ├── ProfileSummaryViewTests.swift │ ├── ProfileViewModelTests.swift │ ├── ProfileViewTests.swift │ ├── ProvileViewSnapshots.swift │ ├── QuickEditorNoticeViewTests.swift │ ├── Resources │ ├── example_avatar.png │ ├── placeholder.png │ └── test.png │ ├── Snapshotting+Additions.swift │ ├── TestActivityIndicator.swift │ ├── TestAvatar.swift │ ├── TestImageFetcher.swift │ ├── TestPalette.swift │ ├── TestPlaceholderDisplayers.swift │ ├── UIImageAdditionsTests.swift │ └── __Snapshots__ │ ├── AboutEditorViewTests │ ├── testAboutEditorViewAllFieldsFixedHeight.1.png │ ├── testAboutEditorViewAllFieldsFixedHeight.2.png │ ├── testAboutEditorViewAllFieldsIntrinsicHeight.1.png │ ├── testAboutEditorViewAllFieldsIntrinsicHeight.2.png │ ├── testAboutEditorViewExtraFieldsIntrinsicHeight.1.png │ ├── testAboutEditorViewExtraFieldsIntrinsicHeight.2.png │ ├── testAboutEditorViewOneFieldFixedHeight.1.png │ ├── testAboutEditorViewOneFieldFixedHeight.2.png │ ├── testAboutEditorViewPersonalFieldsIntrinsicHeight.1.png │ ├── testAboutEditorViewPersonalFieldsIntrinsicHeight.2.png │ ├── testAboutEditorViewProfessionalFieldsIntrinsicHeight.1.png │ ├── testAboutEditorViewProfessionalFieldsIntrinsicHeight.2.png │ ├── testAboutEditorWithUnsavedChanges.1.png │ ├── testAboutEditorWithUnsavedChanges.2.png │ ├── testAuthErrorStateAfterSave.1.png │ ├── testAuthErrorStateAfterSave.2.png │ ├── testLoadingErrorState.1.png │ ├── testLoadingErrorState.2.png │ ├── testLoadingState.1.png │ └── testLoadingState.2.png │ ├── AboutMeBuilderTests │ ├── testAboutMe.testAboutMe-Dark.png │ ├── testAboutMe.testAboutMe-Light.png │ └── testAboutMeWithSmallWidth.1.png │ ├── AvatarPickerProfileViewTests │ ├── testAvatarPickerProfileView.1.png │ ├── testAvatarPickerProfileView.2.png │ ├── testAvatarPickerProfileViewEmpty.1.png │ ├── testAvatarPickerProfileViewEmpty.2.png │ ├── testAvatarPickerProfileViewWithoutLocation.1.png │ └── testAvatarPickerProfileViewWithoutLocation.2.png │ ├── CropFrameOverlayViewTests │ └── testCropFrameOverlay.1.png │ ├── DisplayNameBuilderTests │ ├── testDisplayNameField.testDisplayNameField-Dark.png │ ├── testDisplayNameField.testDisplayNameField-Light.png │ ├── testDisplayNameFieldWithMissingDisplayName.testDisplayNameFieldWithMissingDisplayName-Dark.png │ ├── testDisplayNameFieldWithMissingDisplayName.testDisplayNameFieldWithMissingDisplayName-Light.png │ ├── testDisplayNameFieldWithMissingNames.testDisplayNameFieldWithMissingNames-Dark.png │ ├── testDisplayNameFieldWithMissingNames.testDisplayNameFieldWithMissingNames-Light.png │ └── testDisplayNameFieldWithSmallWidth.1.png │ ├── LargeProfileSummaryViewTests │ ├── testInitiallyEmptyLargeProfileSummaryView.dark.png │ ├── testInitiallyEmptyLargeProfileSummaryView.light.png │ ├── testLargeProfileSummaryCustomAvatarViewImageViewSubview.1.png │ ├── testLargeProfileSummaryView.dark.png │ ├── testLargeProfileSummaryView.light.png │ ├── testLargeProfileSummaryViewCustomAvatarImageViewSubviewCustomStyle.1.png │ ├── testLargeProfileSummaryViewCustomAvatarImageViewWrapper.1.png │ ├── testLargeProfileSummaryViewCustomAvatarView.1.png │ ├── testLargeProfileSummaryViewEmptyState.dark.png │ ├── testLargeProfileSummaryViewEmptyState.light.png │ ├── testLargeProfileSummaryViewEmptyStateCustomPalette.1.png │ ├── testLargeProfileSummaryViewLoadingStateClears.light.png │ ├── testLargeProfileSummaryViewLoadingStateClearsWhenDataIsPresent.light.png │ ├── testLargeProfileSummaryViewLoadingStateClearsWhenEmpty.light.png │ ├── testLargeProfileSummaryViewPlaceholderCanUpdateColors.light.png │ ├── testLargeProfileSummaryViewPlaceholdersCanHide.light.png │ ├── testLargeProfileSummaryViewPlaceholdersCanHideCustomPalette.1.png │ ├── testLargeProfileSummaryViewPlaceholdersCanShow.light.png │ └── testLargeProfileSummaryViewPlaceholdersCanShowCustomPalette.1.png │ ├── LargeProfileViewTests │ ├── testInitiallyEmptyLargeProfileView.Dark.png │ ├── testInitiallyEmptyLargeProfileView.Light.png │ ├── testLargeProfileCustomAvatarImageViewWrapper.1.png │ ├── testLargeProfileCustomAvatarView.1.png │ ├── testLargeProfileCustomAvatarViewImageViewSubview.1.png │ ├── testLargeProfileView.testLargeProfileView-Dark.png │ ├── testLargeProfileView.testLargeProfileView-Light.png │ ├── testLargeProfileViewCustomAvatarImageViewSubviewCustomStyle.1.png │ ├── testLargeProfileViewEmptyState.testLargeProfileView-Dark.png │ ├── testLargeProfileViewEmptyState.testLargeProfileView-Light.png │ ├── testLargeProfileViewEmptyStateCustomPalette.1.png │ ├── testLargeProfileViewLoadingStateClearsWhenDataIsPresent.light.png │ ├── testLargeProfileViewLoadingStateClearsWhenEmpty.light.png │ ├── testLargeProfileViewPlaceholderCanUpdateColors.light.png │ ├── testLargeProfileViewPlaceholdersCanHide.light.png │ ├── testLargeProfileViewPlaceholdersCanHideCustomPalette.1.png │ ├── testLargeProfileViewPlaceholdersCanShow.light.png │ └── testLargeProfileViewPlaceholdersCanShowCustomPalette.1.png │ ├── PersonalInfoBuilderTests │ ├── testPersonalInfoCustom.testPersonalInfoFull-Dark.png │ ├── testPersonalInfoCustom.testPersonalInfoFull-Light.png │ ├── testPersonalInfoFull.testPersonalInfoFull-Dark.png │ ├── testPersonalInfoFull.testPersonalInfoFull-Light.png │ └── testPersonalInfoWithSmallWidth.1.png │ ├── ProfileButtonTests │ ├── testProfileButtonSnapshots.edit.png │ └── testProfileButtonSnapshots.view.png │ ├── ProfileConfigurationTests │ └── testCustomAvatarStyle.1.png │ ├── ProfileSummaryViewTests │ ├── testInitiallyEmptyProfileSummaryView.dark.png │ ├── testInitiallyEmptyProfileSummaryView.light.png │ ├── testProfileSummaryView.dark.png │ ├── testProfileSummaryView.light.png │ ├── testProfileSummaryViewCustomAvatarImageViewSubviewCustomStyle.1.png │ ├── testProfileSummaryViewCustomAvatarImageViewWrapper.1.png │ ├── testProfileSummaryViewCustomAvatarView.1.png │ ├── testProfileSummaryViewCustomAvatarViewImageViewSubview.1.png │ ├── testProfileSummaryViewEmptyState.dark.png │ ├── testProfileSummaryViewEmptyState.light.png │ ├── testProfileSummaryViewEmptyStateCustomPalette.1.png │ ├── testProfileSummaryViewPlaceholdersCanHide.light.png │ ├── testProfileSummaryViewPlaceholdersCanHideCustomPalette.1.png │ ├── testProfileSummaryViewPlaceholdersCanShow.light.png │ ├── testProfileSummaryViewPlaceholdersCanShowCustomPalette.1.png │ ├── testProfileViewSummaryLoadingStateClearsWhenDataIsPresent.light.png │ ├── testProfileViewSummaryLoadingStateClearsWhenEmpty.light.png │ └── testProfileViewSummaryPlaceholderCanUpdateColors.light.png │ ├── ProfileViewTests │ ├── testInitiallyEmptyProfileView.dark.png │ ├── testInitiallyEmptyProfileView.light.png │ ├── testProfileView.dark.png │ ├── testProfileView.light.png │ ├── testProfileViewCustomAvatarImageViewSubviewCustomStyle.1.png │ ├── testProfileViewCustomAvatarImageViewWrapper.1.png │ ├── testProfileViewCustomAvatarView.1.png │ ├── testProfileViewCustomAvatarViewImageViewSubview.1.png │ ├── testProfileViewEmptyState.dark.png │ ├── testProfileViewEmptyState.light.png │ ├── testProfileViewEmptyStateCustomPalette.1.png │ ├── testProfileViewLoadingStateClearsWhenDataIsPresent.light.png │ ├── testProfileViewLoadingStateClearsWhenEmpty.light.png │ ├── testProfileViewPlaceholderCanUpdateColors.light.png │ ├── testProfileViewPlaceholdersCanHide.light.png │ ├── testProfileViewPlaceholdersCanHideCustomPalette.1.png │ ├── testProfileViewPlaceholdersCanShow.light.png │ └── testProfileViewPlaceholdersCanShowCustomPalette.1.png │ ├── ProvileViewSnapshots │ ├── largeProfileSummaryView.view-dark.png │ ├── largeProfileSummaryView.view.png │ ├── largeProfileView.view-dark.png │ ├── largeProfileView.view.png │ ├── profileSummaryView.view-dark.png │ ├── profileSummaryView.view.png │ ├── profileView.view-dark.png │ └── profileView.view.png │ ├── QuickEditorNoticeViewTests │ ├── testQuickEditorNoticeView.1.png │ ├── testQuickEditorNoticeView.2.png │ ├── testQuickEditorNoticeViewWithError.1.png │ ├── testQuickEditorNoticeViewWithError.2.png │ ├── testQuickEditorNoticeViewWithoutToken.1.png │ └── testQuickEditorNoticeViewWithoutToken.2.png │ └── TestPlaceholderDisplayers │ ├── testAccountButtonsPlaceholderDisplayer.placeholder-shown.png │ ├── testBackgroundColorPlaceholderDisplayer.placeholder-hidden.png │ ├── testBackgroundColorPlaceholderDisplayer.placeholder-shown.png │ ├── testProfileButtonPlaceholderDisplayer.placeholder-hidden.png │ ├── testProfileButtonPlaceholderDisplayer.placeholder-shown.png │ ├── testRectangularColorPlaceholderDisplayer.placeholder-hidden.png │ └── testRectangularColorPlaceholderDisplayer.placeholder-shown.png ├── access-control-modifier.swift ├── fastlane ├── Fastfile ├── example.env ├── lanes │ └── localization.rb └── lib │ ├── code_signing_helpers.rb │ ├── env_manager.rb │ └── localizable_source.rb ├── openapi ├── GravatarOpenAPIClient │ ├── .openapi-generator-ignore │ └── .openapi-generator │ │ ├── FILES │ │ └── VERSION ├── openapi.yaml └── templates │ ├── model.mustache │ ├── modelInlineEnumDeclaration.mustache │ └── modelObject.mustache └── version.rb /.buildkite/commands/publish-pod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [ $# -ne 1 ]; then 4 | echo "Error: CocoaPods publishing failed. Specify a path to a podspec." 5 | exit 1 6 | fi 7 | 8 | SLACK_WEBHOOK=$PODS_SLACK_WEBHOOK 9 | PODSPEC_PATH="$1" 10 | 11 | echo "--- :rubygems: Setting up Gems" 12 | install_gems 13 | 14 | echo "--- :cocoapods: Publishing $PODSPEC_PATH to CocoaPods CDN" 15 | publish_pod --allow-warnings --synchronous "$PODSPEC_PATH" 16 | 17 | echo "--- :slack: Notifying Slack" 18 | slack_notify_pod_published "$PODSPEC_PATH" "$SLACK_WEBHOOK" 19 | -------------------------------------------------------------------------------- /.buildkite/commands/upload-demo-to-firebase.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | echo "--- :arrow_down: Downloading Prototype Build" 4 | buildkite-agent artifact download ".build/artifacts/*.ipa" . --step "build_demo" 5 | buildkite-agent artifact download ".build/artifacts/*.app.dSYM.zip" . --step "build_demo" 6 | 7 | echo "--- :rubygems: Setting up Gems" 8 | install_gems 9 | 10 | echo "--- :hammer_and_wrench: Uploading" 11 | bundle exec fastlane ios upload_demo_to_firebase 12 | -------------------------------------------------------------------------------- /.buildkite/commands/validate-pods.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | echo "--- :cocoapods: Validate Gravatar.podspec" 4 | validate_podspec --allow-warnings Gravatar.podspec 5 | 6 | echo "--- :cocoapods: Validate GravatarUI.podspec" 7 | validate_podspec --allow-warnings GravatarUI.podspec 8 | -------------------------------------------------------------------------------- /.buildkite/shared-pipeline-vars: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file is `source`'d before calling `buildkite-agent pipeline upload`, and can be used 4 | # to set up some variables that will be interpolated in the `.yml` pipeline before uploading it. 5 | 6 | export IMAGE_ID="xcode-16.2-macos-14.7.1-v1" 7 | 8 | export CI_TOOLKIT="automattic/a8c-ci-toolkit#3.9.1" 9 | 10 | export SWIFTFORMAT_VERSION=$( awk '/^--minversion/ { print $2 }' .swiftformat ) 11 | -------------------------------------------------------------------------------- /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | -------------------------------------------------------------------------------- /.configure: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Gravatar-SDK-iOS", 3 | "branch": "trunk", 4 | "pinned_hash": "ac80c99c7823e68dfdfa973237a0dbde652fa7b7", 5 | "files_to_copy": [ 6 | { 7 | "file": "iOS/GravatarSDK/Secrets.swift", 8 | "destination": "Demo/Demo/Secrets.swift", 9 | "encrypt": true 10 | } 11 | ], 12 | "file_dependencies": [ 13 | 14 | ] 15 | } -------------------------------------------------------------------------------- /.configure-files/Secrets.swift.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/.configure-files/Secrets.swift.enc -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug report" 3 | about: Report a bug if something isn't working as expected in Gravatar iOS SDK. 4 | title: '' 5 | labels: 6 | - 'Bug' 7 | - '[Priority] Medium' 8 | assignees: '' 9 | 10 | --- 11 | 12 | **Describe the bug** 13 | 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. 18 | 19 | **Screenshots** 20 | 21 | 22 | **Expected behavior** 23 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "✨ New Enhancement" 3 | about: Add an idea to improve an existing feature. 4 | title: '' 5 | labels: 'Enhance' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please add details of the enhancement. 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes # 2 | 3 | ### Description 4 | 5 | ### Testing Steps 6 | -------------------------------------------------------------------------------- /.github/workflows/run-danger.yml: -------------------------------------------------------------------------------- 1 | name: ☢️ Trigger Danger On Buildkite 2 | 3 | on: 4 | pull_request: 5 | types: [labeled, unlabeled, milestoned, demilestoned, ready_for_review] 6 | 7 | jobs: 8 | dangermattic: 9 | if: ${{ (github.event.pull_request.draft == false) }} 10 | uses: Automattic/dangermattic/.github/workflows/reusable-retry-buildkite-step-on-events.yml@v1.1.2.1 11 | with: 12 | org-slug: automattic 13 | pipeline-slug: gravatar-sdk-ios 14 | retry-step-key: danger 15 | build-commit-sha: ${{ github.event.pull_request.head.sha }} 16 | secrets: 17 | buildkite-api-token: ${{ secrets.TRIGGER_BK_BUILD_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.dSYM* 21 | 22 | # Swift Package Manager 23 | .build 24 | .swiftpm 25 | 26 | # Bundler 27 | vendor/bundle 28 | 29 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 30 | # Carthage/Checkouts 31 | 32 | Carthage/Build 33 | 34 | # We recommend against adding the Pods directory to your .gitignore. However 35 | # you should judge for yourself, the pros and cons are mentioned at: 36 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 37 | # 38 | # Note: if you ignore the Pods directory, make sure to uncomment 39 | # `pod install` in .travis.yml 40 | # 41 | # Pods/ 42 | 43 | # Fastlane 44 | fastlane/README.md 45 | fastlane/report.xml 46 | fastlane/test_output 47 | 48 | # SwiftGen (part of wpmreleasetoolkit) 49 | vendor/swiftgen 50 | 51 | # Other 52 | openapi/GravatarOpenAPIClient/Sources/ 53 | Demo/Demo/Secrets.swift 54 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - DerivedData/**/* 4 | - vendor/**/* 5 | - openapi-generator/**/* 6 | NewCops: enable 7 | 8 | Naming/FileName: 9 | Exclude: 10 | - "*.podspec" 11 | 12 | Metrics/BlockLength: 13 | Exclude: &fastlane 14 | - fastlane/Fastfile 15 | - fastlane/**/*.rb 16 | 17 | Metrics/MethodLength: 18 | Max: 30 19 | Exclude: *fastlane 20 | 21 | Layout/LineLength: 22 | Max: 165 23 | Exclude: *fastlane 24 | 25 | Layout/EmptyLines: 26 | Exclude: *fastlane 27 | 28 | Style/AsciiComments: 29 | Exclude: *fastlane 30 | 31 | Style/HashSyntax: 32 | EnforcedShorthandSyntax: never 33 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2.2 2 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | # This is manifest file for the Swift Package Index for it to auto-generate and 2 | # host DocC documentation. 3 | # 4 | # For reference see https://swiftpackageindex.com/swiftpackageindex/spimanifest/documentation/spimanifest/commonusecases#Host-DocC-documentation-in-the-Swift-Package-Index 5 | 6 | version: 1 7 | builder: 8 | configs: 9 | - documentation_targets: [Gravatar, GravatarUI] 10 | platform: ios 11 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | swiftlint_version: 0.57.1 2 | only_rules: # Rules to run 3 | - custom_rules 4 | 5 | # If true, SwiftLint will treat all warnings as errors. 6 | strict: true 7 | 8 | included: 9 | - Sources 10 | - Tests 11 | 12 | custom_rules: 13 | no_ns_localized_string: 14 | included: 15 | - "Sources/.*\\.swift" 16 | name: "No NSLocalizedString" 17 | regex: "NSLocalizedString\\(" 18 | match_kinds: 19 | - identifier 20 | message: "Use `SDKLocalizedString()` instead of `NSLocalizedString()`." 21 | severity: error 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/Gravatar.xcworkspace -scheme Gravatar-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /CODINGSTYLE.md: -------------------------------------------------------------------------------- 1 | ## Coding Style 2 | 3 | Make sure to read [Apple's API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/). 4 | 5 | ### Tooling 6 | 7 | We use [SwiftFormat](https://github.com/apple/swift-format) to enforce a basic swift format style. 8 | 9 | CI will fail if it finds any format issue. 10 | 11 | You can run `SwiftFormat` locally with `make lint` to get warnings, or `make swiftformat` to implement the changes automatically. 12 | 13 | ### URL, ID, API and other technical acronyms and initialisms. 14 | 15 | - Always UPPERCASED when it's part of a Type name. 16 | - Use lowercase everytime it's a prefix, and is not a type name. 17 | - Use lowercase if it's writen alone, and it's not a type name. 18 | 19 | For example: 20 | 21 | ```swift 22 | struct URLType { 23 | let url: URL 24 | let someURL: URL 25 | 26 | init(_ url: URL) 27 | 28 | func api(with userID: String) -> API 29 | func api(id: ID) -> API 30 | } 31 | 32 | let urlSomething = URLType(url) 33 | let someURL = URLType(someURL) 34 | ``` 35 | -------------------------------------------------------------------------------- /Demo/Demo/Base.xcconfig: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_TEAM = PZYM8XX95Q 2 | 3 | // By default use the code sigining identity for development builds that Automatticians have access to. 4 | // This is better than using the generic "Apple Development" identity because it prevents Xcode selecting one of the other matching identities in the users' keychain. 5 | // It can happen that an Automattician has a personal development identity in the Automattic account, but that identity would not be included in the development provisioning profile, and the dev build would fail to code sign. 6 | CODE_SIGN_IDENTITY = Apple Development: Created via API (886NX39KP6) 7 | 8 | // Default the build number to 0 and delegate to build scripts for setting an appropriate number. 9 | CURRENT_PROJECT_VERSION = 0 10 | 11 | SWIFT_VERSION = 5.0 12 | SWIFT_STRICT_CONCURRENCY = complete 13 | -------------------------------------------------------------------------------- /Demo/Demo/Enterprise.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Base.xcconfig" 2 | 3 | DEVELOPMENT_TEAM = 99KV9Z6BKV 4 | 5 | CODE_SIGN_IDENTITY = Apple Distribution 6 | 7 | // Fastlane match uses a common pattern to name provisioning profiles, which allows us use this top-level, centralized definition. 8 | // Each target + build configuration combination will interpolate its bundle id value in here. 9 | // 10 | // Note: This setting works in Xcode and it seems like xcodebuild can also resolve it. 11 | // But once we add Fastlane's build_app (gym) into the pipeline, the resulting provisioning profile value is not interpolated. 12 | // As such, we have to re-define the value explicitly in each target. 13 | // 14 | // Leaving it here for reference in case the Fastlane will be resolved... 15 | PROVISIONING_PROFILE_SPECIFIER = match InHouse $PRODUCT_BUNDLE_IDENTIFIER 16 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Assets.xcassets/AppIcon.appiconset/1024x10244.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Demo/Demo/Gravatar-Demo/Assets.xcassets/AppIcon.appiconset/1024x10244.png -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "1024x10244.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Assets.xcassets/placeholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "placeholder.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Assets.xcassets/placeholder.imageset/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Demo/Demo/Gravatar-Demo/Assets.xcassets/placeholder.imageset/placeholder.png -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Common/FormFields/LabelField.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class LabelField: FormField, @unchecked Sendable { 4 | var title: String? 5 | var subtitle: String? 6 | private let cellID = "LabelCell" 7 | 8 | init(title: String? = nil, subtitle: String? = nil) { 9 | self.title = title 10 | self.subtitle = subtitle 11 | } 12 | 13 | @MainActor 14 | override func dequeueCell(in tableView: UITableView, for indexPath: IndexPath) -> UITableViewCell { 15 | let cell = tableView.dequeueReusableCell(withIdentifier: cellID) ?? UITableViewCell(style: .subtitle, reuseIdentifier: cellID) 16 | 17 | var config = cell.defaultContentConfiguration() 18 | config.text = title 19 | config.secondaryText = subtitle 20 | cell.contentConfiguration = config 21 | 22 | return cell 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Gravatar-Demo.Base.xcconfig: -------------------------------------------------------------------------------- 1 | PRODUCT_BUNDLE_IDENTIFIER = com.automattic.gravatar-sdk-demo-uikit 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Gravatar-Demo.Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Gravatar-Demo.Base.xcconfig" 2 | #include "../Enterprise.xcconfig" 3 | 4 | // Declared explicitly rather than by building on top of what's defined in Base so it can be read by other tools such as Fastlane 5 | PRODUCT_BUNDLE_IDENTIFIER = com.automattic.gravatar-sdk-demo-uikit.prototype-build 6 | 7 | // Unfortunately, we need to explicitly write the bundle identifier. 8 | // See note in Enterprise.xcconfig about this. 9 | PROVISIONING_PROFILE_SPECIFIER = match InHouse com.automattic.gravatar-sdk-demo-uikit.prototype-build 10 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/Controls/AvatarPickerLayoutOptions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import GravatarUI 3 | 4 | enum AvatarPickerLayoutOptions: String, Identifiable, CaseIterable { 5 | var id: String { rawValue } 6 | 7 | case verticalLarge = "vertical - large" 8 | case verticalExpandable = "vertical - expandable" 9 | case verticalExpandablePrioritizeScrolling = "vertical - expandable - prioritize scrolling" 10 | case horizontal = "horizontal" 11 | 12 | var contentLayout: AvatarPickerContentLayout { 13 | switch self { 14 | case .verticalLarge: 15 | .vertical(presentationStyle: .large) 16 | case .verticalExpandable: 17 | .vertical(presentationStyle: .expandableMedium()) 18 | case .verticalExpandablePrioritizeScrolling: 19 | .vertical(presentationStyle: .expandableMedium(prioritizeScrollOverResize: true)) 20 | case .horizontal: 21 | .horizontal() 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/Controls/QEContentLayoutPickerRow.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import GravatarUI 3 | 4 | struct QEContentLayoutPickerRow: View { 5 | @Binding var contentLayoutOptions: AvatarPickerLayoutOptions 6 | 7 | var body: some View { 8 | HStack { 9 | Text("Content Layout") 10 | Spacer() 11 | Picker("Content Layout", selection: $contentLayoutOptions) { 12 | ForEach(AvatarPickerLayoutOptions.allCases) { option in 13 | Text(option.rawValue).tag(option) 14 | } 15 | } 16 | .pickerStyle(MenuPickerStyle()) 17 | } 18 | } 19 | } 20 | 21 | #Preview { 22 | QEContentLayoutPickerRow(contentLayoutOptions: .constant(.verticalExpandable)) 23 | } 24 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/Controls/QEInitialPagePickerRow.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import GravatarUI 3 | 4 | struct QEInitialPagePickerRow: View { 5 | @Binding var initialPage: InitialPage 6 | 7 | var body: some View { 8 | HStack { 9 | Text("Initial Page") 10 | Spacer() 11 | Picker("Initial Page", selection: $initialPage) { 12 | ForEach(InitialPage.allCases) { option in 13 | Text(option.rawValue).tag(option) 14 | } 15 | } 16 | .pickerStyle(MenuPickerStyle()) 17 | } 18 | } 19 | } 20 | 21 | enum InitialPage: String, CaseIterable, Identifiable { 22 | var id: String { 23 | self.rawValue 24 | } 25 | 26 | case avatarPicker = "Avatar Picker" 27 | case aboutEditor = "About Editor" 28 | 29 | func map() -> AvatarPickerAndAboutEditorConfiguration.Page { 30 | switch self { 31 | case .aboutEditor: .aboutEditor 32 | case .avatarPicker: .avatarPicker 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/Controls/QEScopesPickerRow.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct QEScopesPickerRow: View { 4 | @Binding var scope: QEScope 5 | 6 | var body: some View { 7 | HStack { 8 | Text("Scope") 9 | Spacer() 10 | Picker("Scope", selection: $scope) { 11 | ForEach(QEScope.allCases, id: \.rawValue) { option in 12 | Text(option.rawValue).tag(option) 13 | } 14 | } 15 | .pickerStyle(MenuPickerStyle()) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/ProfileViewTypePickerRow.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import GravatarUI 3 | 4 | struct ProfileTypePickerRow: View { 5 | enum Options: String, CaseIterable, Identifiable { 6 | var id: String { 7 | rawValue 8 | } 9 | 10 | case standard 11 | case large 12 | case summary 13 | case largeSummary 14 | } 15 | 16 | @Binding var options: Options 17 | 18 | var body: some View { 19 | HStack { 20 | Text("Style") 21 | Spacer() 22 | Picker("Style", selection: $options) { 23 | ForEach(Options.allCases) { option in 24 | Text(option.rawValue).tag(option) 25 | } 26 | } 27 | .pickerStyle(MenuPickerStyle()) 28 | .onChange(of: options) { _ in 29 | } 30 | } 31 | } 32 | } 33 | 34 | #Preview { 35 | ProfileTypePickerRow(options: .constant(.standard)) 36 | } 37 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/SafariView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import SafariServices 3 | 4 | struct SafariView: UIViewControllerRepresentable { 5 | let url: URL 6 | 7 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { 8 | SFSafariViewController(url: url) 9 | } 10 | 11 | func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { 12 | // No updates needed for SafariViewController 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/SwiftUI/View+Demo.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | extension View { 4 | /// Modify a view with a `ViewBuilder` closure. 5 | /// Allows us to decide which modifier to apply on runtime. 6 | func modifier(@ViewBuilder body: (Self) -> ModifiedContent) -> some View { 7 | body(self) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ar.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ar.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/de.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/de.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/es.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/es.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/fr.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/fr.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/he.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/he.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/id.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/id.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/it.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/it.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ja.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ja.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ko.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ko.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/nl.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/nl.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/pt-BR.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/pt-BR.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ru.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/ru.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/sv.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/sv.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/tr.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/tr.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/zh-Hans.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/zh-Hans.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/zh-Hant.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Gravatar-Demo/zh-Hant.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UINavigationItem"; title = "Root View Controller"; ObjectID = "wYS-DK-y74"; */ 3 | "wYS-DK-y74.title" = "Root View Controller"; 4 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/ar.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/de.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/he.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/id.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/it.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/ja.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/ko.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/nl.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/pt-BR.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/ru.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/sv.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/tr.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Localizations/zh-Hant.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/Demo/Secrets.tpl: -------------------------------------------------------------------------------- 1 | // Secrets used in the demo app. 2 | // Do not modify the .tpl file. 3 | // After a first build of any of the demo apps, a `Secrets.swift` file will be generated. 4 | // Use the generated file to paste the secrets needed from https://gravatar.com/developers/applications 5 | 6 | struct Secrets { 7 | static let apiKey: String? = nil 8 | static let clientID: String = "" 9 | static let redirectURI: String = "" 10 | } 11 | -------------------------------------------------------------------------------- /Demo/Gravatar Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.associated-domains 6 | 7 | webcredentials:gravatar.com 8 | applinks:gravatar.com 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Demo/Gravatar-Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Gravatar-Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Gravatar-Demo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "ef380bfd827500bb40ef65e62058cb22bbebbf217402a19c327d4201d142ba21", 3 | "pins" : [ 4 | { 5 | "identity" : "swift-snapshot-testing", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing", 8 | "state" : { 9 | "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", 10 | "version" : "1.17.6" 11 | } 12 | }, 13 | { 14 | "identity" : "swift-syntax", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/swiftlang/swift-syntax", 17 | "state" : { 18 | "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", 19 | "version" : "509.0.2" 20 | } 21 | } 22 | ], 23 | "version" : 3 24 | } 25 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'cocoapods', '~> 1.14.3' 6 | gem 'danger-dangermattic', '~> 1.2.2' 7 | gem 'fastlane', '~> 2.222' 8 | gem 'fastlane-plugin-firebase_app_distribution', '~> 0.10' 9 | gem 'fastlane-plugin-wpmreleasetoolkit', '~> 13.0' 10 | gem 'rubocop', '~> 1.65' 11 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "ef380bfd827500bb40ef65e62058cb22bbebbf217402a19c327d4201d142ba21", 3 | "pins" : [ 4 | { 5 | "identity" : "swift-snapshot-testing", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", 8 | "state" : { 9 | "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", 10 | "version" : "1.17.6" 11 | } 12 | }, 13 | { 14 | "identity" : "swift-syntax", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/swiftlang/swift-syntax", 17 | "state" : { 18 | "revision" : "303e5c5c36d6a558407d364878df131c3546fad8", 19 | "version" : "510.0.2" 20 | } 21 | } 22 | ], 23 | "version" : 3 24 | } 25 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | The Gravatar team takes security bugs in Gravatar seriously. We appreciate your efforts to responsibly disclose your findings. 6 | 7 | To report a security issue, please use [HackerOne](https://hackerone.com/automattic). 8 | 9 | Report security bugs in third-party modules to the person or team maintaining the module. 10 | -------------------------------------------------------------------------------- /Sources/Gravatar/Bundle+ResourceBundle.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if !SWIFT_PACKAGE 4 | private class BundleFinder: NSObject {} 5 | extension Bundle { 6 | static var module: Bundle { 7 | let defaultBundle = Bundle(for: BundleFinder.self) 8 | // If installed with CocoaPods, resources will be in Gravatar.bundle 9 | // The name of the bundle "Gravatar.bundle" (without the .bundle file extension) 10 | // needs to match the key in the respective Gravatar.podspec: 11 | // `s.resource_bundles = { 'Gravatar' => ['Sources/Gravatar/Resources/*.plist'] }` 12 | if let bundleURL = defaultBundle.resourceURL, 13 | let resourceBundle = Bundle(url: bundleURL.appendingPathComponent("Gravatar.bundle")) 14 | { 15 | return resourceBundle 16 | } 17 | // Otherwise, the default bundle is used for resources 18 | return defaultBundle 19 | } 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /Sources/Gravatar/BundleInfo.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | package enum BundleInfo { 4 | package static var sdkVersion: String? { 5 | getInfoValue(forKey: "CFBundleShortVersionString") as? String 6 | } 7 | 8 | package static var appName: String? { 9 | Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String 10 | } 11 | 12 | private static func getInfoValue(forKey key: String) -> Any? { 13 | // Access the SDKInfo.plist using Bundle.module 14 | guard let url = Bundle.module.url(forResource: "SDKInfo", withExtension: "plist"), 15 | let data = try? Data(contentsOf: url), 16 | let plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any] 17 | else { 18 | return nil 19 | } 20 | return plist[key] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Gravatar/Extensions/String.swift: -------------------------------------------------------------------------------- 1 | import CryptoKit 2 | import Foundation 3 | 4 | extension String { 5 | func hashed() -> String { 6 | self.sanitized.sha256() 7 | } 8 | 9 | private func sha256() -> String { 10 | let hashed = SHA256.hash(data: Data(self.utf8)) 11 | let hashString = hashed.compactMap { String(format: "%02x", $0) }.joined() 12 | return hashString 13 | } 14 | } 15 | 16 | extension String { 17 | var sanitized: String { 18 | self.lowercased() 19 | .trimmingCharacters(in: .whitespaces) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Gravatar/Extensions/UIImage+Additions.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIImage { 4 | /// Saves image into the temp directory as a jpeg file. 5 | /// - Returns: The URL of the file. 6 | package func saveToFile() throws -> URL? { 7 | guard let imageData = jpegData(compressionQuality: 1) else { return nil } 8 | let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("image.jpg") 9 | try imageData.write(to: fileURL) 10 | return fileURL 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Gravatar/Gravatar.docc/3. UploadingAvatar.md: -------------------------------------------------------------------------------- 1 | # Uploading an Avatar 2 | 3 | Let a user to update their avatar. 4 | 5 | You can provide a way to upload a new avatar for your users who have a Gravatar account. 6 | 7 | At the moment, Gravatar uses WordPress.com OAuth2 access token. Thus, you need a WordPress.com access token to update a user's avatar. Check out [the documentation](https://developer.wordpress.com/docs/oauth2/) to find more about how to get consent from the user and how to retrieve an access token. 8 | 9 | Use ``AvatarService`` to upload an avatar. 10 | 11 | ```swift 12 | import Gravatar 13 | 14 | // [...] 15 | 16 | let service = AvatarService() 17 | let image: UIImage = // image of choice from the user 18 | let accessToken = // WordPress.com OAuth2 access token 19 | do { 20 | try await service.upload(image, email: Email("email@domain.com"), accessToken: accessToken) 21 | } catch { 22 | // handle error 23 | } 24 | 25 | `` 26 | -------------------------------------------------------------------------------- /Sources/Gravatar/Gravatar.docc/Gravatar.md: -------------------------------------------------------------------------------- 1 | # ``Gravatar`` 2 | 3 | Gravatar iOS SDK 4 | 5 | @Metadata { 6 | @PageImage( 7 | purpose: icon, 8 | source: "gravatar-sdk" 9 | ) 10 | } 11 | 12 | ## Overview 13 | 14 | An “avatar” is an image that represents you online—a little picture that appears next to your name when you interact with websites. 15 | 16 | A Gravatar is a Globally Recognized Avatar. You upload an image and create your public profile just once, and then when you participate in any Gravatar-enabled site, your Gravatar image and public profile will automatically follow you there. 17 | 18 | This SDK will allow you to easily implement the Gravatar services in your project. 19 | -------------------------------------------------------------------------------- /Sources/Gravatar/Gravatar.docc/Resources/gravatar-sdk@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/Gravatar/Gravatar.docc/Resources/gravatar-sdk@2x.png -------------------------------------------------------------------------------- /Sources/Gravatar/Identifiers/Email.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Represents a Gravatar account email address 4 | public struct Email: Equatable, Sendable { 5 | let string: String 6 | 7 | var hashID: HashID { 8 | HashID(email: self) 9 | } 10 | 11 | /// Initializes a new Email object, representing a Gravatar account email address 12 | /// - Parameter string: The Gravatar account email address` 13 | public init(_ string: String) { 14 | self.string = string.sanitized 15 | } 16 | } 17 | 18 | extension Email: Identifiable { 19 | /// The string that the API expects when specifying a Gravatar type, such as an avatar or profile 20 | public var id: String { 21 | self.hashID.id 22 | } 23 | } 24 | 25 | extension Email: RawRepresentable { 26 | public init?(rawValue: String) { 27 | self.init(rawValue) 28 | } 29 | 30 | public var rawValue: String { 31 | string 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Gravatar/Identifiers/HashID.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A type that provides a hash that represents a gravatar profile 4 | public struct HashID: Equatable, Sendable { 5 | let string: String 6 | 7 | /// Initializes a new `HashID` object, reprensenting the gravatar hash of the Gravatar email address 8 | /// - Parameter string: The gravatar hash of the Gravatar email address 9 | public init(_ string: String) { 10 | self.string = string 11 | } 12 | 13 | /// Initializes a new `HashID` object, reprensenting the gravatar hash of the Gravatar email address 14 | /// - Parameter email: an `Email` containing the Gravatar email address 15 | public init(email: Email) { 16 | self.init(email.string.hashed()) 17 | } 18 | } 19 | 20 | extension HashID: Identifiable { 21 | /// The string that the API expects when specifying a Gravatar type, such as an avatar or profile 22 | public var id: String { 23 | string 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/APIErrorPayload.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Error payload for the REST API calls. 4 | public protocol APIErrorPayload: Sendable { 5 | /// A business error code that identifies this error. (This is not the HTTP status code.) 6 | var code: String? { get } 7 | /// Error message that comes from the REST API. 8 | var message: String? { get } 9 | } 10 | 11 | extension ModelError: APIErrorPayload { 12 | public var message: String? { 13 | error 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/AvatarType.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol AvatarType: Sendable { 4 | var url: String { get } 5 | var id: String { get } 6 | } 7 | 8 | extension Avatar: AvatarType { 9 | public var id: String { 10 | imageId 11 | } 12 | 13 | public var url: String { 14 | imageUrl 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Data+Extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Data { 4 | func decode( 5 | dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .iso8601, 6 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys 7 | ) throws -> T { 8 | let decoder = JSONDecoder() 9 | decoder.dateDecodingStrategy = dateDecodingStrategy 10 | decoder.keyDecodingStrategy = keyDecodingStrategy 11 | return try decoder.decode(T.self, from: self) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Error+Extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Error { 4 | func apiError() -> APIError { 5 | switch self { 6 | case let error as HTTPClientError: 7 | APIError.responseError(reason: error.map()) 8 | case let error as DecodingError: 9 | APIError.decodingError(error) 10 | case let error: 11 | APIError.responseError(reason: .unexpected(error)) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/HTTPStatus.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Some HTTP status codes we handle 4 | package enum HTTPStatus: Int { 5 | case badRequest = 400 6 | case unauthorized = 401 7 | case forbidden = 403 8 | case notFound = 404 9 | case payloadTooLarge = 413 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/HTTPURLResponse+Additions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension HTTPURLResponse { 4 | /// Whether the status code is an error of any kind (`4xx` or `5xx`) 5 | package var isError: Bool { 6 | isClientError || isServerError 7 | } 8 | 9 | /// Whether the status code is a client error code: `4xx` 10 | package var isClientError: Bool { 11 | statusCode >= 400 && statusCode < 500 12 | } 13 | 14 | /// Whether the status code is a client error code: `5xx` 15 | package var isServerError: Bool { 16 | statusCode >= 500 && statusCode < 600 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/ImageDownloadResult.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | /// Represents the result of a Gravatar image download task. 5 | public struct ImageDownloadResult: Sendable { 6 | public init(image: UIImage, sourceURL: URL) { 7 | self.image = image 8 | self.sourceURL = sourceURL 9 | } 10 | 11 | /// Gets the image of this result. 12 | public let image: UIImage 13 | 14 | /// The `URL` which this result is related to. 15 | public let sourceURL: URL 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/ImageDownloader.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | /// Represents a type which can be used by Gravatar to fetch images. 5 | public protocol ImageDownloader: Sendable { 6 | /// Fetches an image from the given `URL`, and delivers the image asynchronously. Throws `ImageFetchingError`. 7 | /// - Parameters: 8 | /// - url: The URL from where to download the image. 9 | /// - forceRefresh: Force the image to be downloaded, ignoring the cache. 10 | /// - processingMethod: Method to use for processing the downloaded `Data`. 11 | /// - Returns: An asynchronously-delivered Result type containing the image and its URL. 12 | func fetchImage( 13 | with url: URL, 14 | forceRefresh: Bool, 15 | processingMethod: ImageProcessingMethod 16 | ) async throws -> ImageDownloadResult 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/ImageUploader.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | typealias HTTPHeaderField = (name: String, value: String) 4 | 5 | /// Represents a type which can be used by Gravatar to upload an image to Gravatar. 6 | protocol ImageUploader: Sendable { 7 | /// Uploads an image to be used as the user's Gravatar profile image, and returns the `URLResponse` of the network tasks asynchronously. Throws 8 | /// `ImageUploadError`. 9 | /// - Parameters: 10 | /// - image: The image to be uploaded. 11 | /// - email: The user email account. 12 | /// - accessToken: The authentication token for the user. 13 | /// - avatarSelection: How to handle avatar selection after uploading a new avatar 14 | /// - additionalHTTPHeaders: Additional headers to add. 15 | /// - Returns: An asynchronously-delivered `URLResponse` instance, containing the response of the upload network task. 16 | @discardableResult 17 | func uploadImage( 18 | _ image: UIImage, 19 | accessToken: String, 20 | avatarSelection: AvatarSelection, 21 | additionalHTTPHeaders: [HTTPHeaderField]? 22 | ) async throws -> (data: Data, response: HTTPURLResponse) 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/Model/Avatar+AvatarDetails.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// To avoid name conflicts between `Rating` and `Avatar.Rating` 4 | package typealias ImageRating = Rating 5 | 6 | extension Avatar: AvatarDetails { 7 | package var imageID: ImageID { 8 | imageId 9 | } 10 | 11 | package var imageURL: String { 12 | imageUrl 13 | } 14 | 15 | package var imageRating: ImageRating { 16 | switch rating { 17 | case .g: 18 | ImageRating.general 19 | case .pg: 20 | ImageRating.parentalGuidance 21 | case .r: 22 | ImageRating.restricted 23 | case .x: 24 | ImageRating.x 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/Model/AvatarDetails.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public typealias ImageID = String 4 | 5 | public protocol AvatarDetails: Sendable { 6 | /// Unique identifier for the image. 7 | var imageID: ImageID { get } 8 | /// Image URL 9 | var imageURL: String { get } 10 | /// Rating associated with the image. 11 | var imageRating: Rating { get } 12 | /// Alternative text description of the image. 13 | var altText: String { get } 14 | /// Whether the image is currently selected as the provided selected email's avatar. 15 | var selected: Bool? { get } 16 | /// Date and time when the image was last updated. 17 | var updatedDate: Date { get } 18 | } 19 | 20 | extension AvatarDetails { 21 | package func url(withSize size: String) -> String { 22 | if let newURL = URLComponents(string: imageURL)?.replacingQueryItem(name: "size", value: size).string { 23 | return newURL 24 | } 25 | return imageURL 26 | } 27 | 28 | package var isSelected: Bool { 29 | selected ?? false 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/Model/AvatarRating+Additions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension AvatarRating { 4 | package func toRating() -> Rating { 5 | switch self { 6 | case .g: 7 | .general 8 | case .pg: 9 | .parentalGuidance 10 | case .r: 11 | .restricted 12 | case .x: 13 | .x 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/ProfileFetching.swift: -------------------------------------------------------------------------------- 1 | protocol ProfileFetching { 2 | /// Fetches a Gravatar user's profile information, and delivers the user profile asynchronously. 3 | /// - Parameter profileID: a ProfileIdentifier 4 | /// - Returns: An asynchronously-delivered user profile. 5 | func fetch(with profileID: ProfileIdentifier) async throws -> Profile 6 | } 7 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/Services/ServiceConfig.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum APIConfig { 4 | private static let baseURLString = "https://api.gravatar.com/" 5 | static let baseURL = URL(string: baseURLString)! 6 | static let baseURLComponents = URLComponents(string: baseURLString)! 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Gravatar/Network/URLSessionProtocol.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// Protocol for dependency injection purposes. [URLSession] conforms to this protocol. 4 | /// 5 | /// [URLSession]: https://developer.apple.com/documentation/foundation/urlsession 6 | public protocol URLSessionProtocol: Sendable { 7 | func data(for request: URLRequest) async throws -> (Data, URLResponse) 8 | 9 | func upload(for request: URLRequest, from bodyData: Data) async throws -> (Data, URLResponse) 10 | } 11 | 12 | extension URLSession: URLSessionProtocol {} 13 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/AssociatedResponse.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct AssociatedResponse: Codable, Hashable, Sendable { 4 | /// Whether the entity is associated with the account. 5 | private(set) var associated: Bool 6 | 7 | init(associated: Bool) { 8 | self.associated = associated 9 | } 10 | 11 | enum CodingKeys: String, CodingKey, CaseIterable { 12 | case associated 13 | } 14 | 15 | // Encodable protocol methods 16 | 17 | func encode(to encoder: Encoder) throws { 18 | var container = encoder.container(keyedBy: CodingKeys.self) 19 | try container.encode(associated, forKey: .associated) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/AvatarRating.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Rating associated with the image. 4 | /// 5 | enum AvatarRating: String, Codable, CaseIterable { 6 | case g = "G" 7 | case pg = "PG" 8 | case r = "R" 9 | case x = "X" 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/CryptoWalletAddress.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A crypto currency wallet address the user accepts. 4 | /// 5 | public struct CryptoWalletAddress: Codable, Hashable, Sendable { 6 | /// The label for the crypto currency. 7 | public private(set) var label: String 8 | /// The wallet address for the crypto currency. 9 | public private(set) var address: String 10 | 11 | init(label: String, address: String) { 12 | self.label = label 13 | self.address = address 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case label 18 | case address 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | public func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(label, forKey: .label) 26 | try container.encode(address, forKey: .address) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/GalleryImage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A gallery image a user has uploaded. 4 | /// 5 | public struct GalleryImage: Codable, Hashable, Sendable { 6 | /// The URL to the image. 7 | public private(set) var url: String 8 | /// The image alt text. 9 | public private(set) var altText: String? 10 | 11 | init(url: String, altText: String? = nil) { 12 | self.url = url 13 | self.altText = altText 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case url 18 | case altText = "alt_text" 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | public func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(url, forKey: .url) 26 | try container.encodeIfPresent(altText, forKey: .altText) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/Interest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// An interest the user has added to their profile. 4 | /// 5 | public struct Interest: Codable, Hashable, Sendable { 6 | /// The unique identifier for the interest. 7 | public private(set) var id: Int 8 | /// The name of the interest as originally defined (most often in English). 9 | public private(set) var name: String 10 | 11 | init(id: Int, name: String) { 12 | self.id = id 13 | self.name = name 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case id 18 | case name 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | public func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(id, forKey: .id) 26 | try container.encode(name, forKey: .name) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/Link.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A link the user has added to their profile. 4 | /// 5 | public struct Link: Codable, Hashable, Sendable { 6 | /// The label for the link. 7 | public private(set) var label: String 8 | /// The URL to the link. 9 | public private(set) var url: String 10 | 11 | init(label: String, url: String) { 12 | self.label = label 13 | self.url = url 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case label 18 | case url 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | public func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(label, forKey: .label) 26 | try container.encode(url, forKey: .url) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/ModelError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// An error response from the API. 4 | /// 5 | struct ModelError: Codable, Hashable, Sendable { 6 | /// The error message 7 | private(set) var error: String 8 | /// The error code for the error message 9 | private(set) var code: String? 10 | 11 | init(error: String, code: String? = nil) { 12 | self.error = error 13 | self.code = code 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case error 18 | case code 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(error, forKey: .error) 26 | try container.encodeIfPresent(code, forKey: .code) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/ProfilePayments.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// The user's public payment information. This is only provided in authenticated API requests. 4 | /// 5 | public struct ProfilePayments: Codable, Hashable, Sendable { 6 | /// A list of payment URLs the user has added to their profile. 7 | public private(set) var links: [Link] 8 | /// A list of crypto currencies the user accepts. 9 | public private(set) var cryptoWallets: [CryptoWalletAddress] 10 | 11 | init(links: [Link], cryptoWallets: [CryptoWalletAddress]) { 12 | self.links = links 13 | self.cryptoWallets = cryptoWallets 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case links 18 | case cryptoWallets = "crypto_wallets" 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | public func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(links, forKey: .links) 26 | try container.encode(cryptoWallets, forKey: .cryptoWallets) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/SetEmailAvatarRequest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct SetEmailAvatarRequest: Codable, Hashable, Sendable { 4 | /// The email SHA256 hash to set the avatar for. 5 | private(set) var emailHash: String 6 | 7 | init(emailHash: String) { 8 | self.emailHash = emailHash 9 | } 10 | 11 | enum CodingKeys: String, CodingKey, CaseIterable { 12 | case emailHash = "email_hash" 13 | } 14 | 15 | // Encodable protocol methods 16 | 17 | func encode(to encoder: Encoder) throws { 18 | var container = encoder.container(keyedBy: CodingKeys.self) 19 | try container.encode(emailHash, forKey: .emailHash) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Gravatar/OpenApi/Generated/UpdateAvatarRequest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// The avatar data to update. Partial updates are supported, so only the provided fields will be updated. 4 | /// 5 | struct UpdateAvatarRequest: Codable, Hashable, Sendable { 6 | /// Rating associated with the image. 7 | private(set) var rating: AvatarRating? 8 | /// Alternative text description of the image. 9 | private(set) var altText: String? 10 | 11 | init(rating: AvatarRating? = nil, altText: String? = nil) { 12 | self.rating = rating 13 | self.altText = altText 14 | } 15 | 16 | enum CodingKeys: String, CodingKey, CaseIterable { 17 | case rating 18 | case altText = "alt_text" 19 | } 20 | 21 | // Encodable protocol methods 22 | 23 | func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encodeIfPresent(rating, forKey: .rating) 26 | try container.encodeIfPresent(altText, forKey: .altText) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Gravatar/Options/AvatarSelection.swift: -------------------------------------------------------------------------------- 1 | /// Defines how to handle avatar selection after uploading a new avatar 2 | public enum AvatarSelection: Equatable, Sendable { 3 | case preserveSelection 4 | case selectUploadedImage(for: Email) 5 | case selectUploadedImageIfNoneSelected(for: Email) 6 | 7 | public static func allCases(for email: Email) -> [AvatarSelection] { 8 | [ 9 | .preserveSelection, 10 | .selectUploadedImage(for: email), 11 | .selectUploadedImageIfNoneSelected(for: email), 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Gravatar/Options/ImageProcessorOption.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | /// Enum defining methods for processing and transforming the image data into a UIImage instance. 5 | /// 6 | /// Each case represents a specific processing or transformation method that will be applied to the image data. 7 | public enum ImageProcessingMethod: Sendable { 8 | /// Apply a custom processor to the image data. 9 | /// - Parameter processor: An instance of a type conforming to `ImageProcessor`. 10 | case custom(processor: ImageProcessor) 11 | 12 | /// A processing method which will directly transform the `Data` to an `UIImage` and return it. 13 | /// 14 | /// - Parameter scaleFactor: The scale factor to use to create the `UIImage`. If nil, UITraitCollection's displayScale is used. 15 | case common(scaleFactor: CGFloat = UITraitCollection.current.displayScale) 16 | } 17 | 18 | extension ImageProcessingMethod { 19 | var processor: ImageProcessor { 20 | switch self { 21 | case .common(let scaleFactor): 22 | DefaultImageProcessor(scaleFactor: scaleFactor) 23 | case .custom(let processor): 24 | processor 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Gravatar/Options/ImageSize.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// The size of the image to be requested. 4 | /// 5 | /// Gravatar images are always square. Providing the desired square side length is sufficient. 6 | /// Note that many users have lower resolution images. Requesting larger sizes may result in low-quality images. 7 | /// For more info, see [Gravatar Image docs](https://docs.gravatar.com/general/images/#size) 8 | public enum ImageSize { 9 | /// Points are preferred when working with UI components, such as UIImageView. 10 | /// - As an example: If the size of the Image View is 40x40, you can request the image size with`.points(40)`. 11 | /// The suitable pixel value is calculated internally, according to the screen of the user's device. 12 | case points(CGFloat) 13 | /// The returned image's size in pixels will be of the exact value passed here. 14 | case pixels(Int) 15 | } 16 | 17 | extension ImageSize { 18 | func pixels(scaleFactor: CGFloat) -> Int { 19 | switch self { 20 | case .pixels(let pixels): 21 | pixels 22 | case .points(let points): 23 | Int(points * scaleFactor) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Gravatar/Options/ImageTransition.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum ImageTransition: Sendable { 4 | /// No animation transition. 5 | case none 6 | /// Fade in the loaded image in a given duration. 7 | case fade(TimeInterval) 8 | } 9 | 10 | extension ImageTransition: Equatable {} 11 | -------------------------------------------------------------------------------- /Sources/Gravatar/ProfileURL.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct ProfileURL { 4 | public let url: URL 5 | public let hash: String 6 | public var avatarURL: AvatarURL? { 7 | AvatarURL(with: .hashID(self.hash)) 8 | } 9 | 10 | static let baseURL: URL? = { 11 | guard 12 | let baseURL = URL(string: .baseURL), 13 | let components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) 14 | else { 15 | return nil 16 | } 17 | return components.url 18 | }() 19 | 20 | public init?(with profileID: ProfileIdentifier) { 21 | guard let url = Self.baseURL?.appending(pathComponent: profileID.id) else { 22 | return nil 23 | } 24 | 25 | self.url = url 26 | self.hash = profileID.id 27 | } 28 | } 29 | 30 | extension String { 31 | fileprivate static let baseURL = "https://gravatar.com/" 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Gravatar/Resources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/Gravatar/Resources/.gitkeep -------------------------------------------------------------------------------- /Sources/Gravatar/Resources/SDKInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleShortVersionString 6 | 3.4.0-rc.1 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/Gravatar/UIImage/Processor/DefaultImageProcessor.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// The default processor. It applies the scale factor on the given image data and converts it into an image. 4 | struct DefaultImageProcessor: ImageProcessor, Sendable { 5 | public let scaleFactor: CGFloat 6 | 7 | public func process(_ data: Data) -> UIImage? { 8 | UIImage(data: data, scale: scaleFactor) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Gravatar/UIImage/Processor/ImageProcessor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | /// Processor to apply to the downloaded image data. 5 | public protocol ImageProcessor: Sendable { 6 | func process(_ data: Data) -> UIImage? 7 | } 8 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/Array+Additions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Array { 4 | subscript(safe index: Int) -> Element? { 5 | guard index >= 0 && index < count else { return nil } 6 | return self[index] 7 | } 8 | } 9 | 10 | extension [Bool] { 11 | var hasMoreThanOneTrue: Bool { 12 | count { $0 } > 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/AssociatedObject.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | func getAssociatedObject(_ object: Any, _ key: UnsafeRawPointer) -> T? { 4 | if #available(iOS 14, *) { // swift 5.3 fixed this issue (https://github.com/apple/swift/issues/46456) 5 | objc_getAssociatedObject(object, key) as? T 6 | } else { 7 | objc_getAssociatedObject(object, key) as AnyObject as? T 8 | } 9 | } 10 | 11 | func setRetainedAssociatedObject(_ object: Any, _ key: UnsafeRawPointer, _ value: some Any) { 12 | objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 13 | } 14 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/Box.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Box { 4 | var value: T 5 | 6 | init(_ value: T) { 7 | self.value = value 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/IdentifiableURL.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct IdentifiableURL: Identifiable { 4 | let url: URL 5 | 6 | init?(url: URL?) { // for convenience 7 | guard let url else { return nil } 8 | self.url = url 9 | } 10 | 11 | public var id: String { 12 | url.absoluteString 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/SimpleCounter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @MainActor 4 | enum SimpleCounter { 5 | private(set) static var current: UInt = 0 6 | static func next() -> UInt { 7 | current += 1 8 | return current 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/UIApplication+Additions.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIApplication { 4 | func dismissKeyboard() { 5 | sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Base/UIView+Additions.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIView { 4 | static func spacer() -> UIView { 5 | let spacer = UIView() 6 | spacer.translatesAutoresizingMaskIntoConstraints = false 7 | spacer.setContentHuggingPriority(.defaultLow, for: .vertical) 8 | spacer.setContentHuggingPriority(.defaultLow, for: .horizontal) 9 | return spacer 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Bundle+ResourceBundle.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if !SWIFT_PACKAGE 4 | private class BundleFinder: NSObject {} 5 | extension Bundle { 6 | static var module: Bundle { 7 | let defaultBundle = Bundle(for: BundleFinder.self) 8 | // If installed with CocoaPods, resources will be in GravatarUI.bundle 9 | if let bundleURL = defaultBundle.resourceURL, 10 | let resourceBundle = Bundle(url: bundleURL.appendingPathComponent("GravatarUI.bundle")) 11 | { 12 | return resourceBundle 13 | } 14 | // Otherwise, the default bundle is used for resources 15 | return defaultBundle 16 | } 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /Sources/GravatarUI/DesignSystem/CGFloat+DesignSystem.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension CGFloat { 4 | enum DS { 5 | enum Padding { 6 | public static let half: CGFloat = 4 7 | public static let single: CGFloat = 8 8 | public static let split: CGFloat = 12 9 | public static let double: CGFloat = 16 10 | public static let medium: CGFloat = 24 11 | public static let large: CGFloat = 32 12 | public static let max: CGFloat = 48 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Error+Extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | extension Error { 5 | func map() -> ImageFetchingError { 6 | switch self { 7 | case let error as ImageFetchingError: 8 | error 9 | case let error: 10 | ImageFetchingError.responseError(reason: .unexpected(error)) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Exports.swift: -------------------------------------------------------------------------------- 1 | @_exported import Gravatar 2 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarCompatibleUI/GravatarCompatible.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | @MainActor 5 | /// Wrapper for `GravatarCompatible` types. 6 | public struct GravatarWrapper { 7 | public let component: Component 8 | public init(_ component: Component) { 9 | self.component = component 10 | } 11 | } 12 | 13 | public protocol GravatarCompatible: AnyObject {} 14 | 15 | @MainActor 16 | /// Provides namespacing for the Gravatar functionality. 17 | extension GravatarCompatible { 18 | /// Returns a wrapper that provides Gravatar's convenience methods and properties. 19 | public var gravatar: GravatarWrapper { 20 | get { GravatarWrapper(self) } 21 | set {} 22 | } 23 | } 24 | 25 | extension UIImageView: GravatarCompatible {} 26 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileSummaryView.view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileSummaryView.view@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileSummaryView.view~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileSummaryView.view~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileView.view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileView.view@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileView.view~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/largeProfileView.view~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileSummaryView.view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileSummaryView.view@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileSummaryView.view~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileSummaryView.view~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileView.view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileView.view@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileView.view~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/profileView.view~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-intrinsic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-intrinsic@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-intrinsic~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-intrinsic~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-medium@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-medium@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-medium~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor-medium~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor~dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/about-editor~dark@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/avatar-and-about@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/avatar-and-about@2x.gif -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/avatar-and-about~dark@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/avatar-and-about~dark@2x.gif -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/horizontal-intrinsic-height.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/horizontal-intrinsic-height.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/vertical-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/vertical-large.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/vertical-medium-expandable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QEExamples/vertical-medium-expandable.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_01_03@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_01_03@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_01_04@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_01_04@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_01_05@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_01_05@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_Header@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_Header@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_Preview@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/QuickEditor_Tutorial/QuickEditor_Tutorial_Preview@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/gravatar-sdk@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/gravatar-sdk@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/tutorial_01_01@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/tutorial_01_01@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/tutorial_01_02@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/tutorial_01_02@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Resources/tutorial_01_header@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/GravatarUI.docc/Resources/tutorial_01_header@2x.png -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/ContactsList/Tut_01.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import GravatarUI 3 | 4 | class TableViewController: UITableViewController { 5 | private static let cellID = "ProfileCell" 6 | 7 | lazy var dataSource = UITableViewDiffableDataSource(tableView: tableView) { [weak self] tableView, indexPath, itemIdentifier in 8 | let cell = tableView.dequeueReusableCell(withIdentifier: Self.cellID) ?? UITableViewCell(style: .default, reuseIdentifier: Self.cellID) 9 | 10 | return cell 11 | } 12 | 13 | var snapshot = NSDiffableDataSourceSnapshot() 14 | } 15 | 16 | enum Section { 17 | // Default section for the table view 18 | case main 19 | } 20 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/ContactsList/Tut_02.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import GravatarUI 3 | 4 | class TableViewController: UITableViewController { 5 | private static let cellID = "ProfileCell" 6 | 7 | lazy var dataSource = UITableViewDiffableDataSource(tableView: tableView) { [weak self] tableView, indexPath, itemIdentifier in 8 | let cell = tableView.dequeueReusableCell(withIdentifier: Self.cellID) ?? UITableViewCell(style: .default, reuseIdentifier: Self.cellID) 9 | 10 | return cell 11 | } 12 | 13 | var snapshot = NSDiffableDataSourceSnapshot() 14 | 15 | var models = [String: ProfileViewConfiguration]() 16 | 17 | func addProfile(with email: String) { 18 | guard !email.isEmpty else { return } 19 | 20 | // Summary profile view configuration. 21 | var config = ProfileViewConfiguration.summary() 22 | // Set in loading state 23 | config.isLoading = true 24 | // Store the configuration to get access to it later on. 25 | models[email] = config 26 | } 27 | } 28 | 29 | enum Section { 30 | case main 31 | } 32 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/ContactsList/Tut_05.swift: -------------------------------------------------------------------------------- 1 | class TableViewController: UITableViewController { 2 | 3 | // ... All code from before ... 4 | 5 | func fetchProfile(with email: String) async throws { 6 | // Create a profile service and fetch a profile using the given email. 7 | let service = ProfileService() 8 | let profile = try await service.fetch(with: .email(email)) 9 | 10 | // Create a Summary ProfileViewConfiguration to display the profile obtained, 11 | // and store it to be retreived later on. 12 | models[email] = .summary(model: profile) 13 | 14 | // Reload the cell for the given email. 15 | // This will update the cell from the loading to the displaying info state. 16 | snapshot.reloadItems([email]) 17 | await dataSource.apply(snapshot) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/ContactsList/Tut_06.swift: -------------------------------------------------------------------------------- 1 | class TableViewController: UITableViewController { 2 | 3 | // ... All code from before ... 4 | 5 | func addProfile(with email: String) { 6 | guard !email.isEmpty else { return } 7 | 8 | var config = ProfileViewConfiguration.summary() 9 | config.isLoading = true 10 | models[email] = config 11 | 12 | snapshot.appendItems([email]) 13 | dataSource.apply(snapshot) 14 | 15 | Task { 16 | do { 17 | try await fetchProfile(with: email) 18 | } catch { 19 | // Do proper error handling 20 | print(error) 21 | } 22 | } 23 | } 24 | 25 | func fetchProfile(with email: String) async throws { 26 | let service = ProfileService() 27 | let profile = try await service.fetch(with: .email(email)) 28 | models[email] = .summary(model: profile) 29 | 30 | snapshot.reloadItems([email]) 31 | await dataSource.apply(snapshot) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/ContactsList/Tut_06_comparison.swift: -------------------------------------------------------------------------------- 1 | class TableViewController: UITableViewController { 2 | 3 | // ... All code from before ... 4 | 5 | func addProfile(with email: String) { 6 | guard !email.isEmpty else { return } 7 | 8 | var config = ProfileViewConfiguration.summary() 9 | config.isLoading = true 10 | models[email] = config 11 | 12 | snapshot.appendItems([email]) 13 | dataSource.apply(snapshot) 14 | } 15 | 16 | func fetchProfile(with email: String) async throws { 17 | let service = ProfileService() 18 | let profile = try await service.fetch(with: .email(email)) 19 | models[email] = .summary(model: profile) 20 | 21 | snapshot.reloadItems([email]) 22 | await dataSource.apply(snapshot) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/QuickEditor/QE_Tut_01.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | var body: some View { 5 | VStack { 6 | Image(systemName: "globe") 7 | .imageScale(.large) 8 | .foregroundStyle(.tint) 9 | Text("Hello, world!") 10 | } 11 | .padding() 12 | } 13 | } 14 | 15 | #Preview { 16 | ContentView() 17 | } 18 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/QuickEditor/QE_Tut_02.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | @AppStorage("pickerEmail") private var email: String = "" 5 | @AppStorage("pickAuthToken") private var authToken: String = "" 6 | @State private var isPresentingPicker: Bool = false 7 | 8 | var body: some View { 9 | VStack { 10 | VStack { 11 | TextField("Enter your email", text: $email) 12 | .font(.callout) 13 | .textInputAutocapitalization(.never) 14 | .keyboardType(.emailAddress) 15 | .disableAutocorrection(true) 16 | SecureField("Enter your auth token", text: $authToken) 17 | .font(.callout) 18 | .textInputAutocapitalization(.never) 19 | .disableAutocorrection(true) 20 | } 21 | .padding(20) 22 | 23 | Button("Show Quick Editor") { 24 | isPresentingPicker = true 25 | } 26 | Spacer() 27 | } 28 | .padding() 29 | } 30 | } 31 | 32 | #Preview { 33 | ContentView() 34 | } 35 | -------------------------------------------------------------------------------- /Sources/GravatarUI/GravatarUI.docc/Tutorials/Tutorials.tutorial: -------------------------------------------------------------------------------- 1 | @Tutorials(name: "Gravatar SDK Tutorials") { 2 | @Intro(title: "Learn to use Gravatar SDK") { 3 | } 4 | 5 | @Chapter(name: "Profile views as Contacts List.") { 6 | Learn how to use GravatarUI to create a contacts list. 7 | 8 | @Image(source: "tutorial_01_header.png", alt: "Add an accessible description for your image here.") 9 | 10 | @TutorialReference(tutorial: "doc:ContactsList") 11 | } 12 | 13 | @Chapter(name: "Adding the Quick Editor") { 14 | Learn how to use GravatarUI to create a contacts list. 15 | 16 | @Image(source: "QuickEditor_Tutorial_Preview.png", alt: "Add an accessible description for your image here.") 17 | 18 | @TutorialReference(tutorial: "doc:QuickEditor") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ImageCropper/CropFrameOverlayView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class CropFrameOverlayView: UIView { 4 | var scrollViewFrame: CGRect = .zero { 5 | didSet { 6 | setNeedsDisplay() 7 | } 8 | } 9 | 10 | override func draw(_ rect: CGRect) { 11 | guard let context = UIGraphicsGetCurrentContext() else { return } 12 | 13 | // Fill the entire view with a semi black color 14 | context.setFillColor(UIColor.black.withAlphaComponent(0.5).cgColor) 15 | context.fill(rect) 16 | 17 | // Create a circular path for the transparent hole 18 | let circlePath = UIBezierPath(ovalIn: scrollViewFrame) 19 | 20 | // Clear the circular area 21 | context.addPath(circlePath.cgPath) 22 | context.clip(using: .evenOdd) 23 | context.clear(scrollViewFrame) 24 | 25 | // Restore context to draw the square border 26 | context.resetClip() 27 | 28 | // Draw a white border around the square 29 | context.setStrokeColor(UIColor.white.cgColor) 30 | context.setLineWidth(1) 31 | context.stroke(scrollViewFrame) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/AboutMeBuilder.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @MainActor 4 | public struct AboutMeBuilder { 5 | let label: UILabel 6 | init(label: UILabel) { 7 | self.label = label 8 | } 9 | 10 | @discardableResult 11 | public func content(_ model: AboutMeModel) -> AboutMeBuilder { 12 | label.text = model.description 13 | label.font = .DS.Body.xSmall 14 | label.numberOfLines = 2 15 | return self 16 | } 17 | 18 | @discardableResult 19 | public func palette(_ paletteType: PaletteType) -> AboutMeBuilder { 20 | label.textColor = paletteType.palette.foreground.primarySlightlyDimmed 21 | return self 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/DisplayNameBuilder.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @MainActor 4 | public struct DisplayNameBuilder { 5 | let label: UILabel 6 | init(label: UILabel) { 7 | self.label = label 8 | } 9 | 10 | @discardableResult 11 | public func content(_ model: DisplayNameModel) -> DisplayNameBuilder { 12 | label.text = model.displayName 13 | label.font = .DS.title2 14 | label.numberOfLines = 0 15 | return self 16 | } 17 | 18 | @discardableResult 19 | public func palette(_ paletteType: PaletteType) -> DisplayNameBuilder { 20 | label.textColor = paletteType.palette.foreground.primary 21 | return self 22 | } 23 | 24 | @discardableResult 25 | func font(_ font: UIFont) -> DisplayNameBuilder { 26 | label.font = font 27 | return self 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/Model/AboutMeModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | public protocol AboutMeModel { 5 | var description: String { get } 6 | } 7 | 8 | extension Profile: AboutMeModel {} 9 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/Model/AccountModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | public protocol AccountModel { 5 | var serviceLabel: String { get } 6 | var shortname: String { get } 7 | var iconURL: URL? { get } 8 | var accountURL: URL? { get } 9 | } 10 | 11 | public protocol AccountListModel { 12 | var accountsList: [AccountModel] { get } 13 | } 14 | 15 | struct GravatarAccountModel: AccountModel { 16 | let serviceLabel: String = "Gravatar" 17 | let shortname: String = "gravatar" 18 | let iconURL: URL? = nil 19 | let accountURL: URL? 20 | } 21 | 22 | extension VerifiedAccount: AccountModel { 23 | public var shortname: String { 24 | serviceLabel.lowercased() 25 | } 26 | 27 | public var iconURL: URL? { 28 | URL(string: serviceIcon) 29 | } 30 | 31 | public var accountURL: URL? { 32 | URL(string: url) 33 | } 34 | } 35 | 36 | extension Profile: AccountListModel { 37 | public var accountsList: [AccountModel] { 38 | [gravatarAccount] + verifiedAccounts 39 | } 40 | 41 | var gravatarAccount: AccountModel { 42 | GravatarAccountModel(accountURL: URL(string: profileUrl)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/Model/AvatarIdentifierProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | public protocol AvatarIdentifierProvider { 5 | var avatarIdentifier: AvatarIdentifier? { get } 6 | } 7 | 8 | extension Profile: AvatarIdentifierProvider { 9 | public var avatarIdentifier: AvatarIdentifier? { 10 | .hashID(hash) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/Model/DisplayNameModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | public protocol DisplayNameModel { 5 | var displayName: String { get } 6 | } 7 | 8 | extension Profile: DisplayNameModel {} 9 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/Model/ProfileMetadataModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | public protocol ProfileMetadataModel { 5 | var profileURL: URL? { get } 6 | var profileEditURL: URL? { get } 7 | } 8 | 9 | extension Profile: ProfileMetadataModel { 10 | public var profileEditURL: URL? { 11 | URL(string: "https://gravatar.com/profile") 12 | } 13 | 14 | public var profileURL: URL? { 15 | URL(string: profileUrl) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileFields/Model/ProfileModel.swift: -------------------------------------------------------------------------------- 1 | public typealias ProfileModel = AboutMeModel & AccountListModel & AvatarIdentifierProvider & DisplayNameModel & PersonalInfoModel & ProfileMetadataModel 2 | public typealias ProfileSummaryModel = AvatarIdentifierProvider & DisplayNameModel & PersonalInfoModel & ProfileMetadataModel 3 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileView/ActivityIndicator/ActivityIndicator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// Describes a general purpose activity indicator. 4 | @MainActor 5 | public protocol ActivityIndicator { 6 | associatedtype T: UIView 7 | func startAnimating(on baseView: T) 8 | func stopAnimating(on baseView: T) 9 | } 10 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileView/Placeholder/PlaceholderDisplayers/LabelPlaceholderDisplayer.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A ``PlaceholderDisplaying`` implementation for a UILabel. 4 | @MainActor 5 | class LabelPlaceholderDisplayer: RectangularPlaceholderDisplayer { 6 | var isAnimating: Bool = false 7 | 8 | override func animationDidEnd() { 9 | isAnimating = false 10 | } 11 | 12 | override func animationWillBegin() { 13 | // If UILabel's backgroundColor is set, the animation won't be visible. So we need to clear it. This is only needed for UILabel so far. 14 | set(viewColor: .clear) 15 | isAnimating = true 16 | } 17 | 18 | override func set(viewColor newColor: UIColor?) { 19 | if !isAnimating { 20 | // If UILabel's backgroundColor is set, the animation won't be visible. 21 | // So prevent setting it if there's an animation in progress. 22 | super.set(viewColor: newColor) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileView/Placeholder/PlaceholderDisplayers/ProfileButtonPlaceholderDisplayer.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A ``PlaceholderDisplaying`` implementation for the "Edit/View profile" button. 4 | @MainActor 5 | class ProfileButtonPlaceholderDisplayer: RectangularPlaceholderDisplayer { 6 | override func showPlaceholder() { 7 | super.showPlaceholder() 8 | baseView.imageView?.isHidden = true 9 | baseView.titleLabel?.isHidden = true 10 | } 11 | 12 | override func hidePlaceholder() { 13 | super.hidePlaceholder() 14 | baseView.imageView?.isHidden = false 15 | baseView.titleLabel?.isHidden = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileView/Placeholder/UIView+Placeholder.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @MainActor 4 | extension UIView { 5 | func turnIntoPlaceholder(cornerRadius: CGFloat?, height: CGFloat?, widthRatioToParent: CGFloat?) -> [NSLayoutConstraint] { 6 | if let cornerRadius { 7 | layer.cornerRadius = cornerRadius 8 | clipsToBounds = true 9 | } 10 | var constraints: [NSLayoutConstraint] = [] 11 | 12 | if let height { 13 | let heightConstraint = heightAnchor.constraint(equalToConstant: height) 14 | heightConstraint.priority = .required 15 | constraints.append(heightConstraint) 16 | } 17 | 18 | if let widthRatioToParent, let superview { 19 | let widthConstraint = widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: widthRatioToParent) 20 | widthConstraint.priority = .required 21 | constraints.append(widthConstraint) 22 | } 23 | return constraints 24 | } 25 | 26 | func resetPlaceholder(cornerRadius: CGFloat) { 27 | layer.cornerRadius = cornerRadius 28 | if cornerRadius == 0 { 29 | clipsToBounds = false 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/GravatarUI/ProfileViewController/ProfileViewModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | @MainActor 5 | public class ProfileViewModel { 6 | @Published public private(set) var isLoading: Bool = false 7 | @Published public private(set) var profileFetchingResult: Result? 8 | private let profileService: ProfileService 9 | 10 | public init(profileService: ProfileService = ProfileService()) { 11 | self.profileService = profileService 12 | } 13 | 14 | public func fetchProfile(profileIdentifier: ProfileIdentifier) async { 15 | defer { 16 | isLoading = false 17 | } 18 | do { 19 | isLoading = true 20 | profileFetchingResult = try await .success(profileService.fetch(with: profileIdentifier)) 21 | } catch let error as APIError { 22 | profileFetchingResult = .failure(error) 23 | } catch { 24 | profileFetchingResult = .failure(.responseError(reason: .unexpected(error))) 25 | } 26 | } 27 | 28 | public func clear() { 29 | profileFetchingResult = nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/calendly.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "calendly.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/calendly.imageset/calendly.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/calendly.imageset/calendly.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/fediverse.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "fediverse.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/fediverse.imageset/fediverse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/fediverse.imageset/fediverse.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/foursquare.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "foursquare.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/foursquare.imageset/foursquare.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/foursquare.imageset/foursquare.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/github.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "github.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/github.imageset/github.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/github.imageset/github.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/gravatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "gravatar.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/gravatar.imageset/gravatar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/gravatar.imageset/gravatar.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/instagram.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "instagram.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/instagram.imageset/instagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/instagram.imageset/instagram.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/mastodongeneric.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mastodongeneric.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/mastodongeneric.imageset/mastodongeneric.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/mastodongeneric.imageset/mastodongeneric.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/stackoverflow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "stackoverflow.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/stackoverflow.imageset/stackoverflow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/stackoverflow.imageset/stackoverflow.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/tiktok.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tiktok.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/tiktok.imageset/tiktok.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/tiktok.imageset/tiktok.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/tripit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tripit.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/tripit.imageset/tripit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/tripit.imageset/tripit.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/tumblr.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tumblr.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/tumblr.imageset/tumblr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/tumblr.imageset/tumblr.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/twitch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "twitch.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/twitch.imageset/twitch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/twitch.imageset/twitch.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/twitter-alt.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "twitter-alt.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/twitter-alt.imageset/twitter-alt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/twitter-alt.imageset/twitter-alt.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/vimeo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "vimeo.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/vimeo.imageset/vimeo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/vimeo.imageset/vimeo.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/wordpress.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "wordpress.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/wordpress.imageset/wordpress.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/AccountIcons.xcassets/wordpress.imageset/wordpress.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/wp-link.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LinkIcon.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/AccountIcons.xcassets/wp-link.imageset/LinkIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/empty-profile-avatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Regular-S.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/more-horizontal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "more-horizontal.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/more-horizontal.imageset/more-horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/pencil.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pencil.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/pencil.imageset/pencil.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/GravatarUI/Resources/Media.xcassets/pencil.imageset/pencil.pdf -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/qe-intro-empty-profile-avatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Avatar 1.svg", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "Avatar.svg", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/GravatarUI/Resources/Media.xcassets/setup-avatar-emoji.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "emoji-smile.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/AvatarPicker/AvatarShareItem.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import UIKit 3 | 4 | struct AvatarShareItem: Identifiable { 5 | let id: String 6 | let fileURL: URL 7 | } 8 | 9 | struct PlaygroundInputItem: Identifiable { 10 | let id: String 11 | let image: Image 12 | } 13 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/AvatarPicker/Views/CTAButtonView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CTAButtonView: View { 4 | let title: String 5 | @Environment(\.isEnabled) var isEnabled 6 | 7 | public init(_ title: String) { 8 | self.title = title 9 | } 10 | 11 | public var body: some View { 12 | Text(title) 13 | .font(.callout).fontWeight(.bold) 14 | .frame(maxWidth: .infinity) 15 | .foregroundColor(.white) 16 | .padding(.vertical, .DS.Padding.split) 17 | .padding(.horizontal, .DS.Padding.double) 18 | .background( 19 | RoundedRectangle(cornerRadius: 4) 20 | .fill(Color(uiColor: isEnabled ? .gravatarBlue : UIColor.systemFill)) 21 | ) 22 | } 23 | } 24 | 25 | #Preview { 26 | CTAButtonView("I am a button") 27 | .padding() 28 | CTAButtonView("I am a disabled button") 29 | .padding() 30 | .disabled(true) 31 | } 32 | 33 | #Preview("Dark mode") { 34 | CTAButtonView("I am a button") 35 | .padding() 36 | .preferredColorScheme(.dark) 37 | } 38 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/AvatarPicker/Views/DimmingActivityIndicator.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct DimmingActivityIndicator: View { 4 | public var body: some View { 5 | ZStack { 6 | Rectangle() 7 | .fill(.black.opacity(0.3)) 8 | ProgressView().tint(.white) 9 | } 10 | } 11 | } 12 | 13 | #Preview { 14 | DimmingActivityIndicator() 15 | .frame(width: 80, height: 80) 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/AvatarPicker/Views/DimmingButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | /// Dims the parent and puts a retry button on it. 4 | struct DimmingButton: View { 5 | let imageName: String 6 | let action: () -> Void 7 | 8 | var body: some View { 9 | Button( 10 | action: action, 11 | label: { 12 | ZStack { 13 | Rectangle() 14 | .fill(.black.opacity(0.3)) 15 | Image(systemName: imageName) 16 | .resizable() 17 | .aspectRatio(contentMode: .fill) 18 | .frame(width: 20, height: 20) 19 | } 20 | } 21 | ) 22 | .foregroundColor(Color.white) 23 | } 24 | } 25 | 26 | /// Dims the parent and puts an exclamation mark on it. 27 | struct DimmingErrorButton: View { 28 | let action: () -> Void 29 | 30 | var body: some View { 31 | DimmingButton(imageName: "exclamationmark.triangle.fill", action: action) 32 | } 33 | } 34 | 35 | #Preview { 36 | VStack { 37 | DimmingErrorButton {}.frame(width: 100, height: 100) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/AvatarPicker/Views/EmailText.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct EmailText: View { 4 | enum Constants { 5 | static let bottomSpacing: CGFloat = .DS.Padding.double 6 | } 7 | 8 | let email: Email? 9 | var body: some View { 10 | if let email = email?.rawValue, !email.isEmpty { 11 | Text(email) 12 | .padding(.bottom, Constants.bottomSpacing / 2) 13 | .font(.footnote) 14 | .foregroundColor(Color(UIColor.secondaryLabel)) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/AvatarPicker/Views/ShareSheet.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | /// Use `ShareLink` after iOS 16+. 4 | struct ShareSheet: UIViewControllerRepresentable { 5 | var items: [Any] 6 | var activities: [UIActivity]? = nil 7 | 8 | func makeUIViewController(context: Context) -> UIActivityViewController { 9 | let controller = UIActivityViewController(activityItems: items, applicationActivities: activities) 10 | controller.excludedActivityTypes = [.print, 11 | .postToWeibo, 12 | .postToTencentWeibo, 13 | .addToReadingList, 14 | .postToVimeo, 15 | .openInIBooks] 16 | return controller 17 | } 18 | 19 | func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) { 20 | // No need to update dynamically 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/ImageCropper.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ImageCropper: UIViewControllerRepresentable, ImageEditorView { 4 | let inputImage: UIImage 5 | var editingDidFinish: @Sendable (UIImage) -> Void 6 | var onCancel: () -> Void 7 | 8 | typealias UIViewControllerType = UINavigationController 9 | 10 | init(inputImage: UIImage, editingDidFinish: @escaping @Sendable (UIImage) -> Void, onCancel: @escaping () -> Void) { 11 | self.inputImage = inputImage 12 | self.editingDidFinish = editingDidFinish 13 | self.onCancel = onCancel 14 | } 15 | 16 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> UINavigationController { 17 | ImageCropperViewController.wrappedInNavigationViewController( 18 | image: inputImage, 19 | onCompletion: editingDidFinish, 20 | onCancel: onCancel 21 | ) 22 | } 23 | 24 | func updateUIViewController( 25 | _ uiViewController: UINavigationController, 26 | context: UIViewControllerRepresentableContext 27 | ) {} 28 | } 29 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/LoadingIndicatorView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct LoadingIndicatorView: View { 4 | var body: some View { 5 | VStack(spacing: 0) { 6 | ProgressView() 7 | .progressViewStyle(CircularProgressViewStyle()) 8 | .controlSize(.regular) 9 | } 10 | .padding(.top, .DS.Padding.large) 11 | } 12 | } 13 | 14 | #Preview { 15 | LoadingIndicatorView() 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/ModalPresentationModifier.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ModalItemPresentationModifier: ViewModifier where T: Identifiable { 4 | @Binding var item: T? 5 | 6 | let onDismiss: (() -> Void)? 7 | let modalViewBuilder: (T) -> ModalView 8 | 9 | init(item: Binding, onDismiss: (() -> Void)? = nil, @ViewBuilder modalView: @escaping (T) -> ModalView) { 10 | self._item = item 11 | self.onDismiss = onDismiss 12 | self.modalViewBuilder = modalView 13 | } 14 | 15 | func body(content: Content) -> some View { 16 | content 17 | .sheet(item: $item) { item in 18 | modalViewBuilder(item) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/OAuthSession/KeychainToken.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct KeychainToken: Codable { 4 | let token: String 5 | var isExpired: Bool = false 6 | 7 | init?(data: Data) { 8 | let decoder = JSONDecoder() 9 | do { 10 | let decodedToken = try decoder.decode(KeychainToken.self, from: data) 11 | self = decodedToken 12 | } catch { 13 | return nil 14 | } 15 | } 16 | 17 | init(token: String) { 18 | self.token = token 19 | } 20 | 21 | var data: Data? { 22 | let encoder = JSONEncoder() 23 | return try? encoder.encode(self) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/OAuthSession/OldAuthenticationSession+Environment.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | private struct OAuthSessionKey: EnvironmentKey { 4 | static let defaultValue: OAuthSession = .shared 5 | } 6 | 7 | extension EnvironmentValues { 8 | public var oauthSession: OAuthSession { 9 | get { self[OAuthSessionKey.self] } 10 | set { self[OAuthSessionKey.self] = newValue } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/ProfileEditor/QuickEditorUpdate.swift: -------------------------------------------------------------------------------- 1 | /// A marker protocol representing a type of update that has occurred. 2 | /// 3 | /// Concrete implementations are defined under the `QuickEditorUpdate` namespace. 4 | /// 5 | /// Example usage: 6 | /// 7 | /// As an example: 8 | /// ```swift 9 | /// updateHandler: { updateType in 10 | /// switch updateType { 11 | /// case is QuickEditorUpdate.Avatar: 12 | /// // Handle avatar update 13 | /// case let update as QuickEditorUpdate.AboutInfo: 14 | /// // Handle profile update using `update.profile` 15 | /// default: 16 | /// break 17 | /// } 18 | /// } 19 | /// ``` 20 | public protocol QuickEditorUpdateType {} 21 | 22 | /// A namespace for concrete update types conforming to `QuickEditorUpdateType`. 23 | public enum QuickEditorUpdate { 24 | /// Represents an update to the user's avatar. 25 | public struct Avatar: QuickEditorUpdateType {} 26 | 27 | /// Represents an update to the user's profile information. 28 | public struct AboutInfo: QuickEditorUpdateType { 29 | /// The updated Profile model. 30 | public let profile: Profile 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/GravatarUI/SwiftUI/SafariView.swift: -------------------------------------------------------------------------------- 1 | import SafariServices 2 | import SwiftUI 3 | 4 | struct SafariView: UIViewControllerRepresentable { 5 | let url: URL 6 | 7 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { 8 | SFSafariViewController(url: url) 9 | } 10 | 11 | func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { 12 | // No updates needed for SafariViewController 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/TestHelpers/HTTPURLResponse+Extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Gravatar 3 | 4 | extension HTTPURLResponse { 5 | package static func successResponse(with url: URL = URL(string: "https://gravatar.com")!) -> HTTPURLResponse { 6 | HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)! 7 | } 8 | 9 | package static func errorResponse(with url: URL = URL(string: "https://gravatar.com")!, code: Int) -> HTTPURLResponse { 10 | HTTPURLResponse(url: url, statusCode: code, httpVersion: nil, headerFields: nil)! 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/TestHelpers/ImageDownloadService+Extension.swift: -------------------------------------------------------------------------------- 1 | import Gravatar 2 | 3 | extension ImageDownloadService { 4 | package static func mock(with session: URLSessionProtocol, cache: ImageCaching? = nil) -> ImageDownloadService { 5 | let service = ImageDownloadService(urlSession: session, cache: cache) 6 | return service 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/avatarSelected.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_id": "9862792c565394...", 3 | "image_url": "https://2.gravatar.com/userimage/1/991a7b71cf9f34...?size=512", 4 | "rating": "G", 5 | "updated_date": "2024-09-18T10:17:53Z", 6 | "alt_text": "John Appleseed's avatar", 7 | "selected": true 8 | } 9 | -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/avatarSetAltTextResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_id": "991a7b71cf9f34...", 3 | "image_url": "https://2.gravatar.com/userimage/1/991a7b71cf934ea2...?size=512", 4 | "rating": "PG", 5 | "updated_date": "2024-09-18T10:17:53Z", 6 | "alt_text": "Updated Alt Text" 7 | } 8 | -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/avatarSetRatingResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_id": "991a7b71cf9f34...", 3 | "image_url": "https://2.gravatar.com/userimage/1/991a7b71cf934ea2...?size=512", 4 | "rating": "PG", 5 | "updated_date": "2024-09-18T10:17:53Z", 6 | "alt_text": "John Appleseed's avatar" 7 | } 8 | -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/avatarUploadResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_id": "6f3eac1c67f970f2a0c2ea8", 3 | "image_url": "https://2.gravatar.com/userimage/133992/9862792c5653946c?size=512", 4 | "rating": "G", 5 | "updated_date": "2024-09-19T11:46:04Z", 6 | "alt_text": "John Appleseed's avatar" 7 | } 8 | -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/example_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/TestHelpers/Resources/example_avatar.png -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/TestHelpers/Resources/placeholder.png -------------------------------------------------------------------------------- /Sources/TestHelpers/Resources/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Sources/TestHelpers/Resources/test.png -------------------------------------------------------------------------------- /Sources/TestHelpers/TestImageProcessor.swift: -------------------------------------------------------------------------------- 1 | import Gravatar 2 | import UIKit 3 | 4 | package final class TestImageProcessor: ImageProcessor { 5 | let image: UIImage? 6 | package init(image: UIImage? = nil) { 7 | self.image = image 8 | } 9 | 10 | package func process(_: Data) -> UIImage? { 11 | image 12 | } 13 | } 14 | 15 | package final class FailingImageProcessor: ImageProcessor { 16 | package func process(_: Data) -> UIImage? { 17 | nil 18 | } 19 | 20 | package init() {} 21 | } 22 | -------------------------------------------------------------------------------- /Sources/TestHelpers/TestURLSessionError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | package class TestURLSessionError: NSError, @unchecked Sendable { 4 | let message: String 5 | 6 | package init(message: String) { 7 | self.message = message 8 | super.init(domain: NSURLErrorDomain, code: 1) 9 | } 10 | 11 | @available(*, unavailable) 12 | required init?(coder: NSCoder) { 13 | fatalError("init(coder:) has not been implemented") 14 | } 15 | 16 | override package var localizedDescription: String { 17 | message 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/GravatarTests/EmailTests.swift: -------------------------------------------------------------------------------- 1 | import Gravatar 2 | import XCTest 3 | 4 | final class EmailTests: XCTestCase { 5 | private let testEmail = "test@example.com" 6 | 7 | func testEmailSanitized() { 8 | let sanitaryEmailString = "test@example.com" 9 | let unsanitaryEmailString = " TeST@Example.com " 10 | 11 | let hopefullySantizedEmail: Email = .init(unsanitaryEmailString) 12 | 13 | XCTAssertEqual(hopefullySantizedEmail.rawValue, sanitaryEmailString) 14 | } 15 | 16 | func testEmailIDIsHashID() { 17 | let emailHashID: HashID = .init(email: .init(testEmail)) 18 | 19 | let email: Email = .init(testEmail) 20 | 21 | XCTAssertEqual(email.id, emailHashID.id) 22 | } 23 | 24 | func testEmailRawValueIsEmailString() throws { 25 | let sut: Email = try XCTUnwrap(.init(rawValue: testEmail)) 26 | 27 | XCTAssertEqual(sut.rawValue, testEmail) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/GravatarTests/GravatarImageCacheTests.swift: -------------------------------------------------------------------------------- 1 | import Gravatar 2 | import TestHelpers 3 | import XCTest 4 | 5 | final class GravatarImageCacheTests: XCTestCase { 6 | private let key = "ImageKey" 7 | 8 | func testSetAndGet() { 9 | let cache = ImageCache() 10 | cache.setEntry(.ready(ImageHelper.testImage), for: key) 11 | let image = cache.getEntry(with: key) 12 | XCTAssertNotNil(image) 13 | } 14 | 15 | func testRequestingMultipleTimes() { 16 | let cache = ImageCache() 17 | let task = Task { 18 | ImageHelper.testImage 19 | } 20 | cache.setEntry(.inProgress(task), for: key) 21 | let image = cache.getEntry(with: key) 22 | XCTAssertNotNil(image) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/GravatarTests/HashIDTests.swift: -------------------------------------------------------------------------------- 1 | import Gravatar 2 | import XCTest 3 | 4 | final class HashIDTests: XCTestCase { 5 | func testInitWithHashString() { 6 | let hash = "testhash123" 7 | 8 | let sut: HashID = .init(hash) 9 | 10 | XCTAssertEqual(sut.id, hash) 11 | } 12 | 13 | func testInitWithEmail() { 14 | let testEmail = "test@example.com" 15 | let email: Email = .init(testEmail) 16 | 17 | let sut: HashID = .init(email: email) 18 | 19 | XCTAssertEqual(sut.id, email.id) 20 | } 21 | 22 | func testUnSanitizedEmailProducesNoramlizedHashID() { 23 | let sanitizedEmail: Email = .init("test@example.com") 24 | let unSanitizedEmail: Email = .init(" TeST@ExAmPle.com ") 25 | 26 | let hashSanitizedEmail: HashID = .init(email: sanitizedEmail) 27 | let hashUnSanitizedEmail: HashID = .init(email: unSanitizedEmail) 28 | 29 | XCTAssertEqual(hashSanitizedEmail, hashUnSanitizedEmail) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/GravatarTests/RenameMeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | final class RenameMeTests_swift: XCTestCase { 4 | override func setUpWithError() throws { 5 | // Put setup code here. This method is called before the invocation of each test method in the class. 6 | } 7 | 8 | override func tearDownWithError() throws { 9 | // Put teardown code here. This method is called after the invocation of each test method in the class. 10 | } 11 | 12 | func testExample() throws { 13 | // This is an example of a functional test case. 14 | // Use XCTAssert and related functions to verify your tests produce the correct results. 15 | // Any test you write for XCTest can be annotated as throws and async. 16 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 17 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 18 | } 19 | 20 | func testPerformanceExample() throws { 21 | // This is an example of a performance test case. 22 | self.measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/GravatarTests/Resources/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarTests/Resources/placeholder.png -------------------------------------------------------------------------------- /Tests/GravatarTests/Resources/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarTests/Resources/test.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/Bundle+ResourceBundle.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if !SWIFT_PACKAGE 4 | private class BundleFinder: NSObject {} 5 | #endif 6 | 7 | extension Bundle { 8 | /// Returns the GravatarUITests Bundle 9 | /// If installed via CocoaPods, this will be GravatarUITestsResources.bundle, 10 | /// otherwise it will be the module bundle. 11 | /// 12 | class var testsBundle: Bundle { 13 | #if SWIFT_PACKAGE 14 | return Bundle.module 15 | #else 16 | let defaultBundle = Bundle(for: BundleFinder.self) 17 | // If installed with CocoaPods, resources will be in GravatarUITestsResources.bundle 18 | if let bundleURL = defaultBundle.resourceURL, 19 | let resourceBundle = Bundle(url: bundleURL.appendingPathComponent("GravatarUITestsResources.bundle")) 20 | { 21 | return resourceBundle 22 | } 23 | // Otherwise, the default bundle is used for resources 24 | return defaultBundle 25 | #endif 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/GravatarUITests/KeychainTokenTests.swift: -------------------------------------------------------------------------------- 1 | @testable import GravatarUI 2 | import Testing 3 | 4 | struct KeychainTokenTests { 5 | @Test 6 | func testKeychainTokenCreatedFromData() async throws { 7 | let token = KeychainToken(token: "someToken") 8 | let data = token.data 9 | #expect(data != nil) 10 | 11 | let decodedToken = KeychainToken(data: data!) 12 | #expect(decodedToken?.token == token.token) 13 | #expect(decodedToken?.isExpired == token.isExpired) 14 | } 15 | 16 | @Test 17 | func testExpiredKeychainTokenCreatedFromData() async throws { 18 | var token = KeychainToken(token: "someToken") 19 | token.isExpired = true 20 | 21 | let data = token.data 22 | #expect(data != nil) 23 | 24 | let decodedToken = KeychainToken(data: data!) 25 | #expect(decodedToken?.token == token.token) 26 | #expect(decodedToken?.isExpired == true) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/GravatarUITests/Resources/example_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/Resources/example_avatar.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/Resources/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/Resources/placeholder.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/Resources/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/Resources/test.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/Snapshotting+Additions.swift: -------------------------------------------------------------------------------- 1 | import SnapshotTesting 2 | import SwiftUI 3 | import XCTest 4 | 5 | extension Snapshotting where Value: SwiftUI.View, Format == UIImage { 6 | static func testStrategy(userInterfaceStyle: UIUserInterfaceStyle = .light, layout: SwiftUISnapshotLayout = .sizeThatFits) -> Self { 7 | let deviceConfig: ViewImageConfig = .iPhone13 8 | let traits = UITraitCollection(traitsFrom: [ 9 | UITraitCollection(displayScale: deviceConfig.traits.displayScale), 10 | UITraitCollection(userInterfaceStyle: userInterfaceStyle), 11 | ]) 12 | 13 | return .image( 14 | layout: layout, 15 | traits: traits 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/GravatarUITests/TestActivityIndicator.swift: -------------------------------------------------------------------------------- 1 | import GravatarUI 2 | import UIKit 3 | 4 | class TestActivityIndicator: ActivityIndicatorProvider { 5 | var animating = false 6 | private(set) var startCount: Int = 0 7 | private(set) var stopCount: Int = 0 8 | 9 | func startAnimatingView() { 10 | animating = true 11 | startCount += 1 12 | } 13 | 14 | func stopAnimatingView() { 15 | animating = false 16 | stopCount += 1 17 | } 18 | 19 | lazy var view: UIView = { 20 | let newView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20)) 21 | newView.backgroundColor = .blue 22 | return newView 23 | }() 24 | 25 | func sizeStrategy(in view: UIView) -> ActivityIndicatorSizeStrategy { 26 | .intrinsicSize 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsFixedHeight.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsFixedHeight.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsFixedHeight.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsFixedHeight.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsIntrinsicHeight.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsIntrinsicHeight.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsIntrinsicHeight.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewAllFieldsIntrinsicHeight.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewExtraFieldsIntrinsicHeight.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewExtraFieldsIntrinsicHeight.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewExtraFieldsIntrinsicHeight.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewExtraFieldsIntrinsicHeight.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewOneFieldFixedHeight.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewOneFieldFixedHeight.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewOneFieldFixedHeight.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewOneFieldFixedHeight.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewPersonalFieldsIntrinsicHeight.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewPersonalFieldsIntrinsicHeight.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewPersonalFieldsIntrinsicHeight.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewPersonalFieldsIntrinsicHeight.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewProfessionalFieldsIntrinsicHeight.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewProfessionalFieldsIntrinsicHeight.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewProfessionalFieldsIntrinsicHeight.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorViewProfessionalFieldsIntrinsicHeight.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorWithUnsavedChanges.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorWithUnsavedChanges.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorWithUnsavedChanges.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAboutEditorWithUnsavedChanges.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAuthErrorStateAfterSave.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAuthErrorStateAfterSave.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAuthErrorStateAfterSave.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testAuthErrorStateAfterSave.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingErrorState.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingErrorState.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingErrorState.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingErrorState.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingState.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingState.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingState.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutEditorViewTests/testLoadingState.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutMeBuilderTests/testAboutMe.testAboutMe-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutMeBuilderTests/testAboutMe.testAboutMe-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutMeBuilderTests/testAboutMe.testAboutMe-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutMeBuilderTests/testAboutMe.testAboutMe-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AboutMeBuilderTests/testAboutMeWithSmallWidth.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AboutMeBuilderTests/testAboutMeWithSmallWidth.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileView.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileView.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileView.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileView.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewEmpty.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewEmpty.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewEmpty.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewEmpty.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewWithoutLocation.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewWithoutLocation.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewWithoutLocation.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/AvatarPickerProfileViewTests/testAvatarPickerProfileViewWithoutLocation.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/CropFrameOverlayViewTests/testCropFrameOverlay.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/CropFrameOverlayViewTests/testCropFrameOverlay.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameField.testDisplayNameField-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameField.testDisplayNameField-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameField.testDisplayNameField-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameField.testDisplayNameField-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingDisplayName.testDisplayNameFieldWithMissingDisplayName-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingDisplayName.testDisplayNameFieldWithMissingDisplayName-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingDisplayName.testDisplayNameFieldWithMissingDisplayName-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingDisplayName.testDisplayNameFieldWithMissingDisplayName-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingNames.testDisplayNameFieldWithMissingNames-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingNames.testDisplayNameFieldWithMissingNames-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingNames.testDisplayNameFieldWithMissingNames-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithMissingNames.testDisplayNameFieldWithMissingNames-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithSmallWidth.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/DisplayNameBuilderTests/testDisplayNameFieldWithSmallWidth.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testInitiallyEmptyLargeProfileSummaryView.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testInitiallyEmptyLargeProfileSummaryView.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testInitiallyEmptyLargeProfileSummaryView.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testInitiallyEmptyLargeProfileSummaryView.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryCustomAvatarViewImageViewSubview.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryCustomAvatarViewImageViewSubview.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryView.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryView.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryView.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryView.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewCustomAvatarImageViewSubviewCustomStyle.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewCustomAvatarImageViewSubviewCustomStyle.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewCustomAvatarImageViewWrapper.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewCustomAvatarImageViewWrapper.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewCustomAvatarView.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewCustomAvatarView.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewEmptyState.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewEmptyState.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewEmptyState.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewEmptyState.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewEmptyStateCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewEmptyStateCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewLoadingStateClears.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewLoadingStateClears.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewLoadingStateClearsWhenDataIsPresent.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewLoadingStateClearsWhenDataIsPresent.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewLoadingStateClearsWhenEmpty.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewLoadingStateClearsWhenEmpty.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholderCanUpdateColors.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholderCanUpdateColors.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanHide.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanHide.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanHideCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanHideCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanShow.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanShow.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanShowCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileSummaryViewTests/testLargeProfileSummaryViewPlaceholdersCanShowCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testInitiallyEmptyLargeProfileView.Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testInitiallyEmptyLargeProfileView.Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testInitiallyEmptyLargeProfileView.Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testInitiallyEmptyLargeProfileView.Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileCustomAvatarImageViewWrapper.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileCustomAvatarImageViewWrapper.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileCustomAvatarView.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileCustomAvatarView.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileCustomAvatarViewImageViewSubview.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileCustomAvatarViewImageViewSubview.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileView.testLargeProfileView-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileView.testLargeProfileView-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileView.testLargeProfileView-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileView.testLargeProfileView-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewCustomAvatarImageViewSubviewCustomStyle.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewCustomAvatarImageViewSubviewCustomStyle.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewEmptyState.testLargeProfileView-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewEmptyState.testLargeProfileView-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewEmptyState.testLargeProfileView-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewEmptyState.testLargeProfileView-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewEmptyStateCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewEmptyStateCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewLoadingStateClearsWhenDataIsPresent.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewLoadingStateClearsWhenDataIsPresent.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewLoadingStateClearsWhenEmpty.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewLoadingStateClearsWhenEmpty.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholderCanUpdateColors.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholderCanUpdateColors.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanHide.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanHide.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanHideCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanHideCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanShow.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanShow.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanShowCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/LargeProfileViewTests/testLargeProfileViewPlaceholdersCanShowCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoCustom.testPersonalInfoFull-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoCustom.testPersonalInfoFull-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoCustom.testPersonalInfoFull-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoCustom.testPersonalInfoFull-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoFull.testPersonalInfoFull-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoFull.testPersonalInfoFull-Dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoFull.testPersonalInfoFull-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoFull.testPersonalInfoFull-Light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoWithSmallWidth.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/PersonalInfoBuilderTests/testPersonalInfoWithSmallWidth.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileButtonTests/testProfileButtonSnapshots.edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileButtonTests/testProfileButtonSnapshots.edit.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileButtonTests/testProfileButtonSnapshots.view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileButtonTests/testProfileButtonSnapshots.view.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileConfigurationTests/testCustomAvatarStyle.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileConfigurationTests/testCustomAvatarStyle.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testInitiallyEmptyProfileSummaryView.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testInitiallyEmptyProfileSummaryView.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testInitiallyEmptyProfileSummaryView.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testInitiallyEmptyProfileSummaryView.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryView.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryView.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryView.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryView.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarImageViewSubviewCustomStyle.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarImageViewSubviewCustomStyle.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarImageViewWrapper.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarImageViewWrapper.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarView.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarView.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarViewImageViewSubview.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewCustomAvatarViewImageViewSubview.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewEmptyState.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewEmptyState.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewEmptyState.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewEmptyState.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewEmptyStateCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewEmptyStateCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanHide.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanHide.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanHideCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanHideCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanShow.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanShow.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanShowCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileSummaryViewPlaceholdersCanShowCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileViewSummaryLoadingStateClearsWhenDataIsPresent.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileViewSummaryLoadingStateClearsWhenDataIsPresent.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileViewSummaryLoadingStateClearsWhenEmpty.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileViewSummaryLoadingStateClearsWhenEmpty.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileViewSummaryPlaceholderCanUpdateColors.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileSummaryViewTests/testProfileViewSummaryPlaceholderCanUpdateColors.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testInitiallyEmptyProfileView.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testInitiallyEmptyProfileView.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testInitiallyEmptyProfileView.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testInitiallyEmptyProfileView.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileView.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileView.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileView.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileView.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarImageViewSubviewCustomStyle.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarImageViewSubviewCustomStyle.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarImageViewWrapper.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarImageViewWrapper.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarView.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarView.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarViewImageViewSubview.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewCustomAvatarViewImageViewSubview.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewEmptyState.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewEmptyState.dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewEmptyState.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewEmptyState.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewEmptyStateCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewEmptyStateCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewLoadingStateClearsWhenDataIsPresent.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewLoadingStateClearsWhenDataIsPresent.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewLoadingStateClearsWhenEmpty.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewLoadingStateClearsWhenEmpty.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholderCanUpdateColors.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholderCanUpdateColors.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanHide.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanHide.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanHideCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanHideCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanShow.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanShow.light.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanShowCustomPalette.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProfileViewTests/testProfileViewPlaceholdersCanShowCustomPalette.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileSummaryView.view-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileSummaryView.view-dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileSummaryView.view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileSummaryView.view.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileView.view-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileView.view-dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileView.view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/largeProfileView.view.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileSummaryView.view-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileSummaryView.view-dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileSummaryView.view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileSummaryView.view.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileView.view-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileView.view-dark.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileView.view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/ProvileViewSnapshots/profileView.view.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeView.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeView.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeView.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeView.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithError.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithError.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithError.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithError.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithoutToken.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithoutToken.1.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithoutToken.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/QuickEditorNoticeViewTests/testQuickEditorNoticeViewWithoutToken.2.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testAccountButtonsPlaceholderDisplayer.placeholder-shown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testAccountButtonsPlaceholderDisplayer.placeholder-shown.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testBackgroundColorPlaceholderDisplayer.placeholder-hidden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testBackgroundColorPlaceholderDisplayer.placeholder-hidden.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testBackgroundColorPlaceholderDisplayer.placeholder-shown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testBackgroundColorPlaceholderDisplayer.placeholder-shown.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testProfileButtonPlaceholderDisplayer.placeholder-hidden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testProfileButtonPlaceholderDisplayer.placeholder-hidden.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testProfileButtonPlaceholderDisplayer.placeholder-shown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testProfileButtonPlaceholderDisplayer.placeholder-shown.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testRectangularColorPlaceholderDisplayer.placeholder-hidden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testRectangularColorPlaceholderDisplayer.placeholder-hidden.png -------------------------------------------------------------------------------- /Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testRectangularColorPlaceholderDisplayer.placeholder-shown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-iOS/71350ef7274a989ca2ed89c512f8b9dd99bc8c3d/Tests/GravatarUITests/__Snapshots__/TestPlaceholderDisplayers/testRectangularColorPlaceholderDisplayer.placeholder-shown.png -------------------------------------------------------------------------------- /fastlane/example.env: -------------------------------------------------------------------------------- 1 | # You don't need to fill in these values for Fastlane to run. 2 | # 3 | # However, if a lane requires some of these environment values and they are not set here, it will fail. 4 | MATCH_S3_ACCESS_KEY= 5 | MATCH_S3_SECRET_ACCESS_KEY= 6 | -------------------------------------------------------------------------------- /openapi/GravatarOpenAPIClient/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | Sources/GravatarOpenAPIClient/Models/AssociatedResponse.swift 2 | Sources/GravatarOpenAPIClient/Models/Avatar.swift 3 | Sources/GravatarOpenAPIClient/Models/AvatarRating.swift 4 | Sources/GravatarOpenAPIClient/Models/CryptoWalletAddress.swift 5 | Sources/GravatarOpenAPIClient/Models/GalleryImage.swift 6 | Sources/GravatarOpenAPIClient/Models/Interest.swift 7 | Sources/GravatarOpenAPIClient/Models/Language.swift 8 | Sources/GravatarOpenAPIClient/Models/Link.swift 9 | Sources/GravatarOpenAPIClient/Models/ModelError.swift 10 | Sources/GravatarOpenAPIClient/Models/Profile.swift 11 | Sources/GravatarOpenAPIClient/Models/ProfileContactInfo.swift 12 | Sources/GravatarOpenAPIClient/Models/ProfilePayments.swift 13 | Sources/GravatarOpenAPIClient/Models/SetEmailAvatarRequest.swift 14 | Sources/GravatarOpenAPIClient/Models/UpdateAvatarRequest.swift 15 | Sources/GravatarOpenAPIClient/Models/UpdateProfileRequest.swift 16 | Sources/GravatarOpenAPIClient/Models/VerifiedAccount.swift 17 | -------------------------------------------------------------------------------- /openapi/GravatarOpenAPIClient/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 7.5.0 2 | -------------------------------------------------------------------------------- /openapi/templates/model.mustache: -------------------------------------------------------------------------------- 1 | {{#models}}{{#model}}// 2 | // {{classname}}.swift 3 | // 4 | // Generated by openapi-generator 5 | // https://openapi-generator.tech 6 | // 7 | 8 | import Foundation 9 | {{#useVapor}} 10 | import Vapor{{/useVapor}} 11 | {{#swiftUseApiNamespace}} 12 | 13 | @available(*, deprecated, renamed: "{{projectName}}API.{{classname}}") 14 | {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} typealias {{classname}} = {{projectName}}API.{{classname}} 15 | 16 | extension {{projectName}}API { 17 | {{/swiftUseApiNamespace}} 18 | {{#description}}/// {{{.}}} 19 | ///{{/description}}{{#isDeprecated}} 20 | @available(*, deprecated, message: "This schema is deprecated."){{/isDeprecated}}{{#vendorExtensions.x-is-one-of-interface}} 21 | {{> modelOneOf}}{{/vendorExtensions.x-is-one-of-interface}}{{^vendorExtensions.x-is-one-of-interface}}{{#isArray}} 22 | {{> modelArray}}{{/isArray}}{{^isArray}}{{#isEnum}} 23 | {{> modelEnum}}{{/isEnum}}{{^isEnum}} 24 | {{> modelObject}}{{/isEnum}}{{/isArray}}{{/vendorExtensions.x-is-one-of-interface}}{{/model}}{{/models}} 25 | {{#swiftUseApiNamespace}} 26 | } 27 | {{/swiftUseApiNamespace}} 28 | -------------------------------------------------------------------------------- /openapi/templates/modelInlineEnumDeclaration.mustache: -------------------------------------------------------------------------------- 1 | {{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} enum {{enumName}}: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}String{{/isContainer}}, {{#useVapor}}Content, Hashable{{/useVapor}}{{^useVapor}}Codable{{^isContainer}}{{^isString}}{{^isInteger}}{{^isFloat}}{{^isDouble}}, JSONEncodable{{/isDouble}}{{/isFloat}}{{/isInteger}}{{/isString}}{{/isContainer}}{{/useVapor}}, CaseIterable{{#enumUnknownDefaultCase}}{{#isInteger}}, CaseIterableDefaultsLast{{/isInteger}}{{#isFloat}}, CaseIterableDefaultsLast{{/isFloat}}{{#isDouble}}, CaseIterableDefaultsLast{{/isDouble}}{{#isString}}, CaseIterableDefaultsLast{{/isString}}{{#isContainer}}, CaseIterableDefaultsLast{{/isContainer}}{{/enumUnknownDefaultCase}}, Sendable { 2 | {{#allowableValues}} 3 | {{#enumVars}} 4 | case {{{name}}} = {{{value}}} 5 | {{/enumVars}} 6 | {{/allowableValues}} 7 | } 8 | -------------------------------------------------------------------------------- /version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Gravatar 4 | VERSION = '3.4.0-rc.1' 5 | SWIFT_VERSIONS = [ 6 | '5.10' 7 | ].freeze 8 | end 9 | --------------------------------------------------------------------------------