react-native-hubspot-wrapper 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HUBSPOT_IOS_SDK_VERSION.json +5 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +19 -0
- package/README.md +119 -0
- package/ReactNativeHubspotWrapper.podspec +27 -0
- package/android/build.gradle +49 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/reactnativehubspotwrapper/HubspotWrapperModule.kt +83 -0
- package/android/src/main/java/com/reactnativehubspotwrapper/HubspotWrapperPackage.kt +32 -0
- package/ios/HubspotMobileSDK/API/APIModels.swift +50 -0
- package/ios/HubspotMobileSDK/API/HubspotAPI.swift +168 -0
- package/ios/HubspotMobileSDK/DeviceTokenSyncState.swift +13 -0
- package/ios/HubspotMobileSDK/HubspotConfig.swift +145 -0
- package/ios/HubspotMobileSDK/HubspotManager+Notifications.swift +178 -0
- package/ios/HubspotMobileSDK/HubspotManager+Properties.swift +53 -0
- package/ios/HubspotMobileSDK/HubspotManager.swift +548 -0
- package/ios/HubspotMobileSDK/HubspotMobileSDK.swift +7 -0
- package/ios/HubspotMobileSDK/HubspotUserProperties.swift +115 -0
- package/ios/HubspotMobileSDK/LICENSE.txt +19 -0
- package/ios/HubspotMobileSDK/PushNotificationChatData.swift +63 -0
- package/ios/HubspotMobileSDK/Resources/Images.xcassets/Contents.json +6 -0
- package/ios/HubspotMobileSDK/Resources/Images.xcassets/GenericChatIcon.imageset/Contents.json +16 -0
- package/ios/HubspotMobileSDK/Resources/Images.xcassets/GenericChatIcon.imageset/chat-open-svg.svg +1 -0
- package/ios/HubspotMobileSDK/Resources/Localizable.xcstrings +28 -0
- package/ios/HubspotMobileSDK/Resources/PrivacyInfo.xcprivacy +62 -0
- package/ios/HubspotMobileSDK/Views/Buttons/FloatingActionButton.swift +126 -0
- package/ios/HubspotMobileSDK/Views/Buttons/TextChatButtonChatButton.swift +78 -0
- package/ios/HubspotMobileSDK/Views/ChatView/HubspotChatView.swift +612 -0
- package/ios/HubspotWrapperImpl.swift +108 -0
- package/ios/RNHubspotWrapper.h +9 -0
- package/ios/RNHubspotWrapper.mm +66 -0
- package/package.json +55 -0
- package/react-native.config.js +11 -0
- package/scripts/update-hubspot-ios-sdk.sh +142 -0
- package/src/index.ts +41 -0
- package/src/specs/NativeHubspotWrapper.ts +17 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2024 Hubspot, Inc
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// PushNotificationChatData.swift
|
|
2
|
+
// Hubspot Mobile SDK
|
|
3
|
+
//
|
|
4
|
+
// Copyright © 2024 Hubspot, Inc.
|
|
5
|
+
|
|
6
|
+
import Foundation
|
|
7
|
+
import UserNotifications
|
|
8
|
+
|
|
9
|
+
/// Holds all the data that is hubspot specific, extracted from a push notification collection. A convenient way to bundle all the known parameters together for passing along from notification delegate to the chat view
|
|
10
|
+
///
|
|
11
|
+
/// Push messages contain additional keys like so:
|
|
12
|
+
///
|
|
13
|
+
/// ```json
|
|
14
|
+
/// "hsPortalId": "abc123",
|
|
15
|
+
/// "hsChatflowId": "id",
|
|
16
|
+
/// "hsThreadId": "threadId",
|
|
17
|
+
/// "hsChatflowParam": "sales"
|
|
18
|
+
/// ```
|
|
19
|
+
/// These keys are also defined as constants here, ``chatflowKey`` , ``chatflowIdKey``, ``threadIdKey`` , ``portalIdKey``
|
|
20
|
+
///
|
|
21
|
+
/// The presence of any of these keys is use to indicate that the push message is for a hubspot chat. They are used by the helper method ``HubspotManager/isHubspotNotification(notification:)`` or ``HubspotManager/isHubspotNotification(notificationData:)``
|
|
22
|
+
///
|
|
23
|
+
public struct PushNotificationChatData: Sendable {
|
|
24
|
+
|
|
25
|
+
/// Push messages contain the portal id in the payload, with the key `hsPortalId`
|
|
26
|
+
public static let portalIdKey = "hsPortalId"
|
|
27
|
+
|
|
28
|
+
/// Push messages contain the chat flow id in the payload, with the key `hsChatflowId`
|
|
29
|
+
public static let chatflowIdKey = "hsChatflowId"
|
|
30
|
+
|
|
31
|
+
/// Push messages contain the thread id in the payload, with the key `hsThreadId'`
|
|
32
|
+
public static let threadIdKey = "hsThreadId"
|
|
33
|
+
|
|
34
|
+
/// Push messages contain the chat flow name in the payload, with the key `hsChatflowParam`
|
|
35
|
+
public static let chatflowKey = "hsChatflowParam"
|
|
36
|
+
|
|
37
|
+
/// The portal id, if present. Can be used for validation.
|
|
38
|
+
public let portalId: String?
|
|
39
|
+
/// The chatflow id , if present. unused currently.
|
|
40
|
+
public let chatflowId: String?
|
|
41
|
+
/// The thread id, if present - not currently used in the embedded chat.
|
|
42
|
+
public let threadId: String?
|
|
43
|
+
/// The chatflow to open when handling the notification
|
|
44
|
+
public let chatflow: String?
|
|
45
|
+
|
|
46
|
+
/// Create instance using the notification if any key is present, or returns nil when no keys are present
|
|
47
|
+
public init?(notification: UNNotification) {
|
|
48
|
+
self.init(notificationData: notification.request.content.userInfo)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Create instance using the user info dictionary if any key is present, or returns nil when no keys are present
|
|
52
|
+
public init?(notificationData: [AnyHashable: Any]) {
|
|
53
|
+
portalId = notificationData[Self.portalIdKey] as? String
|
|
54
|
+
chatflow = notificationData[Self.chatflowKey] as? String
|
|
55
|
+
chatflowId = notificationData[Self.chatflowIdKey] as? String
|
|
56
|
+
threadId = notificationData[Self.threadIdKey] as? String
|
|
57
|
+
|
|
58
|
+
// We want at least one key to be set, otherwise return nil
|
|
59
|
+
if portalId == nil, chatflow == nil, chatflowId == nil, threadId == nil {
|
|
60
|
+
return nil
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images" : [
|
|
3
|
+
{
|
|
4
|
+
"filename" : "chat-open-svg.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
|
+
}
|
package/ios/HubspotMobileSDK/Resources/Images.xcassets/GenericChatIcon.imageset/chat-open-svg.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="30" viewBox="0 0 39 37" class="conversations-visitor-open-icon"><defs><path id="conversations-visitor-open-icon-path-1:r0:" d="M31.4824242 24.6256121L31.4824242 0.501369697 0.476266667 0.501369697 0.476266667 24.6256121z"></path></defs><g fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g transform="translate(-1432 -977) translate(1415.723 959.455)"><g transform="translate(17 17)"><g transform="translate(6.333 .075)"><path fill="#ffffff" d="M30.594 4.773c-.314-1.943-1.486-3.113-3.374-3.38C27.174 1.382 22.576.5 15.36.5c-7.214 0-11.812.882-11.843.889-1.477.21-2.507.967-3.042 2.192a83.103 83.103 0 019.312-.503c6.994 0 11.647.804 12.33.93 3.079.462 5.22 2.598 5.738 5.728.224 1.02.932 4.606.932 8.887 0 2.292-.206 4.395-.428 6.002 1.22-.516 1.988-1.55 2.23-3.044.008-.037.893-3.814.893-8.415 0-4.6-.885-8.377-.89-8.394"></path></g><g fill="#ffffff" transform="translate(0 5.832)"><path d="M31.354 4.473c-.314-1.944-1.487-3.114-3.374-3.382-.046-.01-4.644-.89-11.859-.89-7.214 0-11.813.88-11.843.888-1.903.27-3.075 1.44-3.384 3.363C.884 4.489 0 8.266 0 12.867c0 4.6.884 8.377.889 8.393.314 1.944 1.486 3.114 3.374 3.382.037.007 3.02.578 7.933.801l2.928 5.072a1.151 1.151 0 001.994 0l2.929-5.071c4.913-.224 7.893-.794 7.918-.8 1.902-.27 3.075-1.44 3.384-3.363.01-.037.893-3.814.893-8.414 0-4.601-.884-8.378-.888-8.394"></path></g></g></g></g></svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sourceLanguage" : "en",
|
|
3
|
+
"strings" : {
|
|
4
|
+
"%@" : {
|
|
5
|
+
|
|
6
|
+
},
|
|
7
|
+
"chat.label" : {
|
|
8
|
+
"localizations" : {
|
|
9
|
+
"en" : {
|
|
10
|
+
"stringUnit" : {
|
|
11
|
+
"state" : "translated",
|
|
12
|
+
"value" : "Start Chat"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"Failed to load chat" : {
|
|
18
|
+
|
|
19
|
+
},
|
|
20
|
+
"Missing Chat Flow" : {
|
|
21
|
+
|
|
22
|
+
},
|
|
23
|
+
"Missing Configuration" : {
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"version" : "1.0"
|
|
28
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
6
|
+
<array/>
|
|
7
|
+
<key>NSPrivacyTracking</key>
|
|
8
|
+
<false/>
|
|
9
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
10
|
+
<array>
|
|
11
|
+
<dict>
|
|
12
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
13
|
+
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
|
|
14
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
15
|
+
<true/>
|
|
16
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
17
|
+
<false/>
|
|
18
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
19
|
+
<array>
|
|
20
|
+
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
21
|
+
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
|
22
|
+
</array>
|
|
23
|
+
</dict>
|
|
24
|
+
<dict>
|
|
25
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
26
|
+
<string>NSPrivacyCollectedDataTypeUserID</string>
|
|
27
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
28
|
+
<true/>
|
|
29
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
30
|
+
<false/>
|
|
31
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
32
|
+
<array>
|
|
33
|
+
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
|
34
|
+
</array>
|
|
35
|
+
</dict>
|
|
36
|
+
<dict>
|
|
37
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
38
|
+
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
|
|
39
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
40
|
+
<true/>
|
|
41
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
42
|
+
<false/>
|
|
43
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
44
|
+
<array>
|
|
45
|
+
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
|
46
|
+
</array>
|
|
47
|
+
</dict>
|
|
48
|
+
<dict>
|
|
49
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
50
|
+
<string>NSPrivacyCollectedDataTypeName</string>
|
|
51
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
52
|
+
<true/>
|
|
53
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
54
|
+
<false/>
|
|
55
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
56
|
+
<array>
|
|
57
|
+
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
|
58
|
+
</array>
|
|
59
|
+
</dict>
|
|
60
|
+
</array>
|
|
61
|
+
</dict>
|
|
62
|
+
</plist>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// FloatingActionButton.swift
|
|
2
|
+
// Hubspot Mobile SDK
|
|
3
|
+
//
|
|
4
|
+
// Copyright © 2024 Hubspot, Inc.
|
|
5
|
+
|
|
6
|
+
import SwiftUI
|
|
7
|
+
|
|
8
|
+
/// This is a pre-configured button for opening a chat session. The button manages its own state and presents a ``HubspotChatView`` as a sheet type modal.
|
|
9
|
+
///
|
|
10
|
+
/// It can be placed anywhere that suits your application, but its ideally intended to be floating in a corner over your main content. You can overlay it however you like, for example using a z-stack or an overlay modifier like so:
|
|
11
|
+
/// ```swift
|
|
12
|
+
/// content.overlay(alignment: .bottomTrailing, content: {
|
|
13
|
+
/// FloatingActionButton()
|
|
14
|
+
/// .padding()
|
|
15
|
+
/// })
|
|
16
|
+
/// ```
|
|
17
|
+
///
|
|
18
|
+
/// If you want to use a specific chat flow for this part of the application , for example a flow specific to an account section, or support section, you can provide a chat flow id for the button to open:
|
|
19
|
+
///
|
|
20
|
+
/// ```swift
|
|
21
|
+
/// content.overlay(alignment: .bottomLeading) {
|
|
22
|
+
/// FloatingActionButton(chatFlow: "marketing")
|
|
23
|
+
/// .tint(.yellow)
|
|
24
|
+
/// .padding()
|
|
25
|
+
/// }
|
|
26
|
+
/// ```
|
|
27
|
+
///
|
|
28
|
+
/// This will overlay the button like this:
|
|
29
|
+
/// 
|
|
30
|
+
///
|
|
31
|
+
/// You can also add the button using the ``overlayHubspotFloatingActionButton(manager:chatFlow:)`` view modifier
|
|
32
|
+
///
|
|
33
|
+
/// The background color can be customed by setting the accent / tint colour on the view
|
|
34
|
+
public struct FloatingActionButton: View {
|
|
35
|
+
private let manager: HubspotManager
|
|
36
|
+
private let chatFlow: String?
|
|
37
|
+
|
|
38
|
+
@State var showingChat: Bool = false
|
|
39
|
+
|
|
40
|
+
/// Create the button, optionally specifying the chatflow or manager to use.
|
|
41
|
+
/// - Parameters:
|
|
42
|
+
/// - manager: The manager to use for getting a chat session. By defautl the shared manager is used.
|
|
43
|
+
/// - chatFlow: The specific chat flow to open. Optional.
|
|
44
|
+
public init(
|
|
45
|
+
manager: HubspotManager? = nil,
|
|
46
|
+
chatFlow: String? = nil
|
|
47
|
+
) {
|
|
48
|
+
self.manager = manager ?? HubspotManager.shared
|
|
49
|
+
self.chatFlow = chatFlow
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public var body: some View {
|
|
53
|
+
Button(
|
|
54
|
+
action: showChat,
|
|
55
|
+
label: {
|
|
56
|
+
Image.hubspotChat
|
|
57
|
+
.foregroundColor(.white)
|
|
58
|
+
.padding()
|
|
59
|
+
.background(
|
|
60
|
+
Circle()
|
|
61
|
+
.fill()
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
.sheet(
|
|
66
|
+
isPresented: $showingChat,
|
|
67
|
+
content: {
|
|
68
|
+
HubspotChatView(manager: manager, chatFlow: chatFlow)
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
.onAppear {
|
|
72
|
+
manager.prepareForPotentialChat()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
func showChat() {
|
|
77
|
+
withAnimation {
|
|
78
|
+
showingChat = true
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Convenient helper for overlaying the action button in the bottom right of a view, with default padding. You can use this directly, but its intended instead to be used with ``overlayHubspotFloatingActionButton(manager:chatFlow:)``
|
|
84
|
+
struct FloatingActionButtonOverlayModifier: ViewModifier {
|
|
85
|
+
let manager: HubspotManager
|
|
86
|
+
let chatFlow: String?
|
|
87
|
+
|
|
88
|
+
init(manager: HubspotManager? = nil, chatFlow: String? = nil) {
|
|
89
|
+
self.manager = manager ?? HubspotManager.shared
|
|
90
|
+
self.chatFlow = chatFlow
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func body(content: Content) -> some View {
|
|
94
|
+
content.overlay(
|
|
95
|
+
alignment: .bottomTrailing,
|
|
96
|
+
content: {
|
|
97
|
+
FloatingActionButton(manager: manager, chatFlow: chatFlow)
|
|
98
|
+
.padding()
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
extension View {
|
|
105
|
+
/// Convenience to overlay a floating action button - call on your main content view to overlay button at bottom trailing position with default padding. Set the `chatFlow` property to use a specific flow, otherwise the default flow from your configuration file will be used.
|
|
106
|
+
/// - Parameters:
|
|
107
|
+
/// - manager: The hubspot manager to use
|
|
108
|
+
/// - chatFlow: the chat flow targeting parameter to use
|
|
109
|
+
public func overlayHubspotFloatingActionButton(manager: HubspotManager? = nil, chatFlow: String? = nil) -> some View {
|
|
110
|
+
modifier(FloatingActionButtonOverlayModifier(manager: manager, chatFlow: chatFlow))
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
struct ButtonPreviewProvider: PreviewProvider {
|
|
115
|
+
static var previews: some View {
|
|
116
|
+
FloatingActionButton()
|
|
117
|
+
|
|
118
|
+
HStack {
|
|
119
|
+
FloatingActionButton()
|
|
120
|
+
FloatingActionButton()
|
|
121
|
+
.tint(.orange)
|
|
122
|
+
}
|
|
123
|
+
.previewLayout(.sizeThatFits)
|
|
124
|
+
.previewDisplayName("Colors")
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// TextChatButtonChatButton.swift
|
|
2
|
+
// Hubspot Mobile SDK
|
|
3
|
+
//
|
|
4
|
+
// Copyright © 2024 Hubspot, Inc.
|
|
5
|
+
|
|
6
|
+
import SwiftUI
|
|
7
|
+
|
|
8
|
+
/// This is another button what displays an icon as well as text for starting a chat. Similar to ``FloatingActionButton`` , it can be placed anywhere in your UI that makes sense for you, and it will present the chat view modally with a `.sheet` modifier
|
|
9
|
+
///
|
|
10
|
+
///
|
|
11
|
+
///
|
|
12
|
+
/// Warning: This UI element was created for another proof of concept - it may or may not be included in the final release.
|
|
13
|
+
///
|
|
14
|
+
public struct TextChatButton: View {
|
|
15
|
+
private let customText: LocalizedStringKey?
|
|
16
|
+
private let manager: HubspotManager
|
|
17
|
+
private let chatFlow: String?
|
|
18
|
+
|
|
19
|
+
@State var showingChat: Bool = false
|
|
20
|
+
|
|
21
|
+
/// Create button, optionally specifying the manager to use
|
|
22
|
+
///
|
|
23
|
+
/// - Parameters:
|
|
24
|
+
/// - text: The text in the button - if nil, default text is used.
|
|
25
|
+
/// - manager: The manager to use for getting a chat session. By defautl the shared manager is used.
|
|
26
|
+
/// - chatFlow: The specific chat flow to open. Optional.
|
|
27
|
+
public init(text: LocalizedStringKey? = nil, manager: HubspotManager? = nil, chatFlow: String? = nil) {
|
|
28
|
+
customText = text
|
|
29
|
+
self.manager = manager ?? .shared
|
|
30
|
+
self.chatFlow = chatFlow
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public var body: some View {
|
|
34
|
+
Button(
|
|
35
|
+
action: {
|
|
36
|
+
withAnimation {
|
|
37
|
+
showingChat = true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
label: {
|
|
41
|
+
HStack {
|
|
42
|
+
Image.hubspotChat
|
|
43
|
+
if let customText {
|
|
44
|
+
Text(customText)
|
|
45
|
+
} else {
|
|
46
|
+
Text("chat.label", bundle: .hubspotResources)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
.labelStyle(.titleAndIcon)
|
|
53
|
+
.buttonStyle(TextChatButtonStyle())
|
|
54
|
+
.sheet(
|
|
55
|
+
isPresented: $showingChat,
|
|
56
|
+
content: {
|
|
57
|
+
HubspotChatView(manager: manager, chatFlow: chatFlow)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private struct TextChatButtonStyle: ButtonStyle {
|
|
63
|
+
func makeBody(configuration: Configuration) -> some View {
|
|
64
|
+
configuration.label
|
|
65
|
+
.padding(.horizontal)
|
|
66
|
+
.frame(minHeight: 44)
|
|
67
|
+
.frame(maxWidth: .infinity)
|
|
68
|
+
.foregroundColor(.white)
|
|
69
|
+
.background(
|
|
70
|
+
RoundedRectangle(cornerRadius: 8)
|
|
71
|
+
.fill(.tint)
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#Preview {
|
|
77
|
+
TextChatButton().fixedSize(horizontal: true, vertical: false)
|
|
78
|
+
}
|