@applicaster/quick-brick-native-apple 6.15.3 → 6.16.1
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/apple/QuickBrickApple.podspec.json +2 -2
- package/apple/ios/ReactNative/PushBridge.swift +2 -2
- package/apple/ios/ReactNative/QuickBrickViewController.swift +3 -3
- package/apple/tvos/Helpers/FocusableGroupManager/FocusableManagerModule.swift +8 -8
- package/apple/tvos/Helpers/FocusableGroupManager/GroupProxy.swift +128 -0
- package/apple/tvos/Helpers/FocusableGroupManager/GroupProxyManager.swift +101 -0
- package/apple/tvos/ReactNative/QuickBrickViewController.swift +2 -2
- package/apple/tvos/Views/FocusableGroupView/FocusableGroupView.swift +27 -45
- package/apple/tvos/Views/FocusableView/FocusableView.swift +44 -69
- package/apple/universal/ReactNative/AnalyticsBridge.swift +2 -2
- package/apple/universal/ReactNative/AppLoaderBridge.swift +14 -14
- package/apple/universal/ReactNative/EventBusBridge.swift +2 -2
- package/apple/universal/ReactNative/LocalNotification/LocalNotificationBridge.swift +4 -4
- package/apple/universal/ReactNative/OfflineAssetsBridge.swift +11 -11
- package/apple/universal/ReactNative/PluginsManagerBridge.swift +17 -17
- package/apple/universal/ReactNative/QuickBrickExceptionManagerDelegate.swift +2 -2
- package/apple/universal/ReactNative/ReactNativeCommunicationModule.swift +10 -10
- package/apple/universal/Storages/LocalStorage/LocalStorageBridge.swift +29 -29
- package/apple/universal/Storages/SessionStorage/SessionStorageBridge.swift +15 -15
- package/package.json +1 -1
- package/apple/tvos/Helpers/FocusableGroupManager/FocusableGroupManager+MovementDidFailNotification.swift +0 -181
- package/apple/tvos/Helpers/FocusableGroupManager/FocusableGroupManager.swift +0 -169
- package/apple/tvos/Helpers/FocusableGroupManager/InMemoryFocusableItemStorage.swift +0 -37
- package/apple/tvos/Helpers/FocusableGroupManager/InMemoryGroupStorage.swift +0 -24
- package/apple/tvos/Helpers/FocusableGroupManager/Protocols/FocusableGroupManagerStorages.swift +0 -19
- package/apple/tvos/Helpers/FocusableGroupManager/Protocols/UpdateService.swift +0 -35
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "QuickBrickApple",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.16.1",
|
|
4
4
|
"platforms": {
|
|
5
5
|
"ios": "16.0",
|
|
6
6
|
"tvos": "16.0"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"authors": "Applicaster LTD.",
|
|
17
17
|
"source": {
|
|
18
18
|
"git": "https://github.com/applicaster/Zapp-Frameworks.git",
|
|
19
|
-
"tag": "@@applicaster/quick-brick-native-apple/6.
|
|
19
|
+
"tag": "@@applicaster/quick-brick-native-apple/6.16.1"
|
|
20
20
|
},
|
|
21
21
|
"requires_arc": true,
|
|
22
22
|
"source_files": "universal/**/*.{m,swift}",
|
|
@@ -21,12 +21,12 @@ class PushBridge: NSObject, RCTBridgeModule {
|
|
|
21
21
|
"PushBridge"
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
class func requiresMainQueueSetup() -> Bool {
|
|
25
25
|
true
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/// prefered thread on which to run this native module
|
|
29
|
-
@objc
|
|
29
|
+
@objc var methodQueue: DispatchQueue {
|
|
30
30
|
DispatchQueue.main
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -20,7 +20,7 @@ class QuickBrickViewController: UIViewController, UILayerViewControllerProtocol
|
|
|
20
20
|
|
|
21
21
|
var orientationMask: UIInterfaceOrientationMask = QuickBrickViewController.initialOrientationMask
|
|
22
22
|
|
|
23
|
-
override
|
|
23
|
+
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
|
24
24
|
orientationMask
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ class QuickBrickViewController: UIViewController, UILayerViewControllerProtocol
|
|
|
28
28
|
homeIndicatorAutoHidden
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
override
|
|
31
|
+
override var shouldAutorotate: Bool {
|
|
32
32
|
true
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -64,7 +64,7 @@ class QuickBrickViewController: UIViewController, UILayerViewControllerProtocol
|
|
|
64
64
|
UIDevice.current.orientation
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
func allowOrientationForScreen(_ orientation: ReactNativeOrientation) {
|
|
68
68
|
orientationMask = orientation.toInterfaceOrientationMask()
|
|
69
69
|
forceOrientationWithMaskIfNeeded(orientationMask)
|
|
70
70
|
}
|
|
@@ -44,8 +44,8 @@ class FocusableManagerModule: NSObject, RCTBridgeModule {
|
|
|
44
44
|
let delay = DispatchTime.now() + DispatchTimeInterval.milliseconds(delayTimer)
|
|
45
45
|
|
|
46
46
|
DispatchQueue.main.asyncAfter(deadline: delay) {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
GroupProxyManager.shared.updateFocus(groupId: groupId,
|
|
48
|
+
itemId: itemId)
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -60,13 +60,13 @@ class FocusableManagerModule: NSObject, RCTBridgeModule {
|
|
|
60
60
|
let delay = DispatchTime.now() + DispatchTimeInterval.milliseconds(delayTimer)
|
|
61
61
|
|
|
62
62
|
DispatchQueue.main.asyncAfter(deadline: delay) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
GroupProxyManager.shared.updateFocus(groupId: groupId,
|
|
64
|
+
itemId: itemId,
|
|
65
|
+
needsForceUpdate: true,
|
|
66
|
+
completion: { succeed in
|
|
67
|
+
callback?([succeed])
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
})
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
//
|
|
2
|
+
// GroupProxy.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Anton Kononenko on 12/19/24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import XrayLogger
|
|
10
|
+
|
|
11
|
+
class GroupProxy {
|
|
12
|
+
let groupId: String
|
|
13
|
+
private weak var group: FocusableGroupView?
|
|
14
|
+
private var children: [WeakBox<FocusableView>] = []
|
|
15
|
+
private let lock = NSLock()
|
|
16
|
+
|
|
17
|
+
var delayedPreferredFocus: FocusableView?
|
|
18
|
+
|
|
19
|
+
private lazy var logger = Logger.getLogger(for: "QuickBrickApple/FocusableGroupManager/GroupProxy")
|
|
20
|
+
|
|
21
|
+
init(groupId: String) {
|
|
22
|
+
self.groupId = groupId
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// MARK: - Child Management
|
|
26
|
+
|
|
27
|
+
func addChild(_ view: FocusableView) {
|
|
28
|
+
lock.lock()
|
|
29
|
+
children.append(WeakBox(view))
|
|
30
|
+
lock.unlock()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func removeChild(_ view: FocusableView) {
|
|
34
|
+
lock.lock()
|
|
35
|
+
defer { lock.unlock() }
|
|
36
|
+
|
|
37
|
+
children.removeAll { $0.value === view }
|
|
38
|
+
|
|
39
|
+
if children.isEmpty, group == nil {
|
|
40
|
+
GroupProxyManager.shared.removeProxy(forGroupId: groupId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if delayedPreferredFocus === view {
|
|
44
|
+
delayedPreferredFocus = nil
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// MARK: - Group Binding
|
|
49
|
+
|
|
50
|
+
@MainActor func bindGroup(_ group: FocusableGroupView) {
|
|
51
|
+
// Check for duplicate group binding
|
|
52
|
+
if let existingGroup = self.group {
|
|
53
|
+
logger?.warningLog(message: """
|
|
54
|
+
⚠️ Duplicate group binding detected for groupId: '\(groupId)'
|
|
55
|
+
Previous group: \(existingGroup.debugDescription)
|
|
56
|
+
New group: \(group.debugDescription)
|
|
57
|
+
Overwriting previous binding.
|
|
58
|
+
""")
|
|
59
|
+
|
|
60
|
+
#if DEBUG
|
|
61
|
+
// assertionFailure("Duplicate group binding for groupId '\(groupId)'. Check React Native component IDs.")
|
|
62
|
+
#endif
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
self.group = group
|
|
66
|
+
|
|
67
|
+
if let preferredFocusView = delayedPreferredFocus {
|
|
68
|
+
applyPreferredFocus(group,
|
|
69
|
+
view: preferredFocusView)
|
|
70
|
+
delayedPreferredFocus = nil
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func unbindGroup() {
|
|
75
|
+
lock.lock()
|
|
76
|
+
defer { lock.unlock() }
|
|
77
|
+
|
|
78
|
+
if children.isEmpty {
|
|
79
|
+
GroupProxyManager.shared.removeProxy(forGroupId: groupId)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
group = nil
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// MARK: - Preferred Focus
|
|
86
|
+
|
|
87
|
+
@MainActor private func applyPreferredFocus(_ currentGroup: FocusableGroupView, view: FocusableView) {
|
|
88
|
+
if view.preferredFocus {
|
|
89
|
+
currentGroup.setUserPreferredFocusEnvironments([view])
|
|
90
|
+
} else if let currentPreferred = currentGroup.getUserPreferredFocusEnvironments()?.first as? FocusableView,
|
|
91
|
+
currentPreferred === view {
|
|
92
|
+
currentGroup.setUserPreferredFocusEnvironments(nil)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@MainActor func updatePreferredFocus(_ view: FocusableView) {
|
|
97
|
+
lock.lock()
|
|
98
|
+
let currentGroup = group
|
|
99
|
+
lock.unlock()
|
|
100
|
+
|
|
101
|
+
if let currentGroup {
|
|
102
|
+
applyPreferredFocus(currentGroup,
|
|
103
|
+
view: view)
|
|
104
|
+
} else {
|
|
105
|
+
if view.preferredFocus {
|
|
106
|
+
delayedPreferredFocus = view
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Get the bound group (if any)
|
|
112
|
+
func getGroup() -> FocusableGroupView? {
|
|
113
|
+
lock.lock()
|
|
114
|
+
defer { lock.unlock() }
|
|
115
|
+
|
|
116
|
+
return group
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// MARK: - WeakBox Helper
|
|
121
|
+
|
|
122
|
+
private class WeakBox<T: AnyObject> {
|
|
123
|
+
weak var value: T?
|
|
124
|
+
|
|
125
|
+
init(_ value: T) {
|
|
126
|
+
self.value = value
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// GroupProxyManager.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Anton Kononenko on 12/19/24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import React
|
|
10
|
+
|
|
11
|
+
class GroupProxyManager {
|
|
12
|
+
static let shared = GroupProxyManager()
|
|
13
|
+
|
|
14
|
+
private let lock = NSLock()
|
|
15
|
+
private var proxies: [GroupProxy] = []
|
|
16
|
+
|
|
17
|
+
private init() {}
|
|
18
|
+
|
|
19
|
+
// MARK: - Proxy Access
|
|
20
|
+
|
|
21
|
+
func getOrCreateProxy(forGroupId groupId: String) -> GroupProxy {
|
|
22
|
+
lock.lock()
|
|
23
|
+
defer { lock.unlock() }
|
|
24
|
+
|
|
25
|
+
if let existing = proxies.first(where: { $0.groupId == groupId }) {
|
|
26
|
+
return existing
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let proxy = GroupProxy(groupId: groupId)
|
|
30
|
+
proxies.append(proxy)
|
|
31
|
+
return proxy
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: - Cleanup
|
|
35
|
+
|
|
36
|
+
func removeProxy(forGroupId groupId: String) {
|
|
37
|
+
lock.lock()
|
|
38
|
+
defer { lock.unlock() }
|
|
39
|
+
|
|
40
|
+
proxies.removeAll { $0.groupId == groupId }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// MARK: - Focus Update
|
|
44
|
+
|
|
45
|
+
func updateFocus(groupId: String?,
|
|
46
|
+
itemId: String?,
|
|
47
|
+
needsForceUpdate: Bool = false,
|
|
48
|
+
completion: ((Bool) -> Void)? = nil) {
|
|
49
|
+
guard let groupId,
|
|
50
|
+
let itemId else {
|
|
51
|
+
completion?(false)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
lock.lock()
|
|
56
|
+
let proxy = proxies.first(where: { $0.groupId == groupId })
|
|
57
|
+
lock.unlock()
|
|
58
|
+
|
|
59
|
+
guard let proxy,
|
|
60
|
+
let groupView = proxy.getGroup() else {
|
|
61
|
+
completion?(false)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
DispatchQueue.main.async {
|
|
66
|
+
var rootView: UIView? = groupView
|
|
67
|
+
|
|
68
|
+
while let unwrapedRootView = rootView,
|
|
69
|
+
!unwrapedRootView.isReactRootView() {
|
|
70
|
+
rootView = rootView?.superview
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
guard let rootViewUnwrapped = rootView,
|
|
74
|
+
let superView = rootViewUnwrapped.superview as? RCTRootView else {
|
|
75
|
+
completion?(false)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if needsForceUpdate {
|
|
80
|
+
superView.setNeedsFocusUpdate()
|
|
81
|
+
superView.updateFocusIfNeeded()
|
|
82
|
+
|
|
83
|
+
let timeout: TimeInterval = 2.0
|
|
84
|
+
|
|
85
|
+
groupView.didFocusCallBack = (completion: {
|
|
86
|
+
completion?(true)
|
|
87
|
+
groupView.didFocusCallBack = nil
|
|
88
|
+
}, focusableItemId: itemId)
|
|
89
|
+
|
|
90
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
|
|
91
|
+
if groupView.didFocusCallBack != nil {
|
|
92
|
+
completion?(false)
|
|
93
|
+
groupView.didFocusCallBack = nil
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
completion?(true)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
import UIKit
|
|
9
9
|
|
|
10
10
|
class QuickBrickViewController: UIViewController {
|
|
11
|
-
|
|
11
|
+
func allowOrientationForScreen(_: ReactNativeOrientation) {
|
|
12
12
|
// not implemented
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/// Release Orientation for specific screen to previous state
|
|
16
|
-
|
|
16
|
+
func releaseOrientationForScreen() {
|
|
17
17
|
// not implemented
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -6,13 +6,10 @@
|
|
|
6
6
|
// Copyright © 2019 Kononenko. All rights reserved.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
|
-
import Combine
|
|
10
9
|
import Foundation
|
|
11
10
|
import React
|
|
12
11
|
import UIKit
|
|
13
12
|
|
|
14
|
-
/// Focusable Group View that implements UIFocusGuide instance that catches focus event
|
|
15
|
-
///
|
|
16
13
|
public class FocusableGroupView: RCTTVView {
|
|
17
14
|
override public var debugDescription: String {
|
|
18
15
|
let groupIdDesc = groupId ?? "NO_GROUP"
|
|
@@ -35,9 +32,6 @@ public class FocusableGroupView: RCTTVView {
|
|
|
35
32
|
"""
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
private var cancellables = Set<AnyCancellable>()
|
|
39
|
-
|
|
40
|
-
/// Completion that will be used when focus manager forcing to update focusable group
|
|
41
35
|
public var didFocusCallBack: (completion: () -> Void, focusableItemId: String)?
|
|
42
36
|
|
|
43
37
|
private lazy var activeStateNotifier: FocusableGroupStateNotifier = FocusableGroupStateNotifierDefault(action: notifyReactNativeFocusUpdate)
|
|
@@ -45,40 +39,38 @@ public class FocusableGroupView: RCTTVView {
|
|
|
45
39
|
@objc public var onGroupFocus: RCTDirectEventBlock?
|
|
46
40
|
@objc public var onGroupBlur: RCTDirectEventBlock?
|
|
47
41
|
|
|
48
|
-
/// Define if focus enabled for current view
|
|
49
42
|
@objc public var isFocusDisabled: Bool = false
|
|
50
43
|
|
|
51
|
-
/// Current group will try to find initial index id in groups
|
|
52
44
|
@objc public var dependantGroupIds: [String]?
|
|
53
45
|
|
|
54
|
-
/// Check if preferred focus environment disabled
|
|
55
46
|
@objc public var isWithMemory: Bool = true
|
|
56
47
|
var isPreferredFocusDisabled: Bool {
|
|
57
48
|
isWithMemory == false
|
|
58
49
|
}
|
|
59
50
|
|
|
60
|
-
/// ID of the Connected GroupView provided by React-Native env
|
|
61
51
|
@objc public var itemId: String? {
|
|
62
52
|
didSet {
|
|
63
53
|
if itemId != oldValue {
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
if oldValue != nil {
|
|
55
|
+
proxy?.unbindGroup()
|
|
56
|
+
proxy = nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if let newId = itemId {
|
|
60
|
+
proxy = GroupProxyManager.shared.getOrCreateProxy(forGroupId: newId)
|
|
61
|
+
proxy?.bindGroup(self)
|
|
66
62
|
}
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
65
|
}
|
|
70
66
|
|
|
71
|
-
/// ID of the parent group, if relevant
|
|
72
67
|
@objc public var groupId: String?
|
|
73
68
|
|
|
74
69
|
@MainActor private var userPreferredFocusEnvironments: [UIFocusEnvironment]?
|
|
75
70
|
@MainActor private var customPreferredFocusEnvironment: [UIFocusEnvironment]?
|
|
76
71
|
|
|
77
|
-
private var
|
|
78
|
-
FocusableGroupManager.shared
|
|
79
|
-
}
|
|
72
|
+
private var proxy: GroupProxy?
|
|
80
73
|
|
|
81
|
-
/// Manager that connects View instance to FocusableGroupViewModule
|
|
82
74
|
private weak var module: FocusableGroupViewModule?
|
|
83
75
|
public func setModule(_ module: FocusableGroupViewModule) {
|
|
84
76
|
self.module = module
|
|
@@ -90,17 +82,20 @@ public class FocusableGroupView: RCTTVView {
|
|
|
90
82
|
|
|
91
83
|
override public func removeFromSuperview() {
|
|
92
84
|
super.removeFromSuperview()
|
|
85
|
+
cleanup()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
deinit {
|
|
89
|
+
cleanup()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private func cleanup() {
|
|
93
93
|
guard let itemId else {
|
|
94
94
|
return
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
await manager.unregisterGroup(withId: itemId)
|
|
100
|
-
|
|
101
|
-
cancellables.forEach { $0.cancel() }
|
|
102
|
-
cancellables.removeAll()
|
|
103
|
-
}
|
|
97
|
+
proxy?.unbindGroup()
|
|
98
|
+
proxy = nil
|
|
104
99
|
}
|
|
105
100
|
|
|
106
101
|
public required init?(coder aDecoder: NSCoder) {
|
|
@@ -120,6 +115,14 @@ public class FocusableGroupView: RCTTVView {
|
|
|
120
115
|
customPreferredFocusEnvironment = [view]
|
|
121
116
|
}
|
|
122
117
|
|
|
118
|
+
@MainActor func setUserPreferredFocusEnvironments(_ environments: [UIFocusEnvironment]?) {
|
|
119
|
+
userPreferredFocusEnvironments = environments
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@MainActor func getUserPreferredFocusEnvironments() -> [UIFocusEnvironment]? {
|
|
123
|
+
userPreferredFocusEnvironments
|
|
124
|
+
}
|
|
125
|
+
|
|
123
126
|
let groupFocusGuide = UIFocusGuide()
|
|
124
127
|
|
|
125
128
|
private func setupFocus() {
|
|
@@ -129,27 +132,6 @@ public class FocusableGroupView: RCTTVView {
|
|
|
129
132
|
groupFocusGuide.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
|
130
133
|
groupFocusGuide.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
|
131
134
|
groupFocusGuide.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
|
|
132
|
-
|
|
133
|
-
manager.focusableItemDidUpdatePreferredFocus
|
|
134
|
-
.compactMap { [weak self] focusableView -> FocusableView? in
|
|
135
|
-
guard let self,
|
|
136
|
-
groupId != nil,
|
|
137
|
-
focusableView.groupId == self.itemId
|
|
138
|
-
else {
|
|
139
|
-
return nil
|
|
140
|
-
}
|
|
141
|
-
return focusableView
|
|
142
|
-
}
|
|
143
|
-
.sink { [weak self] focusableView in
|
|
144
|
-
guard let self else { return }
|
|
145
|
-
|
|
146
|
-
if focusableView.preferredFocus {
|
|
147
|
-
userPreferredFocusEnvironments = [focusableView]
|
|
148
|
-
} else if let currentPreferred = userPreferredFocusEnvironments?.first as? FocusableView,
|
|
149
|
-
currentPreferred == focusableView {
|
|
150
|
-
userPreferredFocusEnvironments = nil
|
|
151
|
-
}
|
|
152
|
-
}.store(in: &cancellables)
|
|
153
135
|
}
|
|
154
136
|
|
|
155
137
|
private func createFocusEventParams(context: UIFocusUpdateContext, isActive: Bool) -> [String: Any] {
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
// Copyright © 2019 Anton Kononenko. All rights reserved.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
|
-
import Combine
|
|
10
9
|
import Foundation
|
|
11
10
|
import React
|
|
12
11
|
import UIKit
|
|
@@ -36,8 +35,6 @@ public class FocusableView: ParallaxView {
|
|
|
36
35
|
"""
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
private var cancellables = Set<AnyCancellable>()
|
|
40
|
-
|
|
41
38
|
private weak var module: FocusableViewModule?
|
|
42
39
|
func setModule(_ module: FocusableViewModule) {
|
|
43
40
|
self.module = module
|
|
@@ -58,28 +55,21 @@ public class FocusableView: ParallaxView {
|
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
57
|
|
|
61
|
-
/// Define if view can become focused
|
|
62
58
|
@objc open var focusable = true
|
|
63
59
|
|
|
64
|
-
private var
|
|
65
|
-
FocusableGroupManager.shared
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@MainActor private weak var focusableGroup: FocusableGroupProtocol? {
|
|
69
|
-
didSet {
|
|
70
|
-
manager.notifyFocusableItemDidUpdatePreferredFocus(focusableView: self)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
60
|
+
private var proxy: GroupProxy?
|
|
73
61
|
|
|
74
62
|
@MainActor @objc public var preferredFocus: Bool = false {
|
|
75
63
|
didSet {
|
|
76
|
-
|
|
64
|
+
if oldValue == preferredFocus {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
proxy?.updatePreferredFocus(self)
|
|
77
69
|
}
|
|
78
70
|
}
|
|
79
71
|
|
|
80
72
|
private var isFocusLayoutConfigured = false
|
|
81
|
-
|
|
82
|
-
/// Define if view was registered
|
|
83
73
|
private var isViewRegistered: Bool = false
|
|
84
74
|
|
|
85
75
|
override public init(frame: CGRect) {
|
|
@@ -92,31 +82,34 @@ public class FocusableView: ParallaxView {
|
|
|
92
82
|
initialize()
|
|
93
83
|
}
|
|
94
84
|
|
|
95
|
-
/// Initialize component
|
|
96
85
|
private func initialize() {
|
|
97
86
|
delegate = self
|
|
98
|
-
|
|
99
|
-
manager.focusableGroupRegistrationUpdates
|
|
100
|
-
.filter { $0.itemId == self.groupId }
|
|
101
|
-
.sink { [weak self] focusableGroup in
|
|
102
|
-
guard let self else { return }
|
|
103
|
-
DispatchQueue.main.async {
|
|
104
|
-
self.focusableGroup = focusableGroup
|
|
105
|
-
}
|
|
106
|
-
}.store(in: &cancellables)
|
|
107
87
|
}
|
|
108
88
|
|
|
109
|
-
/// ID of the View provided by React-Native env
|
|
110
89
|
@objc private(set) var itemId: String? {
|
|
111
90
|
didSet {
|
|
112
|
-
|
|
91
|
+
if itemId != oldValue {
|
|
92
|
+
if oldValue != nil {
|
|
93
|
+
proxy?.removeChild(self)
|
|
94
|
+
isViewRegistered = false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
registerView()
|
|
98
|
+
}
|
|
113
99
|
}
|
|
114
100
|
}
|
|
115
101
|
|
|
116
|
-
/// ID of the View provided by React-Native env
|
|
117
102
|
@objc private(set) var groupId: String? {
|
|
118
103
|
didSet {
|
|
119
|
-
|
|
104
|
+
if groupId != oldValue {
|
|
105
|
+
if oldValue != nil {
|
|
106
|
+
proxy?.removeChild(self)
|
|
107
|
+
proxy = nil
|
|
108
|
+
isViewRegistered = false
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
registerView()
|
|
112
|
+
}
|
|
120
113
|
}
|
|
121
114
|
}
|
|
122
115
|
|
|
@@ -129,35 +122,31 @@ public class FocusableView: ParallaxView {
|
|
|
129
122
|
}
|
|
130
123
|
}
|
|
131
124
|
|
|
132
|
-
/// Register View in FocusableGroupManager
|
|
133
125
|
private func registerView() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
else {
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
await MainActor.run {
|
|
144
|
-
isViewRegistered = true
|
|
145
|
-
}
|
|
126
|
+
guard let groupId,
|
|
127
|
+
itemId != nil,
|
|
128
|
+
isViewRegistered == false
|
|
129
|
+
else {
|
|
130
|
+
return
|
|
146
131
|
}
|
|
132
|
+
|
|
133
|
+
proxy = GroupProxyManager.shared.getOrCreateProxy(forGroupId: groupId)
|
|
134
|
+
proxy?.addChild(self)
|
|
135
|
+
isViewRegistered = true
|
|
147
136
|
}
|
|
148
137
|
|
|
149
138
|
override public func removeFromSuperview() {
|
|
150
139
|
super.removeFromSuperview()
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
140
|
+
cleanup()
|
|
141
|
+
}
|
|
154
142
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
deinit {
|
|
144
|
+
cleanup()
|
|
145
|
+
}
|
|
158
146
|
|
|
159
|
-
|
|
160
|
-
|
|
147
|
+
private func cleanup() {
|
|
148
|
+
proxy?.removeChild(self)
|
|
149
|
+
proxy = nil
|
|
161
150
|
}
|
|
162
151
|
|
|
163
152
|
private func addFocusGuideIfNeeded(tag: NSNumber?,
|
|
@@ -226,32 +215,18 @@ public class FocusableView: ParallaxView {
|
|
|
226
215
|
}
|
|
227
216
|
|
|
228
217
|
DispatchQueue.main.async { [weak self] in
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
218
|
+
GroupProxyManager.shared.updateFocus(groupId: self?.groupId,
|
|
219
|
+
itemId: self?.itemId,
|
|
220
|
+
needsForceUpdate: true)
|
|
232
221
|
}
|
|
233
222
|
}
|
|
234
223
|
}
|
|
235
224
|
|
|
236
225
|
override public var canBecomeFocused: Bool {
|
|
237
|
-
guard let focusableGroup else {
|
|
226
|
+
guard let focusableGroup = proxy?.getGroup() else {
|
|
238
227
|
return focusable
|
|
239
228
|
}
|
|
240
229
|
|
|
241
230
|
return focusableGroup.canBecomeFocusable && focusable
|
|
242
231
|
}
|
|
243
232
|
}
|
|
244
|
-
|
|
245
|
-
// TODO: Example of solution for future with groupId and focuse id
|
|
246
|
-
// func hintLeftFocusId() {
|
|
247
|
-
// if let onFocusLeft = dictFromReactNative,
|
|
248
|
-
// let groupId = onFocusLeft["groupId"],
|
|
249
|
-
// let id = onFocusLeft["id"],
|
|
250
|
-
// let view = FocusableGroupManager.item(byGroupId: groupId,
|
|
251
|
-
// andItemId: id) {
|
|
252
|
-
// _ = addFocusGuide(from: self,
|
|
253
|
-
// to: view,
|
|
254
|
-
// direction: .left,
|
|
255
|
-
// debugMode: true)
|
|
256
|
-
// }
|
|
257
|
-
// }
|
|
@@ -16,12 +16,12 @@ class AnalyticsBridge: NSObject, RCTBridgeModule {
|
|
|
16
16
|
"AnalyticsBridge"
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
class func requiresMainQueueSetup() -> Bool {
|
|
20
20
|
true
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/// prefered thread on which to run this native module
|
|
24
|
-
@objc
|
|
24
|
+
@objc var methodQueue: DispatchQueue {
|
|
25
25
|
DispatchQueue.main
|
|
26
26
|
}
|
|
27
27
|
|