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.
Files changed (36) hide show
  1. package/HUBSPOT_IOS_SDK_VERSION.json +5 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION.md +19 -0
  4. package/README.md +119 -0
  5. package/ReactNativeHubspotWrapper.podspec +27 -0
  6. package/android/build.gradle +49 -0
  7. package/android/src/main/AndroidManifest.xml +1 -0
  8. package/android/src/main/java/com/reactnativehubspotwrapper/HubspotWrapperModule.kt +83 -0
  9. package/android/src/main/java/com/reactnativehubspotwrapper/HubspotWrapperPackage.kt +32 -0
  10. package/ios/HubspotMobileSDK/API/APIModels.swift +50 -0
  11. package/ios/HubspotMobileSDK/API/HubspotAPI.swift +168 -0
  12. package/ios/HubspotMobileSDK/DeviceTokenSyncState.swift +13 -0
  13. package/ios/HubspotMobileSDK/HubspotConfig.swift +145 -0
  14. package/ios/HubspotMobileSDK/HubspotManager+Notifications.swift +178 -0
  15. package/ios/HubspotMobileSDK/HubspotManager+Properties.swift +53 -0
  16. package/ios/HubspotMobileSDK/HubspotManager.swift +548 -0
  17. package/ios/HubspotMobileSDK/HubspotMobileSDK.swift +7 -0
  18. package/ios/HubspotMobileSDK/HubspotUserProperties.swift +115 -0
  19. package/ios/HubspotMobileSDK/LICENSE.txt +19 -0
  20. package/ios/HubspotMobileSDK/PushNotificationChatData.swift +63 -0
  21. package/ios/HubspotMobileSDK/Resources/Images.xcassets/Contents.json +6 -0
  22. package/ios/HubspotMobileSDK/Resources/Images.xcassets/GenericChatIcon.imageset/Contents.json +16 -0
  23. package/ios/HubspotMobileSDK/Resources/Images.xcassets/GenericChatIcon.imageset/chat-open-svg.svg +1 -0
  24. package/ios/HubspotMobileSDK/Resources/Localizable.xcstrings +28 -0
  25. package/ios/HubspotMobileSDK/Resources/PrivacyInfo.xcprivacy +62 -0
  26. package/ios/HubspotMobileSDK/Views/Buttons/FloatingActionButton.swift +126 -0
  27. package/ios/HubspotMobileSDK/Views/Buttons/TextChatButtonChatButton.swift +78 -0
  28. package/ios/HubspotMobileSDK/Views/ChatView/HubspotChatView.swift +612 -0
  29. package/ios/HubspotWrapperImpl.swift +108 -0
  30. package/ios/RNHubspotWrapper.h +9 -0
  31. package/ios/RNHubspotWrapper.mm +66 -0
  32. package/package.json +55 -0
  33. package/react-native.config.js +11 -0
  34. package/scripts/update-hubspot-ios-sdk.sh +142 -0
  35. package/src/index.ts +41 -0
  36. 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,6 @@
1
+ {
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+ }
@@ -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
+ }
@@ -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
+ /// ![Demo screenshot](floating-button-example-a)
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
+ }