react-native-legal 1.1.0 → 1.1.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/android/build.gradle +5 -0
- package/android/src/main/java/com/reactnativelegal/ReactNativeLegalActivity.kt +23 -23
- package/android/src/main/java/com/reactnativelegal/ReactNativeLegalModuleImpl.kt +5 -4
- package/android/src/main/java/com/reactnativelegal/ReactNativeLegalTurboPackage.kt +4 -5
- package/android/src/newarch/java/com/reactnativelegal/ReactNativeLegalModule.kt +6 -4
- package/android/src/oldarch/java/com/reactnativelegal/ReactNativeLegalModule.kt +6 -5
- package/ios/ReactNativeLegalDetailViewController.swift +143 -130
- package/ios/ReactNativeLegalLicenseMetadata.swift +2 -2
- package/ios/ReactNativeLegalModule.h +1 -1
- package/ios/ReactNativeLegalModule.mm +2 -2
- package/ios/ReactNativeLegalModuleImpl.swift +74 -63
- package/ios/ReactNativeLegalTableViewController.swift +63 -59
- package/package.json +1 -1
- package/plugin-utils/build/common.d.ts +1 -0
- package/plugin-utils/build/common.js +5 -1
- package/plugin-utils/src/common.ts +9 -2
package/android/build.gradle
CHANGED
|
@@ -12,6 +12,7 @@ buildscript {
|
|
|
12
12
|
dependencies {
|
|
13
13
|
classpath("com.android.tools.build:gradle:7.2.1")
|
|
14
14
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
|
|
15
|
+
classpath("com.diffplug.spotless:com.diffplug.spotless.gradle.plugin:7.0.2")
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -19,6 +20,10 @@ def isNewArchitectureEnabled() {
|
|
|
19
20
|
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
if (project == rootProject) {
|
|
24
|
+
apply from: 'spotless.gradle'
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
apply plugin: 'com.android.library'
|
|
23
28
|
apply plugin: 'kotlin-android'
|
|
24
29
|
if (isNewArchitectureEnabled()) {
|
|
@@ -12,12 +12,10 @@ import com.mikepenz.aboutlibraries.LibsBuilder.Companion.BUNDLE_TITLE
|
|
|
12
12
|
import com.mikepenz.aboutlibraries.ui.LibsSupportFragment
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Based on AboutLibraries LibsActivity,
|
|
16
|
-
*
|
|
17
|
-
* with improved back handling
|
|
18
|
-
* and hidden Toolbar/ActionBar for TV devices
|
|
15
|
+
* Based on AboutLibraries LibsActivity, but simplified (no search filter), with improved back
|
|
16
|
+
* handling and hidden Toolbar/ActionBar for TV devices
|
|
19
17
|
*/
|
|
20
|
-
class ReactNativeLegalActivity: AppCompatActivity() {
|
|
18
|
+
class ReactNativeLegalActivity : AppCompatActivity() {
|
|
21
19
|
private lateinit var fragment: LibsSupportFragment
|
|
22
20
|
|
|
23
21
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
@@ -26,9 +24,7 @@ class ReactNativeLegalActivity: AppCompatActivity() {
|
|
|
26
24
|
setContentView(R.layout.activity_licenses)
|
|
27
25
|
|
|
28
26
|
val bundle = intent.extras
|
|
29
|
-
fragment = LibsSupportFragment().apply {
|
|
30
|
-
arguments = bundle
|
|
31
|
-
}
|
|
27
|
+
fragment = LibsSupportFragment().apply { arguments = bundle }
|
|
32
28
|
|
|
33
29
|
// https://developer.android.com/training/tv/start/hardware.html#runtime-check
|
|
34
30
|
val isTVDevice = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|
|
@@ -39,13 +35,16 @@ class ReactNativeLegalActivity: AppCompatActivity() {
|
|
|
39
35
|
setupToolbar(bundle)
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
supportFragmentManager
|
|
38
|
+
supportFragmentManager
|
|
39
|
+
.beginTransaction()
|
|
40
|
+
.replace(R.id.fragment_container, fragment)
|
|
41
|
+
.commit()
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
46
45
|
if (item.itemId == android.R.id.home) {
|
|
47
46
|
onBackPressedDispatcher.onBackPressed()
|
|
48
|
-
return true
|
|
47
|
+
return true
|
|
49
48
|
}
|
|
50
49
|
return super.onOptionsItemSelected(item)
|
|
51
50
|
}
|
|
@@ -57,9 +56,7 @@ class ReactNativeLegalActivity: AppCompatActivity() {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
private fun setupToolbar(bundle: Bundle?) {
|
|
60
|
-
val title = bundle?.let {
|
|
61
|
-
it.getString(BUNDLE_TITLE, "")
|
|
62
|
-
} ?: ""
|
|
59
|
+
val title = bundle?.let { it.getString(BUNDLE_TITLE, "") } ?: ""
|
|
63
60
|
|
|
64
61
|
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
|
65
62
|
setSupportActionBar(toolbar)
|
|
@@ -71,12 +68,13 @@ class ReactNativeLegalActivity: AppCompatActivity() {
|
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
toolbar.setOnApplyWindowInsetsListener { v, insets ->
|
|
74
|
-
val systemInsets =
|
|
75
|
-
|
|
71
|
+
val systemInsets =
|
|
72
|
+
WindowInsetsCompat.toWindowInsetsCompat(insets)
|
|
73
|
+
.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
76
74
|
v.updatePadding(
|
|
77
75
|
left = v.paddingLeft + systemInsets.left,
|
|
78
76
|
top = v.paddingTop + systemInsets.top,
|
|
79
|
-
right = v.paddingRight + systemInsets.right
|
|
77
|
+
right = v.paddingRight + systemInsets.right,
|
|
80
78
|
)
|
|
81
79
|
|
|
82
80
|
insets
|
|
@@ -85,14 +83,16 @@ class ReactNativeLegalActivity: AppCompatActivity() {
|
|
|
85
83
|
if (toolbar.isAttachedToWindow) {
|
|
86
84
|
toolbar.requestApplyInsets()
|
|
87
85
|
} else {
|
|
88
|
-
toolbar.addOnAttachStateChangeListener(
|
|
89
|
-
|
|
90
|
-
v
|
|
91
|
-
|
|
86
|
+
toolbar.addOnAttachStateChangeListener(
|
|
87
|
+
object : View.OnAttachStateChangeListener {
|
|
88
|
+
override fun onViewAttachedToWindow(v: View) {
|
|
89
|
+
v.removeOnAttachStateChangeListener(this)
|
|
90
|
+
v.requestApplyInsets()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override fun onViewDetachedFromWindow(v: View) = Unit
|
|
92
94
|
}
|
|
93
|
-
|
|
94
|
-
override fun onViewDetachedFromWindow(v: View) = Unit
|
|
95
|
-
})
|
|
95
|
+
)
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -9,10 +9,11 @@ object ReactNativeLegalModuleImpl {
|
|
|
9
9
|
|
|
10
10
|
fun launchLicenseListScreen(reactContext: ReactApplicationContext, licenseHeaderText: String) {
|
|
11
11
|
val context = reactContext.currentActivity ?: return
|
|
12
|
-
val intent =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
val intent =
|
|
13
|
+
Intent(context, ReactNativeLegalActivity::class.java).apply {
|
|
14
|
+
this.putExtra("data", LibsBuilder())
|
|
15
|
+
this.putExtra(LibsBuilder.BUNDLE_TITLE, licenseHeaderText)
|
|
16
|
+
}
|
|
16
17
|
|
|
17
18
|
context.startActivity(intent)
|
|
18
19
|
}
|
|
@@ -8,7 +8,7 @@ import com.facebook.react.module.model.ReactModuleInfo
|
|
|
8
8
|
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
9
9
|
import com.facebook.react.turbomodule.core.interfaces.TurboModule
|
|
10
10
|
|
|
11
|
-
class ReactNativeLegalTurboPackage: TurboReactPackage() {
|
|
11
|
+
class ReactNativeLegalTurboPackage : TurboReactPackage() {
|
|
12
12
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
13
13
|
return when (name) {
|
|
14
14
|
ReactNativeLegalModule.NAME -> ReactNativeLegalModule(reactContext)
|
|
@@ -17,9 +17,8 @@ class ReactNativeLegalTurboPackage: TurboReactPackage() {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
20
|
-
val moduleList: Array<Class<out NativeModule?>> =
|
|
21
|
-
ReactNativeLegalModule::class.java
|
|
22
|
-
)
|
|
20
|
+
val moduleList: Array<Class<out NativeModule?>> =
|
|
21
|
+
arrayOf(ReactNativeLegalModule::class.java)
|
|
23
22
|
val reactModuleInfoMap: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
24
23
|
for (moduleClass in moduleList) {
|
|
25
24
|
val reactModule = moduleClass.getAnnotation(ReactModule::class.java) ?: continue
|
|
@@ -30,7 +29,7 @@ class ReactNativeLegalTurboPackage: TurboReactPackage() {
|
|
|
30
29
|
true,
|
|
31
30
|
reactModule.needsEagerInit,
|
|
32
31
|
reactModule.isCxxModule,
|
|
33
|
-
TurboModule::class.java.isAssignableFrom(moduleClass)
|
|
32
|
+
TurboModule::class.java.isAssignableFrom(moduleClass),
|
|
34
33
|
)
|
|
35
34
|
}
|
|
36
35
|
return ReactModuleInfoProvider { reactModuleInfoMap }
|
|
@@ -4,11 +4,13 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
4
4
|
import com.facebook.react.module.annotations.ReactModule
|
|
5
5
|
|
|
6
6
|
@ReactModule(name = ReactNativeLegalModule.NAME)
|
|
7
|
-
class ReactNativeLegalModule(
|
|
8
|
-
reactContext
|
|
9
|
-
) : NativeReactNativeLegalSpec(reactContext) {
|
|
7
|
+
class ReactNativeLegalModule(reactContext: ReactApplicationContext) :
|
|
8
|
+
NativeReactNativeLegalSpec(reactContext) {
|
|
10
9
|
override fun launchLicenseListScreen(licenseHeaderText: String) {
|
|
11
|
-
ReactNativeLegalModuleImpl.launchLicenseListScreen(
|
|
10
|
+
ReactNativeLegalModuleImpl.launchLicenseListScreen(
|
|
11
|
+
reactApplicationContext,
|
|
12
|
+
licenseHeaderText,
|
|
13
|
+
)
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
companion object {
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
package com.reactnativelegal
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.bridge.Promise
|
|
4
3
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
4
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
5
|
import com.facebook.react.bridge.ReactMethod
|
|
7
6
|
import com.facebook.react.module.annotations.ReactModule
|
|
8
7
|
|
|
9
8
|
@ReactModule(name = ReactNativeLegalModule.NAME)
|
|
10
|
-
class ReactNativeLegalModule(
|
|
11
|
-
|
|
12
|
-
) : ReactContextBaseJavaModule(reactContext) {
|
|
9
|
+
class ReactNativeLegalModule(private val reactContext: ReactApplicationContext) :
|
|
10
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
13
11
|
override fun getName() = NAME
|
|
14
12
|
|
|
15
13
|
@ReactMethod
|
|
16
14
|
fun launchLicenseListScreen(licenseHeaderText: String) {
|
|
17
|
-
ReactNativeLegalModuleImpl.launchLicenseListScreen(
|
|
15
|
+
ReactNativeLegalModuleImpl.launchLicenseListScreen(
|
|
16
|
+
reactApplicationContext,
|
|
17
|
+
licenseHeaderText,
|
|
18
|
+
)
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
companion object {
|
|
@@ -1,156 +1,169 @@
|
|
|
1
1
|
class ReactNativeLegalDetailViewController: UIViewController {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
#if os(tvOS)
|
|
6
|
-
let fontSize: CGFloat = 40
|
|
7
|
-
let horizontalContentInset: CGFloat = 100
|
|
8
|
-
let bottomContentInset: CGFloat = 100
|
|
9
|
-
#else
|
|
10
|
-
let fontSize: CGFloat = 13.3
|
|
11
|
-
let horizontalContentInset: CGFloat = 26
|
|
12
|
-
let bottomContentInset: CGFloat = 16
|
|
13
|
-
#endif
|
|
14
|
-
|
|
15
|
-
textView.alwaysBounceVertical = true
|
|
16
|
-
#if os(iOS)
|
|
17
|
-
textView.dataDetectorTypes = .link
|
|
18
|
-
#endif
|
|
19
|
-
textView.font = UIFont.systemFont(ofSize: fontSize)
|
|
20
|
-
#if os(iOS)
|
|
21
|
-
textView.isEditable = false
|
|
22
|
-
#endif
|
|
23
|
-
#if os(tvOS)
|
|
24
|
-
textView.isSelectable = true
|
|
25
|
-
textView.isUserInteractionEnabled = true
|
|
26
|
-
#endif
|
|
27
|
-
textView.textContainerInset = UIEdgeInsets(top: 0, left: horizontalContentInset, bottom: bottomContentInset, right: horizontalContentInset)
|
|
28
|
-
textView.translatesAutoresizingMaskIntoConstraints = false
|
|
29
|
-
|
|
30
|
-
if #available(iOS 13.0, tvOS 13.0, *) {
|
|
31
|
-
#if os(iOS)
|
|
32
|
-
textView.backgroundColor = .systemBackground
|
|
33
|
-
#endif
|
|
34
|
-
textView.textColor = .secondaryLabel
|
|
35
|
-
}
|
|
2
|
+
private var licenseContentTextView: UITextView = {
|
|
3
|
+
let textView = UITextView()
|
|
36
4
|
|
|
37
|
-
|
|
38
|
-
|
|
5
|
+
#if os(tvOS)
|
|
6
|
+
let fontSize: CGFloat = 40
|
|
7
|
+
let horizontalContentInset: CGFloat = 100
|
|
8
|
+
let bottomContentInset: CGFloat = 100
|
|
9
|
+
#else
|
|
10
|
+
let fontSize: CGFloat = 13.3
|
|
11
|
+
let horizontalContentInset: CGFloat = 26
|
|
12
|
+
let bottomContentInset: CGFloat = 16
|
|
13
|
+
#endif
|
|
39
14
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
15
|
+
textView.alwaysBounceVertical = true
|
|
16
|
+
#if os(iOS)
|
|
17
|
+
textView.dataDetectorTypes = .link
|
|
18
|
+
#endif
|
|
19
|
+
textView.font = UIFont.systemFont(ofSize: fontSize)
|
|
20
|
+
#if os(iOS)
|
|
21
|
+
textView.isEditable = false
|
|
22
|
+
#endif
|
|
23
|
+
#if os(tvOS)
|
|
24
|
+
textView.isSelectable = true
|
|
25
|
+
textView.isUserInteractionEnabled = true
|
|
26
|
+
#endif
|
|
27
|
+
textView.textContainerInset = UIEdgeInsets(
|
|
28
|
+
top: 0, left: horizontalContentInset, bottom: bottomContentInset,
|
|
29
|
+
right: horizontalContentInset)
|
|
30
|
+
textView.translatesAutoresizingMaskIntoConstraints = false
|
|
31
|
+
|
|
32
|
+
if #available(iOS 13.0, tvOS 13.0, *) {
|
|
33
|
+
#if os(iOS)
|
|
34
|
+
textView.backgroundColor = .systemBackground
|
|
35
|
+
#endif
|
|
36
|
+
textView.textColor = .secondaryLabel
|
|
50
37
|
}
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
|
|
39
|
+
return textView
|
|
40
|
+
}()
|
|
41
|
+
|
|
42
|
+
private var metadata: ReactNativeLegalLicenseMetadata
|
|
43
|
+
|
|
44
|
+
init(metadata: ReactNativeLegalLicenseMetadata) {
|
|
45
|
+
self.metadata = metadata
|
|
46
|
+
super.init(nibName: nil, bundle: nil)
|
|
47
|
+
self.title = metadata.name
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
required init?(coder: NSCoder) {
|
|
51
|
+
fatalError("init(coder:) has not been implemented")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#if os(iOS)
|
|
53
55
|
private var scrolledToTopOnMount = false
|
|
54
|
-
|
|
56
|
+
|
|
55
57
|
override func viewDidLayoutSubviews() {
|
|
56
|
-
|
|
58
|
+
super.viewDidLayoutSubviews()
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
if scrolledToTopOnMount {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
licenseContentTextView.setContentOffset(CGPoint.zero, animated: false)
|
|
65
|
+
scrolledToTopOnMount = true
|
|
64
66
|
}
|
|
65
|
-
#endif
|
|
66
|
-
|
|
67
|
-
override func viewDidLoad() {
|
|
68
|
-
super.viewDidLoad()
|
|
69
|
-
|
|
70
|
-
view.addSubview(licenseContentTextView)
|
|
71
|
-
#if os(iOS)
|
|
72
|
-
if #available(iOS 13.0, *) {
|
|
73
|
-
view.backgroundColor = .systemBackground
|
|
74
|
-
}
|
|
75
|
-
#endif
|
|
67
|
+
#endif
|
|
76
68
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
licenseContentTextView.bottomAnchor.constraint(equalToSystemSpacingBelow: view.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0),
|
|
80
|
-
licenseContentTextView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1.0),
|
|
81
|
-
licenseContentTextView.trailingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.trailingAnchor, multiplier: 1.0)
|
|
82
|
-
])
|
|
69
|
+
override func viewDidLoad() {
|
|
70
|
+
super.viewDidLoad()
|
|
83
71
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
|
|
91
|
-
if context.previouslyFocusedView == context.nextFocusedView {
|
|
92
|
-
return
|
|
93
|
-
}
|
|
72
|
+
view.addSubview(licenseContentTextView)
|
|
73
|
+
#if os(iOS)
|
|
74
|
+
if #available(iOS 13.0, *) {
|
|
75
|
+
view.backgroundColor = .systemBackground
|
|
76
|
+
}
|
|
77
|
+
#endif
|
|
94
78
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
79
|
+
NSLayoutConstraint.activate([
|
|
80
|
+
licenseContentTextView.topAnchor.constraint(
|
|
81
|
+
equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor, multiplier: 1.0),
|
|
82
|
+
licenseContentTextView.bottomAnchor.constraint(
|
|
83
|
+
equalToSystemSpacingBelow: view.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0),
|
|
84
|
+
licenseContentTextView.leadingAnchor.constraint(
|
|
85
|
+
equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1.0),
|
|
86
|
+
licenseContentTextView.trailingAnchor.constraint(
|
|
87
|
+
equalToSystemSpacingAfter: view.safeAreaLayoutGuide.trailingAnchor, multiplier: 1.0),
|
|
88
|
+
])
|
|
89
|
+
|
|
90
|
+
licenseContentTextView.text = metadata.content
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#if os(tvOS)
|
|
94
|
+
private var tvRemoteGestureRecognizers: [String: UISwipeGestureRecognizer] = [:]
|
|
95
|
+
|
|
96
|
+
override func didUpdateFocus(
|
|
97
|
+
in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator
|
|
98
|
+
) {
|
|
99
|
+
if context.previouslyFocusedView == context.nextFocusedView {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if context.nextFocusedView == licenseContentTextView {
|
|
104
|
+
licenseContentTextView.becomeFirstResponder()
|
|
105
|
+
self.addSwipeGestureRecognizers()
|
|
106
|
+
} else if context.previouslyFocusedView == licenseContentTextView {
|
|
107
|
+
self.removeSwipeGestureRecognizers()
|
|
108
|
+
licenseContentTextView.resignFirstResponder()
|
|
109
|
+
}
|
|
102
110
|
}
|
|
103
|
-
|
|
111
|
+
|
|
104
112
|
private func addSwipeGestureRecognizers() {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
let recognizerUp = UISwipeGestureRecognizer(target: self, action: #selector(swipedUp))
|
|
114
|
+
recognizerUp.direction = .up
|
|
115
|
+
|
|
116
|
+
let recognizerDown = UISwipeGestureRecognizer(target: self, action: #selector(swipedDown))
|
|
117
|
+
recognizerDown.direction = .down
|
|
118
|
+
|
|
119
|
+
tvRemoteGestureRecognizers["swipedUp"] = recognizerUp
|
|
120
|
+
tvRemoteGestureRecognizers["swipedDown"] = recognizerDown
|
|
121
|
+
|
|
122
|
+
for recognizer in tvRemoteGestureRecognizers.values {
|
|
123
|
+
licenseContentTextView.addGestureRecognizer(recognizer)
|
|
124
|
+
}
|
|
117
125
|
}
|
|
118
|
-
|
|
126
|
+
|
|
119
127
|
private func removeSwipeGestureRecognizers() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
if let recognizerUp = tvRemoteGestureRecognizers["swipedUp"] {
|
|
129
|
+
licenseContentTextView.removeGestureRecognizer(recognizerUp)
|
|
130
|
+
}
|
|
123
131
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
if let recognizerDown = tvRemoteGestureRecognizers["swipedDown"] {
|
|
133
|
+
licenseContentTextView.removeGestureRecognizer(recognizerDown)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
tvRemoteGestureRecognizers.removeAll()
|
|
129
137
|
}
|
|
130
|
-
|
|
138
|
+
|
|
131
139
|
@objc private func swipedUp() {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
let offset =
|
|
141
|
+
licenseContentTextView.contentOffset.y - licenseContentTextView.visibleSize.height / 2
|
|
142
|
+
|
|
143
|
+
self.swipeVertical(with: offset)
|
|
135
144
|
}
|
|
136
|
-
|
|
145
|
+
|
|
137
146
|
@objc private func swipedDown() {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
let offset =
|
|
148
|
+
licenseContentTextView.contentOffset.y + licenseContentTextView.visibleSize.height / 2
|
|
149
|
+
|
|
150
|
+
self.swipeVertical(with: offset)
|
|
141
151
|
}
|
|
142
|
-
|
|
152
|
+
|
|
143
153
|
@objc private func swipeVertical(with offset: CGFloat) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
DispatchQueue.main.async {
|
|
155
|
+
let newOffset = min(
|
|
156
|
+
max(offset, 0),
|
|
157
|
+
self.licenseContentTextView.contentSize.height
|
|
158
|
+
- self.licenseContentTextView.visibleSize.height)
|
|
159
|
+
|
|
160
|
+
UIView.animate(withDuration: 0.5) {
|
|
161
|
+
self.licenseContentTextView.contentOffset = CGPoint(
|
|
162
|
+
x: self.licenseContentTextView.contentOffset.x,
|
|
163
|
+
y: newOffset
|
|
164
|
+
)
|
|
153
165
|
}
|
|
166
|
+
}
|
|
154
167
|
}
|
|
155
|
-
#endif
|
|
168
|
+
#endif
|
|
156
169
|
}
|
|
@@ -19,14 +19,14 @@ RCT_EXPORT_MODULE(ReactNativeLegalModule)
|
|
|
19
19
|
|
|
20
20
|
RCT_EXPORT_METHOD(launchLicenseListScreen : (NSString *)licenseHeaderText)
|
|
21
21
|
{
|
|
22
|
-
|
|
22
|
+
[ReactNativeLegalModuleImpl launchLicenseListScreenWithLicenseHeaderText:licenseHeaderText];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
#if RCT_NEW_ARCH_ENABLED
|
|
26
26
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
27
27
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
28
28
|
{
|
|
29
|
-
|
|
29
|
+
return std::make_shared<facebook::react::NativeReactNativeLegalSpecJSI>(params);
|
|
30
30
|
}
|
|
31
31
|
#endif
|
|
32
32
|
|
|
@@ -1,78 +1,89 @@
|
|
|
1
1
|
import UIKit
|
|
2
2
|
|
|
3
3
|
@objc(ReactNativeLegalModuleImpl)
|
|
4
|
-
public class ReactNativeLegalModuleImpl
|
|
5
|
-
|
|
4
|
+
public class ReactNativeLegalModuleImpl: NSObject {
|
|
5
|
+
@objc public static func launchLicenseListScreen(licenseHeaderText: String) {
|
|
6
|
+
guard
|
|
7
|
+
let settingsBundleUrl = Bundle.main.url(forResource: "Settings", withExtension: "bundle"),
|
|
8
|
+
let settingsBundle = Bundle(url: settingsBundleUrl),
|
|
9
|
+
let licensePlistUrl = settingsBundle.url(
|
|
10
|
+
forResource: "com.mono0926.LicensePlist", withExtension: "plist"),
|
|
11
|
+
let dictArray = parsePlistToDictArray(fileUrl: licensePlistUrl)
|
|
12
|
+
else {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let licenses = parseRawLicenseMetadataToArray(
|
|
17
|
+
rawLicenses: getChildPaneSpecifiers(dictArray: dictArray),
|
|
18
|
+
licensePlistUrl: licensePlistUrl
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
DispatchQueue.main.async {
|
|
22
|
+
guard let presentedViewController = RCTPresentedViewController() else {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let tableViewController = ReactNativeLegalTableViewController(
|
|
27
|
+
title: licenseHeaderText, data: licenses)
|
|
28
|
+
let navController = UINavigationController(rootViewController: tableViewController)
|
|
29
|
+
navController.modalPresentationStyle = .fullScreen
|
|
30
|
+
presentedViewController.present(navController, animated: true)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private static func getChildPaneSpecifiers(dictArray: [[String: Any]]) -> [[String: Any]] {
|
|
35
|
+
return
|
|
36
|
+
dictArray
|
|
37
|
+
.filter { $0["Type"] as? String == "PSChildPaneSpecifier" }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private static func parseRawLicenseMetadataToArray(
|
|
41
|
+
rawLicenses: [[String: Any]], licensePlistUrl: URL
|
|
42
|
+
) -> [ReactNativeLegalLicenseMetadata] {
|
|
43
|
+
return
|
|
44
|
+
rawLicenses
|
|
45
|
+
.map { licenseObj in
|
|
6
46
|
guard
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return
|
|
47
|
+
let file = licenseObj["File"] as? String,
|
|
48
|
+
let title = licenseObj["Title"] as? String
|
|
49
|
+
else {
|
|
50
|
+
return ReactNativeLegalLicenseMetadata()
|
|
12
51
|
}
|
|
13
|
-
|
|
14
|
-
let licenses = parseRawLicenseMetadataToArray(
|
|
15
|
-
rawLicenses: getChildPaneSpecifiers(dictArray: dictArray),
|
|
16
|
-
licensePlistUrl: licensePlistUrl
|
|
17
|
-
)
|
|
18
52
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let tableViewController = ReactNativeLegalTableViewController(title: licenseHeaderText, data: licenses)
|
|
25
|
-
let navController = UINavigationController(rootViewController: tableViewController)
|
|
26
|
-
navController.modalPresentationStyle = .fullScreen
|
|
27
|
-
presentedViewController.present(navController, animated: true)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private static func getChildPaneSpecifiers(dictArray: Array<Dictionary<String, Any>>) -> Array<Dictionary<String, Any>> {
|
|
32
|
-
return dictArray
|
|
33
|
-
.filter { $0["Type"] as? String == "PSChildPaneSpecifier" }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
private static func parseRawLicenseMetadataToArray(rawLicenses: Array<Dictionary<String, Any>>, licensePlistUrl: URL) -> [ReactNativeLegalLicenseMetadata] {
|
|
37
|
-
return rawLicenses
|
|
38
|
-
.map { licenseObj in
|
|
39
|
-
guard
|
|
40
|
-
let file = licenseObj["File"] as? String,
|
|
41
|
-
let title = licenseObj["Title"] as? String else {
|
|
42
|
-
return ReactNativeLegalLicenseMetadata()
|
|
43
|
-
}
|
|
53
|
+
let fileUrl = licensePlistUrl.deletingLastPathComponent().addPathComponent(file)
|
|
54
|
+
.appendingPathExtension("plist")
|
|
44
55
|
|
|
45
|
-
let fileUrl = licensePlistUrl.deletingLastPathComponent().addPathComponent(file).appendingPathExtension("plist")
|
|
46
|
-
|
|
47
|
-
guard
|
|
48
|
-
let data = parsePlistToDictArray(fileUrl: fileUrl),
|
|
49
|
-
let item = data.first(where: { $0["FooterText"] != nil }),
|
|
50
|
-
let footerText = item["FooterText"] as? String else {
|
|
51
|
-
return ReactNativeLegalLicenseMetadata(name: title)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return ReactNativeLegalLicenseMetadata(name: title, content: footerText)
|
|
55
|
-
}
|
|
56
|
-
.filter { $0.name != nil }
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private static func parsePlistToDictArray(fileUrl: URL) -> Array<Dictionary<String, Any>>? {
|
|
60
56
|
guard
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
let data = parsePlistToDictArray(fileUrl: fileUrl),
|
|
58
|
+
let item = data.first(where: { $0["FooterText"] != nil }),
|
|
59
|
+
let footerText = item["FooterText"] as? String
|
|
60
|
+
else {
|
|
61
|
+
return ReactNativeLegalLicenseMetadata(name: title)
|
|
64
62
|
}
|
|
65
|
-
|
|
66
|
-
return
|
|
63
|
+
|
|
64
|
+
return ReactNativeLegalLicenseMetadata(name: title, content: footerText)
|
|
65
|
+
}
|
|
66
|
+
.filter { $0.name != nil }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private static func parsePlistToDictArray(fileUrl: URL) -> [[String: Any]]? {
|
|
70
|
+
guard
|
|
71
|
+
let data = try? NSDictionary(contentsOf: fileUrl, error: ()),
|
|
72
|
+
let array = data["PreferenceSpecifiers"] as? [Any]
|
|
73
|
+
else {
|
|
74
|
+
return nil
|
|
67
75
|
}
|
|
76
|
+
|
|
77
|
+
return array.compactMap { $0 as? [String: Any] }
|
|
78
|
+
}
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
extension URL {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return self.appendingPathComponent(component)
|
|
82
|
+
func addPathComponent(_ component: String) -> URL {
|
|
83
|
+
if #available(iOS 16.0, tvOS 16.0, *) {
|
|
84
|
+
return self.appending(component: component)
|
|
77
85
|
}
|
|
86
|
+
|
|
87
|
+
return self.appendingPathComponent(component)
|
|
88
|
+
}
|
|
78
89
|
}
|
|
@@ -1,72 +1,76 @@
|
|
|
1
1
|
class ReactNativeLegalTableViewController: UITableViewController {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
2
|
+
private let cellIdentifier = "ReactNativeLegalCell"
|
|
3
|
+
|
|
4
|
+
private var data: [ReactNativeLegalLicenseMetadata] = [] {
|
|
5
|
+
didSet {
|
|
6
|
+
tableView.reloadData()
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
#else
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public convenience init(title: String, data: [ReactNativeLegalLicenseMetadata]) {
|
|
11
|
+
#if os(iOS)
|
|
12
|
+
if #available(iOS 13.0, *) {
|
|
13
|
+
self.init(style: .insetGrouped)
|
|
14
|
+
} else {
|
|
18
15
|
self.init(style: .grouped)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
}
|
|
17
|
+
#else
|
|
18
|
+
self.init(style: .grouped)
|
|
19
|
+
#endif
|
|
20
|
+
self.title = title
|
|
21
|
+
self.data = data
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#if os(iOS)
|
|
25
25
|
override func viewDidLoad() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
#endif
|
|
31
|
-
|
|
32
|
-
@objc func dismissTableViewController(_ sender: AnyObject) {
|
|
33
|
-
self.navigationController?.dismiss(animated: true)
|
|
26
|
+
super.viewDidLoad()
|
|
27
|
+
|
|
28
|
+
setupCloseButton()
|
|
34
29
|
}
|
|
35
|
-
|
|
36
|
-
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
37
|
-
let dequeuedCell = tableView.dequeueReusableCell(withIdentifier: CELL_IDENTIFIER) ?? UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: CELL_IDENTIFIER)
|
|
30
|
+
#endif
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
@objc func dismissTableViewController(_ sender: AnyObject) {
|
|
33
|
+
self.navigationController?.dismiss(animated: true)
|
|
34
|
+
}
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
|
|
37
|
+
-> UITableViewCell
|
|
38
|
+
{
|
|
39
|
+
let dequeuedCell =
|
|
40
|
+
tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
|
|
41
|
+
?? UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: cellIdentifier)
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
48
|
-
return data.count
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
52
|
-
let metadata = data[indexPath.row]
|
|
53
|
-
let detailViewController = ReactNativeLegalDetailViewController(metadata: metadata)
|
|
43
|
+
let metadata = data[indexPath.row]
|
|
54
44
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
dequeuedCell.textLabel?.text = metadata.name
|
|
46
|
+
dequeuedCell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
|
|
47
|
+
|
|
48
|
+
return dequeuedCell
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
52
|
+
return data.count
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
56
|
+
let metadata = data[indexPath.row]
|
|
57
|
+
let detailViewController = ReactNativeLegalDetailViewController(metadata: metadata)
|
|
58
|
+
|
|
59
|
+
self.navigationController?.pushViewController(detailViewController, animated: true)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#if os(iOS)
|
|
59
63
|
private func setupCloseButton() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
var barButtonSystemItem: UIBarButtonItem.SystemItem = .done
|
|
65
|
+
if #available(iOS 13.0, *) {
|
|
66
|
+
barButtonSystemItem = .close
|
|
67
|
+
}
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
self.navigationItem.leftBarButtonItem = UIBarButtonItem(
|
|
70
|
+
barButtonSystemItem: barButtonSystemItem,
|
|
71
|
+
target: self,
|
|
72
|
+
action: #selector(dismissTableViewController(_:))
|
|
73
|
+
)
|
|
70
74
|
}
|
|
71
|
-
#endif
|
|
75
|
+
#endif
|
|
72
76
|
}
|
package/package.json
CHANGED
|
@@ -61,6 +61,7 @@ function scanPackage(packageName, version, processedPackages, result) {
|
|
|
61
61
|
result[packageName] = {
|
|
62
62
|
author: parseAuthorField(localPackageJson),
|
|
63
63
|
content: licenseFiles?.[0] ? fs_1.default.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined,
|
|
64
|
+
file: licenseFiles?.[0] ? licenseFiles[0] : undefined,
|
|
64
65
|
description: localPackageJson.description,
|
|
65
66
|
type: parseLicenseField(localPackageJson),
|
|
66
67
|
url: parseRepositoryFieldToUrl(localPackageJson),
|
|
@@ -171,11 +172,14 @@ function generateLicensePlistNPMOutput(licenses, iosProjectPath) {
|
|
|
171
172
|
if (dependency !== normalizedName) {
|
|
172
173
|
renames[normalizedName] = dependency;
|
|
173
174
|
}
|
|
175
|
+
const relativeLicenseFile = licenseObj.file ? path_1.default.relative(iosProjectPath, licenseObj.file) : undefined;
|
|
174
176
|
return {
|
|
175
177
|
name: normalizedName,
|
|
176
178
|
version: licenseObj.version,
|
|
177
179
|
...(licenseObj.url && { source: licenseObj.url }),
|
|
178
|
-
|
|
180
|
+
...(licenseObj.file
|
|
181
|
+
? { file: relativeLicenseFile }
|
|
182
|
+
: { body: licenseObj.content ?? licenseObj.type ?? 'UNKNOWN' }),
|
|
179
183
|
};
|
|
180
184
|
});
|
|
181
185
|
const yamlDoc = {
|
|
@@ -8,6 +8,7 @@ type LicenseObj = {
|
|
|
8
8
|
author?: string;
|
|
9
9
|
content?: string;
|
|
10
10
|
description?: string;
|
|
11
|
+
file?: string;
|
|
11
12
|
type?: string;
|
|
12
13
|
url?: string;
|
|
13
14
|
version: string;
|
|
@@ -31,10 +32,11 @@ type AboutLibrariesLicenseJsonPayload = {
|
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
type LicensePlistPayload = {
|
|
34
|
-
body: string;
|
|
35
35
|
name: string;
|
|
36
36
|
source?: string;
|
|
37
37
|
version: string;
|
|
38
|
+
body?: string;
|
|
39
|
+
file?: string;
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
function compareObjects(a: unknown, b: unknown): boolean {
|
|
@@ -110,6 +112,7 @@ function scanPackage(
|
|
|
110
112
|
result[packageName] = {
|
|
111
113
|
author: parseAuthorField(localPackageJson),
|
|
112
114
|
content: licenseFiles?.[0] ? fs.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined,
|
|
115
|
+
file: licenseFiles?.[0] ? licenseFiles[0] : undefined,
|
|
113
116
|
description: localPackageJson.description,
|
|
114
117
|
type: parseLicenseField(localPackageJson),
|
|
115
118
|
url: parseRepositoryFieldToUrl(localPackageJson),
|
|
@@ -243,11 +246,15 @@ export function generateLicensePlistNPMOutput(licenses: Record<string, LicenseOb
|
|
|
243
246
|
renames[normalizedName] = dependency;
|
|
244
247
|
}
|
|
245
248
|
|
|
249
|
+
const relativeLicenseFile = licenseObj.file ? path.relative(iosProjectPath, licenseObj.file) : undefined;
|
|
250
|
+
|
|
246
251
|
return {
|
|
247
252
|
name: normalizedName,
|
|
248
253
|
version: licenseObj.version,
|
|
249
254
|
...(licenseObj.url && { source: licenseObj.url }),
|
|
250
|
-
|
|
255
|
+
...(licenseObj.file
|
|
256
|
+
? { file: relativeLicenseFile }
|
|
257
|
+
: { body: licenseObj.content ?? licenseObj.type ?? 'UNKNOWN' }),
|
|
251
258
|
} as LicensePlistPayload;
|
|
252
259
|
});
|
|
253
260
|
|