Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Sources/FormbricksSDK/Manager/PresentSurveyManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ final class PresentSurveyManager {
private weak var viewController: UIViewController?

/// Present the webview
func present(environmentResponse: EnvironmentResponse, id: String, completion: ((Bool) -> Void)? = nil) {
func present(environmentResponse: EnvironmentResponse, id: String, overlay: SurveyOverlay = .none, completion: ((Bool) -> Void)? = nil) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
if let window = UIApplication.safeKeyWindow {
let view = FormbricksView(viewModel: FormbricksViewModel(environmentResponse: environmentResponse, surveyId: id))
let view = FormbricksView(viewModel: FormbricksViewModel(environmentResponse: environmentResponse, surveyId: id))
let vc = UIHostingController(rootView: view)
vc.modalPresentationStyle = .overCurrentContext
vc.view.backgroundColor = UIColor.gray.withAlphaComponent(0.6)
vc.view.backgroundColor = Self.backgroundColor(for: overlay)
if let presentationController = vc.presentationController as? UISheetPresentationController {
presentationController.detents = [.large()]
}
Expand All @@ -34,6 +34,18 @@ final class PresentSurveyManager {
}
}

/// Returns the appropriate background color for the given overlay style.
private static func backgroundColor(for overlay: SurveyOverlay) -> UIColor {
switch overlay {
case .dark:
return UIColor(white: 0.2, alpha: 0.6)
case .light:
return UIColor(white: 0.6, alpha: 0.4)
case .none:
return .clear
}
}

/// Dismiss the webview
func dismissView() {
viewController?.dismiss(animated: true)
Expand Down
17 changes: 14 additions & 3 deletions Sources/FormbricksSDK/Manager/SurveyManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ final class SurveyManager {
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout)) { [weak self] in
guard let self = self else { return }
if let environmentResponse = self.environmentResponse {
self.presentSurveyManager.present(environmentResponse: environmentResponse, id: survey.id) { success in
let overlay = self.resolveOverlay(for: survey)
self.presentSurveyManager.present(environmentResponse: environmentResponse, id: survey.id, overlay: overlay) { success in
if !success {
self.isShowingSurvey = false
}
Expand Down Expand Up @@ -189,9 +190,10 @@ private extension SurveyManager {
/// The view controller is presented over the current context.
func showSurvey(withId id: String) {
if let environmentResponse = environmentResponse {
presentSurveyManager.present(environmentResponse: environmentResponse, id: id)
let survey = environmentResponse.data.data.surveys?.first(where: { $0.id == id })
let overlay = resolveOverlay(for: survey)
presentSurveyManager.present(environmentResponse: environmentResponse, id: id, overlay: overlay)
}

}

/// Starts a timer to refresh the environment state after the given timeout (`expiresAt`).
Expand Down Expand Up @@ -345,6 +347,15 @@ extension SurveyManager {
return entry.language.code
}

/// Resolves the overlay style for the given survey, falling back to the project-level default.
/// Survey-level `projectOverwrites.overlay` takes precedence over `project.overlay`.
func resolveOverlay(for survey: Survey?) -> SurveyOverlay {
if let surveyOverlay = survey?.projectOverwrites?.overlay {
return surveyOverlay
}
return environmentResponse?.data.data.project.overlay ?? .none
}

/// Filters the surveys based on the user's segments.
func filterSurveysBasedOnSegments(_ surveys: [Survey], segments: [String]) -> [Survey] {
return surveys.filter { survey in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ struct Project: Codable {
let id: String?
let recontactDays: Int?
let clickOutsideClose: Bool?
let darkOverlay: Bool?
let overlay: SurveyOverlay?
let placement: String?
let inAppSurveyBranding: Bool?
let styling: Styling?
Expand Down
9 changes: 8 additions & 1 deletion Sources/FormbricksSDK/Model/Environment/Survey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,19 @@ enum Placement: String, Codable {
case center = "center"
}

/// Defines the overlay style displayed behind a survey modal.
enum SurveyOverlay: String, Codable {
case none = "none"
case light = "light"
case dark = "dark"
}

struct ProjectOverwrites: Codable {
let brandColor: String?
let highlightBorderColor: String?
let placement: Placement?
let clickOutsideClose: Bool?
let darkOverlay: Bool?
let overlay: SurveyOverlay?
}

struct Survey: Codable {
Expand Down
3 changes: 2 additions & 1 deletion Sources/FormbricksSDK/WebView/FormbricksViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ private class WebViewData {
data["placement"] = project.placement
}

data["darkOverlay"] = matchedSurvey?.projectOverwrites?.darkOverlay ?? project.darkOverlay
data["clickOutside"] = matchedSurvey?.projectOverwrites?.clickOutsideClose ?? project.clickOutsideClose ?? false
data["overlay"] = (matchedSurvey?.projectOverwrites?.overlay ?? project.overlay ?? .none).rawValue

let isMultiLangSurvey = (matchedSurvey?.languages?.count ?? 0) > 1

Expand Down
6 changes: 4 additions & 2 deletions Tests/FormbricksSDKTests/FormbricksSDKTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,10 @@ final class FormbricksSDKTests: XCTestCase {
return
}

// placement should come from survey.projectOverwrites (center), and darkOverlay true
// placement should come from survey.projectOverwrites (center), overlay should be "dark",
// and clickOutside should be false (from survey.projectOverwrites.clickOutsideClose)
XCTAssertEqual(object["placement"] as? String, "center")
XCTAssertEqual(object["darkOverlay"] as? Bool, true)
XCTAssertEqual(object["overlay"] as? String, "dark")
XCTAssertEqual(object["clickOutside"] as? Bool, false)
}
}
4 changes: 2 additions & 2 deletions Tests/FormbricksSDKTests/Mock/Environment.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
],
"project": {
"clickOutsideClose": true,
"darkOverlay": false,
"overlay": "none",
"id": "cm6ovvfnv0003sf0k7zi8r3ac",
"inAppSurveyBranding": true,
"placement": "bottomRight",
Expand Down Expand Up @@ -57,7 +57,7 @@
"name": "Start from scratch",
"projectOverwrites": {
"placement": "center",
"darkOverlay": true,
"overlay": "dark",
"clickOutsideClose": false,
"brandColor": "#ff0000",
"highlightBorderColor": "#00ff00"
Expand Down
Loading